Snapshot of commit d5ec1d5018ed24f1b4f32b1d09df6dbd7e2fc425
from branch master of git://git.jetbrains.org/idea/community.git
diff --git a/java/idea-ui/idea-ui.iml b/java/idea-ui/idea-ui.iml
new file mode 100644
index 0000000..4a549c5
--- /dev/null
+++ b/java/idea-ui/idea-ui.iml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module relativePaths="true" type="JAVA_MODULE" version="4">
+ <component name="NewModuleRootManager" inherit-compiler-output="true">
+ <exclude-output />
+ <content url="file://$MODULE_DIR$">
+ <sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
+ </content>
+ <orderEntry type="inheritedJdk" />
+ <orderEntry type="sourceFolder" forTests="false" />
+ <orderEntry type="module" module-name="openapi" />
+ <orderEntry type="module" module-name="compiler-openapi" />
+ <orderEntry type="module" module-name="compiler-impl" />
+ <orderEntry type="module" module-name="java-impl" />
+ <orderEntry type="library" name="XmlRPC" level="project" />
+ <orderEntry type="library" name="OroMatcher" level="project" />
+ </component>
+</module>
+
diff --git a/java/idea-ui/src/com/intellij/codeInsight/daemon/impl/AttachSourcesNotificationProvider.java b/java/idea-ui/src/com/intellij/codeInsight/daemon/impl/AttachSourcesNotificationProvider.java
new file mode 100644
index 0000000..9e9213d
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/codeInsight/daemon/impl/AttachSourcesNotificationProvider.java
@@ -0,0 +1,327 @@
+/*
+ * Copyright 2000-2012 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.codeInsight.daemon.impl;
+
+import com.intellij.ProjectTopics;
+import com.intellij.codeEditor.JavaEditorFileSwapper;
+import com.intellij.codeInsight.AttachSourcesProvider;
+import com.intellij.ide.highlighter.JavaClassFileType;
+import com.intellij.ide.highlighter.JavaFileType;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.application.Result;
+import com.intellij.openapi.application.WriteAction;
+import com.intellij.openapi.extensions.ExtensionPointName;
+import com.intellij.openapi.extensions.Extensions;
+import com.intellij.openapi.fileChooser.FileChooser;
+import com.intellij.openapi.fileChooser.FileChooserDescriptor;
+import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory;
+import com.intellij.openapi.fileEditor.FileEditor;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.project.ProjectBundle;
+import com.intellij.openapi.roots.*;
+import com.intellij.openapi.roots.libraries.Library;
+import com.intellij.openapi.roots.ui.configuration.PathUIUtils;
+import com.intellij.openapi.ui.Messages;
+import com.intellij.openapi.ui.popup.JBPopupFactory;
+import com.intellij.openapi.ui.popup.ListSeparator;
+import com.intellij.openapi.ui.popup.PopupStep;
+import com.intellij.openapi.ui.popup.util.BaseListPopupStep;
+import com.intellij.openapi.util.ActionCallback;
+import com.intellij.openapi.util.Comparing;
+import com.intellij.openapi.util.Key;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.openapi.vfs.VfsUtil;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.psi.PsiFile;
+import com.intellij.psi.PsiManager;
+import com.intellij.ui.EditorNotificationPanel;
+import com.intellij.ui.EditorNotifications;
+import com.intellij.ui.GuiUtils;
+import com.intellij.util.PathUtil;
+import com.intellij.util.containers.HashMap;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import java.util.*;
+
+/**
+ * @author Dmitry Avdeev
+ */
+public class AttachSourcesNotificationProvider extends EditorNotifications.Provider<EditorNotificationPanel> {
+ private static final ExtensionPointName<AttachSourcesProvider> EXTENSION_POINT_NAME
+ = new ExtensionPointName<AttachSourcesProvider>("com.intellij.attachSourcesProvider");
+
+ private static final Key<EditorNotificationPanel> KEY = Key.create("add sources to class");
+
+ private final Project myProject;
+
+ public AttachSourcesNotificationProvider(Project project, final EditorNotifications notifications) {
+ myProject = project;
+ myProject.getMessageBus().connect(project).subscribe(ProjectTopics.PROJECT_ROOTS, new ModuleRootAdapter() {
+ public void rootsChanged(ModuleRootEvent event) {
+ notifications.updateAllNotifications();
+ }
+ });
+ }
+
+ public Key<EditorNotificationPanel> getKey() {
+ return KEY;
+ }
+
+ public EditorNotificationPanel createNotificationPanel(final VirtualFile file, FileEditor fileEditor) {
+ if (file.getFileType() != JavaClassFileType.INSTANCE) return null;
+ final List<LibraryOrderEntry> libraries = findOrderEntriesContainingFile(file);
+ if (libraries == null) return null;
+
+ PsiFile psiFile = PsiManager.getInstance(myProject).findFile(file);
+ final String fqn = JavaEditorFileSwapper.getFQN(psiFile);
+ if (fqn == null) return null;
+
+ if (JavaEditorFileSwapper.findSourceFile(myProject, file) != null) return null;
+
+ final EditorNotificationPanel panel = new EditorNotificationPanel();
+ final VirtualFile sourceFile = findSourceFile(file);
+
+ final AttachSourcesProvider.AttachSourcesAction defaultAction;
+ if (sourceFile != null) {
+ panel.setText(ProjectBundle.message("library.sources.not.attached"));
+ defaultAction = new AttachJarAsSourcesAction(file);
+ }
+ else {
+ panel.setText(ProjectBundle.message("library.sources.not.found"));
+ defaultAction = new ChooseAndAttachSourcesAction(myProject, panel);
+ }
+
+ List<AttachSourcesProvider.AttachSourcesAction> actions = new ArrayList<AttachSourcesProvider.AttachSourcesAction>();
+
+ boolean hasNonLightAction = false;
+
+ for (AttachSourcesProvider each : Extensions.getExtensions(EXTENSION_POINT_NAME)) {
+ for (AttachSourcesProvider.AttachSourcesAction action : each.getActions(libraries, psiFile)) {
+ if (hasNonLightAction) {
+ if (action instanceof AttachSourcesProvider.LightAttachSourcesAction) {
+ continue; // Don't add LightAttachSourcesAction if non light action exists.
+ }
+ }
+ else {
+ if (!(action instanceof AttachSourcesProvider.LightAttachSourcesAction)) {
+ actions.clear(); // All previous actions is LightAttachSourcesAction and should be removed.
+ hasNonLightAction = true;
+ }
+ }
+
+ actions.add(action);
+ }
+ }
+
+ Collections.sort(actions, new Comparator<AttachSourcesProvider.AttachSourcesAction>() {
+ public int compare(AttachSourcesProvider.AttachSourcesAction o1, AttachSourcesProvider.AttachSourcesAction o2) {
+ return o1.getName().compareToIgnoreCase(o2.getName());
+ }
+ });
+
+ actions.add(defaultAction);
+
+ for (final AttachSourcesProvider.AttachSourcesAction each : actions) {
+ panel.createActionLabel(GuiUtils.getTextWithoutMnemonicEscaping(each.getName()), new Runnable() {
+ public void run() {
+ if (!Comparing.equal(libraries, findOrderEntriesContainingFile(file))) {
+ Messages.showErrorDialog(myProject, "Cannot find library for " + StringUtil.getShortName(fqn), "Error");
+ return;
+ }
+
+ panel.setText(each.getBusyText());
+
+ Runnable onFinish = new Runnable() {
+ public void run() {
+ SwingUtilities.invokeLater(new Runnable() {
+ public void run() {
+ panel.setText(ProjectBundle.message("library.sources.not.found"));
+ }
+ });
+ }
+ };
+ ActionCallback callback = each.perform(findOrderEntriesContainingFile(file));
+ callback.doWhenRejected(onFinish);
+ callback.doWhenDone(onFinish);
+ }
+ });
+ }
+
+ return panel;
+ }
+
+ @Nullable
+ private static VirtualFile findSourceFile(VirtualFile classFile) {
+ final VirtualFile parent = classFile.getParent();
+ String name = classFile.getName();
+ int i = name.indexOf('$');
+ if (i != -1) name = name.substring(0, i);
+ i = name.indexOf('.');
+ if (i != -1) name = name.substring(0, i);
+ return parent.findChild(name + JavaFileType.DOT_DEFAULT_EXTENSION);
+ }
+
+ private static void appendSources(final Library library, final VirtualFile[] files) {
+ ApplicationManager.getApplication().runWriteAction(new Runnable() {
+ public void run() {
+ Library.ModifiableModel model = library.getModifiableModel();
+ for (VirtualFile virtualFile : files) {
+ model.addRoot(virtualFile, OrderRootType.SOURCES);
+ }
+ model.commit();
+ }
+ });
+ }
+
+ @Nullable
+ private List<LibraryOrderEntry> findOrderEntriesContainingFile(VirtualFile file) {
+ final List<LibraryOrderEntry> libs = new ArrayList<LibraryOrderEntry>();
+ List<OrderEntry> entries = ProjectRootManager.getInstance(myProject).getFileIndex().getOrderEntriesForFile(file);
+ for (OrderEntry entry : entries) {
+ if (entry instanceof LibraryOrderEntry) {
+ libs.add ((LibraryOrderEntry)entry);
+ }
+ }
+ return libs.isEmpty() ? null : libs;
+ }
+
+ private static class AttachJarAsSourcesAction implements AttachSourcesProvider.AttachSourcesAction {
+ private final VirtualFile myClassFile;
+
+ public AttachJarAsSourcesAction(VirtualFile classFile) {
+ myClassFile = classFile;
+ }
+
+ public String getName() {
+ return ProjectBundle.message("module.libraries.attach.sources.immediately.button");
+ }
+
+ public String getBusyText() {
+ return ProjectBundle.message("library.attach.sources.action.busy.text");
+ }
+
+ @Override
+ public ActionCallback perform(List<LibraryOrderEntry> orderEntriesContainingFile) {
+ final List<Library.ModifiableModel> modelsToCommit = new ArrayList<Library.ModifiableModel>();
+ for (LibraryOrderEntry orderEntry : orderEntriesContainingFile) {
+ final Library library = orderEntry.getLibrary();
+ if (library == null) continue;
+ final VirtualFile root = findRoot(library);
+ if (root == null) continue;
+ final Library.ModifiableModel model = library.getModifiableModel();
+ model.addRoot(root, OrderRootType.SOURCES);
+ modelsToCommit.add(model);
+ }
+ if (modelsToCommit.isEmpty()) return new ActionCallback.Rejected();
+ new WriteAction() {
+ protected void run(final Result result) {
+ for (Library.ModifiableModel model : modelsToCommit) {
+ model.commit();
+ }
+ }
+ }.execute();
+
+ return new ActionCallback.Done();
+ }
+
+ @Nullable
+ private VirtualFile findRoot(Library library) {
+ for (VirtualFile classesRoot : library.getFiles(OrderRootType.CLASSES)) {
+ if (VfsUtil.isAncestor(classesRoot, myClassFile, true)) {
+ return classesRoot;
+ }
+ }
+ return null;
+ }
+ }
+
+ private static class ChooseAndAttachSourcesAction implements AttachSourcesProvider.AttachSourcesAction {
+ private final Project myProject;
+ private final JComponent myParentComponent;
+
+ public ChooseAndAttachSourcesAction(Project project, JComponent parentComponent) {
+ myProject = project;
+ myParentComponent = parentComponent;
+ }
+
+ public String getName() {
+ return ProjectBundle.message("module.libraries.attach.sources.button");
+ }
+
+ public String getBusyText() {
+ return ProjectBundle.message("library.attach.sources.action.busy.text");
+ }
+
+ public ActionCallback perform(final List<LibraryOrderEntry> libraries) {
+ FileChooserDescriptor descriptor = FileChooserDescriptorFactory.createMultipleJavaPathDescriptor();
+ descriptor.setTitle(ProjectBundle.message("library.attach.sources.action"));
+ descriptor.setDescription(ProjectBundle.message("library.attach.sources.description"));
+ final Library firstLibrary = libraries.get(0).getLibrary();
+ VirtualFile[] roots = firstLibrary != null ? firstLibrary.getFiles(OrderRootType.CLASSES) : VirtualFile.EMPTY_ARRAY;
+ VirtualFile[] candidates = FileChooser.chooseFiles(descriptor, myProject, roots.length == 0 ? null : PathUtil.getLocalFile(roots[0]));
+ final VirtualFile[] files = PathUIUtils.scanAndSelectDetectedJavaSourceRoots(myParentComponent, candidates);
+ if (files.length == 0) {
+ return new ActionCallback.Rejected();
+ }
+ final Map<Library, LibraryOrderEntry> librariesToAppendSourcesTo = new HashMap<Library, LibraryOrderEntry>();
+ for (LibraryOrderEntry library : libraries) {
+ librariesToAppendSourcesTo.put(library.getLibrary(), library);
+ }
+ if (librariesToAppendSourcesTo.size() == 1) {
+ appendSources(firstLibrary, files);
+ } else {
+ librariesToAppendSourcesTo.put(null, null);
+ final Collection<LibraryOrderEntry> orderEntries = librariesToAppendSourcesTo.values();
+ JBPopupFactory.getInstance().createListPopup(new BaseListPopupStep<LibraryOrderEntry>("<html><body>Multiple libraries contain file.<br> Choose libraries to attach sources to</body></html>",
+ orderEntries.toArray(new LibraryOrderEntry[orderEntries.size()])){
+ @Override
+ public ListSeparator getSeparatorAbove(LibraryOrderEntry value) {
+ return value == null ? new ListSeparator() : null;
+ }
+
+ @NotNull
+ @Override
+ public String getTextFor(LibraryOrderEntry value) {
+ if (value != null) {
+ return value.getPresentableName() + " (" + value.getOwnerModule().getName() + ")";
+ }
+ else {
+ return "All";
+ }
+ }
+
+ @Override
+ public PopupStep onChosen(LibraryOrderEntry libraryOrderEntry, boolean finalChoice) {
+ if (libraryOrderEntry != null) {
+ appendSources(libraryOrderEntry.getLibrary(), files);
+ } else {
+ for (Library libOrderEntry : librariesToAppendSourcesTo.keySet()) {
+ if (libOrderEntry != null) {
+ appendSources(libOrderEntry, files);
+ }
+ }
+ }
+ return FINAL_CHOICE;
+ }
+ }).showCenteredInCurrentWindow(myProject);
+ }
+
+ return new ActionCallback.Done();
+ }
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/codeInsight/daemon/impl/SetupSDKNotificationProvider.java b/java/idea-ui/src/com/intellij/codeInsight/daemon/impl/SetupSDKNotificationProvider.java
new file mode 100644
index 0000000..73192a6
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/codeInsight/daemon/impl/SetupSDKNotificationProvider.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright 2000-2012 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.codeInsight.daemon.impl;
+
+import com.intellij.ProjectTopics;
+import com.intellij.ide.highlighter.JavaClassFileType;
+import com.intellij.lang.java.JavaLanguage;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.fileEditor.FileEditor;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.module.ModuleUtil;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.project.ProjectBundle;
+import com.intellij.openapi.projectRoots.Sdk;
+import com.intellij.openapi.roots.*;
+import com.intellij.openapi.roots.ui.configuration.ProjectSettingsService;
+import com.intellij.openapi.util.Key;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.psi.PsiFile;
+import com.intellij.psi.PsiManager;
+import com.intellij.ui.EditorNotificationPanel;
+import com.intellij.ui.EditorNotifications;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * @author Danila Ponomarenko
+ */
+public class SetupSDKNotificationProvider extends EditorNotifications.Provider<EditorNotificationPanel> {
+ private static final Key<EditorNotificationPanel> KEY = Key.create("Setup SDK");
+
+ private final Project myProject;
+
+ public SetupSDKNotificationProvider(Project project, final EditorNotifications notifications) {
+ myProject = project;
+ myProject.getMessageBus().connect(project).subscribe(ProjectTopics.PROJECT_ROOTS, new ModuleRootAdapter() {
+ public void rootsChanged(ModuleRootEvent event) {
+ notifications.updateAllNotifications();
+ }
+ });
+ }
+
+ @Override
+ public Key<EditorNotificationPanel> getKey() {
+ return KEY;
+ }
+
+ @Override
+ public EditorNotificationPanel createNotificationPanel(VirtualFile file, FileEditor fileEditor) {
+ if (file.getFileType() == JavaClassFileType.INSTANCE) return null;
+
+ final PsiFile psiFile = PsiManager.getInstance(myProject).findFile(file);
+ if (psiFile == null) {
+ return null;
+ }
+
+ if (psiFile.getLanguage() != JavaLanguage.INSTANCE) {
+ return null;
+ }
+
+ if (ProjectRootManager.getInstance(myProject).getProjectSdk() != null) {
+ return null;
+ }
+
+ return createPanel(myProject, psiFile);
+ }
+
+ @NotNull
+ private static EditorNotificationPanel createPanel(final @NotNull Project project, final @NotNull PsiFile file) {
+ final EditorNotificationPanel panel = new EditorNotificationPanel();
+ panel.setText(ProjectBundle.message("project.sdk.not.defined"));
+ panel.createActionLabel(ProjectBundle.message("project.sdk.setup"), new Runnable() {
+ @Override
+ public void run() {
+ final Sdk projectSdk = ProjectSettingsService.getInstance(project).chooseAndSetSdk();
+ if (projectSdk == null) return;
+ ApplicationManager.getApplication().runWriteAction(new Runnable() {
+ @Override
+ public void run() {
+ final Module module = ModuleUtil.findModuleForPsiElement(file);
+ if (module != null) {
+ ModuleRootModificationUtil.setSdkInherited(module);
+ }
+ }
+ });
+ }
+ });
+ return panel;
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/facet/FacetInfo.java b/java/idea-ui/src/com/intellij/facet/FacetInfo.java
new file mode 100644
index 0000000..582dcf5
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/facet/FacetInfo.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2000-2009 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.facet;
+
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * @author nik
+*/
+//todo[nik] delete
+public final class FacetInfo {
+ public static final FacetInfo[] EMPTY_ARRAY = new FacetInfo[0];
+ private final FacetType myFacetType;
+ private final FacetConfiguration myConfiguration;
+ private final FacetInfo myUnderlyingFacet;
+ private String myName;
+
+ public FacetInfo(final FacetType facetType, String name, final FacetConfiguration configuration, final FacetInfo underlyingFacet) {
+ myName = name;
+ myFacetType = facetType;
+ myConfiguration = configuration;
+ myUnderlyingFacet = underlyingFacet;
+ }
+
+ public FacetType getFacetType() {
+ return myFacetType;
+ }
+
+ public FacetConfiguration getConfiguration() {
+ return myConfiguration;
+ }
+
+ @Nullable
+ public FacetInfo getUnderlyingFacet() {
+ return myUnderlyingFacet;
+ }
+
+ public String getName() {
+ return myName;
+ }
+
+ public void setName(final String name) {
+ myName = name;
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/facet/impl/ProjectFacetsConfigurator.java b/java/idea-ui/src/com/intellij/facet/impl/ProjectFacetsConfigurator.java
new file mode 100644
index 0000000..506fbfe
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/facet/impl/ProjectFacetsConfigurator.java
@@ -0,0 +1,375 @@
+/*
+ * Copyright 2000-2009 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.facet.impl;
+
+import com.intellij.facet.*;
+import com.intellij.facet.impl.ui.FacetEditorImpl;
+import com.intellij.facet.impl.ui.FacetTreeModel;
+import com.intellij.facet.impl.ui.ProjectConfigurableContext;
+import com.intellij.facet.ui.FacetEditorContext;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.options.ConfigurationException;
+import com.intellij.openapi.roots.ui.configuration.FacetsProvider;
+import com.intellij.openapi.roots.ui.configuration.ModuleConfigurationState;
+import com.intellij.openapi.roots.ui.configuration.ModuleEditor;
+import com.intellij.openapi.roots.ui.configuration.ModulesConfigurator;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.LibrariesContainer;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.LibrariesContainerFactory;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.StructureConfigurableContext;
+import com.intellij.openapi.util.Disposer;
+import com.intellij.openapi.util.UserDataHolder;
+import com.intellij.openapi.util.UserDataHolderBase;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.*;
+
+/**
+ * @author nik
+ */
+public class ProjectFacetsConfigurator implements FacetsProvider {
+ private static final Logger LOG = Logger.getInstance("#com.intellij.facet.impl.ProjectFacetsConfigurator");
+ private final Map<Module, ModifiableFacetModel> myModifiableModels = new HashMap<Module, ModifiableFacetModel>();
+ private final Map<Facet, FacetEditorImpl> myEditors = new LinkedHashMap<Facet, FacetEditorImpl>();
+ private final Map<Module, FacetTreeModel> myTreeModels = new HashMap<Module, FacetTreeModel>();
+ private final Map<FacetInfo, Facet> myInfo2Facet = new HashMap<FacetInfo, Facet>();
+ private final Map<Facet, FacetInfo> myFacet2Info = new HashMap<Facet, FacetInfo>();
+ private final Map<Module, UserDataHolder> mySharedModuleData = new HashMap<Module, UserDataHolder>();
+ private final Set<Facet> myFacetsToDispose = new HashSet<Facet>();
+ private final Set<Facet> myChangedFacets = new HashSet<Facet>();
+ private final Set<Facet> myCreatedFacets = new HashSet<Facet>();
+ private final StructureConfigurableContext myContext;
+ private UserDataHolderBase myProjectData = new UserDataHolderBase();
+
+ public ProjectFacetsConfigurator(final StructureConfigurableContext context, ProjectFacetsConfigurator facetsConfigurator) {
+ myContext = context;
+
+ if (facetsConfigurator != null) {
+ initFrom(facetsConfigurator);
+ }
+ }
+
+ private void initFrom(ProjectFacetsConfigurator facetsConfigurator) {
+ myFacet2Info.putAll(facetsConfigurator.myFacet2Info);
+ myInfo2Facet.putAll(facetsConfigurator.myInfo2Facet);
+ myTreeModels.putAll(facetsConfigurator.myTreeModels);
+ myEditors.putAll(facetsConfigurator.myEditors);
+ }
+
+ public List<Facet> removeFacet(Facet facet) {
+ FacetTreeModel treeModel = getTreeModel(facet.getModule());
+ FacetInfo facetInfo = myFacet2Info.get(facet);
+ if (facetInfo == null) return Collections.emptyList();
+
+ final List<Facet> removed = new ArrayList<Facet>();
+ List<FacetInfo> childrenList = treeModel.getChildren(facetInfo);
+ FacetInfo[] children = childrenList.toArray(new FacetInfo[childrenList.size()]);
+ for (FacetInfo child : children) {
+ Facet childInfo = myInfo2Facet.get(child);
+ if (childInfo != null) {
+ removed.addAll(removeFacet(childInfo));
+ }
+ }
+
+ treeModel.removeFacetInfo(facetInfo);
+ getOrCreateModifiableModel(facet.getModule()).removeFacet(facet);
+ myChangedFacets.remove(facet);
+ if (myCreatedFacets.contains(facet)) {
+ Disposer.dispose(facet);
+ }
+ final FacetEditorImpl facetEditor = myEditors.remove(facet);
+ if (facetEditor != null) {
+ facetEditor.disposeUIResources();
+ }
+ myFacet2Info.remove(facet);
+ myInfo2Facet.remove(facetInfo);
+ removed.add(facet);
+ return removed;
+ }
+
+ public Facet createAndAddFacet(Module module, FacetType<?, ?> type, final @Nullable Facet underlying) {
+ final Collection<? extends Facet> facets = getFacetsByType(module, type.getId());
+ String facetName = type.getDefaultFacetName();
+ int i = 2;
+ while (facetExists(facetName, facets)) {
+ facetName = type.getDefaultFacetName() + i;
+ i++;
+ }
+ final Facet facet = FacetManager.getInstance(module).createFacet(type, facetName, underlying);
+ myCreatedFacets.add(facet);
+ addFacetInfo(facet);
+ getOrCreateModifiableModel(module).addFacet(facet);
+ return facet;
+ }
+
+ private boolean facetExists(final String facetName, final Collection<? extends Facet> facets) {
+ for (Facet facet : facets) {
+ if (getFacetName(facet).equals(facetName)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public void addFacetInfo(final Facet facet) {
+ final FacetInfo exiting = myFacet2Info.get(facet);
+ if (exiting != null) {
+ LOG.assertTrue(exiting.getName().equals(facet.getName()));
+ LOG.assertTrue(exiting.getFacetType().equals(facet.getType()));
+ LOG.assertTrue(exiting.getConfiguration().equals(facet.getConfiguration()));
+ return;
+ }
+
+ FacetInfo info = new FacetInfo(facet.getType(), facet.getName(), facet.getConfiguration(), myFacet2Info.get(facet.getUnderlyingFacet()));
+ myFacet2Info.put(facet, info);
+ myInfo2Facet.put(info, facet);
+ getTreeModel(facet.getModule()).addFacetInfo(info);
+ }
+
+ public void addFacetInfos(final Module module) {
+ final Facet[] facets = getFacetModel(module).getSortedFacets();
+ for (Facet facet : facets) {
+ addFacetInfo(facet);
+ }
+ }
+
+ public void clearMaps() {
+ myModifiableModels.clear();
+ myEditors.clear();
+ myTreeModels.clear();
+ myInfo2Facet.clear();
+ myFacet2Info.clear();
+ myChangedFacets.clear();
+ mySharedModuleData.clear();
+ }
+
+ private boolean isNewFacet(Facet facet) {
+ final ModifiableFacetModel model = myModifiableModels.get(facet.getModule());
+ return model != null && model.isNewFacet(facet);
+ }
+
+ @NotNull
+ public ModifiableFacetModel getOrCreateModifiableModel(final Module module) {
+ ModifiableFacetModel model = myModifiableModels.get(module);
+ if (model == null) {
+ model = FacetManager.getInstance(module).createModifiableModel();
+ myModifiableModels.put(module, model);
+ }
+ return model;
+ }
+
+ @Nullable
+ public FacetEditorImpl getEditor(Facet facet) {
+ return myEditors.get(facet);
+ }
+
+ @NotNull
+ public FacetEditorImpl getOrCreateEditor(Facet facet) {
+ FacetEditorImpl editor = myEditors.get(facet);
+ if (editor == null) {
+ final Facet underlyingFacet = facet.getUnderlyingFacet();
+ final FacetEditorContext parentContext = underlyingFacet != null ? getOrCreateEditor(underlyingFacet).getContext() : null;
+
+ final FacetEditorContext context = createContext(facet, parentContext);
+ editor = new FacetEditorImpl(context, facet.getConfiguration());
+ editor.getComponent();
+ editor.reset();
+ myEditors.put(facet, editor);
+ }
+ return editor;
+ }
+
+ protected FacetEditorContext createContext(final @NotNull Facet facet, final @Nullable FacetEditorContext parentContext) {
+ Module module = facet.getModule();
+ ModulesConfigurator modulesConfigurator = myContext.getModulesConfigurator();
+ ModuleEditor moduleEditor = modulesConfigurator.getModuleEditor(module);
+ if (moduleEditor == null) {
+ LOG.error("ModuleEditor[" + module.getName() + "]==null: disposed = " + module.isDisposed() + ", is in model = "
+ + Arrays.asList(modulesConfigurator.getModules()).contains(module));
+ }
+
+ final ModuleConfigurationState state = moduleEditor.createModuleConfigurationState();
+ return new MyProjectConfigurableContext(facet, parentContext, state);
+ }
+
+ private UserDataHolder getSharedModuleData(final Module module) {
+ UserDataHolder dataHolder = mySharedModuleData.get(module);
+ if (dataHolder == null) {
+ dataHolder = new UserDataHolderBase();
+ mySharedModuleData.put(module, dataHolder);
+ }
+ return dataHolder;
+ }
+
+ @NotNull
+ public FacetModel getFacetModel(Module module) {
+ final ModifiableFacetModel model = myModifiableModels.get(module);
+ if (model != null) {
+ return model;
+ }
+ return FacetManager.getInstance(module);
+ }
+
+ public void commitFacets() {
+ for (ModifiableFacetModel model : myModifiableModels.values()) {
+ model.commit();
+ }
+
+ for (Map.Entry<Facet, FacetEditorImpl> entry : myEditors.entrySet()) {
+ entry.getValue().onFacetAdded(entry.getKey());
+ }
+
+ myModifiableModels.clear();
+ for (Facet facet : myChangedFacets) {
+ Module module = facet.getModule();
+ if (!module.isDisposed()) {
+ module.getMessageBus().syncPublisher(FacetManager.FACETS_TOPIC).facetConfigurationChanged(facet);
+ }
+ }
+ myChangedFacets.clear();
+ }
+
+ public void resetEditors() {
+ for (FacetEditorImpl editor : myEditors.values()) {
+ editor.reset();
+ }
+ }
+
+ public void applyEditors() throws ConfigurationException {
+ for (Map.Entry<Facet, FacetEditorImpl> entry : myEditors.entrySet()) {
+ final FacetEditorImpl editor = entry.getValue();
+ if (editor.isModified()) {
+ myChangedFacets.add(entry.getKey());
+ }
+ editor.apply();
+ }
+ }
+
+ public boolean isModified() {
+ for (ModifiableFacetModel model : myModifiableModels.values()) {
+ if (model.isModified()) {
+ return true;
+ }
+ }
+ for (FacetEditorImpl editor : myEditors.values()) {
+ if (editor.isModified()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public FacetTreeModel getTreeModel(Module module) {
+ FacetTreeModel treeModel = myTreeModels.get(module);
+ if (treeModel == null) {
+ treeModel = new FacetTreeModel();
+ myTreeModels.put(module, treeModel);
+ }
+ return treeModel;
+ }
+
+ public FacetInfo getFacetInfo(final Facet facet) {
+ return myFacet2Info.get(facet);
+ }
+
+ public Facet getFacet(final FacetInfo facetInfo) {
+ return myInfo2Facet.get(facetInfo);
+ }
+
+ public void disposeEditors() {
+ for (Facet facet : myFacetsToDispose) {
+ Disposer.dispose(facet);
+ }
+ myFacetsToDispose.clear();
+ myCreatedFacets.clear();
+
+ for (FacetEditorImpl editor : myEditors.values()) {
+ editor.disposeUIResources();
+ }
+ myProjectData = null;
+ }
+
+ @NotNull
+ public Facet[] getAllFacets(final Module module) {
+ return getFacetModel(module).getAllFacets();
+ }
+
+ @NotNull
+ public <F extends Facet> Collection<F> getFacetsByType(final Module module, final FacetTypeId<F> type) {
+ return getFacetModel(module).getFacetsByType(type);
+ }
+
+ @Nullable
+ public <F extends Facet> F findFacet(final Module module, final FacetTypeId<F> type, final String name) {
+ return getFacetModel(module).findFacet(type, name);
+ }
+
+ private UserDataHolder getProjectData() {
+ if (myProjectData == null) {
+ myProjectData = new UserDataHolderBase();
+ }
+ return myProjectData;
+ }
+
+ public String getFacetName(Facet facet) {
+ final ModifiableFacetModel model = myModifiableModels.get(facet.getModule());
+ if (model != null) {
+ final String newName = model.getNewName(facet);
+ if (newName != null) {
+ return newName;
+ }
+ }
+ return facet.getName();
+ }
+
+ public List<Facet> removeAllFacets(final Module module) {
+ List<Facet> facets = new ArrayList<Facet>();
+ FacetModel facetModel = getOrCreateModifiableModel(module);
+ for (Facet facet : facetModel.getAllFacets()) {
+ if (!myCreatedFacets.contains(facet)) {
+ myFacetsToDispose.add(facet);
+ }
+ LOG.assertTrue(facet.getModule().equals(module), module + " expected but " + facet.getModule() + " found");
+ facets.addAll(removeFacet(facet));
+ }
+ mySharedModuleData.remove(module);
+ myModifiableModels.remove(module);
+ return facets;
+ }
+
+ public boolean hasFacetOfType(Module module, @Nullable Facet parent, FacetTypeId<?> typeId) {
+ final FacetTreeModel treeModel = getTreeModel(module);
+ final FacetInfo parentInfo = getFacetInfo(parent);
+ return treeModel.hasFacetOfType(parentInfo, typeId);
+ }
+
+ private class MyProjectConfigurableContext extends ProjectConfigurableContext {
+ private final LibrariesContainer myContainer;
+
+ public MyProjectConfigurableContext(final Facet facet, final FacetEditorContext parentContext, final ModuleConfigurationState state) {
+ super(facet, ProjectFacetsConfigurator.this.isNewFacet(facet), parentContext, state,
+ ProjectFacetsConfigurator.this.getSharedModuleData(facet.getModule()), getProjectData());
+ myContainer = LibrariesContainerFactory.createContainer(myContext);
+ }
+
+ public LibrariesContainer getContainer() {
+ return myContainer;
+ }
+
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/facet/impl/ui/FacetContextChangeListener.java b/java/idea-ui/src/com/intellij/facet/impl/ui/FacetContextChangeListener.java
new file mode 100644
index 0000000..556262f
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/facet/impl/ui/FacetContextChangeListener.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2000-2009 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.facet.impl.ui;
+
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.roots.ModifiableRootModel;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.EventListener;
+
+/**
+ * @author nik
+ */
+public interface FacetContextChangeListener extends EventListener {
+
+ void moduleRootsChanged(ModifiableRootModel rootModel);
+
+ void facetModelChanged(@NotNull Module module);
+
+}
diff --git a/java/idea-ui/src/com/intellij/facet/impl/ui/FacetEditorContextBase.java b/java/idea-ui/src/com/intellij/facet/impl/ui/FacetEditorContextBase.java
new file mode 100644
index 0000000..f3c20e2
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/facet/impl/ui/FacetEditorContextBase.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright 2000-2009 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.facet.impl.ui;
+
+import com.intellij.facet.Facet;
+import com.intellij.facet.impl.DefaultFacetsProvider;
+import com.intellij.facet.ui.FacetEditorContext;
+import com.intellij.openapi.roots.ModuleRootModel;
+import com.intellij.openapi.roots.OrderRootType;
+import com.intellij.openapi.roots.libraries.Library;
+import com.intellij.openapi.roots.libraries.LibraryTablesRegistrar;
+import com.intellij.openapi.roots.ui.configuration.FacetsProvider;
+import com.intellij.openapi.roots.ui.configuration.ModulesProvider;
+import com.intellij.openapi.roots.ui.configuration.artifacts.ArtifactsStructureConfigurableContext;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.LibrariesContainer;
+import com.intellij.openapi.util.Key;
+import com.intellij.openapi.util.UserDataHolder;
+import com.intellij.openapi.util.UserDataHolderBase;
+import com.intellij.openapi.vfs.VirtualFile;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * @author nik
+ */
+public abstract class FacetEditorContextBase extends UserDataHolderBase implements FacetEditorContext {
+ private final FacetsProvider myFacetsProvider;
+ @Nullable private final FacetEditorContext myParentContext;
+ private final ModulesProvider myModulesProvider;
+ private final Facet myFacet;
+ private final UserDataHolder mySharedModuleData;
+ private final UserDataHolder mySharedProjectData;
+
+ public FacetEditorContextBase(@NotNull Facet facet, final @Nullable FacetEditorContext parentContext, final @Nullable FacetsProvider facetsProvider,
+ final @NotNull ModulesProvider modulesProvider,
+ final UserDataHolder sharedModuleData,
+ final UserDataHolder sharedProjectData) {
+ myFacet = facet;
+ mySharedProjectData = sharedProjectData;
+ mySharedModuleData = sharedModuleData;
+ myParentContext = parentContext;
+ myModulesProvider = modulesProvider;
+ myFacetsProvider = facetsProvider != null ? facetsProvider : DefaultFacetsProvider.INSTANCE;
+ }
+
+ public Library[] getLibraries() {
+ return LibraryTablesRegistrar.getInstance().getLibraryTable(getProject()).getLibraries();
+ }
+
+ @NotNull
+ public String getFacetName() {
+ return myFacet.getName();
+ }
+
+ public VirtualFile[] getLibraryFiles(final Library library, final OrderRootType rootType) {
+ return library.getFiles(rootType);
+ }
+
+ @Nullable
+ public Library findLibrary(@NotNull String name) {
+ for (Library library : getLibraries()) {
+ if (name.equals(library.getName())) {
+ return library;
+ }
+ }
+ return null;
+ }
+
+
+ public UserDataHolder getSharedProjectData() {
+ return mySharedProjectData;
+ }
+
+ //todo[nik] pull up to open API?
+ public UserDataHolder getSharedModuleData() {
+ return mySharedModuleData;
+ }
+
+ @NotNull
+ public abstract ArtifactsStructureConfigurableContext getArtifactsStructureContext();
+
+ @Nullable
+ public <T> T getUserData(@NotNull final Key<T> key) {
+ T t = super.getUserData(key);
+ if (t == null && myParentContext != null) {
+ t = myParentContext.getUserData(key);
+ }
+ return t;
+ }
+
+ @NotNull
+ public FacetsProvider getFacetsProvider() {
+ return myFacetsProvider;
+ }
+
+ @NotNull
+ public ModulesProvider getModulesProvider() {
+ return myModulesProvider;
+ }
+
+ @NotNull
+ public ModuleRootModel getRootModel() {
+ return getModifiableRootModel();
+ }
+
+ public abstract LibrariesContainer getContainer();
+
+ @NotNull
+ public Facet getFacet() {
+ return myFacet;
+ }
+
+ @Nullable
+ public Facet getParentFacet() {
+ return myFacet.getUnderlyingFacet();
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/facet/impl/ui/FacetEditorFacade.java b/java/idea-ui/src/com/intellij/facet/impl/ui/FacetEditorFacade.java
new file mode 100644
index 0000000..2d0f9f6
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/facet/impl/ui/FacetEditorFacade.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2000-2009 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.facet.impl.ui;
+
+import com.intellij.facet.Facet;
+import org.jetbrains.annotations.Nullable;
+import com.intellij.facet.FacetType;
+import com.intellij.facet.FacetInfo;
+import com.intellij.facet.FacetTypeId;
+import com.intellij.openapi.module.ModuleType;
+
+import java.util.Collection;
+
+/**
+ * @author nik
+ */
+public interface FacetEditorFacade {
+
+ boolean nodeHasFacetOfType(final @Nullable FacetInfo facet, FacetTypeId typeId);
+
+ @Nullable
+ FacetInfo getSelectedFacetInfo();
+
+ @Nullable
+ ModuleType getSelectedModuleType();
+
+ Facet createFacet(final FacetInfo parent, final FacetType type);
+
+ Collection<FacetInfo> getFacetsByType(FacetType<?,?> type);
+
+ @Nullable
+ FacetInfo getParent(final FacetInfo facet);
+}
diff --git a/java/idea-ui/src/com/intellij/facet/impl/ui/FacetEditorImpl.java b/java/idea-ui/src/com/intellij/facet/impl/ui/FacetEditorImpl.java
new file mode 100644
index 0000000..9edd3af
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/facet/impl/ui/FacetEditorImpl.java
@@ -0,0 +1,177 @@
+/*
+ * Copyright 2000-2009 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.facet.impl.ui;
+
+import com.intellij.facet.Facet;
+import com.intellij.facet.FacetConfiguration;
+import com.intellij.facet.ui.FacetEditor;
+import com.intellij.facet.ui.FacetEditorContext;
+import com.intellij.facet.ui.FacetEditorTab;
+import com.intellij.openapi.Disposable;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.options.Configurable;
+import com.intellij.openapi.options.UnnamedConfigurable;
+import com.intellij.openapi.options.UnnamedConfigurableGroup;
+import com.intellij.openapi.util.Disposer;
+import com.intellij.ui.TabbedPaneWrapper;
+import com.intellij.util.ui.UIUtil;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+import java.awt.*;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * @author nik
+ */
+public class FacetEditorImpl extends UnnamedConfigurableGroup implements UnnamedConfigurable, FacetEditor {
+ private final FacetEditorTab[] myEditorTabs;
+ private final FacetErrorPanel myErrorPanel;
+ private JComponent myComponent;
+ private @Nullable TabbedPaneWrapper myTabbedPane;
+ private final FacetEditorContext myContext;
+ private final Set<FacetEditorTab> myVisitedTabs = new HashSet<FacetEditorTab>();
+ private int mySelectedTabIndex = 0;
+ private final Disposable myDisposable = Disposer.newDisposable();
+
+ public FacetEditorImpl(final FacetEditorContext context, final FacetConfiguration configuration) {
+ myContext = context;
+ myErrorPanel = new FacetErrorPanel();
+ myEditorTabs = configuration.createEditorTabs(context, myErrorPanel.getValidatorsManager());
+ for (Configurable configurable : myEditorTabs) {
+ add(configurable);
+ }
+ }
+
+ public void reset() {
+ super.reset();
+ myErrorPanel.getValidatorsManager().validate();
+ }
+
+ public JComponent getComponent() {
+ if (myComponent == null) {
+ myComponent = createComponent();
+ }
+ return myComponent;
+ }
+
+ public JComponent createComponent() {
+ final JComponent editorComponent;
+ if (myEditorTabs.length > 1) {
+ final TabbedPaneWrapper tabbedPane = new TabbedPaneWrapper(myDisposable);
+ for (FacetEditorTab editorTab : myEditorTabs) {
+ JComponent c = editorTab.createComponent();
+ if (c != null) {
+ UIUtil.addInsets(c, UIUtil.PANEL_SMALL_INSETS);
+ }
+ tabbedPane.addTab(editorTab.getDisplayName(), c);
+ }
+ tabbedPane.addChangeListener(new ChangeListener() {
+ public void stateChanged(ChangeEvent e) {
+ myEditorTabs[mySelectedTabIndex].onTabLeaving();
+ mySelectedTabIndex = tabbedPane.getSelectedIndex();
+ onTabSelected(myEditorTabs[mySelectedTabIndex]);
+ }
+ });
+ editorComponent = tabbedPane.getComponent();
+ myTabbedPane = tabbedPane;
+ }
+ else if (myEditorTabs.length == 1) {
+ editorComponent = myEditorTabs[0].createComponent();
+ }
+ else {
+ editorComponent = new JPanel();
+ }
+ final JPanel panel = new JPanel(new BorderLayout());
+ panel.add(BorderLayout.CENTER, editorComponent);
+ panel.add(BorderLayout.SOUTH, myErrorPanel.getComponent());
+
+ return panel;
+ }
+
+ private void onTabSelected(final FacetEditorTab selectedTab) {
+ selectedTab.onTabEntering();
+ if (myVisitedTabs.add(selectedTab)) {
+ final JComponent preferredFocusedComponent = selectedTab.getPreferredFocusedComponent();
+ if (preferredFocusedComponent != null) {
+ ApplicationManager.getApplication().invokeLater(new Runnable() {
+ public void run() {
+ if (preferredFocusedComponent.isShowing()) {
+ preferredFocusedComponent.requestFocus();
+ }
+ }
+ });
+ }
+ }
+ }
+
+ public void disposeUIResources() {
+ Disposer.dispose(myDisposable);
+ myErrorPanel.disposeUIResources();
+ super.disposeUIResources();
+ }
+
+ @Nullable
+ public String getHelpTopic() {
+ return 0 <= mySelectedTabIndex && mySelectedTabIndex < myEditorTabs.length ? myEditorTabs[mySelectedTabIndex].getHelpTopic() : null;
+ }
+
+ public void onFacetAdded(@NotNull Facet facet) {
+ for (FacetEditorTab editorTab : myEditorTabs) {
+ editorTab.onFacetInitialized(facet);
+ }
+ }
+
+ public void setSelectedTabName(final String tabName) {
+ getComponent();
+ final TabbedPaneWrapper tabbedPane = myTabbedPane;
+ if (tabbedPane == null) return;
+ for (int i = 0; i < tabbedPane.getTabCount(); i++) {
+ if (tabName.equals(tabbedPane.getTitleAt(i))) {
+ tabbedPane.setSelectedIndex(i);
+ return;
+ }
+ }
+ }
+
+ public FacetEditorContext getContext() {
+ return myContext;
+ }
+
+ public void onFacetSelected() {
+ if (mySelectedTabIndex < myEditorTabs.length) {
+ onTabSelected(myEditorTabs[mySelectedTabIndex]);
+ }
+ }
+
+ public FacetEditorTab[] getEditorTabs() {
+ return myEditorTabs;
+ }
+
+ public <T extends FacetEditorTab> T getEditorTab(@NotNull final Class<T> aClass) {
+ for (FacetEditorTab editorTab : myEditorTabs) {
+ if (aClass.isInstance(editorTab)) {
+ return aClass.cast(editorTab);
+ }
+ }
+ return null;
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/facet/impl/ui/FacetEditorsFactoryImpl.java b/java/idea-ui/src/com/intellij/facet/impl/ui/FacetEditorsFactoryImpl.java
new file mode 100644
index 0000000..59b530e
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/facet/impl/ui/FacetEditorsFactoryImpl.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2000-2009 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.facet.impl.ui;
+
+import com.intellij.facet.impl.ui.libraries.*;
+import com.intellij.facet.ui.FacetEditorContext;
+import com.intellij.facet.ui.FacetEditorsFactory;
+import com.intellij.facet.ui.FacetValidatorsManager;
+import com.intellij.facet.ui.MultipleFacetEditorHelper;
+import com.intellij.facet.ui.libraries.*;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.roots.ui.configuration.libraries.CustomLibraryDescription;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * @author nik
+ */
+public class FacetEditorsFactoryImpl extends FacetEditorsFactory {
+ public static FacetEditorsFactoryImpl getInstanceImpl() {
+ return (FacetEditorsFactoryImpl)getInstance();
+ }
+
+ public FrameworkLibraryValidator createLibraryValidator(@NotNull CustomLibraryDescription libraryDescription,
+ @NotNull FacetEditorContext context,
+ @NotNull FacetValidatorsManager validatorsManager,
+ @NotNull String libraryCategory) {
+ return createLibraryValidator(libraryDescription, new DelegatingLibrariesValidatorContext(context), validatorsManager, libraryCategory);
+ }
+ public FrameworkLibraryValidator createLibraryValidator(@NotNull CustomLibraryDescription libraryDescription,
+ @NotNull LibrariesValidatorContext context,
+ @NotNull FacetValidatorsManager validatorsManager,
+ @NotNull String libraryCategory) {
+ return new FrameworkLibraryValidatorImpl(libraryDescription, context, validatorsManager, libraryCategory);
+ }
+
+ public FacetLibrariesValidator createLibrariesValidator(@NotNull final LibraryInfo[] libraries, final FacetLibrariesValidatorDescription description,
+ final FacetEditorContext context,
+ final FacetValidatorsManager validatorsManager) {
+ return new FacetLibrariesValidatorImpl(libraries, description, new DelegatingLibrariesValidatorContext(context), validatorsManager);
+ }
+
+ public FacetLibrariesValidator createLibrariesValidator(@NotNull final LibraryInfo[] libraries, @NotNull final Module module, @NotNull final String libraryName) {
+ return new FacetLibrariesValidatorImpl(libraries, new FacetLibrariesValidatorDescription(libraryName), new LibrariesValidatorContextImpl(module), null);
+ }
+
+ public LibrariesValidationComponent createLibrariesValidationComponent(LibraryInfo[] libraryInfos, Module module,
+ String defaultLibraryName) {
+ return new LibrariesValidationComponentImpl(libraryInfos, module, defaultLibraryName);
+ }
+
+ public MultipleFacetEditorHelper createMultipleFacetEditorHelper() {
+ return new MultipleFacetEditorHelperImpl();
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/facet/impl/ui/FacetEditorsStateManager.java b/java/idea-ui/src/com/intellij/facet/impl/ui/FacetEditorsStateManager.java
new file mode 100644
index 0000000..a619347
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/facet/impl/ui/FacetEditorsStateManager.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2000-2009 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.facet.impl.ui;
+
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.components.ServiceManager;
+import com.intellij.facet.FacetType;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * @author nik
+ */
+public abstract class FacetEditorsStateManager {
+
+ public static FacetEditorsStateManager getInstance(@NotNull Project project) {
+ return ServiceManager.getService(project, FacetEditorsStateManager.class);
+ }
+
+ public abstract <T> void saveState(@NotNull FacetType<?, ?> type, @Nullable T state);
+
+ @Nullable
+ public abstract <T> T getState(@NotNull FacetType<?, ?> type, @NotNull Class<T> aClass);
+}
diff --git a/java/idea-ui/src/com/intellij/facet/impl/ui/FacetEditorsStateManagerImpl.java b/java/idea-ui/src/com/intellij/facet/impl/ui/FacetEditorsStateManagerImpl.java
new file mode 100644
index 0000000..091c2df
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/facet/impl/ui/FacetEditorsStateManagerImpl.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright 2000-2012 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.facet.impl.ui;
+
+import com.intellij.facet.FacetType;
+import com.intellij.openapi.components.PersistentStateComponent;
+import com.intellij.openapi.components.State;
+import com.intellij.openapi.components.Storage;
+import com.intellij.openapi.components.StoragePathMacros;
+import com.intellij.util.xmlb.XmlSerializer;
+import com.intellij.util.xmlb.annotations.MapAnnotation;
+import com.intellij.util.xmlb.annotations.Property;
+import com.intellij.util.xmlb.annotations.Tag;
+import org.jdom.Element;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author nik
+ */
+@State(
+ name = "FacetEditorsStateManager",
+ storages = {
+ @Storage(
+ file = StoragePathMacros.WORKSPACE_FILE
+ )
+ }
+)
+public class FacetEditorsStateManagerImpl extends FacetEditorsStateManager implements PersistentStateComponent<FacetEditorsStateManagerImpl.FacetEditorsStateBean>{
+ private final Map<String, Object> myFacetTypeStates = new HashMap<String, Object>();
+ private FacetEditorsStateBean myBean = new FacetEditorsStateBean();
+
+ public <T> void saveState(@NotNull final FacetType<?, ?> type, final T state) {
+ String id = type.getStringId();
+ if (state != null) {
+ myFacetTypeStates.put(id, state);
+ }
+ else {
+ myFacetTypeStates.remove(id);
+ myBean.getFacetTypeElements().remove(id);
+ }
+ }
+
+ @Nullable
+ public <T> T getState(@NotNull final FacetType<?, ?> type, @NotNull final Class<T> aClass) {
+ String id = type.getStringId();
+ //noinspection unchecked
+ T state = (T)myFacetTypeStates.get(id);
+ if (state == null) {
+ FacetTypeStateBean bean = myBean.getFacetTypeElements().get(id);
+ if (bean != null) {
+ Element element = bean.getState();
+ if (element != null) {
+ state = XmlSerializer.deserialize(element, aClass);
+ }
+ }
+ }
+ return state;
+ }
+
+ public FacetEditorsStateBean getState() {
+ for (Map.Entry<String, Object> entry : myFacetTypeStates.entrySet()) {
+ FacetTypeStateBean bean = new FacetTypeStateBean();
+ bean.setState(XmlSerializer.serialize(entry.getValue()));
+ myBean.getFacetTypeElements().put(entry.getKey(), bean);
+ }
+ return myBean;
+ }
+
+ public void loadState(final FacetEditorsStateBean state) {
+ myBean = state;
+ }
+
+ public static class FacetEditorsStateBean {
+ private Map<String, FacetTypeStateBean> myFacetTypeElements = new HashMap<String, FacetTypeStateBean>();
+
+ @Property(surroundWithTag = false)
+ @MapAnnotation(surroundKeyWithTag = false, surroundWithTag = false, surroundValueWithTag = false, entryTagName = "facet-type-state", keyAttributeName = "type")
+ public Map<String, FacetTypeStateBean> getFacetTypeElements() {
+ return myFacetTypeElements;
+ }
+
+ public void setFacetTypeElements(final Map<String, FacetTypeStateBean> elements) {
+ myFacetTypeElements = elements;
+ }
+ }
+
+ @Tag("facet-type")
+ public static class FacetTypeStateBean {
+ private Element myState;
+
+ @Tag("state")
+ public Element getState() {
+ return myState;
+ }
+
+ public void setState(final Element state) {
+ myState = state;
+ }
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/facet/impl/ui/FacetTreeModel.java b/java/idea-ui/src/com/intellij/facet/impl/ui/FacetTreeModel.java
new file mode 100644
index 0000000..8271a54
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/facet/impl/ui/FacetTreeModel.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright 2000-2009 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.facet.impl.ui;
+
+import com.intellij.facet.FacetTypeId;
+import com.intellij.facet.FacetInfo;
+import com.intellij.facet.FacetType;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.util.containers.BidirectionalMap;
+import org.jetbrains.annotations.Nullable;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Collection;
+
+/**
+ * @author nik
+ */
+public class FacetTreeModel {
+ private static final Logger LOG = Logger.getInstance("#com.intellij.facet.impl.ui.FacetTreeModel");
+ private static final FacetInfo ROOT = new FacetInfo(null, "", null, null);
+ private final List<FacetInfo> myFacetInfos = new ArrayList<FacetInfo>();
+ private final BidirectionalMap<FacetInfo, FacetInfo> myParents = new BidirectionalMap<FacetInfo, FacetInfo>();
+
+ public void addFacetInfo(FacetInfo info) {
+ myFacetInfos.add(info);
+ myParents.put(info, null2Root(info.getUnderlyingFacet()));
+ }
+
+ private static @NotNull FacetInfo null2Root(@Nullable FacetInfo info) {
+ return info == null ? ROOT : info;
+ }
+
+ private static @Nullable FacetInfo root2Null(@NotNull FacetInfo info) {
+ return info == ROOT ? null : info;
+ }
+
+ public FacetInfo[] getFacetInfos() {
+ return myFacetInfos.toArray(new FacetInfo[myFacetInfos.size()]);
+ }
+
+ public void removeFacetInfo(@NotNull FacetInfo info) {
+ final boolean removed = myFacetInfos.remove(info);
+ LOG.assertTrue(removed);
+ myParents.remove(info);
+ }
+
+ @Nullable
+ public FacetInfo getParent(@NotNull FacetInfo info) {
+ return root2Null(myParents.get(info));
+ }
+
+ @NotNull
+ public List<FacetInfo> getChildren(@Nullable FacetInfo info) {
+ final List<FacetInfo> list = myParents.getKeysByValue(null2Root(info));
+ if (list == null) {
+ return Collections.emptyList();
+ }
+ return Collections.unmodifiableList(list);
+ }
+
+ public List<FacetInfo> getTopLevelFacets() {
+ return getChildren(null);
+ }
+
+ @Nullable
+ public FacetInfo findNearestFacet(@NotNull FacetInfo info) {
+ final FacetInfo parent = getParent(info);
+ final List<FacetInfo> children = getChildren(parent);
+ int index = children.indexOf(info);
+ if (index < children.size() - 1) {
+ return children.get(index + 1);
+ }
+ if (index > 0) {
+ return children.get(index - 1);
+ }
+ return parent;
+ }
+
+ public boolean hasFacetOfType(final @Nullable FacetInfo parent, final FacetTypeId typeId) {
+ final List<FacetInfo> list = getChildren(parent);
+ for (FacetInfo info : list) {
+ if (info.getFacetType().getId() == typeId) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public Collection<FacetInfo> getFacetInfos(final FacetType<?, ?> type) {
+ final FacetInfo[] facetInfos = getFacetInfos();
+ List<FacetInfo> infos = new ArrayList<FacetInfo>();
+ for (FacetInfo facetInfo : facetInfos) {
+ if (facetInfo.getFacetType().equals(type)) {
+ infos.add(facetInfo);
+ }
+ }
+ return infos;
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/facet/impl/ui/ProjectConfigurableContext.java b/java/idea-ui/src/com/intellij/facet/impl/ui/ProjectConfigurableContext.java
new file mode 100644
index 0000000..8f805c1
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/facet/impl/ui/ProjectConfigurableContext.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2000-2009 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.facet.impl.ui;
+
+import com.intellij.facet.Facet;
+import com.intellij.facet.ui.FacetEditorContext;
+import com.intellij.ide.util.projectWizard.ModuleBuilder;
+import com.intellij.ide.util.projectWizard.WizardContext;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.roots.ModifiableRootModel;
+import com.intellij.openapi.roots.ModuleRootModel;
+import com.intellij.openapi.roots.OrderRootType;
+import com.intellij.openapi.roots.libraries.Library;
+import com.intellij.openapi.roots.ui.configuration.ModuleConfigurationState;
+import com.intellij.openapi.roots.ui.configuration.ProjectStructureConfigurable;
+import com.intellij.openapi.roots.ui.configuration.artifacts.ArtifactsStructureConfigurableContext;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.LibrariesContainer;
+import com.intellij.openapi.util.UserDataHolder;
+import com.intellij.openapi.vfs.VirtualFile;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * @author nik
+ */
+public abstract class ProjectConfigurableContext extends FacetEditorContextBase {
+ private final Module myModule;
+ private final boolean myNewFacet;
+ private final ModuleConfigurationState myModuleConfigurationState;
+
+ public ProjectConfigurableContext(final @NotNull Facet facet, final boolean isNewFacet,
+ @Nullable FacetEditorContext parentContext,
+ final ModuleConfigurationState state, final UserDataHolder sharedModuleData,
+ final UserDataHolder sharedProjectData) {
+ super(facet, parentContext, state.getFacetsProvider(), state.getModulesProvider(), sharedModuleData, sharedProjectData);
+ myModuleConfigurationState = state;
+ myNewFacet = isNewFacet;
+ myModule = facet.getModule();
+ }
+
+ @Nullable
+ public ModuleBuilder getModuleBuilder() {
+ return null;
+ }
+
+ public boolean isNewFacet() {
+ return myNewFacet;
+ }
+
+ @NotNull
+ public Project getProject() {
+ return myModule.getProject();
+ }
+
+ @NotNull
+ public Module getModule() {
+ return myModule;
+ }
+
+ @NotNull
+ @Override
+ public ModuleRootModel getRootModel() {
+ return myModuleConfigurationState.getModulesProvider().getRootModel(myModule);
+ }
+
+ @NotNull
+ public ModifiableRootModel getModifiableRootModel() {
+ return myModuleConfigurationState.getRootModel();
+ }
+
+ @Nullable
+ public WizardContext getWizardContext() {
+ return null;
+ }
+
+ public Library createProjectLibrary(final String baseName, final VirtualFile[] roots, final VirtualFile[] sources) {
+ return getContainer().createLibrary(baseName, LibrariesContainer.LibraryLevel.PROJECT, roots, sources);
+ }
+
+ public VirtualFile[] getLibraryFiles(Library library, OrderRootType rootType) {
+ return getContainer().getLibraryFiles(library, rootType);
+ }
+
+ @NotNull
+ @Override
+ public ArtifactsStructureConfigurableContext getArtifactsStructureContext() {
+ return ProjectStructureConfigurable.getInstance(getProject()).getArtifactsStructureConfigurable().getArtifactsStructureContext();
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/facet/impl/ui/actions/AddFacetToModuleAction.java b/java/idea-ui/src/com/intellij/facet/impl/ui/actions/AddFacetToModuleAction.java
new file mode 100644
index 0000000..6e82be2
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/facet/impl/ui/actions/AddFacetToModuleAction.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright 2000-2009 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.facet.impl.ui.actions;
+
+import com.intellij.facet.*;
+import com.intellij.facet.impl.ui.FacetEditorFacade;
+import com.intellij.framework.FrameworkTypeEx;
+import com.intellij.framework.addSupport.impl.AddFrameworkSupportInProjectStructureAction;
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.module.ModuleType;
+import com.intellij.openapi.project.DumbAware;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.roots.ui.configuration.ProjectStructureConfigurable;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.ModuleStructureConfigurable;
+
+import java.util.Collection;
+import java.util.SortedMap;
+import java.util.TreeMap;
+
+/**
+ * @author nik
+*/
+public class AddFacetToModuleAction extends AnAction implements DumbAware {
+ private static final Logger LOG = Logger.getInstance("#com.intellij.facet.impl.ui.actions.AddFacetToModuleAction");
+ private final FacetEditorFacade myEditor;
+ private final Project myProject;
+ private final FacetType myType;
+
+ private AddFacetToModuleAction(final FacetEditorFacade editor, Project project, final FacetType type) {
+ super(type.getPresentableName(), null, type.getIcon());
+ myEditor = editor;
+ myProject = project;
+ myType = type;
+ }
+
+ public void actionPerformed(AnActionEvent e) {
+ FacetInfo parent = myEditor.getSelectedFacetInfo();
+ final FacetTypeId<?> underlyingFacetType = myType.getUnderlyingFacetType();
+ Facet facet;
+ if (parent == null && underlyingFacetType == null || parent != null && parent.getFacetType().getId() == underlyingFacetType) {
+ facet = myEditor.createFacet(parent, myType);
+ }
+ else {
+ LOG.assertTrue(parent != null);
+ final FacetInfo grandParent = myEditor.getParent(parent);
+ LOG.assertTrue(grandParent == null && underlyingFacetType == null ||
+ grandParent != null && grandParent.getFacetType().getId() == underlyingFacetType);
+ facet = myEditor.createFacet(grandParent, myType);
+ }
+ ProjectStructureConfigurable.getInstance(myProject).select(facet, true);
+ }
+
+ public void update(AnActionEvent e) {
+ e.getPresentation().setVisible(isVisible(myEditor, myType));
+ }
+
+ public static boolean isVisible(FacetEditorFacade editor, final FacetType<?, ?> type) {
+ final ModuleType moduleType = editor.getSelectedModuleType();
+ if (moduleType == null || !type.isSuitableModuleType(moduleType)) {
+ return false;
+ }
+
+ final FacetTypeId<?> underlyingTypeId = type.getUnderlyingFacetType();
+ final FacetInfo selectedFacet = editor.getSelectedFacetInfo();
+ if (selectedFacet == null) {
+ return underlyingTypeId == null && canAddFacet(null, type, editor);
+ }
+
+ final FacetTypeId selectedFacetType = selectedFacet.getFacetType().getId();
+ if (selectedFacetType == underlyingTypeId) {
+ return canAddFacet(selectedFacet, type, editor);
+ }
+
+ final FacetInfo parent = editor.getParent(selectedFacet);
+ if (!canAddFacet(parent, type, editor)) {
+ return false;
+ }
+ return parent == null && underlyingTypeId == null || parent != null && parent.getFacetType().getId() == underlyingTypeId;
+ }
+
+ private static boolean canAddFacet(final FacetInfo selectedFacet, final FacetType<?, ?> type, final FacetEditorFacade editor) {
+ return !(type.isOnlyOneFacetAllowed() && editor.nodeHasFacetOfType(selectedFacet, type.getId()));
+ }
+
+ public static Collection<AnAction> createAddFrameworkActions(FacetEditorFacade editor, Project project) {
+ SortedMap<String, AnAction> actions = new TreeMap<String, AnAction>();
+ for (FacetType type : FacetTypeRegistry.getInstance().getFacetTypes()) {
+ actions.put(type.getPresentableName(), new AddFacetToModuleAction(editor, project, type));
+ }
+ for (FrameworkTypeEx frameworkType : FrameworkTypeEx.EP_NAME.getExtensions()) {
+ final AnAction action = new AddFrameworkSupportInProjectStructureAction(frameworkType, frameworkType.createProvider(),
+ ModuleStructureConfigurable.getInstance(project));
+ actions.put(frameworkType.getPresentableName(), action);
+ }
+ return actions.values();
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/facet/impl/ui/actions/GroupToolbarAction.java b/java/idea-ui/src/com/intellij/facet/impl/ui/actions/GroupToolbarAction.java
new file mode 100644
index 0000000..869cafe
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/facet/impl/ui/actions/GroupToolbarAction.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2000-2009 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.facet.impl.ui.actions;
+
+import com.intellij.openapi.actionSystem.*;
+import com.intellij.openapi.ui.popup.JBPopupFactory;
+import com.intellij.openapi.ui.popup.ListPopupStep;
+
+import javax.swing.*;
+
+/**
+ * @author nik
+ */
+public class GroupToolbarAction extends AnAction {
+ private final ActionGroup myGroup;
+ private final JComponent myToolbarComponent;
+
+ public GroupToolbarAction(final ActionGroup group, JComponent toolbarComponent) {
+ super(group.getTemplatePresentation().getText(), group.getTemplatePresentation().getDescription(),
+ group.getTemplatePresentation().getIcon());
+ myGroup = group;
+ myToolbarComponent = toolbarComponent;
+ }
+
+ public void actionPerformed(AnActionEvent e) {
+ final JBPopupFactory popupFactory = JBPopupFactory.getInstance();
+ final ListPopupStep popupStep = popupFactory.createActionsStep(myGroup, e.getDataContext(), false, false,
+ myGroup.getTemplatePresentation().getText(), myToolbarComponent, false,
+ 0, false);
+ popupFactory.createListPopup(popupStep).showUnderneathOf(myToolbarComponent);
+ }
+
+ public void update(AnActionEvent e) {
+ myGroup.update(e);
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/facet/impl/ui/facetType/DefaultFacetSettingsConfigurable.java b/java/idea-ui/src/com/intellij/facet/impl/ui/facetType/DefaultFacetSettingsConfigurable.java
new file mode 100644
index 0000000..f9dbe68
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/facet/impl/ui/facetType/DefaultFacetSettingsConfigurable.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2000-2009 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.facet.impl.ui.facetType;
+
+import com.intellij.facet.FacetConfiguration;
+import com.intellij.facet.FacetType;
+import com.intellij.facet.ProjectFacetManager;
+import com.intellij.facet.ui.DefaultFacetSettingsEditor;
+import com.intellij.openapi.options.Configurable;
+import com.intellij.openapi.options.ConfigurationException;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.project.ProjectBundle;
+import org.jetbrains.annotations.NotNull;
+
+import javax.swing.*;
+
+/**
+ * @author nik
+ */
+public class DefaultFacetSettingsConfigurable<C extends FacetConfiguration> implements Configurable {
+ private final FacetType<?, C> myFacetType;
+ private final Project myProject;
+ private final DefaultFacetSettingsEditor myDelegate;
+ private final C myConfiguration;
+
+ public DefaultFacetSettingsConfigurable(final @NotNull FacetType<?, C> facetType, final @NotNull Project project, final @NotNull DefaultFacetSettingsEditor delegate,
+ @NotNull C configuration) {
+ myFacetType = facetType;
+ myProject = project;
+ myDelegate = delegate;
+ myConfiguration = configuration;
+ }
+
+ public String getDisplayName() {
+ return ProjectBundle.message("facet.defaults.display.name");
+ }
+
+ public String getHelpTopic() {
+ return myDelegate.getHelpTopic();
+ }
+
+ public JComponent createComponent() {
+ return myDelegate.createComponent();
+ }
+
+ public boolean isModified() {
+ return myDelegate.isModified();
+ }
+
+ public void apply() throws ConfigurationException {
+ if (myDelegate.isModified()) {
+ myDelegate.apply();
+ ProjectFacetManager.getInstance(myProject).setDefaultConfiguration(myFacetType, myConfiguration);
+ }
+ }
+
+ public void reset() {
+ myDelegate.reset();
+ }
+
+ public void disposeUIResources() {
+ myDelegate.disposeUIResources();
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/facet/impl/ui/facetType/FacetTypeEditor.java b/java/idea-ui/src/com/intellij/facet/impl/ui/facetType/FacetTypeEditor.java
new file mode 100644
index 0000000..fcae653
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/facet/impl/ui/facetType/FacetTypeEditor.java
@@ -0,0 +1,171 @@
+/*
+ * Copyright 2000-2009 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.facet.impl.ui.facetType;
+
+import com.intellij.facet.Facet;
+import com.intellij.facet.FacetConfiguration;
+import com.intellij.facet.FacetType;
+import com.intellij.facet.ProjectFacetManager;
+import com.intellij.facet.impl.ProjectFacetsConfigurator;
+import com.intellij.facet.impl.invalid.InvalidFacetType;
+import com.intellij.facet.ui.DefaultFacetSettingsEditor;
+import com.intellij.facet.ui.FacetEditor;
+import com.intellij.facet.ui.MultipleFacetSettingsEditor;
+import com.intellij.openapi.Disposable;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.options.Configurable;
+import com.intellij.openapi.options.ConfigurationException;
+import com.intellij.openapi.options.UnnamedConfigurableGroup;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.project.ProjectBundle;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.StructureConfigurableContext;
+import com.intellij.openapi.util.Disposer;
+import com.intellij.ui.TabbedPaneWrapper;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author nik
+ */
+public class FacetTypeEditor extends UnnamedConfigurableGroup {
+ private final Project myProject;
+ private final StructureConfigurableContext myContext;
+ private final FacetType<?, ?> myFacetType;
+ private Configurable myDefaultSettingsConfigurable;
+ private MultipleFacetSettingsEditor myAllFacetsEditor;
+ private List<Configurable> myCurrentConfigurables;
+ private TabbedPaneWrapper myTabbedPane;
+ private final Disposable myDisposable = Disposer.newDisposable();
+
+ public <C extends FacetConfiguration> FacetTypeEditor(@NotNull Project project, final StructureConfigurableContext context, @NotNull FacetType<?, C> facetType) {
+ myProject = project;
+ myContext = context;
+ myFacetType = facetType;
+
+ if (!(facetType instanceof InvalidFacetType)) {
+ C configuration = ProjectFacetManager.getInstance(project).createDefaultConfiguration(facetType);
+ DefaultFacetSettingsEditor defaultSettingsEditor = facetType.createDefaultConfigurationEditor(project, configuration);
+ if (defaultSettingsEditor != null) {
+ myDefaultSettingsConfigurable = new DefaultFacetSettingsConfigurable<C>(facetType, project, defaultSettingsEditor, configuration);
+ add(myDefaultSettingsConfigurable);
+ }
+ }
+ }
+
+ public boolean isVisible() {
+ return myDefaultSettingsConfigurable != null || myAllFacetsEditor != null;
+ }
+
+ @Nullable
+ private MultipleFacetSettingsEditor createAllFacetsEditor() {
+ ProjectFacetsConfigurator facetsConfigurator = myContext.myModulesConfigurator.getFacetsConfigurator();
+ List<Facet> facets = new ArrayList<Facet>();
+ for (Module module : myContext.getModules()) {
+ facets.addAll(facetsConfigurator.getFacetsByType(module, myFacetType.getId()));
+ }
+ if (!facets.isEmpty()) {
+ final FacetEditor[] editors = new FacetEditor[facets.size()];
+ for (int i = 0; i < facets.size(); i++) {
+ editors[i] = facetsConfigurator.getOrCreateEditor(facets.get(i));
+ }
+ return myFacetType.createMultipleConfigurationsEditor(myProject, editors);
+ }
+ return null;
+ }
+
+ @Override
+ public void disposeUIResources() {
+ Disposer.dispose(myDisposable);
+ super.disposeUIResources();
+ }
+
+ public JComponent createComponent() {
+ MultipleFacetSettingsEditor allFacetsEditor = createAllFacetsEditor();
+ if (myAllFacetsEditor != null) {
+ myAllFacetsEditor.disposeUIResources();
+ }
+ myAllFacetsEditor = allFacetsEditor;
+
+ myCurrentConfigurables = new ArrayList<Configurable>();
+ if (myDefaultSettingsConfigurable != null) {
+ myCurrentConfigurables.add(myDefaultSettingsConfigurable);
+ }
+
+ if (myAllFacetsEditor != null) {
+ myCurrentConfigurables.add(new AllFacetsConfigurable(myAllFacetsEditor));
+ }
+
+ if (myCurrentConfigurables.isEmpty()) {
+ return new JPanel();
+ }
+ if (myCurrentConfigurables.size() == 1) {
+ return myCurrentConfigurables.get(0).createComponent();
+ }
+
+ myTabbedPane = new TabbedPaneWrapper(myDisposable);
+ for (Configurable configurable : myCurrentConfigurables) {
+ myTabbedPane.addTab(configurable.getDisplayName(), configurable.createComponent());
+ }
+ return myTabbedPane.getComponent();
+ }
+
+ @Nullable
+ public String getHelpTopic() {
+ int selectedTab = myTabbedPane != null ? myTabbedPane.getSelectedIndex() : 0;
+ if (myCurrentConfigurables != null && 0 <= selectedTab && selectedTab < myCurrentConfigurables.size()) {
+ return myCurrentConfigurables.get(selectedTab).getHelpTopic();
+ }
+ return null;
+ }
+
+ private static class AllFacetsConfigurable implements Configurable {
+ private final MultipleFacetSettingsEditor myEditor;
+
+ public AllFacetsConfigurable(final MultipleFacetSettingsEditor editor) {
+ myEditor = editor;
+ }
+
+ public String getDisplayName() {
+ return ProjectBundle.message("tab.name.all.facets");
+ }
+
+ public String getHelpTopic() {
+ return myEditor.getHelpTopic();
+ }
+
+ public JComponent createComponent() {
+ return myEditor.createComponent();
+ }
+
+ public boolean isModified() {
+ return false;
+ }
+
+ public void apply() throws ConfigurationException {
+ }
+
+ public void reset() {
+ }
+
+ public void disposeUIResources() {
+ myEditor.disposeUIResources();
+ }
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/facet/impl/ui/libraries/DelegatingLibrariesValidatorContext.java b/java/idea-ui/src/com/intellij/facet/impl/ui/libraries/DelegatingLibrariesValidatorContext.java
new file mode 100644
index 0000000..5ef111d
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/facet/impl/ui/libraries/DelegatingLibrariesValidatorContext.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2000-2009 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.facet.impl.ui.libraries;
+
+import com.intellij.facet.impl.ui.FacetEditorContextBase;
+import com.intellij.facet.ui.FacetEditorContext;
+import com.intellij.openapi.roots.ModifiableRootModel;
+import com.intellij.openapi.roots.ModuleRootModel;
+import com.intellij.openapi.roots.ui.configuration.ModulesProvider;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.LibrariesContainer;
+import com.intellij.openapi.module.Module;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * @author nik
+ */
+public class DelegatingLibrariesValidatorContext implements LibrariesValidatorContext {
+ private final FacetEditorContext myDelegate;
+
+ public DelegatingLibrariesValidatorContext(final @NotNull FacetEditorContext delegate) {
+ myDelegate = delegate;
+ }
+
+ @NotNull
+ public Module getModule() {
+ return myDelegate.getModule();
+ }
+
+ public LibrariesContainer getLibrariesContainer() {
+ return ((FacetEditorContextBase)myDelegate).getContainer();
+ }
+
+ @NotNull
+ public ModulesProvider getModulesProvider() {
+ return myDelegate.getModulesProvider();
+ }
+
+ @Nullable
+ public ModifiableRootModel getModifiableRootModel() {
+ return myDelegate.getModifiableRootModel();
+ }
+
+ @NotNull
+ public ModuleRootModel getRootModel() {
+ return myDelegate.getRootModel();
+ }
+
+}
diff --git a/java/idea-ui/src/com/intellij/facet/impl/ui/libraries/DownloadingOptionsDialog.form b/java/idea-ui/src/com/intellij/facet/impl/ui/libraries/DownloadingOptionsDialog.form
new file mode 100644
index 0000000..6b637be
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/facet/impl/ui/libraries/DownloadingOptionsDialog.form
@@ -0,0 +1,97 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.intellij.facet.impl.ui.libraries.DownloadingOptionsDialog">
+ <grid id="27dc6" binding="myPanel" layout-manager="GridLayoutManager" row-count="7" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+ <margin top="0" left="0" bottom="0" right="0"/>
+ <constraints>
+ <xy x="20" y="20" width="500" height="229"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <vspacer id="54e08">
+ <constraints>
+ <grid row="6" column="0" row-span="1" col-span="1" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/>
+ </constraints>
+ </vspacer>
+ <component id="604d3" class="javax.swing.JLabel" binding="myFilesToDownloadLabel">
+ <constraints>
+ <grid row="1" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties>
+ <labelFor value="117b3"/>
+ <text value="&Files to download:"/>
+ </properties>
+ </component>
+ <component id="dd86" class="com.intellij.openapi.ui.TextFieldWithBrowseButton" binding="myDirectoryField">
+ <constraints>
+ <grid row="5" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="0" fill="1" indent="0" use-parent-layout="false">
+ <preferred-size width="350" height="-1"/>
+ </grid>
+ </constraints>
+ <properties/>
+ </component>
+ <component id="85001" class="javax.swing.JLabel" binding="myCopyDownloadedFilesToLabel">
+ <constraints>
+ <grid row="4" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties>
+ <text value="Copy downloaded files &to:"/>
+ </properties>
+ </component>
+ <grid id="6bec" layout-manager="GridLayoutManager" row-count="1" column-count="3" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+ <margin top="0" left="0" bottom="0" right="0"/>
+ <constraints>
+ <grid row="3" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <component id="b5e3e" class="javax.swing.JCheckBox" binding="myDownloadSourcesCheckBox" default-binding="true">
+ <constraints>
+ <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties>
+ <focusable value="false"/>
+ <text value="Download &sources"/>
+ </properties>
+ </component>
+ <hspacer id="598d">
+ <constraints>
+ <grid row="0" column="2" row-span="1" col-span="1" vsize-policy="1" hsize-policy="6" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
+ </constraints>
+ </hspacer>
+ <component id="f9d9a" class="javax.swing.JCheckBox" binding="myDownloadJavadocsCheckBox" default-binding="true">
+ <constraints>
+ <grid row="0" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties>
+ <focusable value="false"/>
+ <text value="Download &javadocs"/>
+ </properties>
+ </component>
+ </children>
+ </grid>
+ <grid id="f9161" binding="myNameWrappingPanel" layout-manager="BorderLayout" hgap="0" vgap="0">
+ <constraints>
+ <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children/>
+ </grid>
+ <scrollpane id="117b3" class="com.intellij.ui.components.JBScrollPane">
+ <constraints>
+ <grid row="2" column="0" row-span="1" col-span="1" vsize-policy="7" hsize-policy="7" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <component id="45c8c" class="com.intellij.ui.CheckBoxList" binding="myFilesList">
+ <constraints/>
+ <properties/>
+ </component>
+ </children>
+ </scrollpane>
+ </children>
+ </grid>
+</form>
diff --git a/java/idea-ui/src/com/intellij/facet/impl/ui/libraries/DownloadingOptionsDialog.java b/java/idea-ui/src/com/intellij/facet/impl/ui/libraries/DownloadingOptionsDialog.java
new file mode 100644
index 0000000..5e29ad5
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/facet/impl/ui/libraries/DownloadingOptionsDialog.java
@@ -0,0 +1,251 @@
+/*
+ * Copyright 2000-2012 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.facet.impl.ui.libraries;
+
+import com.intellij.framework.library.DownloadableLibraryFileDescription;
+import com.intellij.framework.library.DownloadableLibraryType;
+import com.intellij.framework.library.FrameworkLibraryVersion;
+import com.intellij.ui.ListCellRendererWrapper;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory;
+import com.intellij.openapi.project.ProjectBundle;
+import com.intellij.openapi.roots.ui.configuration.libraryEditor.LibraryNameAndLevelPanel;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.LibrariesContainer;
+import com.intellij.openapi.ui.DialogWrapper;
+import com.intellij.openapi.ui.TextFieldWithBrowseButton;
+import com.intellij.openapi.util.Comparing;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.ui.CheckBoxList;
+import com.intellij.ui.CheckBoxListListener;
+import com.intellij.ui.CollectionListModel;
+import com.intellij.util.Function;
+import com.intellij.util.containers.ContainerUtil;
+import com.intellij.util.ui.FormBuilder;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import java.awt.*;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author Dmitry Avdeev
+ */
+public class DownloadingOptionsDialog extends DialogWrapper {
+ private static enum AdditionalDownloadType {SOURCES, DOCUMENTATION}
+ private static final Logger LOG = Logger.getInstance("#com.intellij.facet.impl.ui.libraries.DownloadingOptionsDialog");
+ private JPanel myPanel;
+ private CheckBoxList myFilesList;
+ private TextFieldWithBrowseButton myDirectoryField;
+ private JCheckBox myDownloadSourcesCheckBox;
+ private JCheckBox myDownloadJavadocsCheckBox;
+ private JLabel myFilesToDownloadLabel;
+ private JLabel myCopyDownloadedFilesToLabel;
+ private JPanel myNameWrappingPanel;
+ private JComboBox myVersionComboBox;
+ private final LibraryNameAndLevelPanel myNameAndLevelPanel;
+ private DownloadableLibraryType myLibraryType;
+ private FrameworkLibraryVersion myLastSelectedVersion;
+
+ public DownloadingOptionsDialog(@NotNull Component parent, @NotNull final LibraryDownloadSettings settings, @NotNull List<? extends FrameworkLibraryVersion> versions,
+ final boolean showNameAndLevel) {
+ super(parent, true);
+ setTitle("Downloading Options");
+ myLibraryType = settings.getLibraryType();
+ LOG.assertTrue(!versions.isEmpty());
+
+ final FormBuilder builder = LibraryNameAndLevelPanel.createFormBuilder();
+
+ myVersionComboBox = new JComboBox();
+ for (FrameworkLibraryVersion version : versions) {
+ myVersionComboBox.addItem(version);
+ }
+ myVersionComboBox.setRenderer(new ListCellRendererWrapper<FrameworkLibraryVersion>() {
+ @Override
+ public void customize(JList list, FrameworkLibraryVersion value, int index, boolean selected, boolean hasFocus) {
+ setText(value.getName() + value.getVersionString());
+ }
+ });
+ myVersionComboBox.setSelectedItem(settings.getVersion());
+ if (versions.size() > 1) {
+ builder.addLabeledComponent("&Version:", myVersionComboBox);
+ }
+
+ if (showNameAndLevel) {
+ myNameAndLevelPanel = new LibraryNameAndLevelPanel(builder, settings.getLibraryName(), settings.getLibraryLevel());
+ }
+ else {
+ myNameAndLevelPanel = null;
+ }
+ myNameWrappingPanel.add(builder.getPanel());
+
+ onVersionChanged(settings.getSelectedDownloads());
+ myVersionComboBox.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ onVersionChanged(null);
+ }
+ });
+
+ myFilesList.setBorder(null);
+ myFilesToDownloadLabel.setLabelFor(myFilesList);
+ myDirectoryField.addBrowseFolderListener(ProjectBundle.message("file.chooser.directory.for.downloaded.libraries.title"),
+ ProjectBundle.message("file.chooser.directory.for.downloaded.libraries.description"), null,
+ FileChooserDescriptorFactory.createSingleFolderDescriptor());
+
+ myCopyDownloadedFilesToLabel.setLabelFor(myDirectoryField);
+ myDirectoryField.setText(FileUtil.toSystemDependentName(settings.getDirectoryForDownloadedLibrariesPath()));
+
+ boolean sourcesCheckboxVisible = false;
+ boolean javadocCheckboxVisible = false;
+ for (FrameworkLibraryVersion version : versions) {
+ sourcesCheckboxVisible |= haveAdditionalDownloads(version.getFiles(), AdditionalDownloadType.SOURCES);
+ javadocCheckboxVisible |= haveAdditionalDownloads(version.getFiles(), AdditionalDownloadType.DOCUMENTATION);
+ }
+ myDownloadSourcesCheckBox.setVisible(sourcesCheckboxVisible);
+ myDownloadJavadocsCheckBox.setVisible(javadocCheckboxVisible);
+ myFilesList.setCheckBoxListListener(new CheckBoxListListener() {
+ @Override
+ public void checkBoxSelectionChanged(int index, boolean value) {
+ updateSourcesAndJavadocCheckboxes();
+ }
+ });
+
+ updateSourcesAndJavadocCheckboxes();
+ myDownloadSourcesCheckBox.setSelected(settings.isDownloadSources());
+ myDownloadJavadocsCheckBox.setSelected(settings.isDownloadJavaDocs());
+ init();
+ }
+
+ private void updateSourcesAndJavadocCheckboxes() {
+ final FrameworkLibraryVersion version = getSelectedVersion();
+ boolean sourcesCheckboxEnabled;
+ boolean javadocCheckboxEnabled;
+ if (version == null) {
+ sourcesCheckboxEnabled = javadocCheckboxEnabled = false;
+ }
+ else {
+ final List<DownloadableLibraryFileDescription> descriptions = getSelectedDownloads(version);
+ sourcesCheckboxEnabled = haveAdditionalDownloads(descriptions, AdditionalDownloadType.SOURCES);
+ javadocCheckboxEnabled = haveAdditionalDownloads(descriptions, AdditionalDownloadType.DOCUMENTATION);
+ }
+ setEnabled(myDownloadSourcesCheckBox, sourcesCheckboxEnabled);
+ setEnabled(myDownloadJavadocsCheckBox, javadocCheckboxEnabled);
+ }
+
+ private static void setEnabled(final JCheckBox checkBox, final boolean enabled) {
+ if (!enabled) {
+ checkBox.setSelected(false);
+ }
+ checkBox.setEnabled(enabled);
+ }
+
+ private static boolean haveAdditionalDownloads(final List<? extends DownloadableLibraryFileDescription> descriptions, AdditionalDownloadType type) {
+ for (DownloadableLibraryFileDescription description : descriptions) {
+ if (type == AdditionalDownloadType.SOURCES && description.getSourcesDescription() != null
+ || type == AdditionalDownloadType.DOCUMENTATION && description.getDocumentationDescription() != null) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private void onVersionChanged(final @Nullable List<? extends DownloadableLibraryFileDescription> selectedFiles) {
+ final FrameworkLibraryVersion version = getSelectedVersion();
+ if (Comparing.equal(myLastSelectedVersion, version)) return;
+
+ if (version != null) {
+ final List<? extends DownloadableLibraryFileDescription> downloads = version.getFiles();
+ myFilesList.setModel(new CollectionListModel<JCheckBox>(
+ ContainerUtil.map2Array(downloads, JCheckBox.class, new Function<DownloadableLibraryFileDescription, JCheckBox>() {
+ @Override
+ public JCheckBox fun(DownloadableLibraryFileDescription description) {
+ final boolean selected = selectedFiles != null ? selectedFiles.contains(description) : !description.isOptional();
+ return new JCheckBox(description.getPresentableFileName(), selected);
+ }
+ })));
+ if (myNameAndLevelPanel != null) {
+ myNameAndLevelPanel.setDefaultName(version.getDefaultLibraryName());
+ }
+ }
+ updateSourcesAndJavadocCheckboxes();
+ myLastSelectedVersion = version;
+ }
+
+ @Override
+ protected JComponent createCenterPanel() {
+ return myPanel;
+ }
+
+ @Override
+ public JComponent getPreferredFocusedComponent() {
+ return myFilesList;
+ }
+
+ @Nullable
+ public FrameworkLibraryVersion getSelectedVersion() {
+ return (FrameworkLibraryVersion)myVersionComboBox.getSelectedItem();
+ }
+
+ @Nullable
+ public static LibraryDownloadSettings showDialog(@NotNull JComponent parent,
+ @NotNull LibraryDownloadSettings settings,
+ List<? extends FrameworkLibraryVersion> versions,
+ boolean showNameAndLevel) {
+ final DownloadingOptionsDialog dialog = new DownloadingOptionsDialog(parent, settings, versions, showNameAndLevel);
+ dialog.show();
+ if (!dialog.isOK()) {
+ return null;
+ }
+
+ return dialog.createSettings();
+ }
+
+ private List<DownloadableLibraryFileDescription> getSelectedDownloads(FrameworkLibraryVersion version) {
+ List<DownloadableLibraryFileDescription> selected = new ArrayList<DownloadableLibraryFileDescription>();
+ List<? extends DownloadableLibraryFileDescription> downloads = version.getFiles();
+ for (int i = 0; i < downloads.size(); i++) {
+ if (myFilesList.isItemSelected(i)) {
+ selected.add(downloads.get(i));
+ }
+ }
+ return selected;
+ }
+
+ private LibraryDownloadSettings createSettings() {
+ final FrameworkLibraryVersion version = getSelectedVersion();
+ LOG.assertTrue(version != null);
+ String libraryName;
+ LibrariesContainer.LibraryLevel libraryLevel;
+ if (myNameAndLevelPanel != null) {
+ libraryName = myNameAndLevelPanel.getLibraryName();
+ libraryLevel = myNameAndLevelPanel.getLibraryLevel();
+ }
+ else {
+ libraryName = version.getDefaultLibraryName();
+ libraryLevel = LibrariesContainer.LibraryLevel.PROJECT;
+ }
+
+ final String path = FileUtil.toSystemIndependentName(myDirectoryField.getText());
+ List<DownloadableLibraryFileDescription> selected = getSelectedDownloads(version);
+
+ return new LibraryDownloadSettings(version, myLibraryType, path, libraryName, libraryLevel, selected,
+ myDownloadSourcesCheckBox.isSelected(), myDownloadJavadocsCheckBox.isSelected());
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/facet/impl/ui/libraries/EditLibraryDialog.form b/java/idea-ui/src/com/intellij/facet/impl/ui/libraries/EditLibraryDialog.form
new file mode 100644
index 0000000..c9f9c05
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/facet/impl/ui/libraries/EditLibraryDialog.form
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.intellij.facet.impl.ui.libraries.EditLibraryDialog">
+ <grid id="27dc6" binding="myPanel" layout-manager="GridLayoutManager" row-count="2" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+ <margin top="0" left="0" bottom="0" right="0"/>
+ <constraints>
+ <xy x="20" y="20" width="500" height="400"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <grid id="d0dbf" binding="myEditorPanel" layout-manager="BorderLayout" hgap="0" vgap="0">
+ <constraints>
+ <grid row="1" column="0" row-span="1" col-span="1" vsize-policy="7" hsize-policy="7" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children/>
+ </grid>
+ <grid id="8ed98" binding="myNameAndLevelPanelWrapper" layout-manager="BorderLayout" hgap="0" vgap="0">
+ <constraints>
+ <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children/>
+ </grid>
+ </children>
+ </grid>
+</form>
diff --git a/java/idea-ui/src/com/intellij/facet/impl/ui/libraries/EditLibraryDialog.java b/java/idea-ui/src/com/intellij/facet/impl/ui/libraries/EditLibraryDialog.java
new file mode 100644
index 0000000..a205e45
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/facet/impl/ui/libraries/EditLibraryDialog.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2000-2010 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.facet.impl.ui.libraries;
+
+import com.intellij.openapi.roots.ui.configuration.libraryEditor.LibraryEditor;
+import com.intellij.openapi.roots.ui.configuration.libraryEditor.LibraryNameAndLevelPanel;
+import com.intellij.openapi.roots.ui.configuration.libraryEditor.LibraryRootsComponent;
+import com.intellij.openapi.roots.ui.configuration.libraryEditor.NewLibraryEditor;
+import com.intellij.openapi.ui.DialogWrapper;
+import com.intellij.openapi.util.Disposer;
+import com.intellij.util.ui.FormBuilder;
+
+import javax.swing.*;
+import java.awt.*;
+
+/**
+ * @author Dmitry Avdeev
+ */
+public class EditLibraryDialog extends DialogWrapper {
+
+ private JPanel myEditorPanel;
+ private JPanel myPanel;
+ private JPanel myNameAndLevelPanelWrapper;
+ private final LibraryNameAndLevelPanel myNameAndLevelPanel;
+ private LibraryCompositionSettings mySettings;
+ private LibraryEditor myLibraryEditor;
+ private LibraryRootsComponent myLibraryRootsComponent;
+ private FormBuilder myBuilder;
+
+ public EditLibraryDialog(Component parent, LibraryCompositionSettings settings, final LibraryEditor libraryEditor) {
+ super(parent, true);
+ mySettings = settings;
+ myLibraryEditor = libraryEditor;
+ myLibraryRootsComponent = new LibraryRootsComponent(null, libraryEditor);
+ myLibraryRootsComponent.resetProperties();
+
+ Disposer.register(getDisposable(), myLibraryRootsComponent);
+
+ final boolean newLibrary = libraryEditor instanceof NewLibraryEditor;
+ setTitle((newLibrary ? "Create" : "Edit") + " Library");
+
+ myBuilder = LibraryNameAndLevelPanel.createFormBuilder();
+ myNameAndLevelPanel = new LibraryNameAndLevelPanel(myBuilder, libraryEditor.getName(), newLibrary ? settings.getNewLibraryLevel() : null);
+ init();
+ }
+
+ @Override
+ protected JComponent createCenterPanel() {
+ JComponent editor = myLibraryRootsComponent.getComponent();
+ myEditorPanel.add(editor);
+ myNameAndLevelPanelWrapper.add(myBuilder.getPanel());
+ return myPanel;
+ }
+
+ @Override
+ protected void doOKAction() {
+ myLibraryEditor.setName(myNameAndLevelPanel.getLibraryName());
+ myLibraryRootsComponent.applyProperties();
+ if (myLibraryEditor instanceof NewLibraryEditor) {
+ mySettings.setNewLibraryLevel(myNameAndLevelPanel.getLibraryLevel());
+ }
+ super.doOKAction();
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/facet/impl/ui/libraries/FacetLibrariesValidatorImpl.java b/java/idea-ui/src/com/intellij/facet/impl/ui/libraries/FacetLibrariesValidatorImpl.java
new file mode 100644
index 0000000..0bb6900
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/facet/impl/ui/libraries/FacetLibrariesValidatorImpl.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright 2000-2009 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.facet.impl.ui.libraries;
+
+import com.intellij.facet.Facet;
+import com.intellij.facet.ui.FacetConfigurationQuickFix;
+import com.intellij.facet.ui.FacetValidatorsManager;
+import com.intellij.facet.ui.ValidationResult;
+import com.intellij.facet.ui.libraries.FacetLibrariesValidator;
+import com.intellij.facet.ui.libraries.FacetLibrariesValidatorDescription;
+import com.intellij.facet.ui.libraries.LibraryInfo;
+import com.intellij.ide.IdeBundle;
+import com.intellij.ide.util.frameworkSupport.OldCustomLibraryDescription;
+import com.intellij.openapi.roots.ModuleRootModel;
+import com.intellij.openapi.roots.OrderRootType;
+import com.intellij.openapi.roots.libraries.Library;
+import com.intellij.openapi.roots.ui.configuration.libraries.AddCustomLibraryDialog;
+import com.intellij.openapi.roots.ui.configuration.libraries.CustomLibraryDescription;
+import com.intellij.openapi.vfs.VfsUtil;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.util.Processor;
+import com.intellij.util.containers.ContainerUtil;
+import org.jetbrains.annotations.NotNull;
+
+import javax.swing.*;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author nik
+ */
+public class FacetLibrariesValidatorImpl extends FacetLibrariesValidator {
+ private final LibrariesValidatorContext myContext;
+ private final FacetValidatorsManager myValidatorsManager;
+ private RequiredLibrariesInfo myRequiredLibraries;
+ private FacetLibrariesValidatorDescription myDescription;
+ private final List<Library> myAddedLibraries = new ArrayList<Library>();
+
+ public FacetLibrariesValidatorImpl(LibraryInfo[] requiredLibraries, FacetLibrariesValidatorDescription description,
+ final LibrariesValidatorContext context, FacetValidatorsManager validatorsManager) {
+ myContext = context;
+ myValidatorsManager = validatorsManager;
+ myRequiredLibraries = new RequiredLibrariesInfo(requiredLibraries);
+ myDescription = description;
+ }
+
+ public void setRequiredLibraries(final LibraryInfo[] requiredLibraries) {
+ myRequiredLibraries = new RequiredLibrariesInfo(requiredLibraries);
+ onChange();
+ }
+
+ public boolean isLibrariesAdded() {
+ return false;
+ }
+
+ public void setDescription(@NotNull final FacetLibrariesValidatorDescription description) {
+ myDescription = description;
+ onChange();
+ }
+
+ public ValidationResult check() {
+ if (myRequiredLibraries == null) {
+ return ValidationResult.OK;
+ }
+
+ List<VirtualFile> roots = collectRoots(myContext.getRootModel());
+ RequiredLibrariesInfo.RequiredClassesNotFoundInfo info = myRequiredLibraries.checkLibraries(VfsUtil.toVirtualFileArray(roots));
+ if (info == null) {
+ return ValidationResult.OK;
+ }
+
+ String missingJars = IdeBundle.message("label.missed.libraries.prefix") + " " + info.getMissingJarsText();
+ LibraryInfo[] missingLibraries = info.getLibraryInfos();
+ CustomLibraryDescription description = new OldCustomLibraryDescription(missingLibraries, myDescription.getDefaultLibraryName());
+ return new ValidationResult(missingJars, new LibrariesQuickFix(description));
+ }
+
+ private void onChange() {
+ if (myValidatorsManager != null) {
+ myValidatorsManager.validate();
+ }
+ }
+
+ public void onFacetInitialized(Facet facet) {
+ for (Library addedLibrary : myAddedLibraries) {
+ myDescription.onLibraryAdded(facet, addedLibrary);
+ }
+ }
+
+ private List<VirtualFile> collectRoots(final @NotNull ModuleRootModel rootModel) {
+ final ArrayList<VirtualFile> roots = new ArrayList<VirtualFile>();
+ rootModel.orderEntries().using(myContext.getModulesProvider()).recursively().librariesOnly().forEachLibrary(new Processor<Library>() {
+ @Override
+ public boolean process(Library library) {
+ ContainerUtil.addAll(roots, myContext.getLibrariesContainer().getLibraryFiles(library, OrderRootType.CLASSES));
+ return true;
+ }
+ });
+ return roots;
+ }
+
+ private class LibrariesQuickFix extends FacetConfigurationQuickFix {
+ private CustomLibraryDescription myDescription;
+
+ public LibrariesQuickFix(CustomLibraryDescription description) {
+ super(IdeBundle.message("missing.libraries.fix.button"));
+ myDescription = description;
+ }
+
+ public void run(final JComponent place) {
+ AddCustomLibraryDialog dialog = AddCustomLibraryDialog.createDialog(myDescription, myContext.getLibrariesContainer(),
+ myContext.getModule(), myContext.getModifiableRootModel(), null);
+ dialog.show();
+ myAddedLibraries.addAll(dialog.getAddedLibraries());
+ onChange();
+ }
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/facet/impl/ui/libraries/FrameworkLibraryValidatorImpl.java b/java/idea-ui/src/com/intellij/facet/impl/ui/libraries/FrameworkLibraryValidatorImpl.java
new file mode 100644
index 0000000..56fe2f1
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/facet/impl/ui/libraries/FrameworkLibraryValidatorImpl.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2000-2012 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.facet.impl.ui.libraries;
+
+import com.intellij.facet.ui.FacetConfigurationQuickFix;
+import com.intellij.facet.ui.FacetValidatorsManager;
+import com.intellij.facet.ui.ValidationResult;
+import com.intellij.facet.ui.libraries.FrameworkLibraryValidator;
+import com.intellij.ide.IdeBundle;
+import com.intellij.openapi.roots.libraries.Library;
+import com.intellij.openapi.roots.libraries.LibraryKind;
+import com.intellij.openapi.roots.ui.configuration.libraries.AddCustomLibraryDialog;
+import com.intellij.openapi.roots.ui.configuration.libraries.CustomLibraryDescription;
+import com.intellij.openapi.roots.ui.configuration.libraries.LibraryPresentationManager;
+import com.intellij.openapi.util.Ref;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.util.Processor;
+
+import javax.swing.*;
+import java.util.Set;
+
+/**
+ * @author nik
+ */
+public class FrameworkLibraryValidatorImpl extends FrameworkLibraryValidator {
+ private CustomLibraryDescription myLibraryDescription;
+ private final LibrariesValidatorContext myContext;
+ private final FacetValidatorsManager myValidatorsManager;
+ private final String myLibraryCategoryName;
+
+ public FrameworkLibraryValidatorImpl(CustomLibraryDescription libraryDescription,
+ LibrariesValidatorContext context,
+ FacetValidatorsManager validatorsManager,
+ String libraryCategoryName) {
+ myLibraryDescription = libraryDescription;
+ myContext = context;
+ myValidatorsManager = validatorsManager;
+ myLibraryCategoryName = libraryCategoryName;
+ }
+
+ @Override
+ public ValidationResult check() {
+ final Set<? extends LibraryKind> libraryKinds = myLibraryDescription.getSuitableLibraryKinds();
+ final Ref<Boolean> found = Ref.create(false);
+ myContext.getRootModel().orderEntries().using(myContext.getModulesProvider()).recursively().librariesOnly().forEachLibrary(new Processor<Library>() {
+ @Override
+ public boolean process(Library library) {
+ if (LibraryPresentationManager.getInstance().isLibraryOfKind(library, myContext.getLibrariesContainer(), libraryKinds)) {
+ found.set(true);
+ return false;
+ }
+ return true;
+ }
+ });
+ if (found.get()) return ValidationResult.OK;
+
+ return new ValidationResult(StringUtil.capitalize(myLibraryCategoryName) + " library not found in the module dependencies list", new LibrariesQuickFix(myLibraryDescription));
+ }
+
+ private class LibrariesQuickFix extends FacetConfigurationQuickFix {
+ private CustomLibraryDescription myDescription;
+
+ public LibrariesQuickFix(CustomLibraryDescription description) {
+ super(IdeBundle.message("missing.libraries.fix.button"));
+ myDescription = description;
+ }
+
+ public void run(final JComponent place) {
+ AddCustomLibraryDialog dialog = AddCustomLibraryDialog.createDialog(myDescription, myContext.getLibrariesContainer(),
+ myContext.getModule(), myContext.getModifiableRootModel(), null);
+ dialog.show();
+ myValidatorsManager.validate();
+ }
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/facet/impl/ui/libraries/LibrariesValidationComponentImpl.java b/java/idea-ui/src/com/intellij/facet/impl/ui/libraries/LibrariesValidationComponentImpl.java
new file mode 100644
index 0000000..f733ee5
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/facet/impl/ui/libraries/LibrariesValidationComponentImpl.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2000-2009 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.facet.impl.ui.libraries;
+
+import com.intellij.facet.impl.ui.FacetErrorPanel;
+import com.intellij.facet.ui.libraries.LibrariesValidationComponent;
+import com.intellij.facet.ui.libraries.FacetLibrariesValidatorDescription;
+import com.intellij.facet.ui.libraries.LibraryInfo;
+import com.intellij.openapi.module.Module;
+import com.intellij.util.EventDispatcher;
+
+import javax.swing.*;
+
+/**
+ * @author nik
+ */
+public class LibrariesValidationComponentImpl implements LibrariesValidationComponent {
+ private final EventDispatcher<ValidityListener> myDispatcher = EventDispatcher.create(ValidityListener.class);
+ private final FacetErrorPanel myErrorPanel;
+ private final FacetLibrariesValidatorImpl myLibrariesValidator;
+ private final Module myModule;
+
+ public LibrariesValidationComponentImpl(LibraryInfo[] requiredLibraries, final Module module, String defaultLibraryName) {
+ myErrorPanel = new FacetErrorPanel();
+ FacetLibrariesValidatorDescription description = new FacetLibrariesValidatorDescription(defaultLibraryName);
+ myModule = module;
+ myLibrariesValidator = new FacetLibrariesValidatorImpl(requiredLibraries, description, new LibrariesValidatorContextImpl(myModule),
+ myErrorPanel.getValidatorsManager());
+ myErrorPanel.getValidatorsManager().registerValidator(myLibrariesValidator);
+ myErrorPanel.addListener(new Runnable() {
+ public void run() {
+ myDispatcher.getMulticaster().valididyChanged(myErrorPanel.isOk());
+ }
+ });
+ }
+
+ public JComponent getComponent() {
+ return myErrorPanel.getComponent();
+ }
+
+ public void validate() {
+ myErrorPanel.getValidatorsManager().validate();
+ }
+
+ public boolean isValid() {
+ return myErrorPanel.isOk();
+ }
+
+ public void addValidityListener(final ValidityListener listener) {
+ myDispatcher.addListener(listener);
+ }
+
+ public void removeValidityListener(final ValidityListener listener) {
+ myDispatcher.removeListener(listener);
+ }
+
+ public void setupLibraries() {
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/facet/impl/ui/libraries/LibrariesValidatorContext.java b/java/idea-ui/src/com/intellij/facet/impl/ui/libraries/LibrariesValidatorContext.java
new file mode 100644
index 0000000..9476c58
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/facet/impl/ui/libraries/LibrariesValidatorContext.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2000-2009 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.facet.impl.ui.libraries;
+
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.roots.ModifiableRootModel;
+import com.intellij.openapi.roots.ModuleRootModel;
+import com.intellij.openapi.roots.ui.configuration.ModulesProvider;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.LibrariesContainer;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * @author nik
+ */
+public interface LibrariesValidatorContext {
+ @NotNull
+ ModuleRootModel getRootModel();
+
+ @Nullable
+ ModifiableRootModel getModifiableRootModel();
+
+ @NotNull
+ ModulesProvider getModulesProvider();
+
+ @NotNull
+ Module getModule();
+
+ LibrariesContainer getLibrariesContainer();
+
+}
diff --git a/java/idea-ui/src/com/intellij/facet/impl/ui/libraries/LibrariesValidatorContextImpl.java b/java/idea-ui/src/com/intellij/facet/impl/ui/libraries/LibrariesValidatorContextImpl.java
new file mode 100644
index 0000000..a034ff5
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/facet/impl/ui/libraries/LibrariesValidatorContextImpl.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2000-2009 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.facet.impl.ui.libraries;
+
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.roots.ModifiableRootModel;
+import com.intellij.openapi.roots.ModuleRootManager;
+import com.intellij.openapi.roots.ModuleRootModel;
+import com.intellij.openapi.roots.ui.configuration.DefaultModulesProvider;
+import com.intellij.openapi.roots.ui.configuration.ModulesProvider;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.LibrariesContainer;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.LibrariesContainerFactory;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * @author nik
+ */
+public class LibrariesValidatorContextImpl implements LibrariesValidatorContext {
+ private final Module myModule;
+ private final LibrariesContainer myLibrariesContainer;
+
+ public LibrariesValidatorContextImpl(final @NotNull Module module) {
+ myModule = module;
+ myLibrariesContainer = LibrariesContainerFactory.createContainer(module);
+ }
+
+ @NotNull
+ public ModuleRootModel getRootModel() {
+ return ModuleRootManager.getInstance(myModule);
+ }
+
+ @Nullable
+ public ModifiableRootModel getModifiableRootModel() {
+ return null;
+ }
+
+ @NotNull
+ public ModulesProvider getModulesProvider() {
+ return new DefaultModulesProvider(myModule.getProject());
+ }
+
+ @NotNull
+ public Module getModule() {
+ return myModule;
+ }
+
+ public LibrariesContainer getLibrariesContainer() {
+ return myLibrariesContainer;
+ }
+
+}
diff --git a/java/idea-ui/src/com/intellij/facet/impl/ui/libraries/LibraryCompositionSettings.java b/java/idea-ui/src/com/intellij/facet/impl/ui/libraries/LibraryCompositionSettings.java
new file mode 100644
index 0000000..f58e9f2
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/facet/impl/ui/libraries/LibraryCompositionSettings.java
@@ -0,0 +1,204 @@
+/*
+ * Copyright 2000-2009 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.facet.impl.ui.libraries;
+
+import com.intellij.framework.library.FrameworkLibraryVersion;
+import com.intellij.framework.library.FrameworkLibraryVersionFilter;
+import com.intellij.openapi.Disposable;
+import com.intellij.openapi.roots.ModifiableRootModel;
+import com.intellij.openapi.roots.libraries.Library;
+import com.intellij.openapi.roots.ui.configuration.libraries.CustomLibraryDescription;
+import com.intellij.openapi.roots.ui.configuration.libraryEditor.ExistingLibraryEditor;
+import com.intellij.openapi.roots.ui.configuration.libraryEditor.NewLibraryEditor;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.LibrariesContainer;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.LibrariesContainerFactory;
+import com.intellij.openapi.util.Disposer;
+import com.intellij.util.containers.ContainerUtil;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author nik
+*/
+public class LibraryCompositionSettings implements Disposable {
+ private final CustomLibraryDescription myLibraryDescription;
+ private FrameworkLibraryVersionFilter myVersionFilter;
+ private String myBaseDirectoryPath;
+ private final List<? extends FrameworkLibraryVersion> myAllVersions;
+ private LibrariesContainer.LibraryLevel myNewLibraryLevel;
+ private NewLibraryEditor myNewLibraryEditor;
+ private Library mySelectedLibrary;
+ private boolean myDownloadLibraries;
+ private LibraryDownloadSettings myDownloadSettings;
+ private Map<Library, ExistingLibraryEditor> myExistingLibraryEditors =
+ ContainerUtil.newIdentityTroveMap();
+
+ public LibraryCompositionSettings(final @NotNull CustomLibraryDescription libraryDescription,
+ final @NotNull String baseDirectoryPath,
+ @NotNull FrameworkLibraryVersionFilter versionFilter,
+ final List<? extends FrameworkLibraryVersion> allVersions) {
+ myLibraryDescription = libraryDescription;
+ myVersionFilter = versionFilter;
+ myNewLibraryLevel = libraryDescription.getDefaultLevel();
+ myBaseDirectoryPath = baseDirectoryPath;
+ myAllVersions = allVersions;
+ final List<? extends FrameworkLibraryVersion> versions = getCompatibleVersions();
+ if (!versions.isEmpty()) {
+ myDownloadSettings = createDownloadSettings(versions.get(0));
+ }
+ }
+
+ private LibraryDownloadSettings createDownloadSettings(final FrameworkLibraryVersion version) {
+ return new LibraryDownloadSettings(version, myLibraryDescription.getDownloadableLibraryType(),
+ myNewLibraryLevel, getDefaultDownloadPath(myBaseDirectoryPath));
+ }
+
+ public void setVersionFilter(@NotNull FrameworkLibraryVersionFilter versionFilter) {
+ myVersionFilter = versionFilter;
+ if (myDownloadSettings == null || !versionFilter.isAccepted(myDownloadSettings.getVersion())) {
+ final FrameworkLibraryVersion newLibraryVersion = ContainerUtil.getFirstItem(getCompatibleVersions());
+ if (newLibraryVersion != null) {
+ myDownloadSettings = createDownloadSettings(newLibraryVersion);
+ }
+ else {
+ myDownloadSettings = null;
+ }
+ }
+ }
+
+ public List<? extends FrameworkLibraryVersion> getCompatibleVersions() {
+ final List<FrameworkLibraryVersion> result = new ArrayList<FrameworkLibraryVersion>();
+ for (FrameworkLibraryVersion version : myAllVersions) {
+ if (myVersionFilter.isAccepted(version)) {
+ result.add(version);
+ }
+ }
+ return result;
+ }
+
+ private static String getDefaultDownloadPath(@NotNull String baseDirectoryPath) {
+ return baseDirectoryPath + "/lib";
+ }
+
+ public void setDownloadSettings(LibraryDownloadSettings downloadSettings) {
+ myDownloadSettings = downloadSettings;
+ }
+
+ public ExistingLibraryEditor getOrCreateEditor(@NotNull Library library) {
+ ExistingLibraryEditor libraryEditor = myExistingLibraryEditors.get(library);
+ if (libraryEditor == null) {
+ libraryEditor = new ExistingLibraryEditor(library, null);
+ Disposer.register(this, libraryEditor);
+ myExistingLibraryEditors.put(library, libraryEditor);
+ }
+ return libraryEditor;
+ }
+
+ @NotNull
+ public CustomLibraryDescription getLibraryDescription() {
+ return myLibraryDescription;
+ }
+
+ @Nullable
+ public LibraryDownloadSettings getDownloadSettings() {
+ return myDownloadSettings;
+ }
+
+ @NotNull
+ public String getBaseDirectoryPath() {
+ return myBaseDirectoryPath;
+ }
+
+ public void changeBaseDirectoryPath(@NotNull String baseDirectoryPath) {
+ if (!myBaseDirectoryPath.equals(baseDirectoryPath)) {
+ if (myDownloadSettings != null &&
+ myDownloadSettings.getDirectoryForDownloadedLibrariesPath().equals(getDefaultDownloadPath(myBaseDirectoryPath))) {
+ myDownloadSettings.setDirectoryForDownloadedLibrariesPath(getDefaultDownloadPath(baseDirectoryPath));
+ }
+ myBaseDirectoryPath = baseDirectoryPath;
+ }
+ }
+
+ public void setDownloadLibraries(final boolean downloadLibraries) {
+ myDownloadLibraries = downloadLibraries;
+ }
+
+ public void setSelectedExistingLibrary(@Nullable Library library) {
+ mySelectedLibrary = library;
+ }
+
+ public void setNewLibraryLevel(final LibrariesContainer.LibraryLevel newLibraryLevel) {
+ myNewLibraryLevel = newLibraryLevel;
+ }
+
+ public boolean downloadFiles(final @NotNull JComponent parent) {
+ if (myDownloadLibraries && myDownloadSettings != null) {
+ final NewLibraryEditor libraryEditor = myDownloadSettings.download(parent);
+ if (libraryEditor != null) {
+ myNewLibraryEditor = libraryEditor;
+ }
+ }
+ return true;
+ }
+
+ @Nullable
+ private Library createLibrary(final ModifiableRootModel rootModel, @Nullable LibrariesContainer additionalContainer) {
+ if (myNewLibraryEditor != null) {
+ return LibrariesContainerFactory.createLibrary(additionalContainer, LibrariesContainerFactory.createContainer(rootModel),
+ myNewLibraryEditor, getLibraryLevel());
+ }
+ return null;
+ }
+
+ private LibrariesContainer.LibraryLevel getLibraryLevel() {
+ return myDownloadLibraries ? myDownloadSettings.getLibraryLevel() : myNewLibraryLevel;
+ }
+
+ public LibrariesContainer.LibraryLevel getNewLibraryLevel() {
+ return myNewLibraryLevel;
+ }
+
+ @Nullable
+ public Library addLibraries(final @NotNull ModifiableRootModel rootModel, final @NotNull List<Library> addedLibraries,
+ final @Nullable LibrariesContainer librariesContainer) {
+ Library library = createLibrary(rootModel, librariesContainer);
+
+ if (library != null) {
+ addedLibraries.add(library);
+ if (getLibraryLevel() != LibrariesContainer.LibraryLevel.MODULE) {
+ rootModel.addLibraryEntry(library);
+ }
+ }
+ if (mySelectedLibrary != null) {
+ addedLibraries.add(mySelectedLibrary);
+ rootModel.addLibraryEntry(mySelectedLibrary);
+ }
+ return library;
+ }
+
+ public void setNewLibraryEditor(@Nullable NewLibraryEditor libraryEditor) {
+ myNewLibraryEditor = libraryEditor;
+ }
+
+ @Override
+ public void dispose() {
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/facet/impl/ui/libraries/LibraryDownloadSettings.java b/java/idea-ui/src/com/intellij/facet/impl/ui/libraries/LibraryDownloadSettings.java
new file mode 100644
index 0000000..b358416
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/facet/impl/ui/libraries/LibraryDownloadSettings.java
@@ -0,0 +1,166 @@
+/*
+ * Copyright 2000-2010 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.facet.impl.ui.libraries;
+
+import com.intellij.framework.library.DownloadableLibraryFileDescription;
+import com.intellij.framework.library.DownloadableLibraryType;
+import com.intellij.framework.library.FrameworkLibraryVersion;
+import com.intellij.framework.library.LibraryVersionProperties;
+import com.intellij.openapi.roots.JavadocOrderRootType;
+import com.intellij.openapi.roots.OrderRootType;
+import com.intellij.openapi.roots.ui.configuration.libraryEditor.NewLibraryEditor;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.LibrariesContainer;
+import com.intellij.openapi.util.Condition;
+import com.intellij.openapi.util.Pair;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.util.containers.ContainerUtil;
+import com.intellij.util.download.DownloadableFileDescription;
+import com.intellij.util.download.DownloadableFileService;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author nik
+ */
+public class LibraryDownloadSettings {
+ private FrameworkLibraryVersion myVersion;
+ private final DownloadableLibraryType myLibraryType;
+ private String myDirectoryForDownloadedLibrariesPath;
+ private final String myLibraryName;
+ private final boolean myDownloadSources;
+ private final boolean myDownloadJavaDocs;
+ private final LibrariesContainer.LibraryLevel myLibraryLevel;
+ private final List<? extends DownloadableLibraryFileDescription> mySelectedDownloads;
+
+ public LibraryDownloadSettings(@NotNull FrameworkLibraryVersion libraryVersion,
+ @Nullable DownloadableLibraryType libraryType,
+ final LibrariesContainer.LibraryLevel libraryLevel, final String downloadedLibrariesPath) {
+ this(libraryVersion, libraryType, downloadedLibrariesPath, libraryVersion.getDefaultLibraryName(), libraryLevel,
+ getRequiredFiles(libraryVersion.getFiles()), true, true);
+ }
+
+ public LibraryDownloadSettings(@NotNull FrameworkLibraryVersion libraryVersion, @Nullable DownloadableLibraryType libraryType,
+ @NotNull String directoryForDownloadedLibrariesPath, @NotNull String libraryName,
+ @NotNull LibrariesContainer.LibraryLevel libraryLevel,
+ @NotNull List<? extends DownloadableLibraryFileDescription> selectedDownloads,
+ boolean downloadSources, boolean downloadJavaDocs) {
+ myVersion = libraryVersion;
+ myLibraryType = libraryType;
+ myDirectoryForDownloadedLibrariesPath = directoryForDownloadedLibrariesPath;
+ myLibraryName = libraryName;
+ myDownloadSources = downloadSources;
+ myDownloadJavaDocs = downloadJavaDocs;
+ myLibraryLevel = libraryLevel;
+ mySelectedDownloads = selectedDownloads;
+ }
+
+ private static List<? extends DownloadableLibraryFileDescription> getRequiredFiles(List<? extends DownloadableLibraryFileDescription> files) {
+ return ContainerUtil.filter(files, new Condition<DownloadableLibraryFileDescription>() {
+ @Override
+ public boolean value(DownloadableLibraryFileDescription description) {
+ return !description.isOptional();
+ }
+ });
+ }
+
+ @NotNull
+ public FrameworkLibraryVersion getVersion() {
+ return myVersion;
+ }
+
+ public boolean isDownloadJavaDocs() {
+ return myDownloadJavaDocs;
+ }
+
+ public boolean isDownloadSources() {
+ return myDownloadSources;
+ }
+
+ public String getLibraryName() {
+ return myLibraryName;
+ }
+
+ public String getDirectoryForDownloadedLibrariesPath() {
+ return myDirectoryForDownloadedLibrariesPath;
+ }
+
+ public List<? extends DownloadableLibraryFileDescription> getSelectedDownloads() {
+ return mySelectedDownloads;
+ }
+
+ @NotNull
+ public LibrariesContainer.LibraryLevel getLibraryLevel() {
+ return myLibraryLevel;
+ }
+
+ public DownloadableLibraryType getLibraryType() {
+ return myLibraryType;
+ }
+
+ public void setVersion(FrameworkLibraryVersion version) {
+ myVersion = version;
+ }
+
+ public void setDirectoryForDownloadedLibrariesPath(String directoryForDownloadedLibrariesPath) {
+ myDirectoryForDownloadedLibrariesPath = directoryForDownloadedLibrariesPath;
+ }
+
+ @Nullable
+ public NewLibraryEditor download(JComponent parent) {
+ final List<DownloadableFileDescription> toDownload = new ArrayList<DownloadableFileDescription>(mySelectedDownloads);
+ Map<DownloadableFileDescription, OrderRootType> rootTypes = new HashMap<DownloadableFileDescription, OrderRootType>();
+ for (DownloadableLibraryFileDescription description : mySelectedDownloads) {
+ final DownloadableFileDescription sources = description.getSourcesDescription();
+ if (myDownloadSources && sources != null) {
+ toDownload.add(sources);
+ rootTypes.put(sources, OrderRootType.SOURCES);
+ }
+ final DownloadableFileDescription docs = description.getDocumentationDescription();
+ if (myDownloadJavaDocs && docs != null) {
+ toDownload.add(docs);
+ rootTypes.put(docs, JavadocOrderRootType.getInstance());
+ }
+ }
+
+ List<Pair<VirtualFile,DownloadableFileDescription>> downloaded =
+ DownloadableFileService.getInstance().createDownloader(toDownload, null, parent, myLibraryName + " Library")
+ .toDirectory(myDirectoryForDownloadedLibrariesPath)
+ .downloadAndReturnWithDescriptions();
+ if (downloaded == null) {
+ return null;
+ }
+
+ final NewLibraryEditor libraryEditor;
+ if (myLibraryType != null) {
+ libraryEditor = new NewLibraryEditor(myLibraryType, new LibraryVersionProperties(myVersion.getVersionString()));
+ }
+ else {
+ libraryEditor = new NewLibraryEditor();
+ }
+ libraryEditor.setName(myLibraryName);
+ for (Pair<VirtualFile, DownloadableFileDescription> pair : downloaded) {
+ final OrderRootType rootType = rootTypes.containsKey(pair.getSecond()) ? rootTypes.get(pair.getSecond()) : OrderRootType.CLASSES;
+ libraryEditor.addRoot(pair.getFirst(), rootType);
+ }
+ return libraryEditor;
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/facet/impl/ui/libraries/LibraryOptionsPanel.form b/java/idea-ui/src/com/intellij/facet/impl/ui/libraries/LibraryOptionsPanel.form
new file mode 100644
index 0000000..afaac4e
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/facet/impl/ui/libraries/LibraryOptionsPanel.form
@@ -0,0 +1,204 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.intellij.facet.impl.ui.libraries.LibraryOptionsPanel">
+ <grid id="f17b8" layout-manager="GridLayoutManager" row-count="1" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+ <margin top="0" left="0" bottom="0" right="0"/>
+ <constraints>
+ <xy x="20" y="20" width="433" height="253"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <grid id="dcbe" binding="myRootPanel" layout-manager="CardLayout" hgap="0" vgap="0">
+ <constraints>
+ <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <grid id="27dc6" binding="myPanel" layout-manager="GridLayoutManager" row-count="5" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+ <margin top="0" left="0" bottom="0" right="0"/>
+ <constraints>
+ <card name="editing"/>
+ </constraints>
+ <properties>
+ <enabled value="false"/>
+ </properties>
+ <border type="none"/>
+ <children>
+ <component id="a5b73" class="javax.swing.JRadioButton" binding="myDownloadRadioButton">
+ <constraints>
+ <grid row="1" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false">
+ <preferred-size width="207" height="22"/>
+ </grid>
+ </constraints>
+ <properties>
+ <selected value="false"/>
+ <text value="&Download"/>
+ </properties>
+ </component>
+ <component id="f63e3" class="javax.swing.JRadioButton" binding="myDoNotCreateRadioButton">
+ <constraints>
+ <grid row="2" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties>
+ <text value="Set up library &later"/>
+ </properties>
+ </component>
+ <vspacer id="79970">
+ <constraints>
+ <grid row="4" column="0" row-span="1" col-span="1" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/>
+ </constraints>
+ </vspacer>
+ <grid id="71da4" layout-manager="GridLayoutManager" row-count="1" column-count="5" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+ <margin top="0" left="0" bottom="0" right="0"/>
+ <constraints>
+ <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <component id="4dbad" class="javax.swing.JRadioButton" binding="myUseLibraryRadioButton">
+ <constraints>
+ <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties>
+ <selected value="true"/>
+ <text value="&Use library:"/>
+ </properties>
+ </component>
+ <component id="6d530" class="javax.swing.JComboBox" binding="myExistingLibraryComboBox">
+ <constraints>
+ <grid row="0" column="2" row-span="1" col-span="1" vsize-policy="0" hsize-policy="2" anchor="8" fill="1" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties/>
+ </component>
+ <hspacer id="a5d26">
+ <constraints>
+ <grid row="0" column="4" row-span="1" col-span="1" vsize-policy="1" hsize-policy="6" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
+ </constraints>
+ </hspacer>
+ <component id="eaf39" class="javax.swing.JButton" binding="myCreateButton" default-binding="true">
+ <constraints>
+ <grid row="0" column="3" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties>
+ <text value="&Create..."/>
+ </properties>
+ </component>
+ <component id="3bfa9" class="javax.swing.JLabel" binding="myUseLibraryLabel">
+ <constraints>
+ <grid row="0" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties>
+ <text value="Use library"/>
+ <visible value="false"/>
+ </properties>
+ </component>
+ </children>
+ </grid>
+ <grid id="7ea1" binding="myConfigurationPanel" layout-manager="CardLayout" hgap="0" vgap="0">
+ <constraints>
+ <grid row="3" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <grid id="9c6d3" layout-manager="GridLayoutManager" row-count="1" column-count="3" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+ <margin top="10" left="0" bottom="0" right="0"/>
+ <constraints>
+ <card name="configure"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <component id="55035" class="javax.swing.JButton" binding="myConfigureButton" default-binding="true">
+ <constraints>
+ <grid row="0" column="2" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="1" fill="1" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties>
+ <text value="&Configure..."/>
+ </properties>
+ </component>
+ <grid id="e5850" layout-manager="CardLayout" hgap="0" vgap="0">
+ <constraints>
+ <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <component id="6f310" class="com.intellij.ui.components.JBLabel" binding="myMessageLabel">
+ <constraints>
+ <card name="message"/>
+ </constraints>
+ <properties>
+ <componentStyle value="SMALL"/>
+ <enabled value="true"/>
+ <fontColor value="BRIGHTER"/>
+ <text value="<html>{} jars will be downloaded into <b>lib</b> directory<br> Project level library <b>spring</b> will be created</html>"/>
+ </properties>
+ </component>
+ <component id="84326" class="javax.swing.JLabel" binding="myHiddenLabel">
+ <constraints>
+ <card name="hidden"/>
+ </constraints>
+ <properties>
+ <text value="Label"/>
+ </properties>
+ </component>
+ </children>
+ </grid>
+ <hspacer id="d7447">
+ <constraints>
+ <grid row="0" column="1" row-span="1" col-span="1" vsize-policy="1" hsize-policy="6" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
+ </constraints>
+ </hspacer>
+ </children>
+ </grid>
+ <grid id="17c14" layout-manager="GridLayoutManager" row-count="1" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+ <margin top="0" left="0" bottom="0" right="0"/>
+ <constraints>
+ <card name="empty"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children/>
+ </grid>
+ </children>
+ </grid>
+ </children>
+ </grid>
+ <grid id="b8ccc" layout-manager="GridLayoutManager" row-count="2" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+ <margin top="0" left="0" bottom="0" right="0"/>
+ <constraints>
+ <card name="loading"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <component id="402c7" class="javax.swing.JLabel">
+ <constraints>
+ <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties>
+ <text value="Loading versions..."/>
+ </properties>
+ </component>
+ <vspacer id="b8a37">
+ <constraints>
+ <grid row="1" column="0" row-span="1" col-span="1" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/>
+ </constraints>
+ </vspacer>
+ </children>
+ </grid>
+ </children>
+ </grid>
+ </children>
+ </grid>
+ <buttonGroups>
+ <group name="myButtonGroup" bound="true">
+ <member id="4dbad"/>
+ <member id="a5b73"/>
+ <member id="f63e3"/>
+ </group>
+ </buttonGroups>
+</form>
diff --git a/java/idea-ui/src/com/intellij/facet/impl/ui/libraries/LibraryOptionsPanel.java b/java/idea-ui/src/com/intellij/facet/impl/ui/libraries/LibraryOptionsPanel.java
new file mode 100644
index 0000000..f9bf66f
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/facet/impl/ui/libraries/LibraryOptionsPanel.java
@@ -0,0 +1,437 @@
+/*
+ * Copyright 2000-2012 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.facet.impl.ui.libraries;
+
+import com.intellij.framework.library.DownloadableLibraryDescription;
+import com.intellij.framework.library.DownloadableLibraryType;
+import com.intellij.framework.library.FrameworkLibraryVersion;
+import com.intellij.framework.library.FrameworkLibraryVersionFilter;
+import com.intellij.icons.AllIcons;
+import com.intellij.ide.IdeBundle;
+import com.intellij.ide.util.frameworkSupport.OldCustomLibraryDescription;
+import com.intellij.openapi.Disposable;
+import com.intellij.openapi.application.Result;
+import com.intellij.openapi.application.WriteAction;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.project.ProjectManager;
+import com.intellij.openapi.roots.OrderRootType;
+import com.intellij.openapi.roots.impl.libraries.LibraryEx;
+import com.intellij.openapi.roots.libraries.Library;
+import com.intellij.openapi.roots.libraries.NewLibraryConfiguration;
+import com.intellij.openapi.roots.ui.OrderEntryAppearanceService;
+import com.intellij.openapi.roots.ui.configuration.libraries.CustomLibraryDescription;
+import com.intellij.openapi.roots.ui.configuration.libraries.LibraryPresentationManager;
+import com.intellij.openapi.roots.ui.configuration.libraryEditor.ExistingLibraryEditor;
+import com.intellij.openapi.roots.ui.configuration.libraryEditor.LibraryEditor;
+import com.intellij.openapi.roots.ui.configuration.libraryEditor.NewLibraryEditor;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.LibrariesContainer;
+import com.intellij.openapi.util.Disposer;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.openapi.vfs.LocalFileSystem;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.ui.ColoredListCellRenderer;
+import com.intellij.ui.SortedComboBoxModel;
+import com.intellij.ui.components.JBLabel;
+import com.intellij.util.PathUtil;
+import com.intellij.util.PlatformIcons;
+import com.intellij.util.download.DownloadableFileSetVersions;
+import com.intellij.util.ui.RadioButtonEnumModel;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import java.awt.*;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.ItemEvent;
+import java.awt.event.ItemListener;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.List;
+
+/**
+ * @author Dmitry Avdeev
+ */
+public class LibraryOptionsPanel implements Disposable {
+ private static final Logger LOG = Logger.getInstance("#com.intellij.facet.impl.ui.libraries.LibraryOptionsPanel");
+
+ private JBLabel myMessageLabel;
+ private JPanel myPanel;
+ private JButton myConfigureButton;
+ private JComboBox myExistingLibraryComboBox;
+ private JRadioButton myDoNotCreateRadioButton;
+ private JPanel myConfigurationPanel;
+ private JButton myCreateButton;
+ private JRadioButton myDownloadRadioButton;
+ private JRadioButton myUseLibraryRadioButton;
+ private JLabel myUseLibraryLabel;
+ private JLabel myHiddenLabel;
+ private JPanel myRootPanel;
+ private ButtonGroup myButtonGroup;
+
+ private LibraryCompositionSettings mySettings;
+ private final LibrariesContainer myLibrariesContainer;
+ private SortedComboBoxModel<LibraryEditor> myLibraryComboBoxModel;
+ private boolean myDisposed;
+
+ private enum Choice {
+ USE_LIBRARY,
+ DOWNLOAD,
+ SETUP_LIBRARY_LATER
+ }
+
+ private RadioButtonEnumModel<Choice> myButtonEnumModel;
+
+ public LibraryOptionsPanel(@NotNull final CustomLibraryDescription libraryDescription,
+ @NotNull final String baseDirectoryPath,
+ @NotNull final FrameworkLibraryVersionFilter versionFilter,
+ @NotNull final LibrariesContainer librariesContainer,
+ final boolean showDoNotCreateOption) {
+ myLibrariesContainer = librariesContainer;
+ final DownloadableLibraryDescription description = getDownloadableDescription(libraryDescription);
+ if (description != null) {
+ showCard("loading");
+ description.fetchVersions(new DownloadableFileSetVersions.FileSetVersionsCallback<FrameworkLibraryVersion>() {
+ @Override
+ public void onSuccess(@NotNull final List<? extends FrameworkLibraryVersion> versions) {
+ //noinspection SSBasedInspection
+ SwingUtilities.invokeLater(new Runnable() {
+ @Override
+ public void run() {
+ if (!myDisposed) {
+ showSettingsPanel(libraryDescription, baseDirectoryPath, versionFilter, showDoNotCreateOption, versions);
+ }
+ }
+ });
+ }
+ });
+ }
+ else {
+ showSettingsPanel(libraryDescription, baseDirectoryPath, versionFilter, showDoNotCreateOption,
+ new ArrayList<FrameworkLibraryVersion>());
+ }
+ }
+
+ @Nullable
+ private static DownloadableLibraryDescription getDownloadableDescription(CustomLibraryDescription libraryDescription) {
+ final DownloadableLibraryType type = libraryDescription.getDownloadableLibraryType();
+ if (type != null) return type.getLibraryDescription();
+ if (libraryDescription instanceof OldCustomLibraryDescription) {
+ return ((OldCustomLibraryDescription)libraryDescription).getDownloadableDescription();
+ }
+ return null;
+ }
+
+ private void showCard(final String editing) {
+ ((CardLayout)myRootPanel.getLayout()).show(myRootPanel, editing);
+ }
+
+ private void showSettingsPanel(CustomLibraryDescription libraryDescription,
+ String baseDirectoryPath,
+ FrameworkLibraryVersionFilter versionFilter,
+ boolean showDoNotCreateOption, final List<? extends FrameworkLibraryVersion> versions) {
+ //todo[nik] create mySettings only in apply() method
+ mySettings = new LibraryCompositionSettings(libraryDescription, baseDirectoryPath, versionFilter, versions);
+ Disposer.register(this, mySettings);
+ List<Library> libraries = calculateSuitableLibraries();
+
+ myButtonEnumModel = RadioButtonEnumModel.bindEnum(Choice.class, myButtonGroup);
+ myButtonEnumModel.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ updateState();
+ }
+ });
+
+ myDoNotCreateRadioButton.setVisible(showDoNotCreateOption);
+ myLibraryComboBoxModel = new SortedComboBoxModel<LibraryEditor>(new Comparator<LibraryEditor>() {
+ @Override
+ public int compare(LibraryEditor o1, LibraryEditor o2) {
+ final String name1 = o1.getName();
+ final String name2 = o2.getName();
+ return -StringUtil.notNullize(name1).compareToIgnoreCase(StringUtil.notNullize(name2));
+ }
+ });
+
+ for (Library library : libraries) {
+ ExistingLibraryEditor libraryEditor = myLibrariesContainer.getLibraryEditor(library);
+ if (libraryEditor == null) {
+ libraryEditor = mySettings.getOrCreateEditor(library);
+ }
+ myLibraryComboBoxModel.add(libraryEditor);
+ }
+ myExistingLibraryComboBox.setModel(myLibraryComboBoxModel);
+ if (libraries.isEmpty()) {
+ myLibraryComboBoxModel.add(null);
+ }
+ myExistingLibraryComboBox.setSelectedIndex(0);
+ myExistingLibraryComboBox.addItemListener(new ItemListener() {
+ @Override
+ public void itemStateChanged(ItemEvent e) {
+ if (e.getStateChange() == ItemEvent.SELECTED && e.getItem() != null) {
+ myButtonEnumModel.setSelected(Choice.USE_LIBRARY);
+ }
+ updateState();
+ }
+ });
+ myExistingLibraryComboBox.setRenderer(new ColoredListCellRenderer() {
+ @Override
+ protected void customizeCellRenderer(JList list, Object value, int index, boolean selected, boolean hasFocus) {
+ if (value == null) {
+ append("[No library selected]");
+ }
+ else if (value instanceof ExistingLibraryEditor) {
+ final Library library = ((ExistingLibraryEditor)value).getLibrary();
+ final boolean invalid = !((LibraryEx)library).getInvalidRootUrls(OrderRootType.CLASSES).isEmpty();
+ OrderEntryAppearanceService.getInstance().forLibrary(getProject(), library, invalid).customize(this);
+ }
+ else if (value instanceof NewLibraryEditor) {
+ setIcon(PlatformIcons.LIBRARY_ICON);
+ final String name = ((NewLibraryEditor)value).getName();
+ append(name != null ? name : "<unnamed>");
+ }
+ }
+ });
+
+ boolean canDownload = mySettings.getDownloadSettings() != null;
+ myDownloadRadioButton.setVisible(canDownload);
+ myButtonEnumModel.setSelected(libraries.isEmpty() && canDownload ? Choice.DOWNLOAD : Choice.USE_LIBRARY);
+
+ if (!canDownload && !showDoNotCreateOption) {
+ myUseLibraryRadioButton.setVisible(false);
+ myUseLibraryLabel.setVisible(true);
+ }
+
+ final Dimension minimumSize = new Dimension(-1, myMessageLabel.getFontMetrics(myMessageLabel.getFont()).getHeight() * 2);
+ myHiddenLabel.setMinimumSize(minimumSize);
+
+ myCreateButton.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ doCreate();
+ }
+ });
+ myConfigureButton.addActionListener(new ActionListener() {
+ public void actionPerformed(final ActionEvent e) {
+ doConfigure();
+ }
+ });
+ updateState();
+ showCard("editing");
+ }
+
+ private Project getProject() {
+ Project project = myLibrariesContainer.getProject();
+ if (project == null) {
+ project = ProjectManager.getInstance().getDefaultProject();
+ }
+ return project;
+ }
+
+ private void doConfigure() {
+ switch (myButtonEnumModel.getSelected()) {
+ case DOWNLOAD:
+ final LibraryDownloadSettings oldDownloadSettings = mySettings.getDownloadSettings();
+ LOG.assertTrue(oldDownloadSettings != null);
+ final LibraryDownloadSettings newDownloadSettings = DownloadingOptionsDialog.showDialog(myPanel, oldDownloadSettings,
+ mySettings.getCompatibleVersions(), true);
+ if (newDownloadSettings != null) {
+ mySettings.setDownloadSettings(newDownloadSettings);
+ }
+ break;
+
+ case USE_LIBRARY:
+ final Object item = myExistingLibraryComboBox.getSelectedItem();
+ if (item instanceof LibraryEditor) {
+ EditLibraryDialog dialog = new EditLibraryDialog(myPanel, mySettings, (LibraryEditor)item);
+ dialog.show();
+ if (item instanceof ExistingLibraryEditor) {
+ new WriteAction() {
+ protected void run(final Result result) {
+ ((ExistingLibraryEditor)item).commit();
+ }
+ }.execute();
+ }
+ }
+ break;
+
+ case SETUP_LIBRARY_LATER:
+ break;
+ }
+ updateState();
+ }
+
+ public void changeBaseDirectoryPath(@NotNull String directoryForLibrariesPath) {
+ if (mySettings != null) {
+ mySettings.changeBaseDirectoryPath(directoryForLibrariesPath);
+ updateState();
+ }
+ }
+
+ public void setVersionFilter(@NotNull FrameworkLibraryVersionFilter versionFilter) {
+ if (mySettings != null) {
+ mySettings.setVersionFilter(versionFilter);
+ updateState();
+ }
+ }
+
+ private void doCreate() {
+ final NewLibraryConfiguration libraryConfiguration = mySettings.getLibraryDescription().createNewLibrary(myPanel, getBaseDirectory());
+ if (libraryConfiguration != null) {
+ final NewLibraryEditor libraryEditor = new NewLibraryEditor(libraryConfiguration.getLibraryType(), libraryConfiguration.getProperties());
+ libraryEditor.setName(myLibrariesContainer.suggestUniqueLibraryName(libraryConfiguration.getDefaultLibraryName()));
+ libraryConfiguration.addRoots(libraryEditor);
+ if (myLibraryComboBoxModel.get(0) == null) {
+ myLibraryComboBoxModel.remove(0);
+ }
+ myLibraryComboBoxModel.add(libraryEditor);
+ myLibraryComboBoxModel.setSelectedItem(libraryEditor);
+ myButtonEnumModel.setSelected(Choice.USE_LIBRARY);
+ }
+ }
+
+ private List<Library> calculateSuitableLibraries() {
+ final CustomLibraryDescription description = mySettings.getLibraryDescription();
+ List<Library> suitableLibraries = new ArrayList<Library>();
+ for (Library library : myLibrariesContainer.getAllLibraries()) {
+ if (description instanceof OldCustomLibraryDescription &&
+ ((OldCustomLibraryDescription)description).isSuitableLibrary(library, myLibrariesContainer)
+ || LibraryPresentationManager.getInstance().isLibraryOfKind(library, myLibrariesContainer, description.getSuitableLibraryKinds())) {
+ suitableLibraries.add(library);
+ }
+ }
+ return suitableLibraries;
+ }
+
+ @Nullable
+ private VirtualFile getBaseDirectory() {
+ String path = mySettings.getBaseDirectoryPath();
+ VirtualFile dir = LocalFileSystem.getInstance().findFileByPath(path);
+ if (dir == null) {
+ path = path.substring(0, path.lastIndexOf('/'));
+ dir = LocalFileSystem.getInstance().findFileByPath(path);
+ }
+ return dir;
+ }
+
+ private void updateState() {
+ myMessageLabel.setIcon(null);
+ myConfigureButton.setVisible(true);
+ final LibraryDownloadSettings settings = mySettings.getDownloadSettings();
+ myDownloadRadioButton.setEnabled(settings != null);
+ myDownloadRadioButton.setVisible(settings != null);
+ if (!myDownloadRadioButton.isEnabled() && myDownloadRadioButton.isSelected() && myUseLibraryRadioButton.isVisible()) {
+ myUseLibraryRadioButton.setSelected(true);
+ }
+ String message = "";
+ boolean showConfigurePanel = true;
+ switch (myButtonEnumModel.getSelected()) {
+ case DOWNLOAD:
+ message = getDownloadFilesMessage();
+ break;
+ case USE_LIBRARY:
+ final Object item = myExistingLibraryComboBox.getSelectedItem();
+ if (item == null) {
+ myMessageLabel.setIcon(AllIcons.RunConfigurations.ConfigurationWarning);
+ message = "<b>Error:</b> library is not specified";
+ myConfigureButton.setVisible(false);
+ }
+ else if (item instanceof NewLibraryEditor) {
+ final LibraryEditor libraryEditor = (LibraryEditor)item;
+ message = IdeBundle.message("label.library.will.be.created.description.text", mySettings.getNewLibraryLevel(),
+ libraryEditor.getName(), libraryEditor.getFiles(OrderRootType.CLASSES).length);
+ }
+ else {
+ message = MessageFormat.format("<b>{0}</b> library will be used", ((ExistingLibraryEditor)item).getName());
+ }
+ break;
+ default:
+ showConfigurePanel = false;
+ }
+
+ //show the longest message on the hidden card to ensure that dialog won't jump if user selects another option
+ if (mySettings.getDownloadSettings() != null) {
+ myHiddenLabel.setText(getDownloadFilesMessage());
+ }
+ else {
+ myHiddenLabel.setText(IdeBundle.message("label.library.will.be.created.description.text", mySettings.getNewLibraryLevel(),
+ "name", 10));
+ }
+ ((CardLayout)myConfigurationPanel.getLayout()).show(myConfigurationPanel, showConfigurePanel ? "configure" : "empty");
+ myMessageLabel.setText("<html>" + message + "</html>");
+ }
+
+ private String getDownloadFilesMessage() {
+ final LibraryDownloadSettings downloadSettings = mySettings.getDownloadSettings();
+ if (downloadSettings == null) return "";
+
+ final String downloadPath = downloadSettings.getDirectoryForDownloadedLibrariesPath();
+ final String basePath = mySettings.getBaseDirectoryPath();
+ String path;
+ if (!StringUtil.isEmpty(basePath) && FileUtil.startsWith(downloadPath, basePath)) {
+ path = FileUtil.getRelativePath(basePath, downloadPath, '/');
+ }
+ else {
+ path = PathUtil.getFileName(downloadPath);
+ }
+ return MessageFormat.format("{0} {0, choice, 1#jar|2#jars} will be downloaded into <b>{1}</b> directory<br>" +
+ "{2} library <b>{3}</b> will be created",
+ downloadSettings.getSelectedDownloads().size(),
+ path,
+ downloadSettings.getLibraryLevel(),
+ downloadSettings.getLibraryName());
+ }
+
+ public LibraryCompositionSettings getSettings() {
+ return mySettings;
+ }
+
+ @Nullable
+ public LibraryCompositionSettings apply() {
+ if (mySettings == null) return null;
+
+ final Choice option = myButtonEnumModel.getSelected();
+ mySettings.setDownloadLibraries(option == Choice.DOWNLOAD);
+
+ final Object item = myExistingLibraryComboBox.getSelectedItem();
+ if (option == Choice.USE_LIBRARY && item instanceof ExistingLibraryEditor) {
+ mySettings.setSelectedExistingLibrary(((ExistingLibraryEditor)item).getLibrary());
+ }
+ else {
+ mySettings.setSelectedExistingLibrary(null);
+ }
+
+ if (option == Choice.USE_LIBRARY && item instanceof NewLibraryEditor) {
+ mySettings.setNewLibraryEditor((NewLibraryEditor)item);
+ }
+ else {
+ mySettings.setNewLibraryEditor(null);
+ }
+ return mySettings;
+ }
+
+ public JComponent getMainPanel() {
+ return myRootPanel;
+ }
+
+ @Override
+ public void dispose() {
+ myDisposed = true;
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/facet/impl/ui/libraries/RequiredLibrariesInfo.java b/java/idea-ui/src/com/intellij/facet/impl/ui/libraries/RequiredLibrariesInfo.java
new file mode 100644
index 0000000..2433ae6
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/facet/impl/ui/libraries/RequiredLibrariesInfo.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright 2000-2009 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.facet.impl.ui.libraries;
+
+import com.intellij.facet.ui.libraries.LibraryInfo;
+import com.intellij.openapi.roots.libraries.LibraryUtil;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.openapi.vfs.JarFileSystem;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.util.ArrayUtil;
+import org.apache.commons.codec.binary.Hex;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.security.MessageDigest;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * @author nik
+ */
+public class RequiredLibrariesInfo {
+ private final List<LibraryInfo> myLibraryInfos = new ArrayList<LibraryInfo>();
+
+ public RequiredLibrariesInfo(LibraryInfo... libs) {
+ myLibraryInfos.addAll(new ArrayList<LibraryInfo>(Arrays.asList(libs)));
+ }
+
+ @Nullable
+ public RequiredClassesNotFoundInfo checkLibraries(VirtualFile[] libraryFiles) {
+ return checkLibraries(Arrays.asList(libraryFiles));
+ }
+
+ @Nullable
+ public RequiredClassesNotFoundInfo checkLibraries(List<VirtualFile> libraryFiles) {
+ List<LibraryInfo> infos = new ArrayList<LibraryInfo>();
+ List<String> classes = new ArrayList<String>();
+
+ for (LibraryInfo info : myLibraryInfos) {
+ boolean notFound;
+ final String md5 = info.getMd5();
+ if (!StringUtil.isEmptyOrSpaces(md5)) {
+ notFound = true;
+ for (VirtualFile libraryFile : libraryFiles) {
+ final VirtualFile jarFile = JarFileSystem.getInstance().getVirtualFileForJar(libraryFile);
+ if (md5.equals(md5(jarFile))) {
+ notFound = false;
+ break;
+ }
+ }
+ } else {
+ notFound = false;
+ for (String className : info.getRequiredClasses()) {
+ if (!LibraryUtil.isClassAvailableInLibrary(libraryFiles, className)) {
+ classes.add(className);
+ notFound = true;
+ }
+ }
+ }
+
+ if (notFound) {
+ infos.add(info);
+ }
+ }
+ if (infos.isEmpty()) {
+ return null;
+ }
+ return new RequiredClassesNotFoundInfo(ArrayUtil.toStringArray(classes), infos.toArray(new LibraryInfo[infos.size()]));
+ }
+
+ @Nullable
+ public static String md5(@NotNull VirtualFile file) {
+ try {
+ MessageDigest md5 = MessageDigest.getInstance("MD5");
+ md5.update(file.contentsToByteArray());
+ final byte[] digest = md5.digest();
+
+ return new String(Hex.encodeHex(digest));
+ }
+ catch (Exception e) {
+ return null;
+ }
+ }
+
+ public static String getLibrariesPresentableText(final LibraryInfo[] libraryInfos) {
+ StringBuilder missedJarsText = new StringBuilder();
+ for (int i = 0; i < libraryInfos.length; i++) {
+ if (i > 0) {
+ missedJarsText.append(", ");
+ }
+
+ missedJarsText.append(libraryInfos[i].getName());
+ }
+ return missedJarsText.toString();
+ }
+
+ public static class RequiredClassesNotFoundInfo {
+ private final String[] myClassNames;
+ private final LibraryInfo[] myLibraryInfos;
+
+ public RequiredClassesNotFoundInfo(final String[] classNames, final LibraryInfo[] libraryInfos) {
+ myClassNames = classNames;
+ myLibraryInfos = libraryInfos;
+ }
+
+ public String[] getClassNames() {
+ return myClassNames;
+ }
+
+ public LibraryInfo[] getLibraryInfos() {
+ return myLibraryInfos;
+ }
+
+ public String getMissingJarsText() {
+ return getLibrariesPresentableText(myLibraryInfos);
+ }
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/facet/impl/ui/libraries/versions/VersionsComponent.java b/java/idea-ui/src/com/intellij/facet/impl/ui/libraries/versions/VersionsComponent.java
new file mode 100644
index 0000000..a558dfc
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/facet/impl/ui/libraries/versions/VersionsComponent.java
@@ -0,0 +1,286 @@
+/*
+ * Copyright 2000-2009 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.facet.impl.ui.libraries.versions;
+
+import com.intellij.facet.frameworks.LibrariesDownloadAssistant;
+import com.intellij.facet.frameworks.beans.Artifact;
+import com.intellij.facet.ui.libraries.FacetLibrariesValidator;
+import com.intellij.facet.ui.libraries.FacetLibrariesValidatorDescription;
+import com.intellij.facet.ui.libraries.LibraryInfo;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.roots.libraries.JarVersionDetectionUtil;
+import com.intellij.openapi.util.Pair;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.ui.CollectionComboBoxModel;
+import com.intellij.util.containers.HashSet;
+import com.intellij.util.containers.hash.HashMap;
+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.ActionEvent;
+import java.awt.event.ActionListener;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+public abstract class VersionsComponent {
+ private JPanel myMainPanel;
+ private static final String UNKNOWN_RI_NAME = "Unknown";
+
+ private final @NotNull Module myModule;
+ private final FacetLibrariesValidator myValidator;
+
+ private final ButtonGroup myButtonGroup = new ButtonGroup();
+
+ private final Map<String, Pair<JRadioButton, JComboBox>> myButtons = new HashMap<String, Pair<JRadioButton, JComboBox>>();
+
+ private Artifact myCurrentVersion = null;
+
+ public VersionsComponent(@NotNull final Module module, FacetLibrariesValidator validator) {
+ myModule = module;
+ myValidator = validator;
+ }
+
+ public JPanel getJComponent() {
+ if (myMainPanel == null) {
+ init();
+ }
+ return myMainPanel;
+ }
+
+ @Nullable
+ public Artifact getCurrentVersion() {
+ return myCurrentVersion;
+ }
+
+ private void init() {
+ myMainPanel = new JPanel(new GridBagLayout());
+
+ Set<String> referenceImplementations = getRIs();
+
+ if (referenceImplementations.size() == 1) {
+ String ri = referenceImplementations.iterator().next();
+ addSingletonReferenceImplementationUI(ri);
+ }
+ else {
+ for (String ri : referenceImplementations) {
+ addMultipleReferenceImplementationUI(ri);
+
+ if (myCurrentVersion == null) {
+ myCurrentVersion = getCurrentVersion(ri);
+ }
+ }
+
+ if (myCurrentVersion != null) {
+ Pair<JRadioButton, JComboBox> currentPair = myButtons.get(myCurrentVersion.getName());
+ if (currentPair != null) {
+ currentPair.first.setSelected(true);
+ currentPair.second.setSelectedItem(myCurrentVersion);
+ for (Pair<JRadioButton, JComboBox> buttonsPair : myButtons.values()) {
+ buttonsPair.second.setEnabled(buttonsPair == currentPair);
+ }
+ }
+ }
+ }
+ }
+
+ @Nullable
+ protected String getFacetDetectionClass(@NotNull String currentRI) {
+ return null;
+ }
+
+ @NotNull
+ protected abstract Artifact[] getLibraries();
+
+ @Nullable
+ private Artifact getCurrentVersion(@NotNull String currentRI) {
+ String detectionClass = getFacetDetectionClass(currentRI);
+ if (detectionClass != null) {
+ final String version = JarVersionDetectionUtil.detectJarVersion(detectionClass, myModule);
+ if (version != null) {
+ Artifact approximatedVersion = null;
+ for (Artifact info : getLibraries()) {
+ if (version.equals(info.getVersion())) {
+ return info;
+ }
+ if (version.contains(info.getVersion())) {
+ approximatedVersion = info;
+ }
+ }
+ return approximatedVersion;
+ }
+ }
+
+ return null;
+ }
+
+ private List<Artifact> getSupportedVersions(@NotNull String ri) {
+ List<Artifact> versions = new ArrayList<Artifact>();
+ for (Artifact version : getLibraries()) {
+ if (ri.equals(version.getName())) {
+ versions.add(version);
+ }
+ }
+
+ return versions;
+ }
+
+ private void addSingletonReferenceImplementationUI(@NotNull final String ri) {
+ JComboBox comboBox = createComboBox(ri);
+ addToPanel(new JLabel(ri), comboBox);
+ Artifact version = getCurrentVersion(ri);
+ if (version != null) {
+ comboBox.setSelectedItem(version);
+ }
+ }
+
+ private void addMultipleReferenceImplementationUI(@NotNull final String ri) {
+ final JRadioButton radioButton = createRadioButton(ri);
+ final JComboBox comboBox = createComboBox(ri);
+
+ comboBox.setEnabled(false);
+
+ addToPanel(radioButton, comboBox);
+
+ myButtons.put(ri, new Pair<JRadioButton, JComboBox>(radioButton, comboBox));
+ myButtonGroup.add(radioButton);
+ }
+
+ private void addToPanel(@NotNull JComponent first, @NotNull JComponent second) {
+ myMainPanel.add(first, new GridBagConstraints(0, GridBagConstraints.RELATIVE, 1, 1, 1, 0, GridBagConstraints.LINE_START,
+ GridBagConstraints.BOTH, new Insets(2, 2, 2, 2), 0, 0));
+ myMainPanel.add(second,
+ new GridBagConstraints(1, GridBagConstraints.RELATIVE, 1, 1, 1, 0, GridBagConstraints.LINE_END, GridBagConstraints.BOTH,
+ new Insets(2, 2, 2, 2), 0, 0));
+ }
+
+ private JRadioButton createRadioButton(final String ri) {
+ final JRadioButton radioButton = new JRadioButton(ri);
+ radioButton.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent actionEvent) {
+ for (Pair<JRadioButton, JComboBox> pair : myButtons.values()) {
+ if (pair.getFirst().equals(radioButton)) {
+ JComboBox comboBox = pair.second;
+ comboBox.setEnabled(true);
+
+ Artifact currentVersion = getCurrentVersion(ri);
+ if (currentVersion != null) {
+ comboBox.setSelectedItem(currentVersion);
+ }
+ else {
+ if (comboBox.getSelectedIndex() < 0) {
+ comboBox.setSelectedItem(getAppropriateVersion(getSupportedVersions(ri)));
+ }
+ else {
+ updateCurrentVersion(comboBox); // activate already selected
+ }
+ }
+ }
+ else {
+ pair.second.setEnabled(false);
+ }
+ }
+ }
+ });
+ return radioButton;
+ }
+
+ private JComboBox createComboBox(String ri) {
+ final JComboBox comboBox = new JComboBox();
+
+ List<Artifact> versions = getSupportedVersions(ri);
+ comboBox.setModel(new CollectionComboBoxModel(versions, null));
+
+ comboBox.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ updateCurrentVersion(comboBox);
+ }
+ });
+
+ return comboBox;
+ }
+
+ private void updateCurrentVersion(JComboBox comboBox) {
+ final Artifact versionInfo = getSelectedVersion(comboBox);
+
+ if (versionInfo != null) {
+ myCurrentVersion = versionInfo;
+ myValidator.setDescription(getFacetLibrariesValidatorDescription(versionInfo));
+ myValidator.setRequiredLibraries(getRequiredLibraries(versionInfo));
+ }
+ }
+
+ protected FacetLibrariesValidatorDescription getFacetLibrariesValidatorDescription(Artifact version) {
+ return new FacetLibrariesValidatorDescription(version.getVersion()) {
+ @NonNls
+ public String getDefaultLibraryName() {
+ if (myCurrentVersion != null) {
+ String ri = myCurrentVersion.getName();
+ String version = myCurrentVersion.getVersion();
+
+ return StringUtil.isEmptyOrSpaces(ri) ? version : ri + "." + version;
+ }
+
+ return super.getDefaultLibraryName();
+ }
+ };
+ }
+
+ @Nullable
+ private static Artifact getAppropriateVersion(List<Artifact> versions) {
+ return versions.size() > 0 ? versions.get(0) : null;
+ }
+
+ private static LibraryInfo[] getRequiredLibraries(Artifact version) {
+ final LibraryInfo[] infos = LibrariesDownloadAssistant.getLibraryInfos(version);
+
+ return infos == null ? LibraryInfo.EMPTY_ARRAY : infos;
+ }
+
+ @Nullable
+ private static Artifact getSelectedVersion(@NotNull JComboBox comboBox) {
+ final Object version = comboBox.getModel().getSelectedItem();
+ return version instanceof Artifact ? (Artifact)version : null;
+ }
+
+
+ public FacetLibrariesValidator getValidator() {
+ return myValidator;
+ }
+
+ @NotNull
+ public Module getModule() {
+ return myModule;
+ }
+
+ public Set<String> getRIs() {
+ Set<String> ris = new HashSet<String>();
+ for (Artifact version : getLibraries()) {
+ String ri = version.getName();
+ if (!StringUtil.isEmptyOrSpaces(ri)) {
+ ris.add(ri);
+ }
+ else {
+ ris.add(UNKNOWN_RI_NAME);
+ }
+ }
+ return ris;
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/facet/ui/libraries/FrameworkLibraryValidator.java b/java/idea-ui/src/com/intellij/facet/ui/libraries/FrameworkLibraryValidator.java
new file mode 100644
index 0000000..49b0d00
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/facet/ui/libraries/FrameworkLibraryValidator.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2000-2011 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.facet.ui.libraries;
+
+import com.intellij.facet.ui.FacetEditorValidator;
+
+/**
+ * @author nik
+ */
+public abstract class FrameworkLibraryValidator extends FacetEditorValidator {
+}
diff --git a/java/idea-ui/src/com/intellij/framework/FrameworkTypeEx.java b/java/idea-ui/src/com/intellij/framework/FrameworkTypeEx.java
new file mode 100644
index 0000000..ce58e53
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/framework/FrameworkTypeEx.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2000-2011 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.framework;
+
+import com.intellij.framework.addSupport.FrameworkSupportInModuleProvider;
+import com.intellij.openapi.extensions.ExtensionPointName;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * @author nik
+ */
+public abstract class FrameworkTypeEx extends FrameworkType {
+ public static final ExtensionPointName<FrameworkTypeEx> EP_NAME = ExtensionPointName.create("com.intellij.framework.type");
+
+ protected FrameworkTypeEx(@NotNull String id) {
+ super(id);
+ }
+
+ @NotNull
+ public abstract FrameworkSupportInModuleProvider createProvider();
+
+ @Nullable
+ public String getUnderlyingFrameworkTypeId() {
+ return null;
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/framework/addSupport/FrameworkSupportInModuleConfigurable.java b/java/idea-ui/src/com/intellij/framework/addSupport/FrameworkSupportInModuleConfigurable.java
new file mode 100644
index 0000000..b1ad3f6
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/framework/addSupport/FrameworkSupportInModuleConfigurable.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2000-2011 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.framework.addSupport;
+
+import com.intellij.framework.library.FrameworkLibraryVersionFilter;
+import com.intellij.openapi.Disposable;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.roots.ModifiableModelsProvider;
+import com.intellij.openapi.roots.ModifiableRootModel;
+import com.intellij.openapi.roots.ui.configuration.libraries.CustomLibraryDescription;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+
+/**
+ * @author nik
+ */
+public abstract class FrameworkSupportInModuleConfigurable implements Disposable {
+ @Nullable
+ public abstract JComponent createComponent();
+
+ public abstract void addSupport(@NotNull Module module, @NotNull ModifiableRootModel rootModel,
+ @NotNull ModifiableModelsProvider modifiableModelsProvider);
+
+ @Nullable
+ public CustomLibraryDescription createLibraryDescription() {
+ return null;
+ }
+
+ @NotNull
+ public FrameworkLibraryVersionFilter getLibraryVersionFilter() {
+ return FrameworkLibraryVersionFilter.ALL;
+ }
+
+ public void onFrameworkSelectionChanged(boolean selected) {
+ }
+
+ public boolean isOnlyLibraryAdded() {
+ return false;
+ }
+
+ public boolean isVisible() {
+ return true;
+ }
+
+ @Override
+ public void dispose() {
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/framework/addSupport/FrameworkSupportInModuleProvider.java b/java/idea-ui/src/com/intellij/framework/addSupport/FrameworkSupportInModuleProvider.java
new file mode 100644
index 0000000..190cee5
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/framework/addSupport/FrameworkSupportInModuleProvider.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2000-2011 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.framework.addSupport;
+
+import com.intellij.framework.FrameworkTypeEx;
+import com.intellij.ide.util.frameworkSupport.FrameworkSupportModel;
+import com.intellij.ide.util.projectWizard.ModuleBuilder;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.module.ModuleType;
+import com.intellij.openapi.roots.ui.configuration.FacetsProvider;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * @author nik
+ */
+public abstract class FrameworkSupportInModuleProvider {
+ @NotNull
+ public abstract FrameworkTypeEx getFrameworkType();
+
+ @NotNull
+ public abstract FrameworkSupportInModuleConfigurable createConfigurable(@NotNull FrameworkSupportModel model);
+
+ public abstract boolean isEnabledForModuleType(@NotNull ModuleType moduleType);
+
+ public boolean isEnabledForModuleBuilder(@NotNull ModuleBuilder builder) {
+ return isEnabledForModuleType(builder.getModuleType());
+ }
+
+ public boolean isSupportAlreadyAdded(@NotNull Module module, @NotNull FacetsProvider facetsProvider) {
+ return false;
+ }
+
+ public boolean canAddSupport(@NotNull Module module, @NotNull FacetsProvider facetsProvider) {
+ return !isSupportAlreadyAdded(module, facetsProvider);
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/framework/addSupport/impl/AddFrameworkSupportInProjectStructureAction.java b/java/idea-ui/src/com/intellij/framework/addSupport/impl/AddFrameworkSupportInProjectStructureAction.java
new file mode 100644
index 0000000..4732d07
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/framework/addSupport/impl/AddFrameworkSupportInProjectStructureAction.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright 2000-2011 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.framework.addSupport.impl;
+
+import com.intellij.facet.Facet;
+import com.intellij.facet.impl.ProjectFacetsConfigurator;
+import com.intellij.framework.FrameworkTypeEx;
+import com.intellij.framework.addSupport.FrameworkSupportInModuleProvider;
+import com.intellij.ide.util.frameworkSupport.FrameworkSupportUtil;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.module.ModuleType;
+import com.intellij.openapi.project.DumbAwareAction;
+import com.intellij.openapi.roots.IdeaModifiableModelsProvider;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.LibrariesContainer;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.LibrariesContainerFactory;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.ModuleStructureConfigurable;
+import com.intellij.openapi.vfs.VirtualFile;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * @author nik
+ */
+public class AddFrameworkSupportInProjectStructureAction extends DumbAwareAction {
+ private static final Logger LOG = Logger.getInstance("#com.intellij.framework.addSupport.impl.AddFrameworkSupportInProjectStructureAction");
+ private final FrameworkTypeEx myFrameworkType;
+ private final FrameworkSupportInModuleProvider myProvider;
+ @NotNull private final ModuleStructureConfigurable myModuleStructureConfigurable;
+
+ public AddFrameworkSupportInProjectStructureAction(@NotNull FrameworkTypeEx frameworkType, @NotNull FrameworkSupportInModuleProvider provider,
+ @NotNull ModuleStructureConfigurable moduleStructureConfigurable) {
+ super(frameworkType.getPresentableName(), "Add " + frameworkType.getPresentableName() + " support", frameworkType.getIcon());
+ myFrameworkType = frameworkType;
+ myProvider = provider;
+ myModuleStructureConfigurable = moduleStructureConfigurable;
+ }
+
+ @Override
+ public void update(AnActionEvent e) {
+ e.getPresentation().setVisible(isVisible());
+ }
+
+ private boolean isVisible() {
+ final Module module = getSelectedModule();
+ if (module == null || !myProvider.isEnabledForModuleType(ModuleType.get(module))) {
+ return false;
+ }
+ final ProjectFacetsConfigurator facetsProvider = myModuleStructureConfigurable.getFacetConfigurator();
+ if (!myProvider.canAddSupport(module, facetsProvider)) {
+ return false;
+ }
+
+ final String underlyingFrameworkTypeId = myFrameworkType.getUnderlyingFrameworkTypeId();
+ if (underlyingFrameworkTypeId == null) return true;
+
+ final FrameworkSupportInModuleProvider underlyingProvider = FrameworkSupportUtil.findProvider(underlyingFrameworkTypeId, FrameworkSupportUtil.getAllProviders());
+ if (underlyingProvider == null) {
+ LOG.error("framework not found by id " + underlyingFrameworkTypeId);
+ }
+ return underlyingProvider.isSupportAlreadyAdded(module, facetsProvider);
+ }
+
+ @Nullable
+ private Module getSelectedModule() {
+ final Object selected = myModuleStructureConfigurable.getSelectedObject();
+ if (selected instanceof Module) {
+ return (Module)selected;
+ }
+ final Facet facet = getSelectedFacet();
+ return facet != null ? facet.getModule() : null;
+ }
+
+ @Nullable
+ private Facet getSelectedFacet() {
+ final Object selected = myModuleStructureConfigurable.getSelectedObject();
+ if (selected instanceof Facet) {
+ return ((Facet)selected);
+ }
+ return null;
+ }
+
+ @Override
+ public void actionPerformed(AnActionEvent e) {
+ final Module module = getSelectedModule();
+ if (module == null) return;
+
+ final VirtualFile baseDir = module.getProject().getBaseDir();
+ final String path = baseDir != null ? baseDir.getPath() : "";
+ final LibrariesContainer librariesContainer = LibrariesContainerFactory.createContainer(myModuleStructureConfigurable.getContext());
+ new AddSupportForSingleFrameworkDialog(module, path, myFrameworkType, myProvider, librariesContainer, new IdeaModifiableModelsProvider()).show();
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/framework/addSupport/impl/AddSupportForSingleFrameworkDialog.java b/java/idea-ui/src/com/intellij/framework/addSupport/impl/AddSupportForSingleFrameworkDialog.java
new file mode 100644
index 0000000..81a0fb6
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/framework/addSupport/impl/AddSupportForSingleFrameworkDialog.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright 2000-2011 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.framework.addSupport.impl;
+
+import com.intellij.CommonBundle;
+import com.intellij.facet.impl.ui.libraries.LibraryCompositionSettings;
+import com.intellij.framework.FrameworkTypeEx;
+import com.intellij.framework.addSupport.FrameworkSupportInModuleConfigurable;
+import com.intellij.framework.addSupport.FrameworkSupportInModuleProvider;
+import com.intellij.ide.util.frameworkSupport.FrameworkSupportModelImpl;
+import com.intellij.ide.util.newProjectWizard.FrameworkSupportOptionsComponent;
+import com.intellij.ide.util.newProjectWizard.impl.FrameworkSupportModelBase;
+import com.intellij.openapi.application.Result;
+import com.intellij.openapi.application.WriteAction;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.project.ProjectBundle;
+import com.intellij.openapi.roots.LibraryOrderEntry;
+import com.intellij.openapi.roots.ModifiableModelsProvider;
+import com.intellij.openapi.roots.ModifiableRootModel;
+import com.intellij.openapi.roots.OrderEntry;
+import com.intellij.openapi.roots.libraries.Library;
+import com.intellij.openapi.roots.ui.configuration.libraries.CustomLibraryDescription;
+import com.intellij.openapi.roots.ui.configuration.libraries.LibraryPresentationManager;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.LibrariesContainer;
+import com.intellij.openapi.ui.DialogWrapper;
+import com.intellij.openapi.ui.Messages;
+import com.intellij.openapi.util.Disposer;
+import org.jetbrains.annotations.NotNull;
+
+import javax.swing.*;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author nik
+ */
+public class AddSupportForSingleFrameworkDialog extends DialogWrapper {
+ private final Module myModule;
+ private final FrameworkSupportInModuleConfigurable myConfigurable;
+ private final FrameworkSupportModelBase myModel;
+ private final FrameworkSupportOptionsComponent myComponent;
+ private final FrameworkTypeEx myFrameworkType;
+ private final ModifiableModelsProvider myModifiableModelsProvider;
+
+ public AddSupportForSingleFrameworkDialog(@NotNull Module module,
+ final @NotNull String contentRootPath,
+ FrameworkTypeEx frameworkType, @NotNull FrameworkSupportInModuleProvider provider,
+ @NotNull LibrariesContainer librariesContainer,
+ ModifiableModelsProvider modifiableModelsProvider) {
+ super(module.getProject(), true);
+ myFrameworkType = frameworkType;
+ myModifiableModelsProvider = modifiableModelsProvider;
+ setTitle(ProjectBundle.message("dialog.title.add.framework.0.support", frameworkType.getPresentableName()));
+ myModule = module;
+ myModel = new FrameworkSupportModelImpl(module.getProject(), contentRootPath, librariesContainer);
+ myConfigurable = provider.createConfigurable(myModel);
+ myComponent = new FrameworkSupportOptionsComponent(myModel, myModel.getLibrariesContainer(), myDisposable, myConfigurable, null);
+ Disposer.register(myDisposable, myConfigurable);
+ init();
+ }
+
+ protected void doOKAction() {
+ final LibraryCompositionSettings librarySettings = myComponent.getLibraryCompositionSettings();
+ if (librarySettings != null) {
+ final ModifiableRootModel modifiableModel = myModifiableModelsProvider.getModuleModifiableModel(myModule);
+ if (!askAndRemoveDuplicatedLibraryEntry(modifiableModel, librarySettings.getLibraryDescription())) {
+ if (myConfigurable.isOnlyLibraryAdded()) {
+ myModifiableModelsProvider.disposeModuleModifiableModel(modifiableModel);
+ return;
+ }
+ return;
+ }
+ myModifiableModelsProvider.commitModuleModifiableModel(modifiableModel);
+
+ final boolean downloaded = librarySettings.downloadFiles(getRootPane());
+ if (!downloaded) {
+ int answer = Messages.showYesNoDialog(getRootPane(),
+ ProjectBundle.message("warning.message.some.required.libraries.wasn.t.downloaded"),
+ CommonBundle.getWarningTitle(), Messages.getWarningIcon());
+ if (answer != 0) {
+ return;
+ }
+ }
+ }
+
+ new WriteAction() {
+ protected void run(final Result result) {
+ final ModifiableRootModel rootModel = myModifiableModelsProvider.getModuleModifiableModel(myModule);
+ if (librarySettings != null) {
+ librarySettings.addLibraries(rootModel, new ArrayList<Library>(), myModel.getLibrariesContainer());
+ }
+ myConfigurable.addSupport(myModule, rootModel, myModifiableModelsProvider);
+ myModifiableModelsProvider.commitModuleModifiableModel(rootModel);
+ }
+ }.execute();
+ super.doOKAction();
+ }
+
+ @Override
+ protected String getDimensionServiceKey() {
+ return "#com.intellij.framework.addSupport.AddSupportForSingleFrameworkDialog";
+ }
+
+ @Override
+ protected String getHelpId() {
+ return "reference.frameworks.support.dialog";//todo[nik]
+ }
+
+ protected JComponent createCenterPanel() {
+ return myComponent.getMainPanel();
+ }
+
+ private boolean askAndRemoveDuplicatedLibraryEntry(@NotNull ModifiableRootModel rootModel, @NotNull CustomLibraryDescription description) {
+ List<OrderEntry> existingEntries = new ArrayList<OrderEntry>();
+ final LibrariesContainer container = myModel.getLibrariesContainer();
+ for (OrderEntry entry : rootModel.getOrderEntries()) {
+ if (!(entry instanceof LibraryOrderEntry)) continue;
+ final Library library = ((LibraryOrderEntry)entry).getLibrary();
+ if (library == null) continue;
+
+ if (LibraryPresentationManager.getInstance().isLibraryOfKind(library, container, description.getSuitableLibraryKinds())) {
+ existingEntries.add(entry);
+ }
+ }
+
+ if (!existingEntries.isEmpty()) {
+ String message;
+ if (existingEntries.size() > 1) {
+ message = "There are already " + existingEntries.size() + " " + myFrameworkType.getPresentableName() + " libraries.\n Do you want to replace they?";
+ }
+ else {
+ final String name = existingEntries.get(0).getPresentableName();
+ message = "There is already a " + myFrameworkType.getPresentableName() + " library '" + name + "'.\n Do you want to replace it?";
+ }
+ final int result = Messages.showYesNoCancelDialog(rootModel.getProject(), message, "Library Already Exists",
+ "&Replace", "&Add", "&Cancel", null);
+ if (result == 0) {
+ for (OrderEntry entry : existingEntries) {
+ rootModel.removeOrderEntry(entry);
+ }
+ }
+ else if (result != 1) {
+ return false;
+ }
+ }
+ return true;
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/framework/library/DownloadableLibraryService.java b/java/idea-ui/src/com/intellij/framework/library/DownloadableLibraryService.java
new file mode 100644
index 0000000..13a0ced
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/framework/library/DownloadableLibraryService.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2000-2011 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.framework.library;
+
+import com.intellij.openapi.components.ServiceManager;
+import com.intellij.openapi.roots.libraries.ui.LibraryEditorComponent;
+import com.intellij.openapi.roots.libraries.ui.LibraryPropertiesEditor;
+import com.intellij.openapi.roots.ui.configuration.libraries.CustomLibraryDescription;
+import org.jetbrains.annotations.NotNull;
+
+import java.net.URL;
+
+/**
+ * @author nik
+ */
+public abstract class DownloadableLibraryService {
+ public static DownloadableLibraryService getInstance() {
+ return ServiceManager.getService(DownloadableLibraryService.class);
+ }
+
+ @NotNull
+ public abstract DownloadableLibraryDescription createLibraryDescription(@NotNull String groupId, @NotNull URL... localUrls);
+
+ @NotNull
+ public abstract CustomLibraryDescription createDescriptionForType(Class<? extends DownloadableLibraryType> typeClass);
+
+ @NotNull
+ public abstract LibraryPropertiesEditor createDownloadableLibraryEditor(@NotNull DownloadableLibraryDescription description,
+ @NotNull LibraryEditorComponent<LibraryVersionProperties> editorComponent,
+ @NotNull DownloadableLibraryType libraryType);
+}
diff --git a/java/idea-ui/src/com/intellij/framework/library/DownloadableLibraryType.java b/java/idea-ui/src/com/intellij/framework/library/DownloadableLibraryType.java
new file mode 100644
index 0000000..0db89aa
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/framework/library/DownloadableLibraryType.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2000-2012 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.framework.library;
+
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.roots.libraries.LibraryType;
+import com.intellij.openapi.roots.libraries.NewLibraryConfiguration;
+import com.intellij.openapi.roots.libraries.PersistentLibraryKind;
+import com.intellij.openapi.roots.libraries.ui.LibraryEditorComponent;
+import com.intellij.openapi.roots.libraries.ui.LibraryPropertiesEditor;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.openapi.vfs.VirtualFile;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+
+/**
+ * @author nik
+ */
+public class DownloadableLibraryType extends LibraryType<LibraryVersionProperties> {
+ private final String myLibraryCategoryName;
+ private final DownloadableLibraryDescription myLibraryDescription;
+
+ public DownloadableLibraryType(@NotNull PersistentLibraryKind<LibraryVersionProperties> kind, @NotNull String libraryCategoryName,
+ @NotNull DownloadableLibraryDescription description) {
+ super(kind);
+ myLibraryCategoryName = libraryCategoryName;
+ myLibraryDescription = description;
+ }
+
+ @Override
+ public String getCreateActionName() {
+ return null;
+ }
+
+ @Override
+ public NewLibraryConfiguration createNewLibrary(@NotNull JComponent parentComponent,
+ @Nullable VirtualFile contextDirectory,
+ @NotNull Project project) {
+ return null;
+ }
+
+ @NotNull
+ public DownloadableLibraryDescription getLibraryDescription() {
+ return myLibraryDescription;
+ }
+
+ public String getLibraryCategoryName() {
+ return myLibraryCategoryName;
+ }
+
+ @Override
+ public String getDescription(@NotNull LibraryVersionProperties properties) {
+ final String versionString = properties.getVersionString();
+ return StringUtil.capitalize(myLibraryCategoryName) + " library" + (versionString != null ? " of version " + versionString : "");
+ }
+
+ @Override
+ public LibraryPropertiesEditor createPropertiesEditor(@NotNull LibraryEditorComponent<LibraryVersionProperties> editorComponent) {
+ return DownloadableLibraryService.getInstance().createDownloadableLibraryEditor(myLibraryDescription, editorComponent, this);
+ }
+
+ @Override
+ public Icon getIcon() {
+ return null;
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/framework/library/DownloadableLibraryTypeBase.java b/java/idea-ui/src/com/intellij/framework/library/DownloadableLibraryTypeBase.java
new file mode 100644
index 0000000..a3b54aa
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/framework/library/DownloadableLibraryTypeBase.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2000-2012 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.framework.library;
+
+import com.intellij.openapi.roots.libraries.JarVersionDetectionUtil;
+import com.intellij.openapi.roots.libraries.LibraryUtil;
+import com.intellij.openapi.roots.libraries.PersistentLibraryKind;
+import com.intellij.openapi.vfs.VirtualFile;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import java.net.URL;
+import java.util.List;
+
+public abstract class DownloadableLibraryTypeBase extends DownloadableLibraryType {
+ private final Icon myIcon;
+
+ protected DownloadableLibraryTypeBase(@NotNull String libraryCategoryName,
+ @NotNull String libraryTypeId,
+ @NotNull String groupId,
+ @NotNull Icon icon,
+ @NotNull URL... localUrls) {
+ super(new PersistentLibraryKind<LibraryVersionProperties>(libraryTypeId) {
+ @NotNull
+ @Override
+ public LibraryVersionProperties createDefaultProperties() {
+ return new LibraryVersionProperties();
+ }
+ }, libraryCategoryName,
+ DownloadableLibraryService.getInstance().createLibraryDescription(groupId, localUrls));
+ myIcon = icon;
+ }
+
+ public Icon getIcon() {
+ return myIcon;
+ }
+
+ protected abstract String[] getDetectionClassNames();
+
+ @Override
+ public LibraryVersionProperties detect(@NotNull List<VirtualFile> classesRoots) {
+ for (String className : getDetectionClassNames()) {
+
+ final LibraryVersionProperties versionProperties = detectVersion(classesRoots, className);
+ if (versionProperties != null) return versionProperties;
+ }
+ return null;
+ }
+
+ @Nullable
+ private static LibraryVersionProperties detectVersion(List<VirtualFile> classesRoots, String detectionClass) {
+ if (!LibraryUtil.isClassAvailableInLibrary(classesRoots, detectionClass)) {
+ return null;
+ }
+ final String version = JarVersionDetectionUtil.detectJarVersion(detectionClass, classesRoots);
+ return new LibraryVersionProperties(version);
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/framework/library/FrameworkSupportWithLibrary.java b/java/idea-ui/src/com/intellij/framework/library/FrameworkSupportWithLibrary.java
new file mode 100644
index 0000000..9ca15ad
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/framework/library/FrameworkSupportWithLibrary.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2000-2011 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.framework.library;
+
+import com.intellij.openapi.roots.ui.configuration.libraries.CustomLibraryDescription;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * @author nik
+ */
+public interface FrameworkSupportWithLibrary {
+
+ @Nullable
+ CustomLibraryDescription createLibraryDescription();
+
+ boolean isLibraryOnly();
+}
diff --git a/java/idea-ui/src/com/intellij/framework/library/LibraryVersionProperties.java b/java/idea-ui/src/com/intellij/framework/library/LibraryVersionProperties.java
new file mode 100644
index 0000000..9532f18
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/framework/library/LibraryVersionProperties.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2000-2011 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.framework.library;
+
+import com.intellij.openapi.roots.libraries.LibraryProperties;
+import com.intellij.openapi.util.Comparing;
+import com.intellij.util.xmlb.annotations.Attribute;
+import org.jetbrains.annotations.Nullable;
+
+/**
+* @author nik
+*/
+public class LibraryVersionProperties extends LibraryProperties<LibraryVersionProperties> {
+ private String myVersionString;
+
+ public LibraryVersionProperties() {
+ }
+
+ public LibraryVersionProperties(@Nullable String versionString) {
+ myVersionString = versionString;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return obj instanceof LibraryVersionProperties && Comparing.equal(myVersionString, ((LibraryVersionProperties)obj).myVersionString);
+ }
+
+ @Override
+ public int hashCode() {
+ return Comparing.hashcode(myVersionString);
+ }
+
+ @Override
+ public LibraryVersionProperties getState() {
+ return this;
+ }
+
+ @Override
+ public void loadState(LibraryVersionProperties state) {
+ myVersionString = state.myVersionString;
+ }
+
+ @Attribute("version")
+ public String getVersionString() {
+ return myVersionString;
+ }
+
+ public void setVersionString(String version) {
+ myVersionString = version;
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/framework/library/UnderlyingFrameworkSupportProviderBase.java b/java/idea-ui/src/com/intellij/framework/library/UnderlyingFrameworkSupportProviderBase.java
new file mode 100644
index 0000000..cb6ce2f
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/framework/library/UnderlyingFrameworkSupportProviderBase.java
@@ -0,0 +1,65 @@
+package com.intellij.framework.library;
+
+import com.intellij.framework.FrameworkTypeEx;
+import com.intellij.framework.addSupport.FrameworkSupportInModuleConfigurable;
+import com.intellij.framework.addSupport.FrameworkSupportInModuleProvider;
+import com.intellij.ide.util.frameworkSupport.FrameworkSupportModel;
+import com.intellij.openapi.module.JavaModuleType;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.module.ModuleType;
+import com.intellij.openapi.roots.ModifiableModelsProvider;
+import com.intellij.openapi.roots.ModifiableRootModel;
+import com.intellij.openapi.roots.ui.configuration.libraries.CustomLibraryDescription;
+import org.jetbrains.annotations.NotNull;
+
+import javax.swing.*;
+
+public class UnderlyingFrameworkSupportProviderBase extends FrameworkSupportInModuleProvider {
+ private final FrameworkTypeEx myFrameworkType;
+ private final Class<? extends DownloadableLibraryType> myLibraryTypeClass;
+
+ public UnderlyingFrameworkSupportProviderBase(FrameworkTypeEx frameworkType, Class<? extends DownloadableLibraryType> libraryTypeClass) {
+ myFrameworkType = frameworkType;
+ myLibraryTypeClass = libraryTypeClass;
+ }
+
+ @NotNull
+ @Override
+ public FrameworkTypeEx getFrameworkType() {
+ return myFrameworkType;
+ }
+
+ @NotNull
+ @Override
+ public FrameworkSupportInModuleConfigurable createConfigurable(@NotNull final FrameworkSupportModel model) {
+ return new LibrarySupportConfigurable();
+ }
+
+ public boolean isEnabledForModuleType(@NotNull ModuleType moduleType) {
+ return moduleType instanceof JavaModuleType;
+ }
+
+ private class LibrarySupportConfigurable extends FrameworkSupportInModuleConfigurable {
+ @Override
+ public JComponent createComponent() {
+ return null;
+ }
+
+ @Override
+ public void addSupport(@NotNull Module module,
+ @NotNull ModifiableRootModel rootModel,
+ @NotNull ModifiableModelsProvider modifiableModelsProvider) {
+ }
+
+ @NotNull
+ @Override
+ public CustomLibraryDescription createLibraryDescription() {
+ return DownloadableLibraryService.getInstance().createDescriptionForType(myLibraryTypeClass);
+ }
+
+ @Override
+ public boolean isOnlyLibraryAdded() {
+ return true;
+ }
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/framework/library/impl/DownloadableLibraryPropertiesEditor.java b/java/idea-ui/src/com/intellij/framework/library/impl/DownloadableLibraryPropertiesEditor.java
new file mode 100644
index 0000000..040cc7b
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/framework/library/impl/DownloadableLibraryPropertiesEditor.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2000-2011 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.framework.library.impl;
+
+import com.intellij.facet.impl.ui.libraries.DownloadingOptionsDialog;
+import com.intellij.facet.impl.ui.libraries.LibraryDownloadSettings;
+import com.intellij.framework.library.DownloadableLibraryDescription;
+import com.intellij.framework.library.DownloadableLibraryType;
+import com.intellij.framework.library.FrameworkLibraryVersion;
+import com.intellij.framework.library.LibraryVersionProperties;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.application.ModalityState;
+import com.intellij.openapi.roots.libraries.ui.LibraryEditorComponent;
+import com.intellij.openapi.roots.ui.configuration.libraryEditor.LibraryPropertiesEditorBase;
+import com.intellij.openapi.roots.ui.configuration.libraryEditor.LibraryEditorBase;
+import com.intellij.openapi.roots.ui.configuration.libraryEditor.NewLibraryEditor;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.LibrariesContainer;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.util.download.DownloadableFileSetVersions;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.List;
+
+/**
+ * @author nik
+ */
+public class DownloadableLibraryPropertiesEditor extends LibraryPropertiesEditorBase<LibraryVersionProperties, DownloadableLibraryType> {
+ private final DownloadableLibraryDescription myDescription;
+ private final DownloadableLibraryType myLibraryType;
+ private String myCurrentVersionString;
+
+ public DownloadableLibraryPropertiesEditor(DownloadableLibraryDescription description,
+ LibraryEditorComponent<LibraryVersionProperties> editorComponent,
+ DownloadableLibraryType libraryType) {
+ super(editorComponent, libraryType, "Change &Version...");
+ myDescription = description;
+ myLibraryType = libraryType;
+ myCurrentVersionString = myEditorComponent.getProperties().getVersionString();
+ }
+
+ protected void edit() {
+ final ModalityState current = ModalityState.current();
+ myDescription.fetchVersions(new DownloadableFileSetVersions.FileSetVersionsCallback<FrameworkLibraryVersion>() {
+ @Override
+ public void onSuccess(@NotNull final List<? extends FrameworkLibraryVersion> versions) {
+ ApplicationManager.getApplication().invokeLater(new Runnable() {
+ @Override
+ public void run() {
+ String pathForDownloaded = "";
+ final VirtualFile existingRootDirectory = myEditorComponent.getExistingRootDirectory();
+ if (existingRootDirectory != null) {
+ pathForDownloaded = existingRootDirectory.getPath();
+ }
+ else {
+ final VirtualFile baseDir = myEditorComponent.getBaseDirectory();
+ if (baseDir != null) {
+ pathForDownloaded = baseDir.getPath() + "/lib";
+ }
+ }
+ final LibraryDownloadSettings initialSettings = new LibraryDownloadSettings(getCurrentVersion(versions), myLibraryType,
+ LibrariesContainer.LibraryLevel.PROJECT,
+ pathForDownloaded);
+ final LibraryDownloadSettings settings = DownloadingOptionsDialog.showDialog(getMainPanel(), initialSettings, versions, false);
+ if (settings != null) {
+ final NewLibraryEditor editor = settings.download(getMainPanel());
+ if (editor != null) {
+ final LibraryEditorBase target = (LibraryEditorBase)myEditorComponent.getLibraryEditor();
+ target.removeAllRoots();
+ myEditorComponent.renameLibrary(editor.getName());
+ target.setType(myLibraryType);
+ editor.applyTo(target);
+ myEditorComponent.updateRootsTree();
+ myCurrentVersionString = settings.getVersion().getVersionString();
+ setModified();
+ }
+ }
+ }
+ }, current);
+ }
+ });
+ }
+
+ private FrameworkLibraryVersion getCurrentVersion(List<? extends FrameworkLibraryVersion> versions) {
+ for (FrameworkLibraryVersion version : versions) {
+ if (version.getVersionString().equals(myCurrentVersionString)) {
+ return version;
+ }
+ }
+ return versions.get(0);
+ }
+
+ @Override
+ public void apply() {
+ myEditorComponent.getProperties().setVersionString(myCurrentVersionString);
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/framework/library/impl/DownloadableLibraryServiceImpl.java b/java/idea-ui/src/com/intellij/framework/library/impl/DownloadableLibraryServiceImpl.java
new file mode 100644
index 0000000..f5e3304
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/framework/library/impl/DownloadableLibraryServiceImpl.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2000-2011 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.framework.library.impl;
+
+import com.intellij.framework.library.DownloadableLibraryDescription;
+import com.intellij.framework.library.DownloadableLibraryService;
+import com.intellij.framework.library.DownloadableLibraryType;
+import com.intellij.framework.library.LibraryVersionProperties;
+import com.intellij.ide.util.frameworkSupport.CustomLibraryDescriptionImpl;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.roots.libraries.LibraryType;
+import com.intellij.openapi.roots.libraries.ui.LibraryEditorComponent;
+import com.intellij.openapi.roots.libraries.ui.LibraryPropertiesEditor;
+import com.intellij.openapi.roots.ui.configuration.libraries.CustomLibraryDescription;
+import org.jetbrains.annotations.NotNull;
+
+import java.net.URL;
+
+/**
+ * @author nik
+ */
+public class DownloadableLibraryServiceImpl extends DownloadableLibraryService {
+ private static final Logger LOG = Logger.getInstance("#com.intellij.framework.library.impl.DownloadableLibraryServiceImpl");
+
+ @NotNull
+ @Override
+ public DownloadableLibraryDescription createLibraryDescription(@NotNull String groupId, @NotNull URL... localUrls) {
+ return new LibraryVersionsFetcher(groupId, localUrls);
+ }
+
+ @NotNull
+ @Override
+ public CustomLibraryDescription createDescriptionForType(Class<? extends DownloadableLibraryType> typeClass) {
+ final DownloadableLibraryType libraryType = LibraryType.EP_NAME.findExtension(typeClass);
+ LOG.assertTrue(libraryType != null, typeClass);
+ return new CustomLibraryDescriptionImpl(libraryType);
+ }
+
+ @NotNull
+ @Override
+ public LibraryPropertiesEditor createDownloadableLibraryEditor(@NotNull DownloadableLibraryDescription description,
+ @NotNull LibraryEditorComponent<LibraryVersionProperties> editorComponent,
+ @NotNull DownloadableLibraryType libraryType) {
+ return new DownloadableLibraryPropertiesEditor(description, editorComponent, libraryType);
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/ide/RecentProjectsManager.java b/java/idea-ui/src/com/intellij/ide/RecentProjectsManager.java
new file mode 100644
index 0000000..0e30795
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/ide/RecentProjectsManager.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2000-2012 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.ide;
+
+import com.intellij.ide.impl.ProjectUtil;
+import com.intellij.openapi.components.RoamingType;
+import com.intellij.openapi.components.State;
+import com.intellij.openapi.components.Storage;
+import com.intellij.openapi.components.StoragePathMacros;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.project.ProjectManager;
+import com.intellij.util.messages.MessageBus;
+import org.jetbrains.annotations.NotNull;
+
+@State(
+ name = "RecentProjectsManager",
+ roamingType = RoamingType.DISABLED,
+ storages = {
+ @Storage(
+ file = StoragePathMacros.APP_CONFIG + "/other.xml"
+ )}
+)
+public class RecentProjectsManager extends RecentProjectsManagerBase {
+ public RecentProjectsManager(final ProjectManager projectManager, final MessageBus messageBus) {
+ super(projectManager, messageBus);
+ }
+
+ protected String getProjectPath(@NotNull Project project) {
+ return project.getPresentableUrl();
+ }
+
+ protected void doOpenProject(@NotNull String projectPath, Project projectToClose, boolean forceOpenInNewFrame) {
+ ProjectUtil.openProject(projectPath, projectToClose, forceOpenInNewFrame);
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/ide/actions/ImportModuleAction.java b/java/idea-ui/src/com/intellij/ide/actions/ImportModuleAction.java
new file mode 100644
index 0000000..d588951
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/ide/actions/ImportModuleAction.java
@@ -0,0 +1,192 @@
+/*
+ * Copyright 2000-2012 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.ide.actions;
+
+import com.intellij.ide.impl.NewProjectUtil;
+import com.intellij.ide.util.PropertiesComponent;
+import com.intellij.ide.util.newProjectWizard.AddModuleWizard;
+import com.intellij.ide.util.projectWizard.ProjectBuilder;
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.actionSystem.Presentation;
+import com.intellij.openapi.fileChooser.FileChooserDescriptor;
+import com.intellij.openapi.fileChooser.FileChooserDialog;
+import com.intellij.openapi.fileChooser.FileChooserFactory;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.module.ModuleManager;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.roots.ui.configuration.actions.NewModuleAction;
+import com.intellij.openapi.ui.Messages;
+import com.intellij.openapi.util.Condition;
+import com.intellij.openapi.vfs.LocalFileSystem;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.projectImport.ProjectImportProvider;
+import com.intellij.util.containers.ContainerUtil;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import java.awt.*;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * @author Dmitry Avdeev
+ * Date: 10/31/12
+ */
+public class ImportModuleAction extends AnAction {
+
+ private static final String LAST_IMPORTED_LOCATION = "last.imported.location";
+
+ @Override
+ public void actionPerformed(AnActionEvent e) {
+ final Project project = getEventProject(e);
+ doImport(project);
+ }
+
+ public static List<Module> doImport(Project project) {
+ AddModuleWizard wizard = selectFileAndCreateWizard(project, null);
+ if (wizard == null) {
+ return Collections.emptyList();
+ }
+ if (wizard.getStepCount() > 0 && !wizard.showAndGet()) return Collections.emptyList();
+
+ return createFromWizard(project, wizard);
+ }
+
+ public static List<Module> createFromWizard(Project project, AddModuleWizard wizard) {
+ if (project == null && wizard.getStepCount() > 0) {
+ Project newProject = NewProjectUtil.createFromWizard(wizard, project);
+ return newProject == null ? Collections.<Module>emptyList() : Arrays.asList(ModuleManager.getInstance(newProject).getModules());
+ }
+
+ final ProjectBuilder projectBuilder = wizard.getProjectBuilder();
+ try {
+ if (wizard.getStepCount() > 0) {
+ Module module = new NewModuleAction().createModuleFromWizard(project, null, wizard);
+ return Collections.singletonList(module);
+ }
+ else {
+ return projectBuilder.commit(project);
+ }
+ }
+ finally {
+ if (projectBuilder != null) {
+ projectBuilder.cleanup();
+ }
+ }
+ }
+
+ @Nullable
+ public static AddModuleWizard selectFileAndCreateWizard(final Project project, Component dialogParent) {
+ FileChooserDescriptor descriptor = new FileChooserDescriptor(true, true, true, true, false, false) {
+ FileChooserDescriptor myDelegate = new OpenProjectFileChooserDescriptor(true);
+ @Override
+ public Icon getIcon(VirtualFile file) {
+ Icon icon = myDelegate.getIcon(file);
+ return icon == null ? super.getIcon(file) : icon;
+ }
+ };
+ descriptor.setHideIgnored(false);
+ descriptor.setTitle("Select File or Directory to Import");
+ ProjectImportProvider[] providers = ProjectImportProvider.PROJECT_IMPORT_PROVIDER.getExtensions();
+ String description = getFileChooserDescription(project);
+ descriptor.setDescription(description);
+
+ FileChooserDialog chooser = FileChooserFactory.getInstance().createFileChooser(descriptor, project, dialogParent);
+ VirtualFile toSelect = null;
+ String lastLocation = PropertiesComponent.getInstance().getValue(LAST_IMPORTED_LOCATION);
+ if (lastLocation != null) {
+ toSelect = LocalFileSystem.getInstance().refreshAndFindFileByPath(lastLocation);
+ }
+ VirtualFile[] files = chooser.choose(toSelect, project);
+ if (files.length == 0) {
+ return null;
+ }
+
+ final VirtualFile file = files[0];
+ PropertiesComponent.getInstance().setValue(LAST_IMPORTED_LOCATION, file.getPath());
+ return createImportWizard(project, dialogParent, file, providers);
+ }
+
+ public static String getFileChooserDescription(final Project project) {
+ ProjectImportProvider[] providers = ProjectImportProvider.PROJECT_IMPORT_PROVIDER.getExtensions();
+ List<ProjectImportProvider> list = ContainerUtil.filter(providers, new Condition<ProjectImportProvider>() {
+ @Override
+ public boolean value(ProjectImportProvider provider) {
+ return project != null || provider.canCreateNewProject();
+ }
+ });
+ StringBuilder builder = new StringBuilder("<html>Select ");
+ boolean first = true;
+ if (list.size() > 1) {
+ for (ProjectImportProvider provider : list) {
+ String sample = provider.getFileSample();
+ if (sample != null) {
+ if (!first) {
+ builder.append(", <br>");
+ }
+ else {
+ first = false;
+ }
+ builder.append(sample);
+ }
+ }
+ }
+ builder.append(".</html>");
+ return builder.toString();
+ }
+
+ public static AddModuleWizard createImportWizard(final Project project,
+ Component dialogParent,
+ final VirtualFile file,
+ ProjectImportProvider... providers) {
+ List<ProjectImportProvider> available = ContainerUtil.filter(providers, new Condition<ProjectImportProvider>() {
+ @Override
+ public boolean value(ProjectImportProvider provider) {
+ return provider.canImport(file, project);
+ }
+ });
+ if (available.isEmpty()) {
+ Messages.showInfoMessage(project, "Cannot import anything from " + file.getPath(), "Cannot Import");
+ return null;
+ }
+
+ String path;
+ if (available.size() == 1) {
+ path = available.get(0).getPathToBeImported(file);
+ }
+ else {
+ path = ProjectImportProvider.getDefaultPath(file);
+ }
+
+ ProjectImportProvider[] availableProviders = available.toArray(new ProjectImportProvider[available.size()]);
+
+ return dialogParent == null ? new AddModuleWizard(project, path, availableProviders) : new AddModuleWizard(project, dialogParent, path, availableProviders);
+ }
+
+
+ @Override
+ public void update(AnActionEvent e) {
+ Presentation presentation = e.getPresentation();
+ presentation.setEnabled(getEventProject(e) != null);
+ }
+
+ @Override
+ public boolean isDumbAware() {
+ return true;
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/ide/actions/ImportProjectAction.java b/java/idea-ui/src/com/intellij/ide/actions/ImportProjectAction.java
new file mode 100644
index 0000000..2151bea
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/ide/actions/ImportProjectAction.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2000-2012 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.ide.actions;
+
+import com.intellij.openapi.actionSystem.AnActionEvent;
+
+/**
+ * @author Dmitry Avdeev
+ * Date: 11/6/12
+ */
+public class ImportProjectAction extends ImportModuleAction {
+
+
+ @Override
+ public void actionPerformed(AnActionEvent e) {
+ doImport(null);
+ }
+
+ @Override
+ public void update(AnActionEvent e) {
+
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/ide/actions/NewProjectAction.java b/java/idea-ui/src/com/intellij/ide/actions/NewProjectAction.java
new file mode 100644
index 0000000..7deeece
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/ide/actions/NewProjectAction.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2000-2011 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.ide.actions;
+
+import com.intellij.ide.impl.NewProjectUtil;
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.actionSystem.PlatformDataKeys;
+import com.intellij.openapi.project.DumbAware;
+
+public class NewProjectAction extends AnAction implements DumbAware {
+ public void actionPerformed(AnActionEvent e) {
+ NewProjectUtil.createNewProject(PlatformDataKeys.PROJECT.getData(e.getDataContext()), null);
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/ide/actions/OpenProjectAction.java b/java/idea-ui/src/com/intellij/ide/actions/OpenProjectAction.java
new file mode 100644
index 0000000..c02b1bc
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/ide/actions/OpenProjectAction.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2000-2012 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.ide.actions;
+
+import com.intellij.ide.IdeBundle;
+import com.intellij.ide.highlighter.ProjectFileType;
+import com.intellij.ide.impl.ProjectUtil;
+import com.intellij.openapi.actionSystem.ActionPlaces;
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.actionSystem.PlatformDataKeys;
+import com.intellij.openapi.extensions.Extensions;
+import com.intellij.openapi.fileChooser.FileChooser;
+import com.intellij.openapi.fileChooser.FileChooserDescriptor;
+import com.intellij.openapi.fileChooser.PathChooserDialog;
+import com.intellij.openapi.project.DumbAware;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.SystemInfo;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.openapi.vfs.VfsUtil;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.projectImport.ProjectOpenProcessor;
+import com.intellij.projectImport.ProjectOpenProcessorBase;
+import com.intellij.util.Consumer;
+
+import java.util.Collections;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Set;
+
+public class OpenProjectAction extends AnAction implements DumbAware {
+ public void actionPerformed(AnActionEvent e) {
+ final FileChooserDescriptor descriptor = new OpenProjectFileChooserDescriptor(true);
+ descriptor.setTitle(IdeBundle.message("title.open.project"));
+ final Set<String> extensions = new LinkedHashSet<String>();
+ extensions.add(ProjectFileType.DOT_DEFAULT_EXTENSION);
+ final ProjectOpenProcessor[] openProcessors = Extensions.getExtensions(ProjectOpenProcessor.EXTENSION_POINT_NAME);
+ for (ProjectOpenProcessor openProcessor : openProcessors) {
+ final String[] supportedExtensions = ((ProjectOpenProcessorBase)openProcessor).getSupportedExtensions();
+ if (supportedExtensions != null) {
+ Collections.addAll(extensions, supportedExtensions);
+ }
+ }
+ descriptor.setDescription(IdeBundle.message("filter.project.files", StringUtil.join(extensions, ", ")));
+
+ VirtualFile userHomeDir = null;
+ if (SystemInfo.isUnix) {
+ userHomeDir = VfsUtil.getUserHomeDir();
+ }
+
+ descriptor.putUserData(PathChooserDialog.PREFER_LAST_OVER_EXPLICIT, Boolean.TRUE);
+
+ final Project project = PlatformDataKeys.PROJECT.getData(e.getDataContext());
+ FileChooser.chooseFiles(descriptor, project, userHomeDir, new Consumer<List<VirtualFile>>() {
+ @Override
+ public void consume(final List<VirtualFile> files) {
+ if (files.size() == 1) {
+ ProjectUtil.openOrImport(files.get(0).getPath(), project, false);
+ }
+ }
+ });
+ }
+
+ @Override
+ public void update(AnActionEvent e) {
+ super.update(e);
+ e.getPresentation().setVisible(ActionPlaces.WELCOME_SCREEN.equals(e.getPlace()));
+ }
+}
\ No newline at end of file
diff --git a/java/idea-ui/src/com/intellij/ide/actions/ShowModulePropertiesAction.java b/java/idea-ui/src/com/intellij/ide/actions/ShowModulePropertiesAction.java
new file mode 100644
index 0000000..2fc6d6f
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/ide/actions/ShowModulePropertiesAction.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2000-2009 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.ide.actions;
+
+import com.intellij.openapi.actionSystem.*;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.roots.ui.configuration.ModulesConfigurator;
+
+/**
+ * @author Eugene Zhuravlev
+ * Date: Feb 8, 2004
+ */
+public class ShowModulePropertiesAction extends AnAction{
+
+ public void actionPerformed(AnActionEvent e) {
+ final DataContext dataContext = e.getDataContext();
+ final Project project = PlatformDataKeys.PROJECT.getData(dataContext);
+ if (project == null) {
+ return;
+ }
+ final Module module = LangDataKeys.MODULE_CONTEXT.getData(dataContext);
+ if (module == null) {
+ return;
+ }
+ ModulesConfigurator.showDialog(project, module.getName(), null);
+ }
+
+ public void update(AnActionEvent e) {
+ super.update(e);
+ final DataContext dataContext = e.getDataContext();
+ final Project project = PlatformDataKeys.PROJECT.getData(dataContext);
+ final Module module = LangDataKeys.MODULE_CONTEXT.getData(dataContext);
+ e.getPresentation().setVisible(project != null && module != null);
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/ide/actions/ShowStructureSettingsAction.java b/java/idea-ui/src/com/intellij/ide/actions/ShowStructureSettingsAction.java
new file mode 100644
index 0000000..777105f
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/ide/actions/ShowStructureSettingsAction.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2000-2009 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.ide.actions;
+
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.actionSystem.PlatformDataKeys;
+import com.intellij.openapi.options.ShowSettingsUtil;
+import com.intellij.openapi.options.newEditor.OptionsEditorDialog;
+import com.intellij.openapi.project.DumbAware;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.project.ProjectManager;
+import com.intellij.openapi.roots.ui.configuration.ProjectStructureConfigurable;
+
+public class ShowStructureSettingsAction extends AnAction implements DumbAware {
+ public void actionPerformed(AnActionEvent e) {
+ Project project = PlatformDataKeys.PROJECT.getData(e.getDataContext());
+ if (project == null) {
+ project = ProjectManager.getInstance().getDefaultProject();
+ }
+
+ ShowSettingsUtil.getInstance().editConfigurable(project, OptionsEditorDialog.DIMENSION_KEY, ProjectStructureConfigurable.getInstance(project));
+ }
+}
\ No newline at end of file
diff --git a/java/idea-ui/src/com/intellij/ide/actions/TemplateProjectSettingsGroup.java b/java/idea-ui/src/com/intellij/ide/actions/TemplateProjectSettingsGroup.java
new file mode 100644
index 0000000..13cd068
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/ide/actions/TemplateProjectSettingsGroup.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2000-2012 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.
+ */
+
+/*
+ * @author max
+ */
+package com.intellij.ide.actions;
+
+import com.intellij.execution.actions.EditRunConfigurationsAction;
+import com.intellij.icons.AllIcons;
+import com.intellij.openapi.actionSystem.DefaultActionGroup;
+import com.intellij.openapi.actionSystem.Presentation;
+
+public class TemplateProjectSettingsGroup extends DefaultActionGroup {
+ public TemplateProjectSettingsGroup() {
+ setPopup(true);
+ Presentation presentation = getTemplatePresentation();
+
+ presentation.setText("Project Defaults");
+ presentation.setIcon(AllIcons.General.TemplateProjectSettings);
+
+ add(new TemplateProjectPropertiesAction() {{
+ Presentation p = getTemplatePresentation();
+ p.setText("Settings");
+ p.setIcon(AllIcons.General.TemplateProjectSettings);
+ }});
+
+ add(new TemplateProjectStructureAction(){{
+ Presentation p = getTemplatePresentation();
+ p.setText("Project Structure");
+ p.setIcon(AllIcons.General.TemplateProjectStructure);
+ }});
+
+ add(new EditRunConfigurationsAction() {{
+ Presentation p = getTemplatePresentation();
+ p.setText("Run Configurations");
+ p.setIcon(AllIcons.General.CreateNewProjectfromExistingFiles);
+ }});
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/ide/actions/TemplateProjectStructureAction.java b/java/idea-ui/src/com/intellij/ide/actions/TemplateProjectStructureAction.java
new file mode 100644
index 0000000..0255cb0
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/ide/actions/TemplateProjectStructureAction.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2000-2009 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.ide.actions;
+
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.options.ShowSettingsUtil;
+import com.intellij.openapi.options.newEditor.OptionsEditorDialog;
+import com.intellij.openapi.project.DumbAware;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.project.ex.ProjectManagerEx;
+import com.intellij.openapi.roots.ui.configuration.ProjectStructureConfigurable;
+
+public class TemplateProjectStructureAction extends AnAction implements DumbAware {
+ public void actionPerformed(final AnActionEvent e) {
+ Project defaultProject = ProjectManagerEx.getInstanceEx().getDefaultProject();
+ ShowSettingsUtil.getInstance().editConfigurable(defaultProject, OptionsEditorDialog.DIMENSION_KEY, ProjectStructureConfigurable.getInstance(defaultProject));
+ }
+}
\ No newline at end of file
diff --git a/java/idea-ui/src/com/intellij/ide/impl/NewProjectUtil.java b/java/idea-ui/src/com/intellij/ide/impl/NewProjectUtil.java
new file mode 100644
index 0000000..3fd89d6
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/ide/impl/NewProjectUtil.java
@@ -0,0 +1,252 @@
+/*
+ * Copyright 2000-2012 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.
+ */
+
+/*
+ * @author max
+ */
+package com.intellij.ide.impl;
+
+import com.intellij.ide.GeneralSettings;
+import com.intellij.ide.util.newProjectWizard.AddModuleWizard;
+import com.intellij.ide.util.newProjectWizard.AddModuleWizardPro;
+import com.intellij.ide.util.projectWizard.ProjectBuilder;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.application.ModalityState;
+import com.intellij.openapi.command.CommandProcessor;
+import com.intellij.openapi.components.StorageScheme;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.progress.ProgressManager;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.project.ProjectBundle;
+import com.intellij.openapi.project.ProjectManager;
+import com.intellij.openapi.project.ex.ProjectManagerEx;
+import com.intellij.openapi.projectRoots.JavaSdk;
+import com.intellij.openapi.projectRoots.JavaSdkVersion;
+import com.intellij.openapi.projectRoots.Sdk;
+import com.intellij.openapi.roots.CompilerProjectExtension;
+import com.intellij.openapi.roots.LanguageLevelProjectExtension;
+import com.intellij.openapi.roots.ex.ProjectRootManagerEx;
+import com.intellij.openapi.roots.ui.configuration.ModulesConfigurator;
+import com.intellij.openapi.roots.ui.configuration.ModulesProvider;
+import com.intellij.openapi.startup.StartupManager;
+import com.intellij.openapi.ui.Messages;
+import com.intellij.openapi.util.SystemInfo;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.util.registry.Registry;
+import com.intellij.openapi.vfs.VfsUtilCore;
+import com.intellij.openapi.wm.*;
+import com.intellij.openapi.wm.ex.WindowManagerEx;
+import com.intellij.pom.java.LanguageLevel;
+import com.intellij.ui.mac.MacMainFrameDecorator;
+import com.intellij.util.ui.UIUtil;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import java.awt.*;
+import java.io.File;
+import java.io.IOException;
+
+public class NewProjectUtil {
+ private NewProjectUtil() {
+ }
+
+ public static void createNewProject(Project projectToClose, @Nullable final String defaultPath) {
+ final boolean proceed = ProgressManager.getInstance().runProcessWithProgressSynchronously(new Runnable() {
+ public void run() {
+ ProjectManager.getInstance().getDefaultProject(); //warm up components
+ }
+ }, ProjectBundle.message("project.new.wizard.progress.title"), true, null);
+ if (!proceed) return;
+ final AddModuleWizard dialog = Registry.is("new.project.wizard")
+ ? new AddModuleWizardPro(null, ModulesProvider.EMPTY_MODULES_PROVIDER, defaultPath)
+ : new AddModuleWizard(null, ModulesProvider.EMPTY_MODULES_PROVIDER, defaultPath);
+ dialog.show();
+ if (!dialog.isOK()) {
+ return;
+ }
+
+ createFromWizard(dialog, projectToClose);
+ }
+
+ public static Project createFromWizard(AddModuleWizard dialog, Project projectToClose) {
+ try {
+ return doCreate(dialog, projectToClose);
+ }
+ catch (final IOException e) {
+ UIUtil.invokeLaterIfNeeded(new Runnable() {
+ @Override
+ public void run() {
+ Messages.showErrorDialog(e.getMessage(), "Project Initialization Failed");
+ }
+ });
+ return null;
+ }
+ }
+
+ private static Project doCreate(final AddModuleWizard dialog, @Nullable Project projectToClose) throws IOException {
+ final ProjectManagerEx projectManager = ProjectManagerEx.getInstanceEx();
+ final String projectFilePath = dialog.getNewProjectFilePath();
+ final ProjectBuilder projectBuilder = dialog.getProjectBuilder();
+
+ try {
+ File projectDir = new File(projectFilePath).getParentFile();
+ LOG.assertTrue(projectDir != null, "Cannot create project in '" + projectFilePath + "': no parent file exists");
+ FileUtil.ensureExists(projectDir);
+ if (StorageScheme.DIRECTORY_BASED == dialog.getStorageScheme()) {
+ final File ideaDir = new File(projectFilePath, Project.DIRECTORY_STORE_FOLDER);
+ FileUtil.ensureExists(ideaDir);
+ }
+
+ final Project newProject;
+ if (projectBuilder == null || !projectBuilder.isUpdate()) {
+ String name = dialog.getProjectName();
+ newProject = projectBuilder == null
+ ? projectManager.newProject(name, projectFilePath, true, false)
+ : projectBuilder.createProject(name, projectFilePath);
+ }
+ else {
+ newProject = projectToClose;
+ }
+
+ if (newProject == null) return projectToClose;
+
+ final Sdk jdk = dialog.getNewProjectJdk();
+ if (jdk != null) {
+ CommandProcessor.getInstance().executeCommand(newProject, new Runnable() {
+ public void run() {
+ ApplicationManager.getApplication().runWriteAction(new Runnable() {
+ public void run() {
+ applyJdkToProject(newProject, jdk);
+ }
+ });
+ }
+ }, null, null);
+ }
+
+ final String compileOutput = dialog.getNewCompileOutput();
+ CommandProcessor.getInstance().executeCommand(newProject, new Runnable() {
+ public void run() {
+ ApplicationManager.getApplication().runWriteAction(new Runnable() {
+ public void run() {
+ String canonicalPath = compileOutput;
+ try {
+ canonicalPath = FileUtil.resolveShortWindowsName(compileOutput);
+ }
+ catch (IOException e) {
+ //file doesn't exist
+ }
+ canonicalPath = FileUtil.toSystemIndependentName(canonicalPath);
+ CompilerProjectExtension.getInstance(newProject).setCompilerOutputUrl(VfsUtilCore.pathToUrl(canonicalPath));
+ }
+ });
+ }
+ }, null, null);
+
+ if (!ApplicationManager.getApplication().isUnitTestMode()) {
+ newProject.save();
+ }
+
+ if (projectBuilder != null && !projectBuilder.validate(projectToClose, newProject)) {
+ return projectToClose;
+ }
+
+ if (newProject != projectToClose && !ApplicationManager.getApplication().isUnitTestMode()) {
+ closePreviousProject(projectToClose);
+ }
+
+ if (projectBuilder != null) {
+ projectBuilder.commit(newProject, null, ModulesProvider.EMPTY_MODULES_PROVIDER);
+ }
+
+ final boolean need2OpenProjectStructure = projectBuilder == null || projectBuilder.isOpenProjectSettingsAfter();
+ StartupManager.getInstance(newProject).registerPostStartupActivity(new Runnable() {
+ public void run() {
+ // ensure the dialog is shown after all startup activities are done
+ SwingUtilities.invokeLater(new Runnable() {
+ public void run() {
+ if (newProject.isDisposed() || ApplicationManager.getApplication().isUnitTestMode()) return;
+ if (need2OpenProjectStructure) {
+ ModulesConfigurator.showDialog(newProject, null, null);
+ }
+ ApplicationManager.getApplication().invokeLater(new Runnable() {
+ public void run() {
+ if (newProject.isDisposed()) return;
+ final ToolWindow toolWindow = ToolWindowManager.getInstance(newProject).getToolWindow(ToolWindowId.PROJECT_VIEW);
+ if (toolWindow != null) {
+ toolWindow.activate(null);
+ }
+ }
+ }, ModalityState.NON_MODAL);
+ }
+ });
+ }
+ });
+
+ if (newProject != projectToClose) {
+ ProjectUtil.updateLastProjectLocation(projectFilePath);
+
+ if (SystemInfo.isMacOSLion) {
+ IdeFocusManager instance = IdeFocusManager.findInstance();
+ IdeFrame lastFocusedFrame = instance.getLastFocusedFrame();
+ if (lastFocusedFrame != null) {
+ boolean fullScreen = WindowManagerEx.getInstanceEx().isFullScreen((Frame)lastFocusedFrame);
+ if (fullScreen) {
+ newProject.putUserData(MacMainFrameDecorator.SHOULD_OPEN_IN_FULLSCREEN, Boolean.TRUE);
+ }
+ }
+ }
+
+ projectManager.openProject(newProject);
+ }
+ if (!ApplicationManager.getApplication().isUnitTestMode()) {
+ newProject.save();
+ }
+ return newProject;
+ }
+ finally {
+ if (projectBuilder != null) {
+ projectBuilder.cleanup();
+ }
+ }
+ }
+
+ public static void applyJdkToProject(@NotNull Project project, @NotNull Sdk jdk) {
+ ProjectRootManagerEx rootManager = ProjectRootManagerEx.getInstanceEx(project);
+ rootManager.setProjectSdk(jdk);
+
+ JavaSdkVersion version = JavaSdk.getInstance().getVersion(jdk);
+ if (version != null) {
+ LanguageLevel level = version.getMaxLanguageLevel();
+ LanguageLevelProjectExtension ext = LanguageLevelProjectExtension.getInstance(project);
+ if (level.compareTo(ext.getLanguageLevel()) < 0) {
+ ext.setLanguageLevel(level);
+ }
+ }
+ }
+
+ public static void closePreviousProject(final Project projectToClose) {
+ Project[] openProjects = ProjectManager.getInstance().getOpenProjects();
+ if (openProjects.length > 0) {
+ int exitCode = ProjectUtil.confirmOpenNewProject(true);
+ if (exitCode == GeneralSettings.OPEN_PROJECT_SAME_WINDOW) {
+ ProjectUtil.closeAndDispose(projectToClose != null ? projectToClose : openProjects[openProjects.length - 1]);
+ }
+ }
+ }
+
+ private final static Logger LOG = Logger.getInstance(NewProjectUtil.class);
+}
diff --git a/java/idea-ui/src/com/intellij/ide/impl/ProjectStructureSelectInTarget.java b/java/idea-ui/src/com/intellij/ide/impl/ProjectStructureSelectInTarget.java
new file mode 100644
index 0000000..f4dba90
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/ide/impl/ProjectStructureSelectInTarget.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright 2000-2009 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.ide.impl;
+
+import com.intellij.facet.*;
+import com.intellij.ide.IdeBundle;
+import com.intellij.ide.SelectInContext;
+import com.intellij.ide.SelectInTarget;
+import com.intellij.ide.StandardTargetWeights;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.module.ModuleManager;
+import com.intellij.openapi.project.DumbAware;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.roots.OrderEntry;
+import com.intellij.openapi.roots.ProjectFileIndex;
+import com.intellij.openapi.roots.ProjectRootManager;
+import com.intellij.openapi.roots.libraries.LibraryUtil;
+import com.intellij.openapi.roots.ui.configuration.ModulesConfigurator;
+import com.intellij.openapi.roots.ui.configuration.ProjectSettingsService;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.openapi.vfs.WrappingVirtualFile;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.Collection;
+import java.util.Iterator;
+
+/**
+ * @author nik
+ */
+public class ProjectStructureSelectInTarget implements SelectInTarget, DumbAware {
+ public boolean canSelect(final SelectInContext context) {
+ final ProjectFileIndex fileIndex = ProjectRootManager.getInstance(context.getProject()).getFileIndex();
+ final VirtualFile file = context.getVirtualFile();
+ if (file instanceof WrappingVirtualFile) {
+ final Object o = ((WrappingVirtualFile)file).getWrappedObject(context.getProject());
+ return o instanceof Facet;
+ }
+ return fileIndex.isInContent(file) || fileIndex.isInLibraryClasses(file) || fileIndex.isInLibrarySource(file);
+ }
+
+ public void selectIn(final SelectInContext context, final boolean requestFocus) {
+ final Project project = context.getProject();
+ final VirtualFile file = context.getVirtualFile();
+
+ final Module module;
+ final Facet facet;
+ if (file instanceof WrappingVirtualFile) {
+ final Object o = ((WrappingVirtualFile)file).getWrappedObject(project);
+ facet = o instanceof Facet? (Facet)o : null;
+ module = facet == null? null : facet.getModule();
+ }
+ else {
+ final ProjectFileIndex fileIndex = ProjectRootManager.getInstance(project).getFileIndex();
+ module = fileIndex.getModuleForFile(file);
+ facet = fileIndex.isInSourceContent(file) ? null : findFacet(project, file);
+ }
+ if (module != null || facet != null) {
+ ApplicationManager.getApplication().invokeLater(new Runnable() {
+ public void run() {
+ if (facet != null) {
+ ModulesConfigurator.showFacetSettingsDialog(facet, null);
+ }
+ else {
+ ProjectSettingsService.getInstance(project).openModuleSettings(module);
+ }
+ }
+ });
+ return;
+ }
+
+ final OrderEntry orderEntry = LibraryUtil.findLibraryEntry(file, project);
+ if (orderEntry != null) {
+ ApplicationManager.getApplication().invokeLater(new Runnable() {
+ public void run() {
+ ProjectSettingsService.getInstance(project).openLibraryOrSdkSettings(orderEntry);
+ }
+ });
+ }
+ }
+
+ @Nullable
+ private static Facet findFacet(final @NotNull Project project, final @NotNull VirtualFile file) {
+ for (FacetTypeId id : FacetTypeRegistry.getInstance().getFacetTypeIds()) {
+ if (hasFacetWithRoots(project, id)) {
+ Facet facet = FacetFinder.getInstance(project).findFacet(file, id);
+ if (facet != null) {
+ return facet;
+ }
+ }
+ }
+ return null;
+ }
+
+ private static <F extends Facet> boolean hasFacetWithRoots(final @NotNull Project project, final @NotNull FacetTypeId<F> id) {
+ for (Module module : ModuleManager.getInstance(project).getModules()) {
+ Collection<? extends Facet> facets = FacetManager.getInstance(module).getFacetsByType(id);
+ Iterator<? extends Facet> iterator = facets.iterator();
+ if (iterator.hasNext()) {
+ return iterator.next() instanceof FacetRootsProvider;
+ }
+ }
+ return false;
+ }
+
+ public String getToolWindowId() {
+ return null;
+ }
+
+ public String getMinorViewId() {
+ return null;
+ }
+
+ public String toString() {
+ return IdeBundle.message("select.in.project.settings");
+ }
+
+ public float getWeight() {
+ return StandardTargetWeights.PROJECT_SETTINGS_WEIGHT;
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/ide/palette/impl/PaletteComponentList.java b/java/idea-ui/src/com/intellij/ide/palette/impl/PaletteComponentList.java
new file mode 100644
index 0000000..ea4f6de
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/ide/palette/impl/PaletteComponentList.java
@@ -0,0 +1,401 @@
+/*
+ * Copyright 2000-2012 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.ide.palette.impl;
+
+import com.intellij.ide.dnd.*;
+import com.intellij.ide.palette.PaletteGroup;
+import com.intellij.ide.palette.PaletteItem;
+import com.intellij.openapi.actionSystem.ActionGroup;
+import com.intellij.openapi.actionSystem.ActionManager;
+import com.intellij.openapi.actionSystem.ActionPlaces;
+import com.intellij.openapi.actionSystem.ActionPopupMenu;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.Pair;
+import com.intellij.ui.ColoredListCellRenderer;
+import com.intellij.ui.PopupHandler;
+import com.intellij.ui.components.JBList;
+import com.intellij.util.ui.PlatformColors;
+import com.intellij.util.ui.UIUtil;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import javax.swing.plaf.basic.BasicListUI;
+import java.awt.*;
+import java.awt.event.*;
+
+/**
+ * @author yole
+ */
+public class PaletteComponentList extends JBList {
+ private final Project myProject;
+ private final PaletteGroup myGroup;
+ private int myHoverIndex = -1;
+ private int myBeforeClickSelectedRow = -1;
+ private int myDropTargetIndex = -1;
+ private boolean myNeedClearSelection = false;
+
+ public PaletteComponentList(Project project, PaletteGroup group) {
+ myProject = project;
+ myGroup = group;
+ setModel(new AbstractListModel() {
+ public int getSize() {
+ return myGroup.getItems().length;
+ }
+
+ public Object getElementAt(int index) {
+ return myGroup.getItems() [index];
+ }
+ });
+
+ addMouseListener(new MouseAdapter() {
+ @Override public void mouseEntered(MouseEvent e) {
+ setHoverIndex(locationToIndex(e.getPoint()));
+ }
+
+ @Override public void mouseExited(MouseEvent e) {
+ setHoverIndex(-1);
+ }
+
+ @Override public void mousePressed(MouseEvent e) {
+ myNeedClearSelection = (SwingUtilities.isLeftMouseButton(e) &&
+ myBeforeClickSelectedRow >= 0 &&
+ locationToIndex(e.getPoint()) == myBeforeClickSelectedRow &&
+ !UIUtil.isControlKeyDown(e) && !e.isShiftDown());
+ }
+
+ @Override public void mouseReleased(MouseEvent e) {
+ if (SwingUtilities.isLeftMouseButton(e) &&
+ myBeforeClickSelectedRow >= 0 &&
+ locationToIndex(e.getPoint()) == myBeforeClickSelectedRow &&
+ !UIUtil.isControlKeyDown(e) && !e.isShiftDown() && myNeedClearSelection) {
+ clearSelection();
+ }
+ }
+ });
+
+ addMouseListener(new PopupHandler() {
+ public void invokePopup(final Component comp, final int x, final int y) {
+ requestFocusInWindow();
+ SwingUtilities.invokeLater(new Runnable() {
+ public void run() {
+ int index = locationToIndex(new Point(x, y));
+ PaletteItem[] items = myGroup.getItems();
+ if (index >= 0 && index < items.length) {
+ if (getSelectedIndex() != index) {
+ addSelectionInterval(index, index);
+ }
+ PaletteItem item = items [index];
+ ActionGroup group = item.getPopupActionGroup();
+ if (group != null) {
+ ActionPopupMenu popupMenu = ActionManager.getInstance().createActionPopupMenu(ActionPlaces.UNKNOWN, group);
+ popupMenu.getComponent().show(comp, x, y);
+ }
+ }
+ }
+ });
+ }
+ });
+
+ addMouseMotionListener(new MouseMotionAdapter() {
+ public void mouseMoved(MouseEvent e) {
+ setHoverIndex(locationToIndex(e.getPoint()));
+ }
+ });
+
+ addKeyListener(new KeyListener() {
+ public void keyPressed(KeyEvent e) {
+ PaletteManager.getInstance(myProject).notifyKeyEvent(e);
+ }
+
+ public void keyReleased(KeyEvent e) {
+ PaletteManager.getInstance(myProject).notifyKeyEvent(e);
+ }
+
+ public void keyTyped(KeyEvent e) {
+ PaletteManager.getInstance(myProject).notifyKeyEvent(e);
+ }
+ });
+
+ setCellRenderer(new ComponentCellRenderer());
+
+ setVisibleRowCount(0);
+ setLayoutOrientation(HORIZONTAL_WRAP);
+ setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
+
+ final DnDManager dndManager = DnDManager.getInstance();
+ dndManager.registerSource(new MyDnDSource(), this);
+ dndManager.registerTarget(new MyDnDTarget(), this);
+
+ initActions();
+ }
+
+ private void setHoverIndex(final int index) {
+ if (index != myHoverIndex) {
+ if (myHoverIndex >= 0) repaint(getCellBounds(myHoverIndex, myHoverIndex));
+ myHoverIndex = index;
+ if (myHoverIndex >= 0) repaint(getCellBounds(myHoverIndex, myHoverIndex));
+ }
+ }
+
+ private void setDropTargetIndex(final int index) {
+ if (index != myDropTargetIndex) {
+ myDropTargetIndex = index;
+ repaint();
+ }
+ }
+
+ @Override public void updateUI() {
+ setUI(new ComponentListUI());
+ invalidate();
+ }
+
+ private void initActions() {
+ @NonNls ActionMap map = getActionMap();
+ map.put( "selectPreviousRow", new MoveFocusAction( map.get( "selectPreviousRow" ), false ) );
+ map.put( "selectNextRow", new MoveFocusAction( map.get( "selectNextRow" ), true ) );
+ map.put( "selectPreviousColumn", new MoveFocusAction( new ChangeColumnAction( map.get( "selectPreviousColumn" ), false ), false ) );
+ map.put( "selectNextColumn", new MoveFocusAction( new ChangeColumnAction( map.get( "selectNextColumn" ), true ), true ) );
+ }
+
+ Integer myTempWidth;
+
+ public int getWidth () {
+ return (myTempWidth == null) ? super.getWidth () : myTempWidth.intValue ();
+ }
+
+ public int getPreferredHeight(final int width) {
+ myTempWidth = width;
+ try {
+ return getUI().getPreferredSize(this).height;
+ }
+ finally {
+ myTempWidth = null;
+ }
+ }
+
+ public void takeFocusFrom(PaletteGroupHeader paletteGroup, int indexToSelect) {
+ if (indexToSelect == -1) {
+ //this is not 'our' CategoryButton so we'll assume it's the one below this category list
+ indexToSelect = getModel().getSize() - 1;
+ }
+ else if (getModel().getSize() == 0) {
+ indexToSelect = -1;
+ }
+ requestFocus();
+ setSelectedIndex(indexToSelect);
+ if (indexToSelect >= 0) {
+ ensureIndexIsVisible(indexToSelect);
+ }
+ }
+
+ @Override protected void paintComponent(Graphics g) {
+ super.paintComponent(g);
+ if (myDropTargetIndex >= 0) {
+ int dropLineY;
+ Rectangle rc;
+ if (myDropTargetIndex == myGroup.getItems().length) {
+ rc = getCellBounds(myDropTargetIndex-1, myDropTargetIndex-1);
+ dropLineY = (int)rc.getMaxY()-1;
+ }
+ else {
+ rc = getCellBounds(myDropTargetIndex, myDropTargetIndex);
+ dropLineY = rc.y;
+ }
+ Graphics2D g2d = (Graphics2D) g;
+ g2d.setColor(PlatformColors.BLUE);
+ g2d.setStroke(new BasicStroke(2.0f));
+ g2d.drawLine(rc.x, dropLineY, rc.x+rc.width, dropLineY);
+ g2d.drawLine(rc.x, dropLineY-2, rc.x, dropLineY+2);
+ g2d.drawLine(rc.x+rc.width, dropLineY-2, rc.x+rc.width, dropLineY+2);
+ }
+ }
+
+ class ComponentListUI extends BasicListUI {
+ private ComponentListListener myListener;
+
+ @Override protected void updateLayoutState() {
+ super.updateLayoutState();
+
+ if (list.getLayoutOrientation() == JList.HORIZONTAL_WRAP) {
+ Insets insets = list.getInsets();
+ int listWidth = list.getWidth() - (insets.left + insets.right);
+ if (listWidth >= cellWidth) {
+ int columnCount = listWidth / cellWidth;
+ cellWidth = (columnCount == 0) ? 1 : listWidth / columnCount;
+ }
+ }
+ }
+
+ @Override protected void installListeners() {
+ myListener = new ComponentListListener();
+ addMouseListener(myListener);
+ super.installListeners();
+ }
+
+ @Override
+ protected void uninstallListeners() {
+ if (myListener != null) {
+ removeMouseListener(myListener);
+ }
+ super.uninstallListeners();
+ }
+
+ private class ComponentListListener extends MouseAdapter {
+ @Override public void mousePressed(MouseEvent e) {
+ myBeforeClickSelectedRow = list.getSelectedIndex();
+ }
+ }
+ }
+
+ private static class ComponentCellRenderer extends ColoredListCellRenderer {
+ protected void customizeCellRenderer(JList list, Object value, int index, boolean selected, boolean hasFocus) {
+ PaletteItem paletteItem = (PaletteItem) value;
+ clear();
+ paletteItem.customizeCellRenderer(this, selected, hasFocus);
+ }
+ }
+
+ private class MoveFocusAction extends AbstractAction {
+ private final Action defaultAction;
+ private final boolean focusNext;
+
+ public MoveFocusAction(Action defaultAction, boolean focusNext) {
+ this.defaultAction = defaultAction;
+ this.focusNext = focusNext;
+ }
+
+ public void actionPerformed(ActionEvent e) {
+ int selIndexBefore = getSelectedIndex();
+ defaultAction.actionPerformed(e);
+ int selIndexCurrent = getSelectedIndex();
+ if (selIndexBefore != selIndexCurrent) return;
+
+ if (focusNext && 0 == selIndexCurrent) return;
+
+ KeyboardFocusManager kfm = KeyboardFocusManager.getCurrentKeyboardFocusManager();
+ Container container = kfm.getCurrentFocusCycleRoot();
+ FocusTraversalPolicy policy = container.getFocusTraversalPolicy();
+ if (null == policy) policy = kfm.getDefaultFocusTraversalPolicy();
+ Component next = focusNext
+ ? policy.getComponentAfter(container, PaletteComponentList.this)
+ : policy.getComponentBefore(container, PaletteComponentList.this);
+ if (null != next && next instanceof PaletteGroupHeader) {
+ clearSelection();
+ next.requestFocus();
+ ((PaletteGroupHeader)next).scrollRectToVisible(next.getBounds());
+ }
+ }
+ }
+
+ private class ChangeColumnAction extends AbstractAction {
+ private final Action defaultAction;
+ private final boolean selectNext;
+
+ public ChangeColumnAction(Action defaultAction, boolean selectNext) {
+ this.defaultAction = defaultAction;
+ this.selectNext = selectNext;
+ }
+
+ public void actionPerformed(ActionEvent e) {
+ int selIndexBefore = getSelectedIndex();
+ defaultAction.actionPerformed(e);
+ int selIndexCurrent = getSelectedIndex();
+ if ((selectNext && selIndexBefore < selIndexCurrent) || (!selectNext && selIndexBefore > selIndexCurrent)) return;
+
+ if (selectNext) {
+ if (selIndexCurrent == selIndexBefore + 1) selIndexCurrent++;
+ if (selIndexCurrent < getModel().getSize() - 1) {
+ setSelectedIndex(selIndexCurrent + 1);
+ scrollRectToVisible(getCellBounds(selIndexCurrent + 1, selIndexCurrent + 1));
+ }
+ }
+ else {
+ if (selIndexCurrent > 0) {
+ setSelectedIndex(selIndexCurrent - 1);
+ scrollRectToVisible(getCellBounds(selIndexCurrent - 1, selIndexCurrent - 1));
+ }
+ }
+ }
+ }
+
+ private class MyDnDTarget implements DnDTarget {
+
+ public boolean update(DnDEvent aEvent) {
+ setHoverIndex(-1);
+ if (aEvent.getAttachedObject() instanceof PaletteItem) {
+ setDropTargetIndex(locationToTargetIndex(aEvent.getPoint()));
+ aEvent.setDropPossible(true);
+ }
+ else {
+ setDropTargetIndex(-1);
+ aEvent.setDropPossible(false);
+ }
+ return false;
+ }
+
+ public void drop(DnDEvent aEvent) {
+ setDropTargetIndex(-1);
+ if (aEvent.getAttachedObject() instanceof PaletteItem) {
+ int index = locationToTargetIndex(aEvent.getPoint());
+ if (index >= 0) {
+ myGroup.handleDrop(myProject, (PaletteItem) aEvent.getAttachedObject(), index);
+ }
+ }
+ }
+
+ public void cleanUpOnLeave() {
+ setDropTargetIndex(-1);
+ }
+
+ private int locationToTargetIndex(Point location) {
+ int row = locationToIndex(location);
+ if (row < 0) {
+ return -1;
+ }
+ Rectangle rc = getCellBounds(row, row);
+ return location.y < rc.getCenterY() ? row : row + 1;
+ }
+
+ public void updateDraggedImage(Image image, Point dropPoint, Point imageOffset) {
+ }
+ }
+
+ private class MyDnDSource implements DnDSource {
+ public boolean canStartDragging(DnDAction action, Point dragOrigin) {
+ int index = locationToIndex(dragOrigin);
+ return index >= 0 && myGroup.getItems() [index].startDragging() != null;
+ }
+
+ public DnDDragStartBean startDragging(DnDAction action, Point dragOrigin) {
+ int index = locationToIndex(dragOrigin);
+ if (index < 0) return null;
+ return myGroup.getItems() [index].startDragging();
+ }
+
+ @Nullable
+ public Pair<Image, Point> createDraggedImage(DnDAction action, Point dragOrigin) {
+ return null;
+ }
+
+ public void dragDropEnd() {
+ }
+
+ public void dropActionChanged(final int gestureModifiers) {
+ PaletteManager.getInstance(myProject).notifyDropActionChanged(gestureModifiers);
+ }
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/ide/palette/impl/PaletteContentWindow.java b/java/idea-ui/src/com/intellij/ide/palette/impl/PaletteContentWindow.java
new file mode 100644
index 0000000..b5aacca
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/ide/palette/impl/PaletteContentWindow.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright 2000-2009 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.ide.palette.impl;
+
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import java.awt.*;
+
+/**
+ * @author yole
+ */
+public class PaletteContentWindow extends JPanel implements Scrollable {
+ public PaletteContentWindow() {
+ setLayout(new PaletteLayoutManager());
+ }
+
+ public Dimension getPreferredScrollableViewportSize() {
+ return getPreferredSize();
+ }
+
+ public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction) {
+ return 20;
+ }
+
+ public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction) {
+ return 100;
+ }
+
+ public boolean getScrollableTracksViewportWidth() {
+ return true;
+ }
+
+ public boolean getScrollableTracksViewportHeight() {
+ return false;
+ }
+
+ @Nullable PaletteGroupHeader getLastGroupHeader() {
+ PaletteGroupHeader result = null;
+ for(Component comp: getComponents()) {
+ if (comp instanceof PaletteGroupHeader) {
+ result = (PaletteGroupHeader) comp;
+ }
+ }
+ return result;
+ }
+
+ private static class PaletteLayoutManager implements LayoutManager {
+
+ public void addLayoutComponent(String name, Component comp) {
+ }
+
+ public void layoutContainer(Container parent) {
+ int width = parent.getWidth();
+
+ int height = 0;
+ for(Component c: parent.getComponents()) {
+ if (c instanceof PaletteGroupHeader) {
+ PaletteGroupHeader groupHeader = (PaletteGroupHeader) c;
+ groupHeader.setLocation(0, height);
+ if (groupHeader.isVisible()) {
+ groupHeader.setSize(width, groupHeader.getPreferredSize().height);
+ height += groupHeader.getPreferredSize().height;
+ }
+ else {
+ groupHeader.setSize(0, 0);
+ }
+ if (groupHeader.isSelected() || !groupHeader.isVisible()) {
+ PaletteComponentList componentList = groupHeader.getComponentList();
+ componentList.setSize(width, componentList.getPreferredSize().height);
+ componentList.setLocation(0, height);
+ height += componentList.getHeight();
+ }
+ }
+ }
+ }
+
+ public Dimension minimumLayoutSize(Container parent) {
+ return new Dimension(0, 0);
+ }
+
+ public Dimension preferredLayoutSize(Container parent) {
+ int height = 0;
+ int width = parent.getWidth();
+ for(Component c: parent.getComponents()) {
+ if (c instanceof PaletteGroupHeader) {
+ PaletteGroupHeader groupHeader = (PaletteGroupHeader) c;
+ height += groupHeader.getHeight();
+ if (groupHeader.isSelected()) {
+ height += groupHeader.getComponentList().getPreferredHeight(width);
+ }
+ }
+ }
+ return new Dimension(10 /* not used - tracks viewports width*/, height);
+ }
+
+ public void removeLayoutComponent(Component comp) {
+ }
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/ide/palette/impl/PaletteGroupHeader.java b/java/idea-ui/src/com/intellij/ide/palette/impl/PaletteGroupHeader.java
new file mode 100644
index 0000000..72f197f
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/ide/palette/impl/PaletteGroupHeader.java
@@ -0,0 +1,211 @@
+/*
+ * Copyright 2000-2009 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.ide.palette.impl;
+
+import com.intellij.ide.dnd.DnDEvent;
+import com.intellij.ide.dnd.DnDManager;
+import com.intellij.ide.dnd.DnDTarget;
+import com.intellij.ide.palette.PaletteGroup;
+import com.intellij.ide.palette.PaletteItem;
+import com.intellij.openapi.actionSystem.*;
+import com.intellij.openapi.project.Project;
+import com.intellij.ui.PopupHandler;
+import com.intellij.util.ui.UIUtil;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import javax.swing.border.CompoundBorder;
+import java.awt.*;
+import java.awt.event.*;
+
+/**
+ * @author yole
+ */
+public class PaletteGroupHeader extends JCheckBox implements DataProvider {
+ private final PaletteWindow myPaletteWindow;
+ private PaletteComponentList myComponentList;
+ private final PaletteGroup myGroup;
+
+ public PaletteGroupHeader(PaletteWindow paletteWindow, PaletteGroup group) {
+ myPaletteWindow = paletteWindow;
+ myGroup = group;
+ if (group.getName() == null) {
+ setVisible(false);
+ }
+ else {
+ setText(group.getName());
+ }
+ setSelected(true);
+ addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ if (myComponentList != null) {
+ myComponentList.setVisible(isSelected());
+ }
+ }
+ });
+
+ addMouseListener(new PopupHandler() {
+ public void invokePopup(Component comp, int x, int y) {
+ myPaletteWindow.setLastFocusedGroup(PaletteGroupHeader.this);
+ showGroupPopupMenu(comp, x, y);
+ }
+ });
+
+ setIcon(UIUtil.getTreeCollapsedIcon());
+ setSelectedIcon(UIUtil.getTreeExpandedIcon());
+ setFont(getFont().deriveFont(Font.BOLD));
+ setFocusPainted(false);
+ setMargin(new Insets(0, 3, 0, 3));
+ setOpaque(true);
+ if (getBorder() instanceof CompoundBorder) { // from BasicLookAndFeel
+ Dimension pref = getPreferredSize();
+ pref.height -= 3;
+ setPreferredSize(pref);
+ }
+
+ DnDManager.getInstance().registerTarget(new DnDTarget() {
+ public boolean update(DnDEvent aEvent) {
+ setBorderPainted(true);
+ aEvent.setDropPossible(aEvent.getAttachedObject() instanceof PaletteItem);
+ return true;
+ }
+
+ public void drop(DnDEvent aEvent) {
+ setBorderPainted(false);
+ if (aEvent.getAttachedObject() instanceof PaletteItem) {
+ myGroup.handleDrop(myPaletteWindow.getProject(), (PaletteItem) aEvent.getAttachedObject(), -1);
+ }
+ }
+
+ public void cleanUpOnLeave() {
+ setBorderPainted(false);
+ }
+
+ public void updateDraggedImage(Image image, Point dropPoint, Point imageOffset) {
+ }
+ }, this);
+
+ addFocusListener(new FocusAdapter() {
+ @Override public void focusGained(FocusEvent e) {
+ myPaletteWindow.setLastFocusedGroup(PaletteGroupHeader.this);
+ }
+ });
+
+ initActions();
+ }
+
+ public void showGroupPopupMenu(final Component comp, final int x, final int y) {
+ ActionGroup group = myGroup.getPopupActionGroup();
+ if (group != null) {
+ ActionPopupMenu popupMenu = ActionManager.getInstance().createActionPopupMenu(ActionPlaces.UNKNOWN, group);
+ popupMenu.getComponent().show(comp, x, y);
+ }
+ }
+
+ private void initActions() {
+ @NonNls InputMap inputMap = getInputMap(WHEN_FOCUSED);
+ inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0, false), "moveFocusDown");
+ inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0, false), "moveFocusUp");
+ inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0, false), "collapse");
+ inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0, false), "expand");
+
+ @NonNls ActionMap actionMap = getActionMap();
+ actionMap.put("moveFocusDown", new MoveFocusAction(true));
+ actionMap.put("moveFocusUp", new MoveFocusAction(false));
+ actionMap.put("collapse", new ExpandAction(false));
+ actionMap.put("expand", new ExpandAction(true));
+ }
+
+ @Override public Color getBackground() {
+ if (isFocusOwner()) {
+ return UIUtil.getListSelectionBackground();
+ }
+ return super.getBackground();
+ }
+
+ @Override public Color getForeground() {
+ if (isFocusOwner()) {
+ return UIUtil.getListSelectionForeground();
+ }
+ return super.getForeground();
+ }
+
+ public void setComponentList(final PaletteComponentList componentList) {
+ myComponentList = componentList;
+ }
+
+ public PaletteComponentList getComponentList() {
+ return myComponentList;
+ }
+
+ public PaletteGroup getGroup() {
+ return myGroup;
+ }
+
+ @Nullable public Object getData(String dataId) {
+ Object data = myPaletteWindow.getData(dataId);
+ if (data != null) return data;
+ Project project = PlatformDataKeys.PROJECT.getData(myPaletteWindow);
+ return myGroup.getData(project, dataId);
+ }
+
+ private class MoveFocusAction extends AbstractAction {
+ private final boolean moveDown;
+
+ public MoveFocusAction(boolean moveDown) {
+ this.moveDown = moveDown;
+ }
+
+ public void actionPerformed(ActionEvent e) {
+ KeyboardFocusManager kfm = KeyboardFocusManager.getCurrentKeyboardFocusManager();
+ Container container = kfm.getCurrentFocusCycleRoot();
+ FocusTraversalPolicy policy = container.getFocusTraversalPolicy();
+ if (null == policy) policy = kfm.getDefaultFocusTraversalPolicy();
+ Component next =
+ moveDown ? policy.getComponentAfter(container, PaletteGroupHeader.this) : policy.getComponentBefore(container, PaletteGroupHeader.this);
+ if (null != next && next instanceof PaletteComponentList) {
+ final PaletteComponentList list = (PaletteComponentList)next;
+ if (list.getModel().getSize() != 0) {
+ list.takeFocusFrom(PaletteGroupHeader.this, list == myComponentList ? 0 : -1);
+ return;
+ }
+ else {
+ next = moveDown ? policy.getComponentAfter(container, next) : policy.getComponentBefore(container, next);
+ }
+ }
+ if (null != next && next instanceof PaletteGroupHeader) {
+ next.requestFocus();
+ }
+ }
+ }
+
+ private class ExpandAction extends AbstractAction {
+ private final boolean expand;
+
+ public ExpandAction(boolean expand) {
+ this.expand = expand;
+ }
+
+ public void actionPerformed(ActionEvent e) {
+ if (expand == isSelected()) return;
+ setSelected(expand);
+ if (myComponentList != null) {
+ myComponentList.setVisible(isSelected());
+ }
+ }
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/ide/palette/impl/PaletteManager.java b/java/idea-ui/src/com/intellij/ide/palette/impl/PaletteManager.java
new file mode 100644
index 0000000..b44eeb1
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/ide/palette/impl/PaletteManager.java
@@ -0,0 +1,220 @@
+/*
+ * Copyright 2000-2009 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.ide.palette.impl;
+
+import com.intellij.icons.AllIcons;
+import com.intellij.ide.IdeBundle;
+import com.intellij.ide.palette.PaletteDragEventListener;
+import com.intellij.ide.palette.PaletteItem;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.components.ProjectComponent;
+import com.intellij.openapi.fileEditor.FileEditorManager;
+import com.intellij.openapi.fileEditor.FileEditorManagerEvent;
+import com.intellij.openapi.fileEditor.FileEditorManagerListener;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.startup.StartupManager;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.openapi.wm.ToolWindow;
+import com.intellij.openapi.wm.ToolWindowAnchor;
+import com.intellij.openapi.wm.ToolWindowManager;
+import com.intellij.util.containers.ContainerUtil;
+import com.intellij.util.ui.update.MergingUpdateQueue;
+import com.intellij.util.ui.update.Update;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.event.ListSelectionEvent;
+import javax.swing.event.ListSelectionListener;
+import java.awt.event.KeyEvent;
+import java.awt.event.KeyListener;
+import java.util.List;
+
+/**
+ * @author yole
+ */
+public class PaletteManager implements ProjectComponent {
+ private final Project myProject;
+ private final FileEditorManager myFileEditorManager;
+ private PaletteWindow myPaletteWindow;
+ private ToolWindow myPaletteToolWindow;
+ private final List<KeyListener> myKeyListeners = ContainerUtil.createEmptyCOWList();
+ private final List<PaletteDragEventListener> myDragEventListeners = ContainerUtil.createEmptyCOWList();
+ private final List<ListSelectionListener> mySelectionListeners = ContainerUtil.createEmptyCOWList();
+
+ public PaletteManager(Project project, FileEditorManager fileEditorManager) {
+ myProject = project;
+ myFileEditorManager = fileEditorManager;
+ }
+
+ public void projectOpened() {
+ if (!ApplicationManager.getApplication().isHeadlessEnvironment()) {
+ StartupManager.getInstance(myProject).registerPostStartupActivity(new Runnable() {
+ public void run() {
+ myPaletteWindow = new PaletteWindow(myProject);
+ myPaletteToolWindow = ToolWindowManager.getInstance(myProject).
+ registerToolWindow(IdeBundle.message("toolwindow.palette"),
+ myPaletteWindow,
+ ToolWindowAnchor.RIGHT,
+ myProject,
+ true);
+ myPaletteToolWindow.setIcon(AllIcons.Toolwindows.ToolWindowPalette);
+ setContent();
+ final MyFileEditorManagerListener myListener = new MyFileEditorManagerListener();
+ myFileEditorManager.addFileEditorManagerListener(myListener, myProject);
+ }
+ });
+ }
+ }
+
+ public void projectClosed() {
+ if (myPaletteWindow != null) {
+ myPaletteWindow.dispose();
+ ToolWindowManager.getInstance(myProject).unregisterToolWindow(IdeBundle.message("toolwindow.palette"));
+ myPaletteWindow = null;
+ }
+ }
+
+ @NotNull
+ public String getComponentName() {
+ return "PaletteManager";
+ }
+
+ public void initComponent() {
+ }
+
+ public void disposeComponent() {
+ if (myPaletteWindow != null) {
+ myPaletteWindow.dispose();
+ }
+ }
+
+ public static PaletteManager getInstance(final Project project) {
+ return project.getComponent(PaletteManager.class);
+ }
+
+ public void clearActiveItem() {
+ if (myPaletteWindow != null) {
+ myPaletteWindow.clearActiveItem();
+ }
+ }
+
+ @Nullable
+ public PaletteItem getActiveItem() {
+ if (myPaletteWindow != null) {
+ return myPaletteWindow.getActiveItem();
+ }
+ return null;
+ }
+
+ @Nullable
+ public <T extends PaletteItem> T getActiveItem(Class<T> cls) {
+ PaletteItem item = getActiveItem();
+ if (item != null && item.getClass().isInstance(item)) {
+ //noinspection unchecked
+ return (T)item;
+ }
+ return null;
+ }
+
+ public void addKeyListener(KeyListener l) {
+ myKeyListeners.add(l);
+ }
+
+ public void removeKeyListener(KeyListener l) {
+ myKeyListeners.remove(l);
+ }
+
+ public void addDragEventListener(PaletteDragEventListener l) {
+ myDragEventListeners.add(l);
+ }
+
+ public void removeDragEventListener(PaletteDragEventListener l) {
+ myDragEventListeners.remove(l);
+ }
+
+ public void addSelectionListener(ListSelectionListener l) {
+ mySelectionListeners.add(l);
+ }
+
+ public void removeSelectionListener(ListSelectionListener l) {
+ mySelectionListeners.remove(l);
+ }
+
+ private final MergingUpdateQueue myQueue = new MergingUpdateQueue("palette", 200, true, null);
+
+ private void processFileEditorChange(@Nullable final VirtualFile selectedFile) {
+
+ myQueue.cancelAllUpdates();
+ myQueue.queue(new Update("update") {
+ public void run() {
+ if (myPaletteWindow == null) return;
+ myPaletteWindow.refreshPaletteIfChanged(selectedFile);
+ setContent();
+ }
+ });
+ }
+
+ private void setContent() {
+ if (myPaletteWindow.getActiveGroupCount() == 0) {
+ myPaletteToolWindow.setAvailable(false, null);
+ }
+ else {
+ myPaletteToolWindow.setAvailable(true, null);
+ myPaletteToolWindow.show(null);
+ }
+ }
+
+ void notifyKeyEvent(final KeyEvent e) {
+ for (KeyListener l : myKeyListeners) {
+ if (e.getID() == KeyEvent.KEY_PRESSED) {
+ l.keyPressed(e);
+ }
+ else if (e.getID() == KeyEvent.KEY_RELEASED) {
+ l.keyReleased(e);
+ }
+ else if (e.getID() == KeyEvent.KEY_TYPED) {
+ l.keyTyped(e);
+ }
+ }
+ }
+
+ void notifyDropActionChanged(int gestureModifiers) {
+ for (PaletteDragEventListener l : myDragEventListeners) {
+ l.dropActionChanged(gestureModifiers);
+ }
+ }
+
+ void notifySelectionChanged(final ListSelectionEvent event) {
+ for (ListSelectionListener l : mySelectionListeners) {
+ l.valueChanged(event);
+ }
+ }
+
+ private class MyFileEditorManagerListener implements FileEditorManagerListener {
+ public void fileOpened(FileEditorManager source, VirtualFile file) {
+ processFileEditorChange(file);
+ }
+
+ public void fileClosed(FileEditorManager source, VirtualFile file) {
+ processFileEditorChange(null);
+ }
+
+ public void selectionChanged(FileEditorManagerEvent event) {
+ processFileEditorChange(event.getNewFile());
+ }
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/ide/palette/impl/PaletteWindow.java b/java/idea-ui/src/com/intellij/ide/palette/impl/PaletteWindow.java
new file mode 100644
index 0000000..e18cc60
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/ide/palette/impl/PaletteWindow.java
@@ -0,0 +1,314 @@
+/*
+ * Copyright 2000-2009 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.ide.palette.impl;
+
+import com.intellij.ide.palette.PaletteGroup;
+import com.intellij.ide.palette.PaletteItem;
+import com.intellij.ide.palette.PaletteItemProvider;
+import com.intellij.openapi.actionSystem.*;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.extensions.Extensions;
+import com.intellij.openapi.fileEditor.FileEditorManager;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.ui.PopupHandler;
+import com.intellij.ui.ScrollPaneFactory;
+import com.intellij.ui.components.JBTabbedPane;
+import com.intellij.util.ArrayUtil;
+import com.intellij.util.containers.HashSet;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import javax.swing.event.ListSelectionEvent;
+import javax.swing.event.ListSelectionListener;
+import java.awt.*;
+import java.awt.dnd.DragSource;
+import java.awt.dnd.DragSourceAdapter;
+import java.awt.dnd.DragSourceDropEvent;
+import java.awt.dnd.DragSourceListener;
+import java.awt.event.KeyEvent;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.util.*;
+
+/**
+ * @author yole
+ */
+public class PaletteWindow extends JPanel implements DataProvider {
+ private final Project myProject;
+ private final ArrayList<PaletteGroupHeader> myGroupHeaders = new ArrayList<PaletteGroupHeader>();
+ private final PaletteItemProvider[] myProviders;
+ private final MyPropertyChangeListener myPropertyChangeListener = new MyPropertyChangeListener();
+ private final Set<PaletteGroup> myGroups = new HashSet<PaletteGroup>();
+ private final JTabbedPane myTabbedPane = new JBTabbedPane();
+ private final JScrollPane myScrollPane = ScrollPaneFactory.createScrollPane();
+ private final MyListSelectionListener myListSelectionListener = new MyListSelectionListener();
+ private PaletteGroupHeader myLastFocusedGroup;
+
+ @NonNls private static final String ourHelpID = "guiDesigner.uiTour.palette";
+ private PaletteManager myPaletteManager;
+
+ private final DragSourceListener myDragSourceListener = new DragSourceAdapter() {
+ @Override
+ public void dragDropEnd(DragSourceDropEvent event) {
+ Component component = event.getDragSourceContext().getComponent();
+ if (!event.getDropSuccess() &&
+ component instanceof PaletteComponentList &&
+ getRootPane() == ((JComponent)component).getRootPane()) {
+ clearActiveItem();
+ }
+ }
+ };
+
+ public PaletteWindow(Project project) {
+ myProject = project;
+ myPaletteManager = PaletteManager.getInstance(myProject);
+ myProviders = Extensions.getExtensions(PaletteItemProvider.EP_NAME, project);
+ for (PaletteItemProvider provider : myProviders) {
+ provider.addListener(myPropertyChangeListener);
+ }
+
+ setLayout(new GridLayout(1, 1));
+ myScrollPane.addMouseListener(new MyScrollPanePopupHandler());
+ myScrollPane.setBorder(null);
+ KeyStroke escStroke = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0);
+ new ClearActiveItemAction().registerCustomShortcutSet(new CustomShortcutSet(escStroke), myScrollPane);
+ refreshPalette();
+
+ if (!ApplicationManager.getApplication().isHeadlessEnvironment()) {
+ DragSource.getDefaultDragSource().addDragSourceListener(myDragSourceListener);
+ }
+ }
+
+ public void dispose() {
+ if (!ApplicationManager.getApplication().isHeadlessEnvironment()) {
+ DragSource.getDefaultDragSource().removeDragSourceListener(myDragSourceListener);
+ }
+ }
+
+ public void refreshPalette() {
+ refreshPalette(null);
+ }
+
+ public void refreshPalette(@Nullable VirtualFile selectedFile) {
+ for (PaletteGroupHeader groupHeader : myGroupHeaders) {
+ groupHeader.getComponentList().removeListSelectionListener(myListSelectionListener);
+ }
+ String[] oldTabNames = collectTabNames(myGroups);
+ myTabbedPane.removeAll();
+ myGroupHeaders.clear();
+ myGroups.clear();
+
+ final ArrayList<PaletteGroup> currentGroups = collectCurrentGroups(selectedFile);
+ String[] tabNames = collectTabNames(currentGroups);
+ if (tabNames.length == 1) {
+ if (oldTabNames.length != 1) {
+ remove(myTabbedPane);
+ add(myScrollPane);
+ }
+
+ PaletteContentWindow contentWindow = new PaletteContentWindow();
+ myScrollPane.getViewport().setView(contentWindow);
+
+ for (PaletteGroup group : currentGroups) {
+ addGroupToControl(group, contentWindow);
+ }
+
+ final JComponent view = (JComponent)myScrollPane.getViewport().getView();
+ if (view != null) {
+ view.revalidate();
+ for (Component component : view.getComponents()) {
+ ((JComponent)component).revalidate();
+ }
+ }
+ }
+ else {
+ if (oldTabNames.length <= 1) {
+ remove(myScrollPane);
+ add(myTabbedPane);
+ }
+ for (String tabName : tabNames) {
+ PaletteContentWindow contentWindow = new PaletteContentWindow();
+ JScrollPane scrollPane = ScrollPaneFactory.createScrollPane(contentWindow);
+ scrollPane.addMouseListener(new MyScrollPanePopupHandler());
+ myTabbedPane.add(tabName, scrollPane);
+ for (PaletteGroup group : currentGroups) {
+ if (group.getTabName().equals(tabName)) {
+ addGroupToControl(group, contentWindow);
+ }
+ }
+ }
+ myTabbedPane.revalidate();
+ }
+ }
+
+ private void addGroupToControl(PaletteGroup group, JComponent control) {
+ PaletteGroupHeader groupHeader = new PaletteGroupHeader(this, group);
+ myGroupHeaders.add(groupHeader);
+ myGroups.add(group);
+ control.add(groupHeader);
+ PaletteComponentList componentList = new PaletteComponentList(myProject, group);
+ control.add(componentList);
+ groupHeader.setComponentList(componentList);
+ componentList.addListSelectionListener(myListSelectionListener);
+ }
+
+ private static String[] collectTabNames(final Collection<PaletteGroup> groups) {
+ Set<String> result = new TreeSet<String>();
+ for (PaletteGroup group : groups) {
+ result.add(group.getTabName());
+ }
+ return ArrayUtil.toStringArray(result);
+ }
+
+ private ArrayList<PaletteGroup> collectCurrentGroups(@Nullable VirtualFile selectedFile) {
+ ArrayList<PaletteGroup> result = new ArrayList<PaletteGroup>();
+ if (selectedFile == null) {
+ VirtualFile[] editedFiles = FileEditorManager.getInstance(myProject).getSelectedFiles();
+ if (editedFiles.length > 0) {
+ selectedFile = editedFiles[0];
+ }
+ }
+ if (selectedFile != null) {
+ for (PaletteItemProvider provider : myProviders) {
+ PaletteGroup[] groups = provider.getActiveGroups(selectedFile);
+ Collections.addAll(result, groups);
+ }
+ }
+ return result;
+ }
+
+ public void refreshPaletteIfChanged(VirtualFile selectedFile) {
+ Set<PaletteGroup> currentGroups = new HashSet<PaletteGroup>(collectCurrentGroups(selectedFile));
+ if (!currentGroups.equals(myGroups)) {
+ refreshPalette(selectedFile);
+ }
+ }
+
+ public int getActiveGroupCount() {
+ return myGroups.size();
+ }
+
+ public void clearActiveItem() {
+ if (getActiveItem() == null) return;
+ for (PaletteGroupHeader group : myGroupHeaders) {
+ group.getComponentList().clearSelection();
+ }
+ ListSelectionEvent event = new ListSelectionEvent(this, -1, -1, false);
+ myPaletteManager.notifySelectionChanged(event);
+ }
+
+ @Nullable
+ public PaletteItem getActiveItem() {
+ for (PaletteGroupHeader groupHeader : myGroupHeaders) {
+ if (groupHeader.isSelected() && groupHeader.getComponentList().getSelectedValue() != null) {
+ return (PaletteItem)groupHeader.getComponentList().getSelectedValue();
+ }
+ }
+ return null;
+ }
+
+ @Nullable
+ public Object getData(String dataId) {
+ if (PlatformDataKeys.HELP_ID.is(dataId)) {
+ return ourHelpID;
+ }
+ if (PlatformDataKeys.PROJECT.is(dataId)) {
+ return myProject;
+ }
+ PaletteItem item = getActiveItem();
+ if (item != null) {
+ Object data = item.getData(myProject, dataId);
+ if (data != null) return data;
+ }
+ for (PaletteGroupHeader groupHeader : myGroupHeaders) {
+ if ((groupHeader.isSelected() && groupHeader.getComponentList().getSelectedValue() != null) || groupHeader == myLastFocusedGroup) {
+ return groupHeader.getGroup().getData(myProject, dataId);
+ }
+ }
+ final int tabCount = collectTabNames(myGroups).length;
+ if (tabCount > 0) {
+ JScrollPane activeScrollPane;
+ if (tabCount == 1) {
+ activeScrollPane = myScrollPane;
+ }
+ else {
+ activeScrollPane = (JScrollPane)myTabbedPane.getSelectedComponent();
+ }
+ PaletteContentWindow activeContentWindow = (PaletteContentWindow)activeScrollPane.getViewport().getView();
+ PaletteGroupHeader groupHeader = activeContentWindow.getLastGroupHeader();
+ if (groupHeader != null) {
+ return groupHeader.getGroup().getData(myProject, dataId);
+ }
+ }
+ return null;
+ }
+
+ public Project getProject() {
+ return myProject;
+ }
+
+ void setLastFocusedGroup(final PaletteGroupHeader focusedGroup) {
+ myLastFocusedGroup = focusedGroup;
+ for (PaletteGroupHeader group : myGroupHeaders) {
+ group.getComponentList().clearSelection();
+ }
+ }
+
+ private class MyListSelectionListener implements ListSelectionListener {
+ public void valueChanged(ListSelectionEvent e) {
+ PaletteComponentList sourceList = (PaletteComponentList)e.getSource();
+ for (int i = e.getFirstIndex(); i <= e.getLastIndex(); i++) {
+ if (sourceList.isSelectedIndex(i)) {
+ // selection is being added
+ for (PaletteGroupHeader group : myGroupHeaders) {
+ if (group.getComponentList() != sourceList) {
+ group.getComponentList().clearSelection();
+ }
+ }
+ break;
+ }
+ }
+ myPaletteManager.notifySelectionChanged(e);
+ }
+ }
+
+ private class MyPropertyChangeListener implements PropertyChangeListener {
+ public void propertyChange(PropertyChangeEvent evt) {
+ refreshPalette();
+ }
+ }
+
+ private static class MyScrollPanePopupHandler extends PopupHandler {
+ public void invokePopup(Component comp, int x, int y) {
+ JScrollPane scrollPane = (JScrollPane)comp;
+ PaletteContentWindow contentWindow = (PaletteContentWindow)scrollPane.getViewport().getView();
+ if (contentWindow != null) {
+ PaletteGroupHeader groupHeader = contentWindow.getLastGroupHeader();
+ if (groupHeader != null) {
+ groupHeader.showGroupPopupMenu(comp, x, y);
+ }
+ }
+ }
+ }
+
+ private class ClearActiveItemAction extends AnAction {
+ public void actionPerformed(AnActionEvent e) {
+ clearActiveItem();
+ }
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/ide/projectView/actions/CreateLibraryFromFilesDialog.java b/java/idea-ui/src/com/intellij/ide/projectView/actions/CreateLibraryFromFilesDialog.java
new file mode 100644
index 0000000..a75eb5d
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/ide/projectView/actions/CreateLibraryFromFilesDialog.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright 2000-2011 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.ide.projectView.actions;
+
+import com.intellij.openapi.application.AccessToken;
+import com.intellij.openapi.application.WriteAction;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.module.ModuleUtil;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.roots.ModifiableRootModel;
+import com.intellij.openapi.roots.ModuleRootManager;
+import com.intellij.openapi.roots.ModuleRootModificationUtil;
+import com.intellij.openapi.roots.impl.libraries.LibraryTypeServiceImpl;
+import com.intellij.openapi.roots.libraries.Library;
+import com.intellij.openapi.roots.libraries.ui.OrderRoot;
+import com.intellij.openapi.roots.ui.configuration.ModulesCombobox;
+import com.intellij.openapi.roots.ui.configuration.libraryEditor.LibraryNameAndLevelPanel;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.LibrariesContainer;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.LibrariesContainerFactory;
+import com.intellij.openapi.ui.DialogWrapper;
+import com.intellij.openapi.vfs.JarFileSystem;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.ui.DocumentAdapter;
+import com.intellij.util.ui.FormBuilder;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import javax.swing.event.DocumentEvent;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * @author nik
+ */
+public class CreateLibraryFromFilesDialog extends DialogWrapper {
+ private final LibraryNameAndLevelPanel myNameAndLevelPanel;
+ private final ModulesCombobox myModulesCombobox;
+ private final Project myProject;
+ private final List<OrderRoot> myRoots;
+ private final JPanel myPanel;
+ private final String myDefaultName;
+
+ public CreateLibraryFromFilesDialog(@NotNull Project project, @NotNull List<OrderRoot> roots) {
+ super(project, true);
+ setTitle("Create Library");
+ myProject = project;
+ myRoots = roots;
+ final FormBuilder builder = LibraryNameAndLevelPanel.createFormBuilder();
+ myDefaultName =
+ LibrariesContainerFactory.createContainer(project).suggestUniqueLibraryName(LibraryTypeServiceImpl.suggestLibraryName(roots));
+ myNameAndLevelPanel = new LibraryNameAndLevelPanel(builder, myDefaultName, Arrays.asList(LibrariesContainer.LibraryLevel.values()),
+ LibrariesContainer.LibraryLevel.PROJECT);
+ myNameAndLevelPanel.setDefaultName(myDefaultName);
+ myModulesCombobox = new ModulesCombobox();
+ myModulesCombobox.fillModules(myProject);
+ myModulesCombobox.setSelectedModule(findModule(roots));
+ builder.addLabeledComponent("&Add to module:", myModulesCombobox);
+ myPanel = builder.getPanel();
+ myNameAndLevelPanel.getLibraryNameField().selectAll();
+ myNameAndLevelPanel.getLevelComboBox().addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ onLevelChanged();
+ }
+ });
+ myNameAndLevelPanel.getLibraryNameField().getDocument().addDocumentListener(new DocumentAdapter() {
+ @Override
+ protected void textChanged(DocumentEvent e) {
+ updateOkAction();
+ }
+ });
+ init();
+ }
+
+ private void updateOkAction() {
+ setOKActionEnabled(!myNameAndLevelPanel.getLibraryName().isEmpty()
+ || myNameAndLevelPanel.getLibraryLevel() == LibrariesContainer.LibraryLevel.MODULE && myRoots.size() == 1);
+ }
+
+ private void onLevelChanged() {
+ if (myNameAndLevelPanel.getLibraryLevel() == LibrariesContainer.LibraryLevel.MODULE) {
+ myNameAndLevelPanel.setDefaultName(myRoots.size() == 1 ? "" : myDefaultName);
+ }
+ else {
+ myNameAndLevelPanel.setDefaultName(myDefaultName);
+ if (myNameAndLevelPanel.getLibraryName().isEmpty()) {
+ myNameAndLevelPanel.getLibraryNameField().setText(myDefaultName);
+ }
+ }
+ updateOkAction();
+ }
+
+ @Nullable
+ private Module findModule(List<OrderRoot> roots) {
+ for (OrderRoot root : roots) {
+ Module module = null;
+ final VirtualFile local = JarFileSystem.getInstance().getVirtualFileForJar(root.getFile());
+ if (local != null) {
+ module = ModuleUtil.findModuleForFile(local, myProject);
+ }
+ if (module == null) {
+ module = ModuleUtil.findModuleForFile(root.getFile(), myProject);
+ }
+ if (module != null) {
+ return module;
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public JComponent getPreferredFocusedComponent() {
+ return myNameAndLevelPanel.getLibraryNameField();
+ }
+
+ @Override
+ protected void doOKAction() {
+ final LibrariesContainer.LibraryLevel level = myNameAndLevelPanel.getLibraryLevel();
+ AccessToken token = WriteAction.start();
+ try {
+ final Module module = myModulesCombobox.getSelectedModule();
+ final String libraryName = myNameAndLevelPanel.getLibraryName();
+ if (level == LibrariesContainer.LibraryLevel.MODULE) {
+ final ModifiableRootModel modifiableModel = ModuleRootManager.getInstance(module).getModifiableModel();
+ LibrariesContainerFactory.createContainer(modifiableModel).createLibrary(libraryName, level, myRoots);
+ modifiableModel.commit();
+ }
+ else {
+ final Library library = LibrariesContainerFactory.createContainer(myProject).createLibrary(libraryName, level, myRoots);
+ if (module != null) {
+ ModuleRootModificationUtil.addDependency(module, library);
+ }
+ }
+ }
+ finally {
+ token.finish();
+ }
+ super.doOKAction();
+ }
+
+ @Override
+ protected JComponent createCenterPanel() {
+ return myPanel;
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/ide/projectView/actions/MarkLibraryRootAction.java b/java/idea-ui/src/com/intellij/ide/projectView/actions/MarkLibraryRootAction.java
new file mode 100644
index 0000000..8da8e87
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/ide/projectView/actions/MarkLibraryRootAction.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2000-2011 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.ide.projectView.actions;
+
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.actionSystem.PlatformDataKeys;
+import com.intellij.openapi.module.ModuleManager;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.roots.ProjectFileIndex;
+import com.intellij.openapi.roots.ProjectRootManager;
+import com.intellij.openapi.roots.libraries.ui.OrderRoot;
+import com.intellij.openapi.roots.libraries.ui.impl.RootDetectionUtil;
+import com.intellij.openapi.roots.ui.configuration.libraryEditor.DefaultLibraryRootsComponentDescriptor;
+import com.intellij.openapi.vfs.JarFileSystem;
+import com.intellij.openapi.vfs.VirtualFile;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * @author nik
+ */
+public class MarkLibraryRootAction extends AnAction {
+ @Override
+ public void actionPerformed(AnActionEvent e) {
+ final Project project = getEventProject(e);
+ if (project == null) return;
+
+ final List<VirtualFile> jars = getRoots(e);
+ if (jars.isEmpty()) return;
+
+ final List<OrderRoot> roots = RootDetectionUtil.detectRoots(jars, null, project, new DefaultLibraryRootsComponentDescriptor());
+ new CreateLibraryFromFilesDialog(project, roots).show();
+ }
+
+ @NotNull
+ private static List<VirtualFile> getRoots(AnActionEvent e) {
+ final Project project = getEventProject(e);
+ final VirtualFile[] files = e.getData(PlatformDataKeys.VIRTUAL_FILE_ARRAY);
+ if (project == null || files == null || files.length == 0) return Collections.emptyList();
+
+ List<VirtualFile> roots = new ArrayList<VirtualFile>();
+ for (VirtualFile file : files) {
+ if (file.isDirectory()) {
+ roots.add(file);
+ }
+ else {
+ final VirtualFile root = JarFileSystem.getInstance().getJarRootForLocalFile(file);
+ if (root != null) {
+ roots.add(root);
+ }
+ }
+ }
+ return roots;
+ }
+
+ @Override
+ public void update(AnActionEvent e) {
+ final Project project = getEventProject(e);
+ boolean visible = false;
+ if (project != null && ModuleManager.getInstance(project).getModules().length > 0) {
+ final ProjectFileIndex fileIndex = ProjectRootManager.getInstance(project).getFileIndex();
+ for (VirtualFile root : getRoots(e)) {
+ if (!root.isInLocalFileSystem() && !fileIndex.isInLibraryClasses(root)) {
+ visible = true;
+ break;
+ }
+ if (root.isInLocalFileSystem() && root.isDirectory()) {
+ for (VirtualFile child : root.getChildren()) {
+ final VirtualFile jarRoot = JarFileSystem.getInstance().getJarRootForLocalFile(child);
+ if (jarRoot != null && !fileIndex.isInLibraryClasses(child)) {
+ visible = true;
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ e.getPresentation().setVisible(visible);
+ e.getPresentation().setEnabled(visible);
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/ide/util/frameworkSupport/AddFrameworkSupportAction.java b/java/idea-ui/src/com/intellij/ide/util/frameworkSupport/AddFrameworkSupportAction.java
new file mode 100644
index 0000000..529f2df
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/ide/util/frameworkSupport/AddFrameworkSupportAction.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2000-2009 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.ide.util.frameworkSupport;
+
+import com.intellij.openapi.actionSystem.ActionPlaces;
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.actionSystem.LangDataKeys;
+import com.intellij.openapi.module.Module;
+
+/**
+ * @author nik
+ */
+public class AddFrameworkSupportAction extends AnAction {
+ public void actionPerformed(final AnActionEvent e) {
+ Module module = e.getData(LangDataKeys.MODULE_CONTEXT);
+ if (module == null) return;
+
+ AddFrameworkSupportDialog dialog = AddFrameworkSupportDialog.createDialog(module);
+ if (dialog != null) {
+ dialog.show();
+ }
+ }
+
+ public void update(final AnActionEvent e) {
+ Module module = e.getData(LangDataKeys.MODULE_CONTEXT);
+ boolean enable = module != null && AddFrameworkSupportDialog.isAvailable(module);
+ if (ActionPlaces.isPopupPlace(e.getPlace())) {
+ e.getPresentation().setVisible(enable);
+ }
+ else {
+ e.getPresentation().setEnabled(enable);
+ }
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/ide/util/frameworkSupport/AddFrameworkSupportDialog.java b/java/idea-ui/src/com/intellij/ide/util/frameworkSupport/AddFrameworkSupportDialog.java
new file mode 100644
index 0000000..cfea5df
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/ide/util/frameworkSupport/AddFrameworkSupportDialog.java
@@ -0,0 +1,113 @@
+
+package com.intellij.ide.util.frameworkSupport;
+
+import com.intellij.CommonBundle;
+import com.intellij.facet.impl.DefaultFacetsProvider;
+import com.intellij.framework.addSupport.FrameworkSupportInModuleProvider;
+import com.intellij.ide.util.newProjectWizard.AddSupportForFrameworksPanel;
+import com.intellij.ide.util.newProjectWizard.impl.FrameworkSupportModelBase;
+import com.intellij.openapi.application.Result;
+import com.intellij.openapi.application.WriteAction;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.project.ProjectBundle;
+import com.intellij.openapi.roots.ModifiableRootModel;
+import com.intellij.openapi.roots.ModuleRootManager;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.LibrariesContainer;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.LibrariesContainerFactory;
+import com.intellij.openapi.ui.DialogWrapper;
+import com.intellij.openapi.ui.Messages;
+import com.intellij.openapi.util.Disposer;
+import com.intellij.openapi.vfs.VirtualFile;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import java.util.List;
+
+/**
+ * @author nik
+ */
+public class AddFrameworkSupportDialog extends DialogWrapper {
+ private final AddSupportForFrameworksPanel myAddSupportPanel;
+ private final Module myModule;
+
+ private AddFrameworkSupportDialog(@NotNull Module module, final @NotNull String contentRootPath, final List<FrameworkSupportInModuleProvider> providers) {
+ super(module.getProject(), true);
+ setTitle(ProjectBundle.message("dialog.title.add.frameworks.support"));
+ myModule = module;
+ final LibrariesContainer container = LibrariesContainerFactory.createContainer(module.getProject());
+ final FrameworkSupportModelBase model = new FrameworkSupportModelImpl(module.getProject(), contentRootPath, container);
+ myAddSupportPanel = new AddSupportForFrameworksPanel(providers, model) {
+ @Override
+ protected void onFrameworkStateChanged() {
+ setOKActionEnabled(isOKActionEnabled());
+ }
+ };
+
+ setOKActionEnabled(isOKActionEnabled());
+ Disposer.register(myDisposable, myAddSupportPanel);
+ init();
+ }
+
+ @Nullable
+ public static AddFrameworkSupportDialog createDialog(@NotNull Module module) {
+ VirtualFile[] roots = ModuleRootManager.getInstance(module).getContentRoots();
+ if (roots.length == 0) return null;
+
+ List<FrameworkSupportInModuleProvider> providers = FrameworkSupportUtil.getProviders(module, DefaultFacetsProvider.INSTANCE);
+ if (providers.isEmpty()) return null;
+
+ return new AddFrameworkSupportDialog(module, roots[0].getPath(), providers);
+ }
+
+ public static boolean isAvailable(@NotNull Module module) {
+ VirtualFile[] roots = ModuleRootManager.getInstance(module).getContentRoots();
+ return roots.length != 0 && FrameworkSupportUtil.hasProviders(module, DefaultFacetsProvider.INSTANCE);
+ }
+
+ @Override
+ public boolean isOKActionEnabled() {
+ return myAddSupportPanel.hasSelectedFrameworks();
+ }
+
+ protected void doOKAction() {
+ if (myAddSupportPanel.hasSelectedFrameworks()) {
+ if (!myAddSupportPanel.downloadLibraries()) {
+ int answer = Messages.showYesNoDialog(myAddSupportPanel.getMainPanel(),
+ ProjectBundle.message("warning.message.some.required.libraries.wasn.t.downloaded"),
+ CommonBundle.getWarningTitle(), Messages.getWarningIcon());
+ if (answer != 0) {
+ return;
+ }
+ }
+
+ new WriteAction() {
+ protected void run(final Result result) {
+ ModifiableRootModel model = ModuleRootManager.getInstance(myModule).getModifiableModel();
+ myAddSupportPanel.addSupport(myModule, model);
+ model.commit();
+ }
+ }.execute();
+ }
+ super.doOKAction();
+ }
+
+ @Override
+ protected String getDimensionServiceKey() {
+ return "#com.intellij.ide.util.frameworkSupport.AddFrameworkSupportDialog";
+ }
+
+ @Override
+ protected String getHelpId() {
+ return "reference.frameworks.support.dialog";
+ }
+
+ protected JComponent createCenterPanel() {
+ return myAddSupportPanel.getMainPanel();
+ }
+
+ @Override
+ public JComponent getPreferredFocusedComponent() {
+ return myAddSupportPanel.getFrameworksTree();
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/ide/util/frameworkSupport/CustomLibraryDescriptionBase.java b/java/idea-ui/src/com/intellij/ide/util/frameworkSupport/CustomLibraryDescriptionBase.java
new file mode 100644
index 0000000..75a4999
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/ide/util/frameworkSupport/CustomLibraryDescriptionBase.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2000-2012 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.ide.util.frameworkSupport;
+
+import com.intellij.framework.library.LibraryVersionProperties;
+import com.intellij.ide.IdeBundle;
+import com.intellij.openapi.fileChooser.FileChooser;
+import com.intellij.openapi.fileChooser.FileChooserDescriptor;
+import com.intellij.openapi.roots.OrderRootType;
+import com.intellij.openapi.roots.libraries.NewLibraryConfiguration;
+import com.intellij.openapi.roots.ui.configuration.libraries.CustomLibraryDescription;
+import com.intellij.openapi.roots.ui.configuration.libraryEditor.LibraryEditor;
+import com.intellij.openapi.vfs.VirtualFile;
+import org.jetbrains.annotations.NotNull;
+
+import javax.swing.*;
+
+/**
+ * @author nik
+ */
+public abstract class CustomLibraryDescriptionBase extends CustomLibraryDescription {
+ private final String myDefaultLibraryName;
+
+ protected CustomLibraryDescriptionBase(@NotNull String defaultLibraryName) {
+ myDefaultLibraryName = defaultLibraryName;
+ }
+
+ @Override
+ public NewLibraryConfiguration createNewLibrary(@NotNull JComponent parentComponent, VirtualFile contextDirectory) {
+ final FileChooserDescriptor descriptor = new FileChooserDescriptor(false, false, true, false, false, true);
+ descriptor.setTitle(IdeBundle.message("new.library.file.chooser.title"));
+ descriptor.setDescription(IdeBundle.message("new.library.file.chooser.description"));
+ final VirtualFile[] files = FileChooser.chooseFiles(descriptor, parentComponent, null, contextDirectory);
+ if (files.length == 0) {
+ return null;
+ }
+ return new NewLibraryConfiguration(myDefaultLibraryName, getDownloadableLibraryType(), new LibraryVersionProperties()) {
+ @Override
+ public void addRoots(@NotNull LibraryEditor editor) {
+ for (VirtualFile file : files) {
+ editor.addRoot(file, OrderRootType.CLASSES);
+ }
+ }
+ };
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/ide/util/frameworkSupport/CustomLibraryDescriptionImpl.java b/java/idea-ui/src/com/intellij/ide/util/frameworkSupport/CustomLibraryDescriptionImpl.java
new file mode 100644
index 0000000..8842627
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/ide/util/frameworkSupport/CustomLibraryDescriptionImpl.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2000-2012 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.ide.util.frameworkSupport;
+
+import com.intellij.framework.library.DownloadableLibraryType;
+import com.intellij.openapi.roots.libraries.LibraryKind;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.Collections;
+import java.util.Set;
+
+/**
+ * @author nik
+ */
+public class CustomLibraryDescriptionImpl extends CustomLibraryDescriptionBase {
+ private final DownloadableLibraryType myLibraryType;
+
+ public CustomLibraryDescriptionImpl(@NotNull DownloadableLibraryType downloadableLibraryType) {
+ super(downloadableLibraryType.getLibraryCategoryName());
+ myLibraryType = downloadableLibraryType;
+ }
+
+ @NotNull
+ @Override
+ public Set<? extends LibraryKind> getSuitableLibraryKinds() {
+ return Collections.singleton(myLibraryType.getKind());
+ }
+
+ @Override
+ public DownloadableLibraryType getDownloadableLibraryType() {
+ return myLibraryType;
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/ide/util/frameworkSupport/FrameworkSupportModelImpl.java b/java/idea-ui/src/com/intellij/ide/util/frameworkSupport/FrameworkSupportModelImpl.java
new file mode 100644
index 0000000..dce5b23
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/ide/util/frameworkSupport/FrameworkSupportModelImpl.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2000-2011 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.ide.util.frameworkSupport;
+
+import com.intellij.ide.util.newProjectWizard.impl.FrameworkSupportModelBase;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.LibrariesContainer;
+import org.jetbrains.annotations.NotNull;
+
+/**
+* @author nik
+*/
+public class FrameworkSupportModelImpl extends FrameworkSupportModelBase {
+ private final String myContentRootPath;
+
+ public FrameworkSupportModelImpl(@NotNull final Project project,
+ @NotNull String baseDirectoryForLibrariesPath, LibrariesContainer librariesContainer) {
+ super(project, null, librariesContainer);
+ myContentRootPath = baseDirectoryForLibrariesPath;
+ }
+
+ @NotNull
+ @Override
+ public String getBaseDirectoryForLibrariesPath() {
+ return myContentRootPath;
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/ide/util/frameworkSupport/FrameworkSupportUtil.java b/java/idea-ui/src/com/intellij/ide/util/frameworkSupport/FrameworkSupportUtil.java
new file mode 100644
index 0000000..ae89726
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/ide/util/frameworkSupport/FrameworkSupportUtil.java
@@ -0,0 +1,136 @@
+
+package com.intellij.ide.util.frameworkSupport;
+
+import com.intellij.framework.FrameworkTypeEx;
+import com.intellij.framework.addSupport.FrameworkSupportInModuleProvider;
+import com.intellij.ide.util.newProjectWizard.OldFrameworkSupportProviderWrapper;
+import com.intellij.ide.util.projectWizard.ModuleBuilder;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.module.ModuleType;
+import com.intellij.openapi.roots.ui.configuration.FacetsProvider;
+import com.intellij.openapi.util.Pair;
+import com.intellij.util.graph.CachingSemiGraph;
+import com.intellij.util.graph.DFSTBuilder;
+import com.intellij.util.graph.GraphGenerator;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.*;
+
+/**
+ * @author nik
+ */
+public class FrameworkSupportUtil {
+ private static final Logger LOG = Logger.getInstance("#com.intellij.ide.util.frameworkSupport.FrameworkSupportUtil");
+
+ private FrameworkSupportUtil() {
+ }
+
+ public static List<FrameworkSupportInModuleProvider> getProviders(@NotNull ModuleType moduleType, @NotNull FacetsProvider facetsProvider) {
+ return getProviders(moduleType, null, facetsProvider);
+ }
+
+ public static List<FrameworkSupportInModuleProvider> getProviders(@NotNull Module module, final @NotNull FacetsProvider facetsProvider) {
+ return getProviders(ModuleType.get(module), module, facetsProvider);
+ }
+
+ private static List<FrameworkSupportInModuleProvider> getProviders(@NotNull ModuleType moduleType,
+ @Nullable Module module,
+ @NotNull FacetsProvider facetsProvider) {
+ List<FrameworkSupportInModuleProvider> allProviders = getAllProviders();
+ ArrayList<FrameworkSupportInModuleProvider> result = new ArrayList<FrameworkSupportInModuleProvider>();
+ for (FrameworkSupportInModuleProvider provider : allProviders) {
+ if (provider.isEnabledForModuleType(moduleType) && (module == null || provider.canAddSupport(module, facetsProvider))) {
+ result.add(provider);
+ }
+ }
+ return result;
+ }
+
+ public static List<FrameworkSupportInModuleProvider> getAllProviders() {
+ List<FrameworkSupportInModuleProvider> allTypes = new ArrayList<FrameworkSupportInModuleProvider>();
+ for (FrameworkSupportProvider provider : FrameworkSupportProvider.EXTENSION_POINT.getExtensions()) {
+ allTypes.add(new OldFrameworkSupportProviderWrapper(provider));
+ }
+ for (FrameworkTypeEx type : FrameworkTypeEx.EP_NAME.getExtensions()) {
+ allTypes.add(type.createProvider());
+ }
+ return allTypes;
+ }
+
+ public static List<FrameworkSupportInModuleProvider> getProviders(@NotNull ModuleBuilder builder) {
+ List<FrameworkSupportInModuleProvider> result = new ArrayList<FrameworkSupportInModuleProvider>();
+ for (FrameworkSupportInModuleProvider type : getAllProviders()) {
+ if (type.isEnabledForModuleBuilder(builder)) {
+ result.add(type);
+ }
+ }
+ return result;
+ }
+
+ public static boolean hasProviders(final Module module, @NotNull FacetsProvider facetsProvider) {
+ List<FrameworkSupportInModuleProvider> providers = getProviders(module, facetsProvider);
+ for (FrameworkSupportInModuleProvider provider : providers) {
+ if (provider.getFrameworkType().getUnderlyingFrameworkTypeId() == null) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public static Comparator<FrameworkSupportInModuleProvider> getFrameworkSupportProvidersComparator(final List<FrameworkSupportInModuleProvider> types) {
+ DFSTBuilder<FrameworkSupportInModuleProvider>
+ builder = new DFSTBuilder<FrameworkSupportInModuleProvider>(GraphGenerator.create(CachingSemiGraph.create(new ProvidersGraph(types))));
+ if (!builder.isAcyclic()) {
+ Pair<FrameworkSupportInModuleProvider, FrameworkSupportInModuleProvider> pair = builder.getCircularDependency();
+ LOG.error("Circular dependency between types '" + pair.getFirst().getFrameworkType().getId() + "' and '" + pair.getSecond().getFrameworkType().getId() + "' was found.");
+ }
+
+ return builder.comparator();
+ }
+
+ @Nullable
+ public static FrameworkSupportInModuleProvider findProvider(@NotNull String id, final List<FrameworkSupportInModuleProvider> providers) {
+ for (FrameworkSupportInModuleProvider provider : providers) {
+ if (id.equals(provider.getFrameworkType().getId())) {
+ return provider;
+ }
+ }
+ LOG.info("Cannot find framework support provider '" + id + "'");
+ return null;
+ }
+
+ private static class ProvidersGraph implements GraphGenerator.SemiGraph<FrameworkSupportInModuleProvider> {
+ private final List<FrameworkSupportInModuleProvider> myFrameworkSupportProviders;
+
+ public ProvidersGraph(final List<FrameworkSupportInModuleProvider> frameworkSupportProviders) {
+ myFrameworkSupportProviders = new ArrayList<FrameworkSupportInModuleProvider>(frameworkSupportProviders);
+ }
+
+ public Collection<FrameworkSupportInModuleProvider> getNodes() {
+ return myFrameworkSupportProviders;
+ }
+
+ public Iterator<FrameworkSupportInModuleProvider> getIn(final FrameworkSupportInModuleProvider provider) {
+ List<FrameworkSupportInModuleProvider> dependencies = new ArrayList<FrameworkSupportInModuleProvider>();
+ String underlyingId = provider.getFrameworkType().getUnderlyingFrameworkTypeId();
+ if (underlyingId != null) {
+ FrameworkSupportInModuleProvider underlyingProvider = findProvider(underlyingId, myFrameworkSupportProviders);
+ if (underlyingProvider != null) {
+ dependencies.add(underlyingProvider);
+ }
+ }
+ if (provider instanceof OldFrameworkSupportProviderWrapper) {
+ String[] ids = ((OldFrameworkSupportProviderWrapper)provider).getProvider().getPrecedingFrameworkProviderIds();
+ for (String id : ids) {
+ FrameworkSupportInModuleProvider dependency = findProvider(id, myFrameworkSupportProviders);
+ if (dependency != null) {
+ dependencies.add(dependency);
+ }
+ }
+ }
+ return dependencies.iterator();
+ }
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/ide/util/frameworkSupport/OldCustomLibraryDescription.java b/java/idea-ui/src/com/intellij/ide/util/frameworkSupport/OldCustomLibraryDescription.java
new file mode 100644
index 0000000..7e53c09
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/ide/util/frameworkSupport/OldCustomLibraryDescription.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright 2000-2012 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.ide.util.frameworkSupport;
+
+import com.intellij.facet.impl.ui.libraries.RequiredLibrariesInfo;
+import com.intellij.facet.ui.libraries.LibraryDownloadInfo;
+import com.intellij.facet.ui.libraries.LibraryInfo;
+import com.intellij.framework.library.DownloadableLibraryDescription;
+import com.intellij.framework.library.DownloadableLibraryFileDescription;
+import com.intellij.framework.library.FrameworkLibraryVersion;
+import com.intellij.framework.library.impl.DownloadableLibraryDescriptionImpl;
+import com.intellij.framework.library.impl.DownloadableLibraryFileDescriptionImpl;
+import com.intellij.framework.library.impl.FrameworkLibraryVersionImpl;
+import com.intellij.openapi.roots.OrderRootType;
+import com.intellij.openapi.roots.libraries.Library;
+import com.intellij.openapi.roots.libraries.LibraryKind;
+import com.intellij.openapi.roots.ui.configuration.libraries.CustomLibraryDescription;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.LibrariesContainer;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * @author nik
+ */
+public class OldCustomLibraryDescription extends CustomLibraryDescriptionBase {
+ private final DownloadableLibraryDescription myDownloadableDescription;
+ private final List<FrameworkVersion> myVersions;
+
+ public OldCustomLibraryDescription(@NotNull LibraryInfo[] libraryInfos, @NotNull String defaultLibraryName) {
+ this(Collections.singletonList(new FrameworkVersion(defaultLibraryName, defaultLibraryName, libraryInfos, true)), defaultLibraryName);
+ }
+
+ private OldCustomLibraryDescription(final List<FrameworkVersion> versions, String defaultLibraryName) {
+ super(defaultLibraryName);
+ myVersions = versions;
+ final List<FrameworkLibraryVersion> libraryVersions = new ArrayList<FrameworkLibraryVersion>();
+ for (FrameworkVersion version : versions) {
+ List<DownloadableLibraryFileDescription> downloads = new ArrayList<DownloadableLibraryFileDescription>();
+ for (LibraryInfo info : version.getLibraries()) {
+ final LibraryDownloadInfo downloadingInfo = info.getDownloadingInfo();
+ if (downloadingInfo != null) {
+ downloads.add(new DownloadableLibraryFileDescriptionImpl(downloadingInfo.getDownloadUrl(), downloadingInfo.getFileNamePrefix(),
+ downloadingInfo.getFileNameSuffix(), null, null, false));
+ }
+ }
+ libraryVersions.add(new FrameworkLibraryVersionImpl(version.getVersionName(), downloads, version.getLibraryName()));
+ }
+ myDownloadableDescription = !libraryVersions.isEmpty() ? new DownloadableLibraryDescriptionImpl(libraryVersions) : null;
+ }
+
+ public DownloadableLibraryDescription getDownloadableDescription() {
+ return myDownloadableDescription;
+ }
+
+ public boolean isSuitableLibrary(@NotNull Library library, @NotNull LibrariesContainer container) {
+ for (FrameworkVersion version : myVersions) {
+ RequiredLibrariesInfo info = new RequiredLibrariesInfo(version.getLibraries());
+ if (info.checkLibraries(container.getLibraryFiles(library, OrderRootType.CLASSES)) == null) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @NotNull
+ @Override
+ public Set<? extends LibraryKind> getSuitableLibraryKinds() {
+ return Collections.emptySet();
+ }
+
+ @Nullable
+ public static CustomLibraryDescription createByVersions(List<? extends FrameworkVersion> versions) {
+ String defaultLibraryName = null;
+ List<FrameworkVersion> withLibraries = new ArrayList<FrameworkVersion>();
+ for (FrameworkVersion version : versions) {
+ if (version.getLibraries().length > 0) {
+ if (version.isDefault()) {
+ defaultLibraryName = version.getLibraryName();
+ }
+ withLibraries.add(version);
+ }
+ }
+ if (withLibraries.isEmpty()) return null;
+
+
+ if (defaultLibraryName == null) {
+ defaultLibraryName = withLibraries.get(0).getLibraryName();
+ }
+
+ return new OldCustomLibraryDescription(withLibraries, defaultLibraryName);
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/ide/util/importProject/DetectedRootData.java b/java/idea-ui/src/com/intellij/ide/util/importProject/DetectedRootData.java
new file mode 100644
index 0000000..c7279ac
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/ide/util/importProject/DetectedRootData.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2000-2011 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.ide.util.importProject;
+
+import com.intellij.ide.util.projectWizard.importSources.DetectedProjectRoot;
+import com.intellij.ide.util.projectWizard.importSources.ProjectStructureDetector;
+import com.intellij.util.containers.MultiMap;
+import org.jetbrains.annotations.NotNull;
+
+import java.io.File;
+import java.util.*;
+
+/**
+ * @author nik
+ */
+class DetectedRootData {
+ private final File myDirectory;
+ private MultiMap<DetectedProjectRoot, ProjectStructureDetector> myRoots = new MultiMap<DetectedProjectRoot, ProjectStructureDetector>() {
+ @Override
+ protected Map<DetectedProjectRoot, Collection<ProjectStructureDetector>> createMap() {
+ return new LinkedHashMap<DetectedProjectRoot, Collection<ProjectStructureDetector>>();
+ }
+ };
+ private boolean myIncluded = true;
+ private DetectedProjectRoot mySelectedRoot;
+
+ public DetectedRootData(ProjectStructureDetector detector, DetectedProjectRoot root) {
+ myDirectory = root.getDirectory();
+ mySelectedRoot = root;
+ myRoots.putValue(root, detector);
+ }
+
+ public File getDirectory() {
+ return myDirectory;
+ }
+
+ public DetectedProjectRoot addRoot(ProjectStructureDetector detector, DetectedProjectRoot root) {
+ for (Map.Entry<DetectedProjectRoot, Collection<ProjectStructureDetector>> entry : myRoots.entrySet()) {
+ final DetectedProjectRoot oldRoot = entry.getKey();
+ final DetectedProjectRoot combined = oldRoot.combineWith(root);
+ if (combined != null) {
+ myRoots.remove(oldRoot);
+ final Set<ProjectStructureDetector> values = new HashSet<ProjectStructureDetector>(entry.getValue());
+ values.add(detector);
+ myRoots.put(combined, values);
+ if (mySelectedRoot == oldRoot) {
+ mySelectedRoot = combined;
+ }
+ return combined;
+ }
+ }
+ myRoots.putValue(root, detector);
+ return root;
+ }
+
+ public DetectedProjectRoot[] getAllRoots() {
+ final Set<DetectedProjectRoot> roots = myRoots.keySet();
+ return roots.toArray(new DetectedProjectRoot[roots.size()]);
+ }
+
+ public boolean isIncluded() {
+ return myIncluded;
+ }
+
+ public void setIncluded(boolean included) {
+ myIncluded = included;
+ }
+
+ @NotNull
+ public Collection<ProjectStructureDetector> getSelectedDetectors() {
+ return myRoots.get(mySelectedRoot);
+ }
+
+ public DetectedProjectRoot getSelectedRoot() {
+ return mySelectedRoot;
+ }
+
+
+ public void setSelectedRoot(DetectedProjectRoot root) {
+ mySelectedRoot = root;
+ }
+
+ public void removeRoot(DetectedProjectRoot root) {
+ myRoots.remove(root);
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/ide/util/importProject/DetectedRootsChooser.java b/java/idea-ui/src/com/intellij/ide/util/importProject/DetectedRootsChooser.java
new file mode 100644
index 0000000..d21c884
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/ide/util/importProject/DetectedRootsChooser.java
@@ -0,0 +1,235 @@
+/*
+ * Copyright 2000-2012 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.ide.util.importProject;
+
+import com.intellij.ui.ListCellRendererWrapper;
+import com.intellij.ide.util.projectWizard.importSources.DetectedProjectRoot;
+import com.intellij.openapi.ui.ComboBox;
+import com.intellij.openapi.ui.ComboBoxTableRenderer;
+import com.intellij.ui.CollectionComboBoxModel;
+import com.intellij.ui.ScrollPaneFactory;
+import com.intellij.ui.table.TableView;
+import com.intellij.util.EventDispatcher;
+import com.intellij.util.ui.ColumnInfo;
+import com.intellij.util.ui.ListTableModel;
+import org.jetbrains.annotations.NotNull;
+
+import javax.swing.*;
+import javax.swing.table.DefaultTableCellRenderer;
+import javax.swing.table.TableCellEditor;
+import javax.swing.table.TableCellRenderer;
+import javax.swing.table.TableColumn;
+import java.awt.*;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.KeyEvent;
+import java.util.*;
+import java.util.List;
+
+/**
+ * @author nik
+ */
+public class DetectedRootsChooser {
+ private static final int CHECKBOX_COLUMN_WIDTH = new JCheckBox().getPreferredSize().width + 4;
+ private final ColumnInfo<DetectedRootData,Boolean> myIncludedColumn = new ColumnInfo<DetectedRootData, Boolean>("") {
+
+ @Override
+ public Class getColumnClass() {
+ return Boolean.class;
+ }
+
+ @Override
+ public Boolean valueOf(DetectedRootData detectedRootData) {
+ return detectedRootData.isIncluded();
+ }
+
+ @Override
+ public boolean isCellEditable(DetectedRootData detectedRootData) {
+ return true;
+ }
+
+ @Override
+ public int getWidth(JTable table) {
+ return CHECKBOX_COLUMN_WIDTH;
+ }
+
+ @Override
+ public void setValue(DetectedRootData detectedRootData, Boolean value) {
+ if (value.booleanValue() != detectedRootData.isIncluded()) {
+ detectedRootData.setIncluded(value);
+ myDispatcher.getMulticaster().selectionChanged();
+ }
+ }
+ };
+ private static final ColumnInfo<DetectedRootData, String> ROOT_COLUMN = new ColumnInfo<DetectedRootData, String>("") {
+ @Override
+ public String valueOf(DetectedRootData detectedRootData) {
+ return detectedRootData.getDirectory().getAbsolutePath();
+ }
+ };
+ private static final ColumnInfo<DetectedRootData, DetectedProjectRoot> ROOT_TYPE_COLUMN = new ColumnInfo<DetectedRootData, DetectedProjectRoot>("") {
+ @Override
+ public DetectedProjectRoot valueOf(DetectedRootData detectedRootData) {
+ return detectedRootData.getSelectedRoot();
+ }
+
+ @Override
+ public TableCellRenderer getRenderer(DetectedRootData detectedRootData) {
+ if (isCellEditable(detectedRootData)) {
+ return new ComboBoxTableRenderer<DetectedProjectRoot>(detectedRootData.getAllRoots()) {
+ @Override
+ protected String getTextFor(@NotNull DetectedProjectRoot value) {
+ return value.getRootTypeName();
+ }
+ };
+ }
+ return new DefaultTableCellRenderer() {
+ @Override
+ public Component getTableCellRendererComponent(JTable table,
+ Object value,
+ boolean isSelected,
+ boolean hasFocus,
+ int row,
+ int column) {
+ final Component component = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
+ if (value instanceof DetectedProjectRoot) {
+ setText(((DetectedProjectRoot)value).getRootTypeName());
+ }
+ return component;
+ }
+ };
+ }
+
+ @Override
+ public TableCellEditor getEditor(DetectedRootData o) {
+ final ComboBox comboBox = new ComboBox(new CollectionComboBoxModel(Arrays.asList(o.getAllRoots()), o.getSelectedRoot()));
+ comboBox.setRenderer(new ListCellRendererWrapper<DetectedProjectRoot>() {
+ @Override
+ public void customize(JList list, DetectedProjectRoot value, int index, boolean selected, boolean hasFocus) {
+ setText(value.getRootTypeName());
+ }
+ });
+ return new DefaultCellEditor(comboBox);
+ }
+
+ @Override
+ public boolean isCellEditable(DetectedRootData detectedRootData) {
+ return detectedRootData.getAllRoots().length > 1;
+ }
+
+ @Override
+ public void setValue(DetectedRootData detectedRootData, DetectedProjectRoot value) {
+ detectedRootData.setSelectedRoot(value);
+ }
+ };
+ private TableView<DetectedRootData> myTable;
+ private JComponent myComponent;
+ private final ListTableModel<DetectedRootData> myModel;
+ private final EventDispatcher<RootSelectionListener> myDispatcher = EventDispatcher.create(RootSelectionListener.class);
+
+ public DetectedRootsChooser() {
+ myModel = new ListTableModel<DetectedRootData>();
+ myTable = new TableView<DetectedRootData>(myModel);
+ myTable.setTableHeader(null);
+ myTable.setShowGrid(false);
+ myComponent = ScrollPaneFactory.createScrollPane(myTable);
+ myTable.registerKeyboardAction(
+ new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ invertSelectedRows();
+ }
+ },
+ KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, 0),
+ JComponent.WHEN_FOCUSED
+ );
+ }
+
+ private void invertSelectedRows() {
+ final int[] selectedRows = myTable.getSelectedRows();
+ if (selectedRows.length == 0) return;
+
+ boolean included = false;
+ for (int selectedRow : selectedRows) {
+ included |= myModel.getItems().get(selectedRow).isIncluded();
+ }
+ int first = Integer.MAX_VALUE;
+ int last = -1;
+ for (int selectedRow : selectedRows) {
+ first = Math.min(first, selectedRow);
+ last = Math.max(last, selectedRow);
+ myModel.getItems().get(selectedRow).setIncluded(!included);
+ }
+ myModel.fireTableRowsUpdated(first, last+1);
+ myDispatcher.getMulticaster().selectionChanged();
+ }
+
+ public JComponent getComponent() {
+ return myComponent;
+ }
+
+ public void addSelectionListener(RootSelectionListener listener) {
+ myDispatcher.addListener(listener);
+ }
+
+ public void setAllElementsMarked(boolean mark) {
+ for (DetectedRootData data : myModel.getItems()) {
+ data.setIncluded(mark);
+ }
+ myModel.fireTableRowsUpdated(0, myModel.getRowCount()-1);
+ myDispatcher.getMulticaster().selectionChanged();
+ }
+
+ public List<DetectedRootData> getMarkedElements() {
+ final List<DetectedRootData> result = new ArrayList<DetectedRootData>();
+ for (DetectedRootData data : myModel.getItems()) {
+ if (data.isIncluded()) {
+ result.add(data);
+ }
+ }
+ return result;
+ }
+
+ public void setElements(List<? extends DetectedRootData> roots) {
+ Set<String> rootTypes = new HashSet<String>();
+ for (DetectedRootData root : roots) {
+ for (DetectedProjectRoot projectRoot : root.getAllRoots()) {
+ rootTypes.add(projectRoot.getRootTypeName());
+ }
+ }
+ myModel.setColumnInfos(new ColumnInfo[]{myIncludedColumn, ROOT_COLUMN, ROOT_TYPE_COLUMN});
+ int max = 0;
+ for (String rootType : rootTypes) {
+ max = Math.max(max, myTable.getFontMetrics(myTable.getFont()).stringWidth(rootType));
+ }
+ final TableColumn column = myTable.getColumnModel().getColumn(2);
+ int width = max + 20;//add space for combobox button
+ column.setPreferredWidth(width);
+ column.setMaxWidth(width);
+ myTable.updateColumnSizes();
+ List<DetectedRootData> sortedRoots = new ArrayList<DetectedRootData>(roots);
+ Collections.sort(sortedRoots, new Comparator<DetectedRootData>() {
+ @Override
+ public int compare(DetectedRootData o1, DetectedRootData o2) {
+ return o1.getDirectory().compareTo(o2.getDirectory());
+ }
+ });
+ myModel.setItems(sortedRoots);
+ }
+
+ interface RootSelectionListener extends EventListener {
+ void selectionChanged();
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/ide/util/importProject/FacetBasedDetectedFrameworkDescriptionInWizard.java b/java/idea-ui/src/com/intellij/ide/util/importProject/FacetBasedDetectedFrameworkDescriptionInWizard.java
new file mode 100644
index 0000000..3387210
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/ide/util/importProject/FacetBasedDetectedFrameworkDescriptionInWizard.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2000-2011 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.ide.util.importProject;
+
+import com.intellij.facet.Facet;
+import com.intellij.facet.FacetConfiguration;
+import com.intellij.facet.FacetTypeId;
+import com.intellij.framework.detection.FacetBasedFrameworkDetector;
+import com.intellij.framework.detection.impl.FacetBasedDetectedFrameworkDescription;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.roots.ModifiableModelsProvider;
+import com.intellij.openapi.roots.ui.configuration.FacetsProvider;
+import com.intellij.openapi.roots.ui.configuration.ModulesProvider;
+import com.intellij.openapi.vfs.VirtualFile;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Set;
+
+/**
+ * @author nik
+ */
+public class FacetBasedDetectedFrameworkDescriptionInWizard<F extends Facet, C extends FacetConfiguration> extends FacetBasedDetectedFrameworkDescription<F, C> {
+ private static final Logger LOG = Logger.getInstance("#com.intellij.ide.util.importProject.FacetBasedDetectedFrameworkDescriptionInWizard");
+ private final ModuleDescriptor myModuleDescriptor;
+
+ public FacetBasedDetectedFrameworkDescriptionInWizard(@NotNull ModuleDescriptor moduleDescriptor,
+ FacetBasedFrameworkDetector<F, C> detector,
+ @NotNull C configuration,
+ Set<VirtualFile> files) {
+ super(detector, configuration, files);
+ myModuleDescriptor = moduleDescriptor;
+ }
+
+ @Override
+ protected String getModuleName() {
+ return myModuleDescriptor.getName();
+ }
+
+ @Override
+ public void setupFramework(@NotNull ModifiableModelsProvider modifiableModelsProvider, @NotNull ModulesProvider modulesProvider) {
+ Module module = modulesProvider.getModule(getModuleName());
+ LOG.assertTrue(module != null, getModuleName());
+ doSetup(modifiableModelsProvider, module);
+ }
+
+ @NotNull
+ protected Collection<? extends Facet> getExistentFacets(FacetTypeId<?> underlyingFacetType) {
+ return Collections.emptyList();
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/ide/util/importProject/FrameworkDetectionInWizardContext.java b/java/idea-ui/src/com/intellij/ide/util/importProject/FrameworkDetectionInWizardContext.java
new file mode 100644
index 0000000..d8c14a2
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/ide/util/importProject/FrameworkDetectionInWizardContext.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright 2000-2011 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.ide.util.importProject;
+
+import com.intellij.facet.Facet;
+import com.intellij.facet.FacetConfiguration;
+import com.intellij.facet.FacetType;
+import com.intellij.framework.detection.DetectedFrameworkDescription;
+import com.intellij.framework.detection.FacetBasedFrameworkDetector;
+import com.intellij.framework.detection.impl.FrameworkDetectionContextBase;
+import com.intellij.openapi.util.Pair;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.vfs.LocalFileSystem;
+import com.intellij.openapi.vfs.VfsUtil;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.util.containers.MultiMap;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.io.File;
+import java.util.*;
+
+/**
+ * @author nik
+ */
+public abstract class FrameworkDetectionInWizardContext extends FrameworkDetectionContextBase {
+ protected FrameworkDetectionInWizardContext() {
+ }
+
+ @NotNull
+ @Override
+ public <F extends Facet, C extends FacetConfiguration> List<? extends DetectedFrameworkDescription> createDetectedFacetDescriptions(@NotNull FacetBasedFrameworkDetector<F, C> detector,
+ @NotNull Collection<VirtualFile> files) {
+ final List<ModuleDescriptor> descriptors = getModuleDescriptors();
+ MultiMap<ModuleDescriptor, VirtualFile> filesByModule = new MultiMap<ModuleDescriptor, VirtualFile>();
+ for (VirtualFile file : files) {
+ final File ioFile = VfsUtil.virtualToIoFile(file);
+ ModuleDescriptor descriptor = findDescriptorByFile(descriptors, ioFile);
+ if (descriptor != null) {
+ filesByModule.putValue(descriptor, file);
+ }
+ }
+
+ final List<DetectedFrameworkDescription> result = new ArrayList<DetectedFrameworkDescription>();
+ final FacetType<F,C> facetType = detector.getFacetType();
+ for (ModuleDescriptor module : filesByModule.keySet()) {
+ if (!facetType.isSuitableModuleType(module.getModuleType())) {
+ continue;
+ }
+
+ final List<Pair<C, Collection<VirtualFile>>> pairs =
+ detector.createConfigurations(filesByModule.get(module), Collections.<C>emptyList());
+ for (Pair<C, Collection<VirtualFile>> pair : pairs) {
+ result.add(new FacetBasedDetectedFrameworkDescriptionInWizard<F, C>(module, detector, pair.getFirst(),
+ new HashSet<VirtualFile>(pair.getSecond())));
+ }
+ }
+ return result;
+ }
+
+ protected abstract List<ModuleDescriptor> getModuleDescriptors();
+
+ @Nullable
+ private static ModuleDescriptor findDescriptorByFile(List<ModuleDescriptor> descriptors, File file) {
+ ModuleDescriptor result = null;
+ File nearestRoot = null;
+ for (ModuleDescriptor descriptor : descriptors) {
+ for (File root : descriptor.getContentRoots()) {
+ if (FileUtil.isAncestor(root, file, false) && (nearestRoot == null || FileUtil.isAncestor(nearestRoot, root, true))) {
+ result = descriptor;
+ nearestRoot = root;
+ }
+ }
+ }
+ return result;
+ }
+
+ public VirtualFile getBaseDir() {
+ final String path = getContentPath();
+ return path != null ? LocalFileSystem.getInstance().refreshAndFindFileByPath(path) : null;
+ }
+
+ @Nullable
+ protected abstract String getContentPath();
+}
diff --git a/java/idea-ui/src/com/intellij/ide/util/importProject/FrameworkDetectionStep.form b/java/idea-ui/src/com/intellij/ide/util/importProject/FrameworkDetectionStep.form
new file mode 100644
index 0000000..106ffd1
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/ide/util/importProject/FrameworkDetectionStep.form
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.intellij.ide.util.importProject.FrameworkDetectionStep">
+ <grid id="27dc6" binding="myMainPanel" layout-manager="BorderLayout" hgap="0" vgap="5">
+ <constraints>
+ <xy x="20" y="20" width="743" height="187"/>
+ </constraints>
+ <properties/>
+ <border type="empty">
+ <size top="5" left="5" bottom="5" right="5"/>
+ </border>
+ <children>
+ <component id="586a7" class="javax.swing.JLabel" binding="myFrameworksDetectedLabel">
+ <constraints border-constraint="North"/>
+ <properties>
+ <text resource-bundle="messages/ProjectBundle" key="label.text.the.following.frameworks.are.detected"/>
+ </properties>
+ </component>
+ <grid id="16a29" binding="myFrameworksPanel" layout-manager="BorderLayout" hgap="0" vgap="0">
+ <constraints border-constraint="Center"/>
+ <properties/>
+ <border type="none"/>
+ <children/>
+ </grid>
+ </children>
+ </grid>
+</form>
diff --git a/java/idea-ui/src/com/intellij/ide/util/importProject/FrameworkDetectionStep.java b/java/idea-ui/src/com/intellij/ide/util/importProject/FrameworkDetectionStep.java
new file mode 100644
index 0000000..ef1064d
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/ide/util/importProject/FrameworkDetectionStep.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright 2000-2009 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.ide.util.importProject;
+
+import com.intellij.framework.detection.DetectedFrameworkDescription;
+import com.intellij.framework.detection.DetectionExcludesConfiguration;
+import com.intellij.framework.detection.FrameworkDetectionContext;
+import com.intellij.framework.detection.FrameworkDetector;
+import com.intellij.framework.detection.impl.FrameworkDetectionProcessor;
+import com.intellij.framework.detection.impl.FrameworkDetectionUtil;
+import com.intellij.framework.detection.impl.ui.DetectedFrameworksComponent;
+import com.intellij.ide.util.projectWizard.AbstractStepWithProgress;
+import com.intellij.ide.util.projectWizard.importSources.ProjectFromSourcesBuilder;
+import com.intellij.ide.util.projectWizard.importSources.impl.ProjectFromSourcesBuilderImpl;
+import com.intellij.openapi.application.ApplicationNamesInfo;
+import com.intellij.openapi.progress.ProgressIndicator;
+import com.intellij.openapi.progress.ProgressManager;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.project.ProjectBundle;
+import com.intellij.openapi.roots.ModifiableModelsProvider;
+import com.intellij.openapi.roots.ui.configuration.ModulesProvider;
+import com.intellij.openapi.util.Comparing;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+
+import javax.swing.*;
+import java.awt.*;
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author nik
+ */
+public abstract class FrameworkDetectionStep extends AbstractStepWithProgress<List<? extends DetectedFrameworkDescription>>
+ implements ProjectFromSourcesBuilderImpl.ProjectConfigurationUpdater {
+ private final Icon myIcon;
+ private List<File> myLastRoots = null;
+ private final DetectedFrameworksComponent myDetectedFrameworksComponent;
+ private JPanel myMainPanel;
+ private JPanel myFrameworksPanel;
+ private JLabel myFrameworksDetectedLabel;
+ private final FrameworkDetectionContext myContext;
+
+ public FrameworkDetectionStep(final Icon icon, final ProjectFromSourcesBuilder builder) {
+ super(ProjectBundle.message("message.text.stop.searching.for.frameworks", ApplicationNamesInfo.getInstance().getProductName()));
+ myIcon = icon;
+ myContext = new FrameworkDetectionInWizardContext() {
+ @Override
+ protected List<ModuleDescriptor> getModuleDescriptors() {
+ return FrameworkDetectionStep.this.getModuleDescriptors();
+ }
+
+ @Override
+ protected String getContentPath() {
+ return builder.getBaseProjectPath();
+ }
+ };
+ myDetectedFrameworksComponent = new DetectedFrameworksComponent(myContext);
+ }
+
+ public void updateDataModel() {
+ }
+
+ protected boolean shouldRunProgress() {
+ return myLastRoots == null || !Comparing.haveEqualElements(myLastRoots, getRoots());
+ }
+
+ protected String getProgressText() {
+ return ProjectBundle.message("progress.text.searching.frameworks");
+ }
+
+ protected JComponent createResultsPanel() {
+ JComponent mainPanel = myDetectedFrameworksComponent.getMainPanel();
+ myFrameworksPanel.add(mainPanel, BorderLayout.CENTER);
+ return myMainPanel;
+ }
+
+ protected List<? extends DetectedFrameworkDescription> calculate() {
+ myLastRoots = getRoots();
+
+ ProgressIndicator progressIndicator = ProgressManager.getInstance().getProgressIndicator();
+
+ List<File> roots = new ArrayList<File>();
+ for (ModuleDescriptor moduleDescriptor : getModuleDescriptors()) {
+ roots.addAll(moduleDescriptor.getContentRoots());
+ }
+
+ FrameworkDetectionProcessor processor = new FrameworkDetectionProcessor(progressIndicator, myContext);
+ return processor.processRoots(roots);
+ }
+
+ public abstract List<ModuleDescriptor> getModuleDescriptors();
+
+ private List<File> getRoots() {
+ List<File> roots = new ArrayList<File>();
+ for (ModuleDescriptor moduleDescriptor : getModuleDescriptors()) {
+ roots.addAll(moduleDescriptor.getContentRoots());
+ }
+ return roots;
+ }
+
+ protected void onFinished(final List<? extends DetectedFrameworkDescription> result, final boolean canceled) {
+ myDetectedFrameworksComponent.getTree().rebuildTree(result);
+ if (result.isEmpty()) {
+ myFrameworksDetectedLabel.setText(ProjectBundle.message("label.text.no.frameworks.detected"));
+ }
+ else {
+ myFrameworksDetectedLabel.setText(ProjectBundle.message("label.text.the.following.frameworks.are.detected"));
+ }
+ myFrameworksPanel.setVisible(!result.isEmpty());
+ }
+
+ public Icon getIcon() {
+ return myIcon;
+ }
+
+ public static boolean isEnabled() {
+ return FrameworkDetector.EP_NAME.getExtensions().length > 0;
+ }
+
+ @NonNls
+ public String getHelpId() {
+ return "reference.dialogs.new.project.fromCode.facets";
+ }
+
+ public void updateProject(@NotNull Project project, @NotNull ModifiableModelsProvider modelsProvider, @NotNull ModulesProvider modulesProvider) {
+ FrameworkDetectionUtil.setupFrameworks(myDetectedFrameworksComponent.getSelectedFrameworks(), modelsProvider, modulesProvider);
+ myDetectedFrameworksComponent.processUncheckedNodes(DetectionExcludesConfiguration.getInstance(project));
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/ide/util/importProject/JavaModuleInsight.java b/java/idea-ui/src/com/intellij/ide/util/importProject/JavaModuleInsight.java
new file mode 100644
index 0000000..b7b9f7c
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/ide/util/importProject/JavaModuleInsight.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright 2000-2011 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.ide.util.importProject;
+
+import com.intellij.ide.util.projectWizard.importSources.DetectedProjectRoot;
+import com.intellij.ide.util.projectWizard.importSources.JavaModuleSourceRoot;
+import com.intellij.ide.util.projectWizard.importSources.JavaSourceRootDetectionUtil;
+import com.intellij.lexer.JavaLexer;
+import com.intellij.lexer.Lexer;
+import com.intellij.openapi.module.StdModuleTypes;
+import com.intellij.openapi.progress.ProgressIndicator;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.pom.java.LanguageLevel;
+import com.intellij.psi.JavaTokenType;
+import com.intellij.util.Consumer;
+import com.intellij.util.StringBuilderSpinAllocator;
+import org.jetbrains.annotations.Nullable;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Collection;
+import java.util.Enumeration;
+import java.util.Set;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+
+public class JavaModuleInsight extends ModuleInsight {
+ private final JavaLexer myLexer;
+
+ public JavaModuleInsight(@Nullable final ProgressIndicator progress,
+ Set<String> existingModuleNames,
+ Set<String> existingProjectLibraryNames) {
+ super(progress, existingModuleNames, existingProjectLibraryNames);
+ myLexer = new JavaLexer(LanguageLevel.JDK_1_5);
+ }
+
+ @Override
+ protected boolean isSourceFile(final File file) {
+ return StringUtil.endsWithIgnoreCase(file.getName(), ".java");
+ }
+
+ @Override
+ protected boolean isLibraryFile(final String fileName) {
+ return StringUtil.endsWithIgnoreCase(fileName, ".jar") || StringUtil.endsWithIgnoreCase(fileName, ".zip");
+ }
+
+ @Override
+ protected void scanSourceFileForImportedPackages(final CharSequence chars, final Consumer<String> result) {
+ myLexer.start(chars);
+
+ JavaSourceRootDetectionUtil.skipWhiteSpaceAndComments(myLexer);
+ if (myLexer.getTokenType() == JavaTokenType.PACKAGE_KEYWORD) {
+ advanceLexer(myLexer);
+ if (readPackageName(chars, myLexer) == null) {
+ return;
+ }
+ }
+
+ while (true) {
+ if (myLexer.getTokenType() == JavaTokenType.SEMICOLON) {
+ advanceLexer(myLexer);
+ }
+ if (myLexer.getTokenType() != JavaTokenType.IMPORT_KEYWORD) {
+ return;
+ }
+ advanceLexer(myLexer);
+
+ boolean isStaticImport = false;
+ if (myLexer.getTokenType() == JavaTokenType.STATIC_KEYWORD) {
+ isStaticImport = true;
+ advanceLexer(myLexer);
+ }
+
+ final String packageName = readPackageName(chars, myLexer);
+ if (packageName == null) {
+ return;
+ }
+
+ if (packageName.endsWith(".*")) {
+ result.consume(packageName.substring(0, packageName.length() - ".*".length()));
+ }
+ else {
+ int lastDot = packageName.lastIndexOf('.');
+ if (lastDot > 0) {
+ String _packageName = packageName.substring(0, lastDot);
+ if (isStaticImport) {
+ lastDot = _packageName.lastIndexOf('.');
+ if (lastDot > 0) {
+ result.consume(_packageName.substring(0, lastDot));
+ }
+ }
+ else {
+ result.consume(_packageName);
+ }
+ }
+ }
+ }
+ }
+
+ @Nullable
+ private static String readPackageName(final CharSequence text, final Lexer lexer) {
+ final StringBuilder buffer = StringBuilderSpinAllocator.alloc();
+ try {
+ while (true) {
+ if (lexer.getTokenType() != JavaTokenType.IDENTIFIER && lexer.getTokenType() != JavaTokenType.ASTERISK) {
+ break;
+ }
+ buffer.append(text, lexer.getTokenStart(), lexer.getTokenEnd());
+
+ advanceLexer(lexer);
+ if (lexer.getTokenType() != JavaTokenType.DOT) {
+ break;
+ }
+ buffer.append('.');
+
+ advanceLexer(lexer);
+ }
+
+ String packageName = buffer.toString();
+ if (packageName.length() == 0 || StringUtil.endsWithChar(packageName, '.') || StringUtil.startsWithChar(packageName, '*')) {
+ return null;
+ }
+ return packageName;
+ }
+ finally {
+ StringBuilderSpinAllocator.dispose(buffer);
+ }
+ }
+
+ private static void advanceLexer(final Lexer lexer) {
+ lexer.advance();
+ JavaSourceRootDetectionUtil.skipWhiteSpaceAndComments(lexer);
+ }
+
+ @Override
+ protected void scanLibraryForDeclaredPackages(File file, Consumer<String> result) throws IOException {
+ final ZipFile zip = new ZipFile(file);
+ try {
+ final Enumeration<? extends ZipEntry> entries = zip.entries();
+ while (entries.hasMoreElements()) {
+ final String entryName = entries.nextElement().getName();
+ if (StringUtil.endsWithIgnoreCase(entryName, ".class")) {
+ final int index = entryName.lastIndexOf('/');
+ if (index > 0) {
+ final String packageName = entryName.substring(0, index).replace('/', '.');
+ result.consume(packageName);
+ }
+ }
+ }
+ }
+ finally {
+ zip.close();
+ }
+ }
+
+ protected ModuleDescriptor createModuleDescriptor(final File moduleContentRoot, final Collection<DetectedProjectRoot> sourceRoots) {
+ return new ModuleDescriptor(moduleContentRoot, StdModuleTypes.JAVA, sourceRoots);
+ }
+
+ public boolean isApplicableRoot(final DetectedProjectRoot root) {
+ return root instanceof JavaModuleSourceRoot;
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/ide/util/importProject/LibrariesDetectionStep.java b/java/idea-ui/src/com/intellij/ide/util/importProject/LibrariesDetectionStep.java
new file mode 100644
index 0000000..c62ba5a
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/ide/util/importProject/LibrariesDetectionStep.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright 2000-2009 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.ide.util.importProject;
+
+import com.intellij.ide.util.projectWizard.AbstractStepWithProgress;
+import com.intellij.ide.util.projectWizard.importSources.*;
+import com.intellij.openapi.fileTypes.FileTypeManager;
+import org.jetbrains.annotations.NonNls;
+
+import javax.swing.*;
+import java.io.File;
+import java.util.*;
+
+/**
+ * @author Eugene Zhuravlev
+ * Date: Jan 6, 2004
+ */
+public class LibrariesDetectionStep extends AbstractStepWithProgress<List<LibraryDescriptor>> {
+ private final ProjectFromSourcesBuilder myBuilder;
+ private final ProjectDescriptor myProjectDescriptor;
+ private final ModuleInsight myInsight;
+ private final Icon myIcon;
+ private final String myHelpId;
+ private LibrariesLayoutPanel myLibrariesPanel;
+
+ public LibrariesDetectionStep(ProjectFromSourcesBuilder builder,
+ ProjectDescriptor projectDescriptor, final ModuleInsight insight,
+ Icon icon,
+ @NonNls String helpId) {
+ super("Stop library analysis?");
+ myBuilder = builder;
+ myProjectDescriptor = projectDescriptor;
+ myInsight = insight;
+ myIcon = icon;
+ myHelpId = helpId;
+ }
+
+ public void updateDataModel() {
+ myProjectDescriptor.setLibraries(myLibrariesPanel.getChosenEntries());
+ }
+
+ protected JComponent createResultsPanel() {
+ myLibrariesPanel = new LibrariesLayoutPanel(myInsight);
+ return myLibrariesPanel;
+ }
+
+ protected String getProgressText() {
+ return "Searching for libraries. Please wait.";
+ }
+
+ int myPreviousStateHashCode = -1;
+ protected boolean shouldRunProgress() {
+ final int currentHash = calcStateHashCode();
+ try {
+ return currentHash != myPreviousStateHashCode;
+ }
+ finally {
+ myPreviousStateHashCode = currentHash;
+ }
+ }
+
+ private int calcStateHashCode() {
+ int hash = myBuilder.getBaseProjectPath().hashCode();
+ for (DetectedSourceRoot root : getSourceRoots()) {
+ hash = 31 * hash + root.getDirectory().hashCode();
+ }
+ return hash;
+ }
+
+ protected List<LibraryDescriptor> calculate() {
+ final List<DetectedSourceRoot> sourceRoots = getSourceRoots();
+
+ final HashSet<String> ignored = new HashSet<String>();
+ final StringTokenizer tokenizer = new StringTokenizer(FileTypeManager.getInstance().getIgnoredFilesList(), ";", false);
+ while (tokenizer.hasMoreTokens()) {
+ ignored.add(tokenizer.nextToken());
+ }
+
+ myInsight.setRoots(Collections.singletonList(new File(myBuilder.getBaseProjectPath())), sourceRoots, ignored);
+ myInsight.scanLibraries();
+
+ return myInsight.getSuggestedLibraries();
+ }
+
+ private List<DetectedSourceRoot> getSourceRoots() {
+ final List<DetectedSourceRoot> sourceRoots = new ArrayList<DetectedSourceRoot>();
+ for (ProjectStructureDetector detector : ProjectStructureDetector.EP_NAME.getExtensions()) {
+ for (DetectedProjectRoot root : myBuilder.getProjectRoots(detector)) {
+ if (myInsight.isApplicableRoot(root)) {
+ sourceRoots.add((DetectedSourceRoot)root);
+ }
+ }
+ }
+ return sourceRoots;
+ }
+
+ protected void onFinished(List<LibraryDescriptor> libraries, final boolean canceled) {
+ myLibrariesPanel.rebuild();
+ }
+
+ public Icon getIcon() {
+ return myIcon;
+ }
+
+ public String getHelpId() {
+ return myHelpId;
+ }
+
+}
\ No newline at end of file
diff --git a/java/idea-ui/src/com/intellij/ide/util/importProject/LibrariesLayoutPanel.java b/java/idea-ui/src/com/intellij/ide/util/importProject/LibrariesLayoutPanel.java
new file mode 100644
index 0000000..3f556fb
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/ide/util/importProject/LibrariesLayoutPanel.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright 2000-2009 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.ide.util.importProject;
+
+import java.io.File;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * @author Eugene Zhuravlev
+ * Date: Jul 18, 2007
+ */
+public class LibrariesLayoutPanel extends ProjectLayoutPanel<LibraryDescriptor>{
+
+ public LibrariesLayoutPanel(final ModuleInsight insight) {
+ super(insight);
+ }
+
+ protected String getElementName(final LibraryDescriptor entry) {
+ return entry.getName();
+ }
+
+ protected void setElementName(final LibraryDescriptor entry, final String name) {
+ entry.setName(name);
+ }
+
+ protected List<LibraryDescriptor> getEntries() {
+ final List<LibraryDescriptor> libs = getInsight().getSuggestedLibraries();
+ return libs != null? libs : Collections.<LibraryDescriptor>emptyList();
+ }
+
+ protected Collection getDependencies(final LibraryDescriptor entry) {
+ return entry.getJars();
+ }
+
+ protected LibraryDescriptor merge(final List<LibraryDescriptor> entries) {
+ final ModuleInsight insight = getInsight();
+ LibraryDescriptor mainLib = null;
+ for (LibraryDescriptor entry : entries) {
+ if (mainLib == null) {
+ mainLib = entry;
+ }
+ else {
+ final Collection<File> files = entry.getJars();
+ insight.moveJarsToLibrary(entry, files, mainLib);
+ }
+ }
+ return mainLib;
+ }
+
+ protected LibraryDescriptor split(final LibraryDescriptor entry, final String newEntryName, final Collection<File> extractedData) {
+ return getInsight().splitLibrary(entry, newEntryName, extractedData);
+ }
+
+ protected Collection<File> getContent(final LibraryDescriptor entry) {
+ return entry.getJars();
+ }
+
+ protected String getEntriesChooserTitle() {
+ return "Libraries";
+ }
+
+ protected String getDependenciesTitle() {
+ return "Library contents";
+ }
+
+ @Override
+ protected String getElementTypeName() {
+ return "library";
+ }
+
+ protected String getSplitDialogChooseFilesPrompt() {
+ return "&Select jars to extract to the new library:";
+ }
+
+ protected String getNameAlreadyUsedMessage(final String name) {
+ return "library with name " + name + " already exists";
+ }
+
+ protected String getStepDescriptionText() {
+ return "Please review libraries found. At this stage you can set library names that will be used in the project,\n" +
+ "exclude particular libraries from the project, or move individual files between the libraries.";
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/ide/util/importProject/LibraryDescriptor.java b/java/idea-ui/src/com/intellij/ide/util/importProject/LibraryDescriptor.java
new file mode 100644
index 0000000..abd5db5
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/ide/util/importProject/LibraryDescriptor.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2000-2009 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.ide.util.importProject;
+
+import java.io.File;
+import java.util.Collection;
+import java.util.Collections;
+
+/**
+ * @author Eugene Zhuravlev
+ * Date: Jul 13, 2007
+ */
+public class LibraryDescriptor {
+
+ public static enum Level {
+ GLOBAL, PROJECT, MODULE
+ }
+
+ private String myName;
+ private final Collection<File> myJars;
+ private Level myLevel;
+
+ public LibraryDescriptor(String name, Collection<File> jars) {
+ myName = name;
+ myJars = jars;
+ }
+
+ public String getName() {
+ return myName != null? myName : "";
+ }
+
+ public void setName(final String name) {
+ myName = name;
+ }
+
+ public Level getLevel() {
+ if (myLevel != null) {
+ return myLevel;
+ }
+ return myJars.size() > 1? Level.PROJECT : Level.MODULE;
+ }
+
+ public void setLevel(final Level level) {
+ myLevel = level;
+ }
+
+ public Collection<File> getJars() {
+ return Collections.unmodifiableCollection(myJars);
+ }
+
+ public void addJars(Collection<File> jars) {
+ myJars.addAll(jars);
+ }
+
+ public void removeJars(Collection<File> jars) {
+ myJars.removeAll(jars);
+ }
+
+ public String toString() {
+ return "Lib[" + myName + "]";
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/ide/util/importProject/ModuleDescriptor.java b/java/idea-ui/src/com/intellij/ide/util/importProject/ModuleDescriptor.java
new file mode 100644
index 0000000..2b2562d
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/ide/util/importProject/ModuleDescriptor.java
@@ -0,0 +1,181 @@
+/*
+ * Copyright 2000-2009 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.ide.util.importProject;
+
+import com.intellij.ide.highlighter.ModuleFileType;
+import com.intellij.ide.util.projectWizard.ModuleBuilder;
+import com.intellij.ide.util.projectWizard.importSources.DetectedProjectRoot;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.module.ModuleType;
+import com.intellij.openapi.roots.ModifiableRootModel;
+import com.intellij.openapi.util.InvalidDataException;
+import com.intellij.util.SmartList;
+import com.intellij.util.containers.MultiMap;
+import com.intellij.util.text.CaseInsensitiveStringHashingStrategy;
+import gnu.trove.THashSet;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+
+import java.io.File;
+import java.util.*;
+
+/**
+ * @author Eugene Zhuravlev
+* Date: Jul 13, 2007
+*/
+public class ModuleDescriptor {
+ private String myName;
+ private final MultiMap<File, DetectedProjectRoot> myContentToSourceRoots = new MultiMap<File, DetectedProjectRoot>();
+ private final Set<File> myLibraryFiles = new HashSet<File>();
+ private final Set<ModuleDescriptor> myDependencies = new HashSet<ModuleDescriptor>();
+ private static final Set<String> ourModuleNameStopList = new THashSet<String>(
+ Arrays.asList("java", "src", "source", "sources", "C:", "D:", "E:", "F:", "temp", "tmp"),
+ CaseInsensitiveStringHashingStrategy.INSTANCE
+ );
+
+ private boolean myReuseExistingElement;
+ private List<ModuleBuilder.ModuleConfigurationUpdater> myConfigurationUpdaters = new SmartList<ModuleBuilder.ModuleConfigurationUpdater>();
+ private ModuleType myModuleType;
+
+ public ModuleDescriptor(final File contentRoot, final ModuleType moduleType, final Collection<DetectedProjectRoot> sourceRoots) {
+ myName = suggestModuleName(contentRoot);
+ myContentToSourceRoots.putValues(contentRoot, sourceRoots);
+ myModuleType = moduleType;
+ }
+
+ public ModuleDescriptor(final File contentRoot, final ModuleType moduleType,
+ final DetectedProjectRoot sourceRoot) {
+ this(contentRoot, moduleType, Collections.singletonList(sourceRoot));
+ }
+
+ public void reuseExisting(boolean reuseExistingElement) {
+ myReuseExistingElement = reuseExistingElement;
+ }
+
+ public void addConfigurationUpdater(ModuleBuilder.ModuleConfigurationUpdater updater) {
+ myConfigurationUpdaters.add(updater);
+ }
+
+ public void updateModuleConfiguration(Module module, ModifiableRootModel rootModel) {
+ for (ModuleBuilder.ModuleConfigurationUpdater updater : myConfigurationUpdaters) {
+ updater.update(module, rootModel);
+ }
+ }
+
+ public boolean isReuseExistingElement() {
+ return myReuseExistingElement;
+ }
+
+ public ModuleType getModuleType() {
+ return myModuleType;
+ }
+
+ private static String suggestModuleName(final File contentRoot) {
+ for (File dir = contentRoot; dir != null; dir = dir.getParentFile()) {
+ final String suggestion = dir.getName();
+ if (!ourModuleNameStopList.contains(suggestion)) {
+ return suggestion;
+ }
+ }
+
+ return contentRoot.getName();
+ }
+
+ public String getName() {
+ return myName;
+ }
+
+ public void setName(final String name) {
+ myName = name;
+ }
+
+ public Set<File> getContentRoots() {
+ return Collections.unmodifiableSet(myContentToSourceRoots.keySet());
+ }
+
+ public Collection<? extends DetectedProjectRoot> getSourceRoots() {
+ return myContentToSourceRoots.values();
+ }
+
+ public Collection<DetectedProjectRoot> getSourceRoots(File contentRoot) {
+ return myContentToSourceRoots.get(contentRoot);
+ }
+
+ public void addContentRoot(File contentRoot) {
+ myContentToSourceRoots.put(contentRoot, new HashSet<DetectedProjectRoot>());
+ }
+
+ public Collection<DetectedProjectRoot> removeContentRoot(File contentRoot) {
+ return myContentToSourceRoots.remove(contentRoot);
+ }
+
+ public void addSourceRoot(final File contentRoot, DetectedProjectRoot sourceRoot) {
+ myContentToSourceRoots.putValue(contentRoot, sourceRoot);
+ }
+
+ public void addDependencyOn(ModuleDescriptor dependence) {
+ myDependencies.add(dependence);
+ }
+
+ public void removeDependencyOn(ModuleDescriptor module) {
+ myDependencies.remove(module);
+ }
+
+ public void addLibraryFile(File libFile) {
+ myLibraryFiles.add(libFile);
+ }
+
+ public Set<File> getLibraryFiles() {
+ return myLibraryFiles;
+ }
+
+ public Set<ModuleDescriptor> getDependencies() {
+ return Collections.unmodifiableSet(myDependencies);
+ }
+
+ /**
+ * For debug purposes only
+ */
+ public String toString() {
+ @NonNls final StringBuilder builder = new StringBuilder();
+ builder.append("[Module: ").append(getContentRoots()).append(" | ");
+ for (DetectedProjectRoot sourceRoot : getSourceRoots()) {
+ builder.append(sourceRoot.getDirectory().getName()).append(",");
+ }
+ builder.append("]");
+ return builder.toString();
+ }
+
+ public void clearModuleDependencies() {
+ myDependencies.clear();
+ }
+
+ public void clearLibraryFiles() {
+ myLibraryFiles.clear();
+ }
+
+ @NotNull
+ public String computeModuleFilePath() throws InvalidDataException {
+ final String name = getName();
+ final Set<File> contentRoots = getContentRoots();
+ if (contentRoots.size() > 0) {
+ return contentRoots.iterator().next().getPath() + File.separator + name + ModuleFileType.DOT_DEFAULT_EXTENSION;
+ }
+ else {
+ throw new InvalidDataException("Module " + name + " has no content roots and will not be created.");
+ }
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/ide/util/importProject/ModuleInsight.java b/java/idea-ui/src/com/intellij/ide/util/importProject/ModuleInsight.java
new file mode 100644
index 0000000..816878d
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/ide/util/importProject/ModuleInsight.java
@@ -0,0 +1,484 @@
+/*
+ * Copyright 2000-2009 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.ide.util.importProject;
+
+import com.intellij.ide.util.projectWizard.importSources.DetectedProjectRoot;
+import com.intellij.ide.util.projectWizard.importSources.impl.ProjectFromSourcesBuilderImpl;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.progress.ProcessCanceledException;
+import com.intellij.openapi.progress.ProgressIndicator;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.util.Consumer;
+import com.intellij.util.StringBuilderSpinAllocator;
+import com.intellij.util.containers.ContainerUtil;
+import com.intellij.util.containers.StringInterner;
+import com.intellij.util.text.CharArrayCharSequence;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.*;
+
+/**
+ * @author Eugene Zhuravlev
+ * Date: Jul 3, 2007
+ */
+public abstract class ModuleInsight {
+ private static final Logger LOG = Logger.getInstance("#com.intellij.ide.util.importProject.ModuleInsight");
+ @NotNull private final ProgressIndicatorWrapper myProgress;
+
+ private final Set<File> myEntryPointRoots = new HashSet<File>();
+ private final List<DetectedProjectRoot> mySourceRoots = new ArrayList<DetectedProjectRoot>();
+ private final Set<String> myIgnoredNames = new HashSet<String>();
+
+ private final Map<File, Set<String>> mySourceRootToReferencedPackagesMap = new HashMap<File, Set<String>>();
+ private final Map<File, Set<String>> mySourceRootToPackagesMap = new HashMap<File, Set<String>>();
+ private final Map<File, Set<String>> myJarToPackagesMap = new HashMap<File, Set<String>>();
+ private final StringInterner myInterner = new StringInterner();
+
+ private List<ModuleDescriptor> myModules;
+ private List<LibraryDescriptor> myLibraries;
+ private final Set<String> myExistingModuleNames;
+ private final Set<String> myExistingProjectLibraryNames;
+
+ public ModuleInsight(@Nullable final ProgressIndicator progress, Set<String> existingModuleNames, Set<String> existingProjectLibraryNames) {
+ myExistingModuleNames = existingModuleNames;
+ myExistingProjectLibraryNames = existingProjectLibraryNames;
+ myProgress = new ProgressIndicatorWrapper(progress);
+ setRoots(Collections.<File>emptyList(), Collections.<DetectedProjectRoot>emptyList(), Collections.<String>emptySet());
+ }
+
+ public final void setRoots(final List<File> contentRoots, final List<? extends DetectedProjectRoot> sourceRoots, final Set<String> ignoredNames) {
+ myModules = null;
+ myLibraries = null;
+
+ myEntryPointRoots.clear();
+ myEntryPointRoots.addAll(contentRoots);
+
+ mySourceRoots.clear();
+ mySourceRoots.addAll(sourceRoots);
+
+ myIgnoredNames.clear();
+ myIgnoredNames.addAll(ignoredNames);
+
+ myJarToPackagesMap.clear();
+ myInterner.clear();
+ }
+
+ @Nullable
+ public List<LibraryDescriptor> getSuggestedLibraries() {
+ return myLibraries;
+ }
+
+ @Nullable
+ public List<ModuleDescriptor> getSuggestedModules() {
+ return myModules;
+ }
+
+ public void scanModules() {
+ myProgress.setIndeterminate(true);
+ final Map<File, ModuleDescriptor> contentRootToModules = new HashMap<File, ModuleDescriptor>();
+
+ try {
+ myProgress.pushState();
+
+ List<DetectedProjectRoot> processedRoots = new ArrayList<DetectedProjectRoot>();
+ for (DetectedProjectRoot root : mySourceRoots) {
+ final File sourceRoot = root.getDirectory();
+ if (myIgnoredNames.contains(sourceRoot.getName())) {
+ continue;
+ }
+ myProgress.setText("Scanning " + sourceRoot.getPath());
+
+ final HashSet<String> usedPackages = new HashSet<String>();
+ mySourceRootToReferencedPackagesMap.put(sourceRoot, usedPackages);
+
+ final HashSet<String> selfPackages = new HashSet<String>();
+ mySourceRootToPackagesMap.put(sourceRoot, selfPackages);
+
+ scanSources(sourceRoot, ProjectFromSourcesBuilderImpl.getPackagePrefix(root), usedPackages, selfPackages) ;
+ usedPackages.removeAll(selfPackages);
+ processedRoots.add(root);
+ }
+ myProgress.popState();
+
+ myProgress.pushState();
+ myProgress.setText("Building modules layout...");
+ for (DetectedProjectRoot sourceRoot : processedRoots) {
+ final File srcRoot = sourceRoot.getDirectory();
+ final File moduleContentRoot = myEntryPointRoots.contains(srcRoot)? srcRoot : srcRoot.getParentFile();
+ ModuleDescriptor moduleDescriptor = contentRootToModules.get(moduleContentRoot);
+ if (moduleDescriptor != null) {
+ moduleDescriptor.addSourceRoot(moduleContentRoot, sourceRoot);
+ }
+ else {
+ moduleDescriptor = createModuleDescriptor(moduleContentRoot, Collections.singletonList(sourceRoot));
+ contentRootToModules.put(moduleContentRoot, moduleDescriptor);
+ }
+ }
+
+ buildModuleDependencies(contentRootToModules);
+
+ myProgress.popState();
+ }
+ catch (ProcessCanceledException ignored) {
+ }
+
+ myModules = new ArrayList<ModuleDescriptor>(contentRootToModules.values());
+ final Set<String> moduleNames = new HashSet<String>(myExistingModuleNames);
+ for (ModuleDescriptor module : myModules) {
+ final String suggested = suggestUniqueName(moduleNames, module.getName());
+ module.setName(suggested);
+ moduleNames.add(suggested);
+ }
+ }
+
+ protected abstract ModuleDescriptor createModuleDescriptor(final File moduleContentRoot, Collection<DetectedProjectRoot> sourceRoots);
+
+ private void buildModuleDependencies(final Map<File, ModuleDescriptor> contentRootToModules) {
+ final Set<File> moduleContentRoots = contentRootToModules.keySet();
+
+ for (File contentRoot : moduleContentRoots) {
+ final ModuleDescriptor checkedModule = contentRootToModules.get(contentRoot);
+ myProgress.setText2("Building library dependencies for module " + checkedModule.getName());
+ buildJarDependencies(checkedModule);
+
+ myProgress.setText2("Building module dependencies for module " + checkedModule.getName());
+ for (File aContentRoot : moduleContentRoots) {
+ final ModuleDescriptor aModule = contentRootToModules.get(aContentRoot);
+ if (checkedModule.equals(aModule)) {
+ continue; // avoid self-dependencies
+ }
+ final Collection<? extends DetectedProjectRoot> aModuleRoots = aModule.getSourceRoots();
+ checkModules:
+ for (DetectedProjectRoot srcRoot: checkedModule.getSourceRoots()) {
+ final Set<String> referencedBySourceRoot = mySourceRootToReferencedPackagesMap.get(srcRoot.getDirectory());
+ for (DetectedProjectRoot aSourceRoot : aModuleRoots) {
+ if (ContainerUtil.intersects(referencedBySourceRoot, mySourceRootToPackagesMap.get(aSourceRoot.getDirectory()))) {
+ checkedModule.addDependencyOn(aModule);
+ break checkModules;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private void buildJarDependencies(final ModuleDescriptor module) {
+ for (File jarFile : myJarToPackagesMap.keySet()) {
+ final Set<String> jarPackages = myJarToPackagesMap.get(jarFile);
+ for (DetectedProjectRoot srcRoot : module.getSourceRoots()) {
+ if (ContainerUtil.intersects(mySourceRootToReferencedPackagesMap.get(srcRoot.getDirectory()), jarPackages)) {
+ module.addLibraryFile(jarFile);
+ break;
+ }
+ }
+ }
+ }
+
+ public void scanLibraries() {
+ myProgress.setIndeterminate(true);
+ myProgress.pushState();
+ try {
+ try {
+ for (File root : myEntryPointRoots) {
+ myProgress.setText("Scanning for libraries " + root.getPath());
+ scanRootForLibraries(root);
+ }
+ }
+ catch (ProcessCanceledException ignored) {
+ }
+ myProgress.setText("Building initial libraries layout...");
+ final List<LibraryDescriptor> libraries = buildInitialLibrariesLayout(myJarToPackagesMap.keySet());
+ // correct library names so that there are no duplicates
+ final Set<String> libNames = new HashSet<String>(myExistingProjectLibraryNames);
+ for (LibraryDescriptor library : libraries) {
+ final Collection<File> libJars = library.getJars();
+ final String newName = suggestUniqueName(libNames, libJars.size() == 1? libJars.iterator().next().getName() : library.getName());
+ library.setName(newName);
+ libNames.add(newName);
+ }
+ myLibraries = libraries;
+ }
+ finally {
+ myProgress.popState();
+ }
+ }
+
+ public abstract boolean isApplicableRoot(final DetectedProjectRoot root);
+
+ private static String suggestUniqueName(Set<String> existingNames, String baseName) {
+ String name = baseName;
+ int index = 1;
+ while (existingNames.contains(name)) {
+ name = baseName + (index++);
+ }
+ return name;
+ }
+
+ public void merge(final ModuleDescriptor mainModule, final ModuleDescriptor module) {
+ for (File contentRoot : module.getContentRoots()) {
+ final File _contentRoot = appendContentRoot(mainModule, contentRoot);
+ final Collection<DetectedProjectRoot> sources = module.getSourceRoots(contentRoot);
+ for (DetectedProjectRoot source : sources) {
+ mainModule.addSourceRoot(_contentRoot, source);
+ }
+ }
+ for (File jar : module.getLibraryFiles()) {
+ mainModule.addLibraryFile(jar);
+ }
+ // fix forward dependencies
+ for (ModuleDescriptor dependency : module.getDependencies()) {
+ if (!mainModule.equals(dependency)) { // avoid self-dependencies
+ mainModule.addDependencyOn(dependency);
+ }
+ }
+
+ myModules.remove(module);
+ // fix back dependencies
+ for (ModuleDescriptor moduleDescr : myModules) {
+ if (moduleDescr.getDependencies().contains(module)) {
+ moduleDescr.removeDependencyOn(module);
+ if (!moduleDescr.equals(mainModule)) { // avoid self-dependencies
+ moduleDescr.addDependencyOn(mainModule);
+ }
+ }
+ }
+ }
+
+ public LibraryDescriptor splitLibrary(LibraryDescriptor library, String newLibraryName, final Collection<File> jarsToExtract) {
+ final LibraryDescriptor newLibrary = new LibraryDescriptor(newLibraryName, jarsToExtract);
+ myLibraries.add(newLibrary);
+ library.removeJars(jarsToExtract);
+ if (library.getJars().size() == 0) {
+ removeLibrary(library);
+ }
+ return newLibrary;
+ }
+
+ @Nullable
+ public ModuleDescriptor splitModule(final ModuleDescriptor descriptor, String newModuleName, final Collection<File> contentsToExtract) {
+ ModuleDescriptor newModule = null;
+ for (File root : contentsToExtract) {
+ final Collection<DetectedProjectRoot> sources = descriptor.removeContentRoot(root);
+ if (newModule == null) {
+ newModule = createModuleDescriptor(root, sources != null ? sources : new HashSet<DetectedProjectRoot>());
+ }
+ else {
+ if (sources != null && sources.size() > 0) {
+ for (DetectedProjectRoot source : sources) {
+ newModule.addSourceRoot(root, source);
+ }
+ }
+ else {
+ newModule.addContentRoot(root);
+ }
+ }
+ }
+
+ if (newModule != null) {
+ newModule.setName(newModuleName);
+ myModules.add(newModule);
+ }
+ else {
+ return null;
+ }
+
+ final Map<File, ModuleDescriptor> contentRootToModule = new HashMap<File, ModuleDescriptor>();
+ for (ModuleDescriptor module : myModules) {
+ final Set<File> roots = module.getContentRoots();
+ for (File root : roots) {
+ contentRootToModule.put(root, module);
+ }
+ module.clearModuleDependencies();
+ module.clearLibraryFiles();
+ }
+
+ buildModuleDependencies(contentRootToModule);
+ return newModule;
+ }
+
+ public void removeLibrary(LibraryDescriptor lib) {
+ myLibraries.remove(lib);
+ }
+
+ public void moveJarsToLibrary(final LibraryDescriptor from, Collection<File> files, LibraryDescriptor to) {
+ to.addJars(files);
+ from.removeJars(files);
+ // remove the library if it became empty
+ if (from.getJars().size() == 0) {
+ removeLibrary(from);
+ }
+ }
+
+ public Collection<LibraryDescriptor> getLibraryDependencies(ModuleDescriptor module) {
+ return getLibraryDependencies(module, myLibraries);
+ }
+
+ public static Collection<LibraryDescriptor> getLibraryDependencies(ModuleDescriptor module,
+ final List<LibraryDescriptor> allLibraries) {
+ final Set<LibraryDescriptor> libs = new HashSet<LibraryDescriptor>();
+ for (LibraryDescriptor library : allLibraries) {
+ if (ContainerUtil.intersects(library.getJars(), module.getLibraryFiles())) {
+ libs.add(library);
+ }
+ }
+ return libs;
+ }
+
+ private static File appendContentRoot(final ModuleDescriptor module, final File contentRoot) {
+ final Set<File> moduleRoots = module.getContentRoots();
+ for (File moduleRoot : moduleRoots) {
+ if (FileUtil.isAncestor(moduleRoot, contentRoot, false)) {
+ return moduleRoot; // no need to include a separate root
+ }
+ if (FileUtil.isAncestor(contentRoot, moduleRoot, true)) {
+ final Collection<DetectedProjectRoot> currentSources = module.getSourceRoots(moduleRoot);
+ module.removeContentRoot(moduleRoot);
+ module.addContentRoot(contentRoot);
+ for (DetectedProjectRoot source : currentSources) {
+ module.addSourceRoot(contentRoot, source);
+ }
+ return contentRoot; // no need to include a separate root
+ }
+ }
+ module.addContentRoot(contentRoot);
+ return contentRoot;
+ }
+
+
+ private static List<LibraryDescriptor> buildInitialLibrariesLayout(final Set<File> jars) {
+ final Map<File, LibraryDescriptor> rootToLibraryMap = new HashMap<File, LibraryDescriptor>();
+ for (File jar : jars) {
+ final File parent = jar.getParentFile();
+ LibraryDescriptor lib = rootToLibraryMap.get(parent);
+ if (lib == null) {
+ lib = new LibraryDescriptor(parent.getName(), new HashSet<File>());
+ rootToLibraryMap.put(parent, lib);
+ }
+ lib.addJars(Collections.singleton(jar));
+ }
+ return new ArrayList<LibraryDescriptor>(rootToLibraryMap.values());
+ }
+
+ private void scanSources(final File fromRoot, final String parentPackageName, final Set<String> usedPackages, final Set<String> selfPackages) {
+ if (myIgnoredNames.contains(fromRoot.getName())) {
+ return;
+ }
+ final File[] files = fromRoot.listFiles();
+ if (files != null) {
+ myProgress.checkCanceled();
+ boolean includeParentName = false;
+ for (File file : files) {
+ if (file.isDirectory()) {
+ final String subPackageName;
+ final StringBuilder builder = StringBuilderSpinAllocator.alloc();
+ try {
+ builder.append(parentPackageName);
+ if (builder.length() > 0) {
+ builder.append(".");
+ }
+ builder.append(file.getName());
+ subPackageName = builder.toString();
+ }
+ finally {
+ StringBuilderSpinAllocator.dispose(builder);
+ }
+ scanSources(file, subPackageName, usedPackages, selfPackages);
+ }
+ else {
+ if (isSourceFile(file)) {
+ includeParentName = true;
+ scanSourceFile(file, usedPackages);
+ }
+ }
+ }
+ if (includeParentName) {
+ selfPackages.add(myInterner.intern(parentPackageName));
+ }
+ }
+ }
+
+ protected abstract boolean isSourceFile(final File file);
+
+ private void scanSourceFile(File file, final Set<String> usedPackages) {
+ myProgress.setText2(file.getName());
+ try {
+ final char[] chars = FileUtil.loadFileText(file);
+ scanSourceFileForImportedPackages(new CharArrayCharSequence(chars), new Consumer<String>() {
+ public void consume(final String s) {
+ usedPackages.add(myInterner.intern(s));
+ }
+ });
+ }
+ catch (IOException e) {
+ LOG.info(e);
+ }
+ }
+
+ protected abstract void scanSourceFileForImportedPackages(final CharSequence chars, Consumer<String> result);
+
+ private void scanRootForLibraries(File fromRoot) {
+ if (myIgnoredNames.contains(fromRoot.getName())) {
+ return;
+ }
+ final File[] files = fromRoot.listFiles();
+ if (files != null) {
+ myProgress.checkCanceled();
+ for (File file : files) {
+ if (file.isDirectory()) {
+ scanRootForLibraries(file);
+ }
+ else {
+ final String fileName = file.getName();
+ if (isLibraryFile(fileName)) {
+ if (!myJarToPackagesMap.containsKey(file)) {
+ final HashSet<String> libraryPackages = new HashSet<String>();
+ myJarToPackagesMap.put(file, libraryPackages);
+
+ myProgress.pushState();
+ myProgress.setText2(file.getName());
+ try {
+ scanLibraryForDeclaredPackages(file, new Consumer<String>() {
+ public void consume(final String s) {
+ if (!libraryPackages.contains(s)) {
+ libraryPackages.add(myInterner.intern(s));
+ }
+ }
+ });
+ }
+ catch (IOException e) {
+ LOG.info(e);
+ }
+ catch (InternalError e) { // indicates that file is somehow damaged and cannot be processed
+ LOG.info(e);
+ }
+ finally {
+ myProgress.popState();
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ protected abstract boolean isLibraryFile(final String fileName);
+
+ protected abstract void scanLibraryForDeclaredPackages(File file, Consumer<String> result) throws IOException;
+
+}
diff --git a/java/idea-ui/src/com/intellij/ide/util/importProject/ModulesDetectionStep.java b/java/idea-ui/src/com/intellij/ide/util/importProject/ModulesDetectionStep.java
new file mode 100644
index 0000000..a3877c3
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/ide/util/importProject/ModulesDetectionStep.java
@@ -0,0 +1,160 @@
+/*
+ * Copyright 2000-2009 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.ide.util.importProject;
+
+import com.intellij.ide.IdeBundle;
+import com.intellij.ide.util.projectWizard.AbstractStepWithProgress;
+import com.intellij.ide.util.projectWizard.importSources.DetectedProjectRoot;
+import com.intellij.ide.util.projectWizard.importSources.ProjectFromSourcesBuilder;
+import com.intellij.ide.util.projectWizard.importSources.ProjectStructureDetector;
+import com.intellij.openapi.options.ConfigurationException;
+import com.intellij.openapi.ui.Messages;
+import com.intellij.openapi.util.InvalidDataException;
+import com.intellij.openapi.util.text.StringUtil;
+import org.jetbrains.annotations.NonNls;
+
+import javax.swing.*;
+import java.io.File;
+import java.util.*;
+
+/**
+ * @author Eugene Zhuravlev
+ * Date: Jul 18, 2007
+ */
+public class ModulesDetectionStep extends AbstractStepWithProgress<List<ModuleDescriptor>> {
+ private final ProjectStructureDetector myDetector;
+ private final ProjectFromSourcesBuilder myBuilder;
+ private final ProjectDescriptor myProjectDescriptor;
+ private final ModuleInsight myInsight;
+ private final Icon myIcon;
+ private final String myHelpId;
+ private ModulesLayoutPanel myModulesLayoutPanel;
+
+ public ModulesDetectionStep(ProjectStructureDetector detector,
+ ProjectFromSourcesBuilder builder,
+ ProjectDescriptor projectDescriptor, final ModuleInsight insight,
+ Icon icon,
+ @NonNls String helpId) {
+ super("Stop module analysis?");
+ myDetector = detector;
+ myBuilder = builder;
+ myProjectDescriptor = projectDescriptor;
+ myInsight = insight;
+ myIcon = icon;
+ myHelpId = helpId;
+ }
+
+ public void updateDataModel() {
+ myProjectDescriptor.setModules(myModulesLayoutPanel.getChosenEntries());
+ }
+
+ protected JComponent createResultsPanel() {
+ myModulesLayoutPanel = new ModulesLayoutPanel(myInsight, new ModulesLayoutPanel.LibraryFilter() {
+ public boolean isLibraryChosen(final LibraryDescriptor libDescriptor) {
+ return myProjectDescriptor.isLibraryChosen(libDescriptor);
+ }
+ });
+ return myModulesLayoutPanel;
+ }
+
+ protected String getProgressText() {
+ return "Searching for modules. Please wait.";
+ }
+
+ int myPreviousStateHashCode = -1;
+ protected boolean shouldRunProgress() {
+ final int currentHash = calcStateHashCode();
+ try {
+ return currentHash != myPreviousStateHashCode;
+ }
+ finally {
+ myPreviousStateHashCode = currentHash;
+ }
+ }
+
+ private int calcStateHashCode() {
+ final String contentEntryPath = myBuilder.getBaseProjectPath();
+ int hash = contentEntryPath != null? contentEntryPath.hashCode() : 1;
+ for (DetectedProjectRoot root : myBuilder.getProjectRoots(myDetector)) {
+ hash = 31 * hash + root.getDirectory().hashCode();
+ }
+ final List<LibraryDescriptor> libs = myProjectDescriptor.getLibraries();
+ for (LibraryDescriptor lib : libs) {
+ final Collection<File> files = lib.getJars();
+ for (File file : files) {
+ hash = 31 * hash + file.hashCode();
+ }
+ }
+ return hash;
+ }
+
+ protected List<ModuleDescriptor> calculate() {
+ myInsight.scanModules();
+ final List<ModuleDescriptor> suggestedModules = myInsight.getSuggestedModules();
+ return suggestedModules != null? suggestedModules : Collections.<ModuleDescriptor>emptyList();
+ }
+
+ @Override
+ public boolean validate() throws ConfigurationException {
+ final boolean validated = super.validate();
+ if (!validated) {
+ return false;
+ }
+
+ final List<ModuleDescriptor> modules = myModulesLayoutPanel.getChosenEntries();
+ final Map<String, ModuleDescriptor> errors = new LinkedHashMap<String, ModuleDescriptor>();
+ for (ModuleDescriptor module : modules) {
+ try {
+ final String moduleFilePath = module.computeModuleFilePath();
+ if (new File(moduleFilePath).exists()) {
+ errors.put(IdeBundle.message("warning.message.the.module.file.0.already.exist.and.will.be.overwritten", moduleFilePath), module);
+ }
+ }
+ catch (InvalidDataException e) {
+ errors.put(e.getMessage(), module);
+ }
+ }
+ if (!errors.isEmpty()) {
+ final int answer = Messages.showYesNoCancelDialog(getComponent(),
+ IdeBundle.message("warning.text.0.do.you.want.to.overwrite.these.files",
+ StringUtil.join(errors.keySet(), "\n"), errors.size()),
+ IdeBundle.message("title.file.already.exists"), "Overwrite", "Reuse", "Cancel", Messages.getQuestionIcon());
+ if (answer == 2) {
+ return false;
+ }
+
+ if (answer != 0) {
+ for (ModuleDescriptor moduleDescriptor : errors.values()) {
+ moduleDescriptor.reuseExisting(true);
+ }
+ }
+ }
+ return true;
+ }
+
+ protected void onFinished(final List<ModuleDescriptor> moduleDescriptors, final boolean canceled) {
+ myModulesLayoutPanel.rebuild();
+ }
+
+ @NonNls
+ public String getHelpId() {
+ return myHelpId;
+ }
+
+ public Icon getIcon() {
+ return myIcon;
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/ide/util/importProject/ModulesLayoutPanel.java b/java/idea-ui/src/com/intellij/ide/util/importProject/ModulesLayoutPanel.java
new file mode 100644
index 0000000..c77a7e0
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/ide/util/importProject/ModulesLayoutPanel.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright 2000-2009 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.ide.util.importProject;
+
+import org.jetbrains.annotations.Nullable;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * @author Eugene Zhuravlev
+ * Date: Jul 16, 2007
+ */
+public class ModulesLayoutPanel extends ProjectLayoutPanel<ModuleDescriptor>{
+ private final LibraryFilter myLibrariesFilter;
+
+ public static interface LibraryFilter {
+ boolean isLibraryChosen(LibraryDescriptor libDescriptor);
+ }
+ public ModulesLayoutPanel(ModuleInsight insight, final LibraryFilter libFilter) {
+ super(insight);
+ myLibrariesFilter = libFilter;
+ }
+
+ protected String getElementName(final ModuleDescriptor entry) {
+ return entry.getName();
+ }
+
+ protected void setElementName(final ModuleDescriptor entry, final String name) {
+ entry.setName(name);
+ }
+
+ protected List<ModuleDescriptor> getEntries() {
+ final List<ModuleDescriptor> modules = getInsight().getSuggestedModules();
+ return modules != null? modules : Collections.<ModuleDescriptor>emptyList();
+ }
+
+ protected Collection getDependencies(final ModuleDescriptor entry) {
+ final List deps = new ArrayList();
+ deps.addAll(entry.getDependencies());
+ final Collection<LibraryDescriptor> libDependencies = getInsight().getLibraryDependencies(entry);
+ for (LibraryDescriptor libDependency : libDependencies) {
+ if (myLibrariesFilter.isLibraryChosen(libDependency)) {
+ deps.add(libDependency);
+ }
+ }
+ return deps;
+ }
+
+ @Nullable
+ protected ModuleDescriptor merge(final List<ModuleDescriptor> entries) {
+ final ModuleInsight insight = getInsight();
+ ModuleDescriptor mainDescr = null;
+ for (ModuleDescriptor entry : entries) {
+ if (mainDescr == null) {
+ mainDescr = entry;
+ }
+ else {
+ insight.merge(mainDescr, entry);
+ }
+ }
+ return mainDescr;
+ }
+
+ protected ModuleDescriptor split(final ModuleDescriptor entry, final String newEntryName, final Collection<File> extractedData) {
+ return getInsight().splitModule(entry, newEntryName, extractedData);
+ }
+
+ protected Collection<File> getContent(final ModuleDescriptor entry) {
+ return entry.getContentRoots();
+ }
+
+ protected String getEntriesChooserTitle() {
+ return "Modules";
+ }
+
+ protected String getDependenciesTitle() {
+ return "Module dependencies";
+ }
+
+ @Override
+ protected String getElementTypeName() {
+ return "module";
+ }
+
+ protected String getSplitDialogChooseFilesPrompt() {
+ return "&Select content roots to extract to the new module:";
+ }
+
+ protected String getNameAlreadyUsedMessage(final String name) {
+ return "Module with name " + name + " already exists";
+ }
+
+ protected String getStepDescriptionText() {
+ return "Please review suggested module structure for the project. At this stage you can set module names,\n" +
+ "exclude particular modules from the project, merge or split individual modules.\n" +
+ "All dependencies between the modules as well as dependencies on the libraries will be automatically updated.";
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/ide/util/importProject/ProgressIndicatorWrapper.java b/java/idea-ui/src/com/intellij/ide/util/importProject/ProgressIndicatorWrapper.java
new file mode 100644
index 0000000..d25751c
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/ide/util/importProject/ProgressIndicatorWrapper.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2000-2009 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.ide.util.importProject;
+
+import com.intellij.openapi.progress.ProcessCanceledException;
+import com.intellij.openapi.progress.ProgressIndicator;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * @author Eugene Zhuravlev
+ * Date: Jul 5, 2007
+ */
+public class ProgressIndicatorWrapper {
+ @Nullable
+ private final ProgressIndicator myIndicator;
+
+ public ProgressIndicatorWrapper(@Nullable ProgressIndicator indicator) {
+ myIndicator = indicator;
+ }
+
+ public boolean isCanceled() {
+ return myIndicator != null && myIndicator.isCanceled();
+ }
+
+ public void setText(final String text) {
+ if (myIndicator != null) {
+ myIndicator.setText(text);
+ }
+ }
+
+ public void setText2(final String text) {
+ if (myIndicator != null) {
+ myIndicator.setText2(text);
+ }
+ }
+
+ public void setFraction(final double fraction) {
+ if (myIndicator != null) {
+ myIndicator.setFraction(fraction);
+ }
+ }
+
+ public void pushState() {
+ if (myIndicator != null) {
+ myIndicator.pushState();
+ }
+ }
+
+ public void popState() {
+ if (myIndicator != null) {
+ myIndicator.popState();
+ }
+ }
+
+ public void setIndeterminate(final boolean indeterminate) {
+ if (myIndicator != null) {
+ myIndicator.setIndeterminate(indeterminate);
+ }
+ }
+
+ public void checkCanceled() throws ProcessCanceledException {
+ if (myIndicator != null) {
+ myIndicator.checkCanceled();
+ }
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/ide/util/importProject/ProjectDescriptor.java b/java/idea-ui/src/com/intellij/ide/util/importProject/ProjectDescriptor.java
new file mode 100644
index 0000000..a7f1192
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/ide/util/importProject/ProjectDescriptor.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2000-2011 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.ide.util.importProject;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * @author nik
+ */
+public class ProjectDescriptor {
+ private List<ModuleDescriptor> myModules = Collections.emptyList();
+ private List<LibraryDescriptor> myLibraries = Collections.emptyList();
+ private Set<LibraryDescriptor> myLibrariesSet = Collections.emptySet();
+
+ public List<ModuleDescriptor> getModules() {
+ return myModules;
+ }
+
+ public List<LibraryDescriptor> getLibraries() {
+ return myLibraries;
+ }
+
+ public void setModules(List<ModuleDescriptor> modules) {
+ myModules = modules;
+ }
+
+ public void setLibraries(List<LibraryDescriptor> libraries) {
+ myLibraries = libraries;
+ myLibrariesSet = null;
+ }
+
+ public boolean isLibraryChosen(LibraryDescriptor lib) {
+ Set<LibraryDescriptor> available = myLibrariesSet;
+ if (available == null) {
+ available = new HashSet<LibraryDescriptor>(myLibraries);
+ myLibrariesSet = available;
+ }
+ return available.contains(lib);
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/ide/util/importProject/ProjectLayoutPanel.java b/java/idea-ui/src/com/intellij/ide/util/importProject/ProjectLayoutPanel.java
new file mode 100644
index 0000000..7127849
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/ide/util/importProject/ProjectLayoutPanel.java
@@ -0,0 +1,554 @@
+/*
+ * Copyright 2000-2009 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.ide.util.importProject;
+
+import com.intellij.icons.AllIcons;
+import com.intellij.ide.util.ElementsChooser;
+import com.intellij.ide.util.projectWizard.importSources.DetectedProjectRoot;
+import com.intellij.openapi.actionSystem.*;
+import com.intellij.openapi.ui.DialogWrapper;
+import com.intellij.openapi.ui.InputValidator;
+import com.intellij.openapi.ui.Messages;
+import com.intellij.openapi.ui.Splitter;
+import com.intellij.openapi.ui.ex.MultiLineLabel;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.ui.DocumentAdapter;
+import com.intellij.ui.IdeBorderFactory;
+import com.intellij.ui.ScrollPaneFactory;
+import com.intellij.ui.components.JBList;
+import com.intellij.util.IconUtil;
+import com.intellij.util.PlatformIcons;
+import com.intellij.util.StringBuilderSpinAllocator;
+import com.intellij.util.containers.ContainerUtil;
+import com.intellij.util.ui.FormBuilder;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import javax.swing.event.DocumentEvent;
+import javax.swing.event.ListSelectionEvent;
+import javax.swing.event.ListSelectionListener;
+import java.awt.*;
+import java.awt.event.KeyEvent;
+import java.io.File;
+import java.util.*;
+import java.util.List;
+
+/**
+ * @author Eugene Zhuravlev
+ * Date: Jul 16, 2007
+ */
+abstract class ProjectLayoutPanel<T> extends JPanel {
+
+ private final ElementsChooser<T> myEntriesChooser;
+ private final JList myDependenciesList;
+ private final ModuleInsight myInsight;
+
+ private final Comparator<T> COMPARATOR = new Comparator<T>() {
+ public int compare(final T o1, final T o2) {
+ final int w1 = getWeight(o1);
+ final int w2 = getWeight(o2);
+ if (w1 != w2) {
+ return w1 - w2;
+ }
+ return getElementText(o1).compareToIgnoreCase(getElementText(o2));
+ }
+ };
+
+ public ProjectLayoutPanel(final ModuleInsight insight) {
+ super(new BorderLayout());
+ setBorder(BorderFactory.createEmptyBorder(5, 5, 0, 5));
+ myInsight = insight;
+
+ myEntriesChooser = new ElementsChooser<T>(true) {
+ public String getItemText(@NotNull T element) {
+ return getElementText(element);
+ }
+ };
+ myDependenciesList = createList();
+
+ final Splitter splitter = new Splitter(false);
+
+ final JPanel entriesPanel = new JPanel(new BorderLayout());
+ entriesPanel.add(myEntriesChooser, BorderLayout.CENTER);
+ entriesPanel.setBorder(IdeBorderFactory.createTitledBorder(StringUtil.capitalize(StringUtil.pluralize(getElementTypeName())), false));
+ splitter.setFirstComponent(entriesPanel);
+
+ final JScrollPane depsPane = ScrollPaneFactory.createScrollPane(myDependenciesList);
+ final JPanel depsPanel = new JPanel(new BorderLayout());
+ depsPanel.add(depsPane, BorderLayout.CENTER);
+ depsPanel.setBorder(IdeBorderFactory.createTitledBorder(getDependenciesTitle(), false));
+ splitter.setSecondComponent(depsPanel);
+
+ JPanel groupPanel = new JPanel(new BorderLayout());
+ groupPanel.add(createEntriesActionToolbar().getComponent(), BorderLayout.NORTH);
+ groupPanel.add(splitter, BorderLayout.CENTER);
+ groupPanel.setBorder(BorderFactory.createEmptyBorder(10, 0, 0, 0));
+
+ final MultiLineLabel description = new MultiLineLabel(getStepDescriptionText());
+ add(description, BorderLayout.NORTH);
+ add(groupPanel, BorderLayout.CENTER);
+
+ myEntriesChooser.addListSelectionListener(new ListSelectionListener() {
+ public void valueChanged(final ListSelectionEvent e) {
+ if (e.getValueIsAdjusting()) {
+ return;
+ }
+ final List<T> entries = getSelectedEntries();
+ final Collection deps = getDependencies(entries);
+
+ final DefaultListModel depsModel = (DefaultListModel)myDependenciesList.getModel();
+ depsModel.clear();
+ for (Object dep : alphaSortList(new ArrayList(deps))) {
+ depsModel.addElement(dep);
+ }
+ }
+ });
+ }
+
+ private ActionToolbar createEntriesActionToolbar() {
+ final DefaultActionGroup entriesActions = new DefaultActionGroup();
+
+ final RenameAction rename = new RenameAction();
+ rename.registerCustomShortcutSet(new CustomShortcutSet(KeyStroke.getKeyStroke(KeyEvent.VK_F6, KeyEvent.SHIFT_DOWN_MASK)), this);
+ entriesActions.add(rename);
+
+ final MergeAction merge = new MergeAction();
+ merge.registerCustomShortcutSet(new CustomShortcutSet(KeyStroke.getKeyStroke(KeyEvent.VK_F3, 0)), this);
+ entriesActions.add(merge);
+
+ final SplitAction split = new SplitAction();
+ split.registerCustomShortcutSet(new CustomShortcutSet(KeyStroke.getKeyStroke(KeyEvent.VK_F4, 0)), this);
+ entriesActions.add(split);
+
+ return ActionManager.getInstance().createActionToolbar("ProjectLayoutPanel.Entries", entriesActions, true);
+ }
+
+ public final ModuleInsight getInsight() {
+ return myInsight;
+ }
+
+ private JList createList() {
+ final JList list = new JBList(new DefaultListModel());
+ list.getSelectionModel().setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
+ list.setCellRenderer(new MyListCellRenderer());
+ return list;
+ }
+
+ public final Collection getDependencies(final List<T> entries) {
+ final Set deps = new HashSet();
+ for (T et : entries) {
+ deps.addAll(getDependencies(et));
+ }
+ return deps;
+ }
+
+ @NotNull
+ public List<T> getSelectedEntries() {
+ return myEntriesChooser.getSelectedElements();
+ }
+
+ @NotNull
+ public List<T> getChosenEntries() {
+ return myEntriesChooser.getMarkedElements();
+ }
+
+ public void rebuild() {
+ myEntriesChooser.clear();
+ for (final T entry : alphaSortList(getEntries())) {
+ myEntriesChooser.addElement(entry, true, new EntryProperties(entry));
+ }
+ if (myEntriesChooser.getElementCount() > 0) {
+ myEntriesChooser.selectElements(Collections.singleton(myEntriesChooser.getElementAt(0)));
+ }
+ }
+
+ private List<T> alphaSortList(final List<T> entries) {
+ Collections.sort(entries, COMPARATOR);
+ return entries;
+ }
+
+ @Nullable
+ protected Icon getElementIcon(Object element) {
+ if (element instanceof ModuleDescriptor) {
+ return ((ModuleDescriptor)element).getModuleType().getIcon();
+ }
+ if (element instanceof LibraryDescriptor) {
+ return PlatformIcons.LIBRARY_ICON;
+ }
+ if (element instanceof File) {
+ final File file = (File)element;
+ return file.isDirectory()? PlatformIcons.DIRECTORY_CLOSED_ICON : PlatformIcons.JAR_ICON;
+ }
+ return null;
+ }
+
+ protected int getWeight(Object element) {
+ if (element instanceof File) {
+ return 10;
+ }
+ if (element instanceof ModuleDescriptor) {
+ return 20;
+ }
+ if (element instanceof LibraryDescriptor) {
+ return ((LibraryDescriptor)element).getJars().size() > 1? 30 : 40;
+ }
+ return Integer.MAX_VALUE;
+ }
+
+ protected static String getElementText(Object element) {
+ if (element instanceof LibraryDescriptor) {
+ final StringBuilder builder = StringBuilderSpinAllocator.alloc();
+ try {
+ builder.append(((LibraryDescriptor)element).getName());
+ final Collection<File> jars = ((LibraryDescriptor)element).getJars();
+ if (jars.size() == 1) {
+ final File parentFile = jars.iterator().next().getParentFile();
+ if (parentFile != null) {
+ builder.append(" (");
+ builder.append(parentFile.getPath());
+ builder.append(")");
+ }
+ }
+ return builder.toString();
+ }
+ finally {
+ StringBuilderSpinAllocator.dispose(builder);
+ }
+ }
+
+ if (element instanceof File) {
+ final StringBuilder builder = StringBuilderSpinAllocator.alloc();
+ try {
+ builder.append(((File)element).getName());
+ final File parentFile = ((File)element).getParentFile();
+ if (parentFile != null) {
+ builder.append(" (");
+ builder.append(parentFile.getPath());
+ builder.append(")");
+ }
+ return builder.toString();
+ }
+ finally {
+ StringBuilderSpinAllocator.dispose(builder);
+ }
+ }
+
+ if (element instanceof ModuleDescriptor) {
+ final ModuleDescriptor moduleDescriptor = (ModuleDescriptor)element;
+ final StringBuilder builder = StringBuilderSpinAllocator.alloc();
+ try {
+ builder.append(moduleDescriptor.getName());
+
+ final Set<File> contents = moduleDescriptor.getContentRoots();
+ final int rootCount = contents.size();
+ if (rootCount > 0) {
+ builder.append(" (");
+ builder.append(contents.iterator().next().getPath());
+ if (rootCount > 1) {
+ builder.append("...");
+ }
+ builder.append(")");
+ }
+
+ final Collection<? extends DetectedProjectRoot> sourceRoots = moduleDescriptor.getSourceRoots();
+ if (sourceRoots.size() > 0) {
+ builder.append(" [");
+ for (Iterator<? extends DetectedProjectRoot> it = sourceRoots.iterator(); it.hasNext();) {
+ DetectedProjectRoot root = it.next();
+ builder.append(root.getDirectory().getName());
+ if (it.hasNext()) {
+ builder.append(",");
+ }
+ }
+ builder.append("]");
+ }
+ return builder.toString();
+ }
+ finally {
+ StringBuilderSpinAllocator.dispose(builder);
+ }
+ }
+
+ return "";
+ }
+
+ protected abstract List<T> getEntries();
+
+ protected abstract Collection getDependencies(T entry);
+
+ @Nullable
+ protected abstract T merge(List<T> entries);
+
+ @Nullable
+ protected abstract T split(T entry, String newEntryName, Collection<File> extractedData);
+
+ protected abstract Collection<File> getContent(T entry);
+
+ protected abstract String getElementName(T entry);
+
+ protected abstract void setElementName(T entry, String name);
+
+ protected abstract String getSplitDialogChooseFilesPrompt();
+
+ protected abstract String getNameAlreadyUsedMessage(final String name);
+
+ protected abstract String getStepDescriptionText();
+
+ protected abstract String getEntriesChooserTitle();
+
+ protected abstract String getDependenciesTitle();
+
+ protected abstract String getElementTypeName();
+
+ private boolean isNameAlreadyUsed(String entryName) {
+ final Set<T> itemsToIgnore = new HashSet<T>(myEntriesChooser.getSelectedElements());
+ for (T entry : getEntries()) {
+ if (itemsToIgnore.contains(entry)) {
+ continue;
+ }
+ if (entryName.equals(getElementName(entry))) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private class MergeAction extends AnAction {
+ private MergeAction() {
+ super("Merge", "", AllIcons.Modules.Merge); // todo
+ }
+
+ public void actionPerformed(final AnActionEvent e) {
+ final List<T> elements = myEntriesChooser.getSelectedElements();
+ if (elements.size() > 1) {
+ final String newName = Messages.showInputDialog(
+ ProjectLayoutPanel.this,
+ "Enter new name for merge result:",
+ "Merge",
+ Messages.getQuestionIcon(), getElementName(elements.get(0)), new InputValidator() {
+ public boolean checkInput(final String inputString) {
+ return true;
+ }
+
+ public boolean canClose(final String inputString) {
+ if (isNameAlreadyUsed(inputString.trim())) {
+ Messages.showErrorDialog(getNameAlreadyUsedMessage(inputString), "");
+ return false;
+ }
+ return true;
+ }
+ });
+ if (newName != null) {
+ final T merged = merge(elements);
+ setElementName(merged, newName);
+ for (T element : elements) {
+ myEntriesChooser.removeElement(element);
+ }
+ myEntriesChooser.addElement(merged, true, new EntryProperties(merged));
+ myEntriesChooser.sort(COMPARATOR);
+ myEntriesChooser.selectElements(Collections.singleton(merged));
+ }
+ }
+ }
+
+ public void update(final AnActionEvent e) {
+ super.update(e);
+ e.getPresentation().setEnabled(myEntriesChooser.getSelectedElements().size() > 1);
+ }
+
+ }
+
+ private class SplitAction extends AnAction {
+ private SplitAction() {
+ super("Split", "", AllIcons.Modules.Split); // todo
+ }
+
+ public void actionPerformed(final AnActionEvent e) {
+ final List<T> elements = myEntriesChooser.getSelectedElements();
+
+ if (elements.size() == 1) {
+ final T entry = elements.get(0);
+ final Collection<File> files = getContent(entry);
+
+ final SplitDialog dialog = new SplitDialog(files);
+ dialog.show();
+
+ if (dialog.isOK()) {
+ final String newName = dialog.getName();
+ final Collection<File> chosenFiles = dialog.getChosenFiles();
+
+ final T extracted = split(entry, newName, chosenFiles);
+ if (extracted != null) {
+ if (!getEntries().contains(entry)) {
+ myEntriesChooser.removeElement(entry);
+ }
+ myEntriesChooser.addElement(extracted, true, new EntryProperties(extracted));
+ myEntriesChooser.sort(COMPARATOR);
+ myEntriesChooser.selectElements(Collections.singleton(extracted));
+ }
+ }
+ }
+ }
+ public void update(final AnActionEvent e) {
+ final List<T> elements = myEntriesChooser.getSelectedElements();
+ e.getPresentation().setEnabled(elements.size() == 1 && getContent(elements.get(0)).size() > 1);
+ }
+ }
+
+ private class RenameAction extends AnAction {
+ private RenameAction() {
+ super("Rename", "", IconUtil.getEditIcon()); // todo
+ }
+
+ public void actionPerformed(final AnActionEvent e) {
+ final List<T> elements = myEntriesChooser.getSelectedElements();
+ if (elements.size() == 1) {
+ final T element = elements.get(0);
+ final String newName = Messages.showInputDialog(ProjectLayoutPanel.this, "New name for " + getElementTypeName() + " '" + getElementName(element) + "':",
+ "Rename " + StringUtil.capitalize(getElementTypeName()),
+ Messages.getQuestionIcon(),
+ getElementName(element),
+ new InputValidator() {
+ public boolean checkInput(final String inputString) {
+ return true;
+ }
+
+ public boolean canClose(final String inputString) {
+ if (isNameAlreadyUsed(inputString.trim())) {
+ Messages.showErrorDialog(getNameAlreadyUsedMessage(inputString), "");
+ return false;
+ }
+ return true;
+ }
+ }
+ );
+ if (newName != null) {
+ setElementName(element, newName);
+ myEntriesChooser.sort(COMPARATOR);
+ myEntriesChooser.selectElements(Collections.singleton(element));
+ }
+ }
+ }
+
+ public void update(final AnActionEvent e) {
+ super.update(e);
+ e.getPresentation().setEnabled(myEntriesChooser.getSelectedElements().size() == 1);
+ }
+ }
+
+ private class MyListCellRenderer extends DefaultListCellRenderer {
+ public Component getListCellRendererComponent(final JList list, final Object value, final int index, final boolean isSelected, final boolean cellHasFocus) {
+ final Component comp = super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
+ setText(getElementText(value));
+ setIcon(getElementIcon(value));
+ return comp;
+ }
+ }
+
+ private class SplitDialog extends DialogWrapper {
+ final JTextField myNameField;
+ final ElementsChooser<File> myChooser;
+
+ private SplitDialog(final Collection<File> files) {
+ super(myEntriesChooser, true);
+ setTitle("Split " + StringUtil.capitalize(getElementTypeName()));
+
+ myNameField = new JTextField();
+ myChooser = new ElementsChooser<File>(true) {
+ protected String getItemText(@NotNull final File value) {
+ return getElementText(value);
+ }
+ };
+ for (final File file : files) {
+ myChooser.addElement(file, false, new ElementsChooser.ElementProperties() {
+ public Icon getIcon() {
+ return getElementIcon(file);
+ }
+ public Color getColor() {
+ return null;
+ }
+ });
+ }
+ myChooser.selectElements(ContainerUtil.createMaybeSingletonList(ContainerUtil.getFirstItem(files)));
+ myChooser.addElementsMarkListener(new ElementsChooser.ElementsMarkListener<File>() {
+ @Override
+ public void elementMarkChanged(File element, boolean isMarked) {
+ updateOkButton();
+ }
+ });
+ myNameField.getDocument().addDocumentListener(new DocumentAdapter() {
+ @Override
+ protected void textChanged(DocumentEvent e) {
+ updateOkButton();
+ }
+ });
+
+ init();
+ updateOkButton();
+ }
+
+ private void updateOkButton() {
+ setOKActionEnabled(!getName().isEmpty() && !getChosenFiles().isEmpty());
+ }
+
+ protected void doOKAction() {
+ final String name = getName();
+ if (isNameAlreadyUsed(name)) {
+ Messages.showErrorDialog(getNameAlreadyUsedMessage(name), "");
+ return;
+ }
+ super.doOKAction();
+ }
+
+ @Nullable
+ protected JComponent createCenterPanel() {
+ FormBuilder builder = FormBuilder.createFormBuilder().setVertical(true);
+ builder.addLabeledComponent("&Name:", myNameField);
+ builder.addLabeledComponent(getSplitDialogChooseFilesPrompt(), myChooser);
+ myChooser.setPreferredSize(new Dimension(450, 300));
+ return builder.getPanel();
+ }
+
+ public JComponent getPreferredFocusedComponent() {
+ return myNameField;
+ }
+
+ public String getName() {
+ return myNameField.getText().trim();
+ }
+
+ public Collection<File> getChosenFiles() {
+ return myChooser.getMarkedElements();
+ }
+ }
+
+ private class EntryProperties implements ElementsChooser.ElementProperties {
+ private final T myEntry;
+
+ public EntryProperties(final T entry) {
+ myEntry = entry;
+ }
+
+ public Icon getIcon() {
+ return getElementIcon(myEntry);
+ }
+
+ public Color getColor() {
+ return null;
+ }
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/ide/util/importProject/RootDetectionProcessor.java b/java/idea-ui/src/com/intellij/ide/util/importProject/RootDetectionProcessor.java
new file mode 100644
index 0000000..62f0f4a
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/ide/util/importProject/RootDetectionProcessor.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright 2000-2011 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.ide.util.importProject;
+
+import com.intellij.ide.util.projectWizard.importSources.DetectedProjectRoot;
+import com.intellij.ide.util.projectWizard.importSources.ProjectStructureDetector;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.fileTypes.FileTypeManager;
+import com.intellij.openapi.progress.ProgressIndicator;
+import com.intellij.openapi.progress.ProgressManager;
+import com.intellij.openapi.util.Pair;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.util.ArrayUtil;
+import com.intellij.util.SmartList;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.*;
+
+/**
+ * @author nik
+ */
+public class RootDetectionProcessor {
+ private static final Logger LOG = Logger.getInstance("#com.intellij.ide.util.importProject.RootDetectionProcessor");
+ private final File myBaseDir;
+ private final ProjectStructureDetector[] myDetectors;
+ private final List<DetectedProjectRoot>[] myDetectedRoots;
+ private final FileTypeManager myTypeManager;
+ private final ProgressIndicator myProgressIndicator;
+
+ public RootDetectionProcessor(File baseDir, final ProjectStructureDetector[] detectors) {
+ myBaseDir = getCanonicalDir(baseDir);
+ myDetectors = detectors;
+ //noinspection unchecked
+ myDetectedRoots = new List[myDetectors.length];
+ myTypeManager = FileTypeManager.getInstance();
+ myProgressIndicator = ProgressManager.getInstance().getProgressIndicator();
+ }
+
+ private static File getCanonicalDir(File baseDir) {
+ try {
+ return new File(FileUtil.resolveShortWindowsName(baseDir.getAbsolutePath()));
+ }
+ catch (IOException e) {
+ LOG.info(e);
+ return baseDir;
+ }
+ }
+
+
+ public Map<ProjectStructureDetector, List<DetectedProjectRoot>> findRoots() {
+ if (!myBaseDir.isDirectory()) {
+ return Collections.emptyMap();
+ }
+
+ BitSet enabledDetectors = new BitSet(myDetectors.length);
+ enabledDetectors.set(0, myDetectors.length);
+ for (int i = 0; i < myDetectors.length; i++) {
+ myDetectedRoots[i] = new ArrayList<DetectedProjectRoot>();
+ }
+ processRecursively(myBaseDir, enabledDetectors);
+
+ final Map<ProjectStructureDetector, List<DetectedProjectRoot>> result = new LinkedHashMap<ProjectStructureDetector, List<DetectedProjectRoot>>();
+ for (int i = 0; i < myDetectors.length; i++) {
+ if (!myDetectedRoots[i].isEmpty()) {
+ result.put(myDetectors[i], myDetectedRoots[i]);
+ }
+ }
+ return result;
+ }
+
+ private List<Pair<File, Integer>> processRecursively(File dir, BitSet enabledDetectors) {
+ List<Pair<File, Integer>> parentsToSkip = new SmartList<Pair<File, Integer>>();
+
+ if (myTypeManager.isFileIgnored(dir.getName())) {
+ return parentsToSkip;
+ }
+ if (myProgressIndicator != null) {
+ if (myProgressIndicator.isCanceled()) {
+ return parentsToSkip;
+ }
+ myProgressIndicator.setText2(dir.getPath());
+ }
+
+ File[] children = dir.listFiles();
+
+ if (children == null) {
+ children = ArrayUtil.EMPTY_FILE_ARRAY;
+ }
+
+ BitSet enabledForChildren = enabledDetectors;
+ for (int i = 0, detectorsLength = myDetectors.length; i < detectorsLength; i++) {
+ if (!enabledDetectors.get(i)) continue;
+
+ final ProjectStructureDetector.DirectoryProcessingResult result = myDetectors[i].detectRoots(dir, children, myBaseDir, myDetectedRoots[i]);
+
+ if (!result.isProcessChildren()) {
+ if (enabledForChildren == enabledDetectors) {
+ enabledForChildren = new BitSet();
+ enabledForChildren.or(enabledDetectors);
+ }
+ enabledForChildren.set(i, false);
+ }
+
+ final File parentToSkip = result.getParentToSkip();
+ if (parentToSkip != null && !parentToSkip.equals(dir)) {
+ parentsToSkip.add(Pair.create(parentToSkip, i));
+ }
+ }
+
+ if (!enabledForChildren.isEmpty()) {
+ for (File child : children) {
+ if (child.isDirectory()) {
+ final List<Pair<File, Integer>> toSkip = processRecursively(child, enabledForChildren);
+ if (!toSkip.isEmpty()) {
+ if (enabledForChildren == enabledDetectors) {
+ enabledForChildren = new BitSet();
+ enabledForChildren.or(enabledDetectors);
+ }
+ for (Pair<File, Integer> pair : toSkip) {
+ enabledForChildren.set(pair.getSecond(), false);
+ if (!pair.getFirst().equals(dir)) {
+ parentsToSkip.add(pair);
+ }
+ }
+ if (enabledForChildren.isEmpty()) {
+ break;
+ }
+ }
+ }
+ }
+ }
+ return parentsToSkip;
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/ide/util/importProject/RootsDetectionStep.java b/java/idea-ui/src/com/intellij/ide/util/importProject/RootsDetectionStep.java
new file mode 100644
index 0000000..93d7c09
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/ide/util/importProject/RootsDetectionStep.java
@@ -0,0 +1,263 @@
+/*
+ * Copyright 2000-2011 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.ide.util.importProject;
+
+import com.intellij.ide.IdeBundle;
+import com.intellij.ide.util.newProjectWizard.*;
+import com.intellij.ide.util.projectWizard.AbstractStepWithProgress;
+import com.intellij.ide.util.projectWizard.WizardContext;
+import com.intellij.ide.util.projectWizard.importSources.DetectedProjectRoot;
+import com.intellij.ide.util.projectWizard.importSources.ProjectStructureDetector;
+import com.intellij.ide.util.projectWizard.importSources.impl.ProjectFromSourcesBuilderImpl;
+import com.intellij.openapi.application.ApplicationNamesInfo;
+import com.intellij.openapi.progress.ProgressIndicator;
+import com.intellij.openapi.progress.ProgressManager;
+import com.intellij.openapi.ui.MultiLineLabelUI;
+import com.intellij.openapi.ui.ex.MultiLineLabel;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.ui.IdeBorderFactory;
+import com.intellij.util.containers.MultiMap;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import java.awt.*;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.io.File;
+import java.util.*;
+import java.util.List;
+
+/**
+ * @author nik
+ */
+public class RootsDetectionStep extends AbstractStepWithProgress<List<DetectedRootData>> {
+ private static final String ROOTS_FOUND_CARD = "roots_found";
+ private static final String ROOTS_NOT_FOUND_CARD = "roots_not_found";
+ private final ProjectFromSourcesBuilderImpl myBuilder;
+ private final WizardContext myContext;
+ private final StepSequence mySequence;
+ private final Icon myIcon;
+ private final String myHelpId;
+ private DetectedRootsChooser myDetectedRootsChooser;
+ private String myCurrentBaseProjectPath = null;
+ private JPanel myResultPanel;
+
+ public RootsDetectionStep(ProjectFromSourcesBuilderImpl builder,
+ WizardContext context,
+ StepSequence sequence,
+ Icon icon,
+ @NonNls String helpId) {
+ super(IdeBundle.message("prompt.stop.searching.for.sources", ApplicationNamesInfo.getInstance().getProductName()));
+ myBuilder = builder;
+ myContext = context;
+ mySequence = sequence;
+ myIcon = icon;
+ myHelpId = helpId;
+ }
+
+ protected JComponent createResultsPanel() {
+ final JPanel panel = new JPanel(new GridBagLayout());
+ myDetectedRootsChooser = new DetectedRootsChooser();
+ myDetectedRootsChooser.addSelectionListener(new DetectedRootsChooser.RootSelectionListener() {
+ @Override
+ public void selectionChanged() {
+ updateSelectedTypes();
+ }
+ });
+ final String text = IdeBundle.message("label.project.roots.have.been.found");
+ final JLabel label = new JLabel(text);
+ label.setUI(new MultiLineLabelUI());
+ panel.add(label, new GridBagConstraints(0, GridBagConstraints.RELATIVE, 2, 1, 1.0, 0.0, GridBagConstraints.NORTHWEST,
+ GridBagConstraints.HORIZONTAL, new Insets(8, 10, 0, 10), 0, 0));
+ panel.add(myDetectedRootsChooser.getComponent(),
+ new GridBagConstraints(0, GridBagConstraints.RELATIVE, 2, 1, 1.0, 1.0, GridBagConstraints.NORTHWEST, GridBagConstraints.BOTH,
+ new Insets(8, 10, 8, 10), 0, 0));
+
+ final JButton markAllButton = new JButton(IdeBundle.message("button.mark.all"));
+ panel.add(markAllButton,
+ new GridBagConstraints(0, GridBagConstraints.RELATIVE, 1, 1, 0.0, 0.0, GridBagConstraints.NORTHWEST, GridBagConstraints.NONE,
+ new Insets(0, 10, 8, 2), 0, 0));
+
+ final JButton unmarkAllButton = new JButton(IdeBundle.message("button.unmark.all"));
+ panel.add(unmarkAllButton,
+ new GridBagConstraints(1, GridBagConstraints.RELATIVE, 1, 1, 1.0, 0.0, GridBagConstraints.NORTHWEST, GridBagConstraints.NONE,
+ new Insets(0, 0, 8, 10), 0, 0));
+
+ markAllButton.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ myDetectedRootsChooser.setAllElementsMarked(true);
+ }
+ });
+ unmarkAllButton.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ myDetectedRootsChooser.setAllElementsMarked(false);
+ }
+ });
+
+ myResultPanel = new JPanel(new CardLayout());
+ myResultPanel.add(ROOTS_FOUND_CARD, panel);
+ JPanel notFoundPanel = new JPanel(new BorderLayout());
+ notFoundPanel.setBorder(IdeBorderFactory.createEmptyBorder(5));
+ notFoundPanel.add(BorderLayout.NORTH, new MultiLineLabel(IdeBundle.message("label.project.roots.not.found")));
+ myResultPanel.add(ROOTS_NOT_FOUND_CARD, notFoundPanel);
+ return myResultPanel;
+ }
+
+ public JComponent getPreferredFocusedComponent() {
+ return myDetectedRootsChooser.getComponent();
+ }
+
+ public void updateDataModel() {
+ MultiMap<ProjectStructureDetector, DetectedProjectRoot> roots = new MultiMap<ProjectStructureDetector, DetectedProjectRoot>();
+ final List<DetectedRootData> selectedElements = myDetectedRootsChooser.getMarkedElements();
+ for (final DetectedRootData rootData : selectedElements) {
+ for (ProjectStructureDetector detector : rootData.getSelectedDetectors()) {
+ roots.putValue(detector, rootData.getSelectedRoot());
+ }
+ }
+ myBuilder.setProjectRoots(roots);
+ for (ProjectStructureDetector detector : roots.keySet()) {
+ detector.setupProjectStructure(roots.get(detector), myBuilder.getProjectDescriptor(detector), myBuilder);
+ }
+ updateSelectedTypes();
+ }
+
+ private void updateSelectedTypes() {
+ Set<String> selectedTypes = new LinkedHashSet<String>();
+
+ selectedTypes.add("Existing Sources");
+
+ for (DetectedRootData rootData : myDetectedRootsChooser.getMarkedElements()) {
+ for (ProjectStructureDetector detector : rootData.getSelectedDetectors()) {
+ selectedTypes.add(detector.getDetectorId());
+ }
+ }
+
+ mySequence.setTypes(selectedTypes);
+ myContext.requestWizardButtonsUpdate();
+ }
+
+ protected boolean shouldRunProgress() {
+ final String baseProjectPath = getBaseProjectPath();
+ return myCurrentBaseProjectPath == null ? baseProjectPath != null : !myCurrentBaseProjectPath.equals(baseProjectPath);
+ }
+
+ protected void onFinished(final List<DetectedRootData> foundRoots, final boolean canceled) {
+ final CardLayout layout = (CardLayout)myResultPanel.getLayout();
+ if (foundRoots.size() > 0 && !canceled) {
+ myCurrentBaseProjectPath = getBaseProjectPath();
+ myDetectedRootsChooser.setElements(foundRoots);
+ updateSelectedTypes();
+ layout.show(myResultPanel, ROOTS_FOUND_CARD);
+ }
+ else {
+ myCurrentBaseProjectPath = null;
+ layout.show(myResultPanel, ROOTS_NOT_FOUND_CARD);
+ }
+ myResultPanel.revalidate();
+ }
+
+ protected List<DetectedRootData> calculate() {
+ final String baseProjectPath = getBaseProjectPath();
+ if (baseProjectPath == null) {
+ return Collections.emptyList();
+ }
+
+ final File baseProjectFile = new File(baseProjectPath);
+ Map<ProjectStructureDetector, List<DetectedProjectRoot>> roots = new RootDetectionProcessor(baseProjectFile,
+ ProjectStructureDetector.EP_NAME.getExtensions()).findRoots();
+ final ProgressIndicator progressIndicator = ProgressManager.getInstance().getProgressIndicator();
+ if (progressIndicator != null) {
+ progressIndicator.setText2("Processing " + roots.values().size() + " project roots...");
+ }
+
+ Map<File, DetectedRootData> rootData = new LinkedHashMap<File, DetectedRootData>();
+ for (ProjectStructureDetector detector : roots.keySet()) {
+ for (DetectedProjectRoot detectedRoot : roots.get(detector)) {
+ if (isUnderIncompatibleRoot(detectedRoot, rootData)) {
+ continue;
+ }
+
+ final DetectedRootData data = rootData.get(detectedRoot.getDirectory());
+ if (data == null) {
+ rootData.put(detectedRoot.getDirectory(), new DetectedRootData(detector, detectedRoot));
+ }
+ else {
+ detectedRoot = data.addRoot(detector, detectedRoot);
+ }
+ removeIncompatibleRoots(detectedRoot, rootData);
+ }
+ }
+
+ if (progressIndicator != null) {
+ progressIndicator.setText2("");
+ }
+ return new ArrayList<DetectedRootData>(rootData.values());
+ }
+
+ private static void removeIncompatibleRoots(DetectedProjectRoot root, Map<File, DetectedRootData> rootData) {
+ DetectedRootData[] allRoots = rootData.values().toArray(new DetectedRootData[rootData.values().size()]);
+ for (DetectedRootData child : allRoots) {
+ final File childDirectory = child.getDirectory();
+ if (FileUtil.isAncestor(root.getDirectory(), childDirectory, true)) {
+ for (DetectedProjectRoot projectRoot : child.getAllRoots()) {
+ if (!root.canContainRoot(projectRoot)) {
+ child.removeRoot(projectRoot);
+ }
+ }
+ if (child.getAllRoots().length == 0) {
+ rootData.remove(childDirectory);
+ }
+ }
+ }
+ }
+
+
+ private static boolean isUnderIncompatibleRoot(DetectedProjectRoot root, Map<File, DetectedRootData> rootData) {
+ File directory = root.getDirectory().getParentFile();
+ while (directory != null) {
+ final DetectedRootData data = rootData.get(directory);
+ if (data != null) {
+ for (DetectedProjectRoot parentRoot : data.getAllRoots()) {
+ if (!parentRoot.canContainRoot(root)) {
+ return true;
+ }
+ }
+ }
+ directory = directory.getParentFile();
+ }
+ return false;
+ }
+
+ @Nullable
+ private String getBaseProjectPath() {
+ return myBuilder.getBaseProjectPath();
+ }
+
+ protected String getProgressText() {
+ final String root = getBaseProjectPath();
+ return IdeBundle.message("progress.searching.for.sources", root != null ? root.replace('/', File.separatorChar) : "");
+ }
+
+ public Icon getIcon() {
+ return myIcon;
+ }
+
+ public String getHelpId() {
+ return myHelpId;
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/ide/util/newProjectWizard/AddModuleWizard.java b/java/idea-ui/src/com/intellij/ide/util/newProjectWizard/AddModuleWizard.java
new file mode 100644
index 0000000..167b861
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/ide/util/newProjectWizard/AddModuleWizard.java
@@ -0,0 +1,425 @@
+/*
+ * Copyright 2000-2009 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.
+ */
+
+/*
+ * User: anna
+ * Date: 09-Jul-2007
+ */
+package com.intellij.ide.util.newProjectWizard;
+
+import com.intellij.ide.IdeBundle;
+import com.intellij.ide.highlighter.ModuleFileType;
+import com.intellij.ide.highlighter.ProjectFileType;
+import com.intellij.ide.util.newProjectWizard.modes.CreateFromTemplateMode;
+import com.intellij.ide.util.newProjectWizard.modes.ImportMode;
+import com.intellij.ide.util.newProjectWizard.modes.WizardMode;
+import com.intellij.ide.util.projectWizard.ModuleWizardStep;
+import com.intellij.ide.util.projectWizard.ProjectBuilder;
+import com.intellij.ide.util.projectWizard.WizardContext;
+import com.intellij.ide.wizard.AbstractWizard;
+import com.intellij.ide.wizard.CommitStepException;
+import com.intellij.ide.wizard.Step;
+import com.intellij.openapi.components.StorageScheme;
+import com.intellij.openapi.options.ConfigurationException;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.project.ProjectManager;
+import com.intellij.openapi.projectRoots.ProjectJdkTable;
+import com.intellij.openapi.projectRoots.Sdk;
+import com.intellij.openapi.roots.ProjectRootManager;
+import com.intellij.openapi.roots.ui.configuration.DefaultModulesProvider;
+import com.intellij.openapi.roots.ui.configuration.ModulesProvider;
+import com.intellij.openapi.ui.Messages;
+import com.intellij.openapi.util.Condition;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.projectImport.ProjectImportBuilder;
+import com.intellij.projectImport.ProjectImportProvider;
+import com.intellij.ui.IdeBorderFactory;
+import com.intellij.util.Function;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.jetbrains.annotations.TestOnly;
+
+import javax.swing.*;
+import java.awt.*;
+import java.io.File;
+
+public class AddModuleWizard extends AbstractWizard<ModuleWizardStep>
+{
+ private static final String ADD_MODULE_TITLE = IdeBundle.message("title.add.module");
+ private static final String NEW_PROJECT_TITLE = IdeBundle.message("title.new.project");
+ private final Project myCurrentProject;
+ private ProjectImportProvider[] myImportProviders;
+ private final ModulesProvider myModulesProvider;
+ private WizardContext myWizardContext;
+ private WizardMode myWizardMode;
+
+ /**
+ * @param project if null, the wizard will start creating new project, otherwise will add a new module to the existing project.
+ */
+ public AddModuleWizard(@Nullable final Project project, final @NotNull ModulesProvider modulesProvider, @Nullable String defaultPath) {
+ super(project == null ? NEW_PROJECT_TITLE : ADD_MODULE_TITLE, project);
+ myCurrentProject = project;
+ myModulesProvider = modulesProvider;
+ initModuleWizard(project, defaultPath);
+ }
+
+ /**
+ * @param project if null, the wizard will start creating new project, otherwise will add a new module to the existing proj.
+ */
+ public AddModuleWizard(Component parent, final Project project, @NotNull ModulesProvider modulesProvider) {
+ super(project == null ? NEW_PROJECT_TITLE : ADD_MODULE_TITLE, parent);
+ myCurrentProject = project;
+ myModulesProvider = modulesProvider;
+ initModuleWizard(project, null);
+ }
+
+ /** Import mode */
+ public AddModuleWizard(Project project, String filePath, ProjectImportProvider... importProviders) {
+ super(getImportWizardTitle(project, importProviders), project);
+ myCurrentProject = project;
+ myImportProviders = importProviders;
+ myModulesProvider = DefaultModulesProvider.createForProject(project);
+ initModuleWizard(project, filePath);
+ }
+
+ /** Import mode */
+ public AddModuleWizard(Project project, Component dialogParent, String filePath, ProjectImportProvider... importProviders) {
+ super(getImportWizardTitle(project, importProviders), dialogParent);
+ myCurrentProject = project;
+ myImportProviders = importProviders;
+ myModulesProvider = DefaultModulesProvider.createForProject(project);
+ initModuleWizard(project, filePath);
+ }
+
+ private static String getImportWizardTitle(Project project, ProjectImportProvider... providers) {
+ StringBuilder builder = new StringBuilder("Import ");
+ builder.append(project == null ? "Project" : "Module");
+ if (providers.length == 1) {
+ builder.append(" from ").append(providers[0].getName());
+ }
+ return builder.toString();
+ }
+
+ private void initModuleWizard(@Nullable final Project project, @Nullable final String defaultPath) {
+ myWizardContext = new WizardContext(project);
+ if (defaultPath != null) {
+ myWizardContext.setProjectFileDirectory(defaultPath);
+ myWizardContext.setProjectName(defaultPath.substring(FileUtil.toSystemIndependentName(defaultPath).lastIndexOf("/") + 1));
+ }
+ myWizardContext.addContextListener(new WizardContext.Listener() {
+ public void buttonsUpdateRequested() {
+ updateButtons();
+ }
+
+ public void nextStepRequested() {
+ doNextAction();
+ }
+ });
+
+ if (myImportProviders == null) {
+ myWizardMode = new CreateFromTemplateMode();
+ appendSteps(myWizardMode.getSteps(myWizardContext, myModulesProvider));
+ }
+ else {
+ myWizardMode = new ImportMode(myImportProviders);
+ StepSequence sequence = myWizardMode.getSteps(myWizardContext, DefaultModulesProvider.createForProject(project));
+ appendSteps(sequence);
+ for (ProjectImportProvider provider : myImportProviders) {
+ provider.getBuilder().setFileToImport(defaultPath);
+ }
+ if (myImportProviders.length == 1) {
+ final ProjectImportBuilder builder = myImportProviders[0].getBuilder();
+ myWizardContext.setProjectBuilder(builder);
+ builder.setUpdate(getWizardContext().getProject() != null);
+ }
+ }
+ init();
+ }
+
+ private void appendSteps(@Nullable final StepSequence sequence) {
+ if (sequence != null) {
+ for (ModuleWizardStep step : sequence.getAllSteps()) {
+ addStep(step);
+ }
+ }
+ }
+
+ @Override
+ protected String addStepComponent(Component component) {
+ if (component instanceof JComponent) {
+ ((JComponent)component).setBorder(IdeBorderFactory.createEmptyBorder(0, 0, 0, 0));
+ }
+ return super.addStepComponent(component);
+ }
+
+ protected void updateStep() {
+ if (!mySteps.isEmpty()) {
+ getCurrentStepObject().updateStep();
+ }
+ super.updateStep();
+ myIcon.setIcon(null);
+ }
+
+ protected void dispose() {
+ for (ModuleWizardStep step : mySteps) {
+ step.disposeUIResources();
+ }
+ super.dispose();
+ }
+
+ protected final void doOKAction() {
+ int idx = getCurrentStep();
+ try {
+ do {
+ final ModuleWizardStep step = mySteps.get(idx);
+ if (step != getCurrentStepObject()) {
+ step.updateStep();
+ }
+ if (!commitStepData(step)) {
+ return;
+ }
+ step.onStepLeaving();
+ try {
+ step._commit(true);
+ }
+ catch (CommitStepException e) {
+ String message = e.getMessage();
+ if (message != null) {
+ Messages.showErrorDialog(getCurrentStepComponent(), message);
+ }
+ return;
+ }
+ if (!isLastStep(idx)) {
+ idx = getNextStep(idx);
+ } else {
+ break;
+ }
+ } while (true);
+ }
+ finally {
+ myCurrentStep = idx;
+ updateStep();
+ }
+ super.doOKAction();
+ }
+
+ protected boolean commitStepData(final ModuleWizardStep step) {
+ try {
+ if (!step.validate()) {
+ return false;
+ }
+ }
+ catch (ConfigurationException e) {
+ Messages.showErrorDialog(myCurrentProject, e.getMessage(), e.getTitle());
+ return false;
+ }
+ step.updateDataModel();
+ return true;
+ }
+
+ public void doNextAction() {
+ final ModuleWizardStep step = getCurrentStepObject();
+ if (!commitStepData(step)) {
+ return;
+ }
+ step.onStepLeaving();
+ super.doNextAction();
+ }
+
+ protected void doPreviousAction() {
+ final ModuleWizardStep step = getCurrentStepObject();
+ step.onStepLeaving();
+ super.doPreviousAction();
+ }
+
+ public void doCancelAction() {
+ final ModuleWizardStep step = getCurrentStepObject();
+ step.onStepLeaving();
+ super.doCancelAction();
+ }
+
+ private boolean isLastStep(int step) {
+ return getNextStep(step) == step;
+ }
+
+
+ protected String getHelpID() {
+ ModuleWizardStep step = getCurrentStepObject();
+ if (step != null) {
+ return step.getHelpId();
+ }
+ return null;
+ }
+
+ protected final int getNextStep(final int step) {
+ ModuleWizardStep nextStep = null;
+ final StepSequence stepSequence = getSequence();
+ if (stepSequence != null) {
+ ModuleWizardStep current = mySteps.get(step);
+ nextStep = stepSequence.getNextStep(current);
+ while (nextStep != null && !nextStep.isStepVisible()) {
+ nextStep = stepSequence.getNextStep(nextStep);
+ }
+ }
+ return nextStep == null ? step : mySteps.indexOf(nextStep);
+ }
+
+ public StepSequence getSequence() {
+ return getMode().getSteps(myWizardContext, myModulesProvider);
+ }
+
+ protected final int getPreviousStep(final int step) {
+ ModuleWizardStep previousStep = null;
+ final StepSequence stepSequence = getSequence();
+ if (stepSequence != null) {
+ previousStep = stepSequence.getPreviousStep(mySteps.get(step));
+ while (previousStep != null && !previousStep.isStepVisible()) {
+ previousStep = stepSequence.getPreviousStep(previousStep);
+ }
+ }
+ return previousStep == null ? 0 : mySteps.indexOf(previousStep);
+ }
+
+ private WizardMode getMode() {
+ return myWizardMode;
+ }
+
+ @NotNull
+ public String getNewProjectFilePath() {
+ if (myWizardContext.getProjectStorageFormat() == StorageScheme.DEFAULT) {
+ return myWizardContext.getProjectFileDirectory() + File.separator + myWizardContext.getProjectName() + ProjectFileType.DOT_DEFAULT_EXTENSION;
+ }
+ else {
+ return myWizardContext.getProjectFileDirectory();
+ }
+ }
+
+ @NotNull
+ public StorageScheme getStorageScheme() {
+ return myWizardContext.getProjectStorageFormat();
+ }
+
+ @Nullable
+ public static Sdk getNewProjectJdk(WizardContext context) {
+ if (context.getProjectJdk() != null) {
+ return context.getProjectJdk();
+ }
+ return getProjectSdkByDefault(context);
+ }
+
+ public static Sdk getProjectSdkByDefault(WizardContext context) {
+ final Project project = context.getProject() == null ? ProjectManager.getInstance().getDefaultProject() : context.getProject();
+ final Sdk projectJdk = ProjectRootManager.getInstance(project).getProjectSdk();
+ if (projectJdk != null) {
+ return projectJdk;
+ }
+ return null;
+ }
+
+ @Nullable
+ public static Sdk getMostRecentSuitableSdk(final WizardContext context) {
+ if (context.getProject() == null) {
+ @Nullable final ProjectBuilder projectBuilder = context.getProjectBuilder();
+ return ProjectJdkTable.getInstance().findMostRecentSdk(new Condition<Sdk>() {
+ public boolean value(Sdk sdk) {
+ return projectBuilder == null || projectBuilder.isSuitableSdkType(sdk.getSdkType());
+ }
+ });
+ }
+ return null;
+ }
+
+ @NotNull
+ public WizardContext getWizardContext() {
+ return myWizardContext;
+ }
+
+ @Nullable
+ public Sdk getNewProjectJdk() {
+ return getNewProjectJdk(myWizardContext);
+ }
+
+ @NotNull
+ public String getNewCompileOutput() {
+ final String projectFilePath = myWizardContext.getProjectFileDirectory();
+ @NonNls String path = myWizardContext.getCompilerOutputDirectory();
+ if (path == null) {
+ path = StringUtil.endsWithChar(projectFilePath, '/') ? projectFilePath + "out" : projectFilePath + "/out";
+ }
+ return path;
+ }
+
+ @NonNls
+ public String getModuleFilePath() {
+ return myWizardContext.getProjectFileDirectory() + File.separator + myWizardContext.getProjectName() + ModuleFileType.DOT_DEFAULT_EXTENSION;
+ }
+
+ public ProjectBuilder getProjectBuilder() {
+ return myWizardContext.getProjectBuilder();
+ }
+
+ public String getProjectName() {
+ return myWizardContext.getProjectName();
+ }
+
+ @Override
+ protected String getDimensionServiceKey() {
+ return "NewModule_or_Project.wizard";
+ }
+
+ /**
+ * Allows to ask current wizard to move to the desired step.
+ *
+ * @param filter closure that allows to indicate target step - is called with each of registered steps and is expected
+ * to return <code>true</code> for the step to go to
+ * @return <code>true</code> if current wizard is navigated to the target step; <code>false</code> otherwise
+ */
+ public boolean navigateToStep(@NotNull Function<Step, Boolean> filter) {
+ for (int i = 0, myStepsSize = mySteps.size(); i < myStepsSize; i++) {
+ ModuleWizardStep step = mySteps.get(i);
+ if (filter.fun(step) != Boolean.TRUE) {
+ continue;
+ }
+
+ // Update current step.
+ myCurrentStep = i;
+ updateStep();
+ return true;
+ }
+ return false;
+ }
+
+ public ProjectImportProvider[] getImportProviders() {
+ return myImportProviders;
+ }
+
+ @TestOnly
+ public void doOk() {
+ doOKAction();
+ }
+
+ @TestOnly
+ public boolean isLast() {
+ return isLastStep();
+ }
+
+ @TestOnly
+ public void commit() {
+ commitStepData(getCurrentStepObject());
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/ide/util/newProjectWizard/AddModuleWizardPro.java b/java/idea-ui/src/com/intellij/ide/util/newProjectWizard/AddModuleWizardPro.java
new file mode 100644
index 0000000..ac954f2
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/ide/util/newProjectWizard/AddModuleWizardPro.java
@@ -0,0 +1,205 @@
+package com.intellij.ide.util.newProjectWizard;
+
+import com.intellij.ide.util.projectWizard.ModuleWizardStep;
+import com.intellij.ide.util.projectWizard.WizardContext;
+import com.intellij.ide.wizard.CommitStepException;
+import com.intellij.ide.wizard.Step;
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.actionSystem.CustomShortcutSet;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.roots.ui.configuration.ModulesProvider;
+import com.intellij.openapi.ui.Messages;
+import com.intellij.openapi.ui.ValidationInfo;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import java.awt.*;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.util.Enumeration;
+
+/**
+ * @author Konstantin Bulenkov
+ */
+public class AddModuleWizardPro extends AddModuleWizard {
+ protected JPanel myStepsPanel;
+
+ public AddModuleWizardPro(Project project, @NotNull ModulesProvider modulesProvider, @Nullable String defaultPath) {
+ super(project, modulesProvider, defaultPath);
+ getWizardContext().addContextListener(new WizardContext.Listener() {
+ @Override
+ public void buttonsUpdateRequested() {
+ updateStepsPanel();
+ }
+
+ @Override
+ public void nextStepRequested() {
+ updateStepsPanel();
+ }
+ });
+ }
+
+ @Override
+ protected void updateButtons() {
+ super.updateButtons();
+ if (isLastStep()) {
+ getNextButton().setText("Done");
+ }
+
+ getPreviousButton().setVisible(false);
+ getCancelButton().setVisible(false);
+ }
+
+ public AddModuleWizardPro(Component parent, Project project, @NotNull ModulesProvider modulesProvider) {
+ super(parent, project, modulesProvider);
+ }
+
+ @Override
+ protected void createDefaultActions() {
+ super.createDefaultActions();
+ }
+
+ @Override
+ protected JComponent createCenterPanel() {
+ myStepsPanel = new JPanel() {
+ @Override
+ public void addNotify() {
+ super.addNotify();
+ getNextStepObject().updateDataModel();
+ updateStepsPanel();
+ new AnAction(){
+ @Override
+ public void actionPerformed(AnActionEvent e) {
+ if (!isLastStep()) doNextAction();
+ }
+ }.registerCustomShortcutSet(CustomShortcutSet.fromString("control TAB"), getContentPanel(), getDisposable());
+ new AnAction(){
+ @Override
+ public void actionPerformed(AnActionEvent e) {
+ doPreviousAction();
+ }
+ }.registerCustomShortcutSet(CustomShortcutSet.fromString("control shift TAB"), getContentPanel(), getDisposable());
+ new AnAction(){
+ @Override
+ public void actionPerformed(AnActionEvent e) {
+ myCurrentStep = mySteps.size() - 1;
+ doOKAction();
+ }
+ }.registerCustomShortcutSet(CustomShortcutSet.fromString("control shift ENTER"), getContentPanel(), getDisposable());
+ }
+
+ @Override
+ protected void paintComponent(Graphics g) {
+ super.paintComponent(g);
+ final Component c = getComponent(getComponentCount() - 1);
+ int y = c.getBounds().y + c.getBounds().height + 4;
+ g.translate(0, y);
+ myIcon.paintIcon(g);
+ g.translate(0, -y);
+ }
+ };
+ myStepsPanel.setLayout(new BoxLayout(myStepsPanel, BoxLayout.Y_AXIS) {
+ @Override
+ public void layoutContainer(Container target) {
+ super.layoutContainer(target);
+ int maxWidth = -1;
+ for (Component c : target.getComponents()) {
+ maxWidth = Math.max(maxWidth, c.getWidth());
+ }
+ for (Component c : target.getComponents()) {
+ final Rectangle b = c.getBounds();
+ c.setBounds(b.x, b.y, maxWidth, b.height);
+ }
+ }
+ });
+ myStepsPanel.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 4));
+ updateStepsPanel();
+
+ final JPanel panel = new JPanel(new BorderLayout());
+ panel.add(myStepsPanel, BorderLayout.WEST);
+ panel.add(myContentPanel, BorderLayout.CENTER);
+ return panel;
+
+ }
+
+ @Override
+ public void doNextAction() {
+ super.doNextAction();
+ updateStepsPanel();
+ }
+
+ @Override
+ protected void doPreviousAction() {
+ super.doPreviousAction();
+ updateStepsPanel();
+ }
+
+ @Nullable
+ @Override
+ protected ValidationInfo doValidate() {
+ final ValidationInfo result = super.doValidate();
+ updateStepsPanel();
+ return result;
+ }
+
+ private void updateStepsPanel() {
+ myStepsPanel.removeAll();
+ int index = 0;
+ final ButtonGroup group = new ButtonGroup();
+ while (index != -1) {
+ final int ind = index;
+ final ModuleWizardStep step = mySteps.get(index);
+ final JRadioButton rb = new JRadioButton(step.getName(), index == myCurrentStep);
+ rb.setFocusable(false);
+ rb.setUI(new WizardArrowUI(rb, index < myCurrentStep));
+ myStepsPanel.add(rb);
+ group.add(rb);
+ rb.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ final ModuleWizardStep step = getCurrentStepObject();
+ if (ind > getCurrentStep() && !commitStepData(step)) {
+ updateStepsPanel();
+ return;
+ }
+ step.onStepLeaving();
+
+ // Commit data of current step
+ final Step currentStep = mySteps.get(myCurrentStep);
+ try {
+ currentStep._commit(false);
+ }
+ catch (final CommitStepException exc) {
+ Messages.showErrorDialog(
+ myContentPanel,
+ exc.getMessage()
+ );
+ return;
+ }
+
+ myCurrentStep = ind;
+ updateStep();
+
+ }
+ });
+
+ final int next = getNextStep(index);
+ index = index == next ? -1 : next;
+ }
+
+ final Enumeration<AbstractButton> buttons = group.getElements();
+ while (buttons.hasMoreElements()) {
+ final JRadioButton b = (JRadioButton)buttons.nextElement();
+ b.setUI(new WizardArrowUI(b, index < myCurrentStep));
+ }
+ SwingUtilities.invokeLater(new Runnable() {
+ @Override
+ public void run() {
+ myStepsPanel.revalidate();
+ myStepsPanel.repaint();
+ }
+ });
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/ide/util/newProjectWizard/AddSupportForFrameworksPanel.form b/java/idea-ui/src/com/intellij/ide/util/newProjectWizard/AddSupportForFrameworksPanel.form
new file mode 100644
index 0000000..6a06b11
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/ide/util/newProjectWizard/AddSupportForFrameworksPanel.form
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.intellij.ide.util.newProjectWizard.AddSupportForFrameworksPanel">
+ <grid id="27dc6" binding="myMainPanel" layout-manager="GridLayoutManager" row-count="3" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+ <margin top="0" left="0" bottom="0" right="0"/>
+ <constraints>
+ <xy x="20" y="20" width="487" height="400"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <component id="8fe34" class="javax.swing.JLabel">
+ <constraints>
+ <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties>
+ <enabled value="true"/>
+ <font/>
+ <text resource-bundle="messages/ProjectBundle" key="label.text.please.select.desired.technologies"/>
+ </properties>
+ </component>
+ <component id="ef20d" class="javax.swing.JLabel">
+ <constraints>
+ <grid row="1" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties>
+ <text resource-bundle="messages/ProjectBundle" key="label.text.framework.support.description"/>
+ </properties>
+ </component>
+ <grid id="4fc0e" binding="myFrameworksPanel" layout-manager="BorderLayout" hgap="0" vgap="0">
+ <constraints>
+ <grid row="2" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties/>
+ <border type="none">
+ <size top="3" left="0" bottom="0" right="0"/>
+ </border>
+ <children/>
+ </grid>
+ </children>
+ </grid>
+</form>
diff --git a/java/idea-ui/src/com/intellij/ide/util/newProjectWizard/AddSupportForFrameworksPanel.java b/java/idea-ui/src/com/intellij/ide/util/newProjectWizard/AddSupportForFrameworksPanel.java
new file mode 100644
index 0000000..f50c98a
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/ide/util/newProjectWizard/AddSupportForFrameworksPanel.java
@@ -0,0 +1,325 @@
+/*
+ * Copyright 2000-2009 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.ide.util.newProjectWizard;
+
+import com.intellij.facet.impl.ui.libraries.LibraryCompositionSettings;
+import com.intellij.facet.impl.ui.libraries.LibraryOptionsPanel;
+import com.intellij.facet.ui.FacetBasedFrameworkSupportProvider;
+import com.intellij.framework.addSupport.FrameworkSupportInModuleConfigurable;
+import com.intellij.framework.addSupport.FrameworkSupportInModuleProvider;
+import com.intellij.ide.util.frameworkSupport.FrameworkSupportConfigurable;
+import com.intellij.ide.util.frameworkSupport.FrameworkSupportProvider;
+import com.intellij.ide.util.frameworkSupport.FrameworkSupportUtil;
+import com.intellij.ide.util.newProjectWizard.impl.FrameworkSupportCommunicator;
+import com.intellij.ide.util.newProjectWizard.impl.FrameworkSupportModelBase;
+import com.intellij.openapi.Disposable;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.roots.IdeaModifiableModelsProvider;
+import com.intellij.openapi.roots.ModifiableRootModel;
+import com.intellij.openapi.roots.libraries.Library;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.LibrariesContainer;
+import com.intellij.openapi.ui.Splitter;
+import com.intellij.openapi.util.Comparing;
+import com.intellij.openapi.util.MultiValuesMap;
+import com.intellij.ui.CheckedTreeNode;
+import com.intellij.ui.ScrollPaneFactory;
+import com.intellij.util.containers.ContainerUtil;
+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 javax.swing.event.TreeSelectionEvent;
+import javax.swing.event.TreeSelectionListener;
+import java.awt.*;
+import java.util.*;
+import java.util.List;
+
+/**
+ * @author nik
+ */
+public class AddSupportForFrameworksPanel implements Disposable {
+ private static final Logger LOG = Logger.getInstance("#com.intellij.ide.util.newProjectWizard.AddSupportForFrameworksStep");
+ @NonNls private static final String EMPTY_CARD = "empty";
+ private JPanel myMainPanel;
+ private JPanel myFrameworksPanel;
+ private List<List<FrameworkSupportNode>> myGroups;
+ private final LibrariesContainer myLibrariesContainer;
+ private final List<FrameworkSupportInModuleProvider> myProviders;
+ private final FrameworkSupportModelBase myModel;
+ private final JPanel myOptionsPanel;
+ private final FrameworksTree myFrameworksTree;
+ private final Map<FrameworkSupportNode, FrameworkSupportOptionsComponent> myInitializedOptionsComponents = new HashMap<FrameworkSupportNode, FrameworkSupportOptionsComponent>();
+ private FrameworkSupportNode myLastSelectedNode;
+
+ public AddSupportForFrameworksPanel(final List<FrameworkSupportInModuleProvider> providers, final FrameworkSupportModelBase model) {
+ myModel = model;
+ myLibrariesContainer = model.getLibrariesContainer();
+ myProviders = providers;
+ createNodes();
+
+ final Splitter splitter = new Splitter(false, 0.30f, 0.1f, 0.7f);
+ myFrameworksTree = new FrameworksTree(myGroups) {
+ @Override
+ protected void onNodeStateChanged(CheckedTreeNode node) {
+ if (!(node instanceof FrameworkSupportNode)) return;
+
+ final FrameworkSupportNode frameworkSupportNode = (FrameworkSupportNode)node;
+ if (frameworkSupportNode == getSelectedNode()) {
+ updateOptionsPanel();
+ }
+ final FrameworkSupportInModuleConfigurable configurable = frameworkSupportNode.getConfigurable();
+ configurable.onFrameworkSelectionChanged(node.isChecked());
+ myModel.onFrameworkSelectionChanged(frameworkSupportNode);
+ onFrameworkStateChanged();
+ }
+ };
+ myFrameworksTree.addTreeSelectionListener(new TreeSelectionListener() {
+ public void valueChanged(TreeSelectionEvent e) {
+ onSelectionChanged();
+ }
+ });
+
+ splitter.setFirstComponent(ScrollPaneFactory.createScrollPane(myFrameworksTree));
+ myOptionsPanel = new JPanel(new CardLayout());
+ myOptionsPanel.add(EMPTY_CARD, new JPanel());
+
+ splitter.setSecondComponent(myOptionsPanel);
+ myFrameworksPanel.add(splitter, BorderLayout.CENTER);
+
+ myFrameworksTree.setSelectionRow(0);
+ }
+
+ protected void onFrameworkStateChanged() {}
+
+ private void onSelectionChanged() {
+ if (!myFrameworksTree.isProcessingMouseEventOnCheckbox()) {
+ updateOptionsPanel();
+ }
+
+ final FrameworkSupportNode selectedNode = getSelectedNode();
+ if (!Comparing.equal(selectedNode, myLastSelectedNode)) {
+ applyLibraryOptionsForSelected();
+
+ myLastSelectedNode = selectedNode;
+ }
+ }
+
+ @Override
+ public void dispose() {
+ }
+
+ private void applyLibraryOptionsForSelected() {
+ if (myLastSelectedNode != null) {
+ final FrameworkSupportOptionsComponent optionsComponent = myInitializedOptionsComponents.get(myLastSelectedNode);
+ if (optionsComponent != null) {
+ final LibraryOptionsPanel optionsPanel = optionsComponent.getLibraryOptionsPanel();
+ if (optionsPanel != null) {
+ optionsPanel.apply();
+ }
+ }
+ }
+ }
+
+ private void updateOptionsPanel() {
+ final FrameworkSupportNode node = getSelectedNode();
+ if (node != null) {
+ initializeOptionsPanel(node);
+ showCard(node.getProvider().getFrameworkType().getId());
+ UIUtil.setEnabled(myOptionsPanel, node.isChecked(), true);
+ node.getConfigurable().onFrameworkSelectionChanged(node.isChecked());
+ }
+ else {
+ showCard(EMPTY_CARD);
+ }
+ }
+
+ @Nullable
+ private FrameworkSupportNode getSelectedNode() {
+ final FrameworkSupportNode[] nodes = myFrameworksTree.getSelectedNodes(FrameworkSupportNode.class, null);
+ return nodes.length == 1 ? nodes[0] : null;
+ }
+
+ private void initializeOptionsPanel(final FrameworkSupportNode node) {
+ if (!myInitializedOptionsComponents.containsKey(node)) {
+ final FrameworkSupportNode parentNode = node.getParentNode();
+ if (parentNode != null) {
+ initializeOptionsPanel(parentNode);
+ }
+
+ FrameworkSupportOptionsComponent optionsComponent = new FrameworkSupportOptionsComponent(myModel, myLibrariesContainer, this,
+ node.getConfigurable(),
+ node.getTitle() + " Settings");
+ final String id = node.getProvider().getFrameworkType().getId();
+ myOptionsPanel.add(id, optionsComponent.getMainPanel());
+ myInitializedOptionsComponents.put(node, optionsComponent);
+ }
+ }
+
+ private void showCard(String cardName) {
+ ((CardLayout)myOptionsPanel.getLayout()).show(myOptionsPanel, cardName);
+ }
+
+ private List<LibraryCompositionSettings> getLibrariesCompositionSettingsList() {
+ List<LibraryCompositionSettings> list = new ArrayList<LibraryCompositionSettings>();
+ List<FrameworkSupportNode> selected = getFrameworkNodes(true);
+ for (FrameworkSupportNode node : selected) {
+ ContainerUtil.addIfNotNull(list, getLibraryCompositionSettings(node));
+ }
+ return list;
+ }
+
+ @Nullable
+ private LibraryCompositionSettings getLibraryCompositionSettings(FrameworkSupportNode node) {
+ final FrameworkSupportOptionsComponent optionsComponent = myInitializedOptionsComponents.get(node);
+ return optionsComponent != null ? optionsComponent.getLibraryCompositionSettings() : null;
+ }
+
+ public boolean downloadLibraries() {
+ applyLibraryOptionsForSelected();
+ List<LibraryCompositionSettings> list = getLibrariesCompositionSettingsList();
+ for (LibraryCompositionSettings compositionSettings : list) {
+ if (!compositionSettings.downloadFiles(myMainPanel)) return false;
+ }
+ return true;
+ }
+
+ private void createNodes() {
+ Map<String, FrameworkSupportNode> nodes = new HashMap<String, FrameworkSupportNode>();
+ MultiValuesMap<String, FrameworkSupportNode> groups = new MultiValuesMap<String, FrameworkSupportNode>(true);
+ for (FrameworkSupportInModuleProvider provider : myProviders) {
+ createNode(provider, nodes, groups);
+ }
+
+ myGroups = new ArrayList<List<FrameworkSupportNode>>();
+ for (String groupId : groups.keySet()) {
+ final Collection<FrameworkSupportNode> collection = groups.get(groupId);
+ if (collection != null) {
+ final List<FrameworkSupportNode> group = new ArrayList<FrameworkSupportNode>();
+ for (FrameworkSupportNode node : collection) {
+ if (node.getParentNode() == null) {
+ group.add(node);
+ }
+ }
+ FrameworkSupportNode.sortByName(group);
+ myGroups.add(group);
+ }
+ }
+ }
+
+ @Nullable
+ private FrameworkSupportNode createNode(final FrameworkSupportInModuleProvider provider, final Map<String, FrameworkSupportNode> nodes,
+ final MultiValuesMap<String, FrameworkSupportNode> groups) {
+ FrameworkSupportNode node = nodes.get(provider.getFrameworkType().getId());
+ if (node == null) {
+ String underlyingTypeId = provider.getFrameworkType().getUnderlyingFrameworkTypeId();
+ FrameworkSupportNode parentNode = null;
+ if (underlyingTypeId != null) {
+ FrameworkSupportInModuleProvider parentProvider = FrameworkSupportUtil.findProvider(underlyingTypeId, myProviders);
+ if (parentProvider == null) {
+ LOG.info("Cannot find id = " + underlyingTypeId);
+ return null;
+ }
+ parentNode = createNode(parentProvider, nodes, groups);
+ }
+ node = new FrameworkSupportNode(provider, parentNode, myModel, this);
+ nodes.put(provider.getFrameworkType().getId(), node);
+ final String groupId = provider instanceof OldFrameworkSupportProviderWrapper
+ ? ((OldFrameworkSupportProviderWrapper)provider).getProvider().getGroupId() : null;
+ groups.put(groupId, node);
+ }
+ return node;
+ }
+
+ public JComponent getMainPanel() {
+ return myMainPanel;
+ }
+
+ public FrameworksTree getFrameworksTree() {
+ return myFrameworksTree;
+ }
+
+ public boolean hasSelectedFrameworks() {
+ return !getFrameworkNodes(true).isEmpty();
+ }
+
+ private List<FrameworkSupportNode> getFrameworkNodes(final boolean selectedOnly) {
+ ArrayList<FrameworkSupportNode> list = new ArrayList<FrameworkSupportNode>();
+ if (myGroups != null) {
+ for (List<FrameworkSupportNode> group : myGroups) {
+ addChildFrameworks(group, list, selectedOnly);
+ }
+ }
+ return list;
+ }
+
+ private static void addChildFrameworks(final List<FrameworkSupportNode> list, final List<FrameworkSupportNode> result,
+ final boolean selectedOnly) {
+ for (FrameworkSupportNode node : list) {
+ if (!selectedOnly || node.isChecked()) {
+ result.add(node);
+ addChildFrameworks(node.getChildren(), result, selectedOnly);
+ }
+ }
+ }
+
+ public void addSupport(final @NotNull Module module, final @NotNull ModifiableRootModel rootModel) {
+ List<Library> addedLibraries = new ArrayList<Library>();
+ List<FrameworkSupportNode> selectedFrameworks = getFrameworkNodes(true);
+ sortFrameworks(selectedFrameworks);
+ List<FrameworkSupportConfigurable> selectedConfigurables = new ArrayList<FrameworkSupportConfigurable>();
+ final IdeaModifiableModelsProvider modifiableModelsProvider = new IdeaModifiableModelsProvider();
+ for (FrameworkSupportNode node : selectedFrameworks) {
+ FrameworkSupportInModuleConfigurable configurable = node.getConfigurable();
+ if (configurable instanceof OldFrameworkSupportProviderWrapper.FrameworkSupportConfigurableWrapper) {
+ selectedConfigurables.add(((OldFrameworkSupportProviderWrapper.FrameworkSupportConfigurableWrapper)configurable).getConfigurable());
+ }
+ final LibraryCompositionSettings settings = getLibraryCompositionSettings(node);
+ Library library = settings != null ? settings.addLibraries(rootModel, addedLibraries, myLibrariesContainer) : null;
+ if (configurable instanceof OldFrameworkSupportProviderWrapper.FrameworkSupportConfigurableWrapper) {
+ ((OldFrameworkSupportProviderWrapper.FrameworkSupportConfigurableWrapper)configurable).getConfigurable().addSupport(module, rootModel,
+ library);
+ }
+ else {
+ configurable.addSupport(module, rootModel, modifiableModelsProvider);
+ }
+ }
+ for (FrameworkSupportNode node : selectedFrameworks) {
+ FrameworkSupportInModuleProvider provider = node.getProvider();
+ if (provider instanceof OldFrameworkSupportProviderWrapper) {
+ final FrameworkSupportProvider oldProvider = ((OldFrameworkSupportProviderWrapper)provider).getProvider();
+ if (oldProvider instanceof FacetBasedFrameworkSupportProvider && !addedLibraries.isEmpty()) {
+ ((FacetBasedFrameworkSupportProvider)oldProvider).processAddedLibraries(module, addedLibraries);
+ }
+ }
+ }
+ for (FrameworkSupportCommunicator communicator : FrameworkSupportCommunicator.EP_NAME.getExtensions()) {
+ communicator.onFrameworkSupportAdded(module, rootModel, selectedConfigurables, myModel);
+ }
+ }
+
+ private void sortFrameworks(final List<FrameworkSupportNode> nodes) {
+ final Comparator<FrameworkSupportInModuleProvider> comparator = FrameworkSupportUtil.getFrameworkSupportProvidersComparator(myProviders);
+ Collections.sort(nodes, new Comparator<FrameworkSupportNode>() {
+ public int compare(final FrameworkSupportNode o1, final FrameworkSupportNode o2) {
+ return comparator.compare(o1.getProvider(), o2.getProvider());
+ }
+ });
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/ide/util/newProjectWizard/FrameworkSupportNode.java b/java/idea-ui/src/com/intellij/ide/util/newProjectWizard/FrameworkSupportNode.java
new file mode 100644
index 0000000..180e4e5
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/ide/util/newProjectWizard/FrameworkSupportNode.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2000-2009 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.ide.util.newProjectWizard;
+
+import com.intellij.framework.addSupport.FrameworkSupportInModuleConfigurable;
+import com.intellij.framework.addSupport.FrameworkSupportInModuleProvider;
+import com.intellij.ide.util.newProjectWizard.impl.FrameworkSupportModelBase;
+import com.intellij.openapi.Disposable;
+import com.intellij.openapi.util.Disposer;
+import com.intellij.ui.CheckedTreeNode;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+/**
+* @author nik
+*/
+public class FrameworkSupportNode extends CheckedTreeNode {
+ private final FrameworkSupportInModuleProvider myProvider;
+ private final FrameworkSupportNode myParentNode;
+ private FrameworkSupportInModuleConfigurable myConfigurable;
+ private final List<FrameworkSupportNode> myChildren = new ArrayList<FrameworkSupportNode>();
+ private final FrameworkSupportModelBase myModel;
+ private final Disposable myParentDisposable;
+
+ public FrameworkSupportNode(final FrameworkSupportInModuleProvider provider, final FrameworkSupportNode parentNode, final FrameworkSupportModelBase model,
+ Disposable parentDisposable) {
+ super(provider);
+ myParentDisposable = parentDisposable;
+ setChecked(false);
+ myProvider = provider;
+ myParentNode = parentNode;
+ model.registerComponent(provider, this);
+ myModel = model;
+ if (parentNode != null) {
+ parentNode.add(this);
+ parentNode.myChildren.add(this);
+ }
+ }
+
+ public List<FrameworkSupportNode> getChildren() {
+ return myChildren;
+ }
+
+ public FrameworkSupportInModuleProvider getProvider() {
+ return myProvider;
+ }
+
+ public FrameworkSupportNode getParentNode() {
+ return myParentNode;
+ }
+
+ public synchronized FrameworkSupportInModuleConfigurable getConfigurable() {
+ if (myConfigurable == null) {
+ myConfigurable = myProvider.createConfigurable(myModel);
+ Disposer.register(myParentDisposable, myConfigurable);
+ }
+ return myConfigurable;
+ }
+
+ public static void sortByName(List<FrameworkSupportNode> nodes) {
+ if (nodes.isEmpty()) return;
+
+ Collections.sort(nodes, new Comparator<FrameworkSupportNode>() {
+ public int compare(final FrameworkSupportNode o1, final FrameworkSupportNode o2) {
+ return o1.getTitle().compareToIgnoreCase(o2.getTitle());
+ }
+ });
+ for (FrameworkSupportNode node : nodes) {
+ node.sortChildren();
+ }
+ }
+
+ public String getTitle() {
+ return myProvider.getFrameworkType().getPresentableName();
+ }
+
+ private void sortChildren() {
+ sortByName(myChildren);
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/ide/util/newProjectWizard/FrameworkSupportOptionsComponent.java b/java/idea-ui/src/com/intellij/ide/util/newProjectWizard/FrameworkSupportOptionsComponent.java
new file mode 100644
index 0000000..452bd3b
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/ide/util/newProjectWizard/FrameworkSupportOptionsComponent.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright 2000-2011 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.ide.util.newProjectWizard;
+
+import com.intellij.facet.impl.ui.libraries.LibraryCompositionSettings;
+import com.intellij.facet.impl.ui.libraries.LibraryOptionsPanel;
+import com.intellij.framework.addSupport.FrameworkSupportInModuleConfigurable;
+import com.intellij.ide.util.frameworkSupport.FrameworkSupportConfigurableListener;
+import com.intellij.ide.util.frameworkSupport.FrameworkSupportModelAdapter;
+import com.intellij.ide.util.newProjectWizard.impl.FrameworkSupportModelBase;
+import com.intellij.openapi.Disposable;
+import com.intellij.openapi.roots.ui.configuration.libraries.CustomLibraryDescription;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.LibrariesContainer;
+import com.intellij.openapi.ui.VerticalFlowLayout;
+import com.intellij.openapi.util.Disposer;
+import com.intellij.ui.IdeBorderFactory;
+import com.intellij.ui.SeparatorFactory;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import java.awt.*;
+
+/**
+ * @author nik
+ */
+public class FrameworkSupportOptionsComponent {
+ private final JPanel myMainPanel;
+ private final FrameworkSupportModelBase myModel;
+ private LibraryCompositionSettings myLibraryCompositionSettings;
+ private LibraryOptionsPanel myLibraryOptionsPanel;
+ private FrameworkSupportInModuleConfigurable myConfigurable;
+
+ public FrameworkSupportOptionsComponent(FrameworkSupportModelBase model, LibrariesContainer container, Disposable parentDisposable,
+ final FrameworkSupportInModuleConfigurable configurable, final @Nullable String title) {
+ myModel = model;
+ myConfigurable = configurable;
+ VerticalFlowLayout layout = new VerticalFlowLayout();
+ layout.setVerticalFill(true);
+ myMainPanel = new JPanel(layout);
+
+ //if (title != null) {
+ // myMainPanel.setBorder(IdeBorderFactory.createTitledBorder(title, true));
+ //}
+
+ final JComponent component = myConfigurable.createComponent();
+ if (component != null) {
+ myMainPanel.add(component);
+ }
+
+ final boolean addSeparator = component != null;
+ final JPanel librariesOptionsPanelWrapper = new JPanel(new BorderLayout());
+ myMainPanel.add(librariesOptionsPanelWrapper);
+ if (myConfigurable instanceof OldFrameworkSupportProviderWrapper.FrameworkSupportConfigurableWrapper) {
+ ((OldFrameworkSupportProviderWrapper.FrameworkSupportConfigurableWrapper)myConfigurable).getConfigurable().addListener(
+ new FrameworkSupportConfigurableListener() {
+ public void frameworkVersionChanged() {
+ updateLibrariesPanel();
+ }
+ });
+ }
+ model.addFrameworkListener(new FrameworkSupportModelAdapter() {
+ @Override
+ public void wizardStepUpdated() {
+ updateLibrariesPanel();
+ }
+ }, parentDisposable);
+
+ final CustomLibraryDescription description = myConfigurable.createLibraryDescription();
+ if (description != null) {
+ myLibraryOptionsPanel = new LibraryOptionsPanel(description, myModel.getBaseDirectoryForLibrariesPath(), myConfigurable.getLibraryVersionFilter(),
+ container, !myConfigurable.isOnlyLibraryAdded());
+ Disposer.register(myConfigurable, myLibraryOptionsPanel);
+ if (addSeparator) {
+ JComponent separator1 = SeparatorFactory.createSeparator("Libraries", null);
+ separator1.setBorder(IdeBorderFactory.createEmptyBorder(5, 0, 5, 5));
+ librariesOptionsPanelWrapper.add(BorderLayout.NORTH, separator1);
+ }
+ librariesOptionsPanelWrapper.add(BorderLayout.CENTER, myLibraryOptionsPanel.getMainPanel());
+ }
+ }
+
+ private void updateLibrariesPanel() {
+ if (myLibraryOptionsPanel != null) {
+ myLibraryOptionsPanel.changeBaseDirectoryPath(myModel.getBaseDirectoryForLibrariesPath());
+ myLibraryOptionsPanel.setVersionFilter(myConfigurable.getLibraryVersionFilter());
+ myLibraryOptionsPanel.getMainPanel().setVisible(myConfigurable.isVisible());
+ }
+ }
+
+
+ public JPanel getMainPanel() {
+ return myMainPanel;
+ }
+
+ @Nullable
+ public LibraryCompositionSettings getLibraryCompositionSettings() {
+ if (myLibraryCompositionSettings == null && myLibraryOptionsPanel != null) {
+ myLibraryCompositionSettings = myLibraryOptionsPanel.apply();
+ }
+ return myLibraryCompositionSettings;
+ }
+
+ public LibraryOptionsPanel getLibraryOptionsPanel() {
+ return myLibraryOptionsPanel;
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/ide/util/newProjectWizard/FrameworksTree.java b/java/idea-ui/src/com/intellij/ide/util/newProjectWizard/FrameworksTree.java
new file mode 100644
index 0000000..30a188a
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/ide/util/newProjectWizard/FrameworksTree.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright 2000-2009 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.ide.util.newProjectWizard;
+
+import com.intellij.ui.CheckboxTree;
+import com.intellij.ui.CheckedTreeNode;
+import com.intellij.ui.TreeSpeedSearch;
+import com.intellij.util.containers.Convertor;
+import com.intellij.util.ui.tree.TreeUtil;
+
+import javax.swing.*;
+import javax.swing.tree.TreePath;
+import java.awt.*;
+import java.awt.event.MouseEvent;
+import java.util.List;
+
+/**
+ * @author nik
+ */
+public class FrameworksTree extends CheckboxTree {
+ private boolean myProcessingMouseEventOnCheckbox;
+
+ public FrameworksTree(List<List<FrameworkSupportNode>> groups) {
+ super(new FrameworksTreeRenderer(), new FrameworksRootNode(groups), new CheckPolicy(false, true, true, false));
+ setRootVisible(false);
+ setShowsRootHandles(false);
+ TreeUtil.expandAll(this);
+ }
+
+ @Override
+ protected void processMouseEvent(MouseEvent e) {
+ final TreePath path = getPathForLocation(e.getX(), e.getY());
+ if (path != null) {
+ final Object node = path.getLastPathComponent();
+ if (node instanceof FrameworkSupportNode) {
+ final Rectangle checkboxBounds = ((CheckboxTreeCellRendererBase)getCellRenderer()).myCheckbox.getBounds();
+ final Rectangle pathBounds = getPathBounds(path);
+ checkboxBounds.setLocation(pathBounds.getLocation());
+ if (checkboxBounds.contains(e.getPoint())) {
+ try {
+ myProcessingMouseEventOnCheckbox = true;
+ super.processMouseEvent(e);
+ }
+ finally {
+ myProcessingMouseEventOnCheckbox = false;
+ }
+ return;
+ }
+ }
+ }
+
+ super.processMouseEvent(e);
+ }
+
+ @Override
+ protected void installSpeedSearch() {
+ new TreeSpeedSearch(this, new Convertor<TreePath, String>() {
+ @Override
+ public String convert(TreePath path) {
+ final Object node = path.getLastPathComponent();
+ if (node instanceof FrameworkSupportNode) {
+ return ((FrameworkSupportNode)node).getTitle();
+ }
+ return "";
+ }
+ });
+ }
+
+ public boolean isProcessingMouseEventOnCheckbox() {
+ return myProcessingMouseEventOnCheckbox;
+ }
+
+ private static class FrameworksTreeRenderer extends CheckboxTreeCellRenderer {
+ private FrameworksTreeRenderer() {
+ super(true, false);
+ }
+
+ @Override
+ public void customizeRenderer(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) {
+ if (value instanceof FrameworkSupportNode) {
+ final FrameworkSupportNode node = (FrameworkSupportNode)value;
+ getTextRenderer().append(node.getTitle());
+ getTextRenderer().setIcon(node.getProvider().getFrameworkType().getIcon());
+ }
+ }
+ }
+
+ private static class FrameworksRootNode extends CheckedTreeNode {
+ public FrameworksRootNode(List<List<FrameworkSupportNode>> groups) {
+ super(null);
+ for (List<FrameworkSupportNode> group : groups) {
+ for (FrameworkSupportNode node : group) {
+ add(node);
+ }
+ }
+ }
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/ide/util/newProjectWizard/LoadingProjectTemplate.java b/java/idea-ui/src/com/intellij/ide/util/newProjectWizard/LoadingProjectTemplate.java
new file mode 100644
index 0000000..254ecb5
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/ide/util/newProjectWizard/LoadingProjectTemplate.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2000-2012 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.ide.util.newProjectWizard;
+
+import com.intellij.ide.util.projectWizard.ModuleBuilder;
+import com.intellij.openapi.ui.ValidationInfo;
+import com.intellij.platform.ProjectTemplate;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+* @author Dmitry Avdeev
+* Date: 11/28/12
+*/
+class LoadingProjectTemplate implements ProjectTemplate {
+ @NotNull
+ @Override
+ public String getName() {
+ return "Loading...";
+ }
+
+ @Nullable
+ @Override
+ public String getDescription() {
+ return "Loading samples from JetBrains site. Please wait.";
+ }
+
+ @NotNull
+ @Override
+ public ModuleBuilder createModuleBuilder() {
+ throw new AbstractMethodError();
+ }
+
+ @Nullable
+ @Override
+ public ValidationInfo validateSettings() {
+ return null;
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/ide/util/newProjectWizard/OldFrameworkSupportProviderWrapper.java b/java/idea-ui/src/com/intellij/ide/util/newProjectWizard/OldFrameworkSupportProviderWrapper.java
new file mode 100644
index 0000000..00950ea
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/ide/util/newProjectWizard/OldFrameworkSupportProviderWrapper.java
@@ -0,0 +1,198 @@
+/*
+ * Copyright 2000-2011 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.ide.util.newProjectWizard;
+
+import com.intellij.framework.FrameworkTypeEx;
+import com.intellij.framework.addSupport.FrameworkSupportInModuleConfigurable;
+import com.intellij.framework.addSupport.FrameworkSupportInModuleProvider;
+import com.intellij.framework.library.FrameworkLibraryVersion;
+import com.intellij.framework.library.FrameworkLibraryVersionFilter;
+import com.intellij.framework.library.FrameworkSupportWithLibrary;
+import com.intellij.ide.util.frameworkSupport.*;
+import com.intellij.ide.util.projectWizard.ModuleBuilder;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.module.ModuleType;
+import com.intellij.openapi.roots.ModifiableModelsProvider;
+import com.intellij.openapi.roots.ModifiableRootModel;
+import com.intellij.openapi.roots.ui.configuration.FacetsProvider;
+import com.intellij.openapi.roots.ui.configuration.libraries.CustomLibraryDescription;
+import com.intellij.openapi.util.Disposer;
+import com.intellij.ui.GuiUtils;
+import com.intellij.util.ui.EmptyIcon;
+import org.jetbrains.annotations.NotNull;
+
+import javax.swing.*;
+import java.util.List;
+
+/**
+ * @author nik
+ */
+public class OldFrameworkSupportProviderWrapper extends FrameworkSupportInModuleProvider {
+ private final FrameworkSupportProvider myProvider;
+ private final OldFrameworkSupportProviderWrapper.FrameworkSupportProviderBasedType myType;
+
+ public OldFrameworkSupportProviderWrapper(FrameworkSupportProvider provider) {
+ myProvider = provider;
+ myType = new FrameworkSupportProviderBasedType(myProvider, this);
+ }
+
+ public FrameworkSupportProvider getProvider() {
+ return myProvider;
+ }
+
+ @NotNull
+ @Override
+ public FrameworkTypeEx getFrameworkType() {
+ return myType;
+ }
+
+ @NotNull
+ @Override
+ public FrameworkSupportInModuleConfigurable createConfigurable(@NotNull FrameworkSupportModel model) {
+ final FrameworkSupportConfigurable configurable = myProvider.createConfigurable(model);
+ return new FrameworkSupportConfigurableWrapper(configurable);
+ }
+
+ @Override
+ public boolean isEnabledForModuleType(@NotNull ModuleType moduleType) {
+ return myProvider.isEnabledForModuleType(moduleType);
+ }
+
+ @Override
+ public boolean isEnabledForModuleBuilder(@NotNull ModuleBuilder builder) {
+ return myProvider.isEnabledForModuleBuilder(builder);
+ }
+
+ @Override
+ public boolean isSupportAlreadyAdded(@NotNull Module module, @NotNull FacetsProvider facetsProvider) {
+ return myProvider.isSupportAlreadyAdded(module, facetsProvider);
+ }
+
+ public static class FrameworkSupportProviderBasedType extends FrameworkTypeEx {
+ private final FrameworkSupportProvider myOldProvider;
+ private final OldFrameworkSupportProviderWrapper myNewProvider;
+
+ private FrameworkSupportProviderBasedType(FrameworkSupportProvider oldProvider,
+ OldFrameworkSupportProviderWrapper newProvider) {
+ super(oldProvider.getId());
+ myOldProvider = oldProvider;
+ myNewProvider = newProvider;
+ }
+
+ @NotNull
+ @Override
+ public FrameworkSupportInModuleProvider createProvider() {
+ return myNewProvider;
+ }
+
+ @NotNull
+ @Override
+ public String getPresentableName() {
+ return GuiUtils.getTextWithoutMnemonicEscaping(myOldProvider.getTitle());
+ }
+
+ @Override
+ public String getUnderlyingFrameworkTypeId() {
+ return myOldProvider.getUnderlyingFrameworkId();
+ }
+
+ @NotNull
+ @Override
+ public Icon getIcon() {
+ final Icon icon = myOldProvider.getIcon();
+ return icon != null ? icon : EmptyIcon.ICON_16;
+ }
+
+ public FrameworkSupportProvider getProvider() {
+ return myOldProvider;
+ }
+ }
+
+ public static class FrameworkSupportConfigurableWrapper extends FrameworkSupportInModuleConfigurable {
+ private final FrameworkSupportConfigurable myConfigurable;
+ private final FrameworkLibraryVersionFilter myVersionFilter;
+
+ public FrameworkSupportConfigurableWrapper(FrameworkSupportConfigurable configurable) {
+ Disposer.register(this, configurable);
+ myConfigurable = configurable;
+ myVersionFilter = getVersionFilter(configurable);
+ }
+
+ private FrameworkLibraryVersionFilter getVersionFilter(FrameworkSupportConfigurable configurable) {
+ if (configurable instanceof FrameworkSupportWithLibrary) {
+ final FrameworkLibraryVersionFilter filter = configurable.getVersionFilter();
+ if (filter != null) {
+ return filter;
+ }
+ }
+ return new FrameworkLibraryVersionFilter() {
+ @Override
+ public boolean isAccepted(@NotNull FrameworkLibraryVersion version) {
+ final FrameworkVersion selectedVersion = myConfigurable.getSelectedVersion();
+ return selectedVersion == null || version.getVersionString().equals(selectedVersion.getVersionName());
+ }
+ };
+ }
+
+ public FrameworkSupportConfigurable getConfigurable() {
+ return myConfigurable;
+ }
+
+ @Override
+ public void onFrameworkSelectionChanged(boolean selected) {
+ myConfigurable.onFrameworkSelectionChanged(selected);
+ }
+
+ @Override
+ public boolean isVisible() {
+ return myConfigurable.isVisible();
+ }
+
+ @Override
+ public JComponent createComponent() {
+ return myConfigurable.getComponent();
+ }
+
+ @Override
+ public boolean isOnlyLibraryAdded() {
+ return myConfigurable instanceof FrameworkSupportWithLibrary && ((FrameworkSupportWithLibrary)myConfigurable).isLibraryOnly();
+ }
+
+ @Override
+ public CustomLibraryDescription createLibraryDescription() {
+ if (myConfigurable instanceof FrameworkSupportWithLibrary) {
+ return ((FrameworkSupportWithLibrary)myConfigurable).createLibraryDescription();
+ }
+
+ List<? extends FrameworkVersion> versions = myConfigurable.getVersions();
+ if (versions.isEmpty()) return null;
+ return OldCustomLibraryDescription.createByVersions(versions);
+ }
+
+ @NotNull
+ @Override
+ public FrameworkLibraryVersionFilter getLibraryVersionFilter() {
+ return myVersionFilter;
+ }
+
+ @Override
+ public void addSupport(@NotNull Module module,
+ @NotNull ModifiableRootModel rootModel,
+ @NotNull ModifiableModelsProvider modifiableModelsProvider) {
+ myConfigurable.addSupport(module, rootModel, null);
+ }
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/ide/util/newProjectWizard/ProjectNameStep.java b/java/idea-ui/src/com/intellij/ide/util/newProjectWizard/ProjectNameStep.java
new file mode 100644
index 0000000..cb322d2
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/ide/util/newProjectWizard/ProjectNameStep.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright 2000-2012 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.ide.util.newProjectWizard;
+
+import com.intellij.ide.IdeBundle;
+import com.intellij.ide.util.newProjectWizard.modes.WizardMode;
+import com.intellij.ide.util.projectWizard.*;
+import com.intellij.ide.util.projectWizard.importSources.impl.ProjectFromSourcesBuilderImpl;
+import com.intellij.openapi.options.ConfigurationException;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.projectImport.ProjectFormatPanel;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import java.awt.*;
+import java.io.File;
+
+/**
+ * @author Eugene Zhuravlev
+ * Date: Jul 17, 2007
+ */
+public class ProjectNameStep extends ModuleWizardStep {
+ private final JPanel myPanel;
+ protected final JPanel myAdditionalContentPanel;
+ protected NamePathComponent myNamePathComponent;
+ protected final WizardContext myWizardContext;
+ @Nullable
+ protected final WizardMode myMode;
+ private final ProjectFormatPanel myFormatPanel = new ProjectFormatPanel();
+
+ public ProjectNameStep(WizardContext wizardContext, @Nullable final WizardMode mode) {
+ myWizardContext = wizardContext;
+ myMode = mode;
+ myNamePathComponent = new NamePathComponent(
+ IdeBundle.message("label.project.name"),
+ IdeBundle.message("label.project.files.location"),
+ IdeBundle.message("title.select.project.file.directory", IdeBundle.message("project.new.wizard.project.identification")),
+ IdeBundle.message("description.select.project.file.directory", StringUtil.capitalize(IdeBundle.message("project.new.wizard.project.identification"))),
+ true, false
+ );
+ final String baseDir = myWizardContext.getProjectFileDirectory();
+ final String projectName = myWizardContext.getProjectName();
+ final String initialProjectName = projectName != null ? projectName : ProjectWizardUtil.findNonExistingFileName(baseDir, "untitled", "");
+ myNamePathComponent.setPath(projectName == null ? (baseDir + File.separator + initialProjectName) : baseDir);
+ myNamePathComponent.setNameValue(initialProjectName);
+ myNamePathComponent.getNameComponent().select(0, initialProjectName.length());
+
+ myPanel = new JPanel(new GridBagLayout());
+ myPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
+ myPanel.add(myNamePathComponent, new GridBagConstraints(0, GridBagConstraints.RELATIVE, 1, 1, 1.0, 0.0, GridBagConstraints.NORTHWEST, GridBagConstraints.HORIZONTAL, new Insets(10, 0, 20, 0), 0, 0));
+
+ myNamePathComponent.setVisible(isStepVisible());
+ myAdditionalContentPanel = new JPanel(new GridBagLayout());
+ myPanel.add(myAdditionalContentPanel, new GridBagConstraints(0, GridBagConstraints.RELATIVE, 1, 1, 1.0, 1.0, GridBagConstraints.NORTHWEST, GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0));
+ }
+
+ public JComponent getComponent() {
+ return myPanel;
+ }
+
+ @Override
+ public boolean isStepVisible() {
+ return myWizardContext.getProject() == null;
+ }
+
+ public void updateDataModel() {
+ myWizardContext.setProjectName(getProjectName());
+ final String projectFileDirectory = getProjectFileDirectory();
+ myWizardContext.setProjectFileDirectory(projectFileDirectory);
+ ProjectBuilder moduleBuilder = myWizardContext.getProjectBuilder();
+ if (moduleBuilder != null) {
+ myWizardContext.setProjectBuilder(moduleBuilder);
+ if (moduleBuilder instanceof ModuleBuilder) { // no SourcePathsBuilder here !
+ ((ModuleBuilder)moduleBuilder).setContentEntryPath(projectFileDirectory);
+ }
+ else if (moduleBuilder instanceof ProjectFromSourcesBuilderImpl) {
+ ((ProjectFromSourcesBuilderImpl)moduleBuilder).setBaseProjectPath(projectFileDirectory);
+ }
+ }
+ myFormatPanel.updateData(myWizardContext);
+ }
+
+ public Icon getIcon() {
+ return myWizardContext.getStepIcon();
+ }
+
+ public JComponent getPreferredFocusedComponent() {
+ return myNamePathComponent.getNameComponent();
+ }
+
+ public String getHelpId() {
+ return "reference.dialogs.new.project.fromCode.name";
+ }
+
+ public String getProjectFileDirectory() {
+ return myNamePathComponent.getPath();
+ }
+
+ public String getProjectName() {
+ return myNamePathComponent.getNameValue();
+ }
+
+ public boolean validate() throws ConfigurationException {
+ return myNamePathComponent.validateNameAndPath(myWizardContext, myFormatPanel.isDefault());
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/ide/util/newProjectWizard/ProjectNameWithTypeStep.form b/java/idea-ui/src/com/intellij/ide/util/newProjectWizard/ProjectNameWithTypeStep.form
new file mode 100644
index 0000000..2df2467
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/ide/util/newProjectWizard/ProjectNameWithTypeStep.form
@@ -0,0 +1,167 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.intellij.ide.util.newProjectWizard.ProjectNameWithTypeStep">
+ <grid id="807b" binding="myModulePanel" layout-manager="GridBagLayout">
+ <constraints>
+ <xy x="20" y="20" width="416" height="453"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <grid id="27dc6" binding="myInternalPanel" layout-manager="GridBagLayout">
+ <constraints>
+ <grid row="1" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+ <gridbag weightx="1.0" weighty="1.0"/>
+ </constraints>
+ <properties>
+ <maximumSize width="250" height="150"/>
+ <preferredSize width="250" height="150"/>
+ </properties>
+ <clientProperties>
+ <BorderFactoryClass class="java.lang.String" value="com.intellij.ui.IdeBorderFactory$PlainSmallWithIndent"/>
+ </clientProperties>
+ <border type="none"/>
+ <children>
+ <scrollpane id="d8de8" class="com.intellij.ui.components.JBScrollPane" binding="myModuleTypeScrollPane">
+ <constraints>
+ <grid row="2" column="0" row-span="1" col-span="1" vsize-policy="7" hsize-policy="7" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+ <gridbag top="0" left="0" bottom="0" right="5" weightx="0.1" weighty="1.0"/>
+ </constraints>
+ <properties>
+ <horizontalScrollBarPolicy value="31"/>
+ <preferredSize width="125" height="128"/>
+ </properties>
+ <border type="none"/>
+ <children>
+ <component id="c263e" class="com.intellij.ui.components.JBList" binding="myTypesList">
+ <constraints/>
+ <properties/>
+ </component>
+ </children>
+ </scrollpane>
+ <component id="fb71" class="javax.swing.JLabel" binding="myModuleTypeLabel">
+ <constraints>
+ <grid row="1" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
+ <gridbag top="10" left="0" bottom="2" right="0" weightx="0.0" weighty="0.0"/>
+ </constraints>
+ <properties>
+ <text resource-bundle="messages/ProjectBundle" key="project.new.wizard.select.type.label"/>
+ </properties>
+ </component>
+ <component id="9073d" class="javax.swing.JLabel" binding="myDescriptionLabel">
+ <constraints>
+ <grid row="1" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
+ <gridbag top="10" left="0" bottom="2" right="0" weightx="0.0" weighty="0.0"/>
+ </constraints>
+ <properties>
+ <text resource-bundle="messages/ProjectBundle" key="project.new.wizard.description.title"/>
+ </properties>
+ </component>
+ <scrollpane id="6260" class="com.intellij.ui.components.JBScrollPane" binding="myDescriptionScrollPane">
+ <constraints>
+ <grid row="2" column="1" row-span="1" col-span="1" vsize-policy="7" hsize-policy="7" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+ <gridbag weightx="0.8" weighty="0.0"/>
+ </constraints>
+ <properties>
+ <horizontalScrollBarPolicy value="30"/>
+ <preferredSize width="28" height="20"/>
+ </properties>
+ <border type="none"/>
+ <children>
+ <component id="6e12" class="javax.swing.JEditorPane" binding="myModuleDescriptionPane">
+ <constraints/>
+ <properties/>
+ </component>
+ </children>
+ </scrollpane>
+ <grid id="8a2b7" layout-manager="GridLayoutManager" row-count="3" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+ <margin top="0" left="0" bottom="0" right="0"/>
+ <constraints>
+ <grid row="0" column="0" row-span="1" col-span="2" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+ <gridbag weightx="1.0" weighty="0.0"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <component id="e6ad1" class="javax.swing.JTextField" binding="myModuleName">
+ <constraints>
+ <grid row="0" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="8" fill="1" indent="0" use-parent-layout="false">
+ <preferred-size width="150" height="-1"/>
+ </grid>
+ </constraints>
+ <properties/>
+ </component>
+ <component id="1f996" class="javax.swing.JLabel">
+ <constraints>
+ <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties>
+ <labelFor value="e6ad1"/>
+ <text resource-bundle="messages/ProjectBundle" key="project.new.wizard.module.name.title"/>
+ </properties>
+ </component>
+ <component id="4af6e" class="com.intellij.openapi.ui.TextFieldWithBrowseButton" binding="myModuleContentRoot">
+ <constraints>
+ <grid row="1" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties/>
+ </component>
+ <component id="c4851" class="javax.swing.JLabel">
+ <constraints>
+ <grid row="1" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties>
+ <labelFor value="4af6e"/>
+ <text resource-bundle="messages/ProjectBundle" key="project.new.wizard.module.root.title"/>
+ </properties>
+ </component>
+ <component id="83a50" class="com.intellij.openapi.ui.TextFieldWithBrowseButton" binding="myModuleFileLocation">
+ <constraints>
+ <grid row="2" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties>
+ <preferredSize width="280" height="20"/>
+ </properties>
+ </component>
+ <component id="a4319" class="javax.swing.JLabel">
+ <constraints>
+ <grid row="2" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties>
+ <labelFor value="83a50"/>
+ <text resource-bundle="messages/ProjectBundle" key="project.new.wizard.module.file.title"/>
+ </properties>
+ </component>
+ </children>
+ </grid>
+ </children>
+ </grid>
+ <grid id="fa640" binding="myHeader" layout-manager="GridLayoutManager" row-count="1" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+ <margin top="0" left="0" bottom="0" right="0"/>
+ <constraints>
+ <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+ <gridbag weightx="0.0" weighty="0.0"/>
+ </constraints>
+ <properties>
+ <visible value="true"/>
+ </properties>
+ <border type="none"/>
+ <children>
+ <component id="63a74" class="javax.swing.JCheckBox" binding="myCreateModuleCb">
+ <constraints>
+ <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties>
+ <text value="&Create module"/>
+ </properties>
+ </component>
+ <component id="d48cf" class="javax.swing.JSeparator">
+ <constraints>
+ <grid row="0" column="1" row-span="1" col-span="1" vsize-policy="7" hsize-policy="7" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties/>
+ </component>
+ </children>
+ </grid>
+ </children>
+ </grid>
+</form>
diff --git a/java/idea-ui/src/com/intellij/ide/util/newProjectWizard/ProjectNameWithTypeStep.java b/java/idea-ui/src/com/intellij/ide/util/newProjectWizard/ProjectNameWithTypeStep.java
new file mode 100644
index 0000000..2c4d18b
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/ide/util/newProjectWizard/ProjectNameWithTypeStep.java
@@ -0,0 +1,491 @@
+/*
+ * Copyright 2000-2009 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.ide.util.newProjectWizard;
+
+import com.intellij.ide.BrowserUtil;
+import com.intellij.ide.IdeBundle;
+import com.intellij.ide.highlighter.ModuleFileType;
+import com.intellij.ide.util.BrowseFilesListener;
+import com.intellij.ide.util.newProjectWizard.modes.CreateFromTemplateMode;
+import com.intellij.ide.util.newProjectWizard.modes.WizardMode;
+import com.intellij.ide.util.projectWizard.ModuleBuilder;
+import com.intellij.ide.util.projectWizard.ProjectWizardUtil;
+import com.intellij.ide.util.projectWizard.WizardContext;
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.actionSystem.CustomShortcutSet;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.module.ModuleManager;
+import com.intellij.openapi.options.ConfigurationException;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.project.ProjectBundle;
+import com.intellij.openapi.roots.ui.configuration.ProjectStructureConfigurable;
+import com.intellij.openapi.ui.Messages;
+import com.intellij.openapi.ui.TextFieldWithBrowseButton;
+import com.intellij.openapi.util.Comparing;
+import com.intellij.openapi.util.SystemInfo;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.ui.DocumentAdapter;
+import com.intellij.ui.DoubleClickListener;
+import com.intellij.ui.components.JBScrollPane;
+import com.intellij.util.IJSwingUtilities;
+import com.intellij.util.ui.UIUtil;
+
+import javax.swing.*;
+import javax.swing.event.*;
+import java.awt.*;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.KeyEvent;
+import java.awt.event.MouseEvent;
+import java.io.File;
+import java.util.List;
+
+public class ProjectNameWithTypeStep extends ProjectNameStep {
+ private JEditorPane myModuleDescriptionPane;
+ private JList myTypesList;
+ protected JCheckBox myCreateModuleCb;
+ private JPanel myModulePanel;
+ private JPanel myInternalPanel;
+ private JTextField myModuleName;
+ private TextFieldWithBrowseButton myModuleContentRoot;
+ private TextFieldWithBrowseButton myModuleFileLocation;
+ private JPanel myHeader;
+
+ private JLabel myModuleTypeLabel;
+ private JLabel myDescriptionLabel;
+ private JBScrollPane myModuleTypeScrollPane;
+ private JBScrollPane myDescriptionScrollPane;
+
+ private boolean myModuleNameChangedByUser = false;
+ private boolean myModuleNameDocListenerEnabled = true;
+
+ private boolean myContentRootChangedByUser = false;
+ private boolean myContentRootDocListenerEnabled = true;
+
+ private boolean myImlLocationChangedByUser = false;
+ private boolean myImlLocationDocListenerEnabled = true;
+ private final StepSequence mySequence;
+
+
+ public ProjectNameWithTypeStep(final WizardContext wizardContext, StepSequence sequence, final WizardMode mode) {
+ super(wizardContext, mode);
+ mySequence = sequence;
+ myAdditionalContentPanel.add(myModulePanel,
+ new GridBagConstraints(0, GridBagConstraints.RELATIVE, 1, 1, 1, 1, GridBagConstraints.NORTHWEST,
+ GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0));
+ myHeader.setVisible(myWizardContext.isCreatingNewProject() && !isCreateFromTemplateMode());
+ myCreateModuleCb.addActionListener(new ActionListener() {
+ public void actionPerformed(final ActionEvent e) {
+ UIUtil.setEnabled(myInternalPanel, myCreateModuleCb.isSelected(), true);
+ fireStateChanged();
+ }
+ });
+ myCreateModuleCb.setSelected(true);
+ if (!myWizardContext.isCreatingNewProject()){
+ myInternalPanel.setBorder(null);
+ }
+ myModuleDescriptionPane.setContentType(UIUtil.HTML_MIME);
+ myModuleDescriptionPane.addHyperlinkListener(new HyperlinkListener() {
+ public void hyperlinkUpdate(HyperlinkEvent e) {
+ if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) {
+ try {
+ BrowserUtil.browse(e.getURL());
+ }
+ catch (IllegalThreadStateException ex) {
+ // it's not a problem
+ }
+ }
+ }
+ });
+ myModuleDescriptionPane.setEditable(false);
+
+ final DefaultListModel defaultListModel = new DefaultListModel();
+ for (ModuleBuilder builder : ModuleBuilder.getAllBuilders()) {
+ defaultListModel.addElement(builder);
+ }
+ myTypesList.setModel(defaultListModel);
+ myTypesList.setSelectionModel(new PermanentSingleSelectionModel());
+ myTypesList.setCellRenderer(new DefaultListCellRenderer(){
+ public Component getListCellRendererComponent(final JList list, final Object value, final int index, final boolean isSelected, final boolean cellHasFocus) {
+ final Component rendererComponent = super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
+ final ModuleBuilder builder = (ModuleBuilder)value;
+ setIcon(builder.getBigIcon());
+ setDisabledIcon(builder.getBigIcon());
+ setText(builder.getPresentableName());
+ return rendererComponent;
+ }
+ });
+ myTypesList.addListSelectionListener(new ListSelectionListener() {
+ @SuppressWarnings({"HardCodedStringLiteral"})
+ public void valueChanged(ListSelectionEvent e) {
+ if (e.getValueIsAdjusting()) {
+ return;
+ }
+
+ final ModuleBuilder typeSelected = (ModuleBuilder)myTypesList.getSelectedValue();
+
+ final StringBuilder sb = new StringBuilder("<html><body><font face=\"Verdana\" ");
+ sb.append(SystemInfo.isMac ? "" : "size=\"-1\"").append('>');
+ sb.append(typeSelected.getDescription()).append("</font></body></html>");
+
+ myModuleDescriptionPane.setText(sb.toString());
+
+ boolean focusOwner = myTypesList.isFocusOwner();
+ fireStateChanged();
+ if (focusOwner) {
+ SwingUtilities.invokeLater(new Runnable() {
+ public void run() {
+ myTypesList.requestFocusInWindow();
+ }
+ });
+ }
+ }
+ });
+ myTypesList.setSelectedIndex(0);
+ new DoubleClickListener() {
+ @Override
+ protected boolean onDoubleClick(MouseEvent e) {
+ myWizardContext.requestNextStep();
+ return true;
+ }
+ }.installOn(myTypesList);
+
+ final Dimension preferredSize = calcTypeListPreferredSize(ModuleBuilder.getAllBuilders());
+ final JBScrollPane pane = IJSwingUtilities.findParentOfType(myTypesList, JBScrollPane.class);
+ pane.setPreferredSize(preferredSize);
+ pane.setMinimumSize(preferredSize);
+
+ myNamePathComponent.getNameComponent().getDocument().addDocumentListener(new DocumentAdapter() {
+ protected void textChanged(final DocumentEvent e) {
+ if (!myModuleNameChangedByUser) {
+ setModuleName(myNamePathComponent.getNameValue());
+ }
+ }
+ });
+
+ myModuleContentRoot.addBrowseFolderListener(ProjectBundle.message("project.new.wizard.module.content.root.chooser.title"), ProjectBundle.message("project.new.wizard.module.content.root.chooser.description"),
+ myWizardContext.getProject(), BrowseFilesListener.SINGLE_DIRECTORY_DESCRIPTOR);
+
+ myNamePathComponent.getPathComponent().getDocument().addDocumentListener(new DocumentAdapter() {
+ protected void textChanged(final DocumentEvent e) {
+ if (!myContentRootChangedByUser) {
+ setModuleContentRoot(myNamePathComponent.getPath());
+ }
+ }
+ });
+ myModuleName.getDocument().addDocumentListener(new DocumentAdapter() {
+ protected void textChanged(final DocumentEvent e) {
+ if (myModuleNameDocListenerEnabled) {
+ myModuleNameChangedByUser = true;
+ }
+ String path = getDefaultBaseDir(wizardContext);
+ final String moduleName = getModuleName();
+ if (path.length() > 0 && !Comparing.strEqual(moduleName, myNamePathComponent.getNameValue())) {
+ path += "/" + moduleName;
+ }
+ if (!myContentRootChangedByUser) {
+ final boolean f = myModuleNameChangedByUser;
+ myModuleNameChangedByUser = true;
+ setModuleContentRoot(path);
+ myModuleNameChangedByUser = f;
+ }
+ if (!myImlLocationChangedByUser) {
+ setImlFileLocation(path);
+ }
+ }
+ });
+ myModuleContentRoot.getTextField().getDocument().addDocumentListener(new DocumentAdapter() {
+ protected void textChanged(final DocumentEvent e) {
+ if (myContentRootDocListenerEnabled) {
+ myContentRootChangedByUser = true;
+ }
+ if (!myImlLocationChangedByUser) {
+ setImlFileLocation(myModuleContentRoot.getText());
+ }
+ if (!myModuleNameChangedByUser) {
+ final String path = FileUtil.toSystemIndependentName(myModuleContentRoot.getText());
+ final int idx = path.lastIndexOf("/");
+
+ boolean f = myContentRootChangedByUser;
+ myContentRootChangedByUser = true;
+
+ boolean i = myImlLocationChangedByUser;
+ myImlLocationChangedByUser = true;
+
+ setModuleName(idx >= 0 ? path.substring(idx + 1) : "");
+
+ myContentRootChangedByUser = f;
+ myImlLocationChangedByUser = i;
+ }
+ }
+ });
+
+ myModuleFileLocation.addBrowseFolderListener(ProjectBundle.message("project.new.wizard.module.file.chooser.title"), ProjectBundle.message("project.new.wizard.module.file.description"),
+ myWizardContext.getProject(), BrowseFilesListener.SINGLE_DIRECTORY_DESCRIPTOR);
+ myModuleFileLocation.getTextField().getDocument().addDocumentListener(new DocumentAdapter() {
+ protected void textChanged(final DocumentEvent e) {
+ if (myImlLocationDocListenerEnabled) {
+ myImlLocationChangedByUser = true;
+ }
+ }
+ });
+ myNamePathComponent.getPathComponent().getDocument().addDocumentListener(new DocumentAdapter() {
+ protected void textChanged(final DocumentEvent e) {
+ if (!myImlLocationChangedByUser) {
+ setImlFileLocation(myNamePathComponent.getPath());
+ }
+ }
+ });
+ if (wizardContext.isCreatingNewProject()) {
+ setModuleName(myNamePathComponent.getNameValue());
+ setModuleContentRoot(myNamePathComponent.getPath());
+ setImlFileLocation(myNamePathComponent.getPath());
+ } else {
+ final Project project = wizardContext.getProject();
+ assert project != null;
+ VirtualFile baseDir = project.getBaseDir();
+ if (baseDir != null) { //e.g. was deleted
+ final String baseDirPath = baseDir.getPath();
+ String moduleName = ProjectWizardUtil.findNonExistingFileName(baseDirPath, "untitled", "");
+ String contentRoot = baseDirPath + "/" + moduleName;
+ if (!Comparing.strEqual(project.getName(), wizardContext.getProjectName()) && !wizardContext.isCreatingNewProject() && wizardContext.getProjectName() != null) {
+ moduleName = ProjectWizardUtil.findNonExistingFileName(wizardContext.getProjectFileDirectory(), wizardContext.getProjectName(), "");
+ contentRoot = wizardContext.getProjectFileDirectory();
+ }
+ setModuleName(moduleName);
+ setModuleContentRoot(contentRoot);
+ setImlFileLocation(contentRoot);
+ myModuleName.select(0, moduleName.length());
+ }
+ }
+
+
+ if (isCreateFromTemplateMode()) {
+ replaceModuleTypeOptions(new JPanel());
+ }
+ else {
+ final AnAction arrow = new AnAction() {
+ @Override
+ public void actionPerformed(AnActionEvent e) {
+ if (e.getInputEvent() instanceof KeyEvent) {
+ final int code = ((KeyEvent)e.getInputEvent()).getKeyCode();
+ if (!myCreateModuleCb.isSelected()) return;
+ int i = myTypesList.getSelectedIndex();
+ if (code == KeyEvent.VK_DOWN) {
+ if (++i == myTypesList.getModel().getSize()) return;
+ }
+ else if (code == KeyEvent.VK_UP) {
+ if (--i == -1) return;
+ }
+ myTypesList.setSelectedIndex(i);
+ }
+ }
+ };
+ CustomShortcutSet shortcutSet = new CustomShortcutSet(KeyEvent.VK_UP, KeyEvent.VK_DOWN);
+ arrow.registerCustomShortcutSet(shortcutSet, myNamePathComponent.getNameComponent());
+ arrow.registerCustomShortcutSet(shortcutSet, myModuleName);
+ }
+ }
+
+ private boolean isCreateFromTemplateMode() {
+ return myMode instanceof CreateFromTemplateMode;
+ }
+
+ private Dimension calcTypeListPreferredSize(final List<ModuleBuilder> allModuleTypes) {
+ int width = 0;
+ int height = 0;
+ final FontMetrics fontMetrics = myTypesList.getFontMetrics(myTypesList.getFont());
+ final int fontHeight = fontMetrics.getMaxAscent() + fontMetrics.getMaxDescent();
+ for (final ModuleBuilder type : allModuleTypes) {
+ final Icon icon = type.getBigIcon();
+ final int iconHeight = icon != null ? icon.getIconHeight() : 0;
+ final int iconWidth = icon != null ? icon.getIconWidth() : 0;
+ height += Math.max(iconHeight, fontHeight) + 6;
+ width = Math.max(width, iconWidth + fontMetrics.stringWidth(type.getPresentableName()) + 10);
+ }
+ return new Dimension(width, height);
+ }
+
+ private String getDefaultBaseDir(WizardContext wizardContext) {
+ if (wizardContext.isCreatingNewProject()) {
+ return myNamePathComponent.getPath();
+ } else {
+ final Project project = wizardContext.getProject();
+ assert project != null;
+ final VirtualFile baseDir = project.getBaseDir();
+ if (baseDir != null) {
+ return baseDir.getPath();
+ }
+ return "";
+ }
+ }
+
+ @Override
+ public boolean isStepVisible() {
+ return true;
+ }
+
+ public JComponent getPreferredFocusedComponent() {
+ return myWizardContext.isCreatingNewProject() ? super.getPreferredFocusedComponent() : myModuleName;
+ }
+
+ private void setImlFileLocation(final String path) {
+ myImlLocationDocListenerEnabled = false;
+ myModuleFileLocation.setText(FileUtil.toSystemDependentName(path));
+ myImlLocationDocListenerEnabled = true;
+ }
+
+ private void setModuleContentRoot(final String path) {
+ myContentRootDocListenerEnabled = false;
+ myModuleContentRoot.setText(FileUtil.toSystemDependentName(path));
+ myContentRootDocListenerEnabled = true;
+ }
+
+ private void setModuleName(String moduleName) {
+ myModuleNameDocListenerEnabled = false;
+ myModuleName.setText(moduleName);
+ myModuleNameDocListenerEnabled = true;
+ }
+
+ public void updateStep() {
+ super.updateStep();
+ if (!isCreateFromTemplateMode()) {
+ if (myCreateModuleCb.isSelected()) {
+ mySequence.setType(getSelectedBuilderId());
+ } else {
+ mySequence.setType(null);
+ }
+ }
+ }
+
+ public void updateDataModel() {
+
+ if (!isCreateFromTemplateMode()) {
+ mySequence.setType(myCreateModuleCb.isSelected() ? getSelectedBuilderId() : null);
+ }
+ super.updateDataModel();
+
+ if (!isCreateFromTemplateMode() && myCreateModuleCb.isSelected()) {
+ final ModuleBuilder builder = (ModuleBuilder)myMode.getModuleBuilder();
+ assert builder != null;
+ final String moduleName = getModuleName();
+ builder.setName(moduleName);
+ builder.setModuleFilePath(
+ FileUtil.toSystemIndependentName(myModuleFileLocation.getText()) + "/" + moduleName + ModuleFileType.DOT_DEFAULT_EXTENSION);
+ builder.setContentEntryPath(FileUtil.toSystemIndependentName(myModuleContentRoot.getText()));
+ }
+ }
+
+ protected String getSelectedBuilderId() {
+ return ((ModuleBuilder)myTypesList.getSelectedValue()).getBuilderId();
+ }
+
+ public boolean validate() throws ConfigurationException {
+ if (myCreateModuleCb.isSelected() || !myWizardContext.isCreatingNewProject()) {
+ if (validateModulePaths()) return false;
+ }
+ if (!myWizardContext.isCreatingNewProject()) {
+ validateExistingModuleName();
+ }
+ return !myWizardContext.isCreatingNewProject() || super.validate();
+ }
+
+ private void validateExistingModuleName() throws ConfigurationException {
+ final String moduleName = getModuleName();
+ final Module module;
+ final ProjectStructureConfigurable fromConfigurable = ProjectStructureConfigurable.getInstance(myWizardContext.getProject());
+ if (fromConfigurable != null) {
+ module = fromConfigurable.getModulesConfig().getModule(moduleName);
+ }
+ else {
+ module = ModuleManager.getInstance(myWizardContext.getProject()).findModuleByName(moduleName);
+ }
+ if (module != null) {
+ throw new ConfigurationException("Module \'" + moduleName + "\' already exist in project. Please, specify another name.");
+ }
+ }
+
+ private boolean validateModulePaths() throws ConfigurationException {
+ final String moduleName = getModuleName();
+ final String moduleFileDirectory = myModuleFileLocation.getText();
+ if (moduleFileDirectory.length() == 0) {
+ throw new ConfigurationException("Enter module file location");
+ }
+ if (moduleName.length() == 0) {
+ throw new ConfigurationException("Enter a module name");
+ }
+
+ if (!ProjectWizardUtil.createDirectoryIfNotExists(IdeBundle.message("directory.module.file"), moduleFileDirectory,
+ myImlLocationChangedByUser)) {
+ return true;
+ }
+ if (!ProjectWizardUtil.createDirectoryIfNotExists(IdeBundle.message("directory.module.content.root"), myModuleContentRoot.getText(),
+ myContentRootChangedByUser)) {
+ return true;
+ }
+
+ File moduleFile = new File(moduleFileDirectory, moduleName + ModuleFileType.DOT_DEFAULT_EXTENSION);
+ if (moduleFile.exists()) {
+ int answer = Messages.showYesNoDialog(IdeBundle.message("prompt.overwrite.project.file", moduleFile.getAbsolutePath(),
+ IdeBundle.message("project.new.wizard.module.identification")),
+ IdeBundle.message("title.file.already.exists"), Messages.getQuestionIcon());
+ if (answer != 0) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private static class PermanentSingleSelectionModel extends DefaultListSelectionModel {
+ public PermanentSingleSelectionModel() {
+ super.setSelectionMode(SINGLE_SELECTION);
+ }
+
+ public final void setSelectionMode(int selectionMode) {
+ }
+
+ public final void removeSelectionInterval(int index0, int index1) {
+ }
+ }
+
+ public String getHelpId() {
+ return "reference.dialogs.new.project.fromScratch";
+ }
+
+ protected String getModuleName() {
+ return myModuleName.getText().trim();
+ }
+
+ protected void addModuleNameListener(final DocumentListener listener) {
+ myModuleName.getDocument().addDocumentListener(listener);
+ }
+
+ protected void replaceModuleTypeOptions(final Component component) {
+ myModuleTypeLabel.setVisible(false);
+ myDescriptionLabel.setVisible(false);
+ myModuleTypeScrollPane.setVisible(false);
+ myDescriptionScrollPane.setVisible(false);
+ myInternalPanel.add(component, new GridBagConstraints(0, 2, 1, 1, 1.0, 1.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH,
+ new Insets(10, 0, 0, 0), 0, 0));
+ }
+
+ @Override
+ public String getName() {
+ return "Name and Type";
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/ide/util/newProjectWizard/ProjectTypesList.java b/java/idea-ui/src/com/intellij/ide/util/newProjectWizard/ProjectTypesList.java
new file mode 100644
index 0000000..b292f2d
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/ide/util/newProjectWizard/ProjectTypesList.java
@@ -0,0 +1,296 @@
+/*
+ * Copyright 2000-2012 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.ide.util.newProjectWizard;
+
+import com.intellij.ide.util.projectWizard.WizardContext;
+import com.intellij.openapi.Disposable;
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.actionSystem.CustomShortcutSet;
+import com.intellij.openapi.progress.ProgressIndicator;
+import com.intellij.openapi.progress.ProgressManager;
+import com.intellij.openapi.progress.Task;
+import com.intellij.openapi.ui.popup.ListItemDescriptor;
+import com.intellij.openapi.util.Condition;
+import com.intellij.openapi.util.Pair;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.platform.ProjectTemplate;
+import com.intellij.platform.ProjectTemplatesFactory;
+import com.intellij.platform.templates.RemoteTemplatesFactory;
+import com.intellij.psi.codeStyle.MinusculeMatcher;
+import com.intellij.psi.codeStyle.NameUtil;
+import com.intellij.ui.CollectionListModel;
+import com.intellij.ui.DocumentAdapter;
+import com.intellij.ui.SearchTextField;
+import com.intellij.ui.components.JBList;
+import com.intellij.ui.popup.list.GroupedItemsListRenderer;
+import com.intellij.ui.speedSearch.FilteringListModel;
+import com.intellij.util.containers.MultiMap;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import javax.swing.event.DocumentEvent;
+import java.awt.event.InputEvent;
+import java.awt.event.KeyEvent;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+/**
+ * @author Dmitry Avdeev
+ * Date: 11/21/12
+ */
+public class ProjectTypesList implements Disposable {
+
+ private final JBList myList;
+ private final SearchTextField mySearchField;
+ private final FilteringListModel<TemplateItem> myFilteringListModel;
+ private MinusculeMatcher myMatcher;
+ private Pair<TemplateItem, Integer> myBestMatch;
+
+ private final TemplateItem myLoadingItem;
+
+ public ProjectTypesList(JBList list, SearchTextField searchField, MultiMap<TemplatesGroup, ProjectTemplate> map, final WizardContext context) {
+ myList = list;
+ mySearchField = searchField;
+
+ List<TemplateItem> items = buildItems(map);
+ final RemoteTemplatesFactory factory = new RemoteTemplatesFactory();
+ final String groupName = factory.getGroups()[0];
+ final TemplatesGroup samplesGroup = new TemplatesGroup(groupName, "", null);
+ myLoadingItem = new TemplateItem(new LoadingProjectTemplate(), samplesGroup) {
+ @Override
+ Icon getIcon() {
+ return null;
+ }
+
+ @Override
+ String getDescription() {
+ return "";
+ }
+ };
+ items.add(myLoadingItem);
+ final CollectionListModel<TemplateItem> model = new CollectionListModel<TemplateItem>(items);
+ myFilteringListModel = new FilteringListModel<TemplateItem>(model);
+
+ ProgressManager.getInstance().run(new Task.Backgroundable(context.getProject(), "Loading Samples") {
+ @Override
+ public void run(@NotNull ProgressIndicator indicator) {
+ try {
+ myList.setPaintBusy(true);
+ final ProjectTemplate[] templates = factory.createTemplates(groupName, context);
+ Runnable runnable = new Runnable() {
+ public void run() {
+ int index = myList.getSelectedIndex();
+ model.remove(myLoadingItem);
+ for (ProjectTemplate template : templates) {
+ model.add(new TemplateItem(template, samplesGroup));
+ }
+ myList.setSelectedIndex(index);
+ }
+ };
+ SwingUtilities.invokeLater(runnable);
+ }
+ finally {
+ myList.setPaintBusy(false);
+ }
+ }
+ });
+
+ myList.setCellRenderer(new GroupedItemsListRenderer(new ListItemDescriptor() {
+ @Nullable
+ @Override
+ public String getTextFor(Object value) {
+ return ((TemplateItem)value).getName();
+ }
+
+ @Nullable
+ @Override
+ public String getTooltipFor(Object value) {
+ return null;
+ }
+
+ @Nullable
+ @Override
+ public Icon getIconFor(Object value) {
+ return ((TemplateItem)value).getIcon();
+ }
+
+ @Override
+ public boolean hasSeparatorAboveOf(Object value) {
+ TemplateItem item = (TemplateItem)value;
+ int index = myFilteringListModel.getElementIndex(item);
+ return index == 0 || !myFilteringListModel.getElementAt(index -1).getGroupName().equals(item.getGroupName());
+ }
+
+ @Nullable
+ @Override
+ public String getCaptionAboveOf(Object value) {
+ return ((TemplateItem)value).getGroupName();
+ }
+ }));
+
+ myFilteringListModel.setFilter(new Condition<TemplateItem>() {
+ @Override
+ public boolean value(TemplateItem item) {
+ return item.getMatchingDegree() > Integer.MIN_VALUE;
+ }
+ });
+
+ myList.setModel(myFilteringListModel);
+ mySearchField.addDocumentListener(new DocumentAdapter() {
+ @Override
+ protected void textChanged(DocumentEvent e) {
+ String text = "*" + mySearchField.getText().trim();
+ myMatcher = NameUtil.buildMatcher(text, NameUtil.MatchingCaseSensitivity.NONE);
+
+ TemplateItem value = (TemplateItem)myList.getSelectedValue();
+ int degree = value == null ? Integer.MIN_VALUE : value.getMatchingDegree();
+ myBestMatch = Pair.create(degree > Integer.MIN_VALUE ? value : null, degree);
+
+ myFilteringListModel.refilter();
+ if (myBestMatch.first != null) {
+ myList.setSelectedValue(myBestMatch.first, true);
+ }
+ }
+ });
+
+ new AnAction() {
+ @Override
+ public void actionPerformed(AnActionEvent e) {
+ InputEvent event = e.getInputEvent();
+ if (event instanceof KeyEvent) {
+ int row = myList.getSelectedIndex();
+ int toSelect;
+ switch (((KeyEvent)event).getKeyCode()) {
+ case KeyEvent.VK_UP:
+ toSelect = row == 0 ? myList.getItemsCount() - 1 : row - 1;
+ myList.setSelectedIndex(toSelect);
+ myList.ensureIndexIsVisible(toSelect);
+ break;
+ case KeyEvent.VK_DOWN:
+ toSelect = row < myList.getItemsCount() - 1 ? row + 1 : 0;
+ myList.setSelectedIndex(toSelect);
+ myList.ensureIndexIsVisible(toSelect);
+ break;
+ }
+ }
+ }
+ }.registerCustomShortcutSet(new CustomShortcutSet(KeyEvent.VK_UP, KeyEvent.VK_DOWN), mySearchField);
+ }
+
+ void resetSelection() {
+ if (myList.getSelectedIndex() != -1) return;
+ SelectTemplateSettings settings = SelectTemplateSettings.getInstance();
+ if (settings.getLastGroup() == null || !setSelectedTemplate(settings.getLastGroup(), settings.getLastTemplate())) {
+ myList.setSelectedIndex(0);
+ }
+ }
+
+ void saveSelection() {
+ TemplateItem item = (TemplateItem)myList.getSelectedValue();
+ if (item != null) {
+ SelectTemplateSettings.getInstance().setLastTemplate(item.getGroupName(), item.getName());
+ }
+ }
+
+ private List<TemplateItem> buildItems(MultiMap<TemplatesGroup, ProjectTemplate> map) {
+ List<TemplateItem> items = new ArrayList<TemplateItem>();
+ List<TemplatesGroup> groups = new ArrayList<TemplatesGroup>(map.keySet());
+ Collections.sort(groups, new Comparator<TemplatesGroup>() {
+ @Override
+ public int compare(TemplatesGroup o1, TemplatesGroup o2) {
+ if (o1.getName().equals(ProjectTemplatesFactory.OTHER_GROUP)) return 2;
+ if (o1.getName().equals(ProjectTemplatesFactory.CUSTOM_GROUP)) return 1;
+ return o1.getName().compareTo(o2.getName());
+ }
+ });
+ for (TemplatesGroup group : groups) {
+ for (ProjectTemplate template : map.get(group)) {
+ TemplateItem templateItem = new TemplateItem(template, group);
+ items.add(templateItem);
+ }
+ }
+ return items;
+ }
+
+ @Nullable
+ public ProjectTemplate getSelectedTemplate() {
+ Object value = myList.getSelectedValue();
+ return value instanceof TemplateItem ? ((TemplateItem)value).myTemplate : null;
+ }
+
+ public boolean setSelectedTemplate(@Nullable String group, @Nullable String name) {
+ for (int i = 0; i < myList.getModel().getSize(); i++) {
+ Object o = myList.getModel().getElementAt(i);
+ if (o instanceof TemplateItem && ((TemplateItem)o).myGroup.getName().equals(group) && ((TemplateItem)o).getName().equals(name)) {
+ myList.setSelectedIndex(i);
+ myList.ensureIndexIsVisible(i);
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ @Override
+ public void dispose() {
+ }
+
+ class TemplateItem {
+
+ private final ProjectTemplate myTemplate;
+ private final TemplatesGroup myGroup;
+
+ TemplateItem(ProjectTemplate template, TemplatesGroup group) {
+ myTemplate = template;
+ myGroup = group;
+ }
+
+ String getName() {
+ return myTemplate.getName();
+ }
+
+ public String getGroupName() {
+ return myGroup.getName();
+ }
+
+ Icon getIcon() {
+ return myTemplate.createModuleBuilder().getNodeIcon();
+ }
+
+ protected int getMatchingDegree() {
+ if (myMatcher == null) return Integer.MAX_VALUE;
+ String text = getName() + " " + getGroupName();
+ String description = getDescription();
+ if (description != null) {
+ text += " " + StringUtil.stripHtml(description, false);
+ }
+ int i = myMatcher.matchingDegree(text);
+ if (myBestMatch == null || i > myBestMatch.second) {
+ myBestMatch = Pair.create(this, i);
+ }
+ return i;
+ }
+
+ @Nullable
+ String getDescription() {
+ return myTemplate.getDescription();
+ }
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/ide/util/newProjectWizard/SelectTemplateSettings.java b/java/idea-ui/src/com/intellij/ide/util/newProjectWizard/SelectTemplateSettings.java
new file mode 100644
index 0000000..ce257cc
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/ide/util/newProjectWizard/SelectTemplateSettings.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2000-2012 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.ide.util.newProjectWizard;
+
+import com.intellij.openapi.components.*;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * @author Dmitry Avdeev
+ * Date: 10/9/12
+ */
+@State(name = "SelectProjectTemplateSettings", storages = {@Storage( file = StoragePathMacros.APP_CONFIG + "/other.xml")})
+public class SelectTemplateSettings implements PersistentStateComponent<SelectTemplateSettings> {
+
+ public static SelectTemplateSettings getInstance() {
+ return ServiceManager.getService(SelectTemplateSettings.class);
+ }
+
+ @Nullable
+ public String getLastGroup() {
+ return LAST_TEMPLATE == null ? null : LAST_TEMPLATE.split("/")[0];
+ }
+
+ @Nullable
+ public String getLastTemplate() {
+ if (LAST_TEMPLATE == null) {
+ return null;
+ }
+ else {
+ String[] split = LAST_TEMPLATE.split("/");
+ return split.length > 1 ? split[1] : null;
+ }
+ }
+
+ public void setLastTemplate(String group, String template) {
+ LAST_TEMPLATE = group + "/" + template;
+ }
+
+ @NotNull
+ @Override
+ public SelectTemplateSettings getState() {
+ return this;
+ }
+
+ @Override
+ public void loadState(SelectTemplateSettings state) {
+ EXPERT_MODE = state.EXPERT_MODE;
+ }
+
+ public boolean EXPERT_MODE = false;
+ public String LAST_TEMPLATE = null;
+}
diff --git a/java/idea-ui/src/com/intellij/ide/util/newProjectWizard/SelectTemplateStep.form b/java/idea-ui/src/com/intellij/ide/util/newProjectWizard/SelectTemplateStep.form
new file mode 100644
index 0000000..61e1bcf
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/ide/util/newProjectWizard/SelectTemplateStep.form
@@ -0,0 +1,174 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.intellij.ide.util.newProjectWizard.SelectTemplateStep">
+ <grid id="27dc6" layout-manager="GridLayoutManager" row-count="1" column-count="2" same-size-horizontally="false" same-size-vertically="true" hgap="-1" vgap="-1">
+ <margin top="0" left="0" bottom="0" right="0"/>
+ <constraints>
+ <xy x="20" y="20" width="608" height="393"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <grid id="e5cc3" binding="myRightPanel" layout-manager="GridLayoutManager" row-count="5" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+ <margin top="5" left="5" bottom="0" right="0"/>
+ <constraints>
+ <grid row="0" column="1" row-span="1" col-span="1" vsize-policy="3" hsize-policy="7" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <grid id="5732c" binding="myDescriptionPanel" layout-manager="GridLayoutManager" row-count="1" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+ <margin top="0" left="0" bottom="0" right="0"/>
+ <constraints>
+ <grid row="1" column="0" row-span="1" col-span="1" vsize-policy="7" hsize-policy="7" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties/>
+ <clientProperties>
+ <BorderFactoryClass class="java.lang.String" value="com.intellij.ui.IdeBorderFactory$PlainSmallWithIndent"/>
+ </clientProperties>
+ <border type="etched" title="Description"/>
+ <children>
+ <component id="3ae1b" class="javax.swing.JTextPane" binding="myDescriptionPane">
+ <constraints>
+ <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="7" anchor="0" fill="3" indent="0" use-parent-layout="false">
+ <preferred-size width="150" height="50"/>
+ </grid>
+ </constraints>
+ <properties>
+ <contentType value="text/html"/>
+ <editable value="false"/>
+ <text value="<html> <head> </head> <body> <p style="margin-top: 0"> </p> </body> </html> "/>
+ </properties>
+ </component>
+ </children>
+ </grid>
+ <grid id="5d4a2" binding="myExpertPlaceholder" layout-manager="BorderLayout" hgap="0" vgap="0">
+ <constraints>
+ <grid row="4" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <grid id="a59bf" binding="myExpertPanel" layout-manager="GridBagLayout">
+ <constraints border-constraint="Center"/>
+ <properties/>
+ <border type="none"/>
+ <children/>
+ </grid>
+ </children>
+ </grid>
+ <grid id="fb50c" binding="myModulePanel" layout-manager="GridBagLayout">
+ <constraints>
+ <grid row="3" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <component id="40abf" class="javax.swing.JLabel">
+ <constraints>
+ <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
+ <gridbag weightx="0.0" weighty="1.0"/>
+ </constraints>
+ <properties>
+ <labelFor value="edb8a"/>
+ <text resource-bundle="messages/ProjectBundle" key="project.new.wizard.module.name.title"/>
+ </properties>
+ </component>
+ <component id="edb8a" class="javax.swing.JTextField" binding="myModuleName">
+ <constraints>
+ <grid row="0" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="8" fill="1" indent="0" use-parent-layout="false">
+ <preferred-size width="150" height="-1"/>
+ </grid>
+ <gridbag top="0" left="0" bottom="5" right="0" weightx="1.0" weighty="1.0"/>
+ </constraints>
+ <properties/>
+ </component>
+ <component id="7afef" class="javax.swing.JLabel">
+ <constraints>
+ <grid row="1" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
+ <gridbag weightx="0.0" weighty="1.0"/>
+ </constraints>
+ <properties>
+ <labelFor value="d2d6b"/>
+ <text resource-bundle="messages/ProjectBundle" key="project.new.wizard.module.root.title"/>
+ </properties>
+ </component>
+ <component id="d2d6b" class="com.intellij.openapi.ui.TextFieldWithBrowseButton" binding="myModuleContentRoot">
+ <constraints>
+ <grid row="1" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
+ <gridbag top="0" left="0" bottom="5" right="0" weightx="1.0" weighty="1.0"/>
+ </constraints>
+ <properties/>
+ </component>
+ <component id="2ac4e" class="javax.swing.JLabel">
+ <constraints>
+ <grid row="2" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
+ <gridbag weightx="0.0" weighty="1.0"/>
+ </constraints>
+ <properties>
+ <labelFor value="9e1b8"/>
+ <text resource-bundle="messages/ProjectBundle" key="project.new.wizard.module.file.title"/>
+ </properties>
+ </component>
+ <component id="9e1b8" class="com.intellij.openapi.ui.TextFieldWithBrowseButton" binding="myModuleFileLocation">
+ <constraints>
+ <grid row="2" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
+ <gridbag top="0" left="0" bottom="5" right="0" weightx="1.0" weighty="1.0"/>
+ </constraints>
+ <properties/>
+ </component>
+ </children>
+ </grid>
+ <grid id="caf4" binding="mySettingsPanel" layout-manager="BorderLayout" hgap="0" vgap="0">
+ <constraints>
+ <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="7" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties/>
+ <border type="empty">
+ <size top="0" left="0" bottom="10" right="0"/>
+ </border>
+ <children/>
+ </grid>
+ <vspacer id="44e0b">
+ <constraints>
+ <grid row="2" column="0" row-span="1" col-span="1" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/>
+ </constraints>
+ </vspacer>
+ </children>
+ </grid>
+ <grid id="90483" binding="myLeftPanel" layout-manager="GridLayoutManager" row-count="2" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+ <margin top="0" left="0" bottom="0" right="0"/>
+ <constraints>
+ <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false">
+ <minimum-size width="200" height="-1"/>
+ <preferred-size width="178" height="161"/>
+ </grid>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <component id="3580d" class="com.intellij.ui.SearchTextField" binding="mySearchField" custom-create="true">
+ <constraints>
+ <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties/>
+ </component>
+ <scrollpane id="474de" class="com.intellij.ui.components.JBScrollPane">
+ <constraints>
+ <grid row="1" column="0" row-span="1" col-span="1" vsize-policy="7" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <component id="11306" class="com.intellij.ui.components.JBList" binding="myTemplatesList">
+ <constraints/>
+ <properties>
+ <selectionMode value="0"/>
+ </properties>
+ </component>
+ </children>
+ </scrollpane>
+ </children>
+ </grid>
+ </children>
+ </grid>
+</form>
diff --git a/java/idea-ui/src/com/intellij/ide/util/newProjectWizard/SelectTemplateStep.java b/java/idea-ui/src/com/intellij/ide/util/newProjectWizard/SelectTemplateStep.java
new file mode 100644
index 0000000..fbec518
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/ide/util/newProjectWizard/SelectTemplateStep.java
@@ -0,0 +1,559 @@
+/*
+ * Copyright 2000-2012 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.ide.util.newProjectWizard;
+
+import com.intellij.ide.IdeBundle;
+import com.intellij.ide.highlighter.ModuleFileType;
+import com.intellij.ide.util.BrowseFilesListener;
+import com.intellij.ide.util.projectWizard.*;
+import com.intellij.openapi.components.StorageScheme;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.module.ModuleManager;
+import com.intellij.openapi.options.ConfigurationException;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.project.ProjectBundle;
+import com.intellij.openapi.roots.ui.configuration.ProjectStructureConfigurable;
+import com.intellij.openapi.ui.Messages;
+import com.intellij.openapi.ui.TextFieldWithBrowseButton;
+import com.intellij.openapi.ui.ValidationInfo;
+import com.intellij.openapi.util.Comparing;
+import com.intellij.openapi.util.Disposer;
+import com.intellij.openapi.util.SystemInfo;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.platform.ProjectTemplate;
+import com.intellij.platform.templates.TemplateModuleBuilder;
+import com.intellij.projectImport.ProjectFormatPanel;
+import com.intellij.ui.*;
+import com.intellij.ui.components.JBLabel;
+import com.intellij.ui.components.JBList;
+import com.intellij.util.containers.MultiMap;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.jetbrains.annotations.TestOnly;
+
+import javax.swing.*;
+import javax.swing.event.DocumentEvent;
+import javax.swing.event.ListSelectionEvent;
+import javax.swing.event.ListSelectionListener;
+import java.awt.*;
+import java.io.File;
+
+/**
+ * @author Dmitry Avdeev
+ * Date: 9/26/12
+ */
+public class SelectTemplateStep extends ModuleWizardStep implements SettingsStep {
+
+ private JBList myTemplatesList;
+ private JPanel mySettingsPanel;
+ private SearchTextField mySearchField;
+ private JTextPane myDescriptionPane;
+ private JPanel myDescriptionPanel;
+
+ private JPanel myExpertPlaceholder;
+ private JPanel myExpertPanel;
+ private final HideableDecorator myExpertDecorator;
+
+ private final NamePathComponent myNamePathComponent;
+ private final ProjectFormatPanel myFormatPanel;
+
+ private JTextField myModuleName;
+ private TextFieldWithBrowseButton myModuleContentRoot;
+ private TextFieldWithBrowseButton myModuleFileLocation;
+ private JPanel myModulePanel;
+
+ private JPanel myLeftPanel;
+ private JPanel myRightPanel;
+ private final JBSplitter mySplitter;
+
+ private boolean myModuleNameChangedByUser = false;
+ private boolean myModuleNameDocListenerEnabled = true;
+
+ private boolean myContentRootChangedByUser = false;
+ private boolean myContentRootDocListenerEnabled = true;
+
+ private boolean myImlLocationChangedByUser = false;
+ private boolean myImlLocationDocListenerEnabled = true;
+
+ private final WizardContext myWizardContext;
+ private final StepSequence mySequence;
+ @Nullable
+ private ModuleWizardStep mySettingsStep;
+
+ private final ProjectTypesList myList;
+
+ @Nullable
+ private ModuleBuilder myModuleBuilder;
+
+ public SelectTemplateStep(WizardContext context, StepSequence sequence, final MultiMap<TemplatesGroup, ProjectTemplate> map) {
+
+ myWizardContext = context;
+ mySequence = sequence;
+ Messages.installHyperlinkSupport(myDescriptionPane);
+
+ myFormatPanel = new ProjectFormatPanel();
+ myNamePathComponent = initNamePathComponent(context);
+ if (context.isCreatingNewProject()) {
+ mySettingsPanel.add(myNamePathComponent, BorderLayout.NORTH);
+ addExpertPanel(myModulePanel);
+ }
+ else {
+ mySettingsPanel.add(myModulePanel, BorderLayout.NORTH);
+ }
+ bindModuleSettings();
+
+ myExpertDecorator = new HideableDecorator(myExpertPlaceholder, "Mor&e Settings", false);
+ myExpertPanel.setBorder(IdeBorderFactory.createEmptyBorder(0, IdeBorderFactory.TITLED_BORDER_INDENT, 5, 0));
+ myExpertDecorator.setContentComponent(myExpertPanel);
+
+ myList = new ProjectTypesList(myTemplatesList, mySearchField, map, context);
+
+ myTemplatesList.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
+
+ @Override
+ public void valueChanged(ListSelectionEvent e) {
+ ProjectTemplate template = getSelectedTemplate();
+ boolean loading = template instanceof LoadingProjectTemplate;
+ myModuleBuilder = template == null || loading ? null : template.createModuleBuilder();
+ setupPanels(template);
+ mySequence.setType(myModuleBuilder == null ? null : myModuleBuilder.getBuilderId());
+ myWizardContext.requestWizardButtonsUpdate();
+ }
+ });
+
+ if (myWizardContext.isCreatingNewProject()) {
+ addProjectFormat(myModulePanel);
+ }
+
+ mySplitter = new JBSplitter(false, 0.3f, 0.3f, 0.6f);
+ mySplitter.setSplitterProportionKey("select.template.proportion");
+ myLeftPanel.setMinimumSize(new Dimension(200, 200));
+ mySplitter.setFirstComponent(myLeftPanel);
+ mySplitter.setSecondComponent(myRightPanel);
+ }
+
+ private void addProjectFormat(JPanel panel) {
+ addField("Project \u001bformat:", myFormatPanel.getStorageFormatComboBox(), panel);
+ }
+
+ @Override
+ public void disposeUIResources() {
+ Disposer.dispose(myList);
+ }
+
+ @Override
+ public String getHelpId() {
+ return myWizardContext.isCreatingNewProject() ? "New_Project_Main_Settings" : "Add_Module_Main_Settings";
+ }
+
+ private static NamePathComponent initNamePathComponent(WizardContext context) {
+ NamePathComponent component = new NamePathComponent(
+ IdeBundle.message("label.project.name"),
+ IdeBundle.message("label.project.files.location"),
+ IdeBundle.message("title.select.project.file.directory", IdeBundle.message("project.new.wizard.project.identification")),
+ IdeBundle.message("description.select.project.file.directory", StringUtil
+ .capitalize(IdeBundle.message("project.new.wizard.project.identification"))),
+ true, false
+ );
+ final String baseDir = context.getProjectFileDirectory();
+ final String projectName = context.getProjectName();
+ final String initialProjectName = projectName != null ? projectName : ProjectWizardUtil.findNonExistingFileName(baseDir, "untitled", "");
+ component.setPath(projectName == null ? (baseDir + File.separator + initialProjectName) : baseDir);
+ component.setNameValue(initialProjectName);
+ component.getNameComponent().select(0, initialProjectName.length());
+ return component;
+ }
+
+ private void setupPanels(@Nullable ProjectTemplate template) {
+
+ restorePanel(myNamePathComponent, 4);
+ restorePanel(myModulePanel, myWizardContext.isCreatingNewProject() ? 8 : 6);
+ restorePanel(myExpertPanel, myWizardContext.isCreatingNewProject() ? 1 : 0);
+ mySettingsStep = myModuleBuilder == null ? null : myModuleBuilder.modifySettingsStep(this);
+
+ String description = null;
+ if (template != null) {
+ description = template.getDescription();
+ if (StringUtil.isNotEmpty(description)) {
+ StringBuilder sb = new StringBuilder("<html><body><font face=\"Verdana\" ");
+ sb.append(SystemInfo.isMac ? "" : "size=\"-1\"").append('>');
+ sb.append(description).append("</font></body></html>");
+ description = sb.toString();
+ myDescriptionPane.setText(description);
+ }
+ }
+
+ myExpertPlaceholder.setVisible(!(myModuleBuilder instanceof TemplateModuleBuilder) && myExpertPanel.getComponentCount() > 0);
+ for (int i = 0; i < 6; i++) {
+ myModulePanel.getComponent(i).setVisible(!(myModuleBuilder instanceof EmptyModuleBuilder));
+ }
+ myDescriptionPanel.setVisible(StringUtil.isNotEmpty(description));
+
+ mySettingsPanel.revalidate();
+ mySettingsPanel.repaint();
+ }
+
+ private static int restorePanel(JPanel component, int i) {
+ int removed = 0;
+ while (component.getComponentCount() > i) {
+ component.remove(component.getComponentCount() - 1);
+ removed++;
+ }
+ return removed;
+ }
+
+ @Override
+ public void updateStep() {
+ myList.resetSelection();
+ myExpertDecorator.setOn(SelectTemplateSettings.getInstance().EXPERT_MODE);
+ }
+
+ @Override
+ public void onStepLeaving() {
+ SelectTemplateSettings settings = SelectTemplateSettings.getInstance();
+ settings.EXPERT_MODE = myExpertDecorator.isExpanded();
+ myList.saveSelection();
+ }
+
+ @Override
+ public boolean validate() throws ConfigurationException {
+ ProjectTemplate template = getSelectedTemplate();
+ if (template == null) {
+ throw new ConfigurationException(StringUtil.capitalize(ProjectBundle.message("project.new.wizard.from.template.error", myWizardContext.getPresentationName())), "Error");
+ }
+
+ if (myWizardContext.isCreatingNewProject()) {
+ if (!myNamePathComponent.validateNameAndPath(myWizardContext, myFormatPanel.isDefault())) return false;
+ }
+
+ if (!validateModulePaths()) return false;
+ if (!myWizardContext.isCreatingNewProject()) {
+ validateExistingModuleName();
+ }
+
+ ValidationInfo info = template.validateSettings();
+ if (info != null) {
+ throw new ConfigurationException(info.message, "Error");
+ }
+ if (mySettingsStep != null) {
+ return mySettingsStep.validate();
+ }
+ return true;
+ }
+
+ @Nullable
+ public ProjectTemplate getSelectedTemplate() {
+ return myList.getSelectedTemplate();
+ }
+
+ @Override
+ public JComponent getComponent() {
+ return mySplitter;
+ }
+
+ @Override
+ public JComponent getPreferredFocusedComponent() {
+ return mySearchField;
+ }
+
+ @Override
+ public void updateDataModel() {
+
+ myWizardContext.setProjectBuilder(myModuleBuilder);
+ myWizardContext.setProjectName(myNamePathComponent.getNameValue());
+ myWizardContext.setProjectFileDirectory(myNamePathComponent.getPath());
+ myFormatPanel.updateData(myWizardContext);
+
+ if (myModuleBuilder != null) {
+ final String moduleName = getModuleName();
+ myModuleBuilder.setName(moduleName);
+ myModuleBuilder.setModuleFilePath(
+ FileUtil.toSystemIndependentName(myModuleFileLocation.getText()) + "/" + moduleName + ModuleFileType.DOT_DEFAULT_EXTENSION);
+ myModuleBuilder.setContentEntryPath(FileUtil.toSystemIndependentName(getModuleContentRoot()));
+ if (myModuleBuilder instanceof TemplateModuleBuilder) {
+ myWizardContext.setProjectStorageFormat(StorageScheme.DIRECTORY_BASED);
+ }
+ }
+
+ if (mySettingsStep != null) {
+ mySettingsStep.updateDataModel();
+ }
+ }
+
+ @Override
+ public String getName() {
+ return "Template Type";
+ }
+
+ private void createUIComponents() {
+ mySearchField = new SearchTextField(false);
+ }
+
+ @Override
+ public WizardContext getContext() {
+ return myWizardContext;
+ }
+
+ @Override
+ public void addSettingsField(@NotNull String label, @NotNull JComponent field) {
+
+ JPanel panel = myWizardContext.isCreatingNewProject() ? myNamePathComponent : myModulePanel;
+ addField(label, field, panel);
+ }
+
+ private static void addField(String label, JComponent field, JPanel panel) {
+ JLabel jLabel = new JBLabel(label);
+ jLabel.setLabelFor(field);
+ panel.add(jLabel, new GridBagConstraints(0, GridBagConstraints.RELATIVE, 1, 1, 0, 0, GridBagConstraints.WEST,
+ GridBagConstraints.NONE, new Insets(0, 0, 5, 0), 0, 0));
+ panel.add(field, new GridBagConstraints(1, GridBagConstraints.RELATIVE, 1, 1, 1.0, 0, GridBagConstraints.NORTHWEST,
+ GridBagConstraints.HORIZONTAL, new Insets(0, 0, 5, 0), 0, 0));
+ }
+
+ @Override
+ public void addSettingsComponent(@NotNull JComponent component) {
+ myNamePathComponent.add(component, new GridBagConstraints(0, GridBagConstraints.RELATIVE, 2, 1, 1.0, 0, GridBagConstraints.NORTHWEST,
+ GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0));
+ }
+
+ @Override
+ public void addExpertPanel(@NotNull JComponent panel) {
+ myExpertPanel.add(panel, new GridBagConstraints(0, GridBagConstraints.RELATIVE, 2, 1, 1.0, 0, GridBagConstraints.NORTHWEST,
+ GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0));
+ }
+
+ @Override
+ public void addExpertField(@NotNull String label, @NotNull JComponent field) {
+ JPanel panel = myWizardContext.isCreatingNewProject() ? myModulePanel : myExpertPanel;
+ addField(label, field, panel);
+ }
+
+ public void bindModuleSettings() {
+
+ myNamePathComponent.getNameComponent().getDocument().addDocumentListener(new DocumentAdapter() {
+ protected void textChanged(final DocumentEvent e) {
+ if (!myModuleNameChangedByUser) {
+ setModuleName(myNamePathComponent.getNameValue());
+ }
+ }
+ });
+
+ myModuleContentRoot.addBrowseFolderListener(ProjectBundle.message("project.new.wizard.module.content.root.chooser.title"), ProjectBundle.message("project.new.wizard.module.content.root.chooser.description"),
+ myWizardContext.getProject(), BrowseFilesListener.SINGLE_DIRECTORY_DESCRIPTOR);
+
+ myNamePathComponent.getPathComponent().getDocument().addDocumentListener(new DocumentAdapter() {
+ protected void textChanged(final DocumentEvent e) {
+ if (!myContentRootChangedByUser) {
+ setModuleContentRoot(myNamePathComponent.getPath());
+ }
+ }
+ });
+ myModuleName.getDocument().addDocumentListener(new DocumentAdapter() {
+ protected void textChanged(final DocumentEvent e) {
+ if (myModuleNameDocListenerEnabled) {
+ myModuleNameChangedByUser = true;
+ }
+ String path = getDefaultBaseDir(myWizardContext);
+ final String moduleName = getModuleName();
+ if (path.length() > 0 && !Comparing.strEqual(moduleName, myNamePathComponent.getNameValue())) {
+ path += "/" + moduleName;
+ }
+ if (!myContentRootChangedByUser) {
+ final boolean f = myModuleNameChangedByUser;
+ myModuleNameChangedByUser = true;
+ setModuleContentRoot(path);
+ myModuleNameChangedByUser = f;
+ }
+ if (!myImlLocationChangedByUser) {
+ setImlFileLocation(path);
+ }
+ }
+ });
+ myModuleContentRoot.getTextField().getDocument().addDocumentListener(new DocumentAdapter() {
+ protected void textChanged(final DocumentEvent e) {
+ if (myContentRootDocListenerEnabled) {
+ myContentRootChangedByUser = true;
+ }
+ if (!myImlLocationChangedByUser) {
+ setImlFileLocation(getModuleContentRoot());
+ }
+ if (!myModuleNameChangedByUser) {
+ final String path = FileUtil.toSystemIndependentName(getModuleContentRoot());
+ final int idx = path.lastIndexOf("/");
+
+ boolean f = myContentRootChangedByUser;
+ myContentRootChangedByUser = true;
+
+ boolean i = myImlLocationChangedByUser;
+ myImlLocationChangedByUser = true;
+
+ setModuleName(idx >= 0 ? path.substring(idx + 1) : "");
+
+ myContentRootChangedByUser = f;
+ myImlLocationChangedByUser = i;
+ }
+ }
+ });
+
+ myModuleFileLocation.addBrowseFolderListener(ProjectBundle.message("project.new.wizard.module.file.chooser.title"), ProjectBundle.message("project.new.wizard.module.file.description"),
+ myWizardContext.getProject(), BrowseFilesListener.SINGLE_DIRECTORY_DESCRIPTOR);
+ myModuleFileLocation.getTextField().getDocument().addDocumentListener(new DocumentAdapter() {
+ protected void textChanged(final DocumentEvent e) {
+ if (myImlLocationDocListenerEnabled) {
+ myImlLocationChangedByUser = true;
+ }
+ }
+ });
+ myNamePathComponent.getPathComponent().getDocument().addDocumentListener(new DocumentAdapter() {
+ protected void textChanged(final DocumentEvent e) {
+ if (!myImlLocationChangedByUser) {
+ setImlFileLocation(myNamePathComponent.getPath());
+ }
+ }
+ });
+ if (myWizardContext.isCreatingNewProject()) {
+ setModuleName(myNamePathComponent.getNameValue());
+ setModuleContentRoot(myNamePathComponent.getPath());
+ setImlFileLocation(myNamePathComponent.getPath());
+ } else {
+ final Project project = myWizardContext.getProject();
+ assert project != null;
+ VirtualFile baseDir = project.getBaseDir();
+ if (baseDir != null) { //e.g. was deleted
+ final String baseDirPath = baseDir.getPath();
+ String moduleName = ProjectWizardUtil.findNonExistingFileName(baseDirPath, "untitled", "");
+ String contentRoot = baseDirPath + "/" + moduleName;
+ if (!Comparing.strEqual(project.getName(), myWizardContext.getProjectName()) && !myWizardContext.isCreatingNewProject() && myWizardContext.getProjectName() != null) {
+ moduleName = ProjectWizardUtil.findNonExistingFileName(myWizardContext.getProjectFileDirectory(), myWizardContext.getProjectName(), "");
+ contentRoot = myWizardContext.getProjectFileDirectory();
+ }
+ setModuleName(moduleName);
+ setModuleContentRoot(contentRoot);
+ setImlFileLocation(contentRoot);
+ myModuleName.select(0, moduleName.length());
+ }
+ }
+ }
+
+ private void validateExistingModuleName() throws ConfigurationException {
+ final String moduleName = getModuleName();
+ final Module module;
+ final ProjectStructureConfigurable fromConfigurable = ProjectStructureConfigurable.getInstance(myWizardContext.getProject());
+ if (fromConfigurable != null) {
+ module = fromConfigurable.getModulesConfig().getModule(moduleName);
+ }
+ else {
+ module = ModuleManager.getInstance(myWizardContext.getProject()).findModuleByName(moduleName);
+ }
+ if (module != null) {
+ throw new ConfigurationException("Module \'" + moduleName + "\' already exist in project. Please, specify another name.");
+ }
+ }
+
+ private boolean validateModulePaths() throws ConfigurationException {
+ final String moduleName = getModuleName();
+ final String moduleFileDirectory = myModuleFileLocation.getText();
+ if (moduleFileDirectory.length() == 0) {
+ throw new ConfigurationException("Enter module file location");
+ }
+ if (moduleName.length() == 0) {
+ throw new ConfigurationException("Enter a module name");
+ }
+
+ if (!ProjectWizardUtil.createDirectoryIfNotExists(IdeBundle.message("directory.module.file"), moduleFileDirectory,
+ myImlLocationChangedByUser)) {
+ return false;
+ }
+ if (!ProjectWizardUtil.createDirectoryIfNotExists(IdeBundle.message("directory.module.content.root"), myModuleContentRoot.getText(),
+ myContentRootChangedByUser)) {
+ return false;
+ }
+
+ File moduleFile = new File(moduleFileDirectory, moduleName + ModuleFileType.DOT_DEFAULT_EXTENSION);
+ if (moduleFile.exists()) {
+ int answer = Messages.showYesNoDialog(IdeBundle.message("prompt.overwrite.project.file", moduleFile.getAbsolutePath(),
+ IdeBundle.message("project.new.wizard.module.identification")),
+ IdeBundle.message("title.file.already.exists"), Messages.getQuestionIcon());
+ if (answer != 0) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ protected String getModuleContentRoot() {
+ return myModuleContentRoot.getText();
+ }
+
+ private String getDefaultBaseDir(WizardContext wizardContext) {
+ if (wizardContext.isCreatingNewProject()) {
+ return myNamePathComponent.getPath();
+ } else {
+ final Project project = wizardContext.getProject();
+ assert project != null;
+ final VirtualFile baseDir = project.getBaseDir();
+ if (baseDir != null) {
+ return baseDir.getPath();
+ }
+ return "";
+ }
+ }
+
+ private void setImlFileLocation(final String path) {
+ myImlLocationDocListenerEnabled = false;
+ myModuleFileLocation.setText(FileUtil.toSystemDependentName(path));
+ myImlLocationDocListenerEnabled = true;
+ }
+
+ private void setModuleContentRoot(final String path) {
+ myContentRootDocListenerEnabled = false;
+ myModuleContentRoot.setText(FileUtil.toSystemDependentName(path));
+ myContentRootDocListenerEnabled = true;
+ }
+
+ public void setModuleName(String moduleName) {
+ myModuleNameDocListenerEnabled = false;
+ myModuleName.setText(moduleName);
+ myModuleNameDocListenerEnabled = true;
+ }
+
+ @NotNull
+ public JTextField getModuleNameField() {
+ return myModuleName;
+ }
+
+ protected String getModuleName() {
+ return myModuleName.getText().trim();
+ }
+
+ @TestOnly
+ public boolean setSelectedTemplate(String group, String name) {
+ return myList.setSelectedTemplate(group, name);
+ }
+
+ @TestOnly
+ @Nullable
+ public ModuleWizardStep getSettingsStep() {
+ return mySettingsStep;
+ }
+
+ @Override
+ public Icon getIcon() {
+ return myWizardContext.getStepIcon();
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/ide/util/newProjectWizard/SourcePathsStep.java b/java/idea-ui/src/com/intellij/ide/util/newProjectWizard/SourcePathsStep.java
new file mode 100644
index 0000000..13ad375
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/ide/util/newProjectWizard/SourcePathsStep.java
@@ -0,0 +1,408 @@
+/*
+ * Copyright 2000-2012 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.ide.util.newProjectWizard;
+
+import com.intellij.CommonBundle;
+import com.intellij.ide.IdeBundle;
+import com.intellij.ide.util.BrowseFilesListener;
+import com.intellij.ide.util.ElementsChooser;
+import com.intellij.ide.util.projectWizard.AbstractStepWithProgress;
+import com.intellij.ide.util.projectWizard.SourcePathsBuilder;
+import com.intellij.ide.util.projectWizard.importSources.JavaModuleSourceRoot;
+import com.intellij.ide.util.projectWizard.importSources.JavaSourceRootDetectionUtil;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.application.ApplicationNamesInfo;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.fileChooser.FileChooserDescriptor;
+import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory;
+import com.intellij.openapi.options.ConfigurationException;
+import com.intellij.openapi.ui.Messages;
+import com.intellij.openapi.ui.MultiLineLabelUI;
+import com.intellij.openapi.util.Computable;
+import com.intellij.openapi.util.Pair;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.vfs.LocalFileSystem;
+import com.intellij.openapi.vfs.VfsUtil;
+import com.intellij.openapi.vfs.VfsUtilCore;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.ui.DocumentAdapter;
+import com.intellij.ui.FieldPanel;
+import com.intellij.util.StringBuilderSpinAllocator;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import javax.swing.event.DocumentEvent;
+import java.awt.*;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.ItemEvent;
+import java.awt.event.ItemListener;
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * @author Eugene Zhuravlev
+ * Date: Jan 6, 2004
+ */
+public class SourcePathsStep extends AbstractStepWithProgress<List<JavaModuleSourceRoot>> {
+
+ private static final Logger LOG = Logger.getInstance("#com.intellij.ide.util.newProjectWizard.SourcePathsStep");
+
+ private String myCurrentMode;
+ @NonNls private static final String CREATE_SOURCE_PANEL = "create_source";
+ @NonNls private static final String CHOOSE_SOURCE_PANEL = "choose_source";
+
+ private final SourcePathsBuilder myBuilder;
+ private final Icon myIcon;
+ private final String myHelpId;
+ private ElementsChooser<JavaModuleSourceRoot> mySourcePathsChooser;
+ private String myCurrentContentEntryPath = null;
+ private JRadioButton myRbCreateSource;
+ private JRadioButton myRbNoSource;
+ private JTextField myTfSourceDirectoryName;
+ private JTextField myTfFullPath;
+ private JPanel myResultPanel;
+
+ public SourcePathsStep(SourcePathsBuilder builder, Icon icon, @NonNls String helpId) {
+ super(IdeBundle.message("prompt.stop.searching.for.sources", ApplicationNamesInfo.getInstance().getProductName()));
+ myBuilder = builder;
+ myIcon = icon;
+ myHelpId = helpId;
+ }
+
+ protected JComponent createResultsPanel() {
+ myResultPanel = new JPanel(new CardLayout());
+ myResultPanel.add(createComponentForEmptyRootCase(), CREATE_SOURCE_PANEL);
+ myResultPanel.add(createComponentForChooseSources(), CHOOSE_SOURCE_PANEL);
+ return myResultPanel;
+ }
+
+ private JComponent createComponentForEmptyRootCase() {
+ final JPanel panel = new JPanel(new GridBagLayout());
+ final String text = IdeBundle.message("prompt.please.specify.java.sources.directory");
+
+ final JLabel label = new JLabel(text);
+ label.setUI(new MultiLineLabelUI());
+ panel.add(label, new GridBagConstraints(0, GridBagConstraints.RELATIVE, 1, 1, 1.0, 0.0, GridBagConstraints.NORTHWEST, GridBagConstraints.NONE, new Insets(8, 10, 0, 10), 0, 0));
+
+ myRbCreateSource = new JRadioButton(IdeBundle.message("radio.create.source.directory"), true);
+ panel.add(myRbCreateSource, new GridBagConstraints(0, GridBagConstraints.RELATIVE, 1, 1, 1.0, 0.0, GridBagConstraints.NORTHWEST, GridBagConstraints.NONE, new Insets(8, 10, 0, 10), 0, 0));
+
+ myTfSourceDirectoryName = new JTextField(suggestSourceDirectoryName());
+ final JLabel srcPathLabel = new JLabel(IdeBundle.message("prompt.enter.relative.path.to.module.content.root", File.separator));
+ panel.add(srcPathLabel, new GridBagConstraints(0, GridBagConstraints.RELATIVE, 1, 1, 1.0, 0.0, GridBagConstraints.NORTHWEST, GridBagConstraints.HORIZONTAL, new Insets(8, 30, 0, 0), 0, 0));
+ final FileChooserDescriptor chooserDescriptor = FileChooserDescriptorFactory.createSingleFolderDescriptor();
+ chooserDescriptor.setIsTreeRootVisible(true);
+ final FieldPanel fieldPanel = createFieldPanel(myTfSourceDirectoryName, null, new BrowsePathListener(myTfSourceDirectoryName, chooserDescriptor));
+ panel.add(fieldPanel, new GridBagConstraints(0, GridBagConstraints.RELATIVE, 1, 1, 1.0, 0.0, GridBagConstraints.NORTHWEST, GridBagConstraints.HORIZONTAL, new Insets(8, 30, 0, 10), 0, 0));
+
+ myRbNoSource = new JRadioButton(IdeBundle.message("radio.do.not.create.source.directory"), true);
+ panel.add(myRbNoSource, new GridBagConstraints(0, GridBagConstraints.RELATIVE, 1, 1, 1.0, 1.0, GridBagConstraints.NORTHWEST, GridBagConstraints.NONE, new Insets(8, 10, 0, 10), 0, 0));
+
+ final JLabel fullPathLabel = new JLabel(IdeBundle.message("label.source.directory"));
+ panel.add(fullPathLabel, new GridBagConstraints(0, GridBagConstraints.RELATIVE, 1, 1, 1.0, 0.0, GridBagConstraints.SOUTHWEST, GridBagConstraints.NONE, new Insets(8, 10, 0, 10), 0, 0));
+
+ myTfFullPath = new JTextField();
+ myTfFullPath.setEditable(false);
+ myTfFullPath.setOpaque(false);
+ final Insets borderInsets = myTfFullPath.getBorder().getBorderInsets(myTfFullPath);
+ myTfFullPath.setBorder(BorderFactory.createEmptyBorder(borderInsets.top, borderInsets.left, borderInsets.bottom, borderInsets.right));
+ panel.add(myTfFullPath, new GridBagConstraints(0, GridBagConstraints.RELATIVE, 1, 1, 1.0, 0.0, GridBagConstraints.SOUTHWEST, GridBagConstraints.HORIZONTAL, new Insets(8, 10, 8, 10), 0, 0));
+
+ ButtonGroup group = new ButtonGroup();
+ group.add(myRbCreateSource);
+ group.add(myRbNoSource);
+ myTfSourceDirectoryName.getDocument().addDocumentListener(new DocumentAdapter() {
+ public void textChanged(DocumentEvent event) {
+ updateFullPathField();
+ }
+ });
+
+ myRbCreateSource.addItemListener(new ItemListener() {
+ public void itemStateChanged(ItemEvent e) {
+ final boolean enabled = e.getStateChange() == ItemEvent.SELECTED;
+ srcPathLabel.setEnabled(enabled);
+ fieldPanel.setEnabled(enabled);
+ fullPathLabel.setVisible(enabled);
+ myTfFullPath.setVisible(enabled);
+ if (enabled) {
+ myTfSourceDirectoryName.requestFocus();
+ }
+ }
+ });
+ return panel;
+ }
+
+ @NonNls protected String suggestSourceDirectoryName() {
+ return "src";
+ }
+
+ private void updateFullPathField() {
+ final String sourceDirectoryPath = getSourceDirectoryPath();
+ if (sourceDirectoryPath != null) {
+ myTfFullPath.setText(sourceDirectoryPath.replace('/', File.separatorChar));
+ }
+ else {
+ myTfFullPath.setText("");
+ }
+ }
+
+ private JComponent createComponentForChooseSources() {
+ final JPanel panel = new JPanel(new GridBagLayout());
+ mySourcePathsChooser = new ElementsChooser<JavaModuleSourceRoot>(true) {
+ public String getItemText(@NotNull JavaModuleSourceRoot sourceRoot) {
+ StringBuilder builder = StringBuilderSpinAllocator.alloc();
+ try {
+ builder.append(sourceRoot.getDirectory().getAbsolutePath());
+ final String packagePrefix = sourceRoot.getPackagePrefix();
+ if (!packagePrefix.isEmpty()) {
+ builder.append(" (").append(packagePrefix).append(")");
+ }
+ builder.append(" [").append(sourceRoot.getRootTypeName()).append("]");
+ return builder.toString();
+ }
+ finally {
+ StringBuilderSpinAllocator.dispose(builder);
+ }
+ }
+ };
+ final String text = IdeBundle.message("label.java.source.files.have.been.found");
+ final JLabel label = new JLabel(text);
+ label.setUI(new MultiLineLabelUI());
+ panel.add(label, new GridBagConstraints(0, GridBagConstraints.RELATIVE, 2, 1, 1.0, 0.0, GridBagConstraints.NORTHWEST, GridBagConstraints.HORIZONTAL, new Insets(8, 10, 0, 10), 0, 0));
+ panel.add(mySourcePathsChooser, new GridBagConstraints(0, GridBagConstraints.RELATIVE, 2, 1, 1.0, 1.0, GridBagConstraints.NORTHWEST, GridBagConstraints.BOTH, new Insets(8, 10, 8, 10), 0, 0));
+
+ final JButton markAllButton = new JButton(IdeBundle.message("button.mark.all"));
+ panel.add(markAllButton, new GridBagConstraints(0, GridBagConstraints.RELATIVE, 1, 1, 0.0, 0.0, GridBagConstraints.NORTHWEST, GridBagConstraints.NONE, new Insets(0, 10, 8, 2), 0, 0));
+
+ final JButton unmarkAllButton = new JButton(IdeBundle.message("button.unmark.all"));
+ panel.add(unmarkAllButton, new GridBagConstraints(1, GridBagConstraints.RELATIVE, 1, 1, 1.0, 0.0, GridBagConstraints.NORTHWEST, GridBagConstraints.NONE, new Insets(0, 0, 8, 10), 0, 0));
+
+ markAllButton.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ mySourcePathsChooser.setAllElementsMarked(true);
+ }
+ });
+ unmarkAllButton.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ mySourcePathsChooser.setAllElementsMarked(false);
+ }
+ });
+
+ return panel;
+ }
+
+ public JComponent getPreferredFocusedComponent() {
+ return myRbCreateSource.isSelected()? myTfSourceDirectoryName : mySourcePathsChooser.getComponent();
+ }
+
+ public void updateDataModel() {
+ List<Pair<String,String>> paths = null;
+ if (CHOOSE_SOURCE_PANEL.equals(myCurrentMode)) {
+ final List<JavaModuleSourceRoot> selectedElements = mySourcePathsChooser.getMarkedElements();
+ if (selectedElements.size() > 0) {
+ paths = new ArrayList<Pair<String, String>>(selectedElements.size());
+
+ for (final JavaModuleSourceRoot root : selectedElements) {
+ paths.add(Pair.create(FileUtil.toSystemIndependentName(root.getDirectory().getAbsolutePath()), root.getPackagePrefix()));
+ }
+ }
+ }
+ else {
+ if (myRbCreateSource.isSelected()) {
+ final String sourceDirectoryPath = getSourceDirectoryPath();
+ if (sourceDirectoryPath != null) {
+ paths = Collections.singletonList(Pair.create(sourceDirectoryPath, ""));
+ }
+ }
+ }
+ myBuilder.setContentEntryPath(getContentRootPath());
+ if (paths != null) {
+ myBuilder.setSourcePaths(paths);
+ }
+ else {
+ myBuilder.setSourcePaths(new ArrayList<Pair<String, String>>());
+ }
+ }
+
+ public boolean validate() throws ConfigurationException {
+ if (!super.validate()) {
+ return false;
+ }
+
+ if (CREATE_SOURCE_PANEL.equals(myCurrentMode) && myRbCreateSource.isSelected()) {
+ final String sourceDirectoryPath = getSourceDirectoryPath();
+ final String relativePath = myTfSourceDirectoryName.getText().trim();
+ if (relativePath.length() == 0) {
+ String text = IdeBundle.message("prompt.relative.path.to.sources.empty", FileUtil.toSystemDependentName(sourceDirectoryPath));
+ final int answer = Messages.showYesNoCancelDialog(myTfSourceDirectoryName, text, IdeBundle.message("title.mark.source.directory"),
+ IdeBundle.message("action.mark"), IdeBundle.message("action.do.not.mark"),
+ CommonBundle.getCancelButtonText(), Messages.getQuestionIcon());
+ if (answer == 2) {
+ return false; // cancel
+ }
+ if (answer == 1) { // don't mark
+ myRbNoSource.doClick();
+ }
+ }
+ if (sourceDirectoryPath != null) {
+ final File rootDir = new File(getContentRootPath());
+ final File srcDir = new File(sourceDirectoryPath);
+ if (!FileUtil.isAncestor(rootDir, srcDir, false)) {
+ Messages.showErrorDialog(myTfSourceDirectoryName,
+ IdeBundle.message("error.source.directory.should.be.under.module.content.root.directory"),
+ CommonBundle.getErrorTitle());
+ return false;
+ }
+ try {
+ VfsUtil.createDirectories(srcDir.getPath());
+ }
+ catch (IOException e) {
+ throw new ConfigurationException(e.getMessage());
+ }
+ }
+ }
+ return true;
+ }
+
+ @Nullable
+ private String getSourceDirectoryPath() {
+ final String contentEntryPath = getContentRootPath();
+ if (contentEntryPath != null) {
+ final String dirName = myTfSourceDirectoryName.getText().trim().replace(File.separatorChar, '/');
+ return dirName.length() > 0? contentEntryPath + "/" + dirName : contentEntryPath;
+ }
+ return null;
+ }
+
+ protected boolean shouldRunProgress() {
+ return isContentEntryChanged();
+ }
+
+ protected void onFinished(final List<JavaModuleSourceRoot> foundPaths, final boolean canceled) {
+ if (foundPaths.size() > 0) {
+ myCurrentMode = CHOOSE_SOURCE_PANEL;
+ mySourcePathsChooser.setElements(foundPaths, true);
+ }
+ else {
+ myCurrentMode = CREATE_SOURCE_PANEL;
+ updateFullPathField();
+ }
+ updateStepUI(canceled ? null : getContentRootPath());
+ if (CHOOSE_SOURCE_PANEL.equals(myCurrentMode)) {
+ mySourcePathsChooser.selectElements(foundPaths.subList(0, 1));
+ }
+ else if (CREATE_SOURCE_PANEL.equals(myCurrentMode)) {
+ myTfSourceDirectoryName.selectAll();
+ }
+ }
+
+ private void updateStepUI(final String contentEntryPath) {
+ myCurrentContentEntryPath = contentEntryPath;
+ ((CardLayout)myResultPanel.getLayout()).show(myResultPanel, myCurrentMode);
+ myResultPanel.revalidate();
+ }
+
+ protected boolean isContentEntryChanged() {
+ final String contentEntryPath = getContentRootPath();
+ return myCurrentContentEntryPath == null? contentEntryPath != null : !myCurrentContentEntryPath.equals(contentEntryPath);
+ }
+
+ protected List<JavaModuleSourceRoot> calculate() {
+ return new ArrayList<JavaModuleSourceRoot>(calculateSourceRoots(getContentRootPath()));
+ }
+
+ @NotNull
+ public static Collection<JavaModuleSourceRoot> calculateSourceRoots(final String contentRootPath) {
+ if (contentRootPath == null) {
+ return Collections.emptyList();
+ }
+ return JavaSourceRootDetectionUtil.suggestRoots(new File(contentRootPath));
+ }
+
+ @Nullable
+ private String getContentRootPath() {
+ return myBuilder.getContentEntryPath();
+ }
+
+ protected void setSourceDirectoryName(String name) {
+ name = name == null? "" : name.trim();
+ myTfSourceDirectoryName.setText(name);
+ }
+
+ protected String getProgressText() {
+ final String root = getContentRootPath();
+ return IdeBundle.message("progress.searching.for.sources", root != null? root.replace('/', File.separatorChar) : "") ;
+ }
+
+ private class BrowsePathListener extends BrowseFilesListener {
+ private final FileChooserDescriptor myChooserDescriptor;
+ private final JTextField myField;
+
+ public BrowsePathListener(JTextField textField, final FileChooserDescriptor chooserDescriptor) {
+ super(textField, IdeBundle.message("prompt.select.source.directory"), "", chooserDescriptor);
+ myChooserDescriptor = chooserDescriptor;
+ myField = textField;
+ }
+
+ @Nullable
+ private VirtualFile getContentEntryDir() {
+ final String contentEntryPath = getContentRootPath();
+ if (contentEntryPath != null) {
+ return ApplicationManager.getApplication().runWriteAction(new Computable<VirtualFile>() {
+ public VirtualFile compute() {
+ return LocalFileSystem.getInstance().refreshAndFindFileByPath(contentEntryPath);
+ }
+ });
+ }
+ return null;
+ }
+
+ public void actionPerformed(ActionEvent e) {
+ final VirtualFile contentEntryDir = getContentEntryDir();
+ if (contentEntryDir != null) {
+ myChooserDescriptor.setRoots(contentEntryDir);
+ final String textBefore = myField.getText().trim();
+ super.actionPerformed(e);
+ if (!textBefore.equals(myField.getText().trim())) {
+ final String fullPath = myField.getText().trim().replace(File.separatorChar, '/');
+ final VirtualFile fileByPath = LocalFileSystem.getInstance().findFileByPath(fullPath);
+ LOG.assertTrue(fileByPath != null);
+ myField.setText(VfsUtilCore.getRelativePath(fileByPath, contentEntryDir, File.separatorChar));
+ }
+ }
+ }
+ }
+
+ public Icon getIcon() {
+ return myIcon;
+ }
+
+ public String getHelpId() {
+ return myHelpId;
+ }
+
+ @Override
+ public String getName() {
+ return "Path to Sources";
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/ide/util/newProjectWizard/SourceRootFinder.java b/java/idea-ui/src/com/intellij/ide/util/newProjectWizard/SourceRootFinder.java
new file mode 100644
index 0000000..c0a340e
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/ide/util/newProjectWizard/SourceRootFinder.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2000-2010 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.ide.util.newProjectWizard;
+
+import com.intellij.openapi.extensions.ExtensionPointName;
+import com.intellij.openapi.util.Pair;
+
+import java.io.File;
+import java.util.List;
+
+/**
+ * @author Maxim.Medvedev
+ */
+
+/**
+ * @deprecated use {@link com.intellij.ide.util.projectWizard.importSources.JavaSourceRootDetector} instead
+ */
+@Deprecated
+public interface SourceRootFinder {
+ ExtensionPointName<SourceRootFinder> EP_NAME = ExtensionPointName.create("com.intellij.sourceRootFinder");
+
+ List<Pair<File, String>> findRoots(File dir);
+
+ String getDescription();
+
+ String getName();
+}
diff --git a/java/idea-ui/src/com/intellij/ide/util/newProjectWizard/StepSequence.java b/java/idea-ui/src/com/intellij/ide/util/newProjectWizard/StepSequence.java
new file mode 100644
index 0000000..4d7f47a
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/ide/util/newProjectWizard/StepSequence.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright 2000-2009 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.
+ */
+
+/*
+ * User: anna
+ * Date: 08-Jul-2007
+ */
+package com.intellij.ide.util.newProjectWizard;
+
+import com.intellij.ide.util.projectWizard.ModuleBuilder;
+import com.intellij.ide.util.projectWizard.ModuleWizardStep;
+import com.intellij.ide.util.projectWizard.WizardContext;
+import com.intellij.openapi.module.ModuleType;
+import com.intellij.openapi.roots.ui.configuration.ModulesProvider;
+import com.intellij.util.containers.ContainerUtil;
+import com.intellij.util.containers.MultiMap;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.*;
+
+public class StepSequence {
+ private final List<ModuleWizardStep> myCommonSteps = new ArrayList<ModuleWizardStep>();
+ private final List<ModuleWizardStep> myCommonFinishingSteps = new ArrayList<ModuleWizardStep>();
+ private final MultiMap<String, ModuleWizardStep> mySpecificSteps = new MultiMap<String, ModuleWizardStep>();
+ @NonNls private List<String> myTypes = new ArrayList<String>();
+ private List<ModuleWizardStep> mySelectedSteps;
+
+ public StepSequence(ModuleWizardStep... commonSteps) {
+ myCommonSteps.addAll(Arrays.asList(commonSteps));
+ }
+
+ public void addCommonStep(@NotNull ModuleWizardStep step){
+ myCommonSteps.add(step);
+ }
+
+ public void addCommonFinishingStep(@NotNull ModuleWizardStep step) {
+ myCommonFinishingSteps.add(step);
+ }
+
+ public void addStepsForBuilder(ModuleBuilder builder, WizardContext wizardContext, ModulesProvider modulesProvider) {
+ String id = builder.getBuilderId();
+ if (!mySpecificSteps.containsKey(id)) {
+ mySpecificSteps.put(id, Arrays.asList(builder.createWizardSteps(wizardContext, modulesProvider)));
+ }
+ }
+
+ public void addSpecificStep(String type, ModuleWizardStep step) {
+ mySpecificSteps.putValue(type, step);
+ }
+
+ public List<ModuleWizardStep> getSelectedSteps() {
+ if (mySelectedSteps == null) {
+ mySelectedSteps = new ArrayList<ModuleWizardStep>();
+ mySelectedSteps.addAll(myCommonSteps);
+ for (String type : myTypes) {
+ Collection<ModuleWizardStep> steps = mySpecificSteps.get(type);
+ mySelectedSteps.addAll(steps);
+ }
+ mySelectedSteps.addAll(myCommonFinishingSteps);
+ ContainerUtil.removeDuplicates(mySelectedSteps);
+ }
+
+ return mySelectedSteps;
+ }
+
+ @Nullable
+ public ModuleWizardStep getNextStep(ModuleWizardStep step) {
+ final List<ModuleWizardStep> steps = getSelectedSteps();
+ final int i = steps.indexOf(step);
+ return i < steps.size() - 1 ? steps.get(i + 1) : null;
+ }
+
+ @Nullable
+ public ModuleWizardStep getPreviousStep(ModuleWizardStep step) {
+ final List<ModuleWizardStep> steps = getSelectedSteps();
+ final int i = steps.indexOf(step);
+ return i > 0 ? steps.get(i - 1) : null;
+ }
+
+ public void setTypes(Collection<String> types) {
+ myTypes.clear();
+ myTypes.addAll(types);
+ mySelectedSteps = null;
+ }
+
+ public void setType(@Nullable @NonNls final String type) {
+ setTypes(Collections.singletonList(type == null ? ModuleType.EMPTY.getId() : type));
+ }
+
+ public String getSelectedType() {
+ return ContainerUtil.getFirstItem(myTypes);
+ }
+
+ public List<ModuleWizardStep> getAllSteps() {
+ final List<ModuleWizardStep> result = new ArrayList<ModuleWizardStep>();
+ result.addAll(myCommonSteps);
+ result.addAll(mySpecificSteps.values());
+ result.addAll(myCommonFinishingSteps);
+ ContainerUtil.removeDuplicates(result);
+ return result;
+ }
+
+ public ModuleWizardStep getFirstStep() {
+ return myCommonSteps.get(0);
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/ide/util/newProjectWizard/SupportForFrameworksStep.java b/java/idea-ui/src/com/intellij/ide/util/newProjectWizard/SupportForFrameworksStep.java
new file mode 100644
index 0000000..84292da
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/ide/util/newProjectWizard/SupportForFrameworksStep.java
@@ -0,0 +1,150 @@
+
+package com.intellij.ide.util.newProjectWizard;
+
+import com.intellij.CommonBundle;
+import com.intellij.framework.addSupport.FrameworkSupportInModuleProvider;
+import com.intellij.ide.util.frameworkSupport.FrameworkSupportUtil;
+import com.intellij.ide.util.newProjectWizard.impl.FrameworkSupportModelBase;
+import com.intellij.ide.util.projectWizard.ModuleBuilder;
+import com.intellij.ide.util.projectWizard.ModuleWizardStep;
+import com.intellij.ide.util.projectWizard.ProjectBuilder;
+import com.intellij.ide.util.projectWizard.WizardContext;
+import com.intellij.ide.wizard.CommitStepException;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.project.ProjectBundle;
+import com.intellij.openapi.roots.ModifiableRootModel;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.LibrariesContainer;
+import com.intellij.openapi.ui.Messages;
+import com.intellij.openapi.util.Disposer;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.util.ui.tree.TreeUtil;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.TestOnly;
+
+import javax.swing.*;
+import javax.swing.tree.TreeNode;
+import java.util.List;
+
+/**
+ * @author nik
+ */
+public class SupportForFrameworksStep extends ModuleWizardStep {
+ private final AddSupportForFrameworksPanel mySupportForFrameworksPanel;
+ private final FrameworkSupportModelBase myFrameworkSupportModel;
+ private final WizardContext myContext;
+ private final ModuleBuilder myBuilder;
+ private final ModuleBuilder.ModuleConfigurationUpdater myConfigurationUpdater;
+ private boolean myCommitted;
+
+ public SupportForFrameworksStep(WizardContext context, final ModuleBuilder builder,
+ @NotNull LibrariesContainer librariesContainer) {
+ myContext = context;
+ myBuilder = builder;
+ List<FrameworkSupportInModuleProvider> providers = FrameworkSupportUtil.getProviders(builder);
+ myFrameworkSupportModel = new FrameworkSupportModelInWizard(librariesContainer, builder);
+ mySupportForFrameworksPanel = new AddSupportForFrameworksPanel(providers, myFrameworkSupportModel);
+ myConfigurationUpdater = new ModuleBuilder.ModuleConfigurationUpdater() {
+ public void update(@NotNull final Module module, @NotNull final ModifiableRootModel rootModel) {
+ mySupportForFrameworksPanel.addSupport(module, rootModel);
+ }
+ };
+ builder.addModuleConfigurationUpdater(myConfigurationUpdater);
+ }
+
+ private static String getBaseDirectory(final ModuleBuilder builder) {
+ final String path = builder.getContentEntryPath();
+ return path != null ? FileUtil.toSystemIndependentName(path) : "";
+ }
+
+ public Icon getIcon() {
+ return ICON;
+ }
+
+ @Override
+ public void disposeUIResources() {
+ Disposer.dispose(mySupportForFrameworksPanel);
+ super.disposeUIResources();
+ }
+
+ @NonNls
+ public String getHelpId() {
+ return "reference.dialogs.new.project.technologies";
+ }
+
+ public void _commit(final boolean finishChosen) throws CommitStepException {
+ if (finishChosen && !myCommitted) {
+ boolean ok = mySupportForFrameworksPanel.downloadLibraries();
+ if (!ok) {
+ int answer = Messages.showYesNoDialog(getComponent(),
+ ProjectBundle.message("warning.message.some.required.libraries.wasn.t.downloaded"),
+ CommonBundle.getWarningTitle(), Messages.getWarningIcon());
+ if (answer != 0) {
+ throw new CommitStepException(null);
+ }
+ }
+ myCommitted = true;
+ }
+ }
+
+ public JComponent getComponent() {
+ return mySupportForFrameworksPanel.getMainPanel();
+ }
+
+ @Override
+ public JComponent getPreferredFocusedComponent() {
+ return mySupportForFrameworksPanel.getFrameworksTree();
+ }
+
+ @Override
+ public void updateStep() {
+ ProjectBuilder builder = myContext.getProjectBuilder();
+ if (builder instanceof ModuleBuilder) {
+ myBuilder.updateFrom((ModuleBuilder)builder);
+ ((ModuleBuilder)builder).addModuleConfigurationUpdater(myConfigurationUpdater);
+ }
+ myFrameworkSupportModel.fireWizardStepUpdated();
+ }
+
+ public void updateDataModel() {
+ }
+
+ @Override
+ public String getName() {
+ return "Add Frameworks";
+ }
+
+ private static class FrameworkSupportModelInWizard extends FrameworkSupportModelBase {
+ private final ModuleBuilder myBuilder;
+
+ public FrameworkSupportModelInWizard(LibrariesContainer librariesContainer, ModuleBuilder builder) {
+ super(librariesContainer.getProject(), builder, librariesContainer);
+ myBuilder = builder;
+ }
+
+ @NotNull
+ @Override
+ public String getBaseDirectoryForLibrariesPath() {
+ return getBaseDirectory(myBuilder);
+ }
+ }
+
+ @TestOnly
+ public boolean enableSupport(final String frameworkId) {
+ return !TreeUtil.traverse((TreeNode)mySupportForFrameworksPanel.getFrameworksTree().getModel().getRoot(), new TreeUtil.Traverse() {
+ @Override
+ public boolean accept(Object node) {
+ if (node instanceof FrameworkSupportNode && frameworkId.equals(
+ ((FrameworkSupportNode)node).getProvider().getFrameworkType().getId())) {
+ TreeNode parent = ((FrameworkSupportNode)node).getParent();
+ if (parent instanceof FrameworkSupportNode) {
+ ((FrameworkSupportNode)parent).setChecked(true);
+ }
+ ((FrameworkSupportNode)node).setChecked(true);
+ return false;
+ }
+ return true;
+ }
+ });
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/ide/util/newProjectWizard/TemplatesGroup.java b/java/idea-ui/src/com/intellij/ide/util/newProjectWizard/TemplatesGroup.java
new file mode 100644
index 0000000..12b7595
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/ide/util/newProjectWizard/TemplatesGroup.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2000-2012 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.ide.util.newProjectWizard;
+
+import javax.swing.*;
+
+/**
+ * @author Dmitry Avdeev
+ * Date: 11/20/12
+ */
+public class TemplatesGroup {
+
+ private final String myName;
+ private final String myDescription;
+ private final Icon myIcon;
+
+ public TemplatesGroup(String name, String description, Icon icon) {
+ myName = name;
+ myDescription = description;
+ myIcon = icon;
+ }
+
+ public String getName() {
+ return myName;
+ }
+
+ public String getDescription() {
+ return myDescription;
+ }
+
+ public Icon getIcon() {
+ return myIcon;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ TemplatesGroup group = (TemplatesGroup)o;
+
+ if (!myName.equals(group.myName)) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ return myName.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return myName;
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/ide/util/newProjectWizard/WizardArrowUI.java b/java/idea-ui/src/com/intellij/ide/util/newProjectWizard/WizardArrowUI.java
new file mode 100644
index 0000000..f4e4f68
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/ide/util/newProjectWizard/WizardArrowUI.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2000-2012 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.ide.util.newProjectWizard;
+
+import com.intellij.openapi.ui.GraphicsConfig;
+import com.intellij.ui.Gray;
+import com.intellij.util.ui.GraphicsUtil;
+import com.intellij.util.ui.UIUtil;
+import sun.swing.SwingUtilities2;
+
+import javax.swing.*;
+import javax.swing.border.EmptyBorder;
+import javax.swing.plaf.ComponentUI;
+import javax.swing.plaf.basic.BasicButtonUI;
+import java.awt.*;
+import java.awt.geom.GeneralPath;
+import java.awt.geom.Path2D;
+
+/**
+* @author Konstantin Bulenkov
+*/
+class WizardArrowUI extends BasicButtonUI {
+ private final AbstractButton myButton;
+ private static Rectangle viewRect = new Rectangle();
+ private static Rectangle textRect = new Rectangle();
+ private static Rectangle iconRect = new Rectangle();
+
+
+ public WizardArrowUI(AbstractButton b, boolean valid) {
+ myButton = b;
+ //b.setIcon(EmptyIcon.create(1));
+ b.setOpaque(false);
+ b.setBorder(new EmptyBorder(8, 0, 8, 40));
+ }
+
+ @SuppressWarnings({"MethodOverridesStaticMethodOfSuperclass", "UnusedDeclaration"})
+ public static ComponentUI createUI(JComponent c) {
+ return new WizardArrowUI((AbstractButton)c, false);
+ }
+
+ @Override
+ protected void paintIcon(Graphics g, JComponent c, Rectangle iconRect) {
+ }
+
+ @Override
+ protected int getTextShiftOffset() {
+ return 5;
+ }
+
+ private String layout(AbstractButton b, FontMetrics fm,
+ int width, int height) {
+ Insets i = b.getInsets();
+ viewRect.x = i.left;
+ viewRect.y = i.top;
+ viewRect.width = width - (i.right + viewRect.x);
+ viewRect.height = height - (i.bottom + viewRect.y);
+
+ textRect.x = textRect.y = textRect.width = textRect.height = 0;
+ iconRect.x = iconRect.y = iconRect.width = iconRect.height = 0;
+
+ // layout the text and icon
+ return SwingUtilities.layoutCompoundLabel(
+ b, fm, b.getText(), b.getIcon(),
+ b.getVerticalAlignment(), b.getHorizontalAlignment(),
+ b.getVerticalTextPosition(), b.getHorizontalTextPosition(),
+ viewRect, iconRect, textRect,
+ b.getText() == null ? 0 : b.getIconTextGap());
+ }
+
+ @Override
+ public void paint(Graphics g, JComponent c) {
+ int w = c.getWidth();
+ int h = c.getHeight();
+ layout(myButton, SwingUtilities2.getFontMetrics(c, g), w, h);
+
+ final GraphicsConfig config = GraphicsUtil.setupAAPainting(g);
+ h-=4;
+ if (!myButton.isSelected()) {
+ w-=15;
+ }
+ final Path2D.Double path = new GeneralPath.Double();
+ path.moveTo(0, 0);
+ path.lineTo(w - h / 2, 0);
+ path.lineTo(w, h / 2);
+ path.lineTo(w-h/2, h);
+ path.lineTo(0, h);
+ path.lineTo(0, 0);
+ g.setColor(myButton.isSelected() ? UIUtil.getListSelectionBackground() : Gray._255.withAlpha(200));
+ ((Graphics2D)g).fill(path);
+ g.setColor(Gray._0.withAlpha(50));
+ ((Graphics2D)g).draw(path);
+ config.restore();
+ textRect.x = 2;
+ textRect.y-=7;
+ c.setForeground(UIUtil.getListForeground(myButton.isSelected()));
+ GraphicsUtil.setupAntialiasing(g);
+ paintText(g, c, textRect, myButton.getText());
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/ide/util/newProjectWizard/impl/FrameworkSupportCommunicator.java b/java/idea-ui/src/com/intellij/ide/util/newProjectWizard/impl/FrameworkSupportCommunicator.java
new file mode 100644
index 0000000..b6d325b
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/ide/util/newProjectWizard/impl/FrameworkSupportCommunicator.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2000-2010 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.ide.util.newProjectWizard.impl;
+
+import com.intellij.ide.util.frameworkSupport.FrameworkSupportConfigurable;
+import com.intellij.ide.util.frameworkSupport.FrameworkSupportModel;
+import com.intellij.openapi.extensions.ExtensionPointName;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.roots.ModifiableRootModel;
+
+import java.util.List;
+
+/**
+ * @author nik
+ */
+public abstract class FrameworkSupportCommunicator {
+ public static final ExtensionPointName<FrameworkSupportCommunicator> EP_NAME = ExtensionPointName.create("com.intellij.frameworkSupportCommunicator");
+
+ public abstract void onFrameworkSupportAdded(Module module, ModifiableRootModel rootModel,
+ List<FrameworkSupportConfigurable> selectedFrameworks,
+ FrameworkSupportModel model);
+}
diff --git a/java/idea-ui/src/com/intellij/ide/util/newProjectWizard/impl/FrameworkSupportModelBase.java b/java/idea-ui/src/com/intellij/ide/util/newProjectWizard/impl/FrameworkSupportModelBase.java
new file mode 100644
index 0000000..93575c7
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/ide/util/newProjectWizard/impl/FrameworkSupportModelBase.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright 2000-2009 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.ide.util.newProjectWizard.impl;
+
+import com.intellij.framework.addSupport.FrameworkSupportInModuleProvider;
+import com.intellij.ide.util.frameworkSupport.FrameworkSupportConfigurable;
+import com.intellij.ide.util.frameworkSupport.FrameworkSupportModel;
+import com.intellij.ide.util.frameworkSupport.FrameworkSupportModelListener;
+import com.intellij.ide.util.frameworkSupport.FrameworkSupportProvider;
+import com.intellij.ide.util.newProjectWizard.FrameworkSupportNode;
+import com.intellij.ide.util.newProjectWizard.OldFrameworkSupportProviderWrapper;
+import com.intellij.ide.util.projectWizard.ModuleBuilder;
+import com.intellij.openapi.Disposable;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.LibrariesContainer;
+import com.intellij.openapi.util.UserDataHolderBase;
+import com.intellij.util.EventDispatcher;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author nik
+ */
+public abstract class FrameworkSupportModelBase extends UserDataHolderBase implements FrameworkSupportModel {
+ private final Project myProject;
+ private final ModuleBuilder myModuleBuilder;
+ private final LibrariesContainer myLibrariesContainer;
+ private final EventDispatcher<FrameworkSupportModelListener> myDispatcher = EventDispatcher.create(FrameworkSupportModelListener.class);
+ private final Map<String, FrameworkSupportNode> mySettingsMap = new HashMap<String, FrameworkSupportNode>();
+
+ public FrameworkSupportModelBase(final @Nullable Project project, @Nullable ModuleBuilder builder, @NotNull LibrariesContainer librariesContainer) {
+ myProject = project;
+ myModuleBuilder = builder;
+ myLibrariesContainer = librariesContainer;
+ }
+
+ @NotNull
+ public abstract String getBaseDirectoryForLibrariesPath();
+
+ public void registerComponent(@NotNull final FrameworkSupportInModuleProvider provider, @NotNull final FrameworkSupportNode node) {
+ mySettingsMap.put(provider.getFrameworkType().getId(), node);
+ }
+
+ public Project getProject() {
+ return myProject;
+ }
+
+ public ModuleBuilder getModuleBuilder() {
+ return myModuleBuilder;
+ }
+
+ public boolean isFrameworkSelected(@NotNull @NonNls final String providerId) {
+ final FrameworkSupportNode node = mySettingsMap.get(providerId);
+ return node != null && node.isChecked();
+ }
+
+ public void addFrameworkListener(@NotNull final FrameworkSupportModelListener listener) {
+ myDispatcher.addListener(listener);
+ }
+
+ @Override
+ public void addFrameworkListener(@NotNull final FrameworkSupportModelListener listener, @NotNull Disposable parentDisposable) {
+ myDispatcher.addListener(listener, parentDisposable);
+ }
+
+ public void removeFrameworkListener(@NotNull final FrameworkSupportModelListener listener) {
+ myDispatcher.removeListener(listener);
+ }
+
+ public void setFrameworkComponentEnabled(@NotNull @NonNls final String providerId, final boolean enable) {
+ final FrameworkSupportNode node = mySettingsMap.get(providerId);
+ if (node != null && enable != node.isChecked()) {
+ node.setChecked(enable);
+ }
+ }
+
+ public FrameworkSupportConfigurable getFrameworkConfigurable(@NotNull @NonNls String providerId) {
+ final FrameworkSupportNode node = mySettingsMap.get(providerId);
+ if (node == null) {
+ throw new IllegalArgumentException("provider '" + providerId + " not found");
+ }
+ return ((OldFrameworkSupportProviderWrapper.FrameworkSupportConfigurableWrapper)node.getConfigurable()).getConfigurable();
+ }
+
+ public void onFrameworkSelectionChanged(FrameworkSupportNode node) {
+ final FrameworkSupportModelListener multicaster = myDispatcher.getMulticaster();
+ final FrameworkSupportInModuleProvider provider = node.getProvider();
+ //todo[nik]
+ if (provider instanceof OldFrameworkSupportProviderWrapper) {
+ final FrameworkSupportProvider oldProvider = ((OldFrameworkSupportProviderWrapper) provider).getProvider();
+ if (node.isChecked()) {
+ multicaster.frameworkSelected(oldProvider);
+ }
+ else {
+ multicaster.frameworkUnselected(oldProvider);
+ }
+ }
+ }
+
+ public void fireWizardStepUpdated() {
+ myDispatcher.getMulticaster().wizardStepUpdated();
+ }
+
+ @NotNull
+ public LibrariesContainer getLibrariesContainer() {
+ return myLibrariesContainer;
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/ide/util/newProjectWizard/modes/CreateFromScratchMode.java b/java/idea-ui/src/com/intellij/ide/util/newProjectWizard/modes/CreateFromScratchMode.java
new file mode 100644
index 0000000..78f909d
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/ide/util/newProjectWizard/modes/CreateFromScratchMode.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2000-2009 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.
+ */
+
+/*
+ * User: anna
+ * Date: 10-Jul-2007
+ */
+package com.intellij.ide.util.newProjectWizard.modes;
+
+import com.intellij.ide.util.newProjectWizard.StepSequence;
+import com.intellij.ide.util.projectWizard.EmptyModuleBuilder;
+import com.intellij.ide.util.projectWizard.ModuleBuilder;
+import com.intellij.ide.util.projectWizard.WizardContext;
+import com.intellij.openapi.application.ApplicationNamesInfo;
+import com.intellij.openapi.module.ModuleType;
+import com.intellij.openapi.project.ProjectBundle;
+import com.intellij.openapi.roots.ui.configuration.ModulesProvider;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class CreateFromScratchMode extends WizardMode {
+
+ @NonNls private final Map<String, ModuleBuilder> myBuildersMap = new HashMap<String, ModuleBuilder>();
+
+ @NotNull
+ public String getDisplayName(final WizardContext context) {
+ return ProjectBundle.message("project.new.wizard.from.scratch.title", context.getPresentationName());
+ }
+
+ @NotNull
+ public String getDescription(final WizardContext context) {
+ return ProjectBundle.message("project.new.wizard.from.scratch.description", ApplicationNamesInfo.getInstance().getFullProductName(), context.getPresentationName());
+ }
+
+ @Nullable
+ protected StepSequence createSteps(final WizardContext context, @NotNull final ModulesProvider modulesProvider) {
+ ModuleBuilder[] builders = context.getAllBuilders();
+ for (ModuleBuilder builder : builders) {
+ myBuildersMap.put(builder.getBuilderId(), builder);
+ }
+ myBuildersMap.put(ModuleType.EMPTY.getId(), new EmptyModuleBuilder());
+
+ StepSequence sequence = new StepSequence();
+ for (ModuleBuilder builder : builders) {
+ sequence.addStepsForBuilder(builder, context, modulesProvider);
+ }
+ return sequence;
+ }
+
+ public boolean isAvailable(WizardContext context) {
+ return true;
+ }
+
+ public ModuleBuilder getModuleBuilder() {
+ return myBuildersMap.get(getSelectedType());
+ }
+
+ public void onChosen(final boolean enabled) {
+
+ }
+
+ @Override
+ public String getShortName() {
+ return "Create from Scratch";
+ }
+
+ public void dispose() {
+ super.dispose();
+ myBuildersMap.clear();
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/ide/util/newProjectWizard/modes/CreateFromSourcesMode.java b/java/idea-ui/src/com/intellij/ide/util/newProjectWizard/modes/CreateFromSourcesMode.java
new file mode 100644
index 0000000..0095938
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/ide/util/newProjectWizard/modes/CreateFromSourcesMode.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright 2000-2009 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.
+ */
+
+/*
+ * User: anna
+ * Date: 10-Jul-2007
+ */
+package com.intellij.ide.util.newProjectWizard.modes;
+
+import com.intellij.ide.util.importProject.FrameworkDetectionStep;
+import com.intellij.ide.util.importProject.ModuleDescriptor;
+import com.intellij.ide.util.importProject.ProjectDescriptor;
+import com.intellij.ide.util.importProject.RootsDetectionStep;
+import com.intellij.ide.util.newProjectWizard.ProjectNameStep;
+import com.intellij.ide.util.newProjectWizard.StepSequence;
+import com.intellij.ide.util.projectWizard.ModuleWizardStep;
+import com.intellij.ide.util.projectWizard.ProjectBuilder;
+import com.intellij.ide.util.projectWizard.WizardContext;
+import com.intellij.ide.util.projectWizard.importSources.ProjectStructureDetector;
+import com.intellij.ide.util.projectWizard.importSources.impl.ProjectFromSourcesBuilderImpl;
+import com.intellij.openapi.application.ApplicationNamesInfo;
+import com.intellij.openapi.project.ProjectBundle;
+import com.intellij.openapi.roots.ui.configuration.ModulesProvider;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import java.util.ArrayList;
+import java.util.List;
+
+public abstract class CreateFromSourcesMode extends WizardMode {
+ protected ProjectFromSourcesBuilderImpl myProjectBuilder;
+
+ @NotNull
+ public String getDisplayName(final WizardContext context) {
+ return ProjectBundle.message("project.new.wizard.from.existent.sources.title", context.getPresentationName());
+ }
+
+ @NotNull
+ public String getDescription(final WizardContext context) {
+ return ProjectBundle.message("project.new.wizard.from.existent.sources.description",
+ ApplicationNamesInfo.getInstance().getFullProductName(), context.getPresentationName());
+ }
+
+ @Nullable
+ protected StepSequence createSteps(final WizardContext context, @NotNull final ModulesProvider modulesProvider) {
+ final StepSequence sequence = new StepSequence();
+ addSteps(context, modulesProvider, sequence, null);
+ return sequence;
+ }
+
+ public void addSteps(WizardContext context, ModulesProvider modulesProvider, StepSequence sequence, String specific) {
+ final ProjectFromSourcesBuilderImpl projectBuilder = new ProjectFromSourcesBuilderImpl(context, modulesProvider);
+ myProjectBuilder = projectBuilder;
+
+ final Icon icon = context.getStepIcon();
+ if (context.isCreatingNewProject()) {
+ addStep(sequence, new ProjectNameStep(context, this), specific);
+ }
+ addStep(sequence, new RootsDetectionStep(projectBuilder, context, sequence, icon, "reference.dialogs.new.project.fromCode.source"), specific);
+ for (ProjectStructureDetector detector : ProjectStructureDetector.EP_NAME.getExtensions()) {
+ for (ModuleWizardStep step : detector.createWizardSteps(projectBuilder, projectBuilder.getProjectDescriptor(detector), icon)) {
+ sequence.addSpecificStep(detector.getDetectorId(), step);
+ }
+ }
+
+ if (FrameworkDetectionStep.isEnabled()) {
+ FrameworkDetectionStep frameworkDetectionStep = new FrameworkDetectionStep(icon, projectBuilder) {
+ public List<ModuleDescriptor> getModuleDescriptors() {
+ final List<ModuleDescriptor> moduleDescriptors = new ArrayList<ModuleDescriptor>();
+ for (ProjectDescriptor descriptor : projectBuilder.getSelectedDescriptors()) {
+ moduleDescriptors.addAll(descriptor.getModules());
+ }
+ return moduleDescriptors;
+ }
+ };
+ projectBuilder.addConfigurationUpdater(frameworkDetectionStep);
+ sequence.addCommonFinishingStep(frameworkDetectionStep);
+ }
+ }
+
+ private static void addStep(StepSequence sequence, ModuleWizardStep step, String specific) {
+ if (specific == null) {
+ sequence.addCommonStep(step);
+ }
+ else {
+ sequence.addSpecificStep(specific, step);
+ }
+ }
+
+ public ProjectBuilder getModuleBuilder() {
+ return myProjectBuilder;
+ }
+
+ public void onChosen(final boolean enabled) {
+ }
+
+ @Override
+ public String getShortName() {
+ return "Create from Sources";
+ }
+
+ public void dispose() {
+ myProjectBuilder = null;
+ super.dispose();
+ }
+}
\ No newline at end of file
diff --git a/java/idea-ui/src/com/intellij/ide/util/newProjectWizard/modes/CreateFromTemplateMode.java b/java/idea-ui/src/com/intellij/ide/util/newProjectWizard/modes/CreateFromTemplateMode.java
new file mode 100644
index 0000000..3d72971
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/ide/util/newProjectWizard/modes/CreateFromTemplateMode.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright 2000-2012 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.ide.util.newProjectWizard.modes;
+
+import com.intellij.ide.util.newProjectWizard.SelectTemplateStep;
+import com.intellij.ide.util.newProjectWizard.StepSequence;
+import com.intellij.ide.util.newProjectWizard.TemplatesGroup;
+import com.intellij.ide.util.projectWizard.ModuleBuilder;
+import com.intellij.ide.util.projectWizard.WizardContext;
+import com.intellij.openapi.project.ProjectBundle;
+import com.intellij.openapi.roots.ui.configuration.ModulesProvider;
+import com.intellij.openapi.util.Condition;
+import com.intellij.platform.DirectoryProjectGenerator;
+import com.intellij.platform.ProjectTemplate;
+import com.intellij.platform.ProjectTemplatesFactory;
+import com.intellij.platform.templates.LocalArchivedTemplate;
+import com.intellij.util.containers.ContainerUtil;
+import com.intellij.util.containers.MultiMap;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author Dmitry Avdeev
+ * Date: 9/26/12
+ */
+public class CreateFromTemplateMode extends WizardMode {
+
+ private static final Condition<ProjectTemplate> TEMPLATE_CONDITION = new Condition<ProjectTemplate>() {
+ @Override
+ public boolean value(ProjectTemplate template) {
+ return !(template instanceof DirectoryProjectGenerator);
+ }
+ };
+ private SelectTemplateStep mySelectTemplateStep;
+
+ public static MultiMap<TemplatesGroup, ProjectTemplate> getTemplatesMap(WizardContext context) {
+ ProjectTemplatesFactory[] factories = ProjectTemplatesFactory.EP_NAME.getExtensions();
+ final MultiMap<TemplatesGroup, ProjectTemplate> groups = new MultiMap<TemplatesGroup, ProjectTemplate>();
+ for (ProjectTemplatesFactory factory : factories) {
+ for (String group : factory.getGroups()) {
+ ProjectTemplate[] templates = factory.createTemplates(group, context);
+ List<ProjectTemplate> values = context.isCreatingNewProject() ? Arrays.asList(templates) : ContainerUtil.filter(templates,
+ TEMPLATE_CONDITION);
+ if (!values.isEmpty()) {
+ Icon icon = factory.getGroupIcon(group);
+ TemplatesGroup templatesGroup = new TemplatesGroup(group, null, icon);
+ if (icon != null) {
+ Collection<ProjectTemplate> collection = groups.remove(templatesGroup);
+ groups.putValues(templatesGroup, values);
+ if (collection != null) {
+ groups.putValues(templatesGroup, collection);
+ }
+ }
+ else {
+ groups.putValues(templatesGroup, values);
+ }
+ }
+ }
+ }
+ final MultiMap<TemplatesGroup, ProjectTemplate> sorted = new MultiMap<TemplatesGroup, ProjectTemplate>();
+ // put single leafs under "Other"
+ for (Map.Entry<TemplatesGroup, Collection<ProjectTemplate>> entry : groups.entrySet()) {
+ Collection<ProjectTemplate> templates = entry.getValue();
+ if (templates.size() == 1 &&
+ !ProjectTemplatesFactory.CUSTOM_GROUP.equals(entry.getKey().getName())) {
+
+ if (!(templates.iterator().next() instanceof LocalArchivedTemplate)) {
+ sorted.putValues(new TemplatesGroup(ProjectTemplatesFactory.OTHER_GROUP, null, null), templates);
+ continue;
+ }
+ }
+ sorted.putValues(entry.getKey(), templates);
+ }
+ return sorted;
+ }
+
+ @NotNull
+ @Override
+ public String getDisplayName(WizardContext context) {
+ return ProjectBundle.message("project.new.wizard.from.template.title", context.getPresentationName());
+ }
+
+ @NotNull
+ @Override
+ public String getDescription(WizardContext context) {
+ return ProjectBundle.message("project.new.wizard.from.template.description", context.getPresentationName());
+ }
+
+ @Override
+ public boolean isAvailable(WizardContext context) {
+ return true;
+ }
+
+ @Nullable
+ @Override
+ protected StepSequence createSteps(WizardContext context, @NotNull ModulesProvider modulesProvider) {
+ MultiMap<TemplatesGroup, ProjectTemplate> map = getTemplatesMap(context);
+ StepSequence sequence = new StepSequence();
+ for (ProjectTemplate template : map.values()) {
+ sequence.addStepsForBuilder(template.createModuleBuilder(), context, modulesProvider);
+ }
+ mySelectTemplateStep = new SelectTemplateStep(context, sequence, map);
+ sequence.addCommonStep(mySelectTemplateStep);
+ return sequence;
+ }
+
+ @Nullable
+ @Override
+ public ModuleBuilder getModuleBuilder() {
+ final ProjectTemplate template = mySelectTemplateStep.getSelectedTemplate();
+ if (template == null) {
+ return null;
+ }
+ return template.createModuleBuilder();
+ }
+
+ @Override
+ public String getShortName() {
+ return "Create from Template";
+ }
+
+ @Override
+ public void onChosen(boolean enabled) {
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/ide/util/newProjectWizard/modes/CreateModuleFromSourcesMode.java b/java/idea-ui/src/com/intellij/ide/util/newProjectWizard/modes/CreateModuleFromSourcesMode.java
new file mode 100644
index 0000000..3fec5ca
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/ide/util/newProjectWizard/modes/CreateModuleFromSourcesMode.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2000-2011 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.ide.util.newProjectWizard.modes;
+
+import com.intellij.ide.util.projectWizard.ProjectBuilder;
+import com.intellij.ide.util.projectWizard.WizardContext;
+import com.intellij.openapi.fileChooser.FileChooserDescriptor;
+import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory;
+import com.intellij.openapi.options.ConfigurationException;
+import com.intellij.openapi.ui.TextFieldWithBrowseButton;
+import com.intellij.openapi.util.IconLoader;
+import com.intellij.util.ui.UIUtil;
+
+import javax.swing.*;
+import java.io.File;
+
+/**
+ * @author nik
+ */
+public class CreateModuleFromSourcesMode extends CreateFromSourcesMode {
+ private TextFieldWithBrowseButton myPathPanel;
+
+ public boolean isAvailable(WizardContext context) {
+ return !context.isCreatingNewProject();
+ }
+
+ @Override
+ public ProjectBuilder getModuleBuilder() {
+ myProjectBuilder.setBaseProjectPath(myPathPanel.getText().trim());
+ return myProjectBuilder;
+ }
+
+ @Override
+ public JComponent getAdditionalSettings(WizardContext wizardContext) {
+ myPathPanel = new TextFieldWithBrowseButton();
+ final FileChooserDescriptor descriptor = FileChooserDescriptorFactory.createSingleFolderDescriptor();
+ myPathPanel.addBrowseFolderListener("Select Directory Containing Module Files", null, wizardContext.getProject(), descriptor);
+ onChosen(false);
+ return myPathPanel;
+ }
+
+ @Override
+ public boolean validate() throws ConfigurationException {
+ final String path = myPathPanel.getText().trim();
+ final File file = new File(path);
+ if (!file.exists()) {
+ throw new ConfigurationException("File \'" + path + "\' doesn't exist");
+ }
+ if (!file.isDirectory()) {
+ throw new ConfigurationException("\'" + path + "\' is not a directory");
+ }
+ return super.validate();
+ }
+
+ public void onChosen(final boolean enabled) {
+ UIUtil.setEnabled(myPathPanel, enabled, true);
+ if (enabled) {
+ myPathPanel.getTextField().requestFocusInWindow();
+ }
+ }
+
+ public void dispose() {
+ myPathPanel = null;
+ super.dispose();
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/ide/util/newProjectWizard/modes/CreateProjectFromSourcesMode.java b/java/idea-ui/src/com/intellij/ide/util/newProjectWizard/modes/CreateProjectFromSourcesMode.java
new file mode 100644
index 0000000..1f129f6
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/ide/util/newProjectWizard/modes/CreateProjectFromSourcesMode.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2000-2011 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.ide.util.newProjectWizard.modes;
+
+import com.intellij.ide.util.projectWizard.WizardContext;
+
+/**
+ * @author nik
+ */
+public class CreateProjectFromSourcesMode extends CreateFromSourcesMode {
+ public boolean isAvailable(WizardContext context) {
+ return context.isCreatingNewProject();
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/ide/util/newProjectWizard/modes/ImportImlMode.java b/java/idea-ui/src/com/intellij/ide/util/newProjectWizard/modes/ImportImlMode.java
new file mode 100644
index 0000000..5a88ab8
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/ide/util/newProjectWizard/modes/ImportImlMode.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright 2000-2012 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.
+ */
+
+/*
+ * User: anna
+ * Date: 10-Jul-2007
+ */
+package com.intellij.ide.util.newProjectWizard.modes;
+
+import com.intellij.ide.IdeBundle;
+import com.intellij.ide.util.BrowseFilesListener;
+import com.intellij.ide.util.newProjectWizard.StepSequence;
+import com.intellij.ide.util.projectWizard.ExistingModuleLoader;
+import com.intellij.ide.util.projectWizard.ProjectBuilder;
+import com.intellij.ide.util.projectWizard.WizardContext;
+import com.intellij.openapi.application.ApplicationNamesInfo;
+import com.intellij.openapi.fileChooser.FileChooserDescriptor;
+import com.intellij.openapi.fileTypes.FileTypeManager;
+import com.intellij.openapi.fileTypes.StdFileTypes;
+import com.intellij.openapi.options.ConfigurationException;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.roots.ui.configuration.ModulesProvider;
+import com.intellij.openapi.ui.TextFieldWithBrowseButton;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.util.ui.UIUtil;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import java.io.File;
+
+public class ImportImlMode extends WizardMode {
+ private TextFieldWithBrowseButton myModulePathFieldPanel;
+
+ @NotNull
+ public String getDisplayName(final WizardContext context) {
+ return IdeBundle.message("radio.import.existing.module");
+ }
+
+ @NotNull
+ public String getDescription(final WizardContext context) {
+ return IdeBundle.message("prompt.select.module.file.to.import", ApplicationNamesInfo.getInstance().getFullProductName());
+ }
+
+
+ @Nullable
+ protected StepSequence createSteps(final WizardContext context, @NotNull final ModulesProvider modulesProvider) {
+ return null;
+ }
+
+ public boolean isAvailable(WizardContext context) {
+ return context.getProject() != null;
+ }
+
+ public ProjectBuilder getModuleBuilder() {
+ return setUpLoader(FileUtil.toSystemIndependentName(myModulePathFieldPanel.getText().trim()));
+ }
+
+ public static ExistingModuleLoader setUpLoader(final String moduleFilePath) {
+ final ExistingModuleLoader moduleLoader = new ExistingModuleLoader();
+ moduleLoader.setModuleFilePath(moduleFilePath);
+ final int startIndex = moduleFilePath.lastIndexOf('/');
+ final int endIndex = moduleFilePath.lastIndexOf(".");
+ if (startIndex >= 0 && endIndex > startIndex + 1) {
+ final String name = moduleFilePath.substring(startIndex + 1, endIndex);
+ moduleLoader.setName(name);
+ }
+ return moduleLoader;
+ }
+
+ public void dispose() {
+ myModulePathFieldPanel = null; //todo
+ }
+
+ public JComponent getAdditionalSettings(final WizardContext wizardContext) {
+ JTextField tfModuleFilePath = new JTextField();
+ final String productName = ApplicationNamesInfo.getInstance().getProductName();
+ final String message = IdeBundle.message("prompt.select.module.file.to.import", productName);
+ final BrowseFilesListener listener = new BrowseFilesListener(tfModuleFilePath, message, null, new ModuleFileChooserDescriptor()) {
+ @Override
+ protected VirtualFile getFileToSelect() {
+ final VirtualFile fileToSelect = super.getFileToSelect();
+ if (fileToSelect != null) {
+ return fileToSelect;
+ }
+ final Project project = wizardContext.getProject();
+ return project != null ? project.getBaseDir() : null;
+ }
+ };
+ myModulePathFieldPanel = new TextFieldWithBrowseButton(tfModuleFilePath, listener);
+ onChosen(false);
+ return myModulePathFieldPanel;
+ }
+
+ @Override
+ public boolean validate() throws ConfigurationException {
+ final String imlPath = myModulePathFieldPanel.getText().trim();
+ if (!new File(imlPath).exists()) throw new ConfigurationException("File \'" + imlPath + "\' doesn't exist");
+ if (!FileTypeManager.getInstance().getFileTypeByFileName(imlPath).equals(StdFileTypes.IDEA_MODULE)) throw new ConfigurationException("File \'" + imlPath + "\' doesn't contain IDEA module");
+ return super.validate();
+ }
+
+ public void onChosen(final boolean enabled) {
+ UIUtil.setEnabled(myModulePathFieldPanel, enabled, true);
+ if (enabled) {
+ myModulePathFieldPanel.getTextField().requestFocusInWindow();
+ }
+ }
+
+ @Override
+ public String getShortName() {
+ return "Import from *.iml";
+ }
+
+ private static class ModuleFileChooserDescriptor extends FileChooserDescriptor {
+ public ModuleFileChooserDescriptor() {
+ super(true, false, false, false, false, false);
+ setHideIgnored(false);
+ }
+
+ public boolean isFileVisible(VirtualFile file, boolean showHiddenFiles) {
+ final boolean isVisible = super.isFileVisible(file, showHiddenFiles);
+ if (!isVisible || file.isDirectory()) {
+ return isVisible;
+ }
+ return StdFileTypes.IDEA_MODULE.equals(file.getFileType());
+ }
+ }
+}
\ No newline at end of file
diff --git a/java/idea-ui/src/com/intellij/ide/util/newProjectWizard/modes/ImportMode.java b/java/idea-ui/src/com/intellij/ide/util/newProjectWizard/modes/ImportMode.java
new file mode 100644
index 0000000..6c94172
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/ide/util/newProjectWizard/modes/ImportMode.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright 2000-2009 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.
+ */
+
+/*
+ * User: anna
+ * Date: 10-Jul-2007
+ */
+package com.intellij.ide.util.newProjectWizard.modes;
+
+import com.intellij.ide.util.newProjectWizard.StepSequence;
+import com.intellij.ide.util.projectWizard.ProjectBuilder;
+import com.intellij.ide.util.projectWizard.WizardContext;
+import com.intellij.openapi.application.ApplicationNamesInfo;
+import com.intellij.openapi.extensions.Extensions;
+import com.intellij.openapi.project.ProjectBundle;
+import com.intellij.openapi.roots.ui.configuration.ModulesProvider;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.projectImport.ImportChooserStep;
+import com.intellij.projectImport.ProjectImportBuilder;
+import com.intellij.projectImport.ProjectImportProvider;
+import com.intellij.util.Function;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.Arrays;
+
+public class ImportMode extends WizardMode {
+ private ProjectImportBuilder myBuilder;
+ private final ProjectImportProvider[] myProviders;
+
+ public ImportMode() {
+ this(Extensions.getExtensions(ProjectImportProvider.PROJECT_IMPORT_PROVIDER));
+ }
+ public ImportMode(ProjectImportProvider[] providers) {
+ myProviders = providers;
+ }
+
+ @NotNull
+ public String getDisplayName(final WizardContext context) {
+ return ProjectBundle.message("project.new.wizard.import.title", context.getPresentationName());
+ }
+
+ @NotNull
+ public String getDescription(final WizardContext context) {
+ final String productName = ApplicationNamesInfo.getInstance().getFullProductName();
+ return ProjectBundle.message("project.new.wizard.import.description", productName, context.getPresentationName(), StringUtil.join(
+ Arrays.asList(Extensions.getExtensions(ProjectImportProvider.PROJECT_IMPORT_PROVIDER)),
+ new Function<ProjectImportProvider, String>() {
+ public String fun(final ProjectImportProvider provider) {
+ return provider.getName();
+ }
+ }, ", "));
+ }
+
+ @Nullable
+ protected StepSequence createSteps(final WizardContext context, @NotNull final ModulesProvider modulesProvider) {
+ final StepSequence stepSequence = new StepSequence();
+ if (myProviders.length > 1) {
+ stepSequence.addCommonStep(new ImportChooserStep(myProviders, stepSequence, context));
+ }
+ for (ProjectImportProvider provider : myProviders) {
+ provider.addSteps(stepSequence, context, provider.getId());
+ }
+ if (myProviders.length == 1) {
+ stepSequence.setType(myProviders[0].getId());
+ }
+ return stepSequence;
+ }
+
+ public boolean isAvailable(WizardContext context) {
+ return Extensions.getExtensions(ProjectImportProvider.PROJECT_IMPORT_PROVIDER).length > 0;
+ }
+
+ @Nullable
+ public ProjectBuilder getModuleBuilder() {
+ return myBuilder;
+ }
+
+ public void onChosen(final boolean enabled) {}
+
+ @Override
+ public String getShortName() {
+ return "Import from External Model";
+ }
+}
\ No newline at end of file
diff --git a/java/idea-ui/src/com/intellij/ide/util/newProjectWizard/modes/WizardMode.java b/java/idea-ui/src/com/intellij/ide/util/newProjectWizard/modes/WizardMode.java
new file mode 100644
index 0000000..bf490a8
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/ide/util/newProjectWizard/modes/WizardMode.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2000-2009 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.
+ */
+
+/*
+ * User: anna
+ * Date: 08-Jul-2007
+ */
+package com.intellij.ide.util.newProjectWizard.modes;
+
+import com.intellij.ide.util.newProjectWizard.StepSequence;
+import com.intellij.ide.util.projectWizard.ProjectBuilder;
+import com.intellij.ide.util.projectWizard.WizardContext;
+import com.intellij.openapi.Disposable;
+import com.intellij.openapi.extensions.ExtensionPointName;
+import com.intellij.openapi.options.ConfigurationException;
+import com.intellij.openapi.roots.ui.configuration.ModulesProvider;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+
+public abstract class WizardMode implements Disposable {
+ public static final ExtensionPointName<WizardMode> MODES = ExtensionPointName.create("com.intellij.wizardMode");
+
+ private StepSequence myStepSequence;
+
+ @NotNull
+ public abstract String getDisplayName(final WizardContext context);
+
+ @NotNull
+ public abstract String getDescription(final WizardContext context);
+
+ public abstract boolean isAvailable(final WizardContext context);
+
+ @Nullable
+ public StepSequence getSteps(final WizardContext context, @NotNull final ModulesProvider modulesProvider) {
+ if (myStepSequence == null) {
+ myStepSequence = createSteps(context, modulesProvider);
+ }
+ return myStepSequence;
+ }
+
+ @Nullable
+ protected abstract StepSequence createSteps(final WizardContext context, @NotNull final ModulesProvider modulesProvider);
+
+ @Nullable
+ public abstract ProjectBuilder getModuleBuilder();
+
+ @Nullable
+ public JComponent getAdditionalSettings(WizardContext wizardContext) {
+ return null;
+ }
+
+ public abstract void onChosen(final boolean enabled);
+
+ protected String getSelectedType() {
+ return myStepSequence != null ? myStepSequence.getSelectedType() : null;
+ }
+
+ public void dispose() {
+ myStepSequence = null;
+ }
+
+ public String getShortName() {
+ return "";
+ }
+
+ @Nullable
+ public String getFootnote(final WizardContext wizardContext) {
+ return null;
+ }
+
+ public boolean validate() throws ConfigurationException {
+ return true;
+ }
+}
\ No newline at end of file
diff --git a/java/idea-ui/src/com/intellij/ide/util/projectWizard/AbstractStepWithProgress.java b/java/idea-ui/src/com/intellij/ide/util/projectWizard/AbstractStepWithProgress.java
new file mode 100644
index 0000000..f1c4b5f
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/ide/util/projectWizard/AbstractStepWithProgress.java
@@ -0,0 +1,214 @@
+/*
+ * Copyright 2000-2009 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.ide.util.projectWizard;
+
+import com.intellij.ide.IdeBundle;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.options.ConfigurationException;
+import com.intellij.openapi.progress.ProgressIndicator;
+import com.intellij.openapi.progress.ProgressManager;
+import com.intellij.openapi.progress.util.ProgressIndicatorBase;
+import com.intellij.openapi.ui.Messages;
+import com.intellij.openapi.util.Computable;
+import com.intellij.openapi.util.Ref;
+import com.intellij.util.concurrency.SwingWorker;
+import com.intellij.util.ui.UIUtil;
+import com.intellij.util.ui.update.UiNotifyConnector;
+import org.jetbrains.annotations.NonNls;
+
+import javax.swing.*;
+import java.awt.*;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+
+/**
+ * @author nik
+ */
+public abstract class AbstractStepWithProgress<Result> extends ModuleWizardStep {
+
+ @NonNls private static final String PROGRESS_PANEL = "progress_panel";
+ @NonNls private static final String RESULTS_PANEL = "results_panel";
+ private JPanel myPanel;
+
+ private JLabel myTitleLabel;
+ private JLabel myProgressLabel;
+ private JLabel myProgressLabel2;
+ private ProgressIndicator myProgressIndicator = null;
+ private final String myPromptStopSearch;
+
+ public AbstractStepWithProgress(final String promptStopSearching) {
+ myPromptStopSearch = promptStopSearching;
+ }
+
+ public final JComponent getComponent() {
+ if (myPanel == null) {
+ myPanel = new JPanel(new CardLayout());
+ myPanel.setBorder(BorderFactory.createEtchedBorder());
+
+ myPanel.add(createProgressPanel(), PROGRESS_PANEL);
+ myPanel.add(createResultsPanel(), RESULTS_PANEL);
+ }
+ return myPanel;
+ }
+
+ protected abstract JComponent createResultsPanel();
+
+ protected abstract String getProgressText();
+
+ protected abstract boolean shouldRunProgress();
+
+ protected abstract Result calculate();
+
+ protected abstract void onFinished(Result result, boolean canceled);
+
+ private JPanel createProgressPanel() {
+ final JPanel progressPanel = new JPanel(new GridBagLayout());
+ myTitleLabel = new JLabel();
+ myTitleLabel.setFont(UIUtil.getLabelFont().deriveFont(Font.BOLD));
+ progressPanel.add(myTitleLabel, new GridBagConstraints(0, GridBagConstraints.RELATIVE, 2, 1, 1.0, 0.0, GridBagConstraints.NORTHWEST, GridBagConstraints.HORIZONTAL, new Insets(8, 10, 5, 10), 0, 0));
+
+ myProgressLabel = new JLabel();
+ progressPanel.add(myProgressLabel, new GridBagConstraints(0, GridBagConstraints.RELATIVE, 1, 1, 1.0, 0.0, GridBagConstraints.NORTHWEST, GridBagConstraints.HORIZONTAL, new Insets(8, 10, 0, 10), 0, 0));
+
+ myProgressLabel2 = new JLabel();
+ progressPanel.add(myProgressLabel2, new GridBagConstraints(0, GridBagConstraints.RELATIVE, 1, 1, 1.0, 1.0, GridBagConstraints.NORTHWEST, GridBagConstraints.HORIZONTAL, new Insets(8, 10, 0, 10), 0, 0));
+
+ JButton stopButton = new JButton(IdeBundle.message("button.stop.searching"));
+ stopButton.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ cancelSearch();
+ }
+ });
+ progressPanel.add(stopButton, new GridBagConstraints(1, GridBagConstraints.RELATIVE, 1, 2, 0.0, 1.0, GridBagConstraints.NORTHWEST, GridBagConstraints.NONE, new Insets(10, 0, 0, 10), 0, 0));
+ return progressPanel;
+ }
+
+ private void cancelSearch() {
+ if (myProgressIndicator != null) {
+ myProgressIndicator.cancel();
+ }
+ }
+
+ private synchronized boolean isProgressRunning() {
+ return myProgressIndicator != null && myProgressIndicator.isRunning();
+ }
+
+
+ public void updateStep() {
+ if (shouldRunProgress()) {
+ runProgress();
+ }
+ else {
+ showCard(RESULTS_PANEL);
+ }
+ }
+
+ protected void runProgress() {
+ final MyProgressIndicator progress = new MyProgressIndicator();
+ progress.setModalityProgress(null);
+ final String title = getProgressText();
+ if (title != null) {
+ myTitleLabel.setText(title);
+ }
+ showCard(PROGRESS_PANEL);
+ myProgressIndicator = progress;
+
+ if (ApplicationManager.getApplication().isUnitTestMode()) {
+
+ Result result = ProgressManager.getInstance().runProcess(new Computable<Result>() {
+ @Override
+ public Result compute() {
+ return calculate();
+ }
+ }, progress);
+ onFinished(result, false);
+ return;
+ }
+
+ UiNotifyConnector.doWhenFirstShown(myPanel, new Runnable() {
+ @Override
+ public void run() {
+
+ new SwingWorker() {
+ public Object construct() {
+ final Ref<Result> result = Ref.create(null);
+ ProgressManager.getInstance().runProcess(new Runnable() {
+ public void run() {
+ result.set(calculate());
+ }
+ }, progress);
+ return result.get();
+ }
+
+ public void finished() {
+ myProgressIndicator = null;
+ ApplicationManager.getApplication().invokeLater(new Runnable() {
+ public void run() {
+ final Result result = (Result)get();
+ onFinished(result, progress.isCanceled());
+ showCard(RESULTS_PANEL);
+ }
+ });
+ }
+ }.start();
+ }
+ });
+ }
+
+ private void showCard(final String id) {
+ ((CardLayout)myPanel.getLayout()).show(myPanel, id);
+ myPanel.revalidate();
+ }
+
+ public boolean validate() throws ConfigurationException {
+ if (isProgressRunning()) {
+ final int answer = Messages.showOkCancelDialog(getComponent(), myPromptStopSearch,
+ IdeBundle.message("title.question"), IdeBundle.message("action.continue.searching"), IdeBundle.message("action.stop.searching"), Messages.getWarningIcon());
+ if (answer == 1) { // terminate
+ cancelSearch();
+ }
+ return false;
+ }
+ return true;
+ }
+
+ public void onStepLeaving() {
+ if (isProgressRunning()) {
+ cancelSearch();
+ }
+ }
+
+ protected class MyProgressIndicator extends ProgressIndicatorBase {
+ public void setText(String text) {
+ updateLabel(myProgressLabel, text);
+ super.setText(text);
+ }
+
+ public void setText2(String text) {
+ updateLabel(myProgressLabel2, text);
+ super.setText2(text);
+ }
+
+ private void updateLabel(final JLabel label, final String text) {
+ UIUtil.invokeLaterIfNeeded(new Runnable() {
+ public void run() {
+ label.setText(text);
+ }
+ });
+ }
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/ide/util/projectWizard/ExistingModuleLoader.java b/java/idea-ui/src/com/intellij/ide/util/projectWizard/ExistingModuleLoader.java
new file mode 100644
index 0000000..91bf0e6
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/ide/util/projectWizard/ExistingModuleLoader.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright 2000-2012 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.ide.util.projectWizard;
+
+import com.intellij.CommonBundle;
+import com.intellij.application.options.PathMacrosCollector;
+import com.intellij.application.options.PathMacrosImpl;
+import com.intellij.conversion.ConversionResult;
+import com.intellij.conversion.ConversionService;
+import com.intellij.ide.IdeBundle;
+import com.intellij.openapi.application.PathMacros;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.module.ModifiableModuleModel;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.module.ModuleType;
+import com.intellij.openapi.module.ModuleWithNameAlreadyExists;
+import com.intellij.openapi.options.ConfigurationException;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.project.impl.ProjectMacrosUtil;
+import com.intellij.openapi.roots.ModifiableRootModel;
+import com.intellij.openapi.ui.Messages;
+import com.intellij.openapi.util.InvalidDataException;
+import com.intellij.openapi.util.JDOMUtil;
+import org.jdom.Document;
+import org.jdom.Element;
+import org.jdom.JDOMException;
+import org.jetbrains.annotations.NotNull;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Set;
+
+/**
+ * @author Eugene Zhuravlev
+ * Date: Sep 7, 2004
+ */
+public class ExistingModuleLoader extends ModuleBuilder {
+ private static final Logger LOG = Logger.getInstance("#com.intellij.ide.util.projectWizard.ExistingModuleLoader");
+
+ @NotNull
+ public Module createModule(@NotNull ModifiableModuleModel moduleModel)
+ throws InvalidDataException, IOException, ModuleWithNameAlreadyExists, JDOMException, ConfigurationException {
+ LOG.assertTrue(getName() != null);
+
+ final String moduleFilePath = getModuleFilePath();
+
+ LOG.assertTrue(moduleFilePath != null);
+ LOG.assertTrue(new File(moduleFilePath).exists());
+
+ return moduleModel.loadModule(moduleFilePath);
+ }
+
+ public void setupRootModel(ModifiableRootModel modifiableRootModel) throws ConfigurationException {
+ // empty
+ }
+
+ public ModuleType getModuleType() {
+ return null; // no matter
+ }
+
+ public boolean validate(final Project current, final Project dest) {
+ if (getName() == null) return false;
+ String moduleFilePath = getModuleFilePath();
+ if (moduleFilePath == null) return false;
+ final File file = new File(moduleFilePath);
+ if (file.exists()) {
+ try {
+ final ConversionResult result = ConversionService.getInstance().convertModule(dest, file);
+ if (result.openingIsCanceled()) {
+ return false;
+ }
+ final Document document = JDOMUtil.loadDocument(file);
+ final Element root = document.getRootElement();
+ final Set<String> usedMacros = PathMacrosCollector.getMacroNames(root);
+ final Set<String> definedMacros = PathMacros.getInstance().getAllMacroNames();
+ usedMacros.remove("$" + PathMacrosImpl.MODULE_DIR_MACRO_NAME + "$");
+ usedMacros.removeAll(definedMacros);
+
+ if (usedMacros.size() > 0) {
+ final boolean ok = ProjectMacrosUtil.showMacrosConfigurationDialog(current, usedMacros);
+ if (!ok) {
+ return false;
+ }
+ }
+ }
+ catch (JDOMException e) {
+ Messages.showMessageDialog(e.getMessage(), IdeBundle.message("title.error.reading.file"), Messages.getErrorIcon());
+ return false;
+ }
+ catch (IOException e) {
+ Messages.showMessageDialog(e.getMessage(), IdeBundle.message("title.error.reading.file"), Messages.getErrorIcon());
+ return false;
+ }
+ } else {
+ Messages.showErrorDialog(current, IdeBundle.message("title.module.file.does.not.exist", moduleFilePath),
+ CommonBundle.message("title.error"));
+ return false;
+ }
+ return true;
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/ide/util/projectWizard/ImportFromSourcesProvider.java b/java/idea-ui/src/com/intellij/ide/util/projectWizard/ImportFromSourcesProvider.java
new file mode 100644
index 0000000..78ad23c
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/ide/util/projectWizard/ImportFromSourcesProvider.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2000-2012 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.ide.util.projectWizard;
+
+import com.intellij.ide.util.newProjectWizard.StepSequence;
+import com.intellij.ide.util.newProjectWizard.modes.CreateModuleFromSourcesMode;
+import com.intellij.ide.util.projectWizard.importSources.impl.ProjectFromSourcesBuilderImpl;
+import com.intellij.openapi.roots.ui.configuration.DefaultModulesProvider;
+import com.intellij.projectImport.ProjectImportBuilder;
+import com.intellij.projectImport.ProjectImportProvider;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * @author Dmitry Avdeev
+ * Date: 11/2/12
+ */
+public class ImportFromSourcesProvider extends ProjectImportProvider {
+
+ public ImportFromSourcesProvider() {
+ super(new ProjectFromSourcesBuilderImpl(null, null)); // fake
+ }
+
+ @Override
+ public void addSteps(StepSequence sequence, WizardContext context, String id) {
+ CreateModuleFromSourcesMode mode = new CreateModuleFromSourcesMode() {
+ @Override
+ public ProjectBuilder getModuleBuilder() {
+ return myProjectBuilder;
+ }
+ };
+ mode.addSteps(context, DefaultModulesProvider.createForProject(context.getProject()), sequence, getName());
+ myBuilder = (ProjectImportBuilder)mode.getModuleBuilder();
+ }
+
+ @Nullable
+ @Override
+ public String getFileSample() {
+ return "directory with <b>existing sources</b>";
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/ide/util/projectWizard/JavaSettingsStep.form b/java/idea-ui/src/com/intellij/ide/util/projectWizard/JavaSettingsStep.form
new file mode 100644
index 0000000..b69bc2f
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/ide/util/projectWizard/JavaSettingsStep.form
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.intellij.ide.util.projectWizard.JavaSettingsStep">
+ <grid id="27dc6" binding="myPanel" layout-manager="GridLayoutManager" row-count="1" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+ <margin top="0" left="0" bottom="0" right="0"/>
+ <constraints>
+ <xy x="20" y="20" width="500" height="31"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <component id="1cc8f" class="com.intellij.openapi.ui.TextFieldWithBrowseButton" binding="mySourcePath">
+ <constraints>
+ <grid row="0" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties>
+ <text value="src"/>
+ </properties>
+ </component>
+ <component id="445f5" class="com.intellij.ui.components.JBCheckBox" binding="myCreateSourceRoot" default-binding="true">
+ <constraints>
+ <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="0" fill="0" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties>
+ <selected value="true"/>
+ <text value="&Create source root:"/>
+ </properties>
+ </component>
+ </children>
+ </grid>
+</form>
diff --git a/java/idea-ui/src/com/intellij/ide/util/projectWizard/JavaSettingsStep.java b/java/idea-ui/src/com/intellij/ide/util/projectWizard/JavaSettingsStep.java
new file mode 100644
index 0000000..786f832
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/ide/util/projectWizard/JavaSettingsStep.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2000-2012 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.ide.util.projectWizard;
+
+import com.intellij.ide.IdeBundle;
+import com.intellij.ide.util.BrowseFilesListener;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.projectRoots.SdkTypeId;
+import com.intellij.openapi.ui.ComponentWithBrowseButton;
+import com.intellij.openapi.ui.TextComponentAccessor;
+import com.intellij.openapi.ui.TextFieldWithBrowseButton;
+import com.intellij.openapi.util.Condition;
+import com.intellij.openapi.util.Pair;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.ui.components.JBCheckBox;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.TestOnly;
+
+import javax.swing.*;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.io.File;
+import java.util.Collections;
+
+/**
+ * @author Dmitry Avdeev
+ * Date: 10/23/12
+ */
+public class JavaSettingsStep extends SdkSettingsStep {
+
+ private final ModuleBuilder myModuleBuilder;
+ private JBCheckBox myCreateSourceRoot;
+ private TextFieldWithBrowseButton mySourcePath;
+ private JPanel myPanel;
+
+ public JavaSettingsStep(SettingsStep settingsStep, ModuleBuilder moduleBuilder, @NotNull Condition<SdkTypeId> sdkFilter) {
+ super(settingsStep, moduleBuilder, sdkFilter);
+ myModuleBuilder = moduleBuilder;
+
+ if (moduleBuilder instanceof JavaModuleBuilder) {
+ Project project = settingsStep.getContext().getProject();
+ ComponentWithBrowseButton.BrowseFolderActionListener<JTextField> listener =
+ new ComponentWithBrowseButton.BrowseFolderActionListener<JTextField>(
+ IdeBundle.message("prompt.select.source.directory"), null, mySourcePath, project, BrowseFilesListener.SINGLE_DIRECTORY_DESCRIPTOR,
+ TextComponentAccessor.TEXT_FIELD_WHOLE_TEXT) {
+ @Override
+ protected void onFileChoosen(VirtualFile chosenFile) {
+ String contentEntryPath = myModuleBuilder.getContentEntryPath();
+ String path = chosenFile.getPath();
+ if (contentEntryPath != null) {
+
+ int i = StringUtil.commonPrefixLength(contentEntryPath, path);
+ mySourcePath.setText(path.substring(i));
+ }
+ else {
+ mySourcePath.setText(path);
+ }
+ }
+ };
+ mySourcePath.addBrowseFolderListener(project, listener);
+ myCreateSourceRoot.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ mySourcePath.setEnabled(myCreateSourceRoot.isSelected());
+ }
+ });
+ settingsStep.addExpertPanel(myPanel);
+ }
+ }
+
+ @Override
+ public void updateDataModel() {
+ super.updateDataModel();
+ if (myModuleBuilder instanceof JavaModuleBuilder) {
+ if (myCreateSourceRoot.isSelected()) {
+ String contentEntryPath = myModuleBuilder.getContentEntryPath();
+ if (contentEntryPath != null) {
+ final String dirName = mySourcePath.getText().trim().replace(File.separatorChar, '/');
+ String text = dirName.length() > 0? contentEntryPath + "/" + dirName : contentEntryPath;
+ ((JavaModuleBuilder)myModuleBuilder).setSourcePaths(Collections.singletonList(Pair.create(text, "")));
+ }
+ }
+ else {
+ ((JavaModuleBuilder)myModuleBuilder).setSourcePaths(Collections.<Pair<String,String>>emptyList());
+ }
+ }
+ }
+
+ @TestOnly
+ public void setCreateSourceRoot(boolean create) {
+ myCreateSourceRoot.setSelected(create);
+ }
+
+ @TestOnly
+ public void setSourcePath(String path) {
+ mySourcePath.setText(path);
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/ide/util/projectWizard/JdkChooserPanel.java b/java/idea-ui/src/com/intellij/ide/util/projectWizard/JdkChooserPanel.java
new file mode 100644
index 0000000..a05f325
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/ide/util/projectWizard/JdkChooserPanel.java
@@ -0,0 +1,306 @@
+/*
+ * Copyright 2000-2012 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.ide.util.projectWizard;
+
+import com.intellij.ide.IdeBundle;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.project.ProjectBundle;
+import com.intellij.openapi.project.ProjectManager;
+import com.intellij.openapi.projectRoots.ProjectJdkTable;
+import com.intellij.openapi.projectRoots.Sdk;
+import com.intellij.openapi.projectRoots.SdkType;
+import com.intellij.openapi.projectRoots.ui.ProjectJdksEditor;
+import com.intellij.openapi.roots.ProjectRootManager;
+import com.intellij.openapi.roots.ui.configuration.ProjectStructureConfigurable;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.ProjectSdksModel;
+import com.intellij.openapi.ui.DialogWrapper;
+import com.intellij.openapi.util.Comparing;
+import com.intellij.openapi.wm.ex.WindowManagerEx;
+import com.intellij.ui.ClickListener;
+import com.intellij.ui.DoubleClickListener;
+import com.intellij.ui.ListScrollingUtil;
+import com.intellij.ui.ScrollPaneFactory;
+import com.intellij.ui.components.JBList;
+import com.intellij.util.ArrayUtil;
+import gnu.trove.TIntArrayList;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import javax.swing.event.ListSelectionEvent;
+import javax.swing.event.ListSelectionListener;
+import java.awt.*;
+import java.awt.event.ActionEvent;
+import java.awt.event.MouseEvent;
+import java.util.*;
+
+public class JdkChooserPanel extends JPanel {
+ private JList myList = null;
+ private DefaultListModel myListModel = null;
+ private Sdk myCurrentJdk;
+ @Nullable private final Project myProject;
+ private SdkType[] myAllowedJdkTypes = null;
+
+ public JdkChooserPanel(@Nullable final Project project) {
+ super(new BorderLayout());
+ myProject = project;
+ myListModel = new DefaultListModel();
+ myList = new JBList(myListModel);
+ myList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
+ myList.setCellRenderer(new ProjectJdkListRenderer());
+
+ myList.addListSelectionListener(new ListSelectionListener() {
+ public void valueChanged(ListSelectionEvent e) {
+ myCurrentJdk = (Sdk)myList.getSelectedValue();
+ }
+ });
+ new ClickListener() {
+ @Override
+ public boolean onClick(MouseEvent e, int clickCount) {
+ if (myProject == null) {
+ editJdkTable();
+ }
+ return true;
+ }
+ }.installOn(myList);
+
+ JPanel panel = new JPanel(new BorderLayout());
+ panel.add(ScrollPaneFactory.createScrollPane(myList), BorderLayout.CENTER);
+ add(panel, BorderLayout.CENTER);
+ if (myListModel.getSize() > 0) {
+ myList.setSelectedIndex(0);
+ }
+ }
+
+ /**
+ * Sets the JDK types which may be shown in the panel.
+ *
+ * @param allowedJdkTypes the array of JDK types which may be shown, or null if all JDK types are allowed.
+ * @since 7.0.3
+ */
+ public void setAllowedJdkTypes(@Nullable final SdkType[] allowedJdkTypes) {
+ myAllowedJdkTypes = allowedJdkTypes;
+ }
+
+ public Sdk getChosenJdk() {
+ return myCurrentJdk;
+ }
+
+ public Object[] getAllJdks() {
+ return myListModel.toArray();
+ }
+
+ public void editJdkTable() {
+ ProjectJdksEditor editor = new ProjectJdksEditor((Sdk)myList.getSelectedValue(),
+ myProject != null ? myProject : ProjectManager.getInstance().getDefaultProject(),
+ myList);
+ editor.show();
+ if (editor.isOK()) {
+ Sdk selectedJdk = editor.getSelectedJdk();
+ updateList(selectedJdk, null);
+ }
+ }
+
+ public void updateList(final Sdk selectedJdk, final @Nullable SdkType type) {
+ updateList(selectedJdk, type, null);
+ }
+
+ public void updateList(final Sdk selectedJdk, final @Nullable SdkType type, final @Nullable Sdk[] globalSdks) {
+ final int[] selectedIndices = myList.getSelectedIndices();
+ fillList(type, globalSdks);
+ // restore selection
+ if (selectedJdk != null) {
+ TIntArrayList list = new TIntArrayList();
+ for (int i = 0; i < myListModel.size(); i++) {
+ final Sdk jdk = (Sdk)myListModel.getElementAt(i);
+ if (Comparing.strEqual(jdk.getName(), selectedJdk.getName())){
+ list.add(i);
+ }
+ }
+ final int[] indicesToSelect = list.toNativeArray();
+ if (indicesToSelect.length > 0) {
+ myList.setSelectedIndices(indicesToSelect);
+ }
+ else if (myList.getModel().getSize() > 0) {
+ myList.setSelectedIndex(0);
+ }
+ }
+ else {
+ if (selectedIndices.length > 0) {
+ myList.setSelectedIndices(selectedIndices);
+ }
+ else {
+ myList.setSelectedIndex(0);
+ }
+ }
+
+ myCurrentJdk = (Sdk)myList.getSelectedValue();
+ }
+
+ public JList getPreferredFocusedComponent() {
+ return myList;
+ }
+
+ public void fillList(final @Nullable SdkType type, final @Nullable Sdk[] globalSdks) {
+ myListModel.clear();
+ final Sdk[] jdks;
+ if (myProject == null || myProject.isDefault()) {
+ final Sdk[] allJdks = globalSdks != null ? globalSdks : ProjectJdkTable.getInstance().getAllJdks();
+ jdks = getCompatibleJdks(type, Arrays.asList(allJdks));
+ }
+ else {
+ final ProjectSdksModel projectJdksModel = ProjectStructureConfigurable.getInstance(myProject).getProjectJdksModel();
+ if (!projectJdksModel.isInitialized()){ //should be initialized
+ projectJdksModel.reset(myProject);
+ }
+ final Collection<Sdk> collection = projectJdksModel.getProjectSdks().values();
+ jdks = getCompatibleJdks(type, collection);
+ }
+ Arrays.sort(jdks, new Comparator<Sdk>() {
+ public int compare(final Sdk o1, final Sdk o2) {
+ return o1.getName().compareToIgnoreCase(o2.getName());
+ }
+ });
+ for (Sdk jdk : jdks) {
+ myListModel.addElement(jdk);
+ }
+ }
+
+ private Sdk[] getCompatibleJdks(final @Nullable SdkType type, final Collection<Sdk> collection) {
+ final Set<Sdk> compatibleJdks = new HashSet<Sdk>();
+ for (Sdk projectJdk : collection) {
+ if (isCompatibleJdk(projectJdk, type)) {
+ compatibleJdks.add(projectJdk);
+ }
+ }
+ return compatibleJdks.toArray(new Sdk[compatibleJdks.size()]);
+ }
+
+ private boolean isCompatibleJdk(final Sdk projectJdk, final @Nullable SdkType type) {
+ if (type != null) {
+ return projectJdk.getSdkType() == type;
+ }
+ if (myAllowedJdkTypes != null) {
+ return ArrayUtil.indexOf(myAllowedJdkTypes, projectJdk.getSdkType()) >= 0;
+ }
+ return true;
+ }
+
+ public JComponent getDefaultFocusedComponent() {
+ return myList;
+ }
+
+ public void selectJdk(Sdk defaultJdk) {
+ final int index = myListModel.indexOf(defaultJdk);
+ if (index >= 0) {
+ myList.setSelectedIndex(index);
+ }
+ }
+
+ public void addSelectionListener(final ListSelectionListener listener) {
+ myList.addListSelectionListener(listener);
+ }
+
+ private static Sdk showDialog(final Project project, String title, final Component parent, Sdk jdkToSelect) {
+ final JdkChooserPanel jdkChooserPanel = new JdkChooserPanel(project);
+ jdkChooserPanel.fillList(null, null);
+ final MyDialog dialog = jdkChooserPanel.new MyDialog(parent);
+ if (title != null) {
+ dialog.setTitle(title);
+ }
+ if (jdkToSelect != null) {
+ jdkChooserPanel.selectJdk(jdkToSelect);
+ } else {
+ ListScrollingUtil.ensureSelectionExists(jdkChooserPanel.myList);
+ }
+ new DoubleClickListener() {
+ @Override
+ protected boolean onDoubleClick(MouseEvent e) {
+ dialog.clickDefaultButton();
+ return true;
+ }
+ }.installOn(jdkChooserPanel.myList);
+ dialog.show();
+ return dialog.isOK() ? jdkChooserPanel.getChosenJdk() : null;
+ }
+
+ public static Sdk chooseAndSetJDK(final Project project) {
+ final Sdk projectJdk = ProjectRootManager.getInstance(project).getProjectSdk();
+ final Sdk jdk = showDialog(project, ProjectBundle.message("module.libraries.target.jdk.select.title"), WindowManagerEx.getInstanceEx().getFrame(project), projectJdk);
+ if (jdk == null) {
+ return null;
+ }
+ ApplicationManager.getApplication().runWriteAction(new Runnable() {
+ public void run() {
+ ProjectRootManager.getInstance(project).setProjectSdk(jdk);
+ }
+ });
+ return jdk;
+ }
+
+ public class MyDialog extends DialogWrapper implements ListSelectionListener {
+
+ public MyDialog(Component parent) {
+ super(parent, true);
+ setTitle(IdeBundle.message("title.select.jdk"));
+ init();
+ myList.addListSelectionListener(this);
+ updateOkButton();
+ }
+
+ protected String getDimensionServiceKey() {
+ return "#com.intellij.ide.util.projectWizard.JdkChooserPanel.MyDialog";
+ }
+
+ public void valueChanged(ListSelectionEvent e) {
+ updateOkButton();
+ }
+
+ private void updateOkButton() {
+ setOKActionEnabled(myList.getSelectedValue() != null);
+ }
+
+ public void dispose() {
+ myList.removeListSelectionListener(this);
+ super.dispose();
+ }
+
+ protected JComponent createCenterPanel() {
+ return JdkChooserPanel.this;
+ }
+
+ protected Action[] createActions() {
+ return new Action[]{new ConfigureAction(), getOKAction(), getCancelAction()};
+ }
+
+ public JComponent getPreferredFocusedComponent() {
+ return myList;
+ }
+
+ private final class ConfigureAction extends AbstractAction {
+ public ConfigureAction() {
+ super(IdeBundle.message("button.configure.e"));
+ putValue(Action.MNEMONIC_KEY, new Integer('E'));
+ }
+
+ public void actionPerformed(ActionEvent e) {
+ editJdkTable();
+ }
+ }
+ }
+
+
+}
diff --git a/java/idea-ui/src/com/intellij/ide/util/projectWizard/ModuleImportBuilder.java b/java/idea-ui/src/com/intellij/ide/util/projectWizard/ModuleImportBuilder.java
new file mode 100644
index 0000000..2d84bbb
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/ide/util/projectWizard/ModuleImportBuilder.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2000-2012 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.ide.util.projectWizard;
+
+import com.intellij.ide.util.newProjectWizard.modes.ImportImlMode;
+import com.intellij.openapi.module.ModifiableModuleModel;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.options.ConfigurationException;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.roots.ui.configuration.ModulesProvider;
+import com.intellij.packaging.artifacts.ModifiableArtifactModel;
+import com.intellij.projectImport.ProjectImportBuilder;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import java.util.List;
+
+/**
+ * @author Dmitry Avdeev
+ * Date: 10/31/12
+ */
+public class ModuleImportBuilder extends ProjectImportBuilder {
+
+ @NotNull
+ @Override
+ public String getName() {
+ return "Add IDEA module";
+ }
+
+ @Override
+ public Icon getIcon() {
+ return null;
+ }
+
+ @Override
+ public List getList() {
+ return null;
+ }
+
+ @Override
+ public boolean isMarked(Object element) {
+ return false;
+ }
+
+ @Override
+ public void setList(List list) throws ConfigurationException {
+ }
+
+ @Override
+ public void setOpenProjectSettingsAfter(boolean on) {
+ }
+
+ @Nullable
+ @Override
+ public List<Module> commit(Project project,
+ ModifiableModuleModel model,
+ ModulesProvider modulesProvider,
+ ModifiableArtifactModel artifactModel) {
+
+ return ImportImlMode.setUpLoader(getFileToImport()).commit(project, model, modulesProvider);
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/ide/util/projectWizard/ModuleImportProvider.java b/java/idea-ui/src/com/intellij/ide/util/projectWizard/ModuleImportProvider.java
new file mode 100644
index 0000000..df5ab08
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/ide/util/projectWizard/ModuleImportProvider.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2000-2012 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.ide.util.projectWizard;
+
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.projectImport.ProjectImportProvider;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * @author Dmitry Avdeev
+ * Date: 10/31/12
+ */
+public class ModuleImportProvider extends ProjectImportProvider {
+
+ public ModuleImportProvider() {
+ super(new ModuleImportBuilder());
+ }
+
+ @Override
+ public boolean canImport(VirtualFile fileOrDirectory, Project project) {
+ return project != null && !fileOrDirectory.isDirectory() && "iml".equals(fileOrDirectory.getExtension());
+ }
+
+ @Override
+ public String getPathToBeImported(VirtualFile file) {
+ return file.getPath();
+ }
+
+ @Override
+ public boolean canCreateNewProject() {
+ return false;
+ }
+
+ @Override
+ public ModuleWizardStep[] createSteps(WizardContext context) {
+ return ModuleWizardStep.EMPTY_ARRAY;
+ }
+
+ @Nullable
+ @Override
+ public String getFileSample() {
+ return "Intellij IDEA module file (*.iml)";
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/ide/util/projectWizard/NameLocationStep.java b/java/idea-ui/src/com/intellij/ide/util/projectWizard/NameLocationStep.java
new file mode 100644
index 0000000..3fd71a1
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/ide/util/projectWizard/NameLocationStep.java
@@ -0,0 +1,290 @@
+/*
+ * Copyright 2000-2009 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.ide.util.projectWizard;
+
+import com.intellij.ide.IdeBundle;
+import com.intellij.ide.highlighter.ModuleFileType;
+import com.intellij.ide.util.BrowseFilesListener;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.module.ModuleType;
+import com.intellij.openapi.roots.ModuleRootModel;
+import com.intellij.openapi.roots.ui.configuration.ModulesProvider;
+import com.intellij.openapi.ui.Messages;
+import com.intellij.openapi.ui.MultiLineLabelUI;
+import com.intellij.openapi.vfs.VfsUtil;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.ui.DocumentAdapter;
+import com.intellij.ui.FieldPanel;
+import org.jetbrains.annotations.NonNls;
+
+import javax.swing.*;
+import javax.swing.event.DocumentEvent;
+import javax.swing.event.DocumentListener;
+import java.awt.*;
+import java.awt.event.ActionEvent;
+import java.io.File;
+
+/**
+ * @author Eugene Zhuravlev
+ * Date: Dec 29, 2003
+ */
+public class NameLocationStep extends ModuleWizardStep {
+ private static final Logger LOG = Logger.getInstance("#com.intellij.ide.util.projectWizard.NameLocationStep");
+ private final JPanel myPanel;
+ private final NamePathComponent myNamePathComponent;
+ private final WizardContext myWizardContext;
+ private final JavaModuleBuilder myBuilder;
+ private final ModulesProvider myModulesProvider;
+ private final Icon myIcon;
+ private final String myHelpId;
+ private boolean myModuleFileDirectoryChangedByUser = false;
+ private final JTextField myTfModuleFilePath;
+ private boolean myFirstTimeInitializationDone = false;
+
+ public NameLocationStep(WizardContext wizardContext, JavaModuleBuilder builder,
+ ModulesProvider modulesProvider,
+ Icon icon,
+ @NonNls String helpId) {
+ myWizardContext = wizardContext;
+ myBuilder = builder;
+ myModulesProvider = modulesProvider;
+ myIcon = icon;
+ myHelpId = helpId;
+ myPanel = new JPanel(new GridBagLayout());
+ myPanel.setBorder(BorderFactory.createEtchedBorder());
+
+ final String text = IdeBundle.message("prompt.please.specify.module.name.and.content.root");
+ final JLabel textLabel = new JLabel(text);
+ textLabel.setUI(new MultiLineLabelUI());
+ myPanel.add(textLabel, new GridBagConstraints(0, GridBagConstraints.RELATIVE, 2, 1, 1.0, 0.0, GridBagConstraints.NORTHWEST, GridBagConstraints.NONE, new Insets(8, 10, 8, 10), 0, 0));
+
+ myNamePathComponent = new NamePathComponent(IdeBundle.message("label.module.name"), IdeBundle.message("label.module.content.root"), 'M', 'r',
+ IdeBundle.message("title.select.module.content.root"), "");
+ //if (ModuleType.J2EE_APPLICATION.equals(moduleType)) {
+ // myNamePathComponent.setPathComponentVisible(false);
+ //}
+
+ myPanel.add(myNamePathComponent, new GridBagConstraints(0, GridBagConstraints.RELATIVE, 2, 1, 1.0, 0.0, GridBagConstraints.NORTHWEST, GridBagConstraints.HORIZONTAL, new Insets(8, 10, 0, 10), 0, 0));
+
+ final JLabel label = new JLabel(IdeBundle.message("label.module.file.will.be.saved.in"));
+ myPanel.add(label, new GridBagConstraints(0, GridBagConstraints.RELATIVE, 2, 1, 1.0, 1.0, GridBagConstraints.SOUTHWEST, GridBagConstraints.HORIZONTAL, new Insets(30, 10, 0, 10), 0, 0));
+
+ myTfModuleFilePath = new JTextField();
+ myTfModuleFilePath.setEditable(false);
+ final Insets borderInsets = myTfModuleFilePath.getBorder().getBorderInsets(myTfModuleFilePath);
+ myTfModuleFilePath.setBorder(BorderFactory.createEmptyBorder(borderInsets.top, borderInsets.left, borderInsets.bottom, borderInsets.right));
+ final FieldPanel fieldPanel = createFieldPanel(myTfModuleFilePath, null, null);
+ myPanel.add(fieldPanel, new GridBagConstraints(0, GridBagConstraints.RELATIVE, 1, 1, 1.0, 0.0, GridBagConstraints.NORTHWEST, GridBagConstraints.HORIZONTAL, new Insets(0, 10, 10, 0), 0, 0));
+
+ JButton browseButton = new JButton(IdeBundle.message("button.change.directory"));
+ browseButton.addActionListener(new BrowseModuleFileDirectoryListener(myTfModuleFilePath));
+ myPanel.add(browseButton, new GridBagConstraints(1, GridBagConstraints.RELATIVE, 1, 1, 0.0, 0.0, GridBagConstraints.NORTHWEST, GridBagConstraints.NONE, new Insets(0, 0, 10, 10), 0, 0));
+
+ final DocumentListener documentListener = new DocumentAdapter() {
+ protected void textChanged(DocumentEvent e) {
+ updateModuleFilePathField();
+ }
+ };
+ myNamePathComponent.getNameComponent().getDocument().addDocumentListener(documentListener);
+ myNamePathComponent.getPathComponent().getDocument().addDocumentListener(documentListener);
+ myNamePathComponent.getPathComponent().getDocument().addDocumentListener(new DocumentAdapter() {
+ public void textChanged(DocumentEvent event) {
+ myWizardContext.requestWizardButtonsUpdate();
+ }
+ });
+ }
+
+ @SuppressWarnings({"HardCodedStringLiteral"})
+ private String suggestModuleName(ModuleType moduleType) {
+ return "untitled";
+ }
+
+ public void updateStep() {
+ super.updateStep();
+
+ // initial UI settings
+ if (!myFirstTimeInitializationDone) {
+ if (myWizardContext.isCreatingNewProject()) {
+ setSyncEnabled(false);
+ }
+ else { // project already exists
+ final VirtualFile baseDir = myWizardContext.getProject().getBaseDir();
+ final String projectFileDirectory = baseDir != null ? VfsUtil.virtualToIoFile(baseDir).getPath() : null;
+ if (projectFileDirectory != null) {
+ final String name = ProjectWizardUtil.findNonExistingFileName(projectFileDirectory, suggestModuleName(myBuilder.getModuleType()), "");
+ setModuleName(name);
+ setContentEntryPath(projectFileDirectory + File.separatorChar + name);
+ }
+ }
+ }
+
+ if (myWizardContext.isCreatingNewProject()) { // creating new project
+ // in this mode we depend on the settings of the "project name" step
+ if (!isPathChangedByUser()) {
+ setContentEntryPath(myWizardContext.getProjectFileDirectory().replace('/', File.separatorChar));
+ }
+ if (!isNameChangedByUser()) {
+ setModuleName(myWizardContext.getProjectName());
+ }
+ }
+
+ if (!myFirstTimeInitializationDone) {
+ myNamePathComponent.getNameComponent().selectAll();
+ }
+ myFirstTimeInitializationDone = true;
+ }
+
+ public JComponent getComponent() {
+ return myPanel;
+ }
+
+ public String getModuleName() {
+ return myNamePathComponent.getNameValue();
+ }
+
+ public void setModuleName(String name) {
+ myNamePathComponent.setNameValue(name);
+ }
+
+ public String getContentEntryPath() {
+ final String path = myNamePathComponent.getPath();
+ return path.length() == 0? null : path;
+ }
+
+ public void setContentEntryPath(String path) {
+ myNamePathComponent.setPath(path);
+ }
+
+ public String getModuleFilePath() {
+ return getModuleFileDirectory() + "/" + myNamePathComponent.getNameValue() + ModuleFileType.DOT_DEFAULT_EXTENSION;
+ }
+
+ public boolean isSyncEnabled() {
+ return myNamePathComponent.isSyncEnabled();
+ }
+
+ public void setSyncEnabled(boolean isSyncEnabled) {
+ myNamePathComponent.setSyncEnabled(isSyncEnabled);
+ }
+
+ public String getModuleFileDirectory() {
+ return myTfModuleFilePath.getText().trim().replace(File.separatorChar, '/');
+ }
+
+ public boolean validate() {
+ final String moduleName = getModuleName();
+ if (moduleName.length() == 0) {
+ Messages.showErrorDialog(myNamePathComponent.getNameComponent(), IdeBundle.message("prompt.please.specify.module.name"),
+ IdeBundle.message("title.module.name.not.specified"));
+ return false;
+ }
+ if (isAlreadyExists(moduleName)) {
+ Messages.showErrorDialog(IdeBundle.message("error.module.with.name.already.exists", moduleName), IdeBundle.message("title.module.already.exists"));
+ return false;
+ }
+ final String moduleLocation = getModuleFileDirectory();
+ if (moduleLocation.length() == 0) {
+ Messages.showErrorDialog(myNamePathComponent.getPathComponent(), IdeBundle.message("error.please.specify.module.file.location"),
+ IdeBundle.message("title.module.file.location.not.specified"));
+ return false;
+ }
+
+ final String contentEntryPath = getContentEntryPath();
+ if (contentEntryPath != null) {
+ // the check makes sence only for non-null module root
+ Module[] modules = myModulesProvider.getModules();
+ for (final Module module : modules) {
+ ModuleRootModel rootModel = myModulesProvider.getRootModel(module);
+ LOG.assertTrue(rootModel != null);
+ final VirtualFile[] moduleContentRoots = rootModel.getContentRoots();
+ final String moduleContentRoot = contentEntryPath.replace(File.separatorChar, '/');
+ for (final VirtualFile root : moduleContentRoots) {
+ if (moduleContentRoot.equals(root.getPath())) {
+ Messages.showErrorDialog(myNamePathComponent.getPathComponent(),
+ IdeBundle.message("error.content.root.already.defined.for.module", contentEntryPath, module.getName()),
+ IdeBundle.message("title.module.content.root.already.exists"));
+ return false;
+ }
+ }
+ }
+ if (!ProjectWizardUtil.createDirectoryIfNotExists(IdeBundle.message("directory.module.content.root"), contentEntryPath, myNamePathComponent.isPathChangedByUser())) {
+ return false;
+ }
+ }
+ final String moduleFileDirectory = getModuleFileDirectory();
+ return ProjectWizardUtil.createDirectoryIfNotExists(IdeBundle.message("directory.module.file"), moduleFileDirectory, myModuleFileDirectoryChangedByUser);
+ }
+
+ public void updateDataModel() {
+ myBuilder.setName(getModuleName());
+ myBuilder.setModuleFilePath(getModuleFilePath());
+ myBuilder.setContentEntryPath(getContentEntryPath());
+ }
+
+ public JComponent getPreferredFocusedComponent() {
+ return myNamePathComponent.getNameComponent();
+ }
+
+ private boolean isAlreadyExists(String moduleName) {
+ final Module[] modules = myModulesProvider.getModules();
+ for (Module module : modules) {
+ if (moduleName.equals(module.getName())) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private void updateModuleFilePathField() {
+ if (!myModuleFileDirectoryChangedByUser) {
+ final String dir = myNamePathComponent.getPath().replace('/', File.separatorChar);
+ myTfModuleFilePath.setText(dir);
+ }
+ }
+
+ public boolean isNameChangedByUser() {
+ return myNamePathComponent.isNameChangedByUser();
+ }
+
+ public boolean isPathChangedByUser() {
+ return myNamePathComponent.isPathChangedByUser();
+ }
+
+ private class BrowseModuleFileDirectoryListener extends BrowseFilesListener {
+ private BrowseModuleFileDirectoryListener(final JTextField textField) {
+ super(textField, IdeBundle.message("title.select.module.file.location"),
+ IdeBundle.message("description.select.module.file.location"), SINGLE_DIRECTORY_DESCRIPTOR);
+ }
+
+ public void actionPerformed(ActionEvent e) {
+ final String pathBefore = myTfModuleFilePath.getText().trim();
+ super.actionPerformed(e);
+ final String path = myTfModuleFilePath.getText().trim();
+ if (!path.equals(pathBefore)) {
+ myModuleFileDirectoryChangedByUser = true;
+ }
+ }
+ }
+
+ public Icon getIcon() {
+ return myIcon;
+ }
+
+ public String getHelpId() {
+ return myHelpId;
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/ide/util/projectWizard/NamePathComponent.java b/java/idea-ui/src/com/intellij/ide/util/projectWizard/NamePathComponent.java
new file mode 100644
index 0000000..47889b0
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/ide/util/projectWizard/NamePathComponent.java
@@ -0,0 +1,353 @@
+/*
+ * Copyright 2000-2009 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.ide.util.projectWizard;
+
+import com.intellij.ide.IdeBundle;
+import com.intellij.ide.highlighter.ProjectFileType;
+import com.intellij.ide.util.BrowseFilesListener;
+import com.intellij.openapi.application.ApplicationInfo;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.fileChooser.FileChooserDescriptor;
+import com.intellij.openapi.options.ConfigurationException;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.Messages;
+import com.intellij.ui.DocumentAdapter;
+import com.intellij.ui.FieldPanel;
+import com.intellij.util.ui.UIUtil;
+import org.jetbrains.annotations.NotNull;
+
+import javax.swing.*;
+import javax.swing.event.DocumentEvent;
+import javax.swing.text.AttributeSet;
+import javax.swing.text.BadLocationException;
+import javax.swing.text.PlainDocument;
+import java.awt.*;
+import java.awt.event.ActionEvent;
+import java.io.File;
+
+/**
+ * @author Eugene Zhuravlev
+ * Date: Dec 30, 2003
+ */
+public class NamePathComponent extends JPanel{
+ private static final Logger LOG = Logger.getInstance("#com.intellij.ide.util.projectWizard.NamePathComponent");
+
+ private JTextField myTfName;
+ private JTextField myTfPath;
+ private boolean myIsNameChangedByUser = false;
+ private boolean myIsPathChangedByUser = false;
+ private boolean myIsPathNameSyncEnabled = true;
+ private boolean myIsNamePathSyncEnabled = true;
+ private boolean myIsSyncEnabled = true;
+
+ private FieldPanel myPathPanel;
+ private JLabel myNameLabel;
+ private JLabel myPathLabel;
+ private boolean myForceSync;
+
+ public NamePathComponent(String nameLabelText, String pathLabelText, char nameMnemonic, char locationMnemonic, final String pathChooserTitle, final String pathChooserDescription) {
+ this(nameLabelText, pathLabelText, pathChooserTitle, pathChooserDescription, true);
+ }
+
+ public NamePathComponent(String nameLabelText,
+ String pathLabelText,
+ final String pathChooserTitle,
+ final String pathChooserDescription,
+ boolean hideIgnored) {
+ this(nameLabelText, pathLabelText, pathChooserTitle, pathChooserDescription, hideIgnored, true);
+ }
+
+ public NamePathComponent(String nameLabelText,
+ String pathLabelText,
+ final String pathChooserTitle,
+ final String pathChooserDescription,
+ boolean hideIgnored,
+ boolean bold) {
+ super(new GridBagLayout());
+
+ myTfName = new JTextField();
+ myTfName.setDocument(new NameFieldDocument());
+ myTfName.setPreferredSize(new Dimension(200, myTfName.getPreferredSize().height));
+
+ myTfPath = new JTextField();
+ myTfPath.setDocument(new PathFieldDocument());
+ myTfPath.setPreferredSize(new Dimension(200, myTfPath.getPreferredSize().height));
+
+ myNameLabel = new JLabel(nameLabelText);
+ if (bold) myNameLabel.setFont(UIUtil.getLabelFont().deriveFont(Font.BOLD));
+ myNameLabel.setLabelFor(myTfName);
+ Insets insets = new Insets(0, 0, 5, 4);
+ this.add(myNameLabel, new GridBagConstraints(0, GridBagConstraints.RELATIVE, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.NONE,
+ insets, 0, 0));
+
+ insets = new Insets(0, 0, 5, 0);
+ this.add(myTfName, new GridBagConstraints(1, GridBagConstraints.RELATIVE, 1, 1, 1.0, 0.0, GridBagConstraints.NORTHWEST, GridBagConstraints.HORIZONTAL,
+ insets, 0, 0));
+ // todo: review texts
+ final FileChooserDescriptor chooserDescriptor = (FileChooserDescriptor)BrowseFilesListener.SINGLE_DIRECTORY_DESCRIPTOR.clone();
+ chooserDescriptor.setHideIgnored(hideIgnored);
+ final BrowseFilesListener browseButtonActionListener = new BrowseFilesListener(myTfPath, pathChooserTitle, pathChooserDescription, chooserDescriptor) {
+ public void actionPerformed(ActionEvent e) {
+ super.actionPerformed(e);
+ myIsPathChangedByUser = true;
+ }
+ };
+ myPathPanel = new FieldPanel(myTfPath, null, null, browseButtonActionListener, null);
+ myPathLabel = new JLabel(pathLabelText);
+ myPathLabel.setLabelFor(myTfPath);
+ if (bold) myPathLabel.setFont(UIUtil.getLabelFont().deriveFont(Font.BOLD));
+ insets = new Insets(0, 0, 5, 4);
+ this.add(myPathLabel, new GridBagConstraints(0, GridBagConstraints.RELATIVE, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.NONE,
+ insets, 0, 0));
+ insets = new Insets(0, 0, 5, 0);
+ this.add(myPathPanel, new GridBagConstraints(1, GridBagConstraints.RELATIVE, 1, 1, 1.0, 0.0, GridBagConstraints.NORTHWEST, GridBagConstraints.HORIZONTAL,
+ insets, 0, 0));
+ }
+
+ private String getProjectFilePath(boolean isDefault) {
+ if (isDefault) {
+ return getPath() + "/" + getNameValue() + ProjectFileType.DOT_DEFAULT_EXTENSION;
+ }
+ else {
+ return getPath() + "/" + Project.DIRECTORY_STORE_FOLDER;
+ }
+ }
+
+ public boolean validateNameAndPath(WizardContext context, boolean defaultFormat) throws
+ ConfigurationException {
+ final String name = getNameValue();
+ if (name.length() == 0) {
+ final ApplicationInfo info = ApplicationManager.getApplication().getComponent(ApplicationInfo.class);
+ throw new ConfigurationException(
+ IdeBundle.message("prompt.new.project.file.name", info.getVersionName(), context.getPresentationName()));
+ }
+
+ final String projectFileDirectory = getPath();
+ if (projectFileDirectory.length() == 0) {
+ throw new ConfigurationException(IdeBundle.message("prompt.enter.project.file.location", context.getPresentationName()));
+ }
+
+ final boolean shouldPromptCreation = isPathChangedByUser();
+ if (!ProjectWizardUtil
+ .createDirectoryIfNotExists(IdeBundle.message("directory.project.file.directory", context.getPresentationName()),
+ projectFileDirectory, shouldPromptCreation)) {
+ return false;
+ }
+
+ final File file = new File(projectFileDirectory);
+ if (file.exists() && !file.canWrite()) {
+ throw new ConfigurationException(String.format("Directory '%s' is not writable!\nPlease choose another project location.", projectFileDirectory));
+ }
+
+ boolean shouldContinue = true;
+ final File projectFile = new File(getProjectFilePath(defaultFormat));
+ if (projectFile.exists()) {
+ int answer = Messages.showYesNoDialog(
+ IdeBundle.message("prompt.overwrite.project.file", projectFile.getAbsolutePath(), context.getPresentationName()),
+ IdeBundle.message("title.file.already.exists"), Messages.getQuestionIcon());
+ shouldContinue = (answer == 0);
+ }
+
+ return shouldContinue;
+ }
+
+ public String getNameValue() {
+ return myTfName.getText().trim();
+ }
+
+ public void setNameValue(String name) {
+ final boolean isNameChangedByUser = myIsNameChangedByUser;
+ setNamePathSyncEnabled(false);
+ try {
+ myTfName.setText(name);
+ }
+ finally {
+ myIsNameChangedByUser = isNameChangedByUser;
+ setNamePathSyncEnabled(true);
+ }
+ }
+
+ public String getPath() {
+ return myTfPath.getText().trim().replace(File.separatorChar, '/');
+ }
+
+ public void setPath(String path) {
+ final boolean isPathChangedByUser = myIsPathChangedByUser;
+ setPathNameSyncEnabled(false);
+ try {
+ myTfPath.setText(path);
+ }
+ finally {
+ myIsPathChangedByUser = isPathChangedByUser;
+ setPathNameSyncEnabled(true);
+ }
+ }
+
+ public JTextField getNameComponent() {
+ return myTfName;
+ }
+
+ @NotNull
+ public JLabel getPathLabel() {
+ return myPathLabel;
+ }
+
+ public JTextField getPathComponent() {
+ return myTfPath;
+ }
+
+ @NotNull
+ public FieldPanel getPathPanel() {
+ return myPathPanel;
+ }
+
+ public void setPathComponentVisible(boolean visible) {
+ myPathPanel.setVisible(visible);
+ }
+
+ public void setNameComponentVisible(boolean visible) {
+ myTfName.setVisible(visible);
+ myNameLabel.setVisible(visible);
+ }
+
+ public boolean isNameChangedByUser() {
+ return myIsNameChangedByUser;
+ }
+
+ public boolean isPathChangedByUser() {
+ return myIsPathChangedByUser;
+ }
+
+ public boolean isSyncEnabled() {
+ return myIsSyncEnabled;
+ }
+
+ public void setSyncEnabled(boolean isSyncEnabled) {
+ myIsSyncEnabled = isSyncEnabled;
+ }
+
+ private boolean isPathNameSyncEnabled() {
+ if (!isSyncEnabled()) {
+ return false;
+ }
+ return myIsPathNameSyncEnabled;
+ }
+
+ private void setPathNameSyncEnabled(boolean isPathNameSyncEnabled) {
+ myIsPathNameSyncEnabled = isPathNameSyncEnabled;
+ }
+
+ private boolean isNamePathSyncEnabled() {
+ if (!isSyncEnabled()) {
+ return false;
+ }
+ return myIsNamePathSyncEnabled;
+ }
+
+ private void setNamePathSyncEnabled(boolean isNamePathSyncEnabled) {
+ myIsNamePathSyncEnabled = isNamePathSyncEnabled;
+ }
+
+ public void syncNameToPath(boolean b) {
+ myForceSync = b;
+ if (b) ((PathFieldDocument)myTfPath.getDocument()).syncPathAndName();
+ }
+
+ public void addChangeListener(final Runnable callback) {
+ DocumentAdapter adapter = new DocumentAdapter() {
+ @Override
+ protected void textChanged(DocumentEvent e) {
+ callback.run();
+ }
+ };
+ myTfName.getDocument().addDocumentListener(adapter);
+ myTfPath.getDocument().addDocumentListener(adapter);
+ }
+
+ private class NameFieldDocument extends PlainDocument {
+ public NameFieldDocument() {
+ addDocumentListener(new DocumentAdapter() {
+ public void textChanged(DocumentEvent event) {
+ myIsNameChangedByUser = true;
+ syncNameAndPath();
+ }
+ });
+ }
+
+ public void insertString(int offs, String str, AttributeSet a) throws BadLocationException {
+ boolean ok = true;
+ for (int idx = 0; idx < str.length() && ok; idx++) {
+ char ch = str.charAt(idx);
+ ok = ch != File.separatorChar && ch != '\\' && ch != '/' && ch != '|' && ch != ':';
+ }
+ if (ok) {
+ super.insertString(offs, str, a);
+ }
+ }
+
+ private void syncNameAndPath() {
+ if (isNamePathSyncEnabled() && (myForceSync || !myIsPathChangedByUser)) {
+ try {
+ setPathNameSyncEnabled(false);
+ final String name = getText(0, getLength());
+ final String path = myTfPath.getText().trim();
+ final int lastSeparatorIndex = path.lastIndexOf(File.separator);
+ if (lastSeparatorIndex >= 0) {
+ setPath(path.substring(0, lastSeparatorIndex + 1) + name);
+ }
+ }
+ catch (BadLocationException e) {
+ LOG.error(e);
+ }
+ finally {
+ setPathNameSyncEnabled(true);
+ }
+ }
+ }
+ }
+
+ private class PathFieldDocument extends PlainDocument {
+ public PathFieldDocument() {
+ addDocumentListener(new DocumentAdapter() {
+ public void textChanged(DocumentEvent event) {
+ myIsPathChangedByUser = true;
+ syncPathAndName();
+ }
+ });
+ }
+
+ private void syncPathAndName() {
+ if (isPathNameSyncEnabled() && (myForceSync || !myIsNameChangedByUser)) {
+ try {
+ setNamePathSyncEnabled(false);
+ final String path = getText(0, getLength());
+ final int lastSeparatorIndex = path.lastIndexOf(File.separator);
+ if (lastSeparatorIndex >= 0 && (lastSeparatorIndex + 1) < path.length()) {
+ setNameValue(path.substring(lastSeparatorIndex + 1));
+ }
+ }
+ catch (BadLocationException e) {
+ LOG.error(e);
+ }
+ finally {
+ setNamePathSyncEnabled(true);
+ }
+ }
+ }
+ }
+
+}
diff --git a/java/idea-ui/src/com/intellij/ide/util/projectWizard/OutputPathsStep.java b/java/idea-ui/src/com/intellij/ide/util/projectWizard/OutputPathsStep.java
new file mode 100644
index 0000000..55c67a4
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/ide/util/projectWizard/OutputPathsStep.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright 2000-2009 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.ide.util.projectWizard;
+
+import com.intellij.ide.IdeBundle;
+import com.intellij.openapi.project.ProjectBundle;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.util.ui.UIUtil;
+import org.jetbrains.annotations.NonNls;
+
+import javax.swing.*;
+import java.awt.*;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.io.File;
+
+/**
+ * @author Eugene Zhuravlev
+ * Date: Jan 22, 2004
+ */
+public class OutputPathsStep extends ModuleWizardStep{
+ private final JavaModuleBuilder myDescriptor;
+ private final Icon myIcon;
+ private final String myHelpId;
+ private final NameLocationStep myNameLocationStep;
+ private final JPanel myPanel;
+ private final NamePathComponent myNamePathComponent;
+ private final JRadioButton myRbInheritProjectOutput = new JRadioButton(ProjectBundle.message("project.inherit.compile.output.path"));
+ private final JRadioButton myRbPerModuleOutput = new JRadioButton(ProjectBundle.message("project.module.compile.output.path"));
+
+ public OutputPathsStep(NameLocationStep nameLocationStep, JavaModuleBuilder descriptor, Icon icon, @NonNls String helpId) {
+ myDescriptor = descriptor;
+ myIcon = icon;
+ myHelpId = helpId;
+ myNameLocationStep = nameLocationStep;
+ myNamePathComponent = new NamePathComponent("", IdeBundle.message("label.select.compiler.output.path"), IdeBundle.message("title.select.compiler.output.path"), "", false);
+ myNamePathComponent.setNameComponentVisible(false);
+ myPanel = new JPanel(new GridBagLayout());
+ myPanel.setBorder(BorderFactory.createEtchedBorder());
+ myPanel.add(myRbInheritProjectOutput, new GridBagConstraints(0, GridBagConstraints.RELATIVE, 1, 1, 1.0, 0, GridBagConstraints.NORTHWEST, GridBagConstraints.HORIZONTAL, new Insets(5, 6, 0, 6), 0, 0));
+ myPanel.add(myNamePathComponent, new GridBagConstraints(0, GridBagConstraints.RELATIVE, 1, 1, 1.0, 0, GridBagConstraints.NORTHWEST, GridBagConstraints.HORIZONTAL, new Insets(5, 10, 0, 6), 0, 0));
+ myPanel.add(myRbPerModuleOutput, new GridBagConstraints(0, GridBagConstraints.RELATIVE, 1, 1, 1.0, 1.0, GridBagConstraints.NORTHWEST, GridBagConstraints.HORIZONTAL, new Insets(5, 6, 0, 6), 0, 0));
+
+ final ButtonGroup group = new ButtonGroup();
+ group.add(myRbInheritProjectOutput);
+ group.add(myRbPerModuleOutput);
+ final ActionListener listener = new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ UIUtil.setEnabled(myNamePathComponent, !myRbInheritProjectOutput.isSelected(), true);
+ }
+ };
+ myRbInheritProjectOutput.addActionListener(listener);
+ myRbPerModuleOutput.addActionListener(listener);
+ }
+
+ public JComponent getComponent() {
+ return myPanel;
+ }
+
+ public void updateDataModel() {
+ if (myRbInheritProjectOutput.isSelected()){
+ myDescriptor.setCompilerOutputPath(null);
+ } else {
+ myDescriptor.setCompilerOutputPath(myNamePathComponent.getPath());
+ }
+ }
+
+ public void updateStep() {
+ if (!myNamePathComponent.isPathChangedByUser()) {
+ final String contentEntryPath = myDescriptor.getContentEntryPath();
+ if (contentEntryPath != null) {
+ @NonNls String path = myDescriptor.getPathForOutputPathStep();
+ if (path == null) {
+ path = StringUtil.endsWithChar(contentEntryPath, '/') ? contentEntryPath + "classes" : contentEntryPath + "/classes";
+ }
+ myNamePathComponent.setPath(path.replace('/', File.separatorChar));
+ myNamePathComponent.getPathComponent().selectAll();
+ }
+ }
+ boolean inheritCompileOutput = myDescriptor.getPathForOutputPathStep() == null;
+ myRbInheritProjectOutput.setSelected(inheritCompileOutput);
+ myRbPerModuleOutput.setSelected(!inheritCompileOutput);
+ UIUtil.setEnabled(myNamePathComponent, !inheritCompileOutput, true);
+ }
+
+ public JComponent getPreferredFocusedComponent() {
+ return myNamePathComponent.getPathComponent();
+ }
+
+ public boolean isStepVisible() {
+ return myNameLocationStep.getContentEntryPath() != null;
+ }
+
+ public Icon getIcon() {
+ return myIcon;
+ }
+
+ public String getHelpId() {
+ return myHelpId;
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/ide/util/projectWizard/ProjectJdkForModuleStep.java b/java/idea-ui/src/com/intellij/ide/util/projectWizard/ProjectJdkForModuleStep.java
new file mode 100644
index 0000000..573c2bb
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/ide/util/projectWizard/ProjectJdkForModuleStep.java
@@ -0,0 +1,208 @@
+/*
+ * Copyright 2000-2009 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.ide.util.projectWizard;
+
+import com.intellij.ide.IdeBundle;
+import com.intellij.ide.util.newProjectWizard.AddModuleWizard;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.options.ConfigurationException;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.project.ProjectManager;
+import com.intellij.openapi.project.ex.ProjectManagerEx;
+import com.intellij.openapi.projectRoots.Sdk;
+import com.intellij.openapi.projectRoots.SdkType;
+import com.intellij.openapi.roots.ex.ProjectRootManagerEx;
+import com.intellij.openapi.roots.ui.configuration.ProjectStructureConfigurable;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.JdkListConfigurable;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.ProjectSdksModel;
+import com.intellij.openapi.ui.Messages;
+import com.intellij.openapi.ui.MultiLineLabelUI;
+import com.intellij.util.Consumer;
+import com.intellij.util.ui.UIUtil;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import javax.swing.event.ListSelectionEvent;
+import javax.swing.event.ListSelectionListener;
+import java.awt.*;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+
+/**
+ * @author Eugene Zhuravlev
+ * Date: Jan 21, 2004
+ */
+public class ProjectJdkForModuleStep extends ModuleWizardStep {
+ private final JdkChooserPanel myJdkChooser;
+ private final JPanel myPanel;
+ private final WizardContext myContext;
+ private final SdkType myType;
+ private boolean myInitialized = false;
+ private final JButton mySetAsDefaultButton;
+
+ public ProjectJdkForModuleStep(final WizardContext context, final SdkType type) {
+ myContext = context;
+ myType = type;
+ myJdkChooser = new JdkChooserPanel(getProject(context, type));
+
+ myPanel = new JPanel(new GridBagLayout());
+ myPanel.setBorder(BorderFactory.createEtchedBorder());
+
+ final JLabel label = new JLabel(IdeBundle.message("prompt.please.select.module.jdk", type.getPresentableName()));
+ label.setUI(new MultiLineLabelUI());
+ myPanel.add(label, new GridBagConstraints(0, GridBagConstraints.RELATIVE, 2, 1, 1.0, 0.0, GridBagConstraints.NORTHWEST,
+ GridBagConstraints.HORIZONTAL, new Insets(8, 10, 8, 10), 0, 0));
+
+ final JLabel jdklabel = new JLabel(IdeBundle.message("label.project.jdk"));
+ jdklabel.setFont(UIUtil.getLabelFont().deriveFont(Font.BOLD));
+ myPanel.add(jdklabel, new GridBagConstraints(0, GridBagConstraints.RELATIVE, 2, 1, 1.0, 0.0, GridBagConstraints.NORTHWEST,
+ GridBagConstraints.NONE, new Insets(8, 10, 0, 10), 0, 0));
+
+ myPanel.add(myJdkChooser, new GridBagConstraints(0, GridBagConstraints.RELATIVE, 1, 2, 1.0, 1.0, GridBagConstraints.NORTHWEST,
+ GridBagConstraints.BOTH, new Insets(2, 10, 10, 5), 0, 0));
+ JButton configureButton = new JButton(IdeBundle.message("button.configure"));
+ myPanel.add(configureButton, new GridBagConstraints(1, GridBagConstraints.RELATIVE, 1, 1, 0.0, 0.0, GridBagConstraints.NORTHWEST,
+ GridBagConstraints.NONE, new Insets(2, 0, 5, 5), 0, 0));
+ mySetAsDefaultButton = new JButton("Set Default");
+ mySetAsDefaultButton.setMnemonic('D');
+ myPanel.add(mySetAsDefaultButton, new GridBagConstraints(1, GridBagConstraints.RELATIVE, 1, 1, 0.0, 1.0, GridBagConstraints.NORTHWEST,
+ GridBagConstraints.NONE, new Insets(2, 0, 10, 5), 0, 0));
+
+ configureButton.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+
+ final Project project = getProject(context, type);
+ final ProjectStructureConfigurable projectConfig = ProjectStructureConfigurable.getInstance(project);
+ final JdkListConfigurable jdkConfig = JdkListConfigurable.getInstance(project);
+ final ProjectSdksModel projectJdksModel = projectConfig.getProjectJdksModel();
+ final boolean[] successfullyAdded = new boolean[1];
+ projectJdksModel.doAdd(myPanel, type, new Consumer<Sdk>() {
+ public void consume(final Sdk jdk) {
+ successfullyAdded[0] = jdkConfig.addJdkNode(jdk, false);
+ myJdkChooser.updateList(jdk, type, projectJdksModel.getSdks());
+
+ if (!successfullyAdded[0]) {
+ try {
+ projectJdksModel.apply(jdkConfig);
+ }
+ catch (ConfigurationException e1) {
+ //name can't be wrong
+ }
+ }
+ }
+ });
+ }
+ });
+
+ final Project defaultProject = ProjectManagerEx.getInstanceEx().getDefaultProject();
+ mySetAsDefaultButton.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+
+ final Sdk jdk = getJdk();
+ final Runnable runnable = new Runnable() {
+ public void run() {
+ ProjectRootManagerEx.getInstanceEx(defaultProject).setProjectSdk(jdk);
+ }
+ };
+ ApplicationManager.getApplication().runWriteAction(runnable);
+ mySetAsDefaultButton.setEnabled(false);
+ }
+ });
+
+ myJdkChooser.addSelectionListener(new ListSelectionListener() {
+ @Override
+ public void valueChanged(ListSelectionEvent e) {
+ mySetAsDefaultButton.setEnabled(getJdk() != ProjectRootManagerEx.getInstanceEx(defaultProject).getProjectSdk());
+ }
+ });
+ }
+
+ @Nullable
+ private static Project getProject(final WizardContext context, final SdkType type) {
+ Project project = context.getProject();
+ if (type != null && project == null) { //'module' step inside project creation
+ project = ProjectManager.getInstance().getDefaultProject();
+ }
+ return project;
+ }
+
+ public JComponent getPreferredFocusedComponent() {
+ return myJdkChooser.getPreferredFocusedComponent();
+ }
+
+ public String getHelpId() {
+ return "project.new.page2";
+ }
+
+ public JComponent getComponent() {
+ return myPanel;
+ }
+
+ public void updateDataModel() {
+ myContext.setProjectJdk(getJdk());
+ }
+
+
+ public void updateStep() {
+ if (!myInitialized) { //lazy default project initialization
+ myJdkChooser.fillList(myType, null);
+ final Sdk defaultJdk = getDefaultJdk(myContext);
+ if (defaultJdk != null) {
+ myJdkChooser.selectJdk(defaultJdk);
+ }
+ mySetAsDefaultButton.setEnabled(defaultJdk != null);
+ myInitialized = true;
+ }
+ }
+
+ public Sdk getJdk() {
+ return myJdkChooser.getChosenJdk();
+ }
+
+ public Object[] getAllJdks() {
+ return myJdkChooser.getAllJdks();
+ }
+
+ public Icon getIcon() {
+ return myContext.getStepIcon();
+ }
+
+ @Nullable
+ private static Sdk getDefaultJdk(WizardContext context) {
+ Project defaultProject = ProjectManagerEx.getInstanceEx().getDefaultProject();
+ final Sdk sdk = ProjectRootManagerEx.getInstanceEx(defaultProject).getProjectSdk();
+ if (sdk == null) {
+ return AddModuleWizard.getMostRecentSuitableSdk(context);
+ }
+ return sdk;
+ }
+
+
+ public boolean validate() {
+ final Sdk jdk = myJdkChooser.getChosenJdk();
+ if (jdk == null) {
+ int result = Messages.showOkCancelDialog(IdeBundle.message("prompt.confirm.project.no.jdk"),
+ IdeBundle.message("title.no.jdk.specified"), Messages.getWarningIcon());
+ if (result != 0) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+
+}
diff --git a/java/idea-ui/src/com/intellij/ide/util/projectWizard/ProjectJdkListRenderer.java b/java/idea-ui/src/com/intellij/ide/util/projectWizard/ProjectJdkListRenderer.java
new file mode 100644
index 0000000..5847e66
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/ide/util/projectWizard/ProjectJdkListRenderer.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2000-2012 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.ide.util.projectWizard;
+
+import com.intellij.openapi.projectRoots.Sdk;
+import com.intellij.openapi.roots.ui.OrderEntryAppearanceService;
+import com.intellij.ui.ColoredListCellRendererWrapper;
+import com.intellij.ui.SimpleTextAttributes;
+
+import javax.swing.*;
+
+/**
+ * @author Eugene Zhuravlev
+ * @since May 18, 2005
+ */
+public class ProjectJdkListRenderer extends ColoredListCellRendererWrapper {
+ @Override
+ public void doCustomize(JList list, Object value, int index, boolean selected, boolean hasFocus) {
+ if (value == null || value instanceof Sdk) {
+ OrderEntryAppearanceService.getInstance().forJdk((Sdk)value, false, selected, true).customize(this);
+ }
+ else {
+ final String str = value.toString();
+ if (str != null) {
+ append(str, selected ? SimpleTextAttributes.SELECTED_SIMPLE_CELL_ATTRIBUTES : SimpleTextAttributes.SIMPLE_CELL_ATTRIBUTES);
+ }
+ }
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/ide/util/projectWizard/ProjectJdkStep.java b/java/idea-ui/src/com/intellij/ide/util/projectWizard/ProjectJdkStep.java
new file mode 100644
index 0000000..7c6462f
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/ide/util/projectWizard/ProjectJdkStep.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2000-2009 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.
+ */
+
+/*
+ * Created by IntelliJ IDEA.
+ * User: Anna.Kozlova
+ * Date: 16-Aug-2006
+ * Time: 18:01:13
+ */
+package com.intellij.ide.util.projectWizard;
+
+import com.intellij.ide.IdeBundle;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.options.ConfigurationException;
+import com.intellij.openapi.project.ProjectManager;
+import com.intellij.openapi.projectRoots.Sdk;
+import com.intellij.openapi.roots.ui.configuration.ProjectJdksConfigurable;
+import com.intellij.openapi.ui.Messages;
+import com.intellij.openapi.ui.MultiLineLabelUI;
+
+import javax.swing.*;
+import java.awt.*;
+
+/**
+ * @author Eugene Zhuravlev
+ * Date: Jan 21, 2004
+ */
+public class ProjectJdkStep extends ModuleWizardStep {
+ private final WizardContext myContext;
+
+ protected final ProjectJdksConfigurable myProjectJdksConfigurable;
+
+ private final JComponent myJDKsComponent;
+
+ public ProjectJdkStep(final WizardContext context) {
+ myContext = context;
+ myProjectJdksConfigurable = new ProjectJdksConfigurable(ProjectManager.getInstance().getDefaultProject());
+ myProjectJdksConfigurable.reset();
+ myJDKsComponent = myProjectJdksConfigurable.createComponent();
+ }
+
+ public JComponent getPreferredFocusedComponent() {
+ return myJDKsComponent;
+ }
+
+ public String getHelpId() {
+ return "reference.dialogs.new.project.fromScratch.sdk";
+ }
+
+ public JComponent getComponent() {
+ final JLabel label = new JLabel(IdeBundle.message("prompt.please.select.project.jdk"));
+ label.setUI(new MultiLineLabelUI());
+ final JPanel panel = new JPanel(new GridBagLayout()){
+ public Dimension getPreferredSize() {
+ return new Dimension(-1, 200);
+ }
+ };
+ panel.add(label, new GridBagConstraints(0, GridBagConstraints.RELATIVE, 1, 1, 1, 0,GridBagConstraints.NORTHWEST, GridBagConstraints.HORIZONTAL, new Insets(0,0,0,0),0,0));
+ myJDKsComponent.setBorder(BorderFactory.createEmptyBorder(4, 0, 0, 0));
+ panel.add(myJDKsComponent, new GridBagConstraints(0, GridBagConstraints.RELATIVE, 1, 1, 1, 1.0, GridBagConstraints.NORTHWEST, GridBagConstraints.BOTH, new Insets(0,0,0,0), 0, 0));
+ return panel;
+ }
+
+ public void updateDataModel() {
+ myContext.setProjectJdk(getJdk());
+ }
+
+
+ public Sdk getJdk() {
+ return myProjectJdksConfigurable.getSelectedJdk();
+ }
+
+ public Icon getIcon() {
+ return myContext.getStepIcon();
+ }
+
+ public boolean validate() throws ConfigurationException {
+ final Sdk jdk = myProjectJdksConfigurable.getSelectedJdk();
+ if (jdk == null && !ApplicationManager.getApplication().isUnitTestMode()) {
+ int result = Messages.showOkCancelDialog(IdeBundle.message("prompt.confirm.project.no.jdk"),
+ IdeBundle.message("title.no.jdk.specified"), Messages.getWarningIcon());
+ if (result != 0) {
+ return false;
+ }
+ }
+ myProjectJdksConfigurable.apply();
+ return true;
+ }
+
+ @Override
+ public String getName() {
+ return "Project JDK";
+ }
+
+ public void disposeUIResources() {
+ super.disposeUIResources();
+ myProjectJdksConfigurable.disposeUIResources();
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/ide/util/projectWizard/ProjectNameStep.java b/java/idea-ui/src/com/intellij/ide/util/projectWizard/ProjectNameStep.java
new file mode 100644
index 0000000..35a606e
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/ide/util/projectWizard/ProjectNameStep.java
@@ -0,0 +1,163 @@
+/*
+ * Copyright 2000-2012 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.ide.util.projectWizard;
+
+import com.intellij.ide.IdeBundle;
+import com.intellij.ide.highlighter.ModuleFileType;
+import com.intellij.ide.highlighter.ProjectFileType;
+import com.intellij.openapi.application.ApplicationInfo;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.options.ConfigurationException;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.Messages;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.util.text.StringUtil;
+import org.jetbrains.annotations.NonNls;
+
+import javax.swing.*;
+import java.awt.*;
+import java.io.File;
+import java.util.List;
+
+import static com.intellij.openapi.components.StorageScheme.DIRECTORY_BASED;
+
+/**
+ * @author Eugene Zhuravlev
+ * Date: Jan 21, 2004
+ */
+public class ProjectNameStep extends ModuleWizardStep {
+ private final NamePathComponent myNamePathComponent;
+ private final JPanel myPanel;
+ private final WizardContext myWizardContext;
+
+ public ProjectNameStep(WizardContext wizardContext) {
+ myWizardContext = wizardContext;
+ myNamePathComponent = new NamePathComponent(IdeBundle.message("label.project.name"), IdeBundle.message("label.component.file.location",
+ StringUtil.capitalize(myWizardContext.getPresentationName())), 'a', 'l',
+ IdeBundle.message("title.select.project.file.directory", myWizardContext.getPresentationName()),
+ IdeBundle.message("description.select.project.file.directory", myWizardContext.getPresentationName()));
+ myPanel = new JPanel(new GridBagLayout());
+ myPanel.setBorder(BorderFactory.createEtchedBorder());
+
+ ApplicationInfo info = ApplicationManager.getApplication().getComponent(ApplicationInfo.class);
+ String appName = info.getVersionName();
+ myPanel.add(new JLabel(IdeBundle.message("label.please.enter.project.name", appName, wizardContext.getPresentationName())),
+ new GridBagConstraints(0, GridBagConstraints.RELATIVE, 1, 1, 1.0, 0.0, GridBagConstraints.NORTHWEST, GridBagConstraints.HORIZONTAL, new Insets(8, 10, 8, 10), 0, 0));
+
+ myPanel.add(myNamePathComponent, new GridBagConstraints(0, GridBagConstraints.RELATIVE, 1, 1, 1.0, 1.0, GridBagConstraints.NORTHWEST, GridBagConstraints.HORIZONTAL, new Insets(8, 10, 8, 10), 0, 0));
+ }
+
+ public JComponent getPreferredFocusedComponent() {
+ return myNamePathComponent.getNameComponent();
+ }
+
+ public String getHelpId() {
+ return "reference.dialogs.new.project.import.name";
+ }
+
+ public JComponent getComponent() {
+ return myPanel;
+ }
+
+ public void updateStep() {
+ super.updateStep();
+ myNamePathComponent.setPath(FileUtil.toSystemDependentName(myWizardContext.getProjectFileDirectory()));
+ String name = myWizardContext.getProjectName();
+ if (name == null) {
+ List<String> components = StringUtil.split(FileUtil.toSystemIndependentName(myWizardContext.getProjectFileDirectory()), "/");
+ if (!components.isEmpty()) {
+ name = components.get(components.size()-1);
+ }
+ }
+ myNamePathComponent.setNameValue(name);
+ if (name != null) {
+ myNamePathComponent.getNameComponent().setSelectionStart(0);
+ myNamePathComponent.getNameComponent().setSelectionEnd(name.length());
+ }
+ }
+
+ public void updateDataModel() {
+ myWizardContext.setProjectName(getProjectName());
+ myWizardContext.setProjectFileDirectory(getProjectFileDirectory());
+ }
+
+ public Icon getIcon() {
+ return myWizardContext.getStepIcon();
+ }
+
+ public boolean validate() throws ConfigurationException {
+ String name = myNamePathComponent.getNameValue();
+ if (name.length() == 0) {
+ final ApplicationInfo info = ApplicationManager.getApplication().getComponent(ApplicationInfo.class);
+ throw new ConfigurationException(IdeBundle.message("prompt.new.project.file.name", info.getVersionName(), myWizardContext.getPresentationName()));
+ }
+
+ final String projectFileDirectory = getProjectFileDirectory();
+ if (projectFileDirectory.length() == 0) {
+ throw new ConfigurationException(IdeBundle.message("prompt.enter.project.file.location", myWizardContext.getPresentationName()));
+ }
+
+ final boolean shouldPromptCreation = myNamePathComponent.isPathChangedByUser();
+ if (!ProjectWizardUtil.createDirectoryIfNotExists(IdeBundle.message("directory.project.file.directory",myWizardContext.getPresentationName()), projectFileDirectory, shouldPromptCreation)) {
+ return false;
+ }
+
+ boolean shouldContinue = true;
+
+ final String path = myWizardContext.isCreatingNewProject() && myWizardContext.getProjectStorageFormat() == DIRECTORY_BASED
+ ? getProjectFileDirectory() + "/" + Project.DIRECTORY_STORE_FOLDER : getProjectFilePath();
+ final File projectFile = new File(path);
+ if (projectFile.exists()) {
+ final String title = myWizardContext.isCreatingNewProject()
+ ? IdeBundle.message("title.new.project")
+ : IdeBundle.message("title.add.module");
+ final String message = myWizardContext.isCreatingNewProject() && myWizardContext.getProjectStorageFormat() == DIRECTORY_BASED
+ ? IdeBundle.message("prompt.overwrite.project.folder",
+ Project.DIRECTORY_STORE_FOLDER, projectFile.getParentFile().getAbsolutePath())
+ : IdeBundle.message("prompt.overwrite.project.file",
+ projectFile.getAbsolutePath(), myWizardContext.getPresentationName());
+ int answer = Messages.showYesNoDialog(message, title, Messages.getQuestionIcon());
+ shouldContinue = answer == 0;
+ }
+
+ return shouldContinue;
+ }
+
+ @NonNls
+ public String getProjectFilePath() {
+ return getProjectFileDirectory() + "/" + myNamePathComponent.getNameValue()/*myTfProjectName.getText().trim()*/ +
+ (myWizardContext.getProject() == null ? ProjectFileType.DOT_DEFAULT_EXTENSION : ModuleFileType.DOT_DEFAULT_EXTENSION);
+ }
+
+ public String getProjectFileDirectory() {
+ return FileUtil.toSystemIndependentName(myNamePathComponent.getPath());
+ }
+
+ public String getProjectName() {
+ return myNamePathComponent.getNameValue();
+ }
+
+ @Override
+ public String getName() {
+ return "Name";
+ }
+
+ public boolean isStepVisible() {
+ final ProjectBuilder builder = myWizardContext.getProjectBuilder();
+ if (builder != null && builder.isUpdate()) return false;
+ return super.isStepVisible();
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/ide/util/projectWizard/ProjectWizardStepFactoryImpl.java b/java/idea-ui/src/com/intellij/ide/util/projectWizard/ProjectWizardStepFactoryImpl.java
new file mode 100644
index 0000000..849ef73
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/ide/util/projectWizard/ProjectWizardStepFactoryImpl.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright 2000-2011 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.ide.util.projectWizard;
+
+import com.intellij.ide.util.frameworkSupport.FrameworkSupportUtil;
+import com.intellij.ide.util.newProjectWizard.AddModuleWizard;
+import com.intellij.ide.util.newProjectWizard.SourcePathsStep;
+import com.intellij.ide.util.newProjectWizard.SupportForFrameworksStep;
+import com.intellij.openapi.projectRoots.Sdk;
+import com.intellij.openapi.projectRoots.SdkType;
+import com.intellij.openapi.projectRoots.SdkTypeId;
+import com.intellij.openapi.roots.ui.configuration.ModulesProvider;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.LibrariesContainer;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.LibrariesContainerFactory;
+import com.intellij.openapi.util.Computable;
+import com.intellij.openapi.util.Condition;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+
+/**
+ * @author Eugene Zhuravlev
+ * Date: Oct 6, 2004
+ */
+public class ProjectWizardStepFactoryImpl extends ProjectWizardStepFactory {
+
+ public ModuleWizardStep createNameAndLocationStep(WizardContext wizardContext, JavaModuleBuilder builder, ModulesProvider modulesProvider, Icon icon, String helpId) {
+ return new NameLocationStep(wizardContext, builder, modulesProvider, icon, helpId);
+ }
+
+ public ModuleWizardStep createNameAndLocationStep(final WizardContext wizardContext) {
+ return new ProjectNameStep(wizardContext);
+ }
+
+ /**
+ * @deprecated
+ */
+ public ModuleWizardStep createOutputPathPathsStep(ModuleWizardStep nameAndLocationStep, JavaModuleBuilder builder, Icon icon, String helpId) {
+ return new OutputPathsStep((NameLocationStep)nameAndLocationStep, builder, icon, helpId);
+ }
+
+ public ModuleWizardStep createSourcePathsStep(ModuleWizardStep nameAndLocationStep, SourcePathsBuilder builder, Icon icon, String helpId) {
+ return null;
+ }
+
+ public ModuleWizardStep createSourcePathsStep(final WizardContext context, final SourcePathsBuilder builder, final Icon icon, @NonNls final String helpId) {
+ return new SourcePathsStep(builder, icon, helpId);
+ }
+
+ /**
+ * @deprecated
+ */
+ public ModuleWizardStep createProjectJdkStep(WizardContext context,
+ final JavaModuleBuilder builder,
+ final Computable<Boolean> isVisible,
+ final Icon icon,
+ final String helpId) {
+ return createProjectJdkStep(context, null, builder, isVisible, icon, helpId);
+ }
+
+ public ModuleWizardStep createProjectJdkStep(WizardContext context,
+ SdkType type,
+ final JavaModuleBuilder builder,
+ final Computable<Boolean> isVisible,
+ final Icon icon,
+ @NonNls final String helpId) {
+ return new ProjectJdkForModuleStep(context, type){
+ public void updateDataModel() {
+ super.updateDataModel();
+ builder.setModuleJdk(getJdk());
+ }
+
+ public boolean isStepVisible() {
+ return isVisible.compute().booleanValue();
+ }
+
+ public Icon getIcon() {
+ return icon;
+ }
+
+ @Override
+ public String getName() {
+ return "Specify JDK";
+ }
+
+ public String getHelpId() {
+ return helpId;
+ }
+ };
+ }
+
+ public ModuleWizardStep createProjectJdkStep(final WizardContext wizardContext) {
+ ModuleWizardStep projectSdkStep = wizardContext.getProjectSdkStep();
+ if (projectSdkStep instanceof ProjectJdkStep) {
+ return projectSdkStep;
+ }
+ projectSdkStep = new ProjectJdkStep(wizardContext) {
+ public boolean isStepVisible() {
+ final Sdk newProjectJdk = AddModuleWizard.getProjectSdkByDefault(wizardContext);
+ if (newProjectJdk == null) return true;
+ final ProjectBuilder projectBuilder = wizardContext.getProjectBuilder();
+ return projectBuilder != null && !projectBuilder.isSuitableSdk(newProjectJdk);
+ }
+ };
+ wizardContext.setProjectSdkStep(projectSdkStep);
+ return projectSdkStep;
+ }
+
+ @Nullable
+ @Override
+ public Sdk getNewProjectSdk(WizardContext wizardContext) {
+ return AddModuleWizard.getNewProjectJdk(wizardContext);
+ }
+
+ @Override
+ public ModuleWizardStep createSupportForFrameworksStep(WizardContext wizardContext, ModuleBuilder moduleBuilder) {
+ return createSupportForFrameworksStep(wizardContext, moduleBuilder, ModulesProvider.EMPTY_MODULES_PROVIDER);
+ }
+
+ @Override
+ public ModuleWizardStep createSupportForFrameworksStep(WizardContext context, ModuleBuilder builder, ModulesProvider modulesProvider) {
+ if (!FrameworkSupportUtil.getProviders(builder).isEmpty()) {
+ final LibrariesContainer container = LibrariesContainerFactory.createContainer(context, modulesProvider);
+ return new SupportForFrameworksStep(context, builder, container);
+ }
+ return null;
+ }
+
+ @Override
+ public ModuleWizardStep createJavaSettingsStep(SettingsStep settingsStep, ModuleBuilder moduleBuilder, @NotNull Condition<SdkTypeId> sdkFilter) {
+ return new JavaSettingsStep(settingsStep, moduleBuilder, sdkFilter);
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/ide/util/projectWizard/ProjectWizardUtil.java b/java/idea-ui/src/com/intellij/ide/util/projectWizard/ProjectWizardUtil.java
new file mode 100644
index 0000000..8f380dd
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/ide/util/projectWizard/ProjectWizardUtil.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2000-2009 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.ide.util.projectWizard;
+
+import com.intellij.CommonBundle;
+import com.intellij.ide.IdeBundle;
+import com.intellij.openapi.application.ApplicationNamesInfo;
+import com.intellij.openapi.ui.Messages;
+import com.intellij.openapi.vfs.VfsUtil;
+import org.jetbrains.annotations.NonNls;
+
+import java.io.File;
+import java.io.IOException;
+
+/**
+ * @author cdr
+ */
+public class ProjectWizardUtil {
+ private ProjectWizardUtil() {
+ }
+
+ public static String findNonExistingFileName(String searchDirectory, @NonNls String preferredName, String extension){
+ for (int idx = 0; ; idx++){
+ final String fileName = (idx > 0? preferredName + idx : preferredName) + extension;
+ if(!new File(searchDirectory + File.separator + fileName).exists()) {
+ return fileName;
+ }
+ }
+ }
+
+ public static boolean createDirectoryIfNotExists(final String promptPrefix, String directoryPath, boolean promptUser) {
+ File dir = new File(directoryPath);
+ if (!dir.exists()) {
+ if (promptUser) {
+ final int answer = Messages.showOkCancelDialog(IdeBundle.message("promot.projectwizard.directory.does.not.exist", promptPrefix,
+ dir.getPath(), ApplicationNamesInfo.getInstance().getProductName()),
+ IdeBundle.message("title.directory.does.not.exist"), Messages.getQuestionIcon());
+ if (answer != 0) {
+ return false;
+ }
+ }
+ try {
+ VfsUtil.createDirectories(dir.getPath());
+ }
+ catch (IOException e) {
+ Messages.showErrorDialog(IdeBundle.message("error.failed.to.create.directory", dir.getPath()), CommonBundle.getErrorTitle());
+ return false;
+ }
+ }
+ return true;
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/ide/util/projectWizard/SdkSettingsStep.java b/java/idea-ui/src/com/intellij/ide/util/projectWizard/SdkSettingsStep.java
new file mode 100644
index 0000000..77a2158
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/ide/util/projectWizard/SdkSettingsStep.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright 2000-2012 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.ide.util.projectWizard;
+
+import com.intellij.CommonBundle;
+import com.intellij.ide.IdeBundle;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.options.ConfigurationException;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.project.ProjectManager;
+import com.intellij.openapi.projectRoots.Sdk;
+import com.intellij.openapi.projectRoots.SdkTypeId;
+import com.intellij.openapi.roots.ProjectRootManager;
+import com.intellij.openapi.roots.ui.configuration.JdkComboBox;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.ProjectSdksModel;
+import com.intellij.openapi.ui.Messages;
+import com.intellij.openapi.util.Condition;
+import org.jetbrains.annotations.NotNull;
+
+import javax.swing.*;
+import java.awt.*;
+
+/**
+ * @author Dmitry Avdeev
+ * Date: 10/26/12
+ */
+public class SdkSettingsStep extends ModuleWizardStep {
+ protected final JdkComboBox myJdkComboBox;
+ protected final WizardContext myWizardContext;
+ protected final ProjectSdksModel myModel;
+ private final ModuleBuilder myModuleBuilder;
+
+ public SdkSettingsStep(SettingsStep settingsStep, ModuleBuilder moduleBuilder, @NotNull Condition<SdkTypeId> sdkFilter) {
+ myModuleBuilder = moduleBuilder;
+
+ myWizardContext = settingsStep.getContext();
+ myModel = new ProjectSdksModel();
+ Project project = myWizardContext.getProject();
+ myModel.reset(project);
+
+ myJdkComboBox = new JdkComboBox(myModel, sdkFilter);
+
+ if (project != null) {
+ Sdk sdk = ProjectRootManager.getInstance(project).getProjectSdk();
+ if (sdk != null && moduleBuilder.isSuitableSdkType(sdk.getSdkType())) {
+ // use project SDK
+ return;
+ }
+ }
+ else {
+ // set default project SDK
+ Project defaultProject = ProjectManager.getInstance().getDefaultProject();
+ Sdk sdk = ProjectRootManager.getInstance(defaultProject).getProjectSdk();
+ if (sdk != null && sdkFilter.value(sdk.getSdkType())) {
+ myJdkComboBox.setSelectedJdk(sdk);
+ }
+ }
+
+ JButton button = new JButton("Ne\u001Bw...");
+ myJdkComboBox.setSetupButton(button, project, myModel,
+ project == null ? new JdkComboBox.NoneJdkComboBoxItem() : new JdkComboBox.ProjectJdkComboBoxItem(),
+ null,
+ false);
+ JPanel jdkPanel = new JPanel(new BorderLayout(4, 0));
+ jdkPanel.add(myJdkComboBox);
+ jdkPanel.add(button, BorderLayout.EAST);
+ settingsStep.addSettingsField((project == null ? "Project" : "Module") + " \u001BSDK:", jdkPanel);
+
+ }
+
+ @Override
+ public JComponent getComponent() {
+ return null;
+ }
+
+ @Override
+ public void updateDataModel() {
+ Project project = myWizardContext.getProject();
+ if (project == null) {
+ Sdk jdk = myJdkComboBox.getSelectedJdk();
+ myWizardContext.setProjectJdk(jdk);
+ }
+ else {
+ Sdk sdk = ProjectRootManager.getInstance(project).getProjectSdk();
+ if (sdk == null || !myModuleBuilder.isSuitableSdkType(sdk.getSdkType())) {
+ myModuleBuilder.setModuleJdk(myJdkComboBox.getSelectedJdk());
+ } // else, inherit project jdk
+ }
+ }
+
+ @Override
+ public boolean validate() throws ConfigurationException {
+ if (myJdkComboBox.getSelectedJdk() == null && !ApplicationManager.getApplication().isUnitTestMode()) {
+ int result = Messages.showDialog(getNoSdkMessage(),
+ IdeBundle.message("title.no.jdk.specified"),
+ new String[]{CommonBundle.getYesButtonText(), CommonBundle.getNoButtonText()}, 1, Messages.getWarningIcon());
+ if (result != Messages.YES) {
+ return false;
+ }
+ }
+ try {
+ myModel.apply();
+ } catch (ConfigurationException e) {
+ //IDEA-98382 We should allow Next step if user has wrong SDK
+ }
+ return true;
+ }
+
+ protected String getNoSdkMessage() {
+ return IdeBundle.message("prompt.confirm.project.no.jdk");
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/ide/util/projectWizard/importSources/DetectedProjectRoot.java b/java/idea-ui/src/com/intellij/ide/util/projectWizard/importSources/DetectedProjectRoot.java
new file mode 100644
index 0000000..4465cde
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/ide/util/projectWizard/importSources/DetectedProjectRoot.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2000-2011 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.ide.util.projectWizard.importSources;
+
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.io.File;
+
+/**
+ * @author nik
+ */
+public abstract class DetectedProjectRoot {
+ private final File myDirectory;
+
+ protected DetectedProjectRoot(@NotNull File directory) {
+ myDirectory = directory;
+ }
+
+ public File getDirectory() {
+ return myDirectory;
+ }
+
+ @NotNull
+ public abstract String getRootTypeName();
+
+ @Nullable
+ public DetectedProjectRoot combineWith(@NotNull DetectedProjectRoot root) {
+ return null;
+ }
+
+ public boolean canContainRoot(@NotNull DetectedProjectRoot root) {
+ return true;
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/ide/util/projectWizard/importSources/DetectedSourceRoot.java b/java/idea-ui/src/com/intellij/ide/util/projectWizard/importSources/DetectedSourceRoot.java
new file mode 100644
index 0000000..037f35e
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/ide/util/projectWizard/importSources/DetectedSourceRoot.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2000-2011 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.ide.util.projectWizard.importSources;
+
+import com.intellij.openapi.util.text.StringUtil;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.io.File;
+
+public abstract class DetectedSourceRoot extends DetectedProjectRoot {
+ private String myPackagePrefix;
+
+ public DetectedSourceRoot(final File directory, @Nullable String packagePrefix) {
+ super(directory);
+ myPackagePrefix = packagePrefix;
+ }
+
+ @NotNull
+ public String getPackagePrefix() {
+ return StringUtil.notNullize(myPackagePrefix);
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/ide/util/projectWizard/importSources/JavaModuleSourceRoot.java b/java/idea-ui/src/com/intellij/ide/util/projectWizard/importSources/JavaModuleSourceRoot.java
new file mode 100644
index 0000000..117593c
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/ide/util/projectWizard/importSources/JavaModuleSourceRoot.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2000-2011 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.ide.util.projectWizard.importSources;
+
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.util.containers.ContainerUtil;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author nik
+ */
+public class JavaModuleSourceRoot extends DetectedSourceRoot {
+ private List<String> myLanguages;
+
+ public JavaModuleSourceRoot(File directory, @Nullable String packagePrefix, @NotNull String language) {
+ super(directory, packagePrefix);
+ myLanguages = new ArrayList<String>();
+ myLanguages.add(language);
+ }
+
+ private JavaModuleSourceRoot(File directory, String packagePrefix, List<String> languages) {
+ super(directory, packagePrefix);
+ myLanguages = languages;
+ }
+
+ @NotNull
+ @Override
+ public String getRootTypeName() {
+ return StringUtil.join(myLanguages, ", ");
+ }
+
+ @Override
+ public DetectedProjectRoot combineWith(@NotNull DetectedProjectRoot root) {
+ if (root instanceof JavaModuleSourceRoot) {
+ return combineWith((JavaModuleSourceRoot)root);
+ }
+ return null;
+ }
+
+ @NotNull
+ public JavaModuleSourceRoot combineWith(@NotNull JavaModuleSourceRoot root) {
+ return new JavaModuleSourceRoot(getDirectory(), getPackagePrefix(), ContainerUtil.concat(myLanguages, root.myLanguages));
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/ide/util/projectWizard/importSources/JavaSourceRootDetectionUtil.java b/java/idea-ui/src/com/intellij/ide/util/projectWizard/importSources/JavaSourceRootDetectionUtil.java
new file mode 100644
index 0000000..bbffb7e
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/ide/util/projectWizard/importSources/JavaSourceRootDetectionUtil.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright 2000-2009 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.ide.util.projectWizard.importSources;
+
+import com.intellij.ide.util.importProject.RootDetectionProcessor;
+import com.intellij.lexer.JavaLexer;
+import com.intellij.lexer.Lexer;
+import com.intellij.openapi.util.Pair;
+import com.intellij.openapi.util.SystemInfo;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.pom.java.LanguageLevel;
+import com.intellij.psi.JavaTokenType;
+import com.intellij.psi.impl.source.tree.ElementType;
+import com.intellij.psi.tree.IElementType;
+import com.intellij.psi.tree.TokenSet;
+import com.intellij.util.NullableFunction;
+import com.intellij.util.StringBuilderSpinAllocator;
+import com.intellij.util.containers.ContainerUtil;
+import com.intellij.util.text.CharArrayCharSequence;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+
+public class JavaSourceRootDetectionUtil {
+ private static final TokenSet JAVA_FILE_FIRST_TOKEN_SET = TokenSet.orSet(
+ ElementType.MODIFIER_BIT_SET,
+ ElementType.CLASS_KEYWORD_BIT_SET,
+ TokenSet.create(JavaTokenType.AT, JavaTokenType.IMPORT_KEYWORD)
+ );
+
+ private JavaSourceRootDetectionUtil() { }
+
+ @NotNull
+ public static Collection<JavaModuleSourceRoot> suggestRoots(@NotNull File dir) {
+ final List<JavaSourceRootDetector> detectors = ContainerUtil.findAll(ProjectStructureDetector.EP_NAME.getExtensions(), JavaSourceRootDetector.class);
+ final RootDetectionProcessor processor = new RootDetectionProcessor(dir, detectors.toArray(new JavaSourceRootDetector[detectors.size()]));
+ final Map<ProjectStructureDetector,List<DetectedProjectRoot>> rootsMap = processor.findRoots();
+
+ Map<File, JavaModuleSourceRoot> result = new HashMap<File, JavaModuleSourceRoot>();
+ for (List<DetectedProjectRoot> roots : rootsMap.values()) {
+ for (DetectedProjectRoot root : roots) {
+ if (root instanceof JavaModuleSourceRoot) {
+ final JavaModuleSourceRoot sourceRoot = (JavaModuleSourceRoot)root;
+ final File directory = sourceRoot.getDirectory();
+ final JavaModuleSourceRoot oldRoot = result.remove(directory);
+ if (oldRoot != null) {
+ result.put(directory, oldRoot.combineWith(sourceRoot));
+ }
+ else {
+ result.put(directory, sourceRoot);
+ }
+ }
+ }
+ }
+ return result.values();
+ }
+
+
+ @Nullable
+ public static String getPackageName(CharSequence text) {
+ Lexer lexer = new JavaLexer(LanguageLevel.JDK_1_3);
+ lexer.start(text);
+ skipWhiteSpaceAndComments(lexer);
+ final IElementType firstToken = lexer.getTokenType();
+ if (firstToken != JavaTokenType.PACKAGE_KEYWORD) {
+ if (JAVA_FILE_FIRST_TOKEN_SET.contains(firstToken)) {
+ return "";
+ }
+ return null;
+ }
+ lexer.advance();
+ skipWhiteSpaceAndComments(lexer);
+
+ final StringBuilder buffer = StringBuilderSpinAllocator.alloc();
+ try {
+ while(true){
+ if (lexer.getTokenType() != JavaTokenType.IDENTIFIER) break;
+ buffer.append(text, lexer.getTokenStart(), lexer.getTokenEnd());
+ lexer.advance();
+ skipWhiteSpaceAndComments(lexer);
+ if (lexer.getTokenType() != JavaTokenType.DOT) break;
+ buffer.append('.');
+ lexer.advance();
+ skipWhiteSpaceAndComments(lexer);
+ }
+ String packageName = buffer.toString();
+ if (packageName.length() == 0 || StringUtil.endsWithChar(packageName, '.')) return null;
+ return packageName;
+ }
+ finally {
+ StringBuilderSpinAllocator.dispose(buffer);
+ }
+ }
+
+ public static void skipWhiteSpaceAndComments(Lexer lexer){
+ while(ElementType.JAVA_COMMENT_OR_WHITESPACE_BIT_SET.contains(lexer.getTokenType())) {
+ lexer.advance();
+ }
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/ide/util/projectWizard/importSources/JavaSourceRootDetector.java b/java/idea-ui/src/com/intellij/ide/util/projectWizard/importSources/JavaSourceRootDetector.java
new file mode 100644
index 0000000..9b077fe
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/ide/util/projectWizard/importSources/JavaSourceRootDetector.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2000-2011 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.ide.util.projectWizard.importSources;
+
+import com.intellij.ide.util.projectWizard.importSources.util.CommonSourceRootDetectionUtil;
+import com.intellij.openapi.util.Pair;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.util.NullableFunction;
+import org.jetbrains.annotations.NotNull;
+
+import java.io.File;
+import java.util.List;
+
+/**
+ * @author nik
+ */
+public abstract class JavaSourceRootDetector extends ProjectStructureDetector {
+ @NotNull
+ @Override
+ public DirectoryProcessingResult detectRoots(@NotNull File dir, @NotNull File[] children, @NotNull File base,
+ @NotNull List<DetectedProjectRoot> result) {
+ final String fileExtension = getFileExtension();
+ for (File child : children) {
+ if (child.isFile()) {
+ String extension = FileUtil.getExtension(child.getName());
+ if (extension.equals(fileExtension)) {
+ Pair<File, String> root = CommonSourceRootDetectionUtil.IO_FILE.suggestRootForFileWithPackageStatement(child, base,
+ getPackageNameFetcher(), true);
+ if (root != null) {
+ result.add(new JavaModuleSourceRoot(root.getFirst(), root.getSecond(), getLanguageName()));
+ return DirectoryProcessingResult.skipChildrenAndParentsUpTo(root.getFirst());
+ }
+ else {
+ return DirectoryProcessingResult.SKIP_CHILDREN;
+ }
+ }
+ }
+ }
+ return DirectoryProcessingResult.PROCESS_CHILDREN;
+ }
+
+ @Override
+ public String getDetectorId() {
+ return "Java";
+ }
+
+ @NotNull
+ protected abstract String getLanguageName();
+
+ @NotNull
+ protected abstract String getFileExtension();
+
+ @NotNull
+ protected abstract NullableFunction<CharSequence, String> getPackageNameFetcher();
+}
diff --git a/java/idea-ui/src/com/intellij/ide/util/projectWizard/importSources/ProjectFromSourcesBuilder.java b/java/idea-ui/src/com/intellij/ide/util/projectWizard/importSources/ProjectFromSourcesBuilder.java
new file mode 100644
index 0000000..fc28f8f
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/ide/util/projectWizard/importSources/ProjectFromSourcesBuilder.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2000-2011 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.ide.util.projectWizard.importSources;
+
+import com.intellij.ide.util.importProject.ProjectDescriptor;
+import com.intellij.ide.util.projectWizard.WizardContext;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.Collection;
+import java.util.Set;
+
+/**
+ * @author nik
+ */
+public interface ProjectFromSourcesBuilder {
+ @NotNull
+ Collection<DetectedProjectRoot> getProjectRoots(@NotNull ProjectStructureDetector detector);
+
+ @NotNull
+ ProjectDescriptor getProjectDescriptor(@NotNull ProjectStructureDetector detector);
+
+ String getBaseProjectPath();
+
+ @NotNull
+ Set<String> getExistingModuleNames();
+
+ @NotNull
+ Set<String> getExistingProjectLibraryNames();
+
+ @NotNull
+ WizardContext getContext();
+
+ boolean hasRootsFromOtherDetectors(ProjectStructureDetector thisDetector);
+}
diff --git a/java/idea-ui/src/com/intellij/ide/util/projectWizard/importSources/ProjectStructureDetector.java b/java/idea-ui/src/com/intellij/ide/util/projectWizard/importSources/ProjectStructureDetector.java
new file mode 100644
index 0000000..71b53b2
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/ide/util/projectWizard/importSources/ProjectStructureDetector.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2000-2011 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.ide.util.projectWizard.importSources;
+
+import com.intellij.ide.util.importProject.ProjectDescriptor;
+import com.intellij.ide.util.projectWizard.ModuleWizardStep;
+import com.intellij.openapi.extensions.ExtensionPointName;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import java.io.File;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Override this class to provide automatic detection of modules and libraries for 'Create from existing sources' mode of
+ * the new project/module wizard.
+ *
+ * <p>
+ * The implementation should be registered in your {@code plugin.xml}:
+ * <pre>
+ * <extensions defaultExtensionNs="com.intellij">
+ * <projectStructureDetector implementation="qualified-class-name"/>
+ * </extensions>
+ * </pre>
+ *
+ * @author nik
+ */
+public abstract class ProjectStructureDetector {
+ public static final ExtensionPointName<ProjectStructureDetector> EP_NAME = ExtensionPointName.create("com.intellij.projectStructureDetector");
+
+ /**
+ * This methods is called recursively for all directories under the selected root directory.
+ * @param dir current directory
+ * @param children its children
+ * @param base the root directory in which project files are detected. All detected roots must be located under this directory.
+ * @param result list of detected roots
+ * @return
+ * <li>{@link DirectoryProcessingResult#PROCESS_CHILDREN} if children of {@code dir} need to be processed by this detector</li>
+ * <li>{@link DirectoryProcessingResult#SKIP_CHILDREN} to skip all children of {@code dir} from processing</li>
+ * <li><code>{@link DirectoryProcessingResult#skipChildrenAndParentsUpTo}(parent)</code> to skip all directories under {@code parent} from processing.
+ * {@code parent} must be an ancestor of {@code dir} or {@code dir} itself
+ * </li>
+ */
+ @NotNull
+ public abstract DirectoryProcessingResult detectRoots(@NotNull File dir, @NotNull File[] children, @NotNull File base,
+ @NotNull List<DetectedProjectRoot> result);
+
+ /**
+ * Return additional wizard steps which will be shown if some roots are detected by this detector
+ */
+ public List<ModuleWizardStep> createWizardSteps(ProjectFromSourcesBuilder builder, ProjectDescriptor projectDescriptor, Icon stepIcon) {
+ return Collections.emptyList();
+ }
+
+ public String getDetectorId() {
+ return getClass().getName();
+ }
+
+ /**
+ * Setup modules and libraries for the selected roots
+ */
+ public void setupProjectStructure(@NotNull Collection<DetectedProjectRoot> roots, @NotNull ProjectDescriptor projectDescriptor,
+ @NotNull ProjectFromSourcesBuilder builder) {
+ }
+
+ public static class DirectoryProcessingResult {
+ private boolean myProcessChildren;
+ private File myParentToSkip;
+ public static final DirectoryProcessingResult PROCESS_CHILDREN = new DirectoryProcessingResult(true, null);
+ public static final DirectoryProcessingResult SKIP_CHILDREN = new DirectoryProcessingResult(false, null);
+
+ public static DirectoryProcessingResult skipChildrenAndParentsUpTo(@NotNull File parent) {
+ return new DirectoryProcessingResult(false, parent);
+ }
+
+ private DirectoryProcessingResult(boolean processChildren, File parentToSkip) {
+ myProcessChildren = processChildren;
+ myParentToSkip = parentToSkip;
+ }
+
+ public boolean isProcessChildren() {
+ return myProcessChildren;
+ }
+
+ @Nullable
+ public File getParentToSkip() {
+ return myParentToSkip;
+ }
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/ide/util/projectWizard/importSources/impl/JavaProjectStructureDetector.java b/java/idea-ui/src/com/intellij/ide/util/projectWizard/importSources/impl/JavaProjectStructureDetector.java
new file mode 100644
index 0000000..ac706cc
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/ide/util/projectWizard/importSources/impl/JavaProjectStructureDetector.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2000-2011 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.ide.util.projectWizard.importSources.impl;
+
+import com.intellij.ide.highlighter.JavaFileType;
+import com.intellij.ide.util.DelegatingProgressIndicator;
+import com.intellij.ide.util.importProject.*;
+import com.intellij.ide.util.projectWizard.ModuleWizardStep;
+import com.intellij.ide.util.projectWizard.ProjectWizardStepFactory;
+import com.intellij.ide.util.projectWizard.importSources.JavaSourceRootDetectionUtil;
+import com.intellij.ide.util.projectWizard.importSources.JavaSourceRootDetector;
+import com.intellij.ide.util.projectWizard.importSources.ProjectFromSourcesBuilder;
+import com.intellij.util.NullableFunction;
+import org.jetbrains.annotations.NotNull;
+
+import javax.swing.*;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author nik
+ */
+public class JavaProjectStructureDetector extends JavaSourceRootDetector {
+
+ @NotNull
+ @Override
+ protected String getLanguageName() {
+ return "Java";
+ }
+
+ @NotNull
+ @Override
+ protected String getFileExtension() {
+ return JavaFileType.DEFAULT_EXTENSION;
+ }
+
+ @Override
+ public List<ModuleWizardStep> createWizardSteps(ProjectFromSourcesBuilder builder,
+ ProjectDescriptor projectDescriptor,
+ Icon stepIcon) {
+ final List<ModuleWizardStep> steps = new ArrayList<ModuleWizardStep>();
+ final ModuleInsight moduleInsight = new JavaModuleInsight(new DelegatingProgressIndicator(), builder.getExistingModuleNames(), builder.getExistingProjectLibraryNames());
+ steps.add(new LibrariesDetectionStep(builder, projectDescriptor, moduleInsight, stepIcon, "reference.dialogs.new.project.fromCode.page1"));
+ steps.add(new ModulesDetectionStep(this, builder, projectDescriptor, moduleInsight, stepIcon, "reference.dialogs.new.project.fromCode.page2"));
+ if (builder.getContext().isCreatingNewProject()) {
+ steps.add(ProjectWizardStepFactory.getInstance().createProjectJdkStep(builder.getContext()));
+ }
+ return steps;
+ }
+
+ @NotNull
+ protected NullableFunction<CharSequence, String> getPackageNameFetcher() {
+ return new NullableFunction<CharSequence, String>() {
+ @Override
+ public String fun(CharSequence charSequence) {
+ return JavaSourceRootDetectionUtil.getPackageName(charSequence);
+ }
+ };
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/ide/util/projectWizard/importSources/impl/ProjectFromSourcesBuilderImpl.java b/java/idea-ui/src/com/intellij/ide/util/projectWizard/importSources/impl/ProjectFromSourcesBuilderImpl.java
new file mode 100644
index 0000000..2462a7e
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/ide/util/projectWizard/importSources/impl/ProjectFromSourcesBuilderImpl.java
@@ -0,0 +1,443 @@
+/*
+ * Copyright 2000-2012 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.ide.util.projectWizard.importSources.impl;
+
+import com.intellij.icons.AllIcons;
+import com.intellij.ide.IdeBundle;
+import com.intellij.ide.util.importProject.LibraryDescriptor;
+import com.intellij.ide.util.importProject.ModuleDescriptor;
+import com.intellij.ide.util.importProject.ModuleInsight;
+import com.intellij.ide.util.importProject.ProjectDescriptor;
+import com.intellij.ide.util.newProjectWizard.modes.ImportImlMode;
+import com.intellij.ide.util.projectWizard.ExistingModuleLoader;
+import com.intellij.ide.util.projectWizard.ModuleBuilder;
+import com.intellij.ide.util.projectWizard.WizardContext;
+import com.intellij.ide.util.projectWizard.importSources.DetectedProjectRoot;
+import com.intellij.ide.util.projectWizard.importSources.JavaModuleSourceRoot;
+import com.intellij.ide.util.projectWizard.importSources.ProjectFromSourcesBuilder;
+import com.intellij.ide.util.projectWizard.importSources.ProjectStructureDetector;
+import com.intellij.openapi.application.AccessToken;
+import com.intellij.openapi.application.WriteAction;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.extensions.Extensions;
+import com.intellij.openapi.module.*;
+import com.intellij.openapi.options.ConfigurationException;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.projectRoots.SdkTypeId;
+import com.intellij.openapi.roots.*;
+import com.intellij.openapi.roots.libraries.Library;
+import com.intellij.openapi.roots.libraries.LibraryTable;
+import com.intellij.openapi.roots.ui.configuration.DefaultModulesProvider;
+import com.intellij.openapi.roots.ui.configuration.ModulesProvider;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.LibrariesContainer;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.LibrariesContainerFactory;
+import com.intellij.openapi.ui.Messages;
+import com.intellij.openapi.util.InvalidDataException;
+import com.intellij.openapi.util.JDOMUtil;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.vfs.LocalFileSystem;
+import com.intellij.openapi.vfs.VfsUtil;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.packaging.artifacts.ModifiableArtifactModel;
+import com.intellij.projectImport.ProjectImportBuilder;
+import com.intellij.util.containers.MultiMap;
+import org.jdom.Element;
+import org.jdom.JDOMException;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import java.io.File;
+import java.io.IOException;
+import java.util.*;
+
+/**
+ * @author Eugene Zhuravlev
+ * Date: Jul 17, 2007
+ */
+public class ProjectFromSourcesBuilderImpl extends ProjectImportBuilder implements ProjectFromSourcesBuilder {
+ private static final Logger LOG = Logger.getInstance("#com.intellij.ide.util.projectWizard.importSources.impl.ProjectFromSourcesBuilderImpl");
+ private static final String NAME = "Existing Sources";
+ private String myBaseProjectPath;
+ private final List<ProjectConfigurationUpdater> myUpdaters = new ArrayList<ProjectConfigurationUpdater>();
+ private final Map<ProjectStructureDetector, ProjectDescriptor> myProjectDescriptors = new LinkedHashMap<ProjectStructureDetector, ProjectDescriptor>();
+ private MultiMap<ProjectStructureDetector, DetectedProjectRoot> myRoots = MultiMap.emptyInstance();
+ private final WizardContext myContext;
+ private final ModulesProvider myModulesProvider;
+ private Set<String> myModuleNames;
+ private Set<String> myProjectLibrariesNames;
+
+ public ProjectFromSourcesBuilderImpl(WizardContext context, ModulesProvider modulesProvider) {
+ myContext = context;
+ myModulesProvider = modulesProvider;
+ for (ProjectStructureDetector detector : ProjectStructureDetector.EP_NAME.getExtensions()) {
+ myProjectDescriptors.put(detector, new ProjectDescriptor());
+ }
+ }
+
+ @NotNull
+ @Override
+ public Set<String> getExistingModuleNames() {
+ if (myModuleNames == null) {
+ myModuleNames = new HashSet<String>();
+ for (Module module : myModulesProvider.getModules()) {
+ myModuleNames.add(module.getName());
+ }
+ }
+ return myModuleNames;
+ }
+
+ @NotNull
+ @Override
+ public Set<String> getExistingProjectLibraryNames() {
+ if (myProjectLibrariesNames == null) {
+ myProjectLibrariesNames = new HashSet<String>();
+ final LibrariesContainer container = LibrariesContainerFactory.createContainer(myContext, myModulesProvider);
+ for (Library library : container.getLibraries(LibrariesContainer.LibraryLevel.PROJECT)) {
+ myProjectLibrariesNames.add(library.getName());
+ }
+ }
+ return myProjectLibrariesNames;
+ }
+
+ @NotNull
+ @Override
+ public WizardContext getContext() {
+ return myContext;
+ }
+
+ public void setBaseProjectPath(final String contentRootPath) {
+ myBaseProjectPath = contentRootPath;
+ }
+
+ @Override
+ public String getBaseProjectPath() {
+ return myBaseProjectPath;
+ }
+
+ public void setProjectRoots(MultiMap<ProjectStructureDetector, DetectedProjectRoot> roots) {
+ myRoots = roots;
+ }
+
+ @NotNull
+ @Override
+ public Collection<DetectedProjectRoot> getProjectRoots(@NotNull ProjectStructureDetector detector) {
+ return myRoots.get(detector);
+ }
+
+ @NotNull
+ @Override
+ public String getName() {
+ return NAME;
+ }
+
+ @Override
+ public Icon getIcon() {
+ return AllIcons.Nodes.Folder;
+ }
+
+ @Override
+ public List getList() {
+ return null;
+ }
+
+ @Override
+ public boolean isMarked(Object element) {
+ return false;
+ }
+
+ @Override
+ public void setList(List list) throws ConfigurationException {
+ }
+
+ @Override
+ public void setOpenProjectSettingsAfter(boolean on) {
+ }
+
+ public void setFileToImport(String path) {
+ setBaseProjectPath(path);
+ }
+
+ public List<Module> commit(final Project project, final ModifiableModuleModel model, final ModulesProvider modulesProvider) {
+ final boolean fromProjectStructure = model != null;
+ ModifiableModelsProvider modelsProvider = new IdeaModifiableModelsProvider();
+ final LibraryTable.ModifiableModel projectLibraryTable = modelsProvider.getLibraryTableModifiableModel(project);
+ final Map<LibraryDescriptor, Library> projectLibs = new HashMap<LibraryDescriptor, Library>();
+ final List<Module> result = new ArrayList<Module>();
+ try {
+ AccessToken token = WriteAction.start();
+ try {
+ // create project-level libraries
+ for (ProjectDescriptor projectDescriptor : getSelectedDescriptors()) {
+ for (LibraryDescriptor lib : projectDescriptor.getLibraries()) {
+ if (lib.getLevel() == LibraryDescriptor.Level.PROJECT) {
+ final Collection<File> files = lib.getJars();
+ final Library projectLib = projectLibraryTable.createLibrary(lib.getName());
+ final Library.ModifiableModel libraryModel = projectLib.getModifiableModel();
+ for (File file : files) {
+ libraryModel.addRoot(VfsUtil.getUrlForLibraryRoot(file), OrderRootType.CLASSES);
+ }
+ libraryModel.commit();
+ projectLibs.put(lib, projectLib);
+ }
+ }
+ }
+ if (!fromProjectStructure) {
+ projectLibraryTable.commit();
+ }
+ }
+ finally {
+ token.finish();
+ }
+ }
+ catch (Exception e) {
+ LOG.info(e);
+ Messages.showErrorDialog(IdeBundle.message("error.adding.module.to.project", e.getMessage()), IdeBundle.message("title.add.module"));
+ }
+
+ final Map<ModuleDescriptor, Module> descriptorToModuleMap = new HashMap<ModuleDescriptor, Module>();
+
+ try {
+ AccessToken token = WriteAction.start();
+ try {
+ final ModifiableModuleModel moduleModel = fromProjectStructure ? model : ModuleManager.getInstance(project).getModifiableModel();
+ for (ProjectDescriptor descriptor : getSelectedDescriptors()) {
+ for (final ModuleDescriptor moduleDescriptor : descriptor.getModules()) {
+ final Module module;
+ if (moduleDescriptor.isReuseExistingElement()) {
+ final ExistingModuleLoader moduleLoader =
+ ImportImlMode.setUpLoader(FileUtil.toSystemIndependentName(moduleDescriptor.computeModuleFilePath()));
+ module = moduleLoader.createModule(moduleModel);
+ }
+ else {
+ module = createModule(descriptor, moduleDescriptor, projectLibs, moduleModel);
+ }
+ result.add(module);
+ descriptorToModuleMap.put(moduleDescriptor, module);
+ }
+ }
+
+ if (!fromProjectStructure) {
+ moduleModel.commit();
+ }
+ }
+ finally {
+ token.finish();
+ }
+ }
+ catch (Exception e) {
+ LOG.info(e);
+ Messages.showErrorDialog(IdeBundle.message("error.adding.module.to.project", e.getMessage()), IdeBundle.message("title.add.module"));
+ }
+
+ // setup dependencies between modules
+ try {
+ AccessToken token = WriteAction.start();
+ try {
+ for (ProjectDescriptor data : getSelectedDescriptors()) {
+ for (final ModuleDescriptor descriptor : data.getModules()) {
+ final Module module = descriptorToModuleMap.get(descriptor);
+ if (module == null) {
+ continue;
+ }
+ final Set<ModuleDescriptor> deps = descriptor.getDependencies();
+ if (deps.size() == 0) {
+ continue;
+ }
+ final ModifiableRootModel rootModel = ModuleRootManager.getInstance(module).getModifiableModel();
+ for (ModuleDescriptor dependentDescriptor : deps) {
+ final Module dependentModule = descriptorToModuleMap.get(dependentDescriptor);
+ if (dependentModule != null) {
+ rootModel.addModuleOrderEntry(dependentModule);
+ }
+ }
+ rootModel.commit();
+ }
+ }
+ }
+ finally {
+ token.finish();
+ }
+ }
+ catch (Exception e) {
+ LOG.info(e);
+ Messages.showErrorDialog(IdeBundle.message("error.adding.module.to.project", e.getMessage()), IdeBundle.message("title.add.module"));
+ }
+
+ AccessToken token = WriteAction.start();
+ try {
+ ModulesProvider updatedModulesProvider = fromProjectStructure ? modulesProvider : new DefaultModulesProvider(project);
+ for (ProjectConfigurationUpdater updater : myUpdaters) {
+ updater.updateProject(project, modelsProvider, updatedModulesProvider);
+ }
+ }
+ finally {
+ token.finish();
+ }
+
+
+ return result;
+ }
+
+ @Nullable
+ @Override
+ public List<Module> commit(Project project,
+ ModifiableModuleModel model,
+ ModulesProvider modulesProvider,
+ ModifiableArtifactModel artifactModel) {
+ return commit(project, model, modulesProvider);
+ }
+
+ public Collection<ProjectDescriptor> getSelectedDescriptors() {
+ return myProjectDescriptors.values();
+ }
+
+ public void addConfigurationUpdater(ProjectConfigurationUpdater updater) {
+ myUpdaters.add(updater);
+ }
+
+ public boolean hasRootsFromOtherDetectors(ProjectStructureDetector thisDetector) {
+ for (ProjectStructureDetector projectStructureDetector : Extensions.getExtensions(ProjectStructureDetector.EP_NAME)) {
+ if (projectStructureDetector != thisDetector && !getProjectRoots(projectStructureDetector).isEmpty()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @NotNull
+ private static Module createModule(ProjectDescriptor projectDescriptor, final ModuleDescriptor descriptor,
+ final Map<LibraryDescriptor, Library> projectLibs, final ModifiableModuleModel moduleModel)
+ throws InvalidDataException, IOException, ModuleWithNameAlreadyExists, JDOMException, ConfigurationException {
+
+ final String moduleFilePath = descriptor.computeModuleFilePath();
+ ModuleBuilder.deleteModuleFile(moduleFilePath);
+
+ final Module module = moduleModel.newModule(moduleFilePath, descriptor.getModuleType().getId());
+ final ModifiableRootModel modifiableModel = ModuleRootManager.getInstance(module).getModifiableModel();
+ setupRootModel(projectDescriptor, descriptor, modifiableModel, projectLibs);
+ descriptor.updateModuleConfiguration(module, modifiableModel);
+ modifiableModel.commit();
+ return module;
+ }
+
+ private static void setupRootModel(ProjectDescriptor projectDescriptor, final ModuleDescriptor descriptor,
+ final ModifiableRootModel rootModel, final Map<LibraryDescriptor, Library> projectLibs) {
+ final CompilerModuleExtension compilerModuleExtension = rootModel.getModuleExtension(CompilerModuleExtension.class);
+ compilerModuleExtension.setExcludeOutput(true);
+ rootModel.inheritSdk();
+
+ final Set<File> contentRoots = descriptor.getContentRoots();
+ for (File contentRoot : contentRoots) {
+ final LocalFileSystem lfs = LocalFileSystem.getInstance();
+ VirtualFile moduleContentRoot = lfs.refreshAndFindFileByPath(FileUtil.toSystemIndependentName(contentRoot.getPath()));
+ if (moduleContentRoot != null) {
+ final ContentEntry contentEntry = rootModel.addContentEntry(moduleContentRoot);
+ final Collection<DetectedProjectRoot> sourceRoots = descriptor.getSourceRoots(contentRoot);
+ for (DetectedProjectRoot srcRoot : sourceRoots) {
+ final String srcpath = FileUtil.toSystemIndependentName(srcRoot.getDirectory().getPath());
+ final VirtualFile sourceRoot = lfs.refreshAndFindFileByPath(srcpath);
+ if (sourceRoot != null) {
+ contentEntry.addSourceFolder(sourceRoot, shouldBeTestRoot(srcRoot.getDirectory()), getPackagePrefix(srcRoot));
+ }
+ }
+ }
+ }
+ compilerModuleExtension.inheritCompilerOutputPath(true);
+ final LibraryTable moduleLibraryTable = rootModel.getModuleLibraryTable();
+ for (LibraryDescriptor libDescriptor : ModuleInsight.getLibraryDependencies(descriptor, projectDescriptor.getLibraries())) {
+ final Library projectLib = projectLibs.get(libDescriptor);
+ if (projectLib != null) {
+ rootModel.addLibraryEntry(projectLib);
+ }
+ else {
+ // add as module library
+ final Collection<File> jars = libDescriptor.getJars();
+ for (File file : jars) {
+ Library library = moduleLibraryTable.createLibrary();
+ Library.ModifiableModel modifiableModel = library.getModifiableModel();
+ modifiableModel.addRoot(VfsUtil.getUrlForLibraryRoot(file), OrderRootType.CLASSES);
+ modifiableModel.commit();
+ }
+ }
+ }
+
+ }
+
+ public static String getPackagePrefix(final DetectedProjectRoot srcRoot) {
+ // TODO we can introduce DetectedProjectRootWithPackagePrefix interface
+ return srcRoot instanceof JavaModuleSourceRoot ? ((JavaModuleSourceRoot)srcRoot).getPackagePrefix() : "";
+ }
+
+ @NotNull
+ @Override
+ public ProjectDescriptor getProjectDescriptor(@NotNull ProjectStructureDetector detector) {
+ return myProjectDescriptors.get(detector);
+ }
+
+ private static boolean shouldBeTestRoot(final File srcRoot) {
+ if (isTestRootName(srcRoot.getName())) {
+ return true;
+ }
+ final File parentFile = srcRoot.getParentFile();
+ return parentFile != null && isTestRootName(parentFile.getName());
+ }
+
+ private static boolean isTestRootName(final String name) {
+ return "test".equalsIgnoreCase(name) ||
+ "tests".equalsIgnoreCase(name) ||
+ "testSource".equalsIgnoreCase(name) ||
+ "testSources".equalsIgnoreCase(name) ||
+ "testSrc".equalsIgnoreCase(name);
+ }
+
+ public interface ProjectConfigurationUpdater {
+ void updateProject(@NotNull Project project, @NotNull ModifiableModelsProvider modelsProvider, @NotNull ModulesProvider modulesProvider);
+ }
+
+ @Override
+ public boolean isSuitableSdkType(final SdkTypeId sdkTypeId) {
+ for (ProjectDescriptor projectDescriptor : getSelectedDescriptors()) {
+ for (ModuleDescriptor moduleDescriptor : projectDescriptor.getModules()) {
+ try {
+ final ModuleType moduleType = getModuleType(moduleDescriptor);
+ if (moduleType != null && !moduleType.createModuleBuilder().isSuitableSdkType(sdkTypeId)) return false;
+ }
+ catch (Exception ignore) {
+ }
+ }
+ }
+ return true;
+ }
+
+ @Nullable
+ private static ModuleType getModuleType(ModuleDescriptor moduleDescriptor) throws InvalidDataException, JDOMException, IOException {
+ if (moduleDescriptor.isReuseExistingElement()) {
+ final File file = new File(moduleDescriptor.computeModuleFilePath());
+ if (file.exists()) {
+ final Element rootElement = JDOMUtil.loadDocument(file).getRootElement();
+ final String type = rootElement.getAttributeValue("type");
+ if (type != null) {
+ return ModuleTypeManager.getInstance().findByID(type);
+ }
+ }
+ return null;
+ }
+ else {
+ return moduleDescriptor.getModuleType();
+ }
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/ide/util/projectWizard/importSources/util/CommonSourceRootDetectionUtil.java b/java/idea-ui/src/com/intellij/ide/util/projectWizard/importSources/util/CommonSourceRootDetectionUtil.java
new file mode 100644
index 0000000..23a936b
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/ide/util/projectWizard/importSources/util/CommonSourceRootDetectionUtil.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright 2000-2011 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.ide.util.projectWizard.importSources.util;
+
+import com.intellij.openapi.util.Pair;
+import com.intellij.openapi.util.SystemInfo;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.vfs.VfsUtilCore;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.util.NullableFunction;
+import com.intellij.util.text.CharArrayCharSequence;
+import org.jetbrains.annotations.Nullable;
+
+import java.io.File;
+import java.io.IOException;
+
+public abstract class CommonSourceRootDetectionUtil<F> {
+
+ protected CommonSourceRootDetectionUtil() {
+ }
+
+ @Nullable
+ public Pair<F, String> suggestRootForFileWithPackageStatement(F file,
+ F topmostPossibleRoot,
+ NullableFunction<CharSequence, String> packageNameFetcher,
+ boolean packagePrefixSupported) {
+ if (!isFile(file)) return null;
+
+ final CharSequence chars;
+ try {
+ chars = loadText(file);
+ }
+ catch (IOException e) {
+ return null;
+ }
+
+ String packageName = packageNameFetcher.fun(chars);
+ if (packageName != null) {
+ F root = getParentFile(file);
+ int index = packageName.length();
+ while (index > 0) {
+ int index1 = packageName.lastIndexOf('.', index - 1);
+ String token = packageName.substring(index1 + 1, index);
+ String dirName = getName(root);
+ final boolean equalsToToken = SystemInfo.isFileSystemCaseSensitive ? dirName.equals(token) : dirName.equalsIgnoreCase(token);
+ if (!equalsToToken || root.equals(topmostPossibleRoot)) {
+ String packagePrefix = packageName.substring(0, index);
+ if (!packagePrefixSupported && packagePrefix.length() > 0) {
+ return null;
+ }
+ return Pair.create(root, packagePrefix);
+ }
+ root = getParentFile(root);
+ if (root == null) {
+ return null;
+ }
+ index = index1;
+ }
+ return Pair.create(root, "");
+ }
+
+ return null;
+ }
+
+ protected abstract String getName(final F file);
+
+ @Nullable
+ protected abstract F getParentFile(final F file);
+
+ protected abstract CharSequence loadText(final F file) throws IOException;
+
+ protected abstract boolean isFile(final F file);
+
+ public static final CommonSourceRootDetectionUtil<File> IO_FILE = new CommonSourceRootDetectionUtil<File>() {
+
+ @Override
+ protected String getName(final File file) {
+ return file.getName();
+ }
+
+ @Override
+ protected File getParentFile(final File file) {
+ return file.getParentFile();
+ }
+
+ @Override
+ protected CharSequence loadText(final File file) throws IOException {
+ return new CharArrayCharSequence(FileUtil.loadFileText(file));
+ }
+
+ @Override
+ protected boolean isFile(final File file) {
+ return file.isFile();
+ }
+ };
+
+ public static final CommonSourceRootDetectionUtil<VirtualFile> VIRTUAL_FILE = new CommonSourceRootDetectionUtil<VirtualFile>() {
+
+ @Override
+ protected String getName(VirtualFile file) {
+ return file.getName();
+ }
+
+ @Override
+ protected VirtualFile getParentFile(final VirtualFile file) {
+ return file.getParent();
+ }
+
+ @Override
+ protected CharSequence loadText(final VirtualFile file) throws IOException {
+ return VfsUtilCore.loadText(file);
+ }
+
+ @Override
+ protected boolean isFile(final VirtualFile file) {
+ return !file.isDirectory();
+ }
+ };
+
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/components/impl/stores/IdeaProjectStoreClassProvider.java b/java/idea-ui/src/com/intellij/openapi/components/impl/stores/IdeaProjectStoreClassProvider.java
new file mode 100644
index 0000000..bf2294f
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/components/impl/stores/IdeaProjectStoreClassProvider.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2000-2009 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.components.impl.stores;
+
+import com.intellij.openapi.project.impl.ProjectStoreClassProvider;
+
+/**
+ * @author mike
+ */
+public class IdeaProjectStoreClassProvider implements ProjectStoreClassProvider {
+ @Override
+ public Class<? extends IComponentStore> getProjectStoreClass(final boolean isDefaultProject) {
+ return isDefaultProject ? DefaultProjectStoreImpl.class : IdeaProjectStoreImpl.class;
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/components/impl/stores/IdeaProjectStoreImpl.java b/java/idea-ui/src/com/intellij/openapi/components/impl/stores/IdeaProjectStoreImpl.java
new file mode 100644
index 0000000..18ad3a4
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/components/impl/stores/IdeaProjectStoreImpl.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2000-2012 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.components.impl.stores;
+
+import com.intellij.openapi.components.PathMacroManager;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.project.impl.ProjectImpl;
+import com.intellij.openapi.project.impl.convertors.Convertor01;
+import com.intellij.openapi.project.impl.convertors.Convertor12;
+import com.intellij.openapi.project.impl.convertors.Convertor23;
+import com.intellij.openapi.project.impl.convertors.Convertor34;
+import org.jdom.Element;
+
+/**
+ * @author mike
+ */
+public class IdeaProjectStoreImpl extends ProjectWithModulesStoreImpl {
+ public IdeaProjectStoreImpl(final ProjectImpl project) {
+ super(project);
+ }
+
+ @Override
+ protected StateStorageManager createStateStorageManager() {
+ return new ProjectStateStorageManager(PathMacroManager.getInstance(getComponentManager()).createTrackingSubstitutor(), myProject) {
+ @Override
+ public StorageData createWsStorageData() {
+ return new IdeaWsStorageData(ROOT_TAG_NAME, myProject);
+ }
+
+ @Override
+ public StorageData createIprStorageData() {
+ return new IdeaIprStorageData(ROOT_TAG_NAME, myProject);
+ }
+ };
+ }
+
+ private class IdeaWsStorageData extends WsStorageData {
+ public IdeaWsStorageData(final String rootElementName, final Project project) {
+ super(rootElementName, project);
+ }
+
+ public IdeaWsStorageData(final WsStorageData storageData) {
+ super(storageData);
+ }
+
+ @Override
+ public StorageData clone() {
+ return new IdeaWsStorageData(this);
+ }
+ }
+
+ private class IdeaIprStorageData extends IprStorageData {
+
+ public IdeaIprStorageData(final String rootElementName, Project project) {
+ super(rootElementName, project);
+ }
+
+ public IdeaIprStorageData(final IprStorageData storageData) {
+ super(storageData);
+ }
+
+ @Override
+ public StorageData clone() {
+ return new IdeaIprStorageData(this);
+ }
+
+ @Override
+ protected void convert(final Element root, final int originalVersion) {
+ if (originalVersion < 1) {
+ Convertor01.execute(root);
+ }
+ if (originalVersion < 2) {
+ Convertor12.execute(root);
+ }
+ if (originalVersion < 3) {
+ Convertor23.execute(root);
+ }
+ if (originalVersion < 4) {
+ Convertor34.execute(root, myFilePath, getConversionProblemsStorage());
+ }
+ }
+
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/project/impl/convertors/Convertor01.java b/java/idea-ui/src/com/intellij/openapi/project/impl/convertors/Convertor01.java
new file mode 100644
index 0000000..7f59e82
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/project/impl/convertors/Convertor01.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright 2000-2009 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.project.impl.convertors;
+
+import com.intellij.openapi.vfs.JarFileSystem;
+import com.intellij.openapi.vfs.LocalFileSystem;
+import com.intellij.openapi.vfs.VirtualFileManager;
+import org.jdom.Element;
+
+import java.util.List;
+import java.util.Iterator;
+
+@SuppressWarnings({"HardCodedStringLiteral"})
+public class Convertor01 {
+ private static final String VIRTUAL_FILE_MANAGER_CLASS = "com.intellij.vfs.VirtualFileManager";
+ private static final String JAR_FILE_SYSTEM_CLASS = "com.intellij.vfs.jar.JarFileSystem";
+ private static final String PROJECT_ROOT_CONTAINER_CLASS = "com.intellij.project.ProjectRootContainer";
+
+ private static final String SOURCE_PATH_ENTRY_ATTRIBUTE = "sourcePathEntry";
+ private static final String CLASS_PATH_ENTRY_ATTRIBUTE = "classPathEntry";
+ private static final String OUTPUT_PATH_ENTRY_ATTRIBUTE = "outputPathEntry";
+ private static final String JAVADOC_PATH_ENTRY_ATTRIBUTE = "javadocPathEntry";
+
+ public static void execute(Element root) {
+ Element rootContComponent = Util.findComponent(root, PROJECT_ROOT_CONTAINER_CLASS);
+ if (rootContComponent != null) {
+ for (Iterator iterator = rootContComponent.getChildren("root").iterator(); iterator.hasNext();) {
+ Element element = (Element)iterator.next();
+
+ String url = element.getAttributeValue("file");
+ if (url != null) {
+ boolean isJar = url.indexOf("!/") >= 0;
+ if (isJar) {
+ url = VirtualFileManager.constructUrl(JarFileSystem.PROTOCOL, url);
+ }
+ else {
+ url = VirtualFileManager.constructUrl(LocalFileSystem.PROTOCOL, url);
+ }
+ element.setAttribute("file", url);
+ }
+ Element propertyElement = new Element("property");
+ element.addContent(propertyElement);
+ propertyElement.setAttribute("name", "type");
+ propertyElement.setAttribute("value", "projectFiles");
+ }
+
+ }
+ else {
+ rootContComponent = new Element("component");
+ root.addContent(rootContComponent);
+ rootContComponent.setAttribute("class", PROJECT_ROOT_CONTAINER_CLASS);
+ }
+
+ Element vfManComponent = Util.findComponent(root, VIRTUAL_FILE_MANAGER_CLASS);
+ if (vfManComponent != null) {
+ for (Iterator iterator = vfManComponent.getChildren("fileSystem").iterator(); iterator.hasNext();) {
+ Element node = (Element)iterator.next();
+
+ String fileSystemClass = node.getAttributeValue("class");
+ boolean isJar = JAR_FILE_SYSTEM_CLASS.equals(fileSystemClass);
+ String path = null;
+ String rootType = null;
+
+ List children = node.getChildren();
+ for (Iterator i = children.iterator(); i.hasNext();) {
+ Element node1 = (Element)i.next();
+
+ if ("root".equals(node1.getName())) {
+ path = node1.getAttributeValue("path");
+ }
+ else if ("attribute".equals(node1.getName())) {
+ String name = node1.getAttributeValue("name");
+ if (SOURCE_PATH_ENTRY_ATTRIBUTE.equals(name)) {
+ rootType = "sourcePathEntry";
+ }
+ else if (CLASS_PATH_ENTRY_ATTRIBUTE.equals(name)) {
+ rootType = "classPathEntry";
+ }
+ else if (OUTPUT_PATH_ENTRY_ATTRIBUTE.equals(name)) {
+ rootType = "outputPath";
+ }
+ else if (JAVADOC_PATH_ENTRY_ATTRIBUTE.equals(name)) {
+ rootType = "javadocPathEntry";
+ }
+ /*
+ else if (EXTERNAL_ATTRIBUTE.equals(name)){
+ isExternal = true;
+ }
+ */
+ }
+ }
+
+ String url;
+ if (isJar) {
+ path += JarFileSystem.JAR_SEPARATOR;
+ url = VirtualFileManager.constructUrl(JarFileSystem.PROTOCOL, path);
+ }
+ else {
+ url = VirtualFileManager.constructUrl(LocalFileSystem.PROTOCOL, path);
+ }
+
+ Element element = new Element("root");
+ rootContComponent.addContent(element);
+ element.setAttribute("file", url);
+
+ Element propertyElement = new Element("property");
+ element.addContent(propertyElement);
+ propertyElement.setAttribute("name", "type");
+ if (rootType != null) {
+ propertyElement.setAttribute("value", rootType);
+ }
+
+ /*
+ if (isExternal){
+ propertyElement = document.createElement("property");
+ element.appendChild(propertyElement);
+ propertyElement.setAttribute("name", ProjectRoot.PROP_EXTERNAL);
+ }
+ */
+ }
+
+ root.removeContent(vfManComponent);
+ }
+ }
+}
\ No newline at end of file
diff --git a/java/idea-ui/src/com/intellij/openapi/project/impl/convertors/Convertor12.java b/java/idea-ui/src/com/intellij/openapi/project/impl/convertors/Convertor12.java
new file mode 100644
index 0000000..733ad53
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/project/impl/convertors/Convertor12.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2000-2009 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.project.impl.convertors;
+
+import org.jdom.Element;
+
+@SuppressWarnings({"HardCodedStringLiteral"})
+public class Convertor12 {
+ private static final String OLD_PROJECT_ROOT_CONTAINER_CLASS = "com.intellij.project.ProjectRootContainer";
+ private static final String NEW_PROJECT_ROOT_CONTAINER_CLASS = "com.intellij.projectRoots.ProjectRootContainer";
+
+ public static void execute(Element root) {
+ Element rootContComponent = Util.findComponent(root, OLD_PROJECT_ROOT_CONTAINER_CLASS);
+ if (rootContComponent != null) {
+ rootContComponent.setAttribute("class", NEW_PROJECT_ROOT_CONTAINER_CLASS);
+ }
+ }
+}
\ No newline at end of file
diff --git a/java/idea-ui/src/com/intellij/openapi/project/impl/convertors/Convertor23.java b/java/idea-ui/src/com/intellij/openapi/project/impl/convertors/Convertor23.java
new file mode 100644
index 0000000..f38aaa0
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/project/impl/convertors/Convertor23.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright 2000-2009 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.project.impl.convertors;
+
+import com.intellij.openapi.roots.ProjectRootManager;
+import com.intellij.openapi.vfs.LocalFileSystem;
+import com.intellij.openapi.vfs.VirtualFileManager;
+import org.jdom.Element;
+
+import java.util.Iterator;
+
+/**
+ * @author mike
+ */
+@SuppressWarnings({"HardCodedStringLiteral"})
+public class Convertor23 {
+ private static final String PROJECT_ROOT_CONTAINER_CLASS = "com.intellij.projectRoots.ProjectRootContainer";
+
+ public static void execute(Element root) {
+ Element rootContComponent = Util.findComponent(root, PROJECT_ROOT_CONTAINER_CLASS);
+ if (rootContComponent != null) {
+ rootContComponent.setAttribute("class", ProjectRootManager.class.getName());
+ }
+ convertCompilerConfiguration(root);
+ }
+
+ private static final String COMPILER_CONFIGURATION_CLASS = "com.intellij.openapi.compiler.Compiler";
+ private static final String COMPILER_CONFIGURATION_COMPONENT = "CompilerConfiguration";
+ private static final String COMPILER_WORKSPACE_CONFIGURATION_COMPONENT = "CompilerWorkspaceConfiguration";
+
+ private static final String ATTR_CLASS = "class";
+ private static final String ATTR_OPTION = "option";
+ private static final String ATTR_NAME = "name";
+ private static final String ATTR_VALUE = "value";
+ private static final String ATTR_PATH = "path";
+ private static final String ATTR_URL = "url";
+ private static final String ATTR_EXCLUDE_FROM_COMPILE = "excludeFromCompile";
+
+ public static void convertCompilerConfiguration(Element root){
+ String compileInBackgroundValue = null;
+ Element component = Util.findComponent(root, COMPILER_CONFIGURATION_CLASS);
+ if(component != null){
+ component.setAttribute(ATTR_NAME, COMPILER_CONFIGURATION_COMPONENT);
+ component.removeAttribute(ATTR_CLASS);
+ for(Iterator children = component.getChildren().iterator(); children.hasNext();){
+ Element element = (Element)children.next();
+ String elementName = element.getName();
+ if(ATTR_OPTION.equals(elementName)){
+ String name = element.getAttributeValue(ATTR_NAME);
+ if(name != null){
+ if(name.equals("COMPILE_IN_BACKGROUND")){
+ compileInBackgroundValue = element.getAttributeValue(ATTR_VALUE);
+ }
+ }
+ }
+ else if (ATTR_EXCLUDE_FROM_COMPILE.equals(elementName)){
+ for(Iterator excludeIterator = element.getChildren().iterator(); excludeIterator.hasNext();){
+ Element excludeElement = (Element)excludeIterator.next();
+ String path = excludeElement.getAttributeValue(ATTR_PATH);
+ if(path != null){
+ String url = VirtualFileManager.constructUrl(LocalFileSystem.PROTOCOL, path);
+ excludeElement.removeAttribute(ATTR_PATH);
+ excludeElement.setAttribute(ATTR_URL, url);
+ }
+ }
+ }
+ }
+ }
+ if(compileInBackgroundValue != null){
+ component = Util.findComponent(root, COMPILER_WORKSPACE_CONFIGURATION_COMPONENT);
+ if(component == null){
+ component = new Element("component");
+ component.setAttribute(ATTR_NAME, COMPILER_WORKSPACE_CONFIGURATION_COMPONENT);
+ root.addContent(component);
+ }
+ boolean added = false;
+ for(Iterator children = component.getChildren().iterator(); children.hasNext();){
+ Element element = (Element)children.next();
+ String elementName = element.getName();
+ if(ATTR_OPTION.equals(elementName)){
+ String name = element.getAttributeValue(ATTR_NAME);
+ if(name != null){
+ if(name.equals("COMPILE_IN_BACKGROUND")){
+ element.setAttribute(ATTR_VALUE, compileInBackgroundValue);
+ added = true;
+ }
+ }
+ }
+ }
+ if(!added){
+ Element compileInBackgroundElement = new Element(ATTR_OPTION);
+ compileInBackgroundElement.setAttribute(ATTR_NAME, "COMPILE_IN_BACKGROUND");
+ compileInBackgroundElement.setAttribute(ATTR_VALUE, compileInBackgroundValue);
+ component.addContent(compileInBackgroundElement);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/java/idea-ui/src/com/intellij/openapi/project/impl/convertors/Convertor34.java b/java/idea-ui/src/com/intellij/openapi/project/impl/convertors/Convertor34.java
new file mode 100644
index 0000000..8be1b7c
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/project/impl/convertors/Convertor34.java
@@ -0,0 +1,802 @@
+/*
+ * Copyright 2000-2012 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.project.impl.convertors;
+
+import com.intellij.ide.fileTemplates.FileTemplateManager;
+import com.intellij.ide.highlighter.ModuleFileType;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.project.ProjectBundle;
+import com.intellij.openapi.projectRoots.impl.ProjectRootUtil;
+import com.intellij.openapi.roots.libraries.LibraryTablesRegistrar;
+import com.intellij.openapi.util.JDOMUtil;
+import com.intellij.openapi.vfs.JarFileSystem;
+import com.intellij.openapi.vfs.LocalFileSystem;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.openapi.vfs.VirtualFileManager;
+import org.jdom.Attribute;
+import org.jdom.Document;
+import org.jdom.Element;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.Nullable;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.*;
+
+/**
+ * @author max, dsl
+ */
+public class Convertor34 {
+ private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.project.impl.convertors.Convertor34");
+
+ @NonNls public static final String PROJECT_ROOT_MANAGER = "ProjectRootManager";
+ @NonNls public static final String PROJECT_ROOT_MANAGER_CLASS = "com.intellij.openapi.projectRoots.ProjectRootManager";
+
+ private static final String SOURCE_ROOTS_NOT_UNDER_PROJECT_ROOTS = ProjectBundle.message("project.convert.source.roots.not.under.project.roots.error");
+ private static final String JAVA_DOC_ROOTS_CANNOT_BE_CONVERTED = ProjectBundle.message("project.convert.javadoc.paths.error");
+ private static final String MULTIPLE_OUTPUT_PATHS = ProjectBundle.message("project.convert.multiple.output.paths.error");
+
+ public static void execute(Element root, String filePath, @Nullable ArrayList<String> conversionProblems) {
+ if (filePath == null) return;
+
+ if (conversionProblems == null) {
+ conversionProblems = new ArrayList<String>();
+ }
+ convertProjectFile(root, filePath, conversionProblems);
+ }
+
+ @SuppressWarnings({"HardCodedStringLiteral"})
+ public static String convertLibraryTable34(Element root, String filePath) {
+ if (filePath == null) return null;
+ final Element libraryTable = findNamedChild(root, "component", "ProjectLibraryTable");
+ if (libraryTable == null) return null;
+
+ final Element applicationLibraryTable = new Element("component");
+ applicationLibraryTable.setAttribute("name", "libraryTable");
+
+ final List oldLibraries = libraryTable.getChildren("library");
+ for (int i = 0; i < oldLibraries.size(); i++) {
+ Element oldLibrary = (Element)oldLibraries.get(i);
+ Element newLibrary = convertLibrary(oldLibrary);
+ applicationLibraryTable.addContent(newLibrary);
+ }
+
+ final String ioFilePath = filePath.replace('/', File.separatorChar);
+ String parentPath = new File(ioFilePath).getParent();
+ if (parentPath == null) parentPath = ".";
+ parentPath += "/applicationLibraries.xml";
+ final Element newRoot = new Element("application");
+ newRoot.addContent(applicationLibraryTable);
+ final Document libraryTableDocument = new Document(newRoot);
+ try {
+ JDOMUtil.writeDocument(libraryTableDocument, parentPath, "\n");
+ }
+ catch (IOException e) {
+ LOG.error(e);
+ return null;
+ }
+ return parentPath;
+ }
+
+ @SuppressWarnings({"HardCodedStringLiteral"})
+ private static Element convertLibrary(Element oldLibrary) {
+ final Element library = new Element("library");
+ final Element nameChild = oldLibrary.getChild("name");
+ LOG.assertTrue(nameChild != null);
+ library.setAttribute("name", nameChild.getAttributeValue("value"));
+
+ processLibraryRootContainer(oldLibrary, library, "CLASSES", "classPath");
+ processLibraryRootContainer(oldLibrary, library, "JAVADOC", "javadocPath");
+ processLibraryRootContainer(oldLibrary, library, "SOURCES", "sourcePath");
+ return library;
+ }
+
+ @SuppressWarnings({"HardCodedStringLiteral"})
+ private static void processLibraryRootContainer(Element oldLibrary,
+ final Element library,
+ String newElementType,
+ String oldElementType) {
+ final Element elementCLASSES = new Element(newElementType);
+ final Element rootsElement = oldLibrary.getChild("roots");
+ final Element classPath = rootsElement.getChild(oldElementType);
+ if (classPath != null) {
+ processRootTypeElement(classPath, new SimpleRootProcessor(elementCLASSES));
+ }
+ library.addContent(elementCLASSES);
+ }
+
+ @SuppressWarnings({"HardCodedStringLiteral"})
+ private static void convertProjectFile(Element root, String filePath, ArrayList<String> conversionProblems) {
+ Element rootComponent = null;
+ List components = root.getChildren("component");
+ for (final Object component1 : components) {
+ Element component = (Element)component1;
+ if (isProjectRootManager(component)) rootComponent = component;
+ }
+
+ if (rootComponent == null) return;
+
+ Element module = createModule(root);
+
+ final String moduleFilePath = filePath.substring(0, filePath.lastIndexOf('.')) + ModuleFileType.DOT_DEFAULT_EXTENSION;
+
+ Element moduleRootComponent = convertProjectRootManager(rootComponent, conversionProblems);
+ module.addContent(moduleRootComponent);
+
+ Document moduleDoc = new Document(module);
+
+ try {
+ JDOMUtil.writeDocument(moduleDoc, moduleFilePath, "\n");
+ }
+ catch (IOException e) {
+ LOG.error(e);
+ }
+
+ LocalFileSystem.getInstance().refreshAndFindFileByIoFile(new File(moduleFilePath));
+
+ rootComponent.setAttribute("name", "ProjectRootManager");
+ rootComponent.setAttribute("version", "4");
+
+ Element moduleManager = new Element("component");
+ moduleManager.setAttribute("name", "ProjectModuleManager");
+ addModule(moduleFilePath, moduleManager);
+
+ String moduleFile = new File(moduleFilePath).getName();
+ convertWebApps(moduleManager, root, rootComponent, moduleFile.substring(0, moduleFile.lastIndexOf('.')));
+ root.addContent(moduleManager);
+ }
+
+ @SuppressWarnings({"HardCodedStringLiteral"})
+ private static Element createModule(Element root) {
+ Element module = new Element("module");
+ module.setAttribute("version", "4");
+ String relativePaths = root.getAttributeValue("relativePaths");
+ if (relativePaths != null) {
+ module.setAttribute("relativePaths", relativePaths);
+ }
+ return module;
+ }
+
+ @SuppressWarnings({"HardCodedStringLiteral"})
+ private static void convertWebApps(Element moduleManager, Element projectElement, Element projectRootManager, String mainModule) {
+ Element webRootContainer = findNamedChild(projectElement, "component", "WebRootContainer");
+ if(webRootContainer == null) return;
+ List roots = webRootContainer.getChildren("root");
+
+ for (Iterator iterator = roots.iterator(); iterator.hasNext();) {
+ Element root = (Element)iterator.next();
+ String name = root.getAttributeValue("name");
+ String url = root.getAttributeValue("url");
+
+ if(name == null || url == null) continue;
+
+ String filepath = VirtualFileManager.extractPath(url);
+
+ VirtualFile moduleDirectory = LocalFileSystem.getInstance().findFileByPath(filepath);
+ if(moduleDirectory != null) {
+ Element module = createModule(projectElement);
+ module.setAttribute("type", "J2EE_WEB_MODULE");
+
+ Element rootManager = createWebModuleRootManager(module, moduleDirectory, projectRootManager, mainModule);
+ module.addContent(rootManager);
+
+ Element buildComponent = createWebModuleBuildComponent();
+ module.addContent(buildComponent);
+
+ Element moduleProperties = createWebModuleProperties(moduleDirectory);
+ module.addContent(moduleProperties);
+
+ Document moduleDocument = new Document(module);
+ String moduleName = (!"".equals(name) ? name : moduleDirectory.getName());
+ if(moduleName.equals(mainModule)) moduleName = "web" + moduleName;
+ try {
+ final String modulePath = moduleDirectory.getPath() + "/" + moduleName + ModuleFileType.DOT_DEFAULT_EXTENSION;
+ JDOMUtil.writeDocument(moduleDocument, modulePath, "\n");
+ addModule(modulePath, moduleManager);
+
+ ApplicationManager.getApplication().runWriteAction(new Runnable() {
+ @Override
+ public void run() {
+ LocalFileSystem.getInstance().refreshAndFindFileByIoFile(new File(modulePath));
+ }
+ });
+
+ }
+ catch (IOException e) {
+ LOG.error(e);
+ }
+ }
+ }
+ }
+
+ @SuppressWarnings({"HardCodedStringLiteral"})
+ private static void addSetting(Element parent, String name, String value){
+ Element option = new Element("setting");
+ option.setAttribute("name", name);
+ option.setAttribute("value", value);
+ parent.addContent(option);
+ }
+
+ @SuppressWarnings({"HardCodedStringLiteral"})
+ private static Element createWebModuleProperties(VirtualFile moduleDirectory) {
+ Element component = new Element("component");
+ component.setAttribute("name", "WebModuleProperties");
+
+ try {
+ BufferedWriter writer = null;
+ try {
+ writer = new BufferedWriter(new FileWriter(new File(moduleDirectory.getPath(), "WEB-INF/web.xml")));
+ writer.write(FileTemplateManager.getInstance().getJ2eeTemplate("web.2_3.xml").getText());
+ }
+ finally {
+ if (writer != null) {
+ writer.close();
+ }
+ }
+ }
+ catch (IOException e) {
+ LOG.error(e);
+ }
+
+ Element webRoots = new Element("webroots");
+ component.addContent(webRoots);
+ Element root = new Element("root");
+ webRoots.addContent(root);
+ root.setAttribute("url", moduleDirectory.getUrl());
+ root.setAttribute("relative", "/");
+ return component;
+ }
+
+ @SuppressWarnings({"HardCodedStringLiteral"})
+ private static Element createWebModuleBuildComponent() {
+ Element component = new Element("component");
+ component.setAttribute("name", "WebModuleBuildComponent");
+
+ addSetting(component, "EXPLODED_URL", "file://$MODULE_DIR$");
+ addSetting(component, "EXPLODED_ENABLED", "true");
+
+ return component;
+ }
+
+ @SuppressWarnings({"HardCodedStringLiteral"})
+ private static Element createWebModuleRootManager(Element module, VirtualFile moduleDirectory, Element projectRootManager, String mainModule) {
+ Element newModuleRootManager = new Element("component");
+ newModuleRootManager.setAttribute("name", "NewModuleRootManager");
+
+ Element jdk = projectRootManager.getChild("jdk");
+ if(jdk != null) {
+ String jdkName = jdk.getAttributeValue("name");
+ if (jdkName != null) {
+ Element orderEntry = new Element("orderEntry");
+ orderEntry.setAttribute("type", "jdk");
+ orderEntry.setAttribute("jdkName", jdkName);
+ newModuleRootManager.addContent(orderEntry);
+ }
+ }
+
+ Element orderEntry = new Element("orderEntry");
+ orderEntry.setAttribute("type", "module");
+ orderEntry.setAttribute("module-name", mainModule);
+ newModuleRootManager.addContent(orderEntry);
+
+ Element output = new Element("output");
+ output.setAttribute("url", "file://" + getModulePath("classes", moduleDirectory.getPath()));
+ newModuleRootManager.addContent(output);
+
+ Element content = new Element("content");
+ content.setAttribute("url", "file://" + getModulePath("", moduleDirectory.getPath()));
+ newModuleRootManager.addContent(content);
+
+ VirtualFile classesDir = moduleDirectory.findFileByRelativePath("WEB-INF/classes");
+ if(classesDir != null) {
+ Element classes = createLibraryEntry(classesDir, module, moduleDirectory);
+ newModuleRootManager.addContent(classes);
+ }
+
+ VirtualFile lib = moduleDirectory.findFileByRelativePath("WEB-INF/lib");
+ if(lib != null) {
+ for (VirtualFile virtualFile : lib.getChildren()) {
+ Element libEntry = createLibraryEntry(virtualFile, module, moduleDirectory);
+ newModuleRootManager.addContent(libEntry);
+ }
+ }
+
+ return newModuleRootManager;
+ }
+
+ @SuppressWarnings({"HardCodedStringLiteral"})
+ private static Element createLibraryEntry(VirtualFile file, Element module, VirtualFile moduleDirectory) {
+ String path = file.getPath().substring(moduleDirectory.getPath().length() + 1);
+ if(file.getFileSystem() instanceof JarFileSystem) {
+ path = path + "!/";
+ }
+
+ Element orderEntry = new Element("orderEntry");
+ orderEntry.setAttribute("type", "module-library");
+ Element library = new Element("library");
+ orderEntry.addContent(library);
+ Element classes = new Element("CLASSES");
+ library.addContent(classes);
+ Element root = new Element("root");
+ root.setAttribute("url", "file://" + getModulePath(path, moduleDirectory.getPath()));
+ classes.addContent(root);
+ return orderEntry;
+ }
+
+ private static String getModulePath(String path, String moduleDirectory) {
+ return "".equals(path) ? moduleDirectory : moduleDirectory + "/" + path;
+ }
+
+ @SuppressWarnings({"HardCodedStringLiteral"})
+ private static void addModule(final String moduleFilePath, Element moduleManager) {
+ Element moduleEntry = new Element("module");
+ final String moduleVfsPath = moduleFilePath.replace(File.separatorChar, '/');
+ moduleEntry.setAttribute("filepath", moduleVfsPath);
+ moduleEntry.setAttribute("fileurl", "file://" +moduleVfsPath);
+ Element modulesEntry = moduleManager.getChild("modules");
+ if(modulesEntry == null) {
+ modulesEntry = new Element("modules");
+ moduleManager.addContent(modulesEntry);
+ }
+ modulesEntry.addContent(moduleEntry);
+ }
+
+ private static Element convertProjectRootManager(Element projectRootManager, ArrayList<String> conversionProblems) {
+ return new ProjectToModuleConverter(projectRootManager, conversionProblems).getModuleRootManager();
+ }
+
+ private static interface RootElementProcessor {
+ void processSimpleRoot(Element root);
+
+ void processJdkRoot(Element root);
+
+ void processOutputRoot(Element root);
+
+ void processExcludedOutputRoot(Element root);
+
+ void processLibraryRoot(Element root);
+
+ void processEjbRoot(Element root);
+ }
+
+ private static abstract class EmptyRootProcessor implements RootElementProcessor {
+ @Override
+ public void processSimpleRoot(Element root) {
+ cannotProcess(root);
+ }
+
+ @Override
+ public void processJdkRoot(Element root) {
+ cannotProcess(root);
+ }
+
+ @Override
+ public void processOutputRoot(Element root) {
+ cannotProcess(root);
+ }
+
+ @Override
+ public void processExcludedOutputRoot(Element root) {
+ cannotProcess(root);
+ }
+
+ @Override
+ public void processLibraryRoot(Element root) {
+ cannotProcess(root);
+ }
+
+ @Override
+ public void processEjbRoot(Element root) {
+ cannotProcess(root);
+ }
+
+ @SuppressWarnings({"HardCodedStringLiteral"})
+ protected void cannotProcess(Element root) {
+ LOG.error("Cannot process roots of type " + root.getAttributeValue("type") + " in " + classId());
+ }
+
+ abstract protected String classId();
+
+ }
+
+ @SuppressWarnings({"HardCodedStringLiteral"})
+ private static void processRoot(Element root, RootElementProcessor processor) {
+ LOG.assertTrue("root".equals(root.getName()));
+ final String type = root.getAttributeValue("type");
+ LOG.assertTrue(type != null);
+ if (ProjectRootUtil.SIMPLE_ROOT.equals(type)) {
+ processor.processSimpleRoot(root);
+ }
+ else if (ProjectRootUtil.OUTPUT_ROOT.equals(type)) {
+ processor.processOutputRoot(root);
+ }
+ else if (ProjectRootUtil.JDK_ROOT.equals(type)) {
+ processor.processJdkRoot(root);
+ }
+ else if (ProjectRootUtil.EXCLUDED_OUTPUT.equals(type)) {
+ processor.processExcludedOutputRoot(root);
+ }
+ else if (ProjectRootUtil.LIBRARY_ROOT.equals(type)) {
+ processor.processLibraryRoot(root);
+ }
+ else if (ProjectRootUtil.EJB_ROOT.equals(type)) {
+ processor.processEjbRoot(root);
+ }
+ else if (ProjectRootUtil.COMPOSITE_ROOT.equals(type)) {
+ final List children = root.getChildren("root");
+ for (int i = 0; i < children.size(); i++) {
+ Element element = (Element)children.get(i);
+ processRoot(element, processor);
+ }
+ }
+ else {
+ LOG.error("Unknown root type: " + type);
+ }
+ }
+
+ @SuppressWarnings({"HardCodedStringLiteral"})
+ private static void processRootTypeElement(Element rootTypeElement, RootElementProcessor rootProcessor) {
+ if (rootTypeElement == null) return;
+ final List children = rootTypeElement.getChildren("root");
+ for (int i = 0; i < children.size(); i++) {
+ Element element = (Element)children.get(i);
+ processRoot(element, rootProcessor);
+ }
+ }
+
+
+ @SuppressWarnings({"HardCodedStringLiteral"})
+ private static class ProjectToModuleConverter {
+ private final Element myProjectRootManager;
+ private final Element myModuleRootManager;
+ private final ArrayList<String> myProjectRoots;
+ private final List<String> mySourceFolders;
+ private final ArrayList<String> myExcludeFolders;
+ private final ArrayList<String> myDetectedProblems;
+
+ private ProjectToModuleConverter(Element projectRootManager, ArrayList<String> problems) {
+ myProjectRootManager = projectRootManager;
+ myModuleRootManager = new Element("component");
+ myModuleRootManager.setAttribute("name", "NewModuleRootManager");
+ myProjectRoots = new ArrayList<String>();
+ myDetectedProblems = problems;
+
+ final Element projectPath = projectRootManager.getChild("projectPath");
+ if(projectPath != null) {
+ processRootTypeElement(projectPath, new ProjectRootProcessor());
+ }
+ Collections.sort(myProjectRoots);
+ for (int i = 0; i < myProjectRoots.size(); i++) {
+ String path = myProjectRoots.get(i);
+ final int next = i + 1;
+ while (next < myProjectRoots.size() && myProjectRoots.get(next).startsWith(path)) {
+ myProjectRoots.remove(next);
+ }
+ }
+
+ final Element sourcePath = projectRootManager.getChild("sourcePath");
+ mySourceFolders = new ArrayList<String>();
+ processRootTypeElement(sourcePath, new SourceRootProcessor());
+
+ final Element excludePath = projectRootManager.getChild("excludePath");
+ myExcludeFolders = new ArrayList<String>();
+ processRootTypeElement(excludePath, new ExcludeRootsProcessor());
+
+ Element javadocPath = projectRootManager.getChild("javadocPath");
+ processRootTypeElement(javadocPath, new JavaDocRootProcessor());
+
+ final Element patternExcludeFolder = new Element("excludeFolder");
+ final Element patternSourceFolder = new Element("sourceFolder");
+ patternSourceFolder.setAttribute("isTestSource", "false");
+ Map<String, List<String>> contentToSource = dispatchFolders(myProjectRoots, mySourceFolders);
+ final Map<String, List<String>> contentToExclude = dispatchFolders(myProjectRoots, myExcludeFolders);
+
+ for (String root : myProjectRoots) {
+ final Element contentElement = new Element("content");
+ contentElement.setAttribute("url", root);
+ createFolders(contentElement, patternSourceFolder, contentToSource.get(root));
+ createFolders(contentElement, patternExcludeFolder, contentToExclude.get(root));
+ myModuleRootManager.addContent(contentElement);
+ }
+
+ final Element classPath = projectRootManager.getChild("classPath");
+ processRootTypeElement(classPath, new ClassPathRootProcessor());
+
+ final Element projectElement = (Element)myProjectRootManager.getParent();
+ final Element compilerConfigurationElement = findNamedChild(projectElement, "component", "CompilerConfiguration");
+ if (compilerConfigurationElement != null) {
+ final Element option = findNamedChild(compilerConfigurationElement, "option", "DEFAULT_OUTPUT_PATH");
+ final String path = option == null ? null : option.getAttributeValue("value");
+
+ if (path != null) {
+ final String url = "file://" + path;
+ final Element outputElement = new Element("output");
+ outputElement.setAttribute("url", url);
+ myModuleRootManager.addContent(outputElement);
+ final Element outputTestElement = new Element("output-test");
+ outputTestElement.setAttribute("url", url);
+ myModuleRootManager.addContent(outputTestElement);
+ }
+ // check for multiple outputs
+ {
+ final Element outputMode = findNamedChild(compilerConfigurationElement, "option", "OUTPUT_MODE");
+ final String attributeValue = outputMode != null ? outputMode.getAttributeValue("value") : "";
+ if ("multiple".equals(attributeValue)) {
+ addProblem(MULTIPLE_OUTPUT_PATHS);
+ }
+ }
+ }
+
+ final Element excludeOutput = myProjectRootManager.getChild("exclude_output");
+ if (excludeOutput != null) {
+ final String enabled = excludeOutput.getAttributeValue("enabled");
+ if ("yes".equals(enabled) || "true".equals(enabled)) {
+ myModuleRootManager.addContent(new Element("exclude-output"));
+ }
+ }
+ }
+
+ private static void createFolders(final Element contentElement,
+ final Element patternFolderElement,
+ final List<String> folders) {
+ for (String folder : folders) {
+ Element folderElement = (Element)patternFolderElement.clone();
+ folderElement.setAttribute("url", folder);
+ contentElement.addContent(folderElement);
+ }
+ }
+
+ private static Map<String, List<String>> dispatchFolders(ArrayList<String> projectRoots, List<String> folders) {
+ final Map<String, List<String>> result = new HashMap<String, List<String>>();
+ for (String root : projectRoots) {
+ final ArrayList<String> foldersForRoot = new ArrayList<String>();
+ result.put(root, foldersForRoot);
+ for (Iterator<String> iterator1 = folders.iterator(); iterator1.hasNext();) {
+ String folder = iterator1.next();
+ if (folder.startsWith(root)) {
+ foldersForRoot.add(folder);
+ }
+ }
+ Collections.sort(foldersForRoot);
+ }
+ return result;
+ }
+
+ public Element getModuleRootManager() { return myModuleRootManager; }
+
+ @SuppressWarnings({"HardCodedStringLiteral"})
+ private class JavaDocRootProcessor extends EmptyRootProcessor {
+
+ @Override
+ protected void cannotProcess(Element root) {
+ addProblem(JAVA_DOC_ROOTS_CANNOT_BE_CONVERTED);
+ }
+
+ @Override
+ protected String classId() {
+ return "JavaDocRootProcessor";
+ }
+ }
+
+ private class ProjectRootProcessor extends EmptyRootProcessor {
+ @Override
+ public void processSimpleRoot(Element root) {
+ final String value = root.getAttributeValue("url");
+ myProjectRoots.add(value);
+ }
+
+ @Override
+ public void processEjbRoot(Element root) {
+ // todo[cdr,dsl] implement conversion of EJB roots
+ }
+
+ @Override
+ protected String classId() {
+ return "ProjectRootProcessor";
+ }
+ }
+
+ private void addProblem(final String description) {
+ if (!myDetectedProblems.contains(description)) {
+ myDetectedProblems.add(description);
+ }
+ }
+
+ @SuppressWarnings({"HardCodedStringLiteral"})
+ private class SourceRootProcessor extends EmptyRootProcessor {
+
+ @Override
+ public void processSimpleRoot(Element root) {
+ final String url = root.getAttributeValue("url");
+ boolean found = false;
+ for (int i = 0; i < myProjectRoots.size(); i++) {
+ String projectPath = myProjectRoots.get(i);
+ if (url.startsWith(projectPath)) {
+ mySourceFolders.add(url);
+ found = true;
+ break;
+ }
+ else if (projectPath.startsWith(url)) {
+ myProjectRoots.remove(i);
+ myProjectRoots.add(i, url);
+ mySourceFolders.add(url);
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ addProblem(SOURCE_ROOTS_NOT_UNDER_PROJECT_ROOTS);
+ }
+ }
+
+ @Override
+ public void processJdkRoot(Element root) {
+ }
+
+ @Override
+ public void processLibraryRoot(Element root) {
+ }
+
+ @Override
+ public void processEjbRoot(Element root) {
+ // todo[cdr,dsl] implement conversion of EJB roots
+ }
+
+ @Override
+ protected String classId() {
+ return "SourceRootProcessor";
+ }
+ }
+
+ @SuppressWarnings({"HardCodedStringLiteral"})
+ private class ExcludeRootsProcessor extends EmptyRootProcessor {
+ @Override
+ public void processSimpleRoot(Element root) {
+ final String url = root.getAttributeValue("url");
+ for (int i = 0; i < myProjectRoots.size(); i++) {
+ String projectRoot = myProjectRoots.get(i);
+ if (url.startsWith(projectRoot)) {
+ myExcludeFolders.add(url);
+ }
+ }
+ }
+
+ @Override
+ public void processEjbRoot(Element root) {
+ // todo[cdr,dsl] implement conversion of EJB roots
+ }
+
+ @Override
+ protected String classId() {
+ return "ExcludeRootsProcessor";
+ }
+
+ @Override
+ public void processJdkRoot(Element root) {
+ // [dsl]: fix for SCR24517
+ // [dsl]: I have no idea how such project can be configured in Ariadna,
+ // [dsl]: and what does it mean, but such projects do exist....
+ }
+
+ @Override
+ public void processExcludedOutputRoot(Element root) {
+ }
+ }
+
+ @SuppressWarnings({"HardCodedStringLiteral"})
+ private class ClassPathRootProcessor extends EmptyRootProcessor {
+ @Override
+ public void processSimpleRoot(Element root) {
+ final Element orderEntry = new Element("orderEntry");
+ orderEntry.setAttribute("type", "module-library");
+ final Element libraryElement = new Element("library");
+ final Element classesElement = new Element("CLASSES");
+ final Element rootElement = new Element("root");
+ rootElement.setAttribute((Attribute)root.getAttribute("url").clone());
+ classesElement.addContent(rootElement);
+ libraryElement.addContent(classesElement);
+ orderEntry.addContent(libraryElement);
+ myModuleRootManager.addContent(orderEntry);
+ }
+
+ @Override
+ public void processJdkRoot(Element root) {
+ final Element orderEntry = new Element("orderEntry");
+ orderEntry.setAttribute("type", "jdk");
+ orderEntry.setAttribute("jdkName", root.getAttributeValue("name"));
+ myModuleRootManager.addContent(orderEntry);
+ }
+
+ @Override
+ public void processLibraryRoot(Element root) {
+ final String libraryName = root.getAttributeValue("name");
+ final Element orderEntry = new Element("orderEntry");
+ orderEntry.setAttribute("type", "library");
+ orderEntry.setAttribute("name", libraryName);
+ orderEntry.setAttribute("level", LibraryTablesRegistrar.APPLICATION_LEVEL);
+ myModuleRootManager.addContent(orderEntry);
+ }
+
+ @Override
+ public void processEjbRoot(Element root) {
+ // todo[cdr,dsl] implement conversion of EJB roots
+ }
+
+ @Override
+ protected String classId() {
+ return "ClassPathProcessor";
+ }
+
+ @Override
+ public void processOutputRoot(Element root) {
+ final Element orderEntry = new Element("orderEntry");
+ orderEntry.setAttribute("type", "sourceFolder");
+ myModuleRootManager.addContent(orderEntry);
+ }
+ }
+ }
+
+ @SuppressWarnings({"HardCodedStringLiteral"})
+ private static boolean isProjectRootManager(Element component) {
+ String compName = component.getAttributeValue("name");
+ String compClass = component.getAttributeValue("class");
+ return compName != null && compName.equals(PROJECT_ROOT_MANAGER) ||
+ compClass != null && compClass.equals(PROJECT_ROOT_MANAGER_CLASS);
+ }
+
+ @SuppressWarnings({"HardCodedStringLiteral"})
+ private static Element findNamedChild(Element root, String name, String nameAttributeValue) {
+ final List children = root.getChildren(name);
+ for (int i = 0; i < children.size(); i++) {
+ Element e = (Element)children.get(i);
+ if (nameAttributeValue.equals(e.getAttributeValue("name"))) {
+ return e;
+ }
+ }
+ return null;
+ }
+
+ @SuppressWarnings({"HardCodedStringLiteral"})
+ private static class SimpleRootProcessor extends EmptyRootProcessor {
+ private final Element myTargetElement;
+
+ public SimpleRootProcessor(Element targetElement) {
+ myTargetElement = targetElement;
+ }
+
+ @Override
+ public void processSimpleRoot(Element root) {
+ final String url = root.getAttributeValue("url");
+ final Element newRoot = new Element("root");
+ newRoot.setAttribute("url", url);
+ myTargetElement.addContent(newRoot);
+ }
+
+ @Override
+ public void processEjbRoot(Element root) {
+ // todo[cdr,dsl] implement conversion of EJB roots
+ }
+
+ @Override
+ protected String classId() {
+ return "SimpleRootProcessor";
+ }
+
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/project/impl/convertors/Util.java b/java/idea-ui/src/com/intellij/openapi/project/impl/convertors/Util.java
new file mode 100644
index 0000000..63ef8ed
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/project/impl/convertors/Util.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2000-2009 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.project.impl.convertors;
+
+import org.jdom.Element;
+
+import java.util.Iterator;
+
+class Util {
+ @SuppressWarnings({"HardCodedStringLiteral"})
+ static Element findComponent(Element root, String className) {
+ for (Iterator iterator = root.getChildren("component").iterator(); iterator.hasNext();) {
+ Element element = (Element)iterator.next();
+ String className1 = element.getAttributeValue("class");
+ if (className1 != null && className1.equals(className)) {
+ return element;
+ }
+ }
+ return null;
+ }
+}
\ No newline at end of file
diff --git a/java/idea-ui/src/com/intellij/openapi/projectRoots/ui/ProjectJdksEditor.java b/java/idea-ui/src/com/intellij/openapi/projectRoots/ui/ProjectJdksEditor.java
new file mode 100644
index 0000000..28a7a52
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/projectRoots/ui/ProjectJdksEditor.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2000-2009 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.projectRoots.ui;
+
+import com.intellij.ide.DataManager;
+import com.intellij.openapi.Disposable;
+import com.intellij.openapi.actionSystem.PlatformDataKeys;
+import com.intellij.openapi.options.ConfigurationException;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.project.ProjectBundle;
+import com.intellij.openapi.projectRoots.Sdk;
+import com.intellij.openapi.roots.ui.configuration.ProjectJdksConfigurable;
+import com.intellij.openapi.ui.DialogWrapper;
+import com.intellij.openapi.ui.Messages;
+import com.intellij.openapi.util.Disposer;
+
+import javax.swing.*;
+import java.awt.*;
+
+/**
+ * @author MYakovlev
+ */
+public class ProjectJdksEditor extends DialogWrapper {
+ private ProjectJdksConfigurable myConfigurable;
+ private Sdk myProjectJdk;
+
+
+ public ProjectJdksEditor(final Sdk jdk, Project project, Component parent) {
+ this(jdk, parent, new ProjectJdksConfigurable(project));
+ }
+
+ public ProjectJdksEditor(final Sdk jdk, Component parent, ProjectJdksConfigurable configurable) {
+ super(parent, true);
+ myConfigurable = configurable;
+ SwingUtilities.invokeLater(new Runnable(){
+ @Override
+ public void run() {
+ myConfigurable.selectNodeInTree(jdk != null ? jdk.getName() : null);
+ }
+ });
+ setTitle(ProjectBundle.message("sdk.configure.title"));
+ Disposer.register(myDisposable, new Disposable() {
+ @Override
+ public void dispose() {
+ if (myConfigurable != null) {
+ myConfigurable.disposeUIResources();
+ myConfigurable = null;
+ }
+ }
+ });
+ init();
+ }
+
+ public ProjectJdksEditor(Sdk jdk, Component parent){
+ this(jdk, PlatformDataKeys.PROJECT.getData(DataManager.getInstance().getDataContext()), parent);
+ }
+
+ @Override
+ protected JComponent createCenterPanel(){
+ myConfigurable.reset();
+ return myConfigurable.createComponent();
+ }
+
+ @Override
+ protected void doOKAction(){
+ try{
+ myProjectJdk = myConfigurable.getSelectedJdk(); //before dispose
+ myConfigurable.apply();
+ super.doOKAction();
+ }
+ catch (ConfigurationException e){
+ Messages.showMessageDialog(getContentPane(), e.getMessage(),
+ ProjectBundle.message("sdk.configure.save.settings.error"), Messages.getErrorIcon());
+ }
+ }
+
+ @Override
+ protected String getDimensionServiceKey(){
+ return "#com.intellij.openapi.projectRoots.ui.ProjectJdksEditor";
+ }
+
+ public Sdk getSelectedJdk(){
+ return myProjectJdk;
+ }
+
+}
\ No newline at end of file
diff --git a/java/idea-ui/src/com/intellij/openapi/projectRoots/ui/SdkEditor.java b/java/idea-ui/src/com/intellij/openapi/projectRoots/ui/SdkEditor.java
new file mode 100644
index 0000000..68e1741
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/projectRoots/ui/SdkEditor.java
@@ -0,0 +1,488 @@
+/*
+ * Copyright 2000-2012 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.projectRoots.ui;
+
+import com.intellij.openapi.Disposable;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.options.Configurable;
+import com.intellij.openapi.options.ConfigurationException;
+import com.intellij.openapi.project.ProjectBundle;
+import com.intellij.openapi.projectRoots.*;
+import com.intellij.openapi.projectRoots.impl.ProjectJdkImpl;
+import com.intellij.openapi.projectRoots.impl.SdkConfigurationUtil;
+import com.intellij.openapi.roots.OrderRootType;
+import com.intellij.openapi.roots.ui.OrderRootTypeUIFactory;
+import com.intellij.openapi.ui.Messages;
+import com.intellij.openapi.ui.TextFieldWithBrowseButton;
+import com.intellij.openapi.util.ActionCallback;
+import com.intellij.openapi.util.Comparing;
+import com.intellij.openapi.util.Disposer;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.ui.TabbedPaneWrapper;
+import com.intellij.ui.navigation.History;
+import com.intellij.ui.navigation.Place;
+import com.intellij.util.Consumer;
+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 javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+import java.awt.*;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.io.File;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * @author: MYakovlev
+ * @since Aug 15, 2002
+ */
+public class SdkEditor implements Configurable, Place.Navigator {
+ private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.projectRoots.ui.SdkEditor");
+ @NonNls private static final String SDK_TAB = "sdkTab";
+
+ private Sdk mySdk;
+ private final Map<OrderRootType, SdkPathEditor> myPathEditors = new HashMap<OrderRootType, SdkPathEditor>();
+
+ private TextFieldWithBrowseButton myHomeComponent;
+ private final Map<SdkType, AdditionalDataConfigurable> myAdditionalDataConfigurables = new HashMap<SdkType, AdditionalDataConfigurable>();
+ private final Map<AdditionalDataConfigurable, JComponent> myAdditionalDataComponents = new HashMap<AdditionalDataConfigurable, JComponent>();
+ private JPanel myAdditionalDataPanel;
+ private final SdkModificator myEditedSdkModificator = new EditedSdkModificator();
+
+ // GUI components
+ private JPanel myMainPanel;
+ private TabbedPaneWrapper myTabbedPane;
+ private final SdkModel mySdkModel;
+ private JLabel myHomeFieldLabel;
+ private String myVersionString;
+
+ private String myInitialName;
+ private String myInitialPath;
+ private final History myHistory;
+
+ private final Disposable myDisposable = Disposer.newDisposable();
+
+ public SdkEditor(SdkModel sdkModel, History history, final ProjectJdkImpl sdk) {
+ mySdkModel = sdkModel;
+ myHistory = history;
+ mySdk = sdk;
+ createMainPanel();
+ initSdk(sdk);
+ }
+
+ private void initSdk(Sdk sdk){
+ mySdk = sdk;
+ if (mySdk != null) {
+ myInitialName = mySdk.getName();
+ myInitialPath = mySdk.getHomePath();
+ } else {
+ myInitialName = "";
+ myInitialPath = "";
+ }
+ final AdditionalDataConfigurable additionalDataConfigurable = getAdditionalDataConfigurable();
+ if (additionalDataConfigurable != null) {
+ additionalDataConfigurable.setSdk(sdk);
+ }
+ if (myMainPanel != null){
+ reset();
+ }
+ }
+
+ @Override
+ public String getDisplayName(){
+ return ProjectBundle.message("sdk.configure.editor.title");
+ }
+
+ @Override
+ public String getHelpTopic(){
+ return null;
+ }
+
+ @Override
+ public JComponent createComponent(){
+ return myMainPanel;
+ }
+
+ private void createMainPanel(){
+ myMainPanel = new JPanel(new GridBagLayout());
+
+ myTabbedPane = new TabbedPaneWrapper(myDisposable);
+ for (OrderRootType type : OrderRootType.getAllTypes()) {
+ if (mySdk == null || showTabForType(type)) {
+ final SdkPathEditor pathEditor = OrderRootTypeUIFactory.FACTORY.getByKey(type).createPathEditor(mySdk);
+ if (pathEditor != null) {
+ pathEditor.setAddBaseDir(mySdk.getHomeDirectory());
+ myTabbedPane.addTab(pathEditor.getDisplayName(), pathEditor.createComponent());
+ myPathEditors.put(type, pathEditor);
+ }
+ }
+ }
+
+ myTabbedPane.addChangeListener(new ChangeListener() {
+ @Override
+ public void stateChanged(final ChangeEvent e) {
+ myHistory.pushQueryPlace();
+ }
+ });
+
+ myHomeComponent = createHomeComponent();
+ myHomeComponent.getTextField().setEditable(false);
+
+ myHomeFieldLabel = new JLabel(getHomeFieldLabelValue());
+ myMainPanel.add(myHomeFieldLabel, new GridBagConstraints(0, GridBagConstraints.RELATIVE, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(2, 0, 2, 2), 0, 0));
+ myMainPanel.add(myHomeComponent, new GridBagConstraints(1, GridBagConstraints.RELATIVE, 1, 1, 1.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(2, 2, 2, 0), 0, 0));
+
+ myAdditionalDataPanel = new JPanel(new BorderLayout());
+ myMainPanel.add(myAdditionalDataPanel, new GridBagConstraints(0, GridBagConstraints.RELATIVE, 2, 1, 1.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(2, 0, 0, 0), 0, 0));
+
+ myMainPanel.add(myTabbedPane.getComponent(), new GridBagConstraints(0, GridBagConstraints.RELATIVE, 2, 1, 1.0, 1.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(2, 0, 0, 0), 0, 0));
+ }
+
+ protected TextFieldWithBrowseButton createHomeComponent() {
+ return new TextFieldWithBrowseButton(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ doSelectHomePath();
+ }
+ });
+ }
+
+ protected boolean showTabForType(OrderRootType type) {
+ return ((SdkType) mySdk.getSdkType()).isRootTypeApplicable(type);
+ }
+
+ private String getHomeFieldLabelValue() {
+ if (mySdk != null) {
+ return ((SdkType) mySdk.getSdkType()).getHomeFieldLabel();
+ }
+ return ProjectBundle.message("sdk.configure.general.home.path");
+ }
+
+ @Override
+ public boolean isModified(){
+ boolean isModified = !Comparing.equal(mySdk == null? null : mySdk.getName(), myInitialName);
+ isModified = isModified || !Comparing.equal(FileUtil.toSystemIndependentName(getHomeValue()), FileUtil.toSystemIndependentName(myInitialPath));
+ for (PathEditor pathEditor : myPathEditors.values()) {
+ isModified = isModified || pathEditor.isModified();
+ }
+ final AdditionalDataConfigurable configurable = getAdditionalDataConfigurable();
+ if (configurable != null) {
+ isModified = isModified || configurable.isModified();
+ }
+ return isModified;
+ }
+
+ @Override
+ public void apply() throws ConfigurationException{
+ if(!Comparing.equal(myInitialName, mySdk == null ? "" : mySdk.getName())){
+ if(mySdk == null || mySdk.getName().isEmpty()){
+ throw new ConfigurationException(ProjectBundle.message("sdk.list.name.required.error"));
+ }
+ }
+ if (mySdk != null){
+ myInitialName = mySdk.getName();
+ myInitialPath = mySdk.getHomePath();
+ final SdkModificator sdkModificator = mySdk.getSdkModificator();
+ sdkModificator.setHomePath(getHomeValue().replace(File.separatorChar, '/'));
+ for (SdkPathEditor pathEditor : myPathEditors.values()) {
+ pathEditor.apply(sdkModificator);
+ }
+ ApplicationManager.getApplication().runWriteAction(new Runnable() { // fix SCR #29193
+ @Override
+ public void run() {
+ sdkModificator.commitChanges();
+ }
+ });
+ final AdditionalDataConfigurable configurable = getAdditionalDataConfigurable();
+ if (configurable != null) {
+ configurable.apply();
+ }
+ }
+ }
+
+ @Override
+ public void reset(){
+ if (mySdk == null){
+ setHomePathValue("");
+ for (SdkPathEditor pathEditor : myPathEditors.values()) {
+ pathEditor.reset(null);
+ }
+ }
+ else{
+ final SdkModificator sdkModificator = mySdk.getSdkModificator();
+ for (OrderRootType type : myPathEditors.keySet()) {
+ myPathEditors.get(type).reset(sdkModificator);
+ }
+ sdkModificator.commitChanges();
+ setHomePathValue(mySdk.getHomePath().replace('/', File.separatorChar));
+ }
+ myVersionString = null;
+ myHomeFieldLabel.setText(getHomeFieldLabelValue());
+ updateAdditionalDataComponent();
+ final AdditionalDataConfigurable configurable = getAdditionalDataConfigurable();
+ if (configurable != null) {
+ configurable.reset();
+ }
+
+ myHomeComponent.setEnabled(mySdk != null);
+
+ for(int i = 0; i < myTabbedPane.getTabCount(); i++){
+ myTabbedPane.setEnabledAt(i, mySdk != null);
+ }
+ }
+
+ @Override
+ public void disposeUIResources(){
+ for (final SdkType sdkType : myAdditionalDataConfigurables.keySet()) {
+ final AdditionalDataConfigurable configurable = myAdditionalDataConfigurables.get(sdkType);
+ configurable.disposeUIResources();
+ }
+ myAdditionalDataConfigurables.clear();
+ myAdditionalDataComponents.clear();
+
+ Disposer.dispose(myDisposable);
+ }
+
+ private String getHomeValue() {
+ return myHomeComponent.getText().trim();
+ }
+
+ private void clearAllPaths(){
+ for(PathEditor editor: myPathEditors.values()) {
+ editor.clearList();
+ }
+ }
+
+ private void setHomePathValue(String absolutePath) {
+ myHomeComponent.setText(absolutePath);
+ final Color fg;
+ if (absolutePath != null && !absolutePath.isEmpty()) {
+ final File homeDir = new File(absolutePath);
+ boolean homeMustBeDirectory = mySdk == null || ((SdkType) mySdk.getSdkType()).getHomeChooserDescriptor().isChooseFolders();
+ fg = homeDir.exists() && homeDir.isDirectory() == homeMustBeDirectory
+ ? UIUtil.getFieldForegroundColor()
+ : PathEditor.INVALID_COLOR;
+ }
+ else {
+ fg = UIUtil.getFieldForegroundColor();
+ }
+ myHomeComponent.getTextField().setForeground(fg);
+ }
+
+ private void doSelectHomePath(){
+ final SdkType sdkType = (SdkType)mySdk.getSdkType();
+ SdkConfigurationUtil.selectSdkHome(sdkType, new Consumer<String>() {
+ @Override
+ public void consume(final String path) {
+ doSetHomePath(path, sdkType);
+ }
+ });
+
+ }
+
+ private void doSetHomePath(final String homePath, final SdkType sdkType) {
+ if (homePath == null){
+ return;
+ }
+ setHomePathValue(homePath.replace('/', File.separatorChar));
+
+ final String newSdkName = suggestSdkName(homePath);
+ ((ProjectJdkImpl)mySdk).setName(newSdkName);
+
+ try {
+ final Sdk dummySdk = (Sdk)mySdk.clone();
+ SdkModificator sdkModificator = dummySdk.getSdkModificator();
+ sdkModificator.setHomePath(homePath);
+ sdkModificator.removeAllRoots();
+ sdkModificator.commitChanges();
+
+ sdkType.setupSdkPaths(dummySdk, mySdkModel);
+
+ clearAllPaths();
+ myVersionString = dummySdk.getVersionString();
+ if (myVersionString == null) {
+ Messages.showMessageDialog(ProjectBundle.message("sdk.java.corrupt.error", homePath),
+ ProjectBundle.message("sdk.java.corrupt.title"), Messages.getErrorIcon());
+ }
+ sdkModificator = dummySdk.getSdkModificator();
+ for (OrderRootType type : myPathEditors.keySet()) {
+ myPathEditors.get(type).addPaths(sdkModificator.getRoots(type));
+ }
+ mySdkModel.getMulticaster().sdkHomeSelected(dummySdk, homePath);
+ }
+ catch (CloneNotSupportedException e) {
+ LOG.error(e); // should not happen in normal program
+ }
+ }
+
+ private String suggestSdkName(final String homePath) {
+ final String currentName = mySdk.getName();
+ final String suggestedName = ((SdkType) mySdk.getSdkType()).suggestSdkName(currentName , homePath);
+ if (Comparing.equal(currentName, suggestedName)) return currentName;
+ String newSdkName = suggestedName;
+ final Set<String> allNames = new HashSet<String>();
+ Sdk[] sdks = mySdkModel.getSdks();
+ for (Sdk sdk : sdks) {
+ allNames.add(sdk.getName());
+ }
+ int i = 0;
+ while(allNames.contains(newSdkName)){
+ newSdkName = suggestedName + " (" + ++i + ")";
+ }
+ return newSdkName;
+ }
+
+ private void updateAdditionalDataComponent() {
+ myAdditionalDataPanel.removeAll();
+ final AdditionalDataConfigurable configurable = getAdditionalDataConfigurable();
+ if (configurable != null) {
+ JComponent component = myAdditionalDataComponents.get(configurable);
+ if (component == null) {
+ component = configurable.createComponent();
+ myAdditionalDataComponents.put(configurable, component);
+ }
+ myAdditionalDataPanel.add(component, BorderLayout.CENTER);
+ }
+ }
+
+ @Nullable
+ private AdditionalDataConfigurable getAdditionalDataConfigurable() {
+ if (mySdk == null) {
+ return null;
+ }
+ return initAdditionalDataConfigurable(mySdk);
+ }
+
+ @Nullable
+ private AdditionalDataConfigurable initAdditionalDataConfigurable(Sdk sdk) {
+ final SdkType sdkType = (SdkType)sdk.getSdkType();
+ AdditionalDataConfigurable configurable = myAdditionalDataConfigurables.get(sdkType);
+ if (configurable == null) {
+ configurable = sdkType.createAdditionalDataConfigurable(mySdkModel, myEditedSdkModificator);
+ if (configurable != null) {
+ myAdditionalDataConfigurables.put(sdkType, configurable);
+ }
+ }
+ return configurable;
+ }
+
+ private class EditedSdkModificator implements SdkModificator {
+ @Override
+ public String getName() {
+ return mySdk.getName();
+ }
+
+ @Override
+ public void setName(String name) {
+ ((ProjectJdkImpl)mySdk).setName(name);
+ }
+
+ @Override
+ public String getHomePath() {
+ return getHomeValue();
+ }
+
+ @Override
+ public void setHomePath(String path) {
+ doSetHomePath(path, (SdkType)mySdk.getSdkType());
+ }
+
+ @Override
+ public String getVersionString() {
+ return myVersionString != null? myVersionString : mySdk.getVersionString();
+ }
+
+ @Override
+ public void setVersionString(String versionString) {
+ throw new UnsupportedOperationException(); // not supported for this editor
+ }
+
+ @Override
+ public SdkAdditionalData getSdkAdditionalData() {
+ return mySdk.getSdkAdditionalData();
+ }
+
+ @Override
+ public void setSdkAdditionalData(SdkAdditionalData data) {
+ throw new UnsupportedOperationException(); // not supported for this editor
+ }
+
+ @Override
+ public VirtualFile[] getRoots(OrderRootType rootType) {
+ final PathEditor editor = myPathEditors.get(rootType);
+ if (editor == null) {
+ throw new IllegalStateException("no editor for root type " + rootType);
+ }
+ return editor.getRoots();
+ }
+
+ @Override
+ public void addRoot(VirtualFile root, OrderRootType rootType) {
+ myPathEditors.get(rootType).addPaths(root);
+ }
+
+ @Override
+ public void removeRoot(VirtualFile root, OrderRootType rootType) {
+ myPathEditors.get(rootType).removePaths(root);
+ }
+
+ @Override
+ public void removeRoots(OrderRootType rootType) {
+ myPathEditors.get(rootType).clearList();
+ }
+
+ @Override
+ public void removeAllRoots() {
+ for(PathEditor editor: myPathEditors.values()) {
+ editor.clearList();
+ }
+ }
+
+ @Override
+ public void commitChanges() {
+ }
+
+ @Override
+ public boolean isWritable() {
+ return true;
+ }
+ }
+
+ @Override
+ public ActionCallback navigateTo(@Nullable final Place place, final boolean requestFocus) {
+ if (place == null) return new ActionCallback.Done();
+ myTabbedPane.setSelectedTitle((String)place.getPath(SDK_TAB));
+ return new ActionCallback.Done();
+ }
+
+ @Override
+ public void queryPlace(@NotNull final Place place) {
+ place.putPath(SDK_TAB, myTabbedPane.getSelectedTitle());
+ }
+
+ @Override
+ public void setHistory(final History history) {
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/IdeaModifiableModelsProvider.java b/java/idea-ui/src/com/intellij/openapi/roots/IdeaModifiableModelsProvider.java
new file mode 100644
index 0000000..59ea8ea
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/IdeaModifiableModelsProvider.java
@@ -0,0 +1,110 @@
+package com.intellij.openapi.roots;
+
+import com.intellij.facet.FacetManager;
+import com.intellij.facet.ModifiableFacetModel;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.project.ProjectManager;
+import com.intellij.openapi.roots.libraries.LibraryTable;
+import com.intellij.openapi.roots.libraries.LibraryTablesRegistrar;
+import com.intellij.openapi.roots.ui.configuration.LibraryTableModifiableModelProvider;
+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.StructureConfigurableContext;
+import org.jetbrains.annotations.Nullable;
+
+import java.lang.reflect.Proxy;
+
+/**
+ * @author Dennis.Ushakov
+ */
+public class IdeaModifiableModelsProvider implements ModifiableModelsProvider {
+ @Override
+ @Nullable
+ public ModifiableRootModel getModuleModifiableModel(final Module module) {
+ final Project project = module.getProject();
+ final ModulesConfigurator configurator = getModulesConfigurator(project);
+ if (configurator != null) {
+ if (!configurator.isModuleModelCommitted()) {
+ final ModuleEditor moduleEditor = configurator.getModuleEditor(module);
+ if (moduleEditor != null) {
+ return moduleEditor.getModifiableRootModelProxy();
+ }
+ }
+ }
+ return ModuleRootManager.getInstance(module).getModifiableModel();
+ }
+
+ @Nullable
+ private static ModulesConfigurator getModulesConfigurator(Project project) {
+ StructureConfigurableContext context = getProjectStructureContext(project);
+ return context != null ? context.getModulesConfigurator() : null;
+ }
+
+ @Override
+ public void commitModuleModifiableModel(final ModifiableRootModel model) {
+ if (!(model instanceof Proxy)) {
+ model.commit();
+ }
+ //IDEA should commit this model instead of us, because it is was given from StructureConfigurableContext
+ }
+
+ @Override
+ public void disposeModuleModifiableModel(final ModifiableRootModel model) {
+ if (!(model instanceof Proxy)) {
+ model.dispose();
+ }
+ //IDEA should dispose this model instead of us, because it is was given from StructureConfigurableContext
+ }
+
+ @Override
+ public ModifiableFacetModel getFacetModifiableModel(Module module) {
+ final ModulesConfigurator configurator = getModulesConfigurator(module.getProject());
+ if (configurator != null) {
+ return configurator.getFacetsConfigurator().getOrCreateModifiableModel(module);
+ }
+ return FacetManager.getInstance(module).createModifiableModel();
+ }
+
+ @Override
+ public void commitFacetModifiableModel(Module module, ModifiableFacetModel model) {
+ final ModulesConfigurator configurator = getModulesConfigurator(module.getProject());
+ if (configurator == null || !(configurator.getFacetsConfigurator().getFacetModel(module) instanceof ModifiableFacetModel)) {
+ model.commit();
+ }
+ }
+
+ @Override
+ public LibraryTable.ModifiableModel getLibraryTableModifiableModel() {
+ final Project[] projects = ProjectManager.getInstance().getOpenProjects();
+ for (Project project : projects) {
+ if (!project.isInitialized()) {
+ continue;
+ }
+ StructureConfigurableContext context = getProjectStructureContext(project);
+ LibraryTableModifiableModelProvider provider = context != null ? context.createModifiableModelProvider(LibraryTablesRegistrar.APPLICATION_LEVEL) : null;
+ final LibraryTable.ModifiableModel modifiableModel = provider != null ? provider.getModifiableModel() : null;
+ if (modifiableModel != null) {
+ return modifiableModel;
+ }
+ }
+ return LibraryTablesRegistrar.getInstance().getLibraryTable().getModifiableModel();
+ }
+
+ @Override
+ public LibraryTable.ModifiableModel getLibraryTableModifiableModel(Project project) {
+ StructureConfigurableContext context = getProjectStructureContext(project);
+ if (context != null) {
+ LibraryTableModifiableModelProvider provider = context.createModifiableModelProvider(LibraryTablesRegistrar.PROJECT_LEVEL);
+ return provider.getModifiableModel();
+ }
+ return LibraryTablesRegistrar.getInstance().getLibraryTable(project).getModifiableModel();
+ }
+
+ @Nullable
+ private static StructureConfigurableContext getProjectStructureContext(Project project) {
+ final ProjectStructureConfigurable structureConfigurable = ProjectStructureConfigurable.getInstance(project);
+ return structureConfigurable.isUiInitialized() ? structureConfigurable.getContext() : null;
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/FileAppearanceServiceImpl.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/FileAppearanceServiceImpl.java
new file mode 100644
index 0000000..82682f4
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/FileAppearanceServiceImpl.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2000-2012 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;
+
+import com.intellij.openapi.fileTypes.FileType;
+import com.intellij.openapi.fileTypes.FileTypeManager;
+import com.intellij.openapi.roots.ui.util.*;
+import com.intellij.openapi.vfs.JarFileSystem;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.openapi.vfs.VirtualFileSystem;
+import com.intellij.openapi.vfs.ex.http.HttpFileSystem;
+import com.intellij.ui.HtmlListCellRenderer;
+import com.intellij.ui.SimpleColoredComponent;
+import com.intellij.util.PlatformIcons;
+import org.jetbrains.annotations.NotNull;
+
+import java.io.File;
+
+public class FileAppearanceServiceImpl extends FileAppearanceService {
+ private static CellAppearanceEx EMPTY = new CellAppearanceEx() {
+ @Override
+ public void customize(@NotNull SimpleColoredComponent component) { }
+
+ @Override
+ public void customize(@NotNull HtmlListCellRenderer renderer) { }
+
+ @NotNull
+ @Override
+ public String getText() { return ""; }
+ };
+
+ @NotNull
+ @Override
+ public CellAppearanceEx empty() {
+ return EMPTY;
+ }
+
+ @NotNull
+ @Override
+ public CellAppearanceEx forVirtualFile(@NotNull final VirtualFile file) {
+ if (!file.isValid()) {
+ return forInvalidUrl(file.getPresentableUrl());
+ }
+
+ final VirtualFileSystem fileSystem = file.getFileSystem();
+ if (fileSystem.getProtocol().equals(JarFileSystem.PROTOCOL)) {
+ return new JarSubfileCellAppearance(file);
+ }
+ if (fileSystem instanceof HttpFileSystem) {
+ return new HttpUrlCellAppearance(file);
+ }
+ if (file.isDirectory()) {
+ return SimpleTextCellAppearance.regular(file.getPresentableUrl(), PlatformIcons.FOLDER_ICON);
+ }
+ return new ValidFileCellAppearance(file);
+ }
+
+ @NotNull
+ @Override
+ public CellAppearanceEx forIoFile(@NotNull final File file) {
+ final String absolutePath = file.getAbsolutePath();
+ if (!file.exists()) {
+ return forInvalidUrl(absolutePath);
+ }
+
+ if (file.isDirectory()) {
+ return SimpleTextCellAppearance.regular(absolutePath, PlatformIcons.FOLDER_ICON);
+ }
+
+ final String name = file.getName();
+ final FileType fileType = FileTypeManager.getInstance().getFileTypeByFileName(name);
+ final File parent = file.getParentFile();
+ final CompositeAppearance appearance = CompositeAppearance.textComment(name, parent.getAbsolutePath());
+ appearance.setIcon(fileType.getIcon());
+ return appearance;
+ }
+
+ @Override
+ @NotNull
+ public CellAppearanceEx forInvalidUrl(@NotNull final String text) {
+ return SimpleTextCellAppearance.invalid(text, PlatformIcons.INVALID_ENTRY_ICON);
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/OrderEntryAppearanceServiceImpl.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/OrderEntryAppearanceServiceImpl.java
new file mode 100644
index 0000000..154a035
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/OrderEntryAppearanceServiceImpl.java
@@ -0,0 +1,217 @@
+/*
+ * Copyright 2000-2012 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;
+
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.module.ModuleType;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.project.ProjectBundle;
+import com.intellij.openapi.projectRoots.Sdk;
+import com.intellij.openapi.projectRoots.SdkType;
+import com.intellij.openapi.roots.*;
+import com.intellij.openapi.roots.impl.libraries.LibraryEx;
+import com.intellij.openapi.roots.libraries.Library;
+import com.intellij.openapi.roots.ui.configuration.ProjectStructureConfigurable;
+import com.intellij.openapi.roots.ui.configuration.libraries.LibraryPresentationManager;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.StructureConfigurableContext;
+import com.intellij.openapi.roots.ui.util.CompositeAppearance;
+import com.intellij.openapi.roots.ui.util.SimpleTextCellAppearance;
+import com.intellij.openapi.util.IconLoader;
+import com.intellij.openapi.util.SystemInfo;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.openapi.vfs.JarFileSystem;
+import com.intellij.openapi.vfs.VfsUtilCore;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.openapi.vfs.VirtualFileManager;
+import com.intellij.ui.JBColor;
+import com.intellij.ui.SimpleTextAttributes;
+import com.intellij.util.PathUtil;
+import com.intellij.util.PlatformIcons;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import java.awt.*;
+import java.io.File;
+
+public class OrderEntryAppearanceServiceImpl extends OrderEntryAppearanceService {
+ private static final Icon EXCLUDE_FOLDER_ICON = IconLoader.getDisabledIcon(PlatformIcons.FOLDER_ICON);
+
+ private static final String NO_JDK = ProjectBundle.message("jdk.missing.item");
+
+ @NotNull
+ @Override
+ public CellAppearanceEx forOrderEntry(Project project, @NotNull final OrderEntry orderEntry, final boolean selected) {
+ if (orderEntry instanceof JdkOrderEntry) {
+ JdkOrderEntry jdkLibraryEntry = (JdkOrderEntry)orderEntry;
+ Sdk jdk = jdkLibraryEntry.getJdk();
+ if (!orderEntry.isValid()) {
+ final String oldJdkName = jdkLibraryEntry.getJdkName();
+ return FileAppearanceService.getInstance().forInvalidUrl(oldJdkName != null ? oldJdkName : NO_JDK);
+ }
+ return forJdk(jdk, false, selected, true);
+ }
+ else if (!orderEntry.isValid()) {
+ return FileAppearanceService.getInstance().forInvalidUrl(orderEntry.getPresentableName());
+ }
+ else if (orderEntry instanceof LibraryOrderEntry) {
+ LibraryOrderEntry libraryOrderEntry = (LibraryOrderEntry)orderEntry;
+ if (!libraryOrderEntry.isValid()) { //library can be removed
+ return FileAppearanceService.getInstance().forInvalidUrl(orderEntry.getPresentableName());
+ }
+ Library library = libraryOrderEntry.getLibrary();
+ assert library != null : libraryOrderEntry;
+ return forLibrary(project, library, !((LibraryEx)library).getInvalidRootUrls(OrderRootType.CLASSES).isEmpty());
+ }
+ else if (orderEntry.isSynthetic()) {
+ String presentableName = orderEntry.getPresentableName();
+ Icon icon = orderEntry instanceof ModuleSourceOrderEntry ? sourceFolderIcon(false) : null;
+ return new SimpleTextCellAppearance(presentableName, icon, SimpleTextAttributes.SYNTHETIC_ATTRIBUTES);
+ }
+ else if (orderEntry instanceof ModuleOrderEntry) {
+ final Icon icon = ModuleType.get(((ModuleOrderEntry)orderEntry).getModule()).getIcon();
+ return SimpleTextCellAppearance.regular(orderEntry.getPresentableName(), icon);
+ }
+ else {
+ return CompositeAppearance.single(orderEntry.getPresentableName());
+ }
+ }
+
+ @NotNull
+ @Override
+ public CellAppearanceEx forLibrary(Project project, @NotNull final Library library, final boolean hasInvalidRoots) {
+ final StructureConfigurableContext context = ProjectStructureConfigurable.getInstance(project).getContext();
+ final Icon icon = LibraryPresentationManager.getInstance().getCustomIcon(library, context);
+
+ final String name = library.getName();
+ if (name != null) {
+ return normalOrRedWaved(name, (icon != null ? icon : PlatformIcons.LIBRARY_ICON), hasInvalidRoots);
+ }
+
+ final String[] files = library.getUrls(OrderRootType.CLASSES);
+ if (files.length == 0) {
+ return SimpleTextCellAppearance.invalid(ProjectBundle.message("library.empty.library.item"), PlatformIcons.LIBRARY_ICON);
+ }
+ else if (files.length == 1) {
+ return forVirtualFilePointer(new LightFilePointer(files[0]));
+ }
+
+ final String url = StringUtil.trimEnd(files[0], JarFileSystem.JAR_SEPARATOR);
+ return SimpleTextCellAppearance.regular(PathUtil.getFileName(url), PlatformIcons.LIBRARY_ICON);
+ }
+
+ @NotNull
+ @Override
+ public CellAppearanceEx forJdk(@Nullable final Sdk jdk, final boolean isInComboBox, final boolean selected, final boolean showVersion) {
+ if (jdk == null) {
+ return FileAppearanceService.getInstance().forInvalidUrl(NO_JDK);
+ }
+
+ String name = jdk.getName();
+ CompositeAppearance appearance = new CompositeAppearance();
+ appearance.setIcon(((SdkType) jdk.getSdkType()).getIcon());
+ VirtualFile homeDirectory = jdk.getHomeDirectory();
+ SimpleTextAttributes attributes = getTextAttributes(homeDirectory != null && homeDirectory.isValid(), selected);
+ CompositeAppearance.DequeEnd ending = appearance.getEnding();
+ ending.addText(name, attributes);
+
+ if (showVersion) {
+ String versionString = jdk.getVersionString();
+ if (versionString != null && !versionString.equals(name)) {
+ SimpleTextAttributes textAttributes = isInComboBox ? SimpleTextAttributes.SYNTHETIC_ATTRIBUTES :
+ SystemInfo.isMac && selected ? new SimpleTextAttributes(SimpleTextAttributes.STYLE_PLAIN,
+ Color.WHITE): SimpleTextAttributes.GRAY_ATTRIBUTES;
+ ending.addComment(versionString, textAttributes);
+ }
+ }
+
+ return ending.getAppearance();
+ }
+
+ private static SimpleTextAttributes getTextAttributes(final boolean valid, final boolean selected) {
+ if (!valid) {
+ return SimpleTextAttributes.ERROR_ATTRIBUTES;
+ }
+ else if (selected) {
+ return SimpleTextAttributes.SELECTED_SIMPLE_CELL_ATTRIBUTES;
+ }
+ else {
+ return SimpleTextAttributes.SIMPLE_CELL_ATTRIBUTES;
+ }
+ }
+
+ @NotNull
+ @Override
+ public CellAppearanceEx forContentFolder(@NotNull final ContentFolder folder) {
+ if (folder instanceof SourceFolder) {
+ return formatRelativePath(folder, PlatformIcons.FOLDER_ICON);
+ }
+ else if (folder instanceof ExcludeFolder) {
+ return formatRelativePath(folder, EXCLUDE_FOLDER_ICON);
+ }
+ else {
+ throw new RuntimeException(folder.getClass().getName());
+ }
+ }
+
+ @NotNull
+ @Override
+ public CellAppearanceEx forModule(@NotNull final Module module) {
+ return SimpleTextCellAppearance.regular(module.getName(), ModuleType.get(module).getIcon());
+ }
+
+ @NotNull
+ private static Icon sourceFolderIcon(final boolean testSource) {
+ return testSource ? PlatformIcons.TEST_SOURCE_FOLDER : PlatformIcons.SOURCE_FOLDERS_ICON;
+ }
+
+ @NotNull
+ private static CellAppearanceEx normalOrRedWaved(@NotNull final String text, @Nullable final Icon icon, final boolean waved) {
+ return waved ? new SimpleTextCellAppearance(text, icon, new SimpleTextAttributes(SimpleTextAttributes.STYLE_WAVED, null, JBColor.RED))
+ : SimpleTextCellAppearance.regular(text, icon);
+ }
+
+ @NotNull
+ private static CellAppearanceEx forVirtualFilePointer(@NotNull final LightFilePointer filePointer) {
+ final VirtualFile file = filePointer.getFile();
+ return file != null ? FileAppearanceService.getInstance().forVirtualFile(file)
+ : FileAppearanceService.getInstance().forInvalidUrl(filePointer.getPresentableUrl());
+ }
+
+ @NotNull
+ private static CellAppearanceEx formatRelativePath(@NotNull final ContentFolder folder, @NotNull final Icon icon) {
+ LightFilePointer folderFile = new LightFilePointer(folder.getUrl());
+ VirtualFile file = VirtualFileManager.getInstance().findFileByUrl(folder.getContentEntry().getUrl());
+ if (file == null) return FileAppearanceService.getInstance().forInvalidUrl(folderFile.getPresentableUrl());
+
+ String contentPath = file.getPath();
+ String relativePath;
+ SimpleTextAttributes textAttributes;
+ VirtualFile folderFileFile = folderFile.getFile();
+ if (folderFileFile == null) {
+ String absolutePath = folderFile.getPresentableUrl();
+ relativePath = absolutePath.startsWith(contentPath) ? absolutePath.substring(contentPath.length()) : absolutePath;
+ textAttributes = SimpleTextAttributes.ERROR_ATTRIBUTES;
+ }
+ else {
+ relativePath = VfsUtilCore.getRelativePath(folderFileFile, file, File.separatorChar);
+ textAttributes = SimpleTextAttributes.REGULAR_ATTRIBUTES;
+ }
+
+ relativePath = StringUtil.isEmpty(relativePath) ? "." + File.separatorChar : relativePath;
+ return new SimpleTextCellAppearance(relativePath, icon, textAttributes);
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/AnnotationsEditor.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/AnnotationsEditor.java
new file mode 100644
index 0000000..bd0f3d5
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/AnnotationsEditor.java
@@ -0,0 +1,185 @@
+/*
+ * Copyright 2000-2012 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;
+
+import com.intellij.openapi.fileChooser.FileChooser;
+import com.intellij.openapi.fileChooser.FileChooserDescriptor;
+import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory;
+import com.intellij.openapi.project.ProjectBundle;
+import com.intellij.openapi.roots.JavaModuleExternalPaths;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.ui.*;
+import com.intellij.ui.components.JBLabel;
+import com.intellij.ui.table.JBTable;
+import com.intellij.util.ArrayUtil;
+import com.intellij.util.ui.ItemRemovable;
+import com.intellij.util.ui.UIUtil;
+
+import javax.swing.*;
+import javax.swing.border.Border;
+import javax.swing.table.DefaultTableModel;
+import java.awt.*;
+import java.util.List;
+
+/**
+ * @author Eugene Zhuravlev
+ * Date: Oct 4, 2003
+ * Time: 6:54:57 PM
+ */
+public class AnnotationsEditor extends ModuleElementsEditor {
+ private JTable myTable;
+
+ public static final String NAME = ProjectBundle.message("project.roots.external.annotations.tab.title");
+
+ public AnnotationsEditor(final ModuleConfigurationState state) {
+ super(state);
+ }
+
+ @Override
+ public String getHelpTopic() {
+ return "project.paths.annotations";//todo
+ }
+
+ @Override
+ public String getDisplayName() {
+ return NAME;
+ }
+
+ @Override
+ public void saveData() {
+ TableUtil.stopEditing(myTable);
+ final int count = myTable.getRowCount();
+ String[] urls = ArrayUtil.newStringArray(count);
+ for (int row = 0; row < count; row++) {
+ final TableItem item = ((MyTableModel)myTable.getModel()).getTableItemAt(row);
+ urls[row] = item.getUrl();
+ }
+ getModel().getModuleExtension(JavaModuleExternalPaths.class).setExternalAnnotationUrls(urls);
+ }
+
+ @Override
+ public JComponent createComponentImpl() {
+ final DefaultTableModel tableModel = createModel();
+ myTable = new JBTable(tableModel);
+ myTable.setIntercellSpacing(new Dimension(0, 0));
+ myTable.setDefaultRenderer(TableItem.class, new MyRenderer());
+ myTable.setShowGrid(false);
+ myTable.setDragEnabled(false);
+ myTable.setShowHorizontalLines(false);
+ myTable.setShowVerticalLines(false);
+ myTable.getSelectionModel().setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
+
+ JPanel tablePanel = ToolbarDecorator.createDecorator(myTable)
+ .setAddAction(new AnActionButtonRunnable() {
+ @Override
+ public void run(AnActionButton button) {
+ FileChooserDescriptor myDescriptor = FileChooserDescriptorFactory.createSingleFolderDescriptor();
+ myDescriptor.setTitle(ProjectBundle.message("add.external.annotations.path.title"));
+ myDescriptor.setDescription(ProjectBundle.message("add.external.annotations.path.description"));
+ VirtualFile[] files = FileChooser.chooseFiles(myDescriptor, myTable, myProject, null);
+ final MyTableModel tableModel = (MyTableModel)myTable.getModel();
+ boolean changes = false;
+ for (final VirtualFile file : files) {
+ if (file != null) {
+ tableModel.addTableItem(new TableItem(file));
+ changes = true;
+ }
+ }
+ if (changes) {
+ saveData();
+ TableUtil.selectRows(myTable, new int[] {tableModel.getRowCount() - 1});
+ }
+ }
+ }).setRemoveAction(new AnActionButtonRunnable() {
+ @Override
+ public void run(AnActionButton button) {
+ final List removedItems = TableUtil.removeSelectedItems(myTable);
+ if (removedItems.size() > 0) {
+ saveData();
+ }
+ }
+ }).createPanel();
+
+
+ final JPanel mainPanel = new JPanel(new BorderLayout());
+
+ mainPanel.add(tablePanel, BorderLayout.CENTER);
+ mainPanel.add(new JBLabel(ProjectBundle.message("project.roots.external.annotations.description"), UIUtil.ComponentStyle.SMALL,
+ UIUtil.FontColor.BRIGHTER), BorderLayout.NORTH);
+ return mainPanel;
+ }
+
+ protected DefaultTableModel createModel() {
+ final MyTableModel tableModel = new MyTableModel();
+ final String[] urls = getModel().getModuleExtension(JavaModuleExternalPaths.class).getExternalAnnotationsUrls();
+ for (String javadocUrl : urls) {
+ tableModel.addTableItem(new TableItem(javadocUrl));
+ }
+ return tableModel;
+ }
+
+ @Override
+ public void moduleStateChanged() {
+ if (myTable != null) {
+ final DefaultTableModel tableModel = createModel();
+ myTable.setModel(tableModel);
+ }
+ }
+
+ private static class MyRenderer extends ColoredTableCellRenderer {
+ private static final Border NO_FOCUS_BORDER = BorderFactory.createEmptyBorder(1, 1, 1, 1);
+
+ @Override
+ protected void customizeCellRenderer(JTable table, Object value, boolean selected, boolean hasFocus, int row, int column) {
+ setPaintFocusBorder(false);
+ setFocusBorderAroundIcon(true);
+ setBorder(NO_FOCUS_BORDER);
+
+ final TableItem tableItem = ((TableItem)value);
+ tableItem.getCellAppearance().customize(this);
+ }
+ }
+
+ private static class MyTableModel extends DefaultTableModel implements ItemRemovable{
+ @Override
+ public String getColumnName(int column) {
+ return null;
+ }
+
+ @Override
+ public Class getColumnClass(int columnIndex) {
+ return TableItem.class;
+ }
+
+ @Override
+ public int getColumnCount() {
+ return 1;
+ }
+
+ @Override
+ public boolean isCellEditable(int row, int column) {
+ return false;
+ }
+
+ public TableItem getTableItemAt(int row) {
+ return (TableItem)getValueAt(row, 0);
+ }
+
+ public void addTableItem(TableItem item) {
+ addRow(new Object[] {item});
+ }
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/BuildElementsEditor.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/BuildElementsEditor.java
new file mode 100644
index 0000000..7476456
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/BuildElementsEditor.java
@@ -0,0 +1,298 @@
+/*
+ * Copyright 2000-2009 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.
+ */
+
+/*
+ * Created by IntelliJ IDEA.
+ * User: Anna.Kozlova
+ * Date: 28-Jun-2006
+ * Time: 19:03:32
+ */
+package com.intellij.openapi.roots.ui.configuration;
+
+import com.intellij.ide.util.BrowseFilesListener;
+import com.intellij.openapi.fileChooser.FileChooserDescriptor;
+import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory;
+import com.intellij.openapi.fileChooser.FileChooserFactory;
+import com.intellij.openapi.project.ProjectBundle;
+import com.intellij.openapi.roots.CompilerModuleExtension;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.vfs.VfsUtilCore;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.ui.DocumentAdapter;
+import com.intellij.ui.FieldPanel;
+import com.intellij.ui.IdeBorderFactory;
+import com.intellij.ui.InsertPathAction;
+import com.intellij.util.ui.UIUtil;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import javax.swing.event.DocumentEvent;
+import java.awt.*;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.io.IOException;
+
+public class BuildElementsEditor extends ModuleElementsEditor {
+ private JRadioButton myInheritCompilerOutput;
+ @SuppressWarnings({"FieldCanBeLocal"})
+ private JRadioButton myPerModuleCompilerOutput;
+
+ private CommitableFieldPanel myOutputPathPanel;
+ private CommitableFieldPanel myTestsOutputPathPanel;
+ private JCheckBox myCbExcludeOutput;
+ private JLabel myOutputLabel;
+ private JLabel myTestOutputLabel;
+
+ protected BuildElementsEditor(final ModuleConfigurationState state) {
+ super(state);
+ }
+
+ @Override
+ public JComponent createComponentImpl() {
+ myInheritCompilerOutput = new JRadioButton(ProjectBundle.message("project.inherit.compile.output.path"));
+ myPerModuleCompilerOutput = new JRadioButton(ProjectBundle.message("project.module.compile.output.path"));
+ ButtonGroup group = new ButtonGroup();
+ group.add(myInheritCompilerOutput);
+ group.add(myPerModuleCompilerOutput);
+
+ final ActionListener listener = new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ enableCompilerSettings(!myInheritCompilerOutput.isSelected());
+ }
+ };
+
+ myInheritCompilerOutput.addActionListener(listener);
+ myPerModuleCompilerOutput.addActionListener(listener);
+
+ myOutputPathPanel = createOutputPathPanel(ProjectBundle.message("module.paths.output.title"), new CommitPathRunnable() {
+ @Override
+ public void saveUrl(String url) {
+ if (getCompilerExtension().isCompilerOutputPathInherited()) return; //do not override settings if any
+ getCompilerExtension().setCompilerOutputPath(url);
+ }
+ });
+ myTestsOutputPathPanel = createOutputPathPanel(ProjectBundle.message("module.paths.test.output.title"), new CommitPathRunnable() {
+ @Override
+ public void saveUrl(String url) {
+ if (getCompilerExtension().isCompilerOutputPathInherited()) return; //do not override settings if any
+ getCompilerExtension().setCompilerOutputPathForTests(url);
+ }
+ });
+
+ myCbExcludeOutput = new JCheckBox(ProjectBundle.message("module.paths.exclude.output.checkbox"), getCompilerExtension().isExcludeOutput());
+ myCbExcludeOutput.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(final ActionEvent e) {
+ getCompilerExtension().setExcludeOutput(myCbExcludeOutput.isSelected());
+ }
+ });
+
+ final JPanel outputPathsPanel = new JPanel(new GridBagLayout());
+
+
+ outputPathsPanel.add(myInheritCompilerOutput, new GridBagConstraints(0, GridBagConstraints.RELATIVE, 2, 1, 1.0, 0.0,
+ GridBagConstraints.WEST, GridBagConstraints.NONE,
+ new Insets(6, 0, 0, 4), 0, 0));
+ outputPathsPanel.add(myPerModuleCompilerOutput, new GridBagConstraints(0, GridBagConstraints.RELATIVE, 2, 1, 1.0, 0.0,
+ GridBagConstraints.WEST, GridBagConstraints.NONE,
+ new Insets(6, 0, 0, 4), 0, 0));
+
+ myOutputLabel = new JLabel(ProjectBundle.message("module.paths.output.label"));
+ outputPathsPanel.add(myOutputLabel, new GridBagConstraints(0, GridBagConstraints.RELATIVE, 1, 1, 0.0, 0.0, GridBagConstraints.EAST,
+ GridBagConstraints.NONE, new Insets(6, 12, 0, 4), 0, 0));
+ outputPathsPanel.add(myOutputPathPanel, new GridBagConstraints(1, GridBagConstraints.RELATIVE, 2, 1, 1.0, 0.0, GridBagConstraints.WEST,
+ GridBagConstraints.HORIZONTAL, new Insets(6, 4, 0, 0), 0, 0));
+
+ myTestOutputLabel = new JLabel(ProjectBundle.message("module.paths.test.output.label"));
+ outputPathsPanel.add(myTestOutputLabel, new GridBagConstraints(0, GridBagConstraints.RELATIVE, 1, 1, 0.0, 0.0, GridBagConstraints.EAST,
+ GridBagConstraints.NONE, new Insets(6, 16, 0, 4), 0, 0));
+ outputPathsPanel.add(myTestsOutputPathPanel, new GridBagConstraints(1, GridBagConstraints.RELATIVE, 2, 1, 1.0, 0.0,
+ GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL,
+ new Insets(6, 4, 0, 0), 0, 0));
+
+ outputPathsPanel.add(myCbExcludeOutput, new GridBagConstraints(0, GridBagConstraints.RELATIVE, 2, 1, 1.0, 0.0, GridBagConstraints.WEST,
+ GridBagConstraints.NONE, new Insets(6, 16, 0, 0), 0, 0));
+
+ // fill with data
+ updateOutputPathPresentation();
+
+ //compiler settings
+ final boolean outputPathInherited = getCompilerExtension().isCompilerOutputPathInherited();
+ myInheritCompilerOutput.setSelected(outputPathInherited);
+ myPerModuleCompilerOutput.setSelected(!outputPathInherited);
+ enableCompilerSettings(!outputPathInherited);
+
+ final JPanel panel = new JPanel(new BorderLayout());
+ panel.setBorder(IdeBorderFactory.createTitledBorder(ProjectBundle.message("project.roots.output.compiler.title"),
+ true));
+ panel.add(outputPathsPanel, BorderLayout.NORTH);
+ return panel;
+ }
+
+ private void updateOutputPathPresentation() {
+ if (getCompilerExtension().isCompilerOutputPathInherited()) {
+ final String baseUrl = ProjectStructureConfigurable.getInstance(myProject).getProjectConfig().getCompilerOutputUrl();
+ moduleCompileOutputChanged(baseUrl, getModel().getModule().getName());
+ } else {
+ final VirtualFile compilerOutputPath = getCompilerExtension().getCompilerOutputPath();
+ if (compilerOutputPath != null) {
+ myOutputPathPanel.setText(FileUtil.toSystemDependentName(compilerOutputPath.getPath()));
+ }
+ else {
+ final String compilerOutputUrl = getCompilerExtension().getCompilerOutputUrl();
+ if (compilerOutputUrl != null) {
+ myOutputPathPanel.setText(FileUtil.toSystemDependentName(VfsUtilCore.urlToPath(compilerOutputUrl)));
+ }
+ }
+ final VirtualFile testsOutputPath = getCompilerExtension().getCompilerOutputPathForTests();
+ if (testsOutputPath != null) {
+ myTestsOutputPathPanel.setText(FileUtil.toSystemDependentName(testsOutputPath.getPath()));
+ }
+ else {
+ final String testsOutputUrl = getCompilerExtension().getCompilerOutputUrlForTests();
+ if (testsOutputUrl != null) {
+ myTestsOutputPathPanel.setText(FileUtil.toSystemDependentName(VfsUtilCore.urlToPath(testsOutputUrl)));
+ }
+ }
+ }
+ }
+
+ private void enableCompilerSettings(final boolean enabled) {
+ UIUtil.setEnabled(myOutputPathPanel, enabled, true);
+ UIUtil.setEnabled(myOutputLabel, enabled, true);
+ UIUtil.setEnabled(myTestsOutputPathPanel, enabled, true);
+ UIUtil.setEnabled(myTestOutputLabel, enabled, true);
+ myCbExcludeOutput.setEnabled(enabled);
+ getCompilerExtension().inheritCompilerOutputPath(!enabled);
+ updateOutputPathPresentation();
+ }
+
+ private CommitableFieldPanel createOutputPathPanel(final String title, final CommitPathRunnable commitPathRunnable) {
+ final JTextField textField = new JTextField();
+ final FileChooserDescriptor outputPathsChooserDescriptor = FileChooserDescriptorFactory.createSingleFolderDescriptor();
+ outputPathsChooserDescriptor.setHideIgnored(false);
+ InsertPathAction.addTo(textField, outputPathsChooserDescriptor);
+ FileChooserFactory.getInstance().installFileCompletion(textField, outputPathsChooserDescriptor, true, null);
+ final Runnable commitRunnable = new Runnable() {
+ @Override
+ public void run() {
+ if (!getModel().isWritable()) {
+ return;
+ }
+ final String path = textField.getText().trim();
+ if (path.length() == 0) {
+ commitPathRunnable.saveUrl(null);
+ }
+ else {
+ // should set only absolute paths
+ String canonicalPath;
+ try {
+ canonicalPath = FileUtil.resolveShortWindowsName(path);
+ }
+ catch (IOException e) {
+ canonicalPath = path;
+ }
+ commitPathRunnable.saveUrl(VfsUtilCore.pathToUrl(FileUtil.toSystemIndependentName(canonicalPath)));
+ }
+ }
+ };
+
+ textField.getDocument().addDocumentListener(new DocumentAdapter() {
+ @Override
+ protected void textChanged(DocumentEvent e) {
+ commitRunnable.run();
+ }
+ });
+
+ return new CommitableFieldPanel(textField, null, null, new BrowseFilesListener(textField, title, "", outputPathsChooserDescriptor) {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ super.actionPerformed(e);
+ commitRunnable.run();
+ }
+ }, null, commitRunnable);
+ }
+
+ @Override
+ public void saveData() {
+ myOutputPathPanel.commit();
+ myTestsOutputPathPanel.commit();
+ getCompilerExtension().commit();
+ }
+
+ @Override
+ public String getDisplayName() {
+ return ProjectBundle.message("output.tab.title");
+ }
+
+ @Override
+ @Nullable
+ @NonNls
+ public String getHelpTopic() {
+ return "project.structureModulesPage.outputJavadoc";
+ }
+
+
+ @Override
+ public void moduleStateChanged() {
+ //if content enties tree was changed
+ myCbExcludeOutput.setSelected(getCompilerExtension().isExcludeOutput());
+ }
+
+ @Override
+ public void moduleCompileOutputChanged(final String baseUrl, final String moduleName) {
+ if (getCompilerExtension().isCompilerOutputPathInherited()) {
+ if (baseUrl != null) {
+ myOutputPathPanel.setText(FileUtil.toSystemDependentName(VfsUtilCore.urlToPath(baseUrl + "/" + CompilerModuleExtension
+ .PRODUCTION + "/" + moduleName)));
+ myTestsOutputPathPanel.setText(FileUtil.toSystemDependentName(VfsUtilCore.urlToPath(baseUrl + "/" + CompilerModuleExtension
+ .TEST + "/" + moduleName)));
+ }
+ else {
+ myOutputPathPanel.setText(null);
+ myTestsOutputPathPanel.setText(null);
+ }
+ }
+ }
+
+ public CompilerModuleExtension getCompilerExtension() {
+ return getModel().getModuleExtension(CompilerModuleExtension.class);
+ }
+
+ private interface CommitPathRunnable {
+ void saveUrl(String url);
+ }
+
+ private static class CommitableFieldPanel extends FieldPanel {
+ private final Runnable myCommitRunnable;
+
+ public CommitableFieldPanel(final JTextField textField,
+ String labelText,
+ final String viewerDialogTitle,
+ ActionListener browseButtonActionListener,
+ final Runnable documentListener,
+ final Runnable commitPathRunnable) {
+ super(textField, labelText, viewerDialogTitle, browseButtonActionListener, documentListener);
+ myCommitRunnable = commitPathRunnable;
+ }
+
+ public void commit() {
+ myCommitRunnable.run();
+ }
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/ClasspathEditor.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/ClasspathEditor.java
new file mode 100644
index 0000000..19bb9be
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/ClasspathEditor.java
@@ -0,0 +1,227 @@
+/*
+ * Copyright 2004-2005 Alexey Efimov
+ *
+ * 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;
+
+import com.intellij.ProjectTopics;
+import com.intellij.openapi.Disposable;
+import com.intellij.openapi.options.ConfigurationException;
+import com.intellij.openapi.project.ProjectBundle;
+import com.intellij.openapi.projectRoots.Sdk;
+import com.intellij.openapi.roots.ModifiableRootModel;
+import com.intellij.openapi.roots.ModuleRootEvent;
+import com.intellij.openapi.roots.ModuleRootListener;
+import com.intellij.openapi.roots.OrderEntry;
+import com.intellij.openapi.roots.impl.storage.ClasspathStorage;
+import com.intellij.openapi.roots.impl.storage.ClasspathStorageProvider;
+import com.intellij.openapi.roots.ui.configuration.classpath.ClasspathPanelImpl;
+import com.intellij.openapi.util.Disposer;
+import com.intellij.ui.OrderPanelListener;
+import org.jetbrains.annotations.NotNull;
+
+import javax.swing.*;
+import java.awt.*;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author Eugene Zhuravlev
+ * Date: Oct 4, 2003
+ * Time: 6:54:57 PM
+ */
+public class ClasspathEditor extends ModuleElementsEditor implements ModuleRootListener {
+ public static final String NAME = ProjectBundle.message("modules.classpath.title");
+
+ private ClasspathPanelImpl myPanel;
+
+ private ClasspathFormatPanel myClasspathFormatPanel;
+
+ public ClasspathEditor(final ModuleConfigurationState state) {
+ super(state);
+
+ final Disposable disposable = Disposer.newDisposable();
+
+ state.getProject().getMessageBus().connect(disposable).subscribe(ProjectTopics.PROJECT_ROOTS, this);
+ registerDisposable(disposable);
+ }
+
+ @Override
+ public boolean isModified() {
+ return super.isModified() || (myClasspathFormatPanel != null && myClasspathFormatPanel.isModified());
+ }
+
+ @Override
+ public String getHelpTopic() {
+ return "projectStructure.modules.dependencies";
+ }
+
+ @Override
+ public String getDisplayName() {
+ return NAME;
+ }
+
+ @Override
+ public void saveData() {
+ myPanel.stopEditing();
+ flushChangesToModel();
+ }
+
+ @Override
+ public void apply () throws ConfigurationException {
+ if(myClasspathFormatPanel!=null) {
+ myClasspathFormatPanel.apply();
+ }
+ }
+
+ @Override
+ public void canApply() throws ConfigurationException {
+ super.canApply();
+ if (myClasspathFormatPanel != null) {
+ final String storageID = myClasspathFormatPanel.getSelectedClasspathFormat();
+ ClasspathStorage.getProvider(storageID).assertCompatible(getModel());
+ }
+ }
+
+ @Override
+ public JComponent createComponentImpl() {
+ myPanel = new ClasspathPanelImpl(getState());
+
+ myPanel.addListener(new OrderPanelListener() {
+ @Override
+ public void entryMoved() {
+ flushChangesToModel();
+ }
+ });
+
+ final JPanel panel = new JPanel(new BorderLayout());
+ panel.setBorder(BorderFactory.createEmptyBorder(6, 6, 6, 6));
+ panel.add(myPanel, BorderLayout.CENTER);
+
+ final ModuleJdkConfigurable jdkConfigurable =
+ new ModuleJdkConfigurable(this, ProjectStructureConfigurable.getInstance(myProject).getProjectJdksModel()) {
+ @Override
+ protected ModifiableRootModel getRootModel() {
+ return getState().getRootModel();
+ }
+ };
+ panel.add(jdkConfigurable.createComponent(), BorderLayout.NORTH);
+ jdkConfigurable.reset();
+ registerDisposable(jdkConfigurable);
+
+ List<ClasspathStorageProvider> providers = ClasspathStorage.getProviders();
+ if(providers.size()>1){
+ myClasspathFormatPanel = new ClasspathFormatPanel(providers);
+ panel.add(myClasspathFormatPanel, BorderLayout.SOUTH);
+ }
+
+ return panel;
+ }
+
+ public void flushChangesToModel() {
+ List<OrderEntry> entries = myPanel.getEntries();
+ getModel().rearrangeOrderEntries(entries.toArray(new OrderEntry[entries.size()]));
+ }
+
+ public void selectOrderEntry(@NotNull final OrderEntry entry) {
+ myPanel.selectOrderEntry(entry);
+ }
+
+ @Override
+ public void moduleStateChanged() {
+ if (myPanel != null) {
+ myPanel.initFromModel();
+ }
+ }
+
+ @Override
+ public void beforeRootsChange(ModuleRootEvent event) {
+ }
+
+ @Override
+ public void rootsChanged(ModuleRootEvent event) {
+ if (myPanel != null) {
+ myPanel.rootsChanged();
+ }
+ }
+
+ public void setSdk(final Sdk newJDK) {
+ final ModifiableRootModel model = getModel();
+ if (newJDK != null) {
+ model.setSdk(newJDK);
+ }
+ else {
+ model.inheritSdk();
+ }
+
+ if (myPanel != null) {
+ myPanel.forceInitFromModel();
+ }
+
+ flushChangesToModel();
+ }
+
+ private class ClasspathFormatPanel extends JPanel {
+
+ private final JComboBox cbClasspathFormat;
+
+ private final Map<String,String> formatIdToDescr = new HashMap<String, String>();
+
+ private ClasspathFormatPanel(final List<ClasspathStorageProvider> providers) {
+ super(new GridBagLayout());
+ add(new JLabel(ProjectBundle.message("project.roots.classpath.format.label")),
+ new GridBagConstraints(0,0,1,1,0.0, 0.0, GridBagConstraints.NORTHWEST, GridBagConstraints.NONE, new Insets(10, 6, 6, 0), 0, 0));
+
+ for (ClasspathStorageProvider provider : providers){
+ formatIdToDescr.put ( provider.getID(), provider.getDescription());
+ }
+
+ final Object[] items = formatIdToDescr.values().toArray();
+ cbClasspathFormat = new JComboBox(items);
+ updateClasspathFormat();
+ add(cbClasspathFormat,
+ new GridBagConstraints(1,0,1,1,1.0, 0.0, GridBagConstraints.NORTHWEST, GridBagConstraints.NONE, new Insets(6, 6, 6, 0), 0, 0));
+ }
+
+ private void updateClasspathFormat() {
+ cbClasspathFormat.setSelectedItem(formatIdToDescr.get(getModuleClasspathFormat()));
+ }
+
+ private String getSelectedClasspathFormat() {
+ final String selected = (String)cbClasspathFormat.getSelectedItem();
+ for ( Map.Entry<String,String> entry : formatIdToDescr.entrySet() ) {
+ if ( entry.getValue().equals(selected)) {
+ return entry.getKey();
+ }
+ }
+ throw new IllegalStateException(selected);
+ }
+
+ @NotNull
+ private String getModuleClasspathFormat() {
+ return ClasspathStorage.getStorageType(getModel().getModule());
+ }
+
+ boolean isModified() {
+ return cbClasspathFormat != null && !getSelectedClasspathFormat().equals(getModuleClasspathFormat());
+ }
+
+ void apply() throws ConfigurationException {
+ final String storageID = getSelectedClasspathFormat();
+ ClasspathStorage.getProvider(storageID).assertCompatible(getModel());
+ ClasspathStorage.setStorageType(getModel(), storageID);
+ }
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/ConfigurationError.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/ConfigurationError.java
new file mode 100644
index 0000000..565687d
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/ConfigurationError.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2000-2010 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;
+
+import com.intellij.ui.awt.RelativePoint;
+import org.jetbrains.annotations.NotNull;
+
+import javax.swing.*;
+
+/**
+ * User: spLeaner
+ */
+public abstract class ConfigurationError implements Comparable<ConfigurationError> {
+ private final String myPlainTextTitle;
+ private final String myDescription;
+ private boolean myIgnored;
+
+ protected ConfigurationError(final String plainTextTitle, final String description) {
+ this(plainTextTitle, description, false);
+ }
+
+ protected ConfigurationError(final String plainTextTitle, final String description, final boolean ignored) {
+ myPlainTextTitle = plainTextTitle;
+ myDescription = description;
+ myIgnored = ignored;
+ }
+
+ @NotNull
+ public String getPlainTextTitle() {
+ return myPlainTextTitle;
+ }
+
+ @NotNull
+ public String getDescription() {
+ return myDescription;
+ }
+
+ /**
+ * Called when user invokes "Ignore" action
+ * @param "true" if user invokes "Ignore", "false" if user wish to not ignore this error anymore
+ */
+ public void ignore(final boolean b) {
+ if (b != myIgnored) {
+ myIgnored = b;
+ }
+ }
+
+ /**
+ * @return "true" if this error is ignored
+ */
+ public boolean isIgnored() {
+ return myIgnored;
+ }
+
+ /**
+ * Called when user invokes "Fix" action
+ */
+ public void fix(JComponent contextComponent, RelativePoint relativePoint) {
+ }
+
+ public boolean canBeFixed() {
+ return true;
+ }
+
+ public abstract void navigate();
+
+ @Override
+ public int compareTo(final ConfigurationError o) {
+ if (myIgnored != o.isIgnored()) return -1;
+
+ final int titleResult = getPlainTextTitle().compareTo(o.getPlainTextTitle());
+ if (titleResult != 0) return titleResult;
+
+ final int descriptionResult = getDescription().compareTo(o.getDescription());
+ if (descriptionResult != 0) return descriptionResult;
+
+ return 0;
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/ConfigurationErrors.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/ConfigurationErrors.java
new file mode 100644
index 0000000..e0d3983
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/ConfigurationErrors.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2000-2010 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;
+
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.startup.StartupManager;
+import com.intellij.util.PairProcessor;
+import com.intellij.util.messages.MessageBus;
+import com.intellij.util.messages.Topic;
+import org.jetbrains.annotations.NotNull;
+
+import javax.swing.*;
+import java.awt.*;
+
+/**
+* User: spLeaner
+*/
+public interface ConfigurationErrors {
+ Topic<ConfigurationErrors> TOPIC = Topic.create("Configuration Error", ConfigurationErrors.class, Topic.BroadcastDirection.NONE);
+
+ void addError(@NotNull ConfigurationError error);
+ void removeError(@NotNull ConfigurationError error);
+
+ @SuppressWarnings({"UtilityClassWithoutPrivateConstructor"})
+ class Bus {
+ public static void addError(@NotNull final ConfigurationError error, @NotNull final Project project) {
+ _do(error, project, new PairProcessor<ConfigurationErrors, ConfigurationError>() {
+ @Override
+ public boolean process(final ConfigurationErrors configurationErrors, final ConfigurationError configurationError) {
+ configurationErrors.addError(configurationError);
+ return false;
+ }
+ });
+ }
+
+ public static void removeError(@NotNull final ConfigurationError error, @NotNull final Project project) {
+ _do(error, project, new PairProcessor<ConfigurationErrors, ConfigurationError>() {
+ @Override
+ public boolean process(final ConfigurationErrors configurationErrors, final ConfigurationError configurationError) {
+ configurationErrors.removeError(configurationError);
+ return false;
+ }
+ });
+ }
+
+ public static void _do(@NotNull final ConfigurationError error, @NotNull final Project project,
+ @NotNull final PairProcessor<ConfigurationErrors, ConfigurationError> fun) {
+ if (!project.isInitialized()) {
+ StartupManager.getInstance(project).runWhenProjectIsInitialized(new Runnable() {
+ @Override
+ public void run() {
+ fun.process(project.getMessageBus().syncPublisher(TOPIC), error);
+ }
+ });
+
+ return;
+ }
+
+ final MessageBus bus = project.getMessageBus();
+ if (EventQueue.isDispatchThread()) fun.process(bus.syncPublisher(TOPIC), error);
+ else {
+ //noinspection SSBasedInspection
+ SwingUtilities.invokeLater(new Runnable() {
+ @Override
+ public void run() {
+ fun.process(bus.syncPublisher(TOPIC), error);
+ }
+ });
+ }
+ }
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/ConfigurationErrorsComponent.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/ConfigurationErrorsComponent.java
new file mode 100644
index 0000000..28ad6b5
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/ConfigurationErrorsComponent.java
@@ -0,0 +1,800 @@
+/*
+ * Copyright 2000-2012 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;
+
+import com.intellij.icons.AllIcons;
+import com.intellij.openapi.Disposable;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.GraphicsConfig;
+import com.intellij.openapi.ui.MessageType;
+import com.intellij.openapi.util.Disposer;
+import com.intellij.ui.awt.RelativePoint;
+import com.intellij.ui.components.JBList;
+import com.intellij.ui.components.JBScrollPane;
+import com.intellij.ui.components.labels.LinkLabel;
+import com.intellij.ui.components.labels.LinkListener;
+import com.intellij.util.SystemProperties;
+import com.intellij.util.messages.MessageBusConnection;
+import com.intellij.util.ui.BaseButtonBehavior;
+import com.intellij.util.ui.TimedDeadzone;
+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 javax.swing.event.ListDataEvent;
+import javax.swing.event.ListDataListener;
+import java.awt.*;
+import java.awt.event.ComponentAdapter;
+import java.awt.event.ComponentEvent;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.awt.geom.RoundRectangle2D;
+import java.util.AbstractList;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * User: spLeaner
+ */
+public class ConfigurationErrorsComponent extends JPanel implements Disposable, ListDataListener {
+ private static final int MAX_ERRORS_TO_SHOW = SystemProperties.getIntProperty("idea.project.structure.max.errors.to.show", 100);
+ private static final boolean ONE_LINE = true;
+ private static final boolean MULTI_LINE = false;
+
+ @NonNls private static final String FIX_ACTION_NAME = "FIX";
+ @NonNls private static final String NAVIGATE_ACTION_NAME = "NAVIGATE";
+
+ private ConfigurationErrorsListModel myConfigurationErrorsListModel;
+ private ErrorView myCurrentView;
+
+ public ConfigurationErrorsComponent(@NotNull final Project project) {
+ setLayout(new BorderLayout());
+ myConfigurationErrorsListModel = new ConfigurationErrorsListModel(project);
+ myConfigurationErrorsListModel.addListDataListener(this);
+
+ addComponentListener(new ComponentAdapter() {
+ @Override
+ public void componentResized(final ComponentEvent e) {
+ revalidate();
+ repaint();
+ }
+ });
+
+ ensureCurrentViewIs(ONE_LINE, null);
+ Disposer.register(this, myConfigurationErrorsListModel);
+ }
+
+ @Override
+ public void dispose() {
+ if (myConfigurationErrorsListModel != null) {
+ myConfigurationErrorsListModel.removeListDataListener(this);
+ myConfigurationErrorsListModel = null;
+ }
+ }
+
+ private void ensureCurrentViewIs(final boolean oneLine, @Nullable final Object data) {
+ if (oneLine) {
+ if (myCurrentView instanceof OneLineErrorComponent) return;
+ myConfigurationErrorsListModel.setFilter(true, true);
+ OneLineErrorComponent c = new OneLineErrorComponent(myConfigurationErrorsListModel) {
+ @Override
+ public void onViewChange(Object data) {
+ ensureCurrentViewIs(MULTI_LINE, data);
+ }
+ };
+
+ if (myCurrentView != null) {
+ remove(myCurrentView.self());
+ Disposer.dispose(myCurrentView);
+ }
+
+ myCurrentView = c;
+ }
+ else {
+ myConfigurationErrorsListModel.setFilter(data == null || !"Ignored".equals(data), data == null || "Ignored".equals(data));
+ if (myCurrentView instanceof MultiLineErrorComponent) return;
+ MultiLineErrorComponent c = new MultiLineErrorComponent(myConfigurationErrorsListModel) {
+ @Override
+ public void onViewChange(Object data) {
+ ensureCurrentViewIs(ONE_LINE, data);
+ }
+ };
+
+ if (myCurrentView != null) {
+ remove(myCurrentView.self());
+ Disposer.dispose(myCurrentView);
+ }
+
+ myCurrentView = c;
+ }
+
+ add(myCurrentView.self(), BorderLayout.CENTER);
+ myCurrentView.updateView();
+ UIUtil.adjustWindowToMinimumSize(SwingUtilities.getWindowAncestor(this));
+ revalidate();
+ repaint();
+ }
+
+ @Override
+ public void intervalAdded(final ListDataEvent e) {
+ updateCurrentView();
+ }
+
+ @Override
+ public void intervalRemoved(final ListDataEvent e) {
+ updateCurrentView();
+ }
+
+ @Override
+ public void contentsChanged(final ListDataEvent e) {
+ updateCurrentView();
+ }
+
+ private void updateCurrentView() {
+ if (myCurrentView instanceof MultiLineErrorComponent && myConfigurationErrorsListModel.getSize() == 0) {
+ ensureCurrentViewIs(ONE_LINE, null);
+ }
+
+ myCurrentView.updateView();
+ }
+
+ private interface ErrorView extends Disposable {
+ void updateView();
+ void onViewChange(Object data);
+ JComponent self();
+ }
+
+ private abstract static class MultiLineErrorComponent extends JPanel implements ErrorView {
+ private JList myList = new JBList();
+
+ protected MultiLineErrorComponent(@NotNull final ConfigurationErrorsListModel model) {
+ setLayout(new BorderLayout());
+ setBorder(BorderFactory.createEmptyBorder(5, 0, 5, 0));
+
+ myList.setModel(model);
+ myList.setCellRenderer(new ErrorListRenderer(myList));
+ myList.setBackground(UIUtil.getPanelBackground());
+
+ myList.addMouseListener(new MouseAdapter() {
+ @Override
+ public void mouseClicked(final MouseEvent e) {
+ if (!e.isPopupTrigger()) {
+ processListMouseEvent(e, true);
+ }
+ }
+ });
+
+ myList.addMouseMotionListener(new MouseAdapter() {
+ @Override
+ public void mouseMoved(MouseEvent e) {
+ if (!e.isPopupTrigger()) {
+ processListMouseEvent(e, false);
+ }
+ }
+ });
+
+ myList.addComponentListener(new ComponentAdapter() {
+ @Override
+ public void componentResized(ComponentEvent e) {
+ myList.setCellRenderer(new ErrorListRenderer(myList)); // request cell renderer size invalidation
+ updatePreferredSize();
+ }
+ });
+
+ add(new JBScrollPane(myList), BorderLayout.CENTER);
+ add(buildToolbar(), BorderLayout.WEST);
+ }
+
+ @Override
+ public void dispose() {
+ }
+
+ private void processListMouseEvent(final MouseEvent e, final boolean click) {
+ final int index = myList.locationToIndex(e.getPoint());
+ if (index > -1) {
+ final Object value = myList.getModel().getElementAt(index);
+ if (value != null && value instanceof ConfigurationError) {
+ final ConfigurationError error = (ConfigurationError)value;
+ final Component renderer = myList.getCellRenderer().getListCellRendererComponent(myList, value, index, false, false);
+ if (renderer instanceof ErrorListRenderer) {
+ final Rectangle bounds = myList.getCellBounds(index, index);
+ renderer.setBounds(bounds);
+ renderer.doLayout();
+
+ final Point point = e.getPoint();
+ point.translate(-bounds.x, -bounds.y);
+
+ final Component deepestComponentAt = SwingUtilities.getDeepestComponentAt(renderer, point.x, point.y);
+ if (deepestComponentAt instanceof ToolbarAlikeButton) {
+ final String name = ((ToolbarAlikeButton)deepestComponentAt).getButtonName();
+ if (click) {
+ if (FIX_ACTION_NAME.equals(name)) {
+ onClickFix(error, (JComponent)deepestComponentAt, e);
+ }
+ else if (NAVIGATE_ACTION_NAME.equals(name)) {
+ error.navigate();
+ }
+ else {
+ onClickIgnore(error);
+ }
+ }
+ else {
+ myList.setToolTipText(FIX_ACTION_NAME.equals(name) ? "Fix" : NAVIGATE_ACTION_NAME.equals(name) ? "Navigate to the problem" :
+ error.isIgnored() ? "Not ignore this error" : "Ignore this error");
+ return;
+ }
+ } else {
+ if (e.getClickCount() == 2) {
+ error.navigate();
+ }
+ }
+ }
+ }
+ }
+
+ myList.setToolTipText(null);
+ }
+
+ private void onClickIgnore(@NotNull final ConfigurationError error) {
+ error.ignore(!error.isIgnored());
+ final ListModel model = myList.getModel();
+ if (model instanceof ConfigurationErrorsListModel) {
+ ((ConfigurationErrorsListModel)model).update(error);
+ }
+ }
+
+ private void onClickFix(@NotNull final ConfigurationError error, JComponent component, MouseEvent e) {
+ error.fix(component, new RelativePoint(e));
+ }
+
+ @Override
+ public void addNotify() {
+ super.addNotify();
+ updatePreferredSize();
+ }
+
+ private void updatePreferredSize() {
+ final Window window = SwingUtilities.getWindowAncestor(this);
+ if (window != null) {
+ final Dimension d = window.getSize();
+ final Dimension preferredSize = getPreferredSize();
+ setPreferredSize(new Dimension(preferredSize.width, 200));
+ setMinimumSize(getPreferredSize());
+ }
+ }
+
+ private JComponent buildToolbar() {
+ final JPanel result = new JPanel();
+ result.setBorder(BorderFactory.createEmptyBorder(5, 0, 0, 0));
+ result.setLayout(new BorderLayout());
+ result.add(new ToolbarAlikeButton(AllIcons.Actions.Collapseall) {
+ {
+ setToolTipText("Collapse");
+ }
+
+ @Override
+ public void onClick(MouseEvent e) {
+ onViewChange(null);
+ }
+ }, BorderLayout.NORTH);
+
+ return result;
+ }
+
+ @Override
+ public void updateView() {
+ }
+
+ @Override
+ public JComponent self() {
+ return this;
+ }
+
+ @Override
+ public abstract void onViewChange(@Nullable Object data);
+ }
+
+ private abstract static class ToolbarAlikeButton extends JComponent {
+ private BaseButtonBehavior myBehavior;
+ private Icon myIcon;
+ private String myName;
+
+ private ToolbarAlikeButton(@NotNull final Icon icon, @NotNull final String name) {
+ this(icon);
+ myName = name;
+ }
+
+ private ToolbarAlikeButton(@NotNull final Icon icon) {
+ myIcon = icon;
+
+ myBehavior = new BaseButtonBehavior(this, TimedDeadzone.NULL) {
+ @Override
+ protected void execute(MouseEvent e) {
+ onClick(e);
+ }
+ };
+
+ setOpaque(false);
+ }
+
+ public String getButtonName() {
+ return myName;
+ }
+
+ public void onClick(MouseEvent e) {}
+
+ @Override
+ public Insets getInsets() {
+ return new Insets(2, 2, 2, 2);
+ }
+
+ @Override
+ public Dimension getPreferredSize() {
+ return getMinimumSize();
+ }
+
+ @Override
+ public Dimension getMinimumSize() {
+ final Insets insets = getInsets();
+ return new Dimension(myIcon.getIconWidth() + insets.left + insets.right, myIcon.getIconHeight() + insets.top + insets.bottom);
+ }
+
+ @Override
+ public void paint(final Graphics g) {
+ final Insets insets = getInsets();
+ final Dimension d = getSize();
+
+ int x = (d.width - myIcon.getIconWidth() - insets.left - insets.right) / 2;
+ int y = (d.height - myIcon.getIconHeight() - insets.top - insets.bottom) / 2;
+
+ if (myBehavior.isHovered()) {
+ // todo
+ }
+
+ if (myBehavior.isPressedByMouse()) {
+ x += 1;
+ y += 1;
+ }
+
+ myIcon.paintIcon(this, g, x + insets.left, y + insets.top);
+ }
+ }
+
+ private static class ErrorListRenderer extends JComponent implements ListCellRenderer {
+ private boolean mySelected;
+ private boolean myHasFocus;
+ private JTextPane myText;
+ private JTextPane myFakeTextPane;
+ private JViewport myFakeViewport;
+ private JList myList;
+ private JPanel myButtonsPanel;
+ private JPanel myFixGroup;
+
+ private ErrorListRenderer(@NotNull final JList list) {
+ setLayout(new BorderLayout());
+ setBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2));
+ setOpaque(false);
+
+ myList = list;
+
+ myText = new JTextPane();
+
+ myButtonsPanel = new JPanel(new BorderLayout());
+ myButtonsPanel.setBorder(BorderFactory.createEmptyBorder(5, 3, 5, 3));
+ myButtonsPanel.setOpaque(false);
+ final JPanel buttons = new JPanel();
+ buttons.setOpaque(false);
+ buttons.setLayout(new BoxLayout(buttons, BoxLayout.X_AXIS));
+ myButtonsPanel.add(buttons, BorderLayout.NORTH);
+ add(myButtonsPanel, BorderLayout.EAST);
+
+ myFixGroup = new JPanel();
+ myFixGroup.setOpaque(false);
+ myFixGroup.setLayout(new BoxLayout(myFixGroup, BoxLayout.Y_AXIS));
+
+ myFixGroup.add(new ToolbarAlikeButton(AllIcons.Actions.QuickfixBulb, FIX_ACTION_NAME) {});
+ myFixGroup.add(Box.createHorizontalStrut(3));
+ buttons.add(myFixGroup);
+
+ buttons.add(new ToolbarAlikeButton(AllIcons.General.AutoscrollToSource, NAVIGATE_ACTION_NAME) {});
+ buttons.add(Box.createHorizontalStrut(3));
+
+ buttons.add(new ToolbarAlikeButton(AllIcons.Actions.Cancel, "IGNORE") {});
+
+ myFakeTextPane = new JTextPane();
+ myText.setBorder(BorderFactory.createEmptyBorder(3, 3, 3, 3));
+ myFakeTextPane.setBorder(BorderFactory.createEmptyBorder(3, 0, 3, 0));
+ myText.setOpaque(false);
+ if (UIUtil.isUnderNimbusLookAndFeel()) {
+ myText.setBackground(UIUtil.TRANSPARENT_COLOR);
+ }
+
+ myText.setEditable(false);
+ myFakeTextPane.setEditable(false);
+ myText.setEditorKit(UIUtil.getHTMLEditorKit());
+ myFakeTextPane.setEditorKit(UIUtil.getHTMLEditorKit());
+
+ myFakeViewport = new JViewport();
+ myFakeViewport.setView(myFakeTextPane);
+
+ add(myText, BorderLayout.CENTER);
+ }
+
+ @Override
+ public Dimension getPreferredSize() {
+ final Container parent = myList.getParent();
+ if (parent != null) {
+ myFakeTextPane.setText(myText.getText());
+ final Dimension size = parent.getSize();
+ myFakeViewport.setSize(size);
+ final Dimension preferredSize = myFakeTextPane.getPreferredSize();
+
+ final Dimension buttonsPrefSize = myButtonsPanel.getPreferredSize();
+ final int maxHeight = Math.max(buttonsPrefSize.height, preferredSize.height);
+
+ final Insets insets = getInsets();
+ return new Dimension(Math.min(size.width - 20, preferredSize.width), maxHeight + insets.top + insets.bottom);
+ }
+
+ return super.getPreferredSize();
+ }
+
+ @Override
+ public Component getListCellRendererComponent(final JList list, final Object value, final int index, final boolean isSelected, final boolean cellHasFocus) {
+ final ConfigurationError error = (ConfigurationError)value;
+
+ myList = list;
+
+ mySelected = isSelected;
+ myHasFocus = cellHasFocus;
+
+ myFixGroup.setVisible(error.canBeFixed());
+
+ myText.setText(error.getDescription());
+
+ setBackground(error.isIgnored() ? MessageType.WARNING.getPopupBackground() : MessageType.ERROR.getPopupBackground());
+ return this;
+ }
+
+ @Override
+ protected void paintComponent(Graphics g) {
+ final Graphics2D g2d = (Graphics2D)g;
+
+ final Rectangle bounds = getBounds();
+ final Insets insets = getInsets();
+
+ final GraphicsConfig cfg = new GraphicsConfig(g);
+ cfg.setAntialiasing(true);
+
+ final Shape shape = new RoundRectangle2D.Double(insets.left, insets.top, bounds.width - 1 - insets.left - insets.right,
+ bounds.height - 1 - insets.top - insets.bottom, 6, 6);
+
+ if (mySelected) {
+ g2d.setColor(UIUtil.getListSelectionBackground());
+ g2d.fillRect(0, 0, bounds.width, bounds.height);
+ }
+
+ g2d.setColor(Color.WHITE);
+ g2d.fill(shape);
+
+
+ Color bgColor = getBackground();
+
+ g2d.setColor(bgColor);
+ g2d.fill(shape);
+
+ g2d.setColor(myHasFocus || mySelected ? getBackground().darker().darker() : getBackground().darker());
+ g2d.draw(shape);
+ cfg.restore();
+
+ super.paintComponent(g);
+ }
+ }
+
+ private abstract static class OneLineErrorComponent extends JComponent implements ErrorView, LinkListener {
+ private LinkLabel myErrorsLabel = new LinkLabel(null, null);
+ private LinkLabel myIgnoredErrorsLabel = new LinkLabel(null, null);
+ private JLabel mySingleErrorLabel = new JLabel();
+
+ private ConfigurationErrorsListModel myModel;
+
+ private OneLineErrorComponent(@NotNull final ConfigurationErrorsListModel model) {
+ myModel = model;
+
+ setLayout(new BorderLayout());
+ setOpaque(true);
+
+ updateLabel(myErrorsLabel, MessageType.ERROR.getPopupBackground(), this, "Errors");
+ updateLabel(mySingleErrorLabel, MessageType.ERROR.getPopupBackground(), null, null);
+ updateLabel(myIgnoredErrorsLabel, MessageType.WARNING.getPopupBackground(), this, "Ignored");
+ }
+
+ @Override
+ public void dispose() {
+ myModel = null;
+ }
+
+ private static void updateLabel(@NotNull final JLabel label, @NotNull final Color bgColor, @Nullable final LinkListener listener, @Nullable Object linkData) {
+ label.setBorder(BorderFactory.createEmptyBorder(3, 5, 3, 5));
+ label.setOpaque(true);
+ label.setBackground(bgColor);
+ if (label instanceof LinkLabel) {
+ ((LinkLabel)label).setListener(listener, linkData);
+ }
+ }
+
+ @Override
+ public void updateView() {
+ if (myModel.getSize() == 0) {
+ setBorder(null);
+ } else {
+ if (getBorder() == null) setBorder(
+ BorderFactory.createCompoundBorder(BorderFactory.createMatteBorder(5, 0, 5, 0, UIUtil.getPanelBackground()),
+ BorderFactory.createLineBorder(UIUtil.getPanelBackground().darker())));
+ }
+
+ final List<ConfigurationError> errors = myModel.getErrors();
+ if (errors.size() > 0) {
+ if (errors.size() == 1) {
+ mySingleErrorLabel.setText(myModel.getErrors().get(0).getPlainTextTitle());
+ } else {
+ myErrorsLabel.setText(String.format("%s errors found", getErrorsCount(errors.size())));
+ }
+ }
+
+ final List<ConfigurationError> ignoredErrors = myModel.getIgnoredErrors();
+ if (ignoredErrors.size() > 0) {
+ myIgnoredErrorsLabel.setText(String.format("%s ignored error%s", getErrorsCount(ignoredErrors.size()), ignoredErrors.size() == 1 ? "" : "s"));
+ }
+
+ removeAll();
+ if (errors.size() > 0) {
+ if (errors.size() == 1) {
+ add(wrapLabel(mySingleErrorLabel, errors.get(0)), BorderLayout.CENTER);
+ mySingleErrorLabel.setToolTipText(errors.get(0).getDescription());
+ } else {
+ add(myErrorsLabel, BorderLayout.CENTER);
+ }
+ }
+
+ if (ignoredErrors.size() > 0) {
+ add(myIgnoredErrorsLabel, errors.size() > 0 ? BorderLayout.EAST : BorderLayout.CENTER);
+ }
+
+ revalidate();
+ repaint();
+ }
+
+ private static String getErrorsCount(final int size) {
+ return size < MAX_ERRORS_TO_SHOW ? String.valueOf(size) : MAX_ERRORS_TO_SHOW + "+";
+ }
+
+ private JComponent wrapLabel(@NotNull final JLabel label, @NotNull final ConfigurationError configurationError) {
+ final JPanel result = new JPanel(new BorderLayout());
+ result.setBackground(label.getBackground());
+ result.add(label, BorderLayout.CENTER);
+
+ final JPanel buttonsPanel = new JPanel();
+ buttonsPanel.setOpaque(false);
+ buttonsPanel.setLayout(new BoxLayout(buttonsPanel, BoxLayout.X_AXIS));
+
+ if (configurationError.canBeFixed()) {
+ buttonsPanel.add(new ToolbarAlikeButton(AllIcons.Actions.QuickfixBulb) {
+ {
+ setToolTipText("Fix error");
+ }
+
+ @Override
+ public void onClick(MouseEvent e) {
+ final Object o = myModel.getElementAt(0);
+ if (o instanceof ConfigurationError) {
+ ((ConfigurationError)o).fix(this, new RelativePoint(e));
+ updateView();
+ final Container ancestor = SwingUtilities.getAncestorOfClass(ConfigurationErrorsComponent.class, this);
+ if (ancestor != null && ancestor instanceof JComponent) {
+ ((JComponent)ancestor).revalidate();
+ ancestor.repaint();
+ }
+ }
+ }
+ });
+
+ buttonsPanel.add(Box.createHorizontalStrut(3));
+ }
+
+ buttonsPanel.add(new ToolbarAlikeButton(AllIcons.General.AutoscrollToSource) {
+ {
+ setToolTipText("Navigate to error");
+ }
+
+ @Override
+ public void onClick(MouseEvent e) {
+ final Object o = myModel.getElementAt(0);
+ if (o instanceof ConfigurationError) {
+ ((ConfigurationError)o).navigate();
+ }
+ }
+ });
+
+ buttonsPanel.add(Box.createHorizontalStrut(3));
+
+ buttonsPanel.add(new ToolbarAlikeButton(AllIcons.Actions.Cancel) {
+ {
+ setToolTipText("Ignore error");
+ }
+
+ @Override
+ public void onClick(MouseEvent e) {
+ final Object o = myModel.getElementAt(0);
+ if (o instanceof ConfigurationError) {
+ final ConfigurationError error = (ConfigurationError)o;
+ error.ignore(!error.isIgnored());
+ myModel.update(error);
+ updateView();
+ }
+ }
+ });
+ buttonsPanel.add(Box.createHorizontalStrut(5));
+
+ result.add(buttonsPanel, BorderLayout.EAST);
+
+ return result;
+ }
+
+ @Override
+ public JComponent self() {
+ return this;
+ }
+
+ @Override
+ public abstract void onViewChange(Object data);
+
+ @Override
+ public void linkSelected(LinkLabel aSource, Object data) {
+ onViewChange(data);
+ }
+ }
+
+ //todo[nik] move to ContainerUtil after 11.1
+ @NotNull
+ private static <T> List<T> concat(@NotNull final List<? extends T> list1, @NotNull final List<? extends T> list2) {
+ return new AbstractList<T>() {
+ @Override
+ public T get(int index) {
+ if (index < list1.size()) {
+ return list1.get(index);
+ }
+
+ return list2.get(index - list1.size());
+ }
+
+ @Override
+ public int size() {
+ return list1.size() + list2.size();
+ }
+ };
+ }
+
+ private static class ConfigurationErrorsListModel extends AbstractListModel implements ConfigurationErrors, Disposable {
+ private MessageBusConnection myConnection;
+ private List<ConfigurationError> myNotIgnoredErrors = new ArrayList<ConfigurationError>();
+ private List<ConfigurationError> myAllErrors;
+ private List<ConfigurationError> myIgnoredErrors = new ArrayList<ConfigurationError>();
+
+ private ConfigurationErrorsListModel(@NotNull final Project project) {
+ setFilter(true, true);
+ myConnection = project.getMessageBus().connect();
+ myConnection.subscribe(TOPIC, this);
+ }
+
+ public void setFilter(boolean showNotIgnored, boolean showIgnored) {
+ if (showIgnored && showNotIgnored) {
+ myAllErrors = concat(myNotIgnoredErrors, myIgnoredErrors);
+ }
+ else if (showIgnored) {
+ myAllErrors = myIgnoredErrors;
+ }
+ else {
+ myAllErrors = myNotIgnoredErrors;
+ }
+ }
+
+ @Override
+ public int getSize() {
+ return Math.min(myAllErrors.size(), MAX_ERRORS_TO_SHOW);
+ }
+
+ @Override
+ public Object getElementAt(int index) {
+ return myAllErrors.get(index);
+ }
+
+ @Override
+ public void addError(@NotNull ConfigurationError error) {
+ if (!myAllErrors.contains(error)) {
+ List<ConfigurationError> targetList = error.isIgnored() ? myIgnoredErrors : myNotIgnoredErrors;
+ if (targetList.size() < MAX_ERRORS_TO_SHOW) {
+ targetList.add(0, error);
+ }
+ else {
+ targetList.add(error);
+ }
+
+ int i = myAllErrors.indexOf(error);
+ if (i != -1 && i < MAX_ERRORS_TO_SHOW) {
+ fireIntervalAdded(this, i, i);
+ }
+ }
+ }
+
+ @Override
+ public void removeError(@NotNull ConfigurationError error) {
+ final int i = myAllErrors.indexOf(error);
+ myIgnoredErrors.remove(error);
+ myNotIgnoredErrors.remove(error);
+ if (i != -1 && i < MAX_ERRORS_TO_SHOW) {
+ fireIntervalRemoved(this, i, i);
+ }
+ }
+
+ public List<ConfigurationError> getErrors() {
+ return myNotIgnoredErrors;
+ }
+
+ public List<ConfigurationError> getIgnoredErrors() {
+ return myIgnoredErrors;
+ }
+
+ @Override
+ public void dispose() {
+ if (myConnection != null) {
+ myConnection.disconnect();
+ myConnection = null;
+ }
+ }
+
+ public void update(final ConfigurationError error) {
+ final int i0 = myAllErrors.indexOf(error);
+ if (error.isIgnored()) {
+ if (myNotIgnoredErrors.remove(error)) {
+ myIgnoredErrors.add(0, error);
+ }
+ }
+ else {
+ if (myIgnoredErrors.remove(error)) {
+ myNotIgnoredErrors.add(0, error);
+ }
+ }
+ final int i1 = myAllErrors.indexOf(error);
+ if (i0 == i1 && i0 != -1) {
+ if (i0 < MAX_ERRORS_TO_SHOW) {
+ fireContentsChanged(this, i0, i0);
+ }
+ }
+ else {
+ if (i0 != -1 && i0 < MAX_ERRORS_TO_SHOW) {
+ fireIntervalRemoved(this, i0, i0);
+ }
+ if (i1 != -1 && i1 < MAX_ERRORS_TO_SHOW) {
+ fireIntervalAdded(this, i1, i1);
+ }
+ }
+ }
+ }
+}
+
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/ContentEntriesEditor.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/ContentEntriesEditor.java
new file mode 100644
index 0000000..421d94c
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/ContentEntriesEditor.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2000-2009 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;
+
+import com.intellij.openapi.options.ConfigurationException;
+import com.intellij.openapi.roots.LanguageLevelModuleExtension;
+
+import javax.swing.*;
+import java.awt.*;
+
+/**
+ * @author Eugene Zhuravlev
+ * Date: Oct 4, 2003
+ * Time: 6:54:57 PM
+ */
+public class ContentEntriesEditor extends JavaContentEntriesEditor {
+ private LanguageLevelConfigurable myLanguageLevelConfigurable;
+
+ public ContentEntriesEditor(String moduleName, final ModuleConfigurationState state) {
+ super(moduleName, state);
+ }
+
+ @Override
+ public void disposeUIResources() {
+ if (myLanguageLevelConfigurable != null) myLanguageLevelConfigurable.disposeUIResources();
+ super.disposeUIResources();
+ }
+
+ @Override
+ public boolean isModified() {
+ return super.isModified() || myLanguageLevelConfigurable != null && myLanguageLevelConfigurable.isModified();
+ }
+
+ @Override
+ protected void addAdditionalSettingsToPanel(final JPanel mainPanel) {
+ myLanguageLevelConfigurable = new LanguageLevelConfigurable() {
+ @Override
+ public LanguageLevelModuleExtension getLanguageLevelExtension() {
+ return getModel().getModuleExtension(LanguageLevelModuleExtension.class);
+ }
+ };
+ mainPanel.add(myLanguageLevelConfigurable.createComponent(), BorderLayout.NORTH);
+ myLanguageLevelConfigurable.reset();
+ }
+
+ @Override
+ public void apply() throws ConfigurationException {
+ myLanguageLevelConfigurable.apply();
+ super.apply();
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/DefaultModuleConfigurationEditorFactoryImpl.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/DefaultModuleConfigurationEditorFactoryImpl.java
new file mode 100644
index 0000000..0ff9ae5
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/DefaultModuleConfigurationEditorFactoryImpl.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2000-2009 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;
+
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.module.ModuleConfigurationEditor;
+import com.intellij.openapi.roots.ModifiableRootModel;
+
+/**
+ * @author Eugene Zhuravlev
+ * Date: Oct 28, 2004
+ */
+public class DefaultModuleConfigurationEditorFactoryImpl extends DefaultModuleConfigurationEditorFactory {
+ @Override
+ public ModuleConfigurationEditor createModuleContentRootsEditor(ModuleConfigurationState state) {
+ final ModifiableRootModel rootModel = state.getRootModel();
+ final Module module = rootModel.getModule();
+ final String moduleName = module.getName();
+ return new ContentEntriesEditor(moduleName, state);
+ }
+
+ @Override
+ public ModuleConfigurationEditor createClasspathEditor(ModuleConfigurationState state) {
+ return new ClasspathEditor(state);
+ }
+
+ @Override
+ public ModuleConfigurationEditor createOutputEditor(ModuleConfigurationState state) {
+ return new OutputEditor(state);
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/DefaultModuleEditorsProvider.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/DefaultModuleEditorsProvider.java
new file mode 100644
index 0000000..c2196a3
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/DefaultModuleEditorsProvider.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2000-2009 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;
+
+import com.intellij.openapi.module.JavaModuleType;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.module.ModuleConfigurationEditor;
+import com.intellij.openapi.module.ModuleType;
+import com.intellij.openapi.roots.ModifiableRootModel;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class DefaultModuleEditorsProvider implements ModuleConfigurationEditorProvider {
+ @Override
+ public ModuleConfigurationEditor[] createEditors(ModuleConfigurationState state) {
+ ModifiableRootModel rootModel = state.getRootModel();
+ Module module = rootModel.getModule();
+ if (!(ModuleType.get(module) instanceof JavaModuleType)) {
+ return ModuleConfigurationEditor.EMPTY;
+ }
+
+ String moduleName = module.getName();
+ List<ModuleConfigurationEditor> editors = new ArrayList<ModuleConfigurationEditor>();
+ editors.add(new ContentEntriesEditor(moduleName, state));
+ editors.add(new OutputEditor(state));
+ editors.add(new ClasspathEditor(state));
+ return editors.toArray(new ModuleConfigurationEditor[editors.size()]);
+ }
+}
\ No newline at end of file
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/GeneralProjectSettingsElement.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/GeneralProjectSettingsElement.java
new file mode 100644
index 0000000..68688e1
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/GeneralProjectSettingsElement.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright 2000-2011 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;
+
+import com.intellij.compiler.ModuleCompilerUtil;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.project.ProjectBundle;
+import com.intellij.openapi.roots.ModuleRootModel;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.StructureConfigurableContext;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.daemon.*;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.util.Chunk;
+import com.intellij.util.graph.Graph;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.*;
+
+/**
+ * @author nik
+ */
+public class GeneralProjectSettingsElement extends ProjectStructureElement {
+ public GeneralProjectSettingsElement(@NotNull StructureConfigurableContext context) {
+ super(context);
+ }
+
+ @Override
+ public String getPresentableName() {
+ return "Project";
+ }
+
+ @Override
+ public String getTypeName() {
+ return "Project";
+ }
+
+ @Override
+ public void check(ProjectStructureProblemsHolder problemsHolder) {
+ final Graph<Chunk<ModuleRootModel>> graph = ModuleCompilerUtil.toChunkGraph(myContext.getModulesConfigurator().createGraphGenerator());
+ final Collection<Chunk<ModuleRootModel>> chunks = graph.getNodes();
+ List<String> cycles = new ArrayList<String>();
+ for (Chunk<ModuleRootModel> chunk : chunks) {
+ final Set<ModuleRootModel> modules = chunk.getNodes();
+ List<String> names = new ArrayList<String>();
+ for (ModuleRootModel model : modules) {
+ names.add(model.getModule().getName());
+ }
+ if (modules.size() > 1) {
+ cycles.add(StringUtil.join(names, ", "));
+ }
+ }
+ if (!cycles.isEmpty()) {
+ final Project project = myContext.getProject();
+ final PlaceInProjectStructureBase place = new PlaceInProjectStructureBase(project, ProjectStructureConfigurable.getInstance(project).createModulesPlace(), this);
+ final String message;
+ final String description;
+ if (cycles.size() > 1) {
+ message = "Circular dependencies";
+ @NonNls final String br = "<br> ";
+ StringBuilder cyclesString = new StringBuilder();
+ for (int i = 0; i < cycles.size(); i++) {
+ cyclesString.append(br).append(i + 1).append(". ").append(cycles.get(i));
+ }
+ description = ProjectBundle.message("module.circular.dependency.warning.description", cyclesString);
+ }
+ else {
+ message = ProjectBundle.message("module.circular.dependency.warning.short", cycles.get(0));
+ description = null;
+ }
+ problemsHolder.registerProblem(new ProjectStructureProblemDescription(message, description, place,
+ ProjectStructureProblemType.warning("module-circular-dependency"),
+ Collections.<ConfigurationErrorQuickFix>emptyList()));
+ }
+ }
+
+ @Override
+ public List<ProjectStructureElementUsage> getUsagesInElement() {
+ return Collections.emptyList();
+ }
+
+ @Override
+ public String getId() {
+ return "project:general";
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return obj instanceof GeneralProjectSettingsElement;
+ }
+
+ @Override
+ public int hashCode() {
+ return 0;
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/HeaderHidingTabbedModuleEditor.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/HeaderHidingTabbedModuleEditor.java
new file mode 100644
index 0000000..3a3f04b
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/HeaderHidingTabbedModuleEditor.java
@@ -0,0 +1,101 @@
+package com.intellij.openapi.roots.ui.configuration;
+
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.module.ModuleConfigurationEditor;
+import com.intellij.openapi.project.Project;
+import com.intellij.ui.navigation.Place;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+
+/**
+ * @author ksafonov
+ */
+public abstract class HeaderHidingTabbedModuleEditor extends TabbedModuleEditor {
+
+ public HeaderHidingTabbedModuleEditor(Project project, ModulesProvider modulesProvider, @NotNull Module module) {
+ super(project, modulesProvider, module);
+ }
+
+ @Override
+ protected JComponent createCenterPanel() {
+ ModuleConfigurationEditor singleEditor = getSingleEditor();
+ if (singleEditor != null) {
+ final JComponent component = singleEditor.createComponent();
+ singleEditor.reset();
+ return component;
+ }
+ else {
+ return super.createCenterPanel();
+ }
+ }
+
+ @Nullable
+ private ModuleConfigurationEditor getSingleEditor() {
+ return myEditors.size() == 1 ? myEditors.get(0) : null;
+ }
+
+ @Override
+ public ModuleConfigurationEditor getSelectedEditor() {
+ ModuleConfigurationEditor singleEditor = getSingleEditor();
+ return singleEditor != null ? singleEditor : super.getSelectedEditor();
+ }
+
+ @Override
+ public void selectEditor(String displayName) {
+ if (displayName != null) {
+ ModuleConfigurationEditor singleEditor = getSingleEditor();
+ if (singleEditor != null) {
+ // TODO [ksafonov] commented until IDEA-73889 is implemented
+ //assert singleEditor.getDisplayName().equals(displayName);
+ }
+ else {
+ super.selectEditor(displayName);
+ }
+ }
+ }
+
+ @Override
+ protected void restoreSelectedEditor() {
+ ModuleConfigurationEditor singleEditor = getSingleEditor();
+ if (singleEditor == null) {
+ super.restoreSelectedEditor();
+ }
+ }
+
+ @Override
+ @Nullable
+ public ModuleConfigurationEditor getEditor(@NotNull String displayName) {
+ ModuleConfigurationEditor singleEditor = getSingleEditor();
+ if (singleEditor != null) {
+ if (displayName.equals(singleEditor.getDisplayName())) {
+ return singleEditor;
+ }
+ else {
+ return null;
+ }
+ }
+ else {
+ return super.getEditor(displayName);
+ }
+ }
+
+ @Override
+ protected void disposeCenterPanel() {
+ if (getSingleEditor() == null) {
+ super.disposeCenterPanel();
+ }
+ }
+
+ @Override
+ public void queryPlace(@NotNull Place place) {
+ ModuleConfigurationEditor singleEditor = getSingleEditor();
+ if (singleEditor != null) {
+ place.putPath(SELECTED_EDITOR_NAME, singleEditor.getDisplayName());
+ }
+ else {
+ super.queryPlace(place);
+ }
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/IdeaProjectSettingsService.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/IdeaProjectSettingsService.java
new file mode 100644
index 0000000..926c628
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/IdeaProjectSettingsService.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright 2000-2009 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;
+
+import com.intellij.compiler.actions.ArtifactAwareProjectSettingsService;
+import com.intellij.ide.projectView.impl.ModuleGroup;
+import com.intellij.ide.util.projectWizard.JdkChooserPanel;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.options.ShowSettingsUtil;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.projectRoots.Sdk;
+import com.intellij.openapi.roots.JdkOrderEntry;
+import com.intellij.openapi.roots.LibraryOrderEntry;
+import com.intellij.openapi.roots.OrderEntry;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.ModuleStructureConfigurable;
+import com.intellij.packaging.artifacts.Artifact;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * @author yole
+ */
+public class IdeaProjectSettingsService extends ProjectSettingsService implements ArtifactAwareProjectSettingsService {
+ private final Project myProject;
+
+ public IdeaProjectSettingsService(final Project project) {
+ myProject = project;
+ }
+
+ @Override
+ public void openProjectSettings() {
+ final ProjectStructureConfigurable config = ProjectStructureConfigurable.getInstance(myProject);
+ ShowSettingsUtil.getInstance().editConfigurable(myProject, config, new Runnable() {
+ @Override
+ public void run() {
+ config.selectProjectGeneralSettings(true);
+ }
+ });
+ }
+
+ @Override
+ public void openGlobalLibraries() {
+ final ProjectStructureConfigurable config = ProjectStructureConfigurable.getInstance(myProject);
+ ShowSettingsUtil.getInstance().editConfigurable(myProject, config, new Runnable() {
+ @Override
+ public void run() {
+ config.selectGlobalLibraries(true);
+ }
+ });
+ }
+
+ @Override
+ public boolean canOpenModuleSettings() {
+ return true;
+ }
+
+ @Override
+ public void openModuleSettings(final Module module) {
+ ModulesConfigurator.showDialog(myProject, module.getName(), null);
+ }
+
+ @Override
+ public boolean canOpenModuleLibrarySettings() {
+ return true;
+ }
+
+ @Override
+ public void openModuleLibrarySettings(final Module module) {
+ ModulesConfigurator.showDialog(myProject, module.getName(), ClasspathEditor.NAME);
+ }
+
+ @Override
+ public boolean canOpenContentEntriesSettings() {
+ return true;
+ }
+
+ @Override
+ public void openContentEntriesSettings(final Module module) {
+ ModulesConfigurator.showDialog(myProject, module.getName(), ContentEntriesEditor.NAME);
+ }
+
+ @Override
+ public boolean canOpenModuleDependenciesSettings() {
+ return true;
+ }
+
+ @Override
+ public void openModuleDependenciesSettings(@NotNull final Module module, @Nullable final OrderEntry orderEntry) {
+ ShowSettingsUtil.getInstance().editConfigurable(myProject, ProjectStructureConfigurable.getInstance(myProject), new Runnable() {
+ @Override
+ public void run() {
+ ProjectStructureConfigurable.getInstance(myProject).selectOrderEntry(module, orderEntry);
+ }
+ });
+ }
+
+ @Override
+ public boolean canOpenLibraryOrSdkSettings(OrderEntry orderEntry) {
+ return true;
+ }
+
+ @Override
+ public void openLibraryOrSdkSettings(@NotNull final OrderEntry orderEntry) {
+ final ProjectStructureConfigurable config = ProjectStructureConfigurable.getInstance(myProject);
+ ShowSettingsUtil.getInstance().editConfigurable(myProject, config, new Runnable() {
+ @Override
+ public void run() {
+ if (orderEntry instanceof JdkOrderEntry) {
+ config.select(((JdkOrderEntry)orderEntry).getJdk(), true);
+ } else {
+ config.select((LibraryOrderEntry)orderEntry, true);
+ }
+ }
+ });
+ }
+
+ @Override
+ public boolean processModulesMoved(final Module[] modules, @Nullable final ModuleGroup targetGroup) {
+ final ModuleStructureConfigurable rootConfigurable = ModuleStructureConfigurable.getInstance(myProject);
+ if (rootConfigurable.updateProjectTree(modules, targetGroup)) { //inside project root editor
+ if (targetGroup != null) {
+ rootConfigurable.selectNodeInTree(targetGroup.toString());
+ }
+ else {
+ rootConfigurable.selectNodeInTree(modules[0].getName());
+ }
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public void showModuleConfigurationDialog(String moduleToSelect, String editorNameToSelect) {
+ ModulesConfigurator.showDialog(myProject, moduleToSelect, editorNameToSelect);
+ }
+
+ @Override
+ public Sdk chooseAndSetSdk() {
+ return JdkChooserPanel.chooseAndSetJDK(myProject);
+ }
+
+ @Override
+ public void openArtifactSettings(@Nullable Artifact artifact) {
+ ModulesConfigurator.showArtifactSettings(myProject, artifact);
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/JavaContentEntriesEditor.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/JavaContentEntriesEditor.java
new file mode 100644
index 0000000..2ed1ac7
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/JavaContentEntriesEditor.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright 2000-2012 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;
+
+import com.intellij.ide.util.projectWizard.importSources.JavaModuleSourceRoot;
+import com.intellij.ide.util.projectWizard.importSources.JavaSourceRootDetectionUtil;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.progress.ProgressIndicator;
+import com.intellij.openapi.progress.ProgressManager;
+import com.intellij.openapi.progress.util.ProgressWindow;
+import com.intellij.openapi.progress.util.SmoothProgressAdapter;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.project.ProjectBundle;
+import com.intellij.openapi.roots.ContentEntry;
+import com.intellij.openapi.roots.ModifiableRootModel;
+import com.intellij.openapi.vfs.LocalFileSystem;
+import com.intellij.openapi.vfs.VfsUtil;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.util.concurrency.SwingWorker;
+
+import javax.swing.*;
+import java.awt.*;
+import java.io.File;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class JavaContentEntriesEditor extends CommonContentEntriesEditor {
+ public JavaContentEntriesEditor(String moduleName, ModuleConfigurationState state) {
+ super(moduleName, state, true, true);
+ }
+
+ @Override
+ protected ContentEntryEditor createContentEntryEditor(final String contentEntryUrl) {
+ return new JavaContentEntryEditor(contentEntryUrl) {
+ @Override
+ protected ModifiableRootModel getModel() {
+ return JavaContentEntriesEditor.this.getModel();
+ }
+ };
+ }
+
+ @Override
+ protected ContentEntryTreeEditor createContentEntryTreeEditor(Project project) {
+ return new ContentEntryTreeEditor(project, true, true);
+ }
+
+ @Override
+ protected List<ContentEntry> addContentEntries(VirtualFile[] files) {
+ List<ContentEntry> contentEntries = super.addContentEntries(files);
+ if (!contentEntries.isEmpty()) {
+ final ContentEntry[] contentEntriesArray = contentEntries.toArray(new ContentEntry[contentEntries.size()]);
+ addSourceRoots(myProject, contentEntriesArray, new Runnable() {
+ @Override
+ public void run() {
+ addContentEntryPanels(contentEntriesArray);
+ }
+ });
+ }
+ return contentEntries;
+ }
+
+ private static void addSourceRoots(final Project project, final ContentEntry[] contentEntries, final Runnable finishRunnable) {
+ final HashMap<ContentEntry, Collection<JavaModuleSourceRoot>> entryToRootMap = new HashMap<ContentEntry, Collection<JavaModuleSourceRoot>>();
+ final Map<File, ContentEntry> fileToEntryMap = new HashMap<File, ContentEntry>();
+ for (final ContentEntry contentEntry : contentEntries) {
+ final VirtualFile file = contentEntry.getFile();
+ if (file != null) {
+ entryToRootMap.put(contentEntry, null);
+ fileToEntryMap.put(VfsUtil.virtualToIoFile(file), contentEntry);
+ }
+ }
+
+ final ProgressWindow progressWindow = new ProgressWindow(true, project);
+ final ProgressIndicator progressIndicator = new SmoothProgressAdapter(progressWindow, project);
+
+ final Runnable searchRunnable = new Runnable() {
+ @Override
+ public void run() {
+ final Runnable process = new Runnable() {
+ @Override
+ public void run() {
+ for (final File file : fileToEntryMap.keySet()) {
+ progressIndicator.setText(ProjectBundle.message("module.paths.searching.source.roots.progress", file.getPath()));
+ final Collection<JavaModuleSourceRoot> roots = JavaSourceRootDetectionUtil.suggestRoots(file);
+ entryToRootMap.put(fileToEntryMap.get(file), roots);
+ }
+ }
+ };
+ progressWindow.setTitle(ProjectBundle.message("module.paths.searching.source.roots.title"));
+ ProgressManager.getInstance().runProcess(process, progressIndicator);
+ }
+ };
+
+ final Runnable addSourcesRunnable = new Runnable() {
+ @Override
+ public void run() {
+ for (final ContentEntry contentEntry : contentEntries) {
+ final Collection<JavaModuleSourceRoot> suggestedRoots = entryToRootMap.get(contentEntry);
+ if (suggestedRoots != null) {
+ for (final JavaModuleSourceRoot suggestedRoot : suggestedRoots) {
+ final VirtualFile sourceRoot = LocalFileSystem.getInstance().findFileByIoFile(suggestedRoot.getDirectory());
+ final VirtualFile fileContent = contentEntry.getFile();
+ if (sourceRoot != null && fileContent != null && VfsUtil.isAncestor(fileContent, sourceRoot, false)) {
+ contentEntry.addSourceFolder(sourceRoot, false, suggestedRoot.getPackagePrefix());
+ }
+ }
+ }
+ }
+ if (finishRunnable != null) {
+ finishRunnable.run();
+ }
+ }
+ };
+
+ new SwingWorker() {
+ @Override
+ public Object construct() {
+ searchRunnable.run();
+ return null;
+ }
+
+ @Override
+ public void finished() {
+ addSourcesRunnable.run();
+ }
+ }.start();
+ }
+
+ @Override
+ protected JPanel createBottomControl(Module module) {
+ final JPanel innerPanel = new JPanel(new GridBagLayout());
+ innerPanel.setBorder(BorderFactory.createEmptyBorder(6, 0, 0, 6));
+ return innerPanel;
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/JavaContentEntryEditor.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/JavaContentEntryEditor.java
new file mode 100644
index 0000000..41b8c8d
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/JavaContentEntryEditor.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2000-2012 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;
+
+import com.intellij.openapi.roots.CompilerModuleExtension;
+import com.intellij.openapi.roots.ContentEntry;
+import com.intellij.openapi.roots.ExcludeFolder;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.vfs.VfsUtil;
+import com.intellij.openapi.vfs.VirtualFile;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+public abstract class JavaContentEntryEditor extends ContentEntryEditor {
+ private final CompilerModuleExtension myCompilerExtension;
+
+ public JavaContentEntryEditor(final String contentEntryUrl) {
+ super(contentEntryUrl, true, true);
+ myCompilerExtension = getModel().getModuleExtension(CompilerModuleExtension.class);
+ }
+
+ @Override
+ protected ContentRootPanel createContentRootPane() {
+ return new JavaContentRootPanel(this) {
+ @Nullable
+ @Override
+ protected ContentEntry getContentEntry() {
+ return JavaContentEntryEditor.this.getContentEntry();
+ }
+ };
+ }
+
+ @Override
+ protected ExcludeFolder doAddExcludeFolder(@NotNull final VirtualFile file) {
+ final boolean isCompilerOutput = isCompilerOutput(file);
+ if (isCompilerOutput) {
+ myCompilerExtension.setExcludeOutput(true);
+ return null;
+ }
+ return super.doAddExcludeFolder(file);
+ }
+
+ @Override
+ protected void doRemoveExcludeFolder(@NotNull final ExcludeFolder excludeFolder) {
+ final VirtualFile file = excludeFolder.getFile();
+ if (file != null) {
+ if (isCompilerOutput(file)) {
+ myCompilerExtension.setExcludeOutput(false);
+ }
+ }
+ super.doRemoveExcludeFolder(excludeFolder);
+ }
+
+ private boolean isCompilerOutput(@NotNull final VirtualFile file) {
+ final VirtualFile compilerOutputPath = myCompilerExtension.getCompilerOutputPath();
+ if (file.equals(compilerOutputPath)) {
+ return true;
+ }
+
+ final VirtualFile compilerOutputPathForTests = myCompilerExtension.getCompilerOutputPathForTests();
+ if (file.equals(compilerOutputPathForTests)) {
+ return true;
+ }
+
+ final String path = file.getPath();
+ if (myCompilerExtension.isCompilerOutputPathInherited()) {
+ final ProjectStructureConfigurable instance = ProjectStructureConfigurable.getInstance(getModel().getModule().getProject());
+ final String compilerOutput = VfsUtil.urlToPath(instance.getProjectConfig().getCompilerOutputUrl());
+ if (FileUtil.pathsEqual(compilerOutput, path)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/JavaContentRootPanel.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/JavaContentRootPanel.java
new file mode 100644
index 0000000..d0af3a1
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/JavaContentRootPanel.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2000-2009 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;
+
+import com.intellij.icons.AllIcons;
+import com.intellij.openapi.project.ProjectBundle;
+import com.intellij.openapi.roots.ContentFolder;
+import com.intellij.openapi.roots.SourceFolder;
+import com.intellij.openapi.ui.Messages;
+import com.intellij.ui.roots.IconActionComponent;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import java.awt.*;
+
+public abstract class JavaContentRootPanel extends ContentRootPanel {
+
+ public JavaContentRootPanel(ActionCallback callback) {
+ super(callback, true, true);
+ }
+
+ @Override
+ @Nullable
+ protected JComponent createAdditionalComponent(ContentFolder folder) {
+ if (folder instanceof SourceFolder) {
+ return createAddPrefixComponent((SourceFolder)folder);
+ }
+ return null;
+ }
+
+ private JComponent createAddPrefixComponent(final SourceFolder folder) {
+ final IconActionComponent iconComponent = new IconActionComponent(AllIcons.Modules.SetPackagePrefix,
+ AllIcons.Modules.SetPackagePrefixRollover,
+ ProjectBundle.message("module.paths.package.prefix.tooltip"), new Runnable() {
+ @Override
+ public void run() {
+ final String message = ProjectBundle.message("module.paths.package.prefix.prompt",
+ toRelativeDisplayPath(folder.getUrl(), getContentEntry().getUrl() + ":"));
+ final String prefix = Messages.showInputDialog(JavaContentRootPanel.this, message,
+ ProjectBundle.message("module.paths.package.prefix.title"), Messages.getQuestionIcon(), folder.getPackagePrefix(), null);
+ if (prefix != null) {
+ myCallback.setPackagePrefix(folder, prefix);
+ }
+ }
+ });
+ final JPanel panel = new JPanel(new BorderLayout());
+ panel.setOpaque(false);
+ panel.add(iconComponent, BorderLayout.CENTER);
+ panel.add(Box.createHorizontalStrut(3), BorderLayout.EAST);
+ return panel;
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/JavaVfsSourceRootDetectionUtil.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/JavaVfsSourceRootDetectionUtil.java
new file mode 100644
index 0000000..f2c817f
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/JavaVfsSourceRootDetectionUtil.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright 2000-2012 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;
+
+import com.intellij.ide.util.projectWizard.importSources.JavaSourceRootDetectionUtil;
+import com.intellij.openapi.fileEditor.impl.LoadTextUtil;
+import com.intellij.openapi.fileTypes.FileType;
+import com.intellij.openapi.fileTypes.FileTypeManager;
+import com.intellij.openapi.fileTypes.StdFileTypes;
+import com.intellij.openapi.progress.ProcessCanceledException;
+import com.intellij.openapi.progress.ProgressIndicator;
+import com.intellij.openapi.util.SystemInfo;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.openapi.vfs.VfsUtilCore;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.openapi.vfs.VirtualFileVisitor;
+import com.intellij.util.containers.ContainerUtil;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class JavaVfsSourceRootDetectionUtil {
+ private JavaVfsSourceRootDetectionUtil() {}
+
+ /**
+ * Scan directory and detect java source roots within it. The source root is detected as the following:
+ * <ol>
+ * <li>It contains at least one Java file.</li>
+ * <li>Java file is located in the sub-folder that matches package statement in the file.</li>
+ * </ol>
+ *
+ * @param dir a directory to scan
+ * @param progressIndicator
+ * @return a list of found source roots within directory. If no source roots are found, a empty list is returned.
+ */
+ @NotNull
+ public static List<VirtualFile> suggestRoots(@NotNull VirtualFile dir, @NotNull final ProgressIndicator progressIndicator) {
+ if (!dir.isDirectory()) {
+ return ContainerUtil.emptyList();
+ }
+
+ final FileTypeManager typeManager = FileTypeManager.getInstance();
+ final ArrayList<VirtualFile> foundDirectories = new ArrayList<VirtualFile>();
+ try {
+ VfsUtilCore.visitChildrenRecursively(dir, new VirtualFileVisitor() {
+ @NotNull
+ @Override
+ public Result visitFileEx(@NotNull VirtualFile file) {
+ progressIndicator.checkCanceled();
+
+ if (file.isDirectory()) {
+ if (typeManager.isFileIgnored(file) || StringUtil.startsWithIgnoreCase(file.getName(), "testData")) {
+ return SKIP_CHILDREN;
+ }
+ }
+ else {
+ FileType type = typeManager.getFileTypeByFileName(file.getName());
+ if (StdFileTypes.JAVA == type) {
+ VirtualFile root = suggestRootForJavaFile(file);
+ if (root != null) {
+ foundDirectories.add(root);
+ return skipTo(root);
+ }
+ }
+ }
+
+ return CONTINUE;
+ }
+ });
+ }
+ catch (ProcessCanceledException ignore) { }
+
+ return foundDirectories;
+ }
+
+ @Nullable
+ private static VirtualFile suggestRootForJavaFile(VirtualFile javaFile) {
+ if (javaFile.isDirectory()) return null;
+
+ CharSequence chars = LoadTextUtil.loadText(javaFile);
+
+ String packageName = JavaSourceRootDetectionUtil.getPackageName(chars);
+ if (packageName != null){
+ VirtualFile root = javaFile.getParent();
+ int index = packageName.length();
+ while(index > 0){
+ int index1 = packageName.lastIndexOf('.', index - 1);
+ String token = packageName.substring(index1 + 1, index);
+ String dirName = root.getName();
+ final boolean equalsToToken = SystemInfo.isFileSystemCaseSensitive ? dirName.equals(token) : dirName.equalsIgnoreCase(token);
+ if (!equalsToToken) {
+ return null;
+ }
+ root = root.getParent();
+ if (root == null){
+ return null;
+ }
+ index = index1;
+ }
+ return root;
+ }
+
+ return null;
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/JavadocEditor.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/JavadocEditor.java
new file mode 100644
index 0000000..1f5d785
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/JavadocEditor.java
@@ -0,0 +1,206 @@
+/*
+ * Copyright 2000-2012 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;
+
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.fileChooser.FileChooser;
+import com.intellij.openapi.fileChooser.FileChooserDescriptor;
+import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory;
+import com.intellij.openapi.project.ProjectBundle;
+import com.intellij.openapi.projectRoots.ui.Util;
+import com.intellij.openapi.roots.JavaModuleExternalPaths;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.ui.*;
+import com.intellij.ui.components.JBLabel;
+import com.intellij.ui.table.JBTable;
+import com.intellij.util.ArrayUtil;
+import com.intellij.util.IconUtil;
+import com.intellij.util.ui.ItemRemovable;
+import com.intellij.util.ui.UIUtil;
+
+import javax.swing.*;
+import javax.swing.border.Border;
+import javax.swing.table.DefaultTableModel;
+import java.awt.*;
+import java.util.List;
+
+/**
+ * @author Eugene Zhuravlev
+ * Date: Oct 4, 2003
+ * Time: 6:54:57 PM
+ */
+public class JavadocEditor extends ModuleElementsEditor {
+ private JTable myTable;
+
+ private static final String NAME = ProjectBundle.message("module.javadoc.title");
+
+ public JavadocEditor(ModuleConfigurationState state) {
+ super(state);
+ }
+
+ @Override
+ public String getHelpTopic() {
+ return "projectStructure.modules.paths";
+ }
+
+ @Override
+ public String getDisplayName() {
+ return NAME;
+ }
+
+ @Override
+ public void saveData() {
+ TableUtil.stopEditing(myTable);
+ final int count = myTable.getRowCount();
+ String[] urls = ArrayUtil.newStringArray(count);
+ for (int row = 0; row < count; row++) {
+ final TableItem item = ((MyTableModel)myTable.getModel()).getTableItemAt(row);
+ urls[row] = item.getUrl();
+ }
+ getModel().getModuleExtension(JavaModuleExternalPaths.class).setJavadocUrls(urls);
+ }
+
+ @Override
+ public JComponent createComponentImpl() {
+ final DefaultTableModel tableModel = createModel();
+ myTable = new JBTable(tableModel);
+ myTable.setIntercellSpacing(new Dimension(0, 0));
+ myTable.setDefaultRenderer(TableItem.class, new MyRenderer());
+ myTable.setShowGrid(false);
+ myTable.setDragEnabled(false);
+ myTable.setShowHorizontalLines(false);
+ myTable.setShowVerticalLines(false);
+ myTable.getSelectionModel().setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
+
+ JPanel tablePanel = ToolbarDecorator.createDecorator(myTable)
+ .setAddAction(new AnActionButtonRunnable() {
+ @Override
+ public void run(AnActionButton button) {
+ FileChooserDescriptor myDescriptor = FileChooserDescriptorFactory.createMultipleJavaPathDescriptor();
+ myDescriptor.setTitle(ProjectBundle.message("module.javadoc.add.path.title"));
+ myDescriptor.setDescription(ProjectBundle.message("module.javadoc.add.path.prompt"));
+ VirtualFile[] files = FileChooser.chooseFiles(myDescriptor, myTable, myProject, null);
+ final MyTableModel tableModel = (MyTableModel)myTable.getModel();
+ boolean changes = false;
+ for (final VirtualFile file : files) {
+ if (file != null) {
+ tableModel.addTableItem(new TableItem(file));
+ changes = true;
+ }
+ }
+ if (changes) {
+ saveData();
+ TableUtil.selectRows(myTable, new int[]{tableModel.getRowCount() - 1});
+ }
+ }
+ }).addExtraAction(new AnActionButton(ProjectBundle.message("module.javadoc.add.url.button"), IconUtil.getAddLinkIcon()) {
+ @Override
+ public void actionPerformed(AnActionEvent e) {
+ VirtualFile[] files = new VirtualFile[]{Util.showSpecifyJavadocUrlDialog(myTable)};
+ final MyTableModel tableModel = (MyTableModel)myTable.getModel();
+ boolean changes = false;
+ for (final VirtualFile file : files) {
+ if (file != null) {
+ tableModel.addTableItem(new TableItem(file));
+ changes = true;
+ }
+ }
+ if (changes) {
+ saveData();
+ TableUtil.selectRows(myTable, new int[]{tableModel.getRowCount() - 1});
+ }
+ }
+ }).setRemoveAction(new AnActionButtonRunnable() {
+ @Override
+ public void run(AnActionButton button) {
+ final List removedItems = TableUtil.removeSelectedItems(myTable);
+ if (removedItems.size() > 0) {
+ saveData();
+ }
+ }
+ }).setButtonComparator("Add", ProjectBundle.message("module.javadoc.add.url.button"), "Remove").createPanel();
+
+ final JPanel mainPanel = new JPanel(new BorderLayout());
+ mainPanel.add(tablePanel, BorderLayout.CENTER);
+ mainPanel.add(
+ new JBLabel(ProjectBundle.message("project.roots.javadoc.tab.description"), UIUtil.ComponentStyle.SMALL, UIUtil.FontColor.BRIGHTER),
+ BorderLayout.NORTH);
+ return mainPanel;
+ }
+
+ protected DefaultTableModel createModel() {
+ final MyTableModel tableModel = new MyTableModel();
+ final String[] javadocUrls = getModel().getModuleExtension(JavaModuleExternalPaths.class).getJavadocUrls();
+ for (String javadocUrl : javadocUrls) {
+ tableModel.addTableItem(new TableItem(javadocUrl));
+ }
+ return tableModel;
+ }
+
+ @Override
+ public void moduleStateChanged() {
+ if (myTable != null) {
+ final DefaultTableModel tableModel = createModel();
+ myTable.setModel(tableModel);
+ }
+ }
+
+ private static class MyRenderer extends ColoredTableCellRenderer {
+ private static final Border NO_FOCUS_BORDER = BorderFactory.createEmptyBorder(1, 1, 1, 1);
+
+ @Override
+ protected void customizeCellRenderer(JTable table, Object value, boolean selected, boolean hasFocus, int row, int column) {
+ setPaintFocusBorder(false);
+ setFocusBorderAroundIcon(true);
+ setBorder(NO_FOCUS_BORDER);
+
+ final TableItem tableItem = ((TableItem)value);
+ if (tableItem != null) {
+ tableItem.getCellAppearance().customize(this);
+ }
+ }
+ }
+
+ private static class MyTableModel extends DefaultTableModel implements ItemRemovable {
+ @Override
+ public String getColumnName(int column) {
+ return null;
+ }
+
+ @Override
+ public Class getColumnClass(int columnIndex) {
+ return TableItem.class;
+ }
+
+ @Override
+ public int getColumnCount() {
+ return 1;
+ }
+
+ @Override
+ public boolean isCellEditable(int row, int column) {
+ return false;
+ }
+
+ public TableItem getTableItemAt(int row) {
+ return (TableItem)getValueAt(row, 0);
+ }
+
+ public void addTableItem(TableItem item) {
+ addRow(new Object[]{item});
+ }
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/JdkComboBox.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/JdkComboBox.java
new file mode 100644
index 0000000..f81f840
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/JdkComboBox.java
@@ -0,0 +1,374 @@
+/*
+ * Copyright 2000-2012 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;
+
+import com.intellij.ide.DataManager;
+import com.intellij.ide.util.projectWizard.ProjectJdkListRenderer;
+import com.intellij.openapi.actionSystem.*;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.project.ProjectBundle;
+import com.intellij.openapi.projectRoots.Sdk;
+import com.intellij.openapi.projectRoots.SdkType;
+import com.intellij.openapi.projectRoots.SdkTypeId;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.JdkListConfigurable;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.ProjectSdksModel;
+import com.intellij.openapi.ui.ComboBoxWithWidePopup;
+import com.intellij.openapi.ui.popup.JBPopupFactory;
+import com.intellij.openapi.util.Computable;
+import com.intellij.openapi.util.Condition;
+import com.intellij.openapi.util.Conditions;
+import com.intellij.ui.ScreenUtil;
+import com.intellij.ui.SimpleTextAttributes;
+import com.intellij.util.Consumer;
+import com.intellij.util.containers.ContainerUtil;
+import com.intellij.util.ui.EmptyIcon;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import java.awt.*;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.util.*;
+import java.util.List;
+
+/**
+ * @author Eugene Zhuravlev
+ * @since May 18, 2005
+ */
+public class JdkComboBox extends ComboBoxWithWidePopup {
+
+ private static final Icon EMPTY_ICON = EmptyIcon.create(1, 16);
+
+ @Nullable
+ private final Condition<SdkTypeId> myFilter;
+ @Nullable
+ private final Condition<SdkTypeId> myCreationFilter;
+
+ public JdkComboBox(@NotNull final ProjectSdksModel jdkModel) {
+ this(jdkModel, null);
+ }
+
+ public JdkComboBox(@NotNull final ProjectSdksModel jdkModel,
+ @Nullable Condition<SdkTypeId> filter) {
+ this(jdkModel, filter, filter);
+ }
+
+ public JdkComboBox(@NotNull final ProjectSdksModel jdkModel,
+ @Nullable Condition<SdkTypeId> filter,
+ @Nullable Condition<SdkTypeId> creationFilter) {
+ super(new JdkComboBoxModel(jdkModel, getSdkFilter(filter)));
+ myFilter = filter;
+ myCreationFilter = creationFilter;
+ setRenderer(new ProjectJdkListRenderer() {
+ @Override
+ public void doCustomize(JList list, Object value, int index, boolean selected, boolean hasFocus) {
+ if (JdkComboBox.this.isEnabled()) {
+ setIcon(EMPTY_ICON); // to fix vertical size
+ if (value instanceof InvalidJdkComboBoxItem) {
+ final String str = value.toString();
+ append(str, SimpleTextAttributes.ERROR_ATTRIBUTES);
+ }
+ else if (value instanceof ProjectJdkComboBoxItem) {
+ final Sdk jdk = jdkModel.getProjectSdk();
+ if (jdk != null) {
+ setIcon(((SdkType) jdk.getSdkType()).getIcon());
+ append(ProjectBundle.message("project.roots.project.jdk.inherited"), SimpleTextAttributes.REGULAR_ATTRIBUTES);
+ append(" (" + jdk.getName() + ")", SimpleTextAttributes.GRAYED_ATTRIBUTES);
+ }
+ else {
+ final String str = value.toString();
+ append(str, SimpleTextAttributes.ERROR_ATTRIBUTES);
+ }
+ }
+ else {
+ super.doCustomize(list, value != null ? ((JdkComboBoxItem)value).getJdk()
+ : new NoneJdkComboBoxItem(), index, selected, hasFocus);
+ }
+ }
+ }
+ });
+ }
+
+ @Override
+ public Dimension getPreferredSize() {
+ final Rectangle rec = ScreenUtil.getScreenRectangle(0, 0);
+ final Dimension size = super.getPreferredSize();
+ final int maxWidth = rec.width / 4;
+ if (size.width > maxWidth) {
+ size.width = maxWidth;
+ }
+ return size;
+ }
+
+ @Override
+ public Dimension getMinimumSize() {
+ final Dimension minSize = super.getMinimumSize();
+ final Dimension prefSize = getPreferredSize();
+ if (minSize.width > prefSize.width) {
+ minSize.width = prefSize.width;
+ }
+ return minSize;
+ }
+
+ public void setSetupButton(final JButton setUpButton,
+ @Nullable final Project project,
+ final ProjectSdksModel jdksModel,
+ final JdkComboBoxItem firstItem,
+ @Nullable final Condition<Sdk> additionalSetup,
+ final boolean moduleJdkSetup) {
+ setSetupButton(setUpButton, project, jdksModel, firstItem, additionalSetup,
+ ProjectBundle.message("project.roots.set.up.jdk.title", moduleJdkSetup ? 1 : 2));
+ }
+
+ public void setSetupButton(final JButton setUpButton,
+ @Nullable final Project project,
+ final ProjectSdksModel jdksModel,
+ final JdkComboBoxItem firstItem,
+ @Nullable final Condition<Sdk> additionalSetup,
+ final String actionGroupTitle) {
+ setUpButton.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ DefaultActionGroup group = new DefaultActionGroup();
+ jdksModel.createAddActions(group, JdkComboBox.this, new Consumer<Sdk>() {
+ @Override
+ public void consume(final Sdk jdk) {
+ if (project != null) {
+ final JdkListConfigurable configurable = JdkListConfigurable.getInstance(project);
+ configurable.addJdkNode(jdk, false);
+ }
+ reloadModel(new JdkComboBoxItem(jdk), project);
+ setSelectedJdk(jdk); //restore selection
+ if (additionalSetup != null) {
+ if (additionalSetup.value(jdk)) { //leave old selection
+ setSelectedJdk(firstItem.getJdk());
+ }
+ }
+ }
+ }, myCreationFilter);
+ final DataContext dataContext = DataManager.getInstance().getDataContext(JdkComboBox.this);
+ if (group.getChildrenCount() > 1) {
+ JBPopupFactory.getInstance()
+ .createActionGroupPopup(actionGroupTitle, group, dataContext, JBPopupFactory.ActionSelectionAid.MNEMONICS, false)
+ .showUnderneathOf(setUpButton);
+ }
+ else {
+ final AnActionEvent event =
+ new AnActionEvent(null, dataContext, ActionPlaces.UNKNOWN, new Presentation(""), ActionManager.getInstance(), 0);
+ group.getChildren(event)[0].actionPerformed(event);
+ }
+ }
+ });
+ }
+
+ public void setEditButton(final JButton editButton, final Project project, final Computable<Sdk> retrieveJDK){
+ editButton.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ final Sdk projectJdk = retrieveJDK.compute();
+ if (projectJdk != null) {
+ ProjectStructureConfigurable.getInstance(project).select(projectJdk, true);
+ }
+ }
+ });
+ addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ final JdkComboBoxItem selectedItem = getSelectedItem();
+ if (selectedItem instanceof ProjectJdkComboBoxItem) {
+ editButton.setEnabled(ProjectStructureConfigurable.getInstance(project).getProjectJdksModel().getProjectSdk() != null);
+ }
+ else {
+ editButton.setEnabled(!(selectedItem instanceof InvalidJdkComboBoxItem) && selectedItem != null && selectedItem.getJdk() != null);
+ }
+ }
+ });
+ }
+
+ @Override
+ public JdkComboBoxItem getSelectedItem() {
+ return (JdkComboBoxItem)super.getSelectedItem();
+ }
+
+ @Nullable
+ public Sdk getSelectedJdk() {
+ final JdkComboBoxItem selectedItem = (JdkComboBoxItem)super.getSelectedItem();
+ return selectedItem != null? selectedItem.getJdk() : null;
+ }
+
+ public void setSelectedJdk(Sdk jdk) {
+ final int index = indexOf(jdk);
+ if (index >= 0) {
+ setSelectedIndex(index);
+ }
+ }
+
+ public void setInvalidJdk(String name) {
+ removeInvalidElement();
+ addItem(new InvalidJdkComboBoxItem(name));
+ setSelectedIndex(getModel().getSize() - 1);
+ }
+
+ private int indexOf(Sdk jdk) {
+ final JdkComboBoxModel model = (JdkComboBoxModel)getModel();
+ final int count = model.getSize();
+ for (int idx = 0; idx < count; idx++) {
+ final JdkComboBoxItem elementAt = model.getElementAt(idx);
+ if (jdk == null) {
+ if (elementAt instanceof NoneJdkComboBoxItem || elementAt instanceof ProjectJdkComboBoxItem) {
+ return idx;
+ }
+ }
+ else {
+ Sdk elementAtJdk = elementAt.getJdk();
+ if (elementAtJdk != null && jdk.getName().equals(elementAtJdk.getName())) {
+ return idx;
+ }
+ }
+ }
+ return -1;
+ }
+
+ private void removeInvalidElement() {
+ final JdkComboBoxModel model = (JdkComboBoxModel)getModel();
+ final int count = model.getSize();
+ for (int idx = 0; idx < count; idx++) {
+ final JdkComboBoxItem elementAt = model.getElementAt(idx);
+ if (elementAt instanceof InvalidJdkComboBoxItem) {
+ removeItemAt(idx);
+ break;
+ }
+ }
+ }
+
+ public void reloadModel(JdkComboBoxItem firstItem, @Nullable Project project) {
+ final DefaultComboBoxModel model = ((DefaultComboBoxModel)getModel());
+ if (project == null) {
+ model.addElement(firstItem);
+ return;
+ }
+ model.removeAllElements();
+ model.addElement(firstItem);
+ final ProjectSdksModel projectJdksModel = ProjectStructureConfigurable.getInstance(project).getProjectJdksModel();
+ List<Sdk> projectJdks = new ArrayList<Sdk>(projectJdksModel.getProjectSdks().values());
+ if (myFilter != null) {
+ projectJdks = ContainerUtil.filter(projectJdks, getSdkFilter(myFilter));
+ }
+ Collections.sort(projectJdks, new Comparator<Sdk>() {
+ @Override
+ public int compare(final Sdk o1, final Sdk o2) {
+ return o1.getName().compareToIgnoreCase(o2.getName());
+ }
+ });
+ for (Sdk projectJdk : projectJdks) {
+ model.addElement(new JdkComboBox.JdkComboBoxItem(projectJdk));
+ }
+ }
+
+ private static class JdkComboBoxModel extends DefaultComboBoxModel {
+ public JdkComboBoxModel(final ProjectSdksModel jdksModel, Condition<Sdk> sdkFilter) {
+ Sdk[] jdks = jdksModel.getSdks();
+ if (sdkFilter != null) {
+ final List<Sdk> filtered = ContainerUtil.filter(jdks, sdkFilter);
+ jdks = filtered.toArray(new Sdk[filtered.size()]);
+ }
+ Arrays.sort(jdks, new Comparator<Sdk>() {
+ @Override
+ public int compare(final Sdk s1, final Sdk s2) {
+ return s1.getName().compareToIgnoreCase(s2.getName());
+ }
+ });
+ for (Sdk jdk : jdks) {
+ addElement(new JdkComboBoxItem(jdk));
+ }
+ }
+
+ // implements javax.swing.ListModel
+ @Override
+ public JdkComboBoxItem getElementAt(int index) {
+ return (JdkComboBoxItem)super.getElementAt(index);
+ }
+ }
+
+ private static Condition<Sdk> getSdkFilter(@Nullable final Condition<SdkTypeId> filter) {
+ return filter == null ? Conditions.<Sdk>alwaysTrue() : new Condition<Sdk>() {
+ @Override
+ public boolean value(Sdk sdk) {
+ return filter.value(sdk.getSdkType());
+ }
+ };
+ }
+
+ public static class JdkComboBoxItem {
+ private final Sdk myJdk;
+
+ public JdkComboBoxItem(@Nullable Sdk jdk) {
+ myJdk = jdk;
+ }
+
+ public Sdk getJdk() {
+ return myJdk;
+ }
+
+ @Nullable
+ public String getSdkName() {
+ return myJdk != null ? myJdk.getName() : null;
+ }
+
+ public String toString() {
+ return myJdk.getName();
+ }
+ }
+
+ public static class ProjectJdkComboBoxItem extends JdkComboBoxItem {
+ public ProjectJdkComboBoxItem() {
+ super(null);
+ }
+
+ public String toString() {
+ return ProjectBundle.message("jdk.combo.box.project.item");
+ }
+ }
+
+ public static class NoneJdkComboBoxItem extends JdkComboBoxItem {
+ public NoneJdkComboBoxItem() {
+ super(null);
+ }
+
+ public String toString() {
+ return ProjectBundle.message("jdk.combo.box.none.item");
+ }
+ }
+
+ private static class InvalidJdkComboBoxItem extends JdkComboBoxItem {
+ private final String mySdkName;
+
+ public InvalidJdkComboBoxItem(String name) {
+ super(null);
+ mySdkName = name;
+ }
+
+ @Override
+ public String getSdkName() {
+ return mySdkName;
+ }
+
+ public String toString() {
+ return ProjectBundle.message("jdk.combo.box.invalid.item", mySdkName);
+ }
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/LanguageLevelCombo.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/LanguageLevelCombo.java
new file mode 100644
index 0000000..dce7820
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/LanguageLevelCombo.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2000-2012 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;
+
+import com.intellij.ui.ListCellRendererWrapper;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.project.ProjectBundle;
+import com.intellij.openapi.roots.LanguageLevelProjectExtension;
+import com.intellij.openapi.ui.ComboBox;
+import com.intellij.pom.java.LanguageLevel;
+
+import javax.swing.*;
+
+/**
+ * @author ven
+ */
+public class LanguageLevelCombo extends ComboBox {
+ public static final String USE_PROJECT_LANGUAGE_LEVEL = ProjectBundle.message("project.language.level.combo.item");
+
+ public LanguageLevelCombo() {
+ for (LanguageLevel level : LanguageLevel.values()) {
+ addItem(level);
+ }
+ setRenderer(new ListCellRendererWrapper() {
+ @Override
+ public void customize(final JList list, final Object value, final int index, final boolean selected, final boolean hasFocus) {
+ if (value instanceof LanguageLevel) {
+ setText(((LanguageLevel)value).getPresentableText());
+ }
+ else if (value instanceof String) {
+ setText((String)value);
+ }
+ }
+ });
+ }
+
+ public void reset(Project project) {
+ setSelectedItem(LanguageLevelProjectExtension.getInstance(project).getLanguageLevel());
+ }
+
+ @Override
+ public void setSelectedItem(Object anObject) {
+ if (anObject == null) {
+ anObject = USE_PROJECT_LANGUAGE_LEVEL;
+ }
+ super.setSelectedItem(anObject);
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/LanguageLevelConfigurable.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/LanguageLevelConfigurable.java
new file mode 100644
index 0000000..76366f4
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/LanguageLevelConfigurable.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2000-2011 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;
+
+import com.intellij.openapi.options.ConfigurationException;
+import com.intellij.openapi.options.UnnamedConfigurable;
+import com.intellij.openapi.project.ProjectBundle;
+import com.intellij.openapi.roots.LanguageLevelModuleExtension;
+import com.intellij.pom.java.LanguageLevel;
+
+import javax.swing.*;
+import java.awt.*;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+
+/**
+ * User: anna
+ * Date: 06-Jun-2006
+ */
+public abstract class LanguageLevelConfigurable implements UnnamedConfigurable {
+ private LanguageLevelCombo myLanguageLevelCombo;
+ private JPanel myPanel = new JPanel(new GridBagLayout());
+
+ public LanguageLevelConfigurable() {
+ myLanguageLevelCombo = new LanguageLevelCombo();
+ myLanguageLevelCombo.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(final ActionEvent e) {
+ final Object languageLevel = myLanguageLevelCombo.getSelectedItem();
+ getLanguageLevelExtension().setLanguageLevel(languageLevel instanceof LanguageLevel ? (LanguageLevel)languageLevel : null);
+ }
+ });
+ myLanguageLevelCombo.insertItemAt(LanguageLevelCombo.USE_PROJECT_LANGUAGE_LEVEL, 0);
+
+ myPanel.add(new JLabel(ProjectBundle.message("module.module.language.level")),
+ new GridBagConstraints(0, 0, 1, 1, 0, 0, GridBagConstraints.NORTHWEST, GridBagConstraints.NONE, new Insets(12, 6, 12, 0), 0, 0));
+ myPanel.add(myLanguageLevelCombo,
+ new GridBagConstraints(1, 0, 1, 1, 0, 0, GridBagConstraints.NORTHWEST, GridBagConstraints.NONE, new Insets(6, 6, 12, 0), 0, 0));
+ myPanel.add(new JLabel(ProjectBundle.message("module.module.language.level.comment")),
+ new GridBagConstraints(2, 0, 1, 1, 1, 0, GridBagConstraints.NORTHWEST, GridBagConstraints.NONE, new Insets(12, 6, 12, 0), 0, 0));
+ }
+
+ @Override
+ public JComponent createComponent() {
+ return myPanel;
+ }
+
+ @Override
+ public boolean isModified() {
+ return getLanguageLevelExtension().isChanged();
+ }
+
+ @Override
+ public void apply() throws ConfigurationException {
+ getLanguageLevelExtension().commit();
+ }
+
+ @Override
+ public void reset() {
+ myLanguageLevelCombo.setSelectedItem(getLanguageLevelExtension().getLanguageLevel());
+ }
+
+ @Override
+ public void disposeUIResources() {
+ myPanel = null;
+ myLanguageLevelCombo = null;
+ }
+
+ public abstract LanguageLevelModuleExtension getLanguageLevelExtension();
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/LibrariesAlphaComparator.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/LibrariesAlphaComparator.java
new file mode 100644
index 0000000..1463e17
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/LibrariesAlphaComparator.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2000-2009 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;
+
+import com.intellij.openapi.roots.OrderRootType;
+import com.intellij.openapi.roots.libraries.Library;
+import com.intellij.openapi.vfs.VirtualFile;
+
+import java.util.Comparator;
+
+/**
+ * @author Eugene Zhuravlev
+ * Date: Jan 14, 2004
+ */
+public class LibrariesAlphaComparator implements Comparator<Library> {
+ public static LibrariesAlphaComparator INSTANCE = new LibrariesAlphaComparator();
+
+ @Override
+ public int compare(Library library1, Library library2) {
+ String name1 = library1.getName();
+ if (name1 != null && name1.length() == 0) {
+ name1 = null;
+ }
+ String name2 = library2.getName();
+ if (name2 != null && name2.length() == 0) {
+ name2 = null;
+ }
+ if (name1 == null && name2 == null) {
+ final VirtualFile[] files1 = library1.getFiles(OrderRootType.CLASSES);
+ final VirtualFile[] files2 = library2.getFiles(OrderRootType.CLASSES);
+ name1 = files1.length > 0? files1[0].getName() : null;
+ name2 = files2.length > 0? files2[0].getName() : null;
+ }
+ return compareNames(name1, name2);
+ }
+
+ public int compareNames(String name1, String name2) {
+ if (name1 == null && name2 == null) {
+ return 0;
+ }
+ else if (name1 == null) {
+ return -1;
+ }
+ else if (name2 == null) {
+ return +1;
+ }
+ else {
+ return name1.compareToIgnoreCase(name2);
+ }
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/LibraryRootsComponentForm.form b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/LibraryRootsComponentForm.form
new file mode 100644
index 0000000..d6a4003
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/LibraryRootsComponentForm.form
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.intellij.openapi.roots.ui.configuration.libraryEditor.LibraryRootsComponent">
+ <grid id="e9129" binding="myPanel" layout-manager="GridLayoutManager" row-count="3" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="2">
+ <margin top="0" left="0" bottom="0" right="0"/>
+ <constraints>
+ <xy x="97" y="50" width="403" height="352"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <xy id="bc98" binding="myTreePanel" layout-manager="XYLayout" hgap="-1" vgap="-1">
+ <margin top="0" left="0" bottom="0" right="0"/>
+ <constraints>
+ <grid row="2" column="0" row-span="1" col-span="1" vsize-policy="7" hsize-policy="7" anchor="0" fill="3" indent="0" use-parent-layout="false">
+ <preferred-size width="300" height="-1"/>
+ </grid>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children/>
+ </xy>
+ <component id="8c4b3" class="com.intellij.openapi.ui.ex.MultiLineLabel" binding="myPropertiesLabel">
+ <constraints>
+ <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties/>
+ </component>
+ <grid id="19827" binding="myPropertiesPanel" layout-manager="BorderLayout" hgap="0" vgap="0">
+ <constraints>
+ <grid row="1" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children/>
+ </grid>
+ </children>
+ </grid>
+</form>
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/LibraryTableModifiableModelProvider.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/LibraryTableModifiableModelProvider.java
new file mode 100644
index 0000000..988ae30
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/LibraryTableModifiableModelProvider.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2000-2009 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;
+
+import com.intellij.openapi.roots.libraries.LibraryTable;
+
+public interface LibraryTableModifiableModelProvider {
+
+ LibraryTable.ModifiableModel getModifiableModel();
+
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/ModuleConfigurableWrapper.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/ModuleConfigurableWrapper.java
new file mode 100644
index 0000000..a2edaf0
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/ModuleConfigurableWrapper.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2000-2011 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;
+
+import com.intellij.openapi.module.ModuleConfigurationEditor;
+import com.intellij.openapi.options.Configurable;
+import com.intellij.openapi.options.ConfigurationException;
+
+import javax.swing.*;
+
+/**
+* @author nik
+*/
+class ModuleConfigurableWrapper implements ModuleConfigurationEditor {
+ private final Configurable myModuleConfigurable;
+
+ public ModuleConfigurableWrapper(Configurable moduleConfigurable) {
+ myModuleConfigurable = moduleConfigurable;
+ }
+
+ @Override
+ public void saveData() {
+
+ }
+
+ @Override
+ public void moduleStateChanged() {
+ }
+
+ @Override
+ public String getDisplayName() {
+ return myModuleConfigurable.getDisplayName();
+ }
+
+ @Override
+ public String getHelpTopic() {
+ return myModuleConfigurable.getHelpTopic();
+ }
+
+ @Override
+ public JComponent createComponent() {
+ return myModuleConfigurable.createComponent();
+ }
+
+ @Override
+ public boolean isModified() {
+ return myModuleConfigurable.isModified();
+ }
+
+ @Override
+ public void apply() throws ConfigurationException {
+ myModuleConfigurable.apply();
+ }
+
+ @Override
+ public void reset() {
+ myModuleConfigurable.reset();
+ }
+
+ @Override
+ public void disposeUIResources() {
+ myModuleConfigurable.disposeUIResources();
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/ModuleEditor.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/ModuleEditor.java
new file mode 100644
index 0000000..cdea51f
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/ModuleEditor.java
@@ -0,0 +1,556 @@
+/*
+ * Copyright 2000-2009 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;
+
+import com.intellij.facet.impl.ProjectFacetsConfigurator;
+import com.intellij.openapi.Disposable;
+import com.intellij.openapi.actionSystem.DataProvider;
+import com.intellij.openapi.actionSystem.LangDataKeys;
+import com.intellij.openapi.extensions.ExtensionPointName;
+import com.intellij.openapi.extensions.Extensions;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.module.ModuleConfigurationEditor;
+import com.intellij.openapi.module.impl.ModuleConfigurationStateImpl;
+import com.intellij.openapi.options.Configurable;
+import com.intellij.openapi.options.ConfigurationException;
+import com.intellij.openapi.options.ModuleConfigurableEP;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.roots.ModifiableRootModel;
+import com.intellij.openapi.roots.ModuleRootManager;
+import com.intellij.openapi.roots.ModuleRootModel;
+import com.intellij.openapi.roots.OrderEntry;
+import com.intellij.openapi.roots.impl.ModuleRootManagerImpl;
+import com.intellij.openapi.roots.impl.libraries.LibraryEx;
+import com.intellij.openapi.roots.impl.libraries.LibraryTableBase;
+import com.intellij.openapi.roots.libraries.Library;
+import com.intellij.openapi.roots.libraries.LibraryTable;
+import com.intellij.ui.navigation.History;
+import com.intellij.ui.navigation.Place;
+import com.intellij.util.EventDispatcher;
+import com.intellij.util.containers.ContainerUtil;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import java.awt.*;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.util.*;
+import java.util.List;
+
+/**
+ * @author Eugene Zhuravlev
+ * Date: Oct 4, 2003
+ * Time: 6:29:56 PM
+ */
+@SuppressWarnings({"AssignmentToStaticFieldFromInstanceMethod"})
+public abstract class ModuleEditor implements Place.Navigator, Disposable {
+ private static final ExtensionPointName<ModuleConfigurableEP> MODULE_CONFIGURABLES = ExtensionPointName.create("com.intellij.moduleConfigurable");
+ public static final String SELECTED_EDITOR_NAME = "selectedEditor";
+
+ private final Project myProject;
+ private JPanel myGenericSettingsPanel;
+ private ModifiableRootModel myModifiableRootModel; // important: in order to correctly update OrderEntries UI use corresponding proxy for the model
+
+ private final ModulesProvider myModulesProvider;
+ private String myName;
+ private final Module myModule;
+
+ protected final List<ModuleConfigurationEditor> myEditors = new ArrayList<ModuleConfigurationEditor>();
+ private ModifiableRootModel myModifiableRootModelProxy;
+
+ private final EventDispatcher<ChangeListener> myEventDispatcher = EventDispatcher.create(ChangeListener.class);
+ @NonNls private static final String METHOD_COMMIT = "commit";
+
+ protected History myHistory;
+
+ public ModuleEditor(Project project, ModulesProvider modulesProvider,
+ @NotNull Module module) {
+ myProject = project;
+ myModulesProvider = modulesProvider;
+ myModule = module;
+ myName = module.getName();
+ }
+
+ public void init(History history) {
+ myHistory = history;
+
+ for (ModuleConfigurationEditor each : myEditors) {
+ if (each instanceof ModuleElementsEditor) {
+ ((ModuleElementsEditor)each).setHistory(myHistory);
+ }
+ }
+
+ restoreSelectedEditor();
+ }
+
+ public abstract ProjectFacetsConfigurator getFacetsConfigurator();
+
+ protected abstract JComponent createCenterPanel();
+
+ @Nullable
+ public abstract ModuleConfigurationEditor getSelectedEditor();
+
+ public abstract void selectEditor(String displayName);
+
+ protected abstract void restoreSelectedEditor();
+
+ @Nullable
+ public abstract ModuleConfigurationEditor getEditor(@NotNull String displayName);
+
+ protected abstract void disposeCenterPanel();
+
+ public interface ChangeListener extends EventListener {
+ void moduleStateChanged(ModifiableRootModel moduleRootModel);
+ }
+
+ public void addChangeListener(ChangeListener listener) {
+ myEventDispatcher.addListener(listener);
+ }
+
+ public void removeChangeListener(ChangeListener listener) {
+ myEventDispatcher.removeListener(listener);
+ }
+
+ @Nullable
+ public Module getModule() {
+ final Module[] all = myModulesProvider.getModules();
+ for (Module each : all) {
+ if (each == myModule) return myModule;
+ }
+
+ return myModulesProvider.getModule(myName);
+ }
+
+ public ModifiableRootModel getModifiableRootModel() {
+ if (myModifiableRootModel == null) {
+ final Module module = getModule();
+ if (module != null) {
+ myModifiableRootModel = ((ModuleRootManagerImpl)ModuleRootManager.getInstance(module)).getModifiableModel(new UIRootConfigurationAccessor(myProject));
+ }
+ }
+ return myModifiableRootModel;
+ }
+
+ public OrderEntry[] getOrderEntries() {
+ if (myModifiableRootModel == null) { // do not clone all model if not necessary
+ return ModuleRootManager.getInstance(getModule()).getOrderEntries();
+ }
+ else {
+ return myModifiableRootModel.getOrderEntries();
+ }
+ }
+
+ public ModifiableRootModel getModifiableRootModelProxy() {
+ if (myModifiableRootModelProxy == null) {
+ final ModifiableRootModel rootModel = getModifiableRootModel();
+ if (rootModel != null) {
+ myModifiableRootModelProxy = (ModifiableRootModel)Proxy.newProxyInstance(
+ getClass().getClassLoader(), new Class[]{ModifiableRootModel.class}, new ModifiableRootModelInvocationHandler(rootModel)
+ );
+ }
+ }
+ return myModifiableRootModelProxy;
+ }
+
+ public ModuleRootModel getRootModel() {
+ if (myModifiableRootModel != null) {
+ return getModifiableRootModelProxy();
+ }
+ return ModuleRootManager.getInstance(myModule);
+ }
+
+ public boolean isModified() {
+ for (ModuleConfigurationEditor moduleElementsEditor : myEditors) {
+ if (moduleElementsEditor.isModified()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private void createEditors(Module module) {
+ ModuleConfigurationEditorProvider[] providers = collectProviders(module);
+ ModuleConfigurationState state = createModuleConfigurationState();
+ for (ModuleConfigurationEditorProvider provider : providers) {
+ ContainerUtil.addAll(myEditors, provider.createEditors(state));
+ }
+
+ for (final Configurable moduleConfigurable : myModule.getComponents(Configurable.class)) {
+ myEditors.add(new ModuleConfigurableWrapper(moduleConfigurable));
+ }
+ for(ModuleConfigurableEP extension : myModule.getExtensions(MODULE_CONFIGURABLES)) {
+ myEditors.add(new ModuleConfigurableWrapper(extension.createConfigurable()));
+ }
+ }
+
+ private static ModuleConfigurationEditorProvider[] collectProviders(final Module module) {
+ List<ModuleConfigurationEditorProvider> result = new ArrayList<ModuleConfigurationEditorProvider>();
+ ContainerUtil.addAll(result, module.getComponents(ModuleConfigurationEditorProvider.class));
+ ContainerUtil.addAll(result, Extensions.getExtensions(ModuleConfigurationEditorProvider.EP_NAME, module));
+ return result.toArray(new ModuleConfigurationEditorProvider[result.size()]);
+ }
+
+ public ModuleConfigurationState createModuleConfigurationState() {
+ return new ModuleConfigurationStateImpl(myProject, myModulesProvider) {
+ @Override
+ public ModifiableRootModel getRootModel() {
+ return getModifiableRootModelProxy();
+ }
+
+ @Override
+ public FacetsProvider getFacetsProvider() {
+ return getFacetsConfigurator();
+ }
+ };
+ }
+
+ private JPanel createPanel() {
+ getModifiableRootModel(); //initialize model if needed
+ getModifiableRootModelProxy();
+
+ myGenericSettingsPanel = new ModuleEditorPanel();
+
+ createEditors(getModule());
+
+ JPanel northPanel = new JPanel(new GridBagLayout());
+
+ myGenericSettingsPanel.add(northPanel, BorderLayout.NORTH);
+
+ final JComponent component = createCenterPanel();
+ myGenericSettingsPanel.add(component, BorderLayout.CENTER);
+ return myGenericSettingsPanel;
+ }
+
+ public JPanel getPanel() {
+ if (myGenericSettingsPanel == null) {
+ myGenericSettingsPanel = createPanel();
+ }
+
+ return myGenericSettingsPanel;
+ }
+
+ public void moduleCountChanged() {
+ updateOrderEntriesInEditors();
+ }
+
+ private void updateOrderEntriesInEditors() {
+ if (getModule() != null) { //module with attached module libraries was deleted
+ getPanel(); //init editor if needed
+ for (final ModuleConfigurationEditor myEditor : myEditors) {
+ myEditor.moduleStateChanged();
+ }
+ myEventDispatcher.getMulticaster().moduleStateChanged(getModifiableRootModelProxy());
+ }
+ }
+
+ public void updateCompilerOutputPathChanged(String baseUrl, String moduleName){
+ if (myGenericSettingsPanel == null) return; //wasn't initialized yet
+ for (final ModuleConfigurationEditor myEditor : myEditors) {
+ if (myEditor instanceof ModuleElementsEditor) {
+ ((ModuleElementsEditor)myEditor).moduleCompileOutputChanged(baseUrl, moduleName);
+ }
+ }
+ }
+
+ @Override
+ public void dispose() {
+ try {
+ for (final ModuleConfigurationEditor myEditor : myEditors) {
+ myEditor.disposeUIResources();
+ }
+
+ myEditors.clear();
+
+ disposeCenterPanel();
+
+ if (myModifiableRootModel != null) {
+ myModifiableRootModel.dispose();
+ }
+
+ myGenericSettingsPanel = null;
+ }
+ finally {
+ myModifiableRootModel = null;
+ myModifiableRootModelProxy = null;
+ }
+ }
+
+ public ModifiableRootModel apply() throws ConfigurationException {
+ try {
+ for (ModuleConfigurationEditor editor : myEditors) {
+ editor.saveData();
+ editor.apply();
+ }
+
+ return myModifiableRootModel;
+ }
+ finally {
+ myModifiableRootModel = null;
+ myModifiableRootModelProxy = null;
+ }
+ }
+
+ public void canApply() throws ConfigurationException {
+ for (ModuleConfigurationEditor editor : myEditors) {
+ if (editor instanceof ModuleElementsEditor) {
+ ((ModuleElementsEditor)editor).canApply();
+ }
+ }
+ }
+
+ public String getName() {
+ return myName;
+ }
+
+ private class ModifiableRootModelInvocationHandler implements InvocationHandler {
+ private final ModifiableRootModel myDelegateModel;
+ @NonNls private final Set<String> myCheckedNames = new HashSet<String>(
+ Arrays.asList("addOrderEntry", "addLibraryEntry", "addInvalidLibrary", "addModuleOrderEntry", "addInvalidModuleEntry",
+ "removeOrderEntry", "setSdk", "inheritSdk", "inheritCompilerOutputPath", "setExcludeOutput", "replaceEntryOfType"));
+
+ ModifiableRootModelInvocationHandler(ModifiableRootModel model) {
+ myDelegateModel = model;
+ }
+
+ @Override
+ public Object invoke(Object object, Method method, Object[] params) throws Throwable {
+ final boolean needUpdate = myCheckedNames.contains(method.getName());
+ try {
+ final Object result = method.invoke(myDelegateModel, unwrapParams(params));
+ if (result instanceof LibraryTable) {
+ return Proxy.newProxyInstance(getClass().getClassLoader(), new Class[]{LibraryTable.class},
+ new LibraryTableInvocationHandler((LibraryTable)result));
+ }
+ return result;
+ }
+ catch (InvocationTargetException e) {
+ throw e.getCause();
+ }
+ finally {
+ if (needUpdate) {
+ updateOrderEntriesInEditors();
+ }
+ }
+ }
+ }
+
+ private class LibraryTableInvocationHandler implements InvocationHandler, ProxyDelegateAccessor {
+ private final LibraryTable myDelegateTable;
+ @NonNls private final Set<String> myCheckedNames = new HashSet<String>(Arrays.asList("removeLibrary" /*,"createLibrary"*/));
+
+ LibraryTableInvocationHandler(LibraryTable table) {
+ myDelegateTable = table;
+ }
+
+ @Override
+ public Object invoke(Object object, Method method, Object[] params) throws Throwable {
+ final boolean needUpdate = myCheckedNames.contains(method.getName());
+ try {
+ final Object result = method.invoke(myDelegateTable, unwrapParams(params));
+ if (result instanceof Library) {
+ return Proxy.newProxyInstance(getClass().getClassLoader(), new Class[]{result instanceof LibraryEx ? LibraryEx.class : Library.class},
+ new LibraryInvocationHandler((Library)result));
+ }
+ else if (result instanceof LibraryTable.ModifiableModel) {
+ return Proxy.newProxyInstance(getClass().getClassLoader(), new Class[]{LibraryTableBase.ModifiableModelEx.class},
+ new LibraryTableModelInvocationHandler((LibraryTable.ModifiableModel)result));
+ }
+ if (result instanceof Library[]) {
+ Library[] libraries = (Library[])result;
+ for (int idx = 0; idx < libraries.length; idx++) {
+ Library library = libraries[idx];
+ libraries[idx] =
+ (Library)Proxy.newProxyInstance(getClass().getClassLoader(), new Class[]{library instanceof LibraryEx ? LibraryEx.class : Library.class},
+ new LibraryInvocationHandler(library));
+ }
+ }
+ return result;
+ }
+ catch (InvocationTargetException e) {
+ throw e.getCause();
+ }
+ finally {
+ if (needUpdate) {
+ updateOrderEntriesInEditors();
+ }
+ }
+ }
+
+ @Override
+ public Object getDelegate() {
+ return myDelegateTable;
+ }
+ }
+
+ private class LibraryInvocationHandler implements InvocationHandler, ProxyDelegateAccessor {
+ private final Library myDelegateLibrary;
+
+ LibraryInvocationHandler(Library delegateLibrary) {
+ myDelegateLibrary = delegateLibrary;
+ }
+
+ @Override
+ public Object invoke(Object object, Method method, Object[] params) throws Throwable {
+ try {
+ final Object result = method.invoke(myDelegateLibrary, unwrapParams(params));
+ if (result instanceof LibraryEx.ModifiableModelEx) {
+ return Proxy.newProxyInstance(getClass().getClassLoader(), new Class[]{LibraryEx.ModifiableModelEx.class},
+ new LibraryModifiableModelInvocationHandler((LibraryEx.ModifiableModelEx)result));
+ }
+ return result;
+ }
+ catch (InvocationTargetException e) {
+ throw e.getCause();
+ }
+ }
+
+ @Override
+ public Object getDelegate() {
+ return myDelegateLibrary;
+ }
+ }
+
+ private class LibraryModifiableModelInvocationHandler implements InvocationHandler, ProxyDelegateAccessor {
+ private final Library.ModifiableModel myDelegateModel;
+
+ LibraryModifiableModelInvocationHandler(Library.ModifiableModel delegateModel) {
+ myDelegateModel = delegateModel;
+ }
+
+ @Override
+ public Object invoke(Object object, Method method, Object[] params) throws Throwable {
+ final boolean needUpdate = METHOD_COMMIT.equals(method.getName());
+ try {
+ return method.invoke(myDelegateModel, unwrapParams(params));
+ }
+ catch (InvocationTargetException e) {
+ throw e.getCause();
+ }
+ finally {
+ if (needUpdate) {
+ updateOrderEntriesInEditors();
+ }
+ }
+ }
+
+ @Override
+ public Object getDelegate() {
+ return myDelegateModel;
+ }
+ }
+
+ private class LibraryTableModelInvocationHandler implements InvocationHandler, ProxyDelegateAccessor {
+ private final LibraryTable.ModifiableModel myDelegateModel;
+
+ LibraryTableModelInvocationHandler(LibraryTable.ModifiableModel delegateModel) {
+ myDelegateModel = delegateModel;
+ }
+
+ @Override
+ public Object invoke(Object object, Method method, Object[] params) throws Throwable {
+ final boolean needUpdate = METHOD_COMMIT.equals(method.getName());
+ try {
+ Object result = method.invoke(myDelegateModel, unwrapParams(params));
+ if (result instanceof Library[]) {
+ Library[] libraries = (Library[])result;
+ for (int idx = 0; idx < libraries.length; idx++) {
+ Library library = libraries[idx];
+ libraries[idx] =
+ (Library)Proxy.newProxyInstance(getClass().getClassLoader(), new Class[]{LibraryEx.class},
+ new LibraryInvocationHandler(library));
+ }
+ }
+ if (result instanceof Library) {
+ result =
+ Proxy.newProxyInstance(getClass().getClassLoader(), new Class[]{LibraryEx.class},
+ new LibraryInvocationHandler((Library)result));
+ }
+ return result;
+ }
+ catch (InvocationTargetException e) {
+ throw e.getCause();
+ }
+ finally {
+ if (needUpdate) {
+ updateOrderEntriesInEditors();
+ }
+ }
+ }
+
+ @Override
+ public Object getDelegate() {
+ return myDelegateModel;
+ }
+ }
+
+ public interface ProxyDelegateAccessor {
+ Object getDelegate();
+ }
+
+ private static Object[] unwrapParams(Object[] params) {
+ if (params == null || params.length == 0) {
+ return params;
+ }
+ final Object[] unwrappedParams = new Object[params.length];
+ for (int idx = 0; idx < params.length; idx++) {
+ Object param = params[idx];
+ if (param != null && Proxy.isProxyClass(param.getClass())) {
+ final InvocationHandler invocationHandler = Proxy.getInvocationHandler(param);
+ if (invocationHandler instanceof ProxyDelegateAccessor) {
+ param = ((ProxyDelegateAccessor)invocationHandler).getDelegate();
+ }
+ }
+ unwrappedParams[idx] = param;
+ }
+ return unwrappedParams;
+ }
+
+ @Nullable
+ public String getHelpTopic() {
+ if (myEditors.isEmpty()) {
+ return null;
+ }
+ final ModuleConfigurationEditor selectedEditor = getSelectedEditor();
+ return selectedEditor != null ? selectedEditor.getHelpTopic() : null;
+ }
+
+ public void setModuleName(final String name) {
+ myName = name;
+ }
+
+ private class ModuleEditorPanel extends JPanel implements DataProvider{
+ public ModuleEditorPanel() {
+ super(new BorderLayout());
+ }
+
+ @Override
+ public Object getData(String dataId) {
+ if (LangDataKeys.MODULE_CONTEXT.is(dataId)) {
+ return getModule();
+ }
+ return null;
+ }
+
+ }
+
+ @Override
+ public void setHistory(final History history) {
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/ModuleJdkConfigurable.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/ModuleJdkConfigurable.java
new file mode 100644
index 0000000..378931a
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/ModuleJdkConfigurable.java
@@ -0,0 +1,196 @@
+/*
+ * Copyright 2000-2011 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;
+
+import com.intellij.openapi.Disposable;
+import com.intellij.openapi.application.ApplicationBundle;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.project.ProjectBundle;
+import com.intellij.openapi.projectRoots.Sdk;
+import com.intellij.openapi.projectRoots.SdkModel;
+import com.intellij.openapi.roots.ModifiableRootModel;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.ModuleStructureConfigurable;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.ProjectSdksModel;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.StructureConfigurableContext;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.daemon.ModuleProjectStructureElement;
+import com.intellij.openapi.ui.DialogWrapper;
+import com.intellij.openapi.ui.Messages;
+import com.intellij.openapi.util.Computable;
+import com.intellij.openapi.util.Condition;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import java.awt.*;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+
+/**
+ * User: anna
+ * Date: 05-Jun-2006
+ */
+public abstract class ModuleJdkConfigurable implements Disposable {
+ private JdkComboBox myCbModuleJdk;
+ private Sdk mySelectedModuleJdk = null;
+ private JPanel myJdkPanel;
+ private ClasspathEditor myModuleEditor;
+ private final ProjectSdksModel myJdksModel;
+ private boolean myFreeze = false;
+ private final SdkModel.Listener myListener = new SdkModel.Listener() {
+ @Override
+ public void sdkAdded(Sdk sdk) {
+ reloadModel();
+ }
+
+ @Override
+ public void beforeSdkRemove(Sdk sdk) {
+ reloadModel();
+ }
+
+ @Override
+ public void sdkChanged(Sdk sdk, String previousName) {
+ reloadModel();
+ }
+
+ @Override
+ public void sdkHomeSelected(Sdk sdk, String newSdkHome) {
+ reloadModel();
+ }
+ };
+
+ public ModuleJdkConfigurable(ClasspathEditor moduleEditor, ProjectSdksModel jdksModel) {
+ myModuleEditor = moduleEditor;
+ myJdksModel = jdksModel;
+ myJdksModel.addListener(myListener);
+ init();
+ }
+
+ /**
+ * @return null if JDK should be inherited
+ */
+ @Nullable
+ public Sdk getSelectedModuleJdk() {
+ return myJdksModel.findSdk(mySelectedModuleJdk);
+ }
+
+ public boolean isInheritJdk() {
+ return myCbModuleJdk.getSelectedItem()instanceof JdkComboBox.ProjectJdkComboBoxItem;
+ }
+
+ public JComponent createComponent() {
+ return myJdkPanel;
+ }
+
+ private void reloadModel() {
+ myFreeze = true;
+ myCbModuleJdk.reloadModel(new JdkComboBox.ProjectJdkComboBoxItem(), getRootModel().getModule().getProject());
+ reset();
+ myFreeze = false;
+ }
+
+ protected abstract ModifiableRootModel getRootModel();
+
+ private void init() {
+ myJdkPanel = new JPanel(new GridBagLayout());
+ myCbModuleJdk = new JdkComboBox(myJdksModel);
+ myCbModuleJdk.insertItemAt(new JdkComboBox.ProjectJdkComboBoxItem(), 0);
+ myCbModuleJdk.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ if (myFreeze) return;
+
+ final Sdk newJdk = myCbModuleJdk.getSelectedJdk();
+ myModuleEditor.setSdk(newJdk);
+
+ clearCaches();
+ }
+ });
+ myJdkPanel.add(new JLabel(ProjectBundle.message("module.libraries.target.jdk.module.radio")),
+ new GridBagConstraints(0, 0, 1, 1, 0, 0, GridBagConstraints.NORTHWEST, GridBagConstraints.NONE, new Insets(12, 6, 12, 0), 0, 0));
+ myJdkPanel.add(myCbModuleJdk, new GridBagConstraints(1, 0, 1, 1, 0, 1.0,
+ GridBagConstraints.NORTHWEST, GridBagConstraints.NONE,
+ new Insets(6, 6, 12, 0), 0, 0));
+ final Project project = getRootModel().getModule().getProject();
+ final JButton setUpButton = new JButton(ApplicationBundle.message("button.new"));
+ myCbModuleJdk
+ .setSetupButton(setUpButton, project, myJdksModel, new JdkComboBox.ProjectJdkComboBoxItem(), new Condition<Sdk>() {
+ @Override
+ public boolean value(Sdk jdk) {
+ final Sdk projectJdk = myJdksModel.getProjectSdk();
+ if (projectJdk == null) {
+ final int res =
+ Messages.showYesNoDialog(myJdkPanel,
+ ProjectBundle.message("project.roots.no.jdk.on.project.message"),
+ ProjectBundle.message("project.roots.no.jdk.on.project.title"),
+ Messages.getInformationIcon());
+ if (res == DialogWrapper.OK_EXIT_CODE) {
+ myJdksModel.setProjectSdk(jdk);
+ return true;
+ }
+ }
+ return false;
+ }
+ }, true);
+ myJdkPanel.add(setUpButton, new GridBagConstraints(2, 0, 1, 1, 0, 0,
+ GridBagConstraints.WEST, GridBagConstraints.NONE,
+ new Insets(0, 4, 7, 0), 0, 0));
+ final JButton editButton = new JButton(ApplicationBundle.message("button.edit"));
+ myCbModuleJdk.setEditButton(editButton, getRootModel().getModule().getProject(), new Computable<Sdk>() {
+ @Override
+ @Nullable
+ public Sdk compute() {
+ return getRootModel().getSdk();
+ }
+ });
+ myJdkPanel.add(editButton,
+ new GridBagConstraints(GridBagConstraints.RELATIVE, 0, 1, 1, 1.0, 0, GridBagConstraints.WEST, GridBagConstraints.NONE,
+ new Insets(0, 4, 7, 0), 0, 0));
+ }
+
+ private void clearCaches() {
+ final Module module = getRootModel().getModule();
+ final Project project = module.getProject();
+ final StructureConfigurableContext context = ModuleStructureConfigurable.getInstance(project).getContext();
+ context.getDaemonAnalyzer().queueUpdate(new ModuleProjectStructureElement(context, module));
+ }
+
+ public void reset() {
+ myFreeze = true;
+ final String jdkName = getRootModel().getSdkName();
+ if (jdkName != null && !getRootModel().isSdkInherited()) {
+ mySelectedModuleJdk = myJdksModel.findSdk(jdkName);
+ if (mySelectedModuleJdk != null) {
+ myCbModuleJdk.setSelectedJdk(mySelectedModuleJdk);
+ } else {
+ myCbModuleJdk.setInvalidJdk(jdkName);
+ clearCaches();
+ }
+ }
+ else {
+ myCbModuleJdk.setSelectedJdk(null);
+ }
+ myFreeze = false;
+ }
+
+ @Override
+ public void dispose() {
+ myModuleEditor = null;
+ myCbModuleJdk = null;
+ myJdkPanel = null;
+ myJdksModel.removeListener(myListener);
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/ModulesConfigurator.form b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/ModulesConfigurator.form
new file mode 100644
index 0000000..292c1f6
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/ModulesConfigurator.form
@@ -0,0 +1,76 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.intellij.openapi.roots.ui.configuration.ProjectConfigurable">
+ <grid id="27dc6" binding="myWholePanel" layout-manager="GridLayoutManager" row-count="5" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="10">
+ <margin top="14" left="4" bottom="4" right="4"/>
+ <constraints>
+ <xy x="20" y="20" width="907" height="282"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <vspacer id="2fb9d">
+ <constraints>
+ <grid row="4" column="0" row-span="1" col-span="1" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/>
+ </constraints>
+ </vspacer>
+ <component id="e1ed8" class="javax.swing.JLabel">
+ <constraints>
+ <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties>
+ <text resource-bundle="messages/ProjectBundle" key="project.language.level"/>
+ </properties>
+ </component>
+ <grid id="be5" layout-manager="GridLayoutManager" row-count="1" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+ <margin top="0" left="4" bottom="10" right="0"/>
+ <constraints>
+ <grid row="1" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <component id="1f983" class="com.intellij.openapi.roots.ui.configuration.LanguageLevelCombo" binding="myLanguageLevelCombo" custom-create="true">
+ <constraints>
+ <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="7" anchor="8" fill="0" indent="0" use-parent-layout="false">
+ <minimum-size width="-1" height="26"/>
+ </grid>
+ </constraints>
+ <properties/>
+ </component>
+ </children>
+ </grid>
+ <component id="faaba" class="javax.swing.JLabel">
+ <constraints>
+ <grid row="2" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="1" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties>
+ <text resource-bundle="messages/ProjectBundle" key="project.compiler.output"/>
+ </properties>
+ </component>
+ <grid id="faeb8" layout-manager="GridLayoutManager" row-count="1" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+ <margin top="0" left="4" bottom="10" right="0"/>
+ <constraints>
+ <grid row="3" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <component id="60d6f" class="com.intellij.ui.FieldPanel" binding="myProjectCompilerOutput" custom-create="true">
+ <constraints>
+ <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="0" fill="1" indent="0" use-parent-layout="false">
+ <minimum-size width="215" height="-1"/>
+ </grid>
+ </constraints>
+ <properties/>
+ </component>
+ </children>
+ </grid>
+ </children>
+ </grid>
+ <buttonGroups>
+ <group name="buttonGroup1">
+ <member id="d8427"/>
+ <member id="db78e"/>
+ </group>
+ </buttonGroups>
+</form>
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
new file mode 100644
index 0000000..89c6617
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/ModulesConfigurator.java
@@ -0,0 +1,578 @@
+/*
+ * Copyright 2000-2012 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;
+
+import com.intellij.compiler.ModuleCompilerUtil;
+import com.intellij.facet.Facet;
+import com.intellij.facet.FacetModel;
+import com.intellij.facet.impl.ProjectFacetsConfigurator;
+import com.intellij.facet.impl.ui.FacetEditorImpl;
+import com.intellij.ide.actions.ImportModuleAction;
+import com.intellij.ide.util.newProjectWizard.AddModuleWizard;
+import com.intellij.ide.util.projectWizard.ModuleBuilder;
+import com.intellij.ide.util.projectWizard.ProjectBuilder;
+import com.intellij.openapi.Disposable;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.module.ModifiableModuleModel;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.module.ModuleManager;
+import com.intellij.openapi.options.ConfigurationException;
+import com.intellij.openapi.options.ShowSettingsUtil;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.project.ProjectBundle;
+import com.intellij.openapi.projectRoots.Sdk;
+import com.intellij.openapi.roots.ContentEntry;
+import com.intellij.openapi.roots.ModifiableRootModel;
+import com.intellij.openapi.roots.ModuleRootModel;
+import com.intellij.openapi.roots.impl.ModifiableModelCommitter;
+import com.intellij.openapi.roots.ui.configuration.actions.ModuleDeleteProvider;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.ModuleStructureConfigurable;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.ProjectSdksModel;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.StructureConfigurableContext;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.daemon.ModuleProjectStructureElement;
+import com.intellij.openapi.ui.Messages;
+import com.intellij.openapi.util.Comparing;
+import com.intellij.openapi.util.Computable;
+import com.intellij.openapi.util.Disposer;
+import com.intellij.openapi.vfs.VfsUtilCore;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.packaging.artifacts.Artifact;
+import com.intellij.packaging.artifacts.ModifiableArtifactModel;
+import com.intellij.projectImport.ProjectImportBuilder;
+import com.intellij.util.containers.HashMap;
+import com.intellij.util.graph.GraphGenerator;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.awt.*;
+import java.util.*;
+import java.util.List;
+
+/**
+ * @author Eugene Zhuravlev
+ * Date: Dec 15, 2003
+ */
+public class ModulesConfigurator implements ModulesProvider, ModuleEditor.ChangeListener {
+ private static final Logger LOG = Logger.getInstance("#" + ModulesConfigurator.class.getName());
+
+ private final Project myProject;
+ private final List<ModuleEditor> myModuleEditors = new ArrayList<ModuleEditor>();
+ private final Comparator<ModuleEditor> myModuleEditorComparator = new Comparator<ModuleEditor>() {
+ @Override
+ public int compare(ModuleEditor editor1, ModuleEditor editor2) {
+ return ModulesAlphaComparator.INSTANCE.compare(editor1.getModule(), editor2.getModule());
+ }
+
+ @SuppressWarnings({"EqualsWhichDoesntCheckParameterClass"})
+ public boolean equals(Object o) {
+ return false;
+ }
+ };
+ private boolean myModified = false;
+ private ModifiableModuleModel myModuleModel;
+ private boolean myModuleModelCommitted = false;
+ private ProjectFacetsConfigurator myFacetsConfigurator;
+
+ private StructureConfigurableContext myContext;
+ private final List<ModuleEditor.ChangeListener> myAllModulesChangeListeners = new ArrayList<ModuleEditor.ChangeListener>();
+
+ public ModulesConfigurator(Project project) {
+ myProject = project;
+ myModuleModel = ModuleManager.getInstance(myProject).getModifiableModel();
+ }
+
+ public void setContext(final StructureConfigurableContext context) {
+ myContext = context;
+ myFacetsConfigurator = createFacetsConfigurator();
+ }
+
+ public ProjectFacetsConfigurator getFacetsConfigurator() {
+ return myFacetsConfigurator;
+ }
+
+ public void disposeUIResources() {
+ ApplicationManager.getApplication().runWriteAction(new Runnable() {
+ @Override
+ public void run() {
+ for (final ModuleEditor moduleEditor : myModuleEditors) {
+ Disposer.dispose(moduleEditor);
+ }
+ myModuleEditors.clear();
+
+ myModuleModel.dispose();
+
+ myFacetsConfigurator.disposeEditors();
+ }
+ });
+
+ }
+
+ @Override
+ @NotNull
+ public Module[] getModules() {
+ return myModuleModel.getModules();
+ }
+
+ @Override
+ @Nullable
+ public Module getModule(String name) {
+ final Module moduleByName = myModuleModel.findModuleByName(name);
+ if (moduleByName != null) {
+ return moduleByName;
+ }
+ return myModuleModel.getModuleToBeRenamed(name); //if module was renamed
+ }
+
+ @Nullable
+ public ModuleEditor getModuleEditor(Module module) {
+ for (final ModuleEditor moduleEditor : myModuleEditors) {
+ if (module.equals(moduleEditor.getModule())) {
+ return moduleEditor;
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public ModuleRootModel getRootModel(@NotNull Module module) {
+ return getOrCreateModuleEditor(module).getRootModel();
+ }
+
+ public ModuleEditor getOrCreateModuleEditor(Module module) {
+ LOG.assertTrue(getModule(module.getName()) != null, "Module has been deleted");
+ ModuleEditor editor = getModuleEditor(module);
+ if (editor == null) {
+ editor = doCreateModuleEditor(module);
+ }
+ return editor;
+ }
+
+ private ModuleEditor doCreateModuleEditor(final Module module) {
+ final ModuleEditor moduleEditor = new HeaderHidingTabbedModuleEditor(myProject, this, module) {
+ @Override
+ public ProjectFacetsConfigurator getFacetsConfigurator() {
+ return myFacetsConfigurator;
+ }
+ };
+
+ myModuleEditors.add(moduleEditor);
+
+ moduleEditor.addChangeListener(this);
+ Disposer.register(moduleEditor, new Disposable() {
+ @Override
+ public void dispose() {
+ moduleEditor.removeChangeListener(ModulesConfigurator.this);
+ }
+ });
+ return moduleEditor;
+ }
+
+ @Override
+ public FacetModel getFacetModel(@NotNull Module module) {
+ return myFacetsConfigurator.getOrCreateModifiableModel(module);
+ }
+
+ public void resetModuleEditors() {
+ myModuleModel = ModuleManager.getInstance(myProject).getModifiableModel();
+
+ ApplicationManager.getApplication().runWriteAction(new Runnable() {
+ @Override
+ public void run() {
+ if (!myModuleEditors.isEmpty()) {
+ LOG.error("module editors was not disposed");
+ myModuleEditors.clear();
+ }
+ final Module[] modules = myModuleModel.getModules();
+ if (modules.length > 0) {
+ for (Module module : modules) {
+ getOrCreateModuleEditor(module);
+ }
+ Collections.sort(myModuleEditors, myModuleEditorComparator);
+ }
+ }
+ });
+ myFacetsConfigurator.resetEditors();
+ myModified = false;
+ }
+
+ @Override
+ public void moduleStateChanged(final ModifiableRootModel moduleRootModel) {
+ for (ModuleEditor.ChangeListener listener : myAllModulesChangeListeners) {
+ listener.moduleStateChanged(moduleRootModel);
+ }
+ myContext.getDaemonAnalyzer().queueUpdate(new ModuleProjectStructureElement(myContext, moduleRootModel.getModule()));
+ }
+
+ public void addAllModuleChangeListener(ModuleEditor.ChangeListener listener) {
+ myAllModulesChangeListeners.add(listener);
+ }
+
+ public GraphGenerator<ModuleRootModel> createGraphGenerator() {
+ final Map<Module, ModuleRootModel> models = new HashMap<Module, ModuleRootModel>();
+ for (ModuleEditor moduleEditor : myModuleEditors) {
+ models.put(moduleEditor.getModule(), moduleEditor.getRootModel());
+ }
+ return ModuleCompilerUtil.createGraphGenerator(models);
+ }
+
+ public void apply() throws ConfigurationException {
+ // validate content and source roots
+ final Map<VirtualFile, String> contentRootToModuleNameMap = new HashMap<VirtualFile, String>();
+ final Map<VirtualFile, VirtualFile> srcRootsToContentRootMap = new HashMap<VirtualFile, VirtualFile>();
+ for (final ModuleEditor moduleEditor : myModuleEditors) {
+ final ModifiableRootModel rootModel = moduleEditor.getModifiableRootModel();
+ final ContentEntry[] contents = rootModel.getContentEntries();
+ for (ContentEntry contentEntry : contents) {
+ final VirtualFile contentRoot = contentEntry.getFile();
+ if (contentRoot == null) {
+ continue;
+ }
+ final String moduleName = moduleEditor.getName();
+ final String previousName = contentRootToModuleNameMap.put(contentRoot, moduleName);
+ if (previousName != null && !previousName.equals(moduleName)) {
+ throw new ConfigurationException(
+ ProjectBundle.message("module.paths.validation.duplicate.content.error", contentRoot.getPresentableUrl(), previousName, moduleName)
+ );
+ }
+ for (VirtualFile srcRoot : contentEntry.getSourceFolderFiles()) {
+ final VirtualFile anotherContentRoot = srcRootsToContentRootMap.put(srcRoot, contentRoot);
+ if (anotherContentRoot != null) {
+ final String problematicModule;
+ final String correctModule;
+ if (VfsUtilCore.isAncestor(anotherContentRoot, contentRoot, true)) {
+ problematicModule = contentRootToModuleNameMap.get(anotherContentRoot);
+ correctModule = contentRootToModuleNameMap.get(contentRoot);
+ }
+ else {
+ problematicModule = contentRootToModuleNameMap.get(contentRoot);
+ correctModule = contentRootToModuleNameMap.get(anotherContentRoot);
+ }
+ throw new ConfigurationException(
+ ProjectBundle.message("module.paths.validation.duplicate.source.root.error", problematicModule, srcRoot.getPresentableUrl(), correctModule)
+ );
+ }
+ }
+ }
+ }
+ // additional validation: directories marked as src roots must belong to the same module as their corresponding content root
+ for (Map.Entry<VirtualFile, VirtualFile> entry : srcRootsToContentRootMap.entrySet()) {
+ final VirtualFile srcRoot = entry.getKey();
+ final VirtualFile correspondingContent = entry.getValue();
+ final String expectedModuleName = contentRootToModuleNameMap.get(correspondingContent);
+
+ for (VirtualFile candidateContent = srcRoot; candidateContent != null && !candidateContent.equals(correspondingContent); candidateContent = candidateContent.getParent()) {
+ final String moduleName = contentRootToModuleNameMap.get(candidateContent);
+ if (moduleName != null && !moduleName.equals(expectedModuleName)) {
+ throw new ConfigurationException(
+ ProjectBundle.message("module.paths.validation.source.root.belongs.to.another.module.error", srcRoot.getPresentableUrl(), expectedModuleName, moduleName)
+ );
+ }
+ }
+ }
+
+ final List<ModifiableRootModel> models = new ArrayList<ModifiableRootModel>(myModuleEditors.size());
+ for (ModuleEditor moduleEditor : myModuleEditors) {
+ moduleEditor.canApply();
+ }
+
+ final Map<Sdk, Sdk> modifiedToOriginalMap = new HashMap<Sdk, Sdk>();
+ final ProjectSdksModel projectJdksModel = ProjectStructureConfigurable.getInstance(myProject).getProjectJdksModel();
+ for (Map.Entry<Sdk, Sdk> entry : projectJdksModel.getProjectSdks().entrySet()) {
+ modifiedToOriginalMap.put(entry.getValue(), entry.getKey());
+ }
+
+ for (final ModuleEditor moduleEditor : myModuleEditors) {
+ final ModifiableRootModel model = moduleEditor.apply();
+ if (model != null) {
+ if (!model.isSdkInherited()) {
+ // make sure the sdk is set to original SDK stored in the JDK Table
+ final Sdk modelSdk = model.getSdk();
+ if (modelSdk != null) {
+ final Sdk original = modifiedToOriginalMap.get(modelSdk);
+ if (original != null) {
+ model.setSdk(original);
+ }
+ }
+ }
+ models.add(model);
+ }
+ }
+ myFacetsConfigurator.applyEditors();
+
+ ApplicationManager.getApplication().runWriteAction(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ final ModifiableRootModel[] rootModels = models.toArray(new ModifiableRootModel[models.size()]);
+ ModifiableModelCommitter.multiCommit(rootModels, myModuleModel);
+ myModuleModelCommitted = true;
+ myFacetsConfigurator.commitFacets();
+
+ }
+ finally {
+ ModuleStructureConfigurable.getInstance(myProject).getFacetEditorFacade().clearMaps(false);
+
+ myFacetsConfigurator = createFacetsConfigurator();
+ myModuleModel = ModuleManager.getInstance(myProject).getModifiableModel();
+ myModuleModelCommitted = false;
+ }
+ }
+ });
+
+ myModified = false;
+ }
+
+ private ProjectFacetsConfigurator createFacetsConfigurator() {
+ return new ProjectFacetsConfigurator(myContext, myFacetsConfigurator);
+ }
+
+ public void setModified(final boolean modified) {
+ myModified = modified;
+ }
+
+ public ModifiableModuleModel getModuleModel() {
+ return myModuleModel;
+ }
+
+ public boolean isModuleModelCommitted() {
+ return myModuleModelCommitted;
+ }
+
+ public boolean deleteModule(final Module module) {
+ ModuleEditor moduleEditor = getModuleEditor(module);
+ if (moduleEditor == null) return true;
+ return doRemoveModule(moduleEditor);
+ }
+
+
+ @Nullable
+ public List<Module> addModule(Component parent, boolean anImport) {
+ if (myProject.isDefault()) return null;
+ final ProjectBuilder builder = runModuleWizard(parent, anImport);
+ if (builder != null ) {
+ final List<Module> modules = new ArrayList<Module>();
+ final List<Module> commitedModules;
+ if (builder instanceof ProjectImportBuilder<?>) {
+ final ModifiableArtifactModel artifactModel =
+ ProjectStructureConfigurable.getInstance(myProject).getArtifactsStructureConfigurable().getModifiableArtifactModel();
+ commitedModules = ((ProjectImportBuilder<?>)builder).commit(myProject, myModuleModel, this, artifactModel);
+ }
+ else {
+ commitedModules = builder.commit(myProject, myModuleModel, this);
+ }
+ if (commitedModules != null) {
+ modules.addAll(commitedModules);
+ }
+ ApplicationManager.getApplication().runWriteAction(new Runnable() {
+ @Override
+ public void run() {
+ for (Module module : modules) {
+ getOrCreateModuleEditor(module);
+ }
+ }
+ });
+ return modules;
+ }
+ return null;
+ }
+
+ private Module createModule(final ModuleBuilder builder) {
+ final Exception[] ex = new Exception[]{null};
+ final Module module = ApplicationManager.getApplication().runWriteAction(new Computable<Module>() {
+ @Override
+ @SuppressWarnings({"ConstantConditions"})
+ public Module compute() {
+ try {
+ return builder.createModule(myModuleModel);
+ }
+ catch (Exception e) {
+ ex[0] = e;
+ return null;
+ }
+ }
+ });
+ if (ex[0] != null) {
+ Messages.showErrorDialog(ProjectBundle.message("module.add.error.message", ex[0].getMessage()),
+ ProjectBundle.message("module.add.error.title"));
+ }
+ return module;
+ }
+
+ @Nullable
+ public Module addModule(final ModuleBuilder moduleBuilder) {
+ final Module module = createModule(moduleBuilder);
+ if (module != null) {
+ ApplicationManager.getApplication().runWriteAction(new Runnable() {
+ @Override
+ public void run() {
+ getOrCreateModuleEditor(module);
+ Collections.sort(myModuleEditors, myModuleEditorComparator);
+ }
+ });
+ processModuleCountChanged();
+ }
+ return module;
+ }
+
+ @Nullable
+ ProjectBuilder runModuleWizard(Component dialogParent, boolean anImport) {
+ AddModuleWizard wizard;
+ if (anImport) {
+ wizard = ImportModuleAction.selectFileAndCreateWizard(myProject, dialogParent);
+ if (wizard == null) return null;
+ if (wizard.getStepCount() == 0) return wizard.getProjectBuilder();
+ }
+ else {
+ wizard = new AddModuleWizard(dialogParent, myProject, this);
+ }
+ wizard.show();
+ if (wizard.isOK()) {
+ final ProjectBuilder builder = wizard.getProjectBuilder();
+ if (builder instanceof ModuleBuilder) {
+ final ModuleBuilder moduleBuilder = (ModuleBuilder)builder;
+ if (moduleBuilder.getName() == null) {
+ moduleBuilder.setName(wizard.getProjectName());
+ }
+ if (moduleBuilder.getModuleFilePath() == null) {
+ moduleBuilder.setModuleFilePath(wizard.getModuleFilePath());
+ }
+ }
+ if (!builder.validate(myProject, myProject)) {
+ return null;
+ }
+ return wizard.getProjectBuilder();
+ }
+
+ return null;
+ }
+
+
+ private boolean doRemoveModule(@NotNull ModuleEditor selectedEditor) {
+
+ String question;
+ if (myModuleEditors.size() == 1) {
+ question = ProjectBundle.message("module.remove.last.confirmation");
+ }
+ else {
+ question = ProjectBundle.message("module.remove.confirmation", selectedEditor.getModule().getName());
+ }
+ int result =
+ Messages.showYesNoDialog(myProject, question, ProjectBundle.message("module.remove.confirmation.title"), Messages.getQuestionIcon());
+ if (result != 0) {
+ return false;
+ }
+ // do remove
+ myModuleEditors.remove(selectedEditor);
+
+ // destroyProcess removed module
+ final Module moduleToRemove = selectedEditor.getModule();
+ // remove all dependencies on the module that is about to be removed
+ List<ModifiableRootModel> modifiableRootModels = new ArrayList<ModifiableRootModel>();
+ for (final ModuleEditor moduleEditor : myModuleEditors) {
+ final ModifiableRootModel modifiableRootModel = moduleEditor.getModifiableRootModelProxy();
+ modifiableRootModels.add(modifiableRootModel);
+ }
+
+ // destroyProcess editor
+ ModuleDeleteProvider.removeModule(moduleToRemove, null, modifiableRootModels, myModuleModel);
+ processModuleCountChanged();
+ Disposer.dispose(selectedEditor);
+
+ return true;
+ }
+
+
+ private void processModuleCountChanged() {
+ for (ModuleEditor moduleEditor : myModuleEditors) {
+ moduleEditor.moduleCountChanged();
+ }
+ }
+
+ public void processModuleCompilerOutputChanged(String baseUrl) {
+ for (ModuleEditor moduleEditor : myModuleEditors) {
+ moduleEditor.updateCompilerOutputPathChanged(baseUrl, moduleEditor.getName());
+ }
+ }
+
+ public boolean isModified() {
+ if (myModuleModel.isChanged()) {
+ return true;
+ }
+ for (ModuleEditor moduleEditor : myModuleEditors) {
+ if (moduleEditor.isModified()) {
+ return true;
+ }
+ }
+ return myModified || myFacetsConfigurator.isModified();
+ }
+
+ public static boolean showArtifactSettings(@NotNull Project project, @Nullable final Artifact artifact) {
+ final ProjectStructureConfigurable configurable = ProjectStructureConfigurable.getInstance(project);
+ return ShowSettingsUtil.getInstance().editConfigurable(project, configurable, new Runnable() {
+ @Override
+ public void run() {
+ configurable.select(artifact, true);
+ }
+ });
+ }
+
+ public static boolean showFacetSettingsDialog(@NotNull final Facet facet,
+ @Nullable final String tabNameToSelect) {
+ final Project project = facet.getModule().getProject();
+ final ProjectStructureConfigurable config = ProjectStructureConfigurable.getInstance(project);
+ return ShowSettingsUtil.getInstance().editConfigurable(project, config, new Runnable() {
+ @Override
+ public void run() {
+ final ModuleStructureConfigurable modulesConfig = config.getModulesConfig();
+ config.select(facet, true).doWhenDone(new Runnable() {
+ @Override
+ public void run() {
+ if (tabNameToSelect != null) {
+ FacetEditorImpl facetEditor = modulesConfig.getFacetConfigurator().getOrCreateEditor(facet);
+ facetEditor.setSelectedTabName(tabNameToSelect);
+ }
+ }
+ });
+ }
+ });
+ }
+
+ public static boolean showDialog(Project project, @Nullable final String moduleToSelect, @Nullable final String editorNameToSelect) {
+ final ProjectStructureConfigurable config = ProjectStructureConfigurable.getInstance(project);
+ return ShowSettingsUtil.getInstance().editConfigurable(project, config, new Runnable() {
+ @Override
+ public void run() {
+ config.select(moduleToSelect, editorNameToSelect, true);
+ }
+ });
+ }
+
+ public void moduleRenamed(Module module, final String oldName, final String name) {
+ for (ModuleEditor moduleEditor : myModuleEditors) {
+ if (module == moduleEditor.getModule() && Comparing.strEqual(moduleEditor.getName(), oldName)) {
+ moduleEditor.setModuleName(name);
+ moduleEditor.updateCompilerOutputPathChanged(ProjectStructureConfigurable.getInstance(myProject).getProjectConfig().getCompilerOutputUrl(), name);
+ myContext.getDaemonAnalyzer().queueUpdate(new ModuleProjectStructureElement(myContext, module));
+ return;
+ }
+ }
+ }
+
+ public StructureConfigurableContext getContext() {
+ return myContext;
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/OutputEditor.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/OutputEditor.java
new file mode 100644
index 0000000..fe3ce1a
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/OutputEditor.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2000-2009 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.
+ */
+
+/*
+ * Created by IntelliJ IDEA.
+ * User: Anna.Kozlova
+ * Date: 12-Aug-2006
+ * Time: 20:14:02
+ */
+package com.intellij.openapi.roots.ui.configuration;
+
+import com.intellij.openapi.project.ProjectBundle;
+import com.intellij.ui.IdeBorderFactory;
+import com.intellij.util.ui.UIUtil;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import javax.swing.border.EmptyBorder;
+import java.awt.*;
+
+public class OutputEditor extends ModuleElementsEditor {
+ private final BuildElementsEditor myCompilerOutputEditor;
+ private final JavadocEditor myJavadocEditor;
+ private final AnnotationsEditor myAnnotationsEditor;
+
+ protected OutputEditor(final ModuleConfigurationState state) {
+ super(state);
+ myCompilerOutputEditor = new BuildElementsEditor(state);
+ myJavadocEditor = new JavadocEditor(state);
+ myAnnotationsEditor = new AnnotationsEditor(state);
+ }
+
+ @Override
+ protected JComponent createComponentImpl() {
+ final JPanel panel = new JPanel(new GridBagLayout());
+ panel.setBorder(new EmptyBorder(UIUtil.PANEL_SMALL_INSETS));
+ final GridBagConstraints gc =
+ new GridBagConstraints(0, GridBagConstraints.RELATIVE, 1, 1, 1, 0, GridBagConstraints.NORTHWEST, GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0);
+ panel.add(myCompilerOutputEditor.createComponentImpl(), gc);
+ final JPanel javadocPanel = (JPanel)myJavadocEditor.createComponentImpl();
+ javadocPanel.setBorder(IdeBorderFactory.createTitledBorder(myJavadocEditor.getDisplayName(), false));
+ gc.weighty = 1;
+ panel.add(javadocPanel, gc);
+ final JPanel annotationsPanel = (JPanel)myAnnotationsEditor.createComponentImpl();
+ annotationsPanel.setBorder(IdeBorderFactory.createTitledBorder(myAnnotationsEditor.getDisplayName(), false));
+ panel.add(annotationsPanel, gc);
+ return panel;
+ }
+
+ @Override
+ public void saveData() {
+ myCompilerOutputEditor.saveData();
+ myJavadocEditor.saveData();
+ myAnnotationsEditor.saveData();
+ }
+
+ @Override
+ public String getDisplayName() {
+ return ProjectBundle.message("project.roots.path.tab.title");
+ }
+
+
+ @Override
+ public void moduleStateChanged() {
+ super.moduleStateChanged();
+ myCompilerOutputEditor.moduleStateChanged();
+ myJavadocEditor.moduleStateChanged();
+ myAnnotationsEditor.moduleStateChanged();
+ }
+
+
+ @Override
+ public void moduleCompileOutputChanged(final String baseUrl, final String moduleName) {
+ super.moduleCompileOutputChanged(baseUrl, moduleName);
+ myCompilerOutputEditor.moduleCompileOutputChanged(baseUrl, moduleName);
+ myJavadocEditor.moduleCompileOutputChanged(baseUrl, moduleName);
+ myAnnotationsEditor.moduleCompileOutputChanged(baseUrl, moduleName);
+ }
+
+ @Override
+ @Nullable
+ @NonNls
+ public String getHelpTopic() {
+ return "projectStructure.modules.paths";
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/PathUIUtils.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/PathUIUtils.java
new file mode 100644
index 0000000..435640d
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/PathUIUtils.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2000-2009 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;
+
+import com.intellij.openapi.progress.ProgressIndicator;
+import com.intellij.openapi.roots.OrderRootType;
+import com.intellij.openapi.roots.libraries.ui.OrderRoot;
+import com.intellij.openapi.roots.libraries.ui.RootDetector;
+import com.intellij.openapi.roots.libraries.ui.impl.LibraryRootsDetectorImpl;
+import com.intellij.openapi.roots.libraries.ui.impl.RootDetectionUtil;
+import com.intellij.openapi.vfs.VfsUtil;
+import com.intellij.openapi.vfs.VirtualFile;
+import org.jetbrains.annotations.NotNull;
+
+import java.awt.*;
+import java.util.*;
+import java.util.List;
+
+/**
+ * This utility class contains utility methods for selecting paths.
+ *
+ * @author Constantine.Plotnikov
+ */
+public class PathUIUtils {
+ public static final RootDetector JAVA_SOURCE_ROOT_DETECTOR = new RootDetector(OrderRootType.SOURCES, false, "sources") {
+ @NotNull
+ @Override
+ public Collection<VirtualFile> detectRoots(@NotNull VirtualFile rootCandidate,
+ @NotNull ProgressIndicator progressIndicator) {
+ return JavaVfsSourceRootDetectionUtil.suggestRoots(rootCandidate, progressIndicator);
+ }
+ };
+
+ private PathUIUtils() {
+ }
+
+ /**
+ * This method takes a candidates for the project root, then scans the candidates and
+ * if multiple candidates or non root source directories are found whithin some
+ * directories, it shows a dialog that allows selecting or deselecting them.
+ * @param parent a parent parent or project
+ * @param rootCandidates a candidates for roots
+ * @return a array of source folders or empty array if non was selected or dialog was canceled.
+ */
+ public static VirtualFile[] scanAndSelectDetectedJavaSourceRoots(Component parentComponent, final VirtualFile[] rootCandidates) {
+ final List<OrderRoot> orderRoots = RootDetectionUtil.detectRoots(Arrays.asList(rootCandidates), parentComponent, null,
+ new LibraryRootsDetectorImpl(Collections.singletonList(JAVA_SOURCE_ROOT_DETECTOR)),
+ new OrderRootType[0]);
+ final List<VirtualFile> result = new ArrayList<VirtualFile>();
+ for (OrderRoot root : orderRoots) {
+ result.add(root.getFile());
+ }
+ return VfsUtil.toVirtualFileArray(result);
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/ProjectConfigurable.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/ProjectConfigurable.java
new file mode 100644
index 0000000..9c46d56
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/ProjectConfigurable.java
@@ -0,0 +1,307 @@
+/*
+ * Copyright 2000-2012 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;
+
+import com.intellij.icons.AllIcons;
+import com.intellij.ide.util.BrowseFilesListener;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.components.StorageScheme;
+import com.intellij.openapi.fileChooser.FileChooserDescriptor;
+import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory;
+import com.intellij.openapi.fileChooser.FileChooserFactory;
+import com.intellij.openapi.options.ConfigurationException;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.project.ProjectBundle;
+import com.intellij.openapi.project.ex.ProjectEx;
+import com.intellij.openapi.roots.CompilerProjectExtension;
+import com.intellij.openapi.roots.LanguageLevelProjectExtension;
+import com.intellij.openapi.roots.ModifiableRootModel;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.ProjectSdksModel;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.ProjectStructureElementConfigurable;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.StructureConfigurableContext;
+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.DetailsComponent;
+import com.intellij.openapi.util.Comparing;
+import com.intellij.openapi.util.EmptyRunnable;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.openapi.vfs.VfsUtil;
+import com.intellij.pom.java.LanguageLevel;
+import com.intellij.ui.DocumentAdapter;
+import com.intellij.ui.FieldPanel;
+import com.intellij.ui.InsertPathAction;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import javax.swing.event.DocumentEvent;
+import java.awt.*;
+import java.io.IOException;
+
+/**
+ * @author Eugene Zhuravlev
+ * Date: Dec 15, 2003
+ */
+public class ProjectConfigurable extends ProjectStructureElementConfigurable<Project> implements DetailsComponent.Facade {
+
+ private final Project myProject;
+
+ private LanguageLevelCombo myLanguageLevelCombo;
+ private ProjectJdkConfigurable myProjectJdkConfigurable;
+
+ private FieldPanel myProjectCompilerOutput;
+
+ private JTextField myProjectName;
+
+ private JPanel myPanel;
+
+ private final StructureConfigurableContext myContext;
+ private final ModulesConfigurator myModulesConfigurator;
+ private JPanel myWholePanel;
+
+ private boolean myFreeze = false;
+ private DetailsComponent myDetailsComponent;
+ private final GeneralProjectSettingsElement mySettingsElement;
+
+ public ProjectConfigurable(Project project,
+ final StructureConfigurableContext context,
+ ModulesConfigurator configurator,
+ ProjectSdksModel model) {
+ myProject = project;
+ myContext = context;
+ myModulesConfigurator = configurator;
+ mySettingsElement = new GeneralProjectSettingsElement(context);
+ final ProjectStructureDaemonAnalyzer daemonAnalyzer = context.getDaemonAnalyzer();
+ myModulesConfigurator.addAllModuleChangeListener(new ModuleEditor.ChangeListener() {
+ @Override
+ public void moduleStateChanged(ModifiableRootModel moduleRootModel) {
+ daemonAnalyzer.queueUpdate(mySettingsElement);
+ }
+ });
+ init(model);
+ }
+
+ @Override
+ public ProjectStructureElement getProjectStructureElement() {
+ return mySettingsElement;
+ }
+
+ @Override
+ public DetailsComponent getDetailsComponent() {
+ return myDetailsComponent;
+ }
+
+ @Override
+ public JComponent createOptionsPanel() {
+ myDetailsComponent = new DetailsComponent();
+ myDetailsComponent.setContent(myPanel);
+ myDetailsComponent.setText(getBannerSlogan());
+
+ myProjectJdkConfigurable.createComponent(); //reload changed jdks
+
+ return myDetailsComponent.getComponent();
+ }
+
+ private void init(final ProjectSdksModel model) {
+ myPanel = new JPanel(new GridBagLayout());
+ myPanel.setPreferredSize(new Dimension(700, 500));
+
+ if (((ProjectEx)myProject).getStateStore().getStorageScheme().equals(StorageScheme.DIRECTORY_BASED)) {
+ final JPanel namePanel = new JPanel(new BorderLayout());
+ final JLabel label =
+ new JLabel("<html><body><b>Project name:</b></body></html>", SwingConstants.LEFT);
+ namePanel.add(label, BorderLayout.NORTH);
+
+ myProjectName = new JTextField();
+ myProjectName.setColumns(40);
+
+ final JPanel nameFieldPanel = new JPanel();
+ nameFieldPanel.setLayout(new BoxLayout(nameFieldPanel, BoxLayout.X_AXIS));
+ nameFieldPanel.add(Box.createHorizontalStrut(4));
+ nameFieldPanel.add(myProjectName);
+
+ namePanel.add(nameFieldPanel, BorderLayout.CENTER);
+ final JPanel wrapper = new JPanel(new FlowLayout(FlowLayout.LEFT));
+ wrapper.add(namePanel);
+ wrapper.setAlignmentX(0);
+ myPanel.add(wrapper, new GridBagConstraints(0, GridBagConstraints.RELATIVE, 1, 1, 0.0, 0.0,
+ GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL,
+ new Insets(4, 0, 10, 0), 0, 0));
+ }
+
+ myProjectJdkConfigurable = new ProjectJdkConfigurable(myProject, model);
+ myPanel.add(myProjectJdkConfigurable.createComponent(), new GridBagConstraints(0, GridBagConstraints.RELATIVE, 1, 1, 0.0, 0.0,
+ GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL,
+ new Insets(4, 4, 0, 0), 0, 0));
+
+ myPanel.add(myWholePanel, new GridBagConstraints(0, GridBagConstraints.RELATIVE, 1, 1, 1.0, 1.0, GridBagConstraints.NORTHWEST,
+ GridBagConstraints.NONE, new Insets(4, 0, 0, 0), 0, 0));
+
+
+ myProjectCompilerOutput.getTextField().getDocument().addDocumentListener(new DocumentAdapter() {
+ @Override
+ protected void textChanged(DocumentEvent e) {
+ if (myFreeze) return;
+ myModulesConfigurator.processModuleCompilerOutputChanged(getCompilerOutputUrl());
+ }
+ });
+ }
+
+ @Override
+ public void disposeUIResources() {
+ if (myProjectJdkConfigurable != null) {
+ myProjectJdkConfigurable.disposeUIResources();
+ }
+ }
+
+ @Override
+ public void reset() {
+ myFreeze = true;
+ try {
+ myProjectJdkConfigurable.reset();
+ final String compilerOutput = getOriginalCompilerOutputUrl();
+ if (compilerOutput != null) {
+ myProjectCompilerOutput.setText(FileUtil.toSystemDependentName(VfsUtil.urlToPath(compilerOutput)));
+ }
+ myLanguageLevelCombo.reset(myProject);
+
+ if (myProjectName != null) {
+ myProjectName.setText(myProject.getName());
+ }
+ }
+ finally {
+ myFreeze = false;
+ }
+
+ myContext.getDaemonAnalyzer().queueUpdate(mySettingsElement);
+ }
+
+
+ @Override
+ public void apply() throws ConfigurationException {
+ final CompilerProjectExtension compilerProjectExtension = CompilerProjectExtension.getInstance(myProject);
+ assert compilerProjectExtension != null : myProject;
+
+ if (myProjectName != null && StringUtil.isEmptyOrSpaces(myProjectName.getText())) {
+ throw new ConfigurationException("Please, specify project name!");
+ }
+
+ ApplicationManager.getApplication().runWriteAction(new Runnable() {
+ @Override
+ public void run() {
+ // set the output path first so that handlers of RootsChanged event sent after JDK is set
+ // would see the updated path
+ String canonicalPath = myProjectCompilerOutput.getText();
+ if (canonicalPath != null && canonicalPath.length() > 0) {
+ try {
+ canonicalPath = FileUtil.resolveShortWindowsName(canonicalPath);
+ }
+ catch (IOException e) {
+ //file doesn't exist yet
+ }
+ canonicalPath = FileUtil.toSystemIndependentName(canonicalPath);
+ compilerProjectExtension.setCompilerOutputUrl(VfsUtil.pathToUrl(canonicalPath));
+ }
+ else {
+ compilerProjectExtension.setCompilerOutputPointer(null);
+ }
+
+ final LanguageLevel newLevel = (LanguageLevel)myLanguageLevelCombo.getSelectedItem();
+ LanguageLevelProjectExtension.getInstance(myProject).setLanguageLevel(newLevel);
+ myProjectJdkConfigurable.apply();
+
+ if (myProjectName != null) {
+ ((ProjectEx)myProject).setProjectName(myProjectName.getText().trim());
+ if (myDetailsComponent != null) myDetailsComponent.setText(getBannerSlogan());
+ }
+ }
+ });
+ }
+
+
+ @Override
+ public void setDisplayName(final String name) {
+ //do nothing
+ }
+
+ @Override
+ public Project getEditableObject() {
+ return myProject;
+ }
+
+ @Override
+ public String getBannerSlogan() {
+ return ProjectBundle.message("project.roots.project.banner.text", myProject.getName());
+ }
+
+ @Override
+ public String getDisplayName() {
+ return ProjectBundle.message("project.roots.project.display.name");
+ }
+
+ @Override
+ public Icon getIcon(boolean open) {
+ return AllIcons.Nodes.Project;
+ }
+
+ @Override
+ @Nullable
+ @NonNls
+ public String getHelpTopic() {
+ return "reference.settingsdialog.project.structure.general";
+ }
+
+
+ @Override
+ @SuppressWarnings({"SimplifiableIfStatement"})
+ public boolean isModified() {
+ if (!LanguageLevelProjectExtension.getInstance(myProject).getLanguageLevel().equals(myLanguageLevelCombo.getSelectedItem())) {
+ return true;
+ }
+ final String compilerOutput = getOriginalCompilerOutputUrl();
+ if (!Comparing.strEqual(FileUtil.toSystemIndependentName(VfsUtil.urlToPath(compilerOutput)),
+ FileUtil.toSystemIndependentName(myProjectCompilerOutput.getText()))) return true;
+ if (myProjectJdkConfigurable.isModified()) return true;
+ if (myProjectName != null) {
+ if (!myProjectName.getText().trim().equals(myProject.getName())) return true;
+ }
+
+ return false;
+ }
+
+ @Nullable
+ private String getOriginalCompilerOutputUrl() {
+ final CompilerProjectExtension extension = CompilerProjectExtension.getInstance(myProject);
+ return extension != null ? extension.getCompilerOutputUrl() : null;
+ }
+
+ private void createUIComponents() {
+ myLanguageLevelCombo = new LanguageLevelCombo();
+ final JTextField textField = new JTextField();
+ final FileChooserDescriptor outputPathsChooserDescriptor = FileChooserDescriptorFactory.createSingleFolderDescriptor();
+ InsertPathAction.addTo(textField, outputPathsChooserDescriptor);
+ outputPathsChooserDescriptor.setHideIgnored(false);
+ BrowseFilesListener listener = new BrowseFilesListener(textField, "", ProjectBundle.message("project.compiler.output"), outputPathsChooserDescriptor);
+ myProjectCompilerOutput = new FieldPanel(textField, null, null, listener, EmptyRunnable.getInstance());
+ FileChooserFactory.getInstance().installFileCompletion(myProjectCompilerOutput.getTextField(), outputPathsChooserDescriptor, true, null);
+ }
+
+ public String getCompilerOutputUrl() {
+ return VfsUtil.pathToUrl(myProjectCompilerOutput.getText().trim());
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/ProjectJdkConfigurable.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/ProjectJdkConfigurable.java
new file mode 100644
index 0000000..252ce5e
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/ProjectJdkConfigurable.java
@@ -0,0 +1,181 @@
+/*
+ * Copyright 2000-2011 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;
+
+import com.intellij.openapi.application.ApplicationBundle;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.options.UnnamedConfigurable;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.project.ProjectBundle;
+import com.intellij.openapi.projectRoots.Sdk;
+import com.intellij.openapi.projectRoots.SdkModel;
+import com.intellij.openapi.roots.ProjectRootManager;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.ModuleStructureConfigurable;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.ProjectSdksModel;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.StructureConfigurableContext;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.daemon.ModuleProjectStructureElement;
+import com.intellij.openapi.util.Comparing;
+import com.intellij.openapi.util.Computable;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import java.awt.*;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+
+/**
+ * User: anna
+ * Date: 05-Jun-2006
+ */
+public class ProjectJdkConfigurable implements UnnamedConfigurable {
+ private JdkComboBox myCbProjectJdk;
+ private JPanel myJdkPanel;
+ private final Project myProject;
+ private final ProjectSdksModel myJdksModel;
+ private final SdkModel.Listener myListener = new SdkModel.Listener() {
+ @Override
+ public void sdkAdded(Sdk sdk) {
+ reloadModel();
+ }
+
+ @Override
+ public void beforeSdkRemove(Sdk sdk) {
+ reloadModel();
+ }
+
+ @Override
+ public void sdkChanged(Sdk sdk, String previousName) {
+ reloadModel();
+ }
+
+ @Override
+ public void sdkHomeSelected(Sdk sdk, String newSdkHome) {
+ reloadModel();
+ }
+ };
+
+ private boolean myFreeze = false;
+
+ public ProjectJdkConfigurable(Project project, final ProjectSdksModel jdksModel) {
+ myProject = project;
+ myJdksModel = jdksModel;
+ myJdksModel.addListener(myListener);
+ }
+
+ @Nullable
+ public Sdk getSelectedProjectJdk() {
+ return myJdksModel.findSdk(myCbProjectJdk.getSelectedJdk());
+ }
+
+ @Override
+ public JComponent createComponent() {
+ if (myJdkPanel == null) {
+ myJdkPanel = new JPanel(new GridBagLayout());
+ myCbProjectJdk = new JdkComboBox(myJdksModel);
+ myCbProjectJdk.insertItemAt(new JdkComboBox.NoneJdkComboBoxItem(), 0);
+ myCbProjectJdk.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ if (myFreeze) return;
+ myJdksModel.setProjectSdk(myCbProjectJdk.getSelectedJdk());
+ clearCaches();
+ }
+ });
+ myJdkPanel.add(new JLabel(ProjectBundle.message("module.libraries.target.jdk.project.radio")), new GridBagConstraints(0, 0, 3, 1, 0, 0, GridBagConstraints.NORTHWEST, GridBagConstraints.NONE, new Insets(0, 0, 4, 0), 0, 0));
+ myJdkPanel.add(myCbProjectJdk, new GridBagConstraints(0, 1, 1, 1, 0, 1.0, GridBagConstraints.NORTHWEST, GridBagConstraints.NONE, new Insets(0, 4, 0, 0), 0, 0));
+ final JButton setUpButton = new JButton(ApplicationBundle.message("button.new"));
+ myCbProjectJdk.setSetupButton(setUpButton, myProject, myJdksModel, new JdkComboBox.NoneJdkComboBoxItem(), null, false);
+ myJdkPanel.add(setUpButton, new GridBagConstraints(1, 1, 1, 1, 0, 0, GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0, 4, 0, 0), 0, 0));
+ final JButton editButton = new JButton(ApplicationBundle.message("button.edit"));
+ myCbProjectJdk.setEditButton(editButton, myProject, new Computable<Sdk>() {
+ @Override
+ @Nullable
+ public Sdk compute() {
+ return myJdksModel.getProjectSdk();
+ }
+ });
+
+ myJdkPanel.add(editButton, new GridBagConstraints(GridBagConstraints.RELATIVE, 1, 1, 1, 1.0, 0, GridBagConstraints.NORTHWEST,
+ GridBagConstraints.NONE, new Insets(0, 4, 0, 0), 0, 0));
+ }
+ return myJdkPanel;
+ }
+
+ private void reloadModel() {
+ myFreeze = true;
+ final Sdk projectJdk = myJdksModel.getProjectSdk();
+ myCbProjectJdk.reloadModel(new JdkComboBox.NoneJdkComboBoxItem(), myProject);
+ final String sdkName = projectJdk == null ? ProjectRootManager.getInstance(myProject).getProjectSdkName() : projectJdk.getName();
+ if (sdkName != null) {
+ final Sdk jdk = myJdksModel.findSdk(sdkName);
+ if (jdk != null) {
+ myCbProjectJdk.setSelectedJdk(jdk);
+ } else {
+ myCbProjectJdk.setInvalidJdk(sdkName);
+ clearCaches();
+ }
+ } else {
+ myCbProjectJdk.setSelectedJdk(null);
+ }
+ myFreeze = false;
+ }
+
+ private void clearCaches() {
+ final ModuleStructureConfigurable rootConfigurable = ModuleStructureConfigurable.getInstance(myProject);
+ Module[] modules = rootConfigurable.getModules();
+ for (Module module : modules) {
+ final StructureConfigurableContext context = rootConfigurable.getContext();
+ context.getDaemonAnalyzer().queueUpdate(new ModuleProjectStructureElement(context, module));
+ }
+ }
+
+ @Override
+ public boolean isModified() {
+ final Sdk projectJdk = ProjectRootManager.getInstance(myProject).getProjectSdk();
+ return !Comparing.equal(projectJdk, getSelectedProjectJdk());
+ }
+
+ @Override
+ public void apply() {
+ ProjectRootManager.getInstance(myProject).setProjectSdk(getSelectedProjectJdk());
+ }
+
+ @Override
+ public void reset() {
+ reloadModel();
+
+ final String sdkName = ProjectRootManager.getInstance(myProject).getProjectSdkName();
+ if (sdkName != null) {
+ final Sdk jdk = myJdksModel.findSdk(sdkName);
+ if (jdk != null) {
+ myCbProjectJdk.setSelectedJdk(jdk);
+ } else {
+ myCbProjectJdk.setInvalidJdk(sdkName);
+ }
+ } else {
+ myCbProjectJdk.setSelectedJdk(null);
+ }
+ }
+
+ @Override
+ public void disposeUIResources() {
+ myJdksModel.removeListener(myListener);
+ myJdkPanel = null;
+ myCbProjectJdk = null;
+ }
+
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/ProjectJdksConfigurable.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/ProjectJdksConfigurable.java
new file mode 100644
index 0000000..356eddb
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/ProjectJdksConfigurable.java
@@ -0,0 +1,229 @@
+/*
+ * Copyright 2000-2009 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.
+ */
+
+/*
+ * Created by IntelliJ IDEA.
+ * User: Anna.Kozlova
+ * Date: 16-Aug-2006
+ * Time: 16:56:21
+ */
+package com.intellij.openapi.roots.ui.configuration;
+
+import com.intellij.ide.util.PropertiesComponent;
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.DefaultActionGroup;
+import com.intellij.openapi.options.ConfigurationException;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.project.ProjectBundle;
+import com.intellij.openapi.projectRoots.Sdk;
+import com.intellij.openapi.projectRoots.impl.ProjectJdkImpl;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.JdkConfigurable;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.ProjectSdksModel;
+import com.intellij.openapi.ui.MasterDetailsComponent;
+import com.intellij.openapi.ui.MasterDetailsStateService;
+import com.intellij.openapi.ui.NamedConfigurable;
+import com.intellij.openapi.ui.Splitter;
+import com.intellij.openapi.util.Conditions;
+import com.intellij.ui.TreeSpeedSearch;
+import com.intellij.util.Consumer;
+import com.intellij.util.IconUtil;
+import com.intellij.util.containers.Convertor;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.tree.DefaultMutableTreeNode;
+import javax.swing.tree.TreePath;
+import java.awt.*;
+import java.util.*;
+
+public class ProjectJdksConfigurable extends MasterDetailsComponent {
+
+ private final ProjectSdksModel myProjectJdksModel;
+ private final Project myProject;
+ @NonNls
+ private static final String SPLITTER_PROPORTION = "project.jdk.splitter";
+
+ public ProjectJdksConfigurable(Project project) {
+ this(project, ProjectStructureConfigurable.getInstance(project).getProjectJdksModel());
+ }
+
+ public ProjectJdksConfigurable(Project project, ProjectSdksModel sdksModel) {
+ myProject = project;
+ myProjectJdksModel = sdksModel;
+ initTree();
+ }
+
+ @Override
+ protected String getComponentStateKey() {
+ return "ProjectJDKs.UI";
+ }
+
+ @Override
+ protected MasterDetailsStateService getStateService() {
+ return MasterDetailsStateService.getInstance(myProject);
+ }
+
+ @Override
+ protected void initTree() {
+ super.initTree();
+ new TreeSpeedSearch(myTree, new Convertor<TreePath, String>() {
+ @Override
+ public String convert(final TreePath treePath) {
+ return ((MyNode)treePath.getLastPathComponent()).getDisplayName();
+ }
+ }, true);
+
+ myTree.setRootVisible(false);
+ }
+
+ @Override
+ public void reset() {
+ super.reset();
+
+ myProjectJdksModel.reset(myProject);
+
+ myRoot.removeAllChildren();
+ final Map<Sdk, Sdk> sdks = myProjectJdksModel.getProjectSdks();
+ for (Sdk sdk : sdks.keySet()) {
+ final JdkConfigurable configurable = new JdkConfigurable((ProjectJdkImpl)sdks.get(sdk), myProjectJdksModel, TREE_UPDATER, myHistory, myProject);
+ addNode(new MyNode(configurable), myRoot);
+ }
+ selectJdk(myProjectJdksModel.getProjectSdk()); //restore selection
+ final String value = PropertiesComponent.getInstance().getValue(SPLITTER_PROPORTION);
+ if (value != null) {
+ try {
+ final Splitter splitter = extractSplitter();
+ if (splitter != null) {
+ (splitter).setProportion(Float.parseFloat(value));
+ }
+ }
+ catch (NumberFormatException e) {
+ //do not set proportion
+ }
+ }
+ }
+
+ @Nullable
+ private Splitter extractSplitter() {
+ final Component[] components = myWholePanel.getComponents();
+ if (components.length == 1 && components[0] instanceof Splitter) {
+ return (Splitter)components[0];
+ }
+ return null;
+ }
+
+ @Override
+ public void apply() throws ConfigurationException {
+ super.apply();
+ boolean modifiedJdks = false;
+ for (int i = 0; i < myRoot.getChildCount(); i++) {
+ final NamedConfigurable configurable = ((MyNode)myRoot.getChildAt(i)).getConfigurable();
+ if (configurable.isModified()) {
+ configurable.apply();
+ modifiedJdks = true;
+ }
+ }
+
+ if (myProjectJdksModel.isModified() || modifiedJdks) myProjectJdksModel.apply(this);
+ myProjectJdksModel.setProjectSdk(getSelectedJdk());
+ }
+
+
+ @Override
+ public boolean isModified() {
+ return super.isModified() || myProjectJdksModel.isModified();
+ }
+
+
+ @Override
+ public void disposeUIResources() {
+ final Splitter splitter = extractSplitter();
+ if (splitter != null) {
+ PropertiesComponent.getInstance().setValue(SPLITTER_PROPORTION, String.valueOf(splitter.getProportion()));
+ }
+ myProjectJdksModel.disposeUIResources();
+ super.disposeUIResources();
+ }
+
+ @Override
+ @Nullable
+ protected ArrayList<AnAction> createActions(final boolean fromPopup) {
+ final ArrayList<AnAction> actions = new ArrayList<AnAction>();
+ DefaultActionGroup group = new DefaultActionGroup(ProjectBundle.message("add.new.jdk.text"), true);
+ group.getTemplatePresentation().setIcon(IconUtil.getAddIcon());
+ myProjectJdksModel.createAddActions(group, myTree, new Consumer<Sdk>() {
+ @Override
+ public void consume(final Sdk projectJdk) {
+ addNode(new MyNode(new JdkConfigurable(((ProjectJdkImpl)projectJdk), myProjectJdksModel, TREE_UPDATER, myHistory, myProject), false), myRoot);
+ selectNodeInTree(findNodeByObject(myRoot, projectJdk));
+ }
+ });
+ actions.add(new MyActionGroupWrapper(group));
+ actions.add(new MyDeleteAction(forAll(Conditions.alwaysTrue())));
+ return actions;
+ }
+
+ @Override
+ protected void processRemovedItems() {
+ final Set<Sdk> jdks = new HashSet<Sdk>();
+ for(int i = 0; i < myRoot.getChildCount(); i++){
+ final DefaultMutableTreeNode node = (DefaultMutableTreeNode)myRoot.getChildAt(i);
+ final NamedConfigurable namedConfigurable = (NamedConfigurable)node.getUserObject();
+ jdks.add(((JdkConfigurable)namedConfigurable).getEditableObject());
+ }
+ final HashMap<Sdk, Sdk> sdks = new HashMap<Sdk, Sdk>(myProjectJdksModel.getProjectSdks());
+ for (Sdk sdk : sdks.values()) {
+ if (!jdks.contains(sdk)) {
+ myProjectJdksModel.removeSdk(sdk);
+ }
+ }
+ }
+
+ @Override
+ protected boolean wasObjectStored(Object editableObject) {
+ //noinspection RedundantCast
+ return myProjectJdksModel.getProjectSdks().containsKey((Sdk)editableObject);
+ }
+
+ @Nullable
+ public Sdk getSelectedJdk() {
+ return (Sdk)getSelectedObject();
+ }
+
+ public void selectJdk(final Sdk projectJdk) {
+ selectNodeInTree(projectJdk);
+ }
+
+ @Override
+ @Nullable
+ public String getDisplayName() {
+ return null;
+ }
+
+ @Override
+ @Nullable
+ @NonNls
+ public String getHelpTopic() {
+ return null;
+ }
+
+ @Override
+ protected
+ @Nullable
+ String getEmptySelectionString() {
+ return "Select an SDK to view or edit its details here";
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/ProjectStructureConfigurable.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/ProjectStructureConfigurable.java
new file mode 100644
index 0000000..fd855a7
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/ProjectStructureConfigurable.java
@@ -0,0 +1,637 @@
+/*
+ * Copyright 2000-2009 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;
+
+import com.intellij.compiler.server.BuildManager;
+import com.intellij.facet.Facet;
+import com.intellij.ide.util.PropertiesComponent;
+import com.intellij.openapi.actionSystem.*;
+import com.intellij.openapi.components.ServiceManager;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.module.ModuleManager;
+import com.intellij.openapi.options.BaseConfigurable;
+import com.intellij.openapi.options.Configurable;
+import com.intellij.openapi.options.ConfigurationException;
+import com.intellij.openapi.options.SearchableConfigurable;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.project.ProjectBundle;
+import com.intellij.openapi.project.ProjectManager;
+import com.intellij.openapi.projectRoots.Sdk;
+import com.intellij.openapi.roots.LibraryOrderEntry;
+import com.intellij.openapi.roots.OrderEntry;
+import com.intellij.openapi.roots.libraries.Library;
+import com.intellij.openapi.roots.libraries.LibraryTablesRegistrar;
+import com.intellij.openapi.roots.ui.configuration.artifacts.ArtifactsStructureConfigurable;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.*;
+import com.intellij.openapi.ui.DetailsComponent;
+import com.intellij.openapi.ui.MasterDetailsComponent;
+import com.intellij.openapi.ui.Splitter;
+import com.intellij.openapi.util.ActionCallback;
+import com.intellij.openapi.util.Disposer;
+import com.intellij.openapi.wm.ex.IdeFocusTraversalPolicy;
+import com.intellij.packaging.artifacts.Artifact;
+import com.intellij.ui.components.panels.Wrapper;
+import com.intellij.ui.navigation.BackAction;
+import com.intellij.ui.navigation.ForwardAction;
+import com.intellij.ui.navigation.History;
+import com.intellij.ui.navigation.Place;
+import com.intellij.util.io.storage.HeavyProcessLatch;
+import com.intellij.util.ui.UIUtil;
+import org.jetbrains.annotations.Nls;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import java.awt.*;
+import java.util.ArrayList;
+import java.util.List;
+
+public class ProjectStructureConfigurable extends BaseConfigurable implements SearchableConfigurable, Place.Navigator {
+
+ public static final DataKey<ProjectStructureConfigurable> KEY = DataKey.create("ProjectStructureConfiguration");
+
+ protected final UIState myUiState = new UIState();
+ private Splitter mySplitter;
+ private JComponent myToolbarComponent;
+ @NonNls public static final String CATEGORY = "category";
+ private JComponent myToFocus;
+ private boolean myWasUiDisposed;
+ private ConfigurationErrorsComponent myErrorsComponent;
+
+ public static class UIState {
+ public float proportion;
+ public float sideProportion;
+
+ public String lastEditedConfigurable;
+ }
+
+ private final Project myProject;
+ private final FacetStructureConfigurable myFacetStructureConfigurable;
+ private final ArtifactsStructureConfigurable myArtifactsStructureConfigurable;
+
+ private History myHistory = new History(this);
+ private SidePanel mySidePanel;
+
+ private JPanel myComponent;
+ private final Wrapper myDetails = new Wrapper();
+
+ private Configurable mySelectedConfigurable;
+
+ private final ProjectSdksModel myProjectJdksModel = new ProjectSdksModel();
+
+ private ProjectConfigurable myProjectConfig;
+ private final ProjectLibrariesConfigurable myProjectLibrariesConfig;
+ private final GlobalLibrariesConfigurable myGlobalLibrariesConfig;
+ private ModuleStructureConfigurable myModulesConfig;
+
+ private boolean myUiInitialized;
+
+ private final List<Configurable> myName2Config = new ArrayList<Configurable>();
+ private final StructureConfigurableContext myContext;
+ private final ModulesConfigurator myModuleConfigurator;
+ private JdkListConfigurable myJdkListConfig;
+
+ private final JLabel myEmptySelection = new JLabel("<html><body><center>Select a setting to view or edit its details here</center></body></html>", JLabel.CENTER);
+
+ public ProjectStructureConfigurable(final Project project,
+ final ProjectLibrariesConfigurable projectLibrariesConfigurable,
+ final GlobalLibrariesConfigurable globalLibrariesConfigurable,
+ final ModuleStructureConfigurable moduleStructureConfigurable,
+ FacetStructureConfigurable facetStructureConfigurable,
+ ArtifactsStructureConfigurable artifactsStructureConfigurable) {
+ myProject = project;
+ myFacetStructureConfigurable = facetStructureConfigurable;
+ myArtifactsStructureConfigurable = artifactsStructureConfigurable;
+
+ myModuleConfigurator = new ModulesConfigurator(myProject);
+ myContext = new StructureConfigurableContext(myProject, myModuleConfigurator);
+ myModuleConfigurator.setContext(myContext);
+
+ myProjectLibrariesConfig = projectLibrariesConfigurable;
+ myGlobalLibrariesConfig = globalLibrariesConfigurable;
+ myModulesConfig = moduleStructureConfigurable;
+
+ myProjectLibrariesConfig.init(myContext);
+ myGlobalLibrariesConfig.init(myContext);
+ myModulesConfig.init(myContext);
+ myFacetStructureConfigurable.init(myContext);
+ if (!project.isDefault()) {
+ myArtifactsStructureConfigurable.init(myContext, myModulesConfig, myProjectLibrariesConfig, myGlobalLibrariesConfig);
+ }
+
+ final PropertiesComponent propertiesComponent = PropertiesComponent.getInstance(myProject);
+ myUiState.lastEditedConfigurable = propertiesComponent.getValue("project.structure.last.edited");
+ final String proportion = propertiesComponent.getValue("project.structure.proportion");
+ myUiState.proportion = proportion != null ? Float.parseFloat(proportion) : 0;
+ final String sideProportion = propertiesComponent.getValue("project.structure.side.proportion");
+ myUiState.sideProportion = sideProportion != null ? Float.parseFloat(sideProportion) : 0;
+ }
+
+ @Override
+ @NotNull
+ @NonNls
+ public String getId() {
+ return "project.structure";
+ }
+
+ @Override
+ @Nullable
+ public Runnable enableSearch(final String option) {
+ return null;
+ }
+
+ @Override
+ @Nls
+ public String getDisplayName() {
+ return ProjectBundle.message("project.settings.display.name");
+ }
+
+ @Override
+ @Nullable
+ @NonNls
+ public String getHelpTopic() {
+ return mySelectedConfigurable != null ? mySelectedConfigurable.getHelpTopic() : "";
+ }
+
+ @Override
+ public JComponent createComponent() {
+ myComponent = new MyPanel();
+
+ mySplitter = new Splitter(false, .15f);
+ mySplitter.setHonorComponentsMinimumSize(true);
+
+ initSidePanel();
+
+ final JPanel left = new JPanel(new BorderLayout()) {
+ @Override
+ public Dimension getMinimumSize() {
+ final Dimension original = super.getMinimumSize();
+ return new Dimension(Math.max(original.width, 100), original.height);
+ }
+ };
+
+ final DefaultActionGroup toolbarGroup = new DefaultActionGroup();
+ toolbarGroup.add(new BackAction(myComponent));
+ toolbarGroup.add(new ForwardAction(myComponent));
+ final ActionToolbar toolbar = ActionManager.getInstance().createActionToolbar(ActionPlaces.UNKNOWN, toolbarGroup, true);
+ toolbar.setTargetComponent(myComponent);
+ myToolbarComponent = toolbar.getComponent();
+ left.add(myToolbarComponent, BorderLayout.NORTH);
+ left.add(mySidePanel, BorderLayout.CENTER);
+
+ mySplitter.setFirstComponent(left);
+ mySplitter.setSecondComponent(myDetails);
+
+ myComponent.add(mySplitter, BorderLayout.CENTER);
+ myErrorsComponent = new ConfigurationErrorsComponent(myProject);
+ myComponent.add(myErrorsComponent, BorderLayout.SOUTH);
+
+ myUiInitialized = true;
+
+ return myComponent;
+ }
+
+ private void initSidePanel() {
+ boolean isDefaultProject = myProject == ProjectManager.getInstance().getDefaultProject();
+
+ mySidePanel = new SidePanel(this, myHistory);
+ mySidePanel.addSeparator("Project Settings");
+ addProjectConfig();
+ if (!isDefaultProject) {
+ addModulesConfig();
+ }
+ addProjectLibrariesConfig();
+
+ if (!isDefaultProject) {
+ addFacetsConfig();
+ addArtifactsConfig();
+ }
+ mySidePanel.addSeparator("Platform Settings");
+ addJdkListConfig();
+ addGlobalLibrariesConfig();
+ }
+
+ private void addArtifactsConfig() {
+ addConfigurable(myArtifactsStructureConfigurable);
+ }
+
+ public ArtifactsStructureConfigurable getArtifactsStructureConfigurable() {
+ return myArtifactsStructureConfigurable;
+ }
+
+ private void addFacetsConfig() {
+ if (myFacetStructureConfigurable.isVisible()) {
+ addConfigurable(myFacetStructureConfigurable);
+ }
+ }
+
+ private void addJdkListConfig() {
+ if (myJdkListConfig == null) {
+ myJdkListConfig = JdkListConfigurable.getInstance(myProject);
+ myJdkListConfig.init(myContext);
+ }
+ addConfigurable(myJdkListConfig);
+ }
+
+ private void addProjectConfig() {
+ myProjectConfig = new ProjectConfigurable(myProject, myContext, myModuleConfigurator, myProjectJdksModel);
+ addConfigurable(myProjectConfig);
+ }
+
+ private void addProjectLibrariesConfig() {
+ addConfigurable(myProjectLibrariesConfig);
+ }
+
+ private void addGlobalLibrariesConfig() {
+ addConfigurable(myGlobalLibrariesConfig);
+ }
+
+ private void addModulesConfig() {
+ myModulesConfig = ModuleStructureConfigurable.getInstance(myProject);
+ addConfigurable(myModulesConfig);
+ }
+
+ @Override
+ public boolean isModified() {
+ for (Configurable each : myName2Config) {
+ if (each.isModified()) return true;
+ }
+
+ return false;
+ }
+
+ @Override
+ public void apply() throws ConfigurationException {
+ for (Configurable each : myName2Config) {
+ if (each instanceof BaseStructureConfigurable && each.isModified()) {
+ ((BaseStructureConfigurable)each).checkCanApply();
+ }
+ }
+ for (Configurable each : myName2Config) {
+ if (each.isModified()) {
+ each.apply();
+ }
+ }
+
+ myContext.getDaemonAnalyzer().clearCaches();
+ SwingUtilities.invokeLater(new Runnable() {
+ public void run() {
+ BuildManager.getInstance().scheduleAutoMake();
+ }
+ });
+ }
+
+ @Override
+ public void reset() {
+ // need this to ensure VFS operations will not block because of storage flushing
+ // and other maintenance IO tasks run in background
+ HeavyProcessLatch.INSTANCE.processStarted();
+
+ try {
+ myWasUiDisposed = false;
+
+ myContext.reset();
+
+ myProjectJdksModel.reset(myProject);
+
+ Configurable toSelect = null;
+ for (Configurable each : myName2Config) {
+ if (myUiState.lastEditedConfigurable != null && myUiState.lastEditedConfigurable.equals(each.getDisplayName())) {
+ toSelect = each;
+ }
+ if (each instanceof MasterDetailsComponent) {
+ ((MasterDetailsComponent)each).setHistory(myHistory);
+ }
+ each.reset();
+ }
+
+ myHistory.clear();
+
+ if (toSelect == null && myName2Config.size() > 0) {
+ toSelect = myName2Config.iterator().next();
+ }
+
+ removeSelected();
+
+ navigateTo(toSelect != null ? createPlaceFor(toSelect) : null, false);
+
+ if (myUiState.proportion > 0) {
+ mySplitter.setProportion(myUiState.proportion);
+ }
+ }
+ finally {
+ HeavyProcessLatch.INSTANCE.processFinished();
+ }
+ }
+
+ public void hideSidePanel() {
+ mySplitter.getFirstComponent().setVisible(false);
+ }
+
+ @Override
+ public void disposeUIResources() {
+ if (!myUiInitialized) return;
+ final PropertiesComponent propertiesComponent = PropertiesComponent.getInstance(myProject);
+ propertiesComponent.setValue("project.structure.last.edited", myUiState.lastEditedConfigurable);
+ propertiesComponent.setValue("project.structure.proportion", String.valueOf(myUiState.proportion));
+ propertiesComponent.setValue("project.structure.side.proportion", String.valueOf(myUiState.sideProportion));
+
+ myWasUiDisposed = true;
+
+ myUiState.proportion = mySplitter.getProportion();
+ saveSideProportion();
+ myContext.getDaemonAnalyzer().stop();
+ for (Configurable each : myName2Config) {
+ each.disposeUIResources();
+ }
+ myContext.clear();
+ myName2Config.clear();
+
+ myModuleConfigurator.getFacetsConfigurator().clearMaps();
+
+ Disposer.dispose(myErrorsComponent);
+
+ myUiInitialized = false;
+ }
+
+ public boolean isUiInitialized() {
+ return myUiInitialized;
+ }
+
+ public History getHistory() {
+ return myHistory;
+ }
+
+ @Override
+ public void setHistory(final History history) {
+ myHistory = history;
+ }
+
+ @Override
+ public void queryPlace(@NotNull final Place place) {
+ place.putPath(CATEGORY, mySelectedConfigurable);
+ Place.queryFurther(mySelectedConfigurable, place);
+ }
+
+ public ActionCallback selectProjectGeneralSettings(final boolean requestFocus) {
+ Place place = createPlaceFor(myProjectConfig);
+ return navigateTo(place, requestFocus);
+ }
+
+ public ActionCallback select(@Nullable final String moduleToSelect, @Nullable String editorNameToSelect, final boolean requestFocus) {
+ Place place = createModulesPlace();
+ if (moduleToSelect != null) {
+ final Module module = ModuleManager.getInstance(myProject).findModuleByName(moduleToSelect);
+ assert module != null;
+ place = place.putPath(ModuleStructureConfigurable.TREE_OBJECT, module).putPath(ModuleEditor.SELECTED_EDITOR_NAME, editorNameToSelect);
+ }
+ return navigateTo(place, requestFocus);
+ }
+
+ public Place createModulesPlace() {
+ return createPlaceFor(myModulesConfig);
+ }
+
+ public Place createModulePlace(@NotNull Module module) {
+ return createModulesPlace().putPath(ModuleStructureConfigurable.TREE_OBJECT, module);
+ }
+
+ public ActionCallback select(@Nullable final Facet facetToSelect, final boolean requestFocus) {
+ Place place = createModulesPlace();
+ if (facetToSelect != null) {
+ place = place.putPath(ModuleStructureConfigurable.TREE_OBJECT, facetToSelect);
+ }
+ return navigateTo(place, requestFocus);
+ }
+
+ public ActionCallback select(@NotNull Sdk sdk, final boolean requestFocus) {
+ Place place = createPlaceFor(myJdkListConfig);
+ place.putPath(BaseStructureConfigurable.TREE_NAME, sdk.getName());
+ return navigateTo(place, requestFocus);
+ }
+
+ public ActionCallback selectGlobalLibraries(final boolean requestFocus) {
+ Place place = createPlaceFor(myGlobalLibrariesConfig);
+ return navigateTo(place, requestFocus);
+ }
+
+ public ActionCallback selectProjectOrGlobalLibrary(@NotNull Library library, boolean requestFocus) {
+ Place place = createProjectOrGlobalLibraryPlace(library);
+ return navigateTo(place, requestFocus);
+ }
+
+ public Place createProjectOrGlobalLibraryPlace(Library library) {
+ Place place = createPlaceFor(getConfigurableFor(library));
+ place.putPath(BaseStructureConfigurable.TREE_NAME, library.getName());
+ return place;
+ }
+
+ public ActionCallback select(@Nullable Artifact artifact, boolean requestFocus) {
+ Place place = createArtifactPlace(artifact);
+ return navigateTo(place, requestFocus);
+ }
+
+ public Place createArtifactPlace(Artifact artifact) {
+ Place place = createPlaceFor(myArtifactsStructureConfigurable);
+ if (artifact != null) {
+ place.putPath(BaseStructureConfigurable.TREE_NAME, artifact.getName());
+ }
+ return place;
+ }
+
+ public ActionCallback select(@NotNull LibraryOrderEntry libraryOrderEntry, final boolean requestFocus) {
+ final Library lib = libraryOrderEntry.getLibrary();
+ if (lib == null || lib.getTable() == null) {
+ return selectOrderEntry(libraryOrderEntry.getOwnerModule(), libraryOrderEntry);
+ }
+ Place place = createPlaceFor(getConfigurableFor(lib));
+ place.putPath(BaseStructureConfigurable.TREE_NAME, libraryOrderEntry.getLibraryName());
+ return navigateTo(place, requestFocus);
+ }
+
+ public ActionCallback selectOrderEntry(@NotNull final Module module, @Nullable final OrderEntry orderEntry) {
+ return ModuleStructureConfigurable.getInstance(myProject).selectOrderEntry(module, orderEntry);
+ }
+
+ @Override
+ public ActionCallback navigateTo(@Nullable final Place place, final boolean requestFocus) {
+ final Configurable toSelect = (Configurable)place.getPath(CATEGORY);
+
+ JComponent detailsContent = myDetails.getTargetComponent();
+
+ if (mySelectedConfigurable != toSelect) {
+ if (mySelectedConfigurable instanceof BaseStructureConfigurable) {
+ ((BaseStructureConfigurable)mySelectedConfigurable).onStructureUnselected();
+ }
+ saveSideProportion();
+ removeSelected();
+
+ if (toSelect != null) {
+ detailsContent = toSelect.createComponent();
+ myDetails.setContent(detailsContent);
+ }
+
+ mySelectedConfigurable = toSelect;
+ if (mySelectedConfigurable != null) {
+ myUiState.lastEditedConfigurable = mySelectedConfigurable.getDisplayName();
+ }
+
+ if (toSelect instanceof MasterDetailsComponent) {
+ final MasterDetailsComponent masterDetails = (MasterDetailsComponent)toSelect;
+ if (myUiState.sideProportion > 0) {
+ masterDetails.getSplitter().setProportion(myUiState.sideProportion);
+ }
+ masterDetails.setHistory(myHistory);
+ }
+
+ if (toSelect instanceof DetailsComponent.Facade) {
+ ((DetailsComponent.Facade)toSelect).getDetailsComponent().setBannerMinHeight(myToolbarComponent.getPreferredSize().height);
+ }
+
+ if (toSelect instanceof BaseStructureConfigurable) {
+ ((BaseStructureConfigurable)toSelect).onStructureSelected();
+ }
+ }
+
+
+
+ if (detailsContent != null) {
+ JComponent toFocus = IdeFocusTraversalPolicy.getPreferredFocusedComponent(detailsContent);
+ if (toFocus == null) {
+ toFocus = detailsContent;
+ }
+ if (requestFocus) {
+ myToFocus = toFocus;
+ UIUtil.requestFocus(toFocus);
+ }
+ }
+
+ final ActionCallback result = new ActionCallback();
+ Place.goFurther(toSelect, place, requestFocus).notifyWhenDone(result);
+
+ myDetails.revalidate();
+ myDetails.repaint();
+
+ if (toSelect != null) {
+ mySidePanel.select(createPlaceFor(toSelect));
+ }
+
+ if (!myHistory.isNavigatingNow() && mySelectedConfigurable != null) {
+ myHistory.pushQueryPlace();
+ }
+
+ return result;
+ }
+
+ private void saveSideProportion() {
+ if (mySelectedConfigurable instanceof MasterDetailsComponent) {
+ myUiState.sideProportion = ((MasterDetailsComponent)mySelectedConfigurable).getSplitter().getProportion();
+ }
+ }
+
+ private void removeSelected() {
+ myDetails.removeAll();
+ mySelectedConfigurable = null;
+ myUiState.lastEditedConfigurable = null;
+
+ myDetails.add(myEmptySelection, BorderLayout.CENTER);
+ }
+
+ public static ProjectStructureConfigurable getInstance(final Project project) {
+ return ServiceManager.getService(project, ProjectStructureConfigurable.class);
+ }
+
+ public ProjectSdksModel getProjectJdksModel() {
+ return myProjectJdksModel;
+ }
+
+ public JdkListConfigurable getJdkConfig() {
+ return myJdkListConfig;
+ }
+
+ public ProjectLibrariesConfigurable getProjectLibrariesConfig() {
+ return myProjectLibrariesConfig;
+ }
+
+ public GlobalLibrariesConfigurable getGlobalLibrariesConfig() {
+ return myGlobalLibrariesConfig;
+ }
+
+ public ModuleStructureConfigurable getModulesConfig() {
+ return myModulesConfig;
+ }
+
+ public ProjectConfigurable getProjectConfig() {
+ return myProjectConfig;
+ }
+
+ private void addConfigurable(Configurable configurable) {
+ myName2Config.add(configurable);
+
+ mySidePanel.addPlace(createPlaceFor(configurable), new Presentation(configurable.getDisplayName()));
+ }
+
+ private static Place createPlaceFor(final Configurable configurable) {
+ return new Place().putPath(CATEGORY, configurable);
+ }
+
+
+ public StructureConfigurableContext getContext() {
+ return myContext;
+ }
+
+ private class MyPanel extends JPanel implements DataProvider {
+ public MyPanel() {
+ super(new BorderLayout());
+ }
+
+ @Override
+ @Nullable
+ public Object getData(@NonNls final String dataId) {
+ if (KEY.is(dataId)) {
+ return ProjectStructureConfigurable.this;
+ } else if (History.KEY.is(dataId)) {
+ return getHistory();
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ public Dimension getPreferredSize() {
+ return new Dimension(1024, 768);
+ }
+ }
+
+ public BaseLibrariesConfigurable getConfigurableFor(final Library library) {
+ if (LibraryTablesRegistrar.PROJECT_LEVEL.equals(library.getTable().getTableLevel())) {
+ return myProjectLibrariesConfig;
+ } else {
+ return myGlobalLibrariesConfig;
+ }
+ }
+
+ @Override
+ public JComponent getPreferredFocusedComponent() {
+ return myToFocus;
+ }
+
+ protected void hideErrorsComponent() {
+ myErrorsComponent.setVisible(false);
+ }
+
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/SidePanel.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/SidePanel.java
new file mode 100644
index 0000000..193ce66
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/SidePanel.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright 2000-2009 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;
+
+import com.intellij.openapi.actionSystem.Presentation;
+import com.intellij.openapi.ui.popup.ListItemDescriptor;
+import com.intellij.ui.ScrollPaneFactory;
+import com.intellij.ui.components.JBList;
+import com.intellij.ui.navigation.History;
+import com.intellij.ui.navigation.Place;
+import com.intellij.ui.popup.list.GroupedItemsListRenderer;
+import org.jetbrains.annotations.NotNull;
+
+import javax.swing.*;
+import javax.swing.event.ListSelectionEvent;
+import javax.swing.event.ListSelectionListener;
+import java.awt.*;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+public class SidePanel extends JPanel {
+
+ private final JList myList;
+ private final DefaultListModel myModel;
+ private final Place.Navigator myNavigator;
+ private final ArrayList<Place> myPlaces = new ArrayList<Place>();
+
+ private final Map<Integer, String> myIndex2Separator = new HashMap<Integer, String>();
+ private final Map<Place, Presentation> myPlace2Presentation = new HashMap<Place, Presentation>();
+ private final History myHistory;
+
+ public SidePanel(Place.Navigator navigator, History history) {
+ myHistory = history;
+ myNavigator = navigator;
+
+ setLayout(new BorderLayout());
+
+ myModel = new DefaultListModel();
+ myList = new JBList(myModel);
+
+ final ListItemDescriptor descriptor = new ListItemDescriptor() {
+ @Override
+ public String getTextFor(final Object value) {
+ return myPlace2Presentation.get(value).getText();
+ }
+
+ @Override
+ public String getTooltipFor(final Object value) {
+ return getTextFor(value);
+ }
+
+ @Override
+ public Icon getIconFor(final Object value) {
+ return null;
+ //return myPlace2Presentation.get(value).getIcon();
+ }
+
+ @Override
+ public boolean hasSeparatorAboveOf(final Object value) {
+ final int index = myPlaces.indexOf(value);
+ return myIndex2Separator.get(index) != null;
+ }
+
+ @Override
+ public String getCaptionAboveOf(final Object value) {
+ return myIndex2Separator.get(myPlaces.indexOf(value));
+ }
+ };
+
+ myList.setCellRenderer(new GroupedItemsListRenderer(descriptor));
+
+
+ add(ScrollPaneFactory.createScrollPane(myList), BorderLayout.CENTER);
+ myList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
+
+ myList.addListSelectionListener(new ListSelectionListener() {
+ @Override
+ public void valueChanged(final ListSelectionEvent e) {
+ if (e.getValueIsAdjusting()) return;
+ final Object value = myList.getSelectedValue();
+ if (value != null) {
+ myNavigator.navigateTo(((Place)value), false);
+ }
+ }
+ });
+ }
+
+ public void addPlace(Place place, @NotNull Presentation presentation) {
+ myModel.addElement(place);
+ myPlaces.add(place);
+ myPlace2Presentation.put(place, presentation);
+ revalidate();
+ repaint();
+ }
+
+ public void addSeparator(String text) {
+ myIndex2Separator.put(myPlaces.size(), text);
+ }
+
+ public Collection<Place> getPlaces() {
+ return myPlaces;
+ }
+
+ public void select(final Place place) {
+ myList.setSelectedValue(place, true);
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/TabbedModuleEditor.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/TabbedModuleEditor.java
new file mode 100644
index 0000000..2763b59
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/TabbedModuleEditor.java
@@ -0,0 +1,141 @@
+package com.intellij.openapi.roots.ui.configuration;
+
+import com.intellij.ide.util.PropertiesComponent;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.module.ModuleConfigurationEditor;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.ActionCallback;
+import com.intellij.ui.TabbedPaneWrapper;
+import com.intellij.ui.navigation.Place;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import javax.swing.event.ChangeEvent;
+
+/**
+ * @author ksafonov
+ */
+public abstract class TabbedModuleEditor extends ModuleEditor {
+
+ private static final String SELECTED_EDITOR_KEY = TabbedModuleEditor.class.getName() + ".selectedEditor";
+
+ private TabbedPaneWrapper myTabbedPane;
+
+ public TabbedModuleEditor(Project project, ModulesProvider modulesProvider, @NotNull Module module) {
+ super(project, modulesProvider, module);
+ }
+
+ private static String getSavedSelectedEditor() {
+ return PropertiesComponent.getInstance().getValue(SELECTED_EDITOR_KEY);
+ }
+
+ private void saveSelectedEditor() {
+ final String selectedTabName = getSelectedTabName();
+ if (selectedTabName != null) {
+ // already disposed
+ PropertiesComponent.getInstance().setValue(SELECTED_EDITOR_KEY, selectedTabName);
+ }
+ }
+
+ @Override
+ protected JComponent createCenterPanel() {
+ myTabbedPane = new TabbedPaneWrapper(this);
+
+ for (ModuleConfigurationEditor editor : myEditors) {
+ myTabbedPane.addTab(editor.getDisplayName(), editor.createComponent());
+ editor.reset();
+ }
+ restoreSelectedEditor();
+
+ myTabbedPane.addChangeListener(new javax.swing.event.ChangeListener() {
+ @Override
+ public void stateChanged(ChangeEvent e) {
+ saveSelectedEditor();
+ if (myHistory != null) {
+ myHistory.pushQueryPlace();
+ }
+ }
+ });
+ return myTabbedPane.getComponent();
+ }
+
+ @Override
+ protected void restoreSelectedEditor() {
+ selectEditor(getSavedSelectedEditor());
+ }
+
+ @Override
+ public ActionCallback navigateTo(@Nullable final Place place, final boolean requestFocus) {
+ if (place != null) {
+ selectEditor((String)place.getPath(SELECTED_EDITOR_NAME));
+ }
+ return new ActionCallback.Done();
+ }
+
+ @Override
+ public void queryPlace(@NotNull final Place place) {
+ place.putPath(SELECTED_EDITOR_NAME, getSavedSelectedEditor());
+ }
+
+ @Nullable
+ private String getSelectedTabName() {
+ return myTabbedPane == null || myTabbedPane.getSelectedIndex() == -1 ? null : myTabbedPane.getTitleAt(myTabbedPane.getSelectedIndex());
+ }
+
+ @Override
+ public void selectEditor(@Nullable String name) {
+ if (name != null) {
+ getPanel();
+ final int editorTabIndex = getEditorTabIndex(name);
+ if (editorTabIndex >= 0 && editorTabIndex < myTabbedPane.getTabCount()) {
+ myTabbedPane.setSelectedIndex(editorTabIndex);
+ saveSelectedEditor();
+ }
+ }
+ }
+
+ private int getEditorTabIndex(final String editorName) {
+ if (myTabbedPane != null && editorName != null) {
+ final int tabCount = myTabbedPane.getTabCount();
+ for (int idx = 0; idx < tabCount; idx++) {
+ if (editorName.equals(myTabbedPane.getTitleAt(idx))) {
+ return idx;
+ }
+ }
+ }
+ return -1;
+ }
+
+ @Override
+ @Nullable
+ public ModuleConfigurationEditor getEditor(@NotNull String displayName) {
+ int index = getEditorTabIndex(displayName);
+ if (0 <= index && index < myEditors.size()) {
+ return myEditors.get(index);
+ }
+ return null;
+ }
+
+ @Override
+ public ModuleConfigurationEditor getSelectedEditor() {
+ if (myTabbedPane == null) {
+ return null;
+ }
+
+ String title = myTabbedPane.getSelectedTitle();
+ if (title == null) {
+ return null;
+ }
+
+ return getEditor(title);
+ }
+
+ @Override
+ protected void disposeCenterPanel() {
+ if (myTabbedPane != null) {
+ saveSelectedEditor();
+ myTabbedPane = null;
+ }
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/TableItem.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/TableItem.java
new file mode 100644
index 0000000..f1f171d
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/TableItem.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2000-2011 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;
+
+import com.intellij.openapi.roots.ui.CellAppearanceEx;
+import com.intellij.openapi.roots.ui.FileAppearanceService;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.openapi.vfs.VirtualFileManager;
+import org.jetbrains.annotations.NotNull;
+
+class TableItem {
+ private final String myUrl;
+ private final CellAppearanceEx myCellAppearance;
+
+ public TableItem(@NotNull final VirtualFile file) {
+ myUrl = file.getUrl();
+ myCellAppearance = FileAppearanceService.getInstance().forVirtualFile(file);
+ }
+
+ public TableItem(@NotNull final String url) {
+ myUrl = url;
+
+ final VirtualFile file = VirtualFileManager.getInstance().findFileByUrl(url);
+ if (file != null) {
+ myCellAppearance = FileAppearanceService.getInstance().forVirtualFile(file);
+ }
+ else {
+ myCellAppearance = FileAppearanceService.getInstance().forInvalidUrl(url);
+ }
+ }
+
+ @NotNull
+ public String getUrl() {
+ return myUrl;
+ }
+
+ @NotNull
+ public CellAppearanceEx getCellAppearance() {
+ return myCellAppearance;
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/UIRootConfigurationAccessor.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/UIRootConfigurationAccessor.java
new file mode 100644
index 0000000..f6e38b3
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/UIRootConfigurationAccessor.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2000-2009 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;
+
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.projectRoots.Sdk;
+import com.intellij.openapi.roots.ProjectRootManager;
+import com.intellij.openapi.roots.impl.RootConfigurationAccessor;
+import com.intellij.openapi.roots.libraries.Library;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.ModuleStructureConfigurable;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.ProjectSdksModel;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.StructureConfigurableContext;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * @author yole
+ */
+public class UIRootConfigurationAccessor extends RootConfigurationAccessor {
+ private final Project myProject;
+
+ public UIRootConfigurationAccessor(final Project project) {
+ myProject = project;
+ }
+
+ @Override
+ @Nullable
+ public Library getLibrary(Library library, final String libraryName, final String libraryLevel) {
+ final StructureConfigurableContext context = ProjectStructureConfigurable.getInstance(myProject).getContext();
+ if (library == null) {
+ if (libraryName != null) {
+ library = context.getLibrary(libraryName, libraryLevel);
+ }
+ } else {
+ final Library model = context.getLibraryModel(library);
+ if (model != null) {
+ library = model;
+ }
+ library = context.getLibrary(library.getName(), library.getTable().getTableLevel());
+ }
+ return library;
+ }
+
+ @Override
+ @Nullable
+ public Sdk getSdk(final Sdk sdk, final String sdkName) {
+ final ProjectSdksModel model = ProjectStructureConfigurable.getInstance(myProject).getJdkConfig().getJdksTreeModel();
+ return sdkName != null ? model.findSdk(sdkName) : sdk;
+ }
+
+ @Override
+ public Module getModule(final Module module, final String moduleName) {
+ if (module == null) {
+ return ModuleStructureConfigurable.getInstance(myProject).getModule(moduleName);
+ }
+ return module;
+ }
+
+ @Override
+ public Sdk getProjectSdk(final Project project) {
+ return ProjectStructureConfigurable.getInstance(project).getProjectJdksModel().getProjectSdk();
+ }
+
+ @Override
+ @Nullable
+ public String getProjectSdkName(final Project project) {
+ final String projectJdkName = ProjectRootManager.getInstance(project).getProjectSdkName();
+ final Sdk projectJdk = getProjectSdk(project);
+ if (projectJdk != null) {
+ return projectJdk.getName();
+ }
+ else {
+ final ProjectSdksModel projectJdksModel = ProjectStructureConfigurable.getInstance(project).getProjectJdksModel();
+ return projectJdksModel.findSdk(projectJdkName) == null ? projectJdkName : null;
+ }
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/actions/NewModuleAction.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/actions/NewModuleAction.java
new file mode 100644
index 0000000..56ef125
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/actions/NewModuleAction.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright 2000-2009 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.actions;
+
+import com.intellij.ide.util.newProjectWizard.AddModuleWizard;
+import com.intellij.ide.util.newProjectWizard.AddModuleWizardPro;
+import com.intellij.ide.util.projectWizard.ModuleBuilder;
+import com.intellij.ide.util.projectWizard.ProjectBuilder;
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.actionSystem.PlatformDataKeys;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.project.DumbAware;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.project.ProjectBundle;
+import com.intellij.openapi.roots.ui.configuration.DefaultModulesProvider;
+import com.intellij.openapi.roots.ui.configuration.ModulesConfigurator;
+import com.intellij.openapi.util.registry.Registry;
+import com.intellij.openapi.vfs.VirtualFile;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.List;
+
+/**
+ * @author Eugene Zhuravlev
+ * Date: Jan 5, 2004
+ */
+public class NewModuleAction extends AnAction implements DumbAware {
+ public NewModuleAction() {
+ super(ProjectBundle.message("module.new.action"), ProjectBundle.message("module.new.action.description"), null);
+ }
+
+ @Override
+ public void actionPerformed(AnActionEvent e) {
+ final Project project = getEventProject(e);
+ if (project == null) {
+ return;
+ }
+ Object dataFromContext = prepareDataFromContext(e);
+
+ String defaultPath = null;
+ final VirtualFile virtualFile = e.getData(PlatformDataKeys.VIRTUAL_FILE);
+ if (virtualFile != null && virtualFile.isDirectory()) {
+ defaultPath = virtualFile.getPath();
+ }
+ final AddModuleWizard wizard = Registry.is("new.project.wizard")
+ ? new AddModuleWizardPro(project, new DefaultModulesProvider(project), defaultPath)
+ : new AddModuleWizard(project, new DefaultModulesProvider(project), defaultPath);
+
+ wizard.show();
+
+ if (wizard.isOK()) {
+ createModuleFromWizard(project, dataFromContext, wizard);
+ }
+ }
+
+ @Nullable
+ public Module createModuleFromWizard(Project project, @Nullable Object dataFromContext, AddModuleWizard wizard) {
+ final ProjectBuilder builder = wizard.getProjectBuilder();
+ if (builder instanceof ModuleBuilder) {
+ final ModuleBuilder moduleBuilder = (ModuleBuilder)builder;
+ if (moduleBuilder.getName() == null) {
+ moduleBuilder.setName(wizard.getProjectName());
+ }
+ if (moduleBuilder.getModuleFilePath() == null) {
+ moduleBuilder.setModuleFilePath(wizard.getModuleFilePath());
+ }
+ }
+ if (!builder.validate(project, project)) {
+ return null;
+ }
+ Module module;
+ if (builder instanceof ModuleBuilder) {
+ module = ((ModuleBuilder) builder).commitModule(project, null);
+ if (module != null) {
+ processCreatedModule(module, dataFromContext);
+ }
+ return module;
+ }
+ else {
+ List<Module> modules = builder.commit(project, null, new DefaultModulesProvider(project));
+ if (builder.isOpenProjectSettingsAfter()) {
+ ModulesConfigurator.showDialog(project, null, null);
+ }
+ module = modules == null || modules.isEmpty() ? null : modules.get(0);
+ }
+ project.save();
+ return module;
+ }
+
+ @Nullable
+ protected Object prepareDataFromContext(final AnActionEvent e) {
+ return null;
+ }
+
+ protected void processCreatedModule(final Module module, @Nullable final Object dataFromContext) {
+ }
+
+ @Override
+ public void update(AnActionEvent e) {
+ super.update(e);
+ e.getPresentation().setEnabled(getEventProject(e) != null);
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/actions/NewModuleInGroupAction.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/actions/NewModuleInGroupAction.java
new file mode 100644
index 0000000..68f2460
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/actions/NewModuleInGroupAction.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2000-2009 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.actions;
+
+import com.intellij.ide.projectView.impl.ModuleGroup;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.actionSystem.DataKeys;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.module.impl.ModuleManagerImpl;
+
+/**
+ * @author yole
+ */
+public class NewModuleInGroupAction extends NewModuleAction {
+ @Override
+ public void update(final AnActionEvent e) {
+ super.update(e);
+ final ModuleGroup[] moduleGroups = ModuleGroup.ARRAY_DATA_KEY.getData(e.getDataContext());
+ final Module[] modules = e.getData(DataKeys.MODULE_CONTEXT_ARRAY);
+ e.getPresentation().setVisible((moduleGroups != null && moduleGroups.length > 0) ||
+ (modules != null && modules.length > 0));
+ }
+
+ @Override
+ protected Object prepareDataFromContext(final AnActionEvent e) {
+ final ModuleGroup[] moduleGroups = ModuleGroup.ARRAY_DATA_KEY.getData(e.getDataContext());
+ if (moduleGroups != null && moduleGroups.length > 0) {
+ return moduleGroups [0];
+ }
+ return null;
+ }
+
+ @Override
+ protected void processCreatedModule(final Module module, final Object dataFromContext) {
+ ModuleGroup group = (ModuleGroup) dataFromContext;
+ if (group != null) {
+ ModuleManagerImpl.getInstanceImpl(module.getProject()).setModuleGroupPath(module, group.getGroupPath());
+ }
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/ArtifactConfigurable.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/ArtifactConfigurable.java
new file mode 100644
index 0000000..b352a8c
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/ArtifactConfigurable.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2000-2011 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.artifacts;
+
+import com.intellij.openapi.options.ConfigurationException;
+import com.intellij.openapi.ui.ComboBox;
+import com.intellij.openapi.util.Comparing;
+import com.intellij.packaging.artifacts.Artifact;
+import com.intellij.packaging.artifacts.ArtifactType;
+
+import javax.swing.*;
+import java.awt.*;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+
+/**
+ * @author nik
+ */
+public class ArtifactConfigurable extends ArtifactConfigurableBase {
+ private boolean myIsInUpdateName;
+
+ public ArtifactConfigurable(Artifact originalArtifact, ArtifactsStructureConfigurableContextImpl artifactsStructureContext, final Runnable updateTree) {
+ super(originalArtifact, artifactsStructureContext, updateTree, true);
+ }
+
+ @Override
+ public void setDisplayName(String name) {
+ final String oldName = getArtifact().getName();
+ if (name != null && !name.equals(oldName) && !myIsInUpdateName) {
+ myArtifactsStructureContext.getOrCreateModifiableArtifactModel().getOrCreateModifiableArtifact(myOriginalArtifact).setName(name);
+ getEditor().updateOutputPath(oldName, name);
+ }
+ }
+
+ @Override
+ public void updateName() {
+ myIsInUpdateName = true;
+ try {
+ super.updateName();
+ }
+ finally {
+ myIsInUpdateName = false;
+ }
+ }
+
+ @Override
+ public JComponent createOptionsPanel() {
+ return getEditor().createMainComponent();
+ }
+
+ @Override
+ protected JComponent createTopRightComponent() {
+ final ComboBox artifactTypeBox = new ComboBox();
+ for (ArtifactType type : ArtifactType.getAllTypes()) {
+ artifactTypeBox.addItem(type);
+ }
+
+ artifactTypeBox.setRenderer(new ArtifactTypeCellRenderer(artifactTypeBox.getRenderer()));
+
+ artifactTypeBox.setSelectedItem(getArtifact().getArtifactType());
+ artifactTypeBox.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ final ArtifactType selected = (ArtifactType)artifactTypeBox.getSelectedItem();
+ if (selected != null && !Comparing.equal(selected, getArtifact().getArtifactType())) {
+ getEditor().setArtifactType(selected);
+ }
+ }
+ });
+
+ final JPanel panel = new JPanel(new FlowLayout());
+ panel.add(new JLabel("Type: "));
+ panel.add(artifactTypeBox);
+ return panel;
+ }
+
+ @Override
+ public boolean isModified() {
+ return getEditor().isModified();
+ }
+
+ @Override
+ public void apply() throws ConfigurationException {
+ getEditor().apply();
+ }
+
+ @Override
+ public void reset() {
+ }
+
+ @Override
+ public String getHelpTopic() {
+ return getEditor().getHelpTopic();
+ }
+
+ private ArtifactEditorImpl getEditor() {
+ return myArtifactsStructureContext.getOrCreateEditor(myOriginalArtifact);
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/ArtifactConfigurableBase.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/ArtifactConfigurableBase.java
new file mode 100644
index 0000000..5a902e7
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/ArtifactConfigurableBase.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2000-2010 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.artifacts;
+
+import com.intellij.openapi.project.ProjectBundle;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.ProjectStructureElementConfigurable;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.daemon.ProjectStructureElement;
+import com.intellij.packaging.artifacts.Artifact;
+import org.jetbrains.annotations.Nls;
+
+import javax.swing.*;
+
+/**
+ * @author nik
+ */
+public abstract class ArtifactConfigurableBase extends ProjectStructureElementConfigurable<Artifact> {
+ protected final Artifact myOriginalArtifact;
+ protected final ArtifactsStructureConfigurableContextImpl myArtifactsStructureContext;
+ private final ProjectStructureElement myProjectStructureElement;
+
+ protected ArtifactConfigurableBase(Artifact originalArtifact,
+ ArtifactsStructureConfigurableContextImpl artifactsStructureContext,
+ Runnable updateTree,
+ final boolean nameEditable) {
+ super(nameEditable, updateTree);
+ myOriginalArtifact = originalArtifact;
+ myArtifactsStructureContext = artifactsStructureContext;
+ myProjectStructureElement = myArtifactsStructureContext.getOrCreateArtifactElement(myOriginalArtifact);
+ }
+
+ @Override
+ public ProjectStructureElement getProjectStructureElement() {
+ return myProjectStructureElement;
+ }
+
+ protected Artifact getArtifact() {
+ return myArtifactsStructureContext.getArtifactModel().getArtifactByOriginal(myOriginalArtifact);
+ }
+
+ @Override
+ public Artifact getEditableObject() {
+ return getArtifact();
+ }
+
+ @Override
+ public String getBannerSlogan() {
+ return ProjectBundle.message("banner.slogan.artifact.0", getDisplayName());
+ }
+
+ @Override
+ @Nls
+ public String getDisplayName() {
+ return getArtifact().getName();
+ }
+
+ @Override
+ public Icon getIcon(boolean open) {
+ return getArtifact().getArtifactType().getIcon();
+ }
+
+ @Override
+ public void disposeUIResources() {
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/ArtifactEditorContextImpl.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/ArtifactEditorContextImpl.java
new file mode 100644
index 0000000..fb1ecf9
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/ArtifactEditorContextImpl.java
@@ -0,0 +1,217 @@
+/*
+ * Copyright 2000-2009 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.artifacts;
+
+import com.intellij.facet.Facet;
+import com.intellij.openapi.module.ModifiableModuleModel;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.roots.ModifiableRootModel;
+import com.intellij.openapi.roots.ModuleRootModel;
+import com.intellij.openapi.roots.OrderEntry;
+import com.intellij.openapi.roots.impl.ModuleLibraryOrderEntryImpl;
+import com.intellij.openapi.roots.impl.libraries.LibraryImpl;
+import com.intellij.openapi.roots.libraries.Library;
+import com.intellij.openapi.roots.libraries.LibraryTable;
+import com.intellij.openapi.roots.ui.configuration.ChooseModulesDialog;
+import com.intellij.openapi.roots.ui.configuration.FacetsProvider;
+import com.intellij.openapi.roots.ui.configuration.ModulesProvider;
+import com.intellij.openapi.roots.ui.configuration.ProjectStructureConfigurable;
+import com.intellij.packaging.artifacts.Artifact;
+import com.intellij.packaging.artifacts.ArtifactModel;
+import com.intellij.packaging.artifacts.ArtifactType;
+import com.intellij.packaging.artifacts.ModifiableArtifactModel;
+import com.intellij.packaging.elements.CompositePackagingElement;
+import com.intellij.packaging.elements.ManifestFileProvider;
+import com.intellij.packaging.impl.ui.ChooseArtifactsDialog;
+import com.intellij.packaging.ui.ArtifactEditor;
+import com.intellij.packaging.ui.ArtifactEditorContext;
+import com.intellij.packaging.ui.ManifestFileConfiguration;
+import com.intellij.util.ui.classpath.ChooseLibrariesFromTablesDialog;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * @author nik
+ */
+public class ArtifactEditorContextImpl implements ArtifactEditorContext {
+ private final ArtifactsStructureConfigurableContext myParent;
+ private final ArtifactEditorEx myEditor;
+
+ public ArtifactEditorContextImpl(ArtifactsStructureConfigurableContext parent, ArtifactEditorEx editor) {
+ myParent = parent;
+ myEditor = editor;
+ }
+
+ @Override
+ @NotNull
+ public ModifiableArtifactModel getOrCreateModifiableArtifactModel() {
+ return myParent.getOrCreateModifiableArtifactModel();
+ }
+
+ @Override
+ public ModifiableModuleModel getModifiableModuleModel() {
+ return myParent.getModifiableModuleModel();
+ }
+
+ @Override
+ @NotNull
+ public ModifiableRootModel getOrCreateModifiableRootModel(@NotNull Module module) {
+ return myParent.getOrCreateModifiableRootModel(module);
+ }
+
+ @Override
+ public ManifestFileConfiguration getManifestFile(CompositePackagingElement<?> element, ArtifactType artifactType) {
+ return myParent.getManifestFile(element, artifactType);
+ }
+
+ @Override
+ @NotNull
+ public Project getProject() {
+ return myParent.getProject();
+ }
+
+ @Override
+ public CompositePackagingElement<?> getRootElement(@NotNull Artifact artifact) {
+ return myParent.getRootElement(artifact);
+ }
+
+ @Override
+ public void editLayout(@NotNull Artifact artifact, Runnable runnable) {
+ myParent.editLayout(artifact, runnable);
+ }
+
+ @Override
+ public ArtifactEditor getOrCreateEditor(Artifact artifact) {
+ return myParent.getOrCreateEditor(artifact);
+ }
+
+ @Override
+ public ArtifactEditor getThisArtifactEditor() {
+ return myEditor;
+ }
+
+ @Override
+ public void selectArtifact(@NotNull Artifact artifact) {
+ ProjectStructureConfigurable.getInstance(getProject()).select(artifact, true);
+ }
+
+ @Override
+ public void selectFacet(@NotNull Facet<?> facet) {
+ ProjectStructureConfigurable.getInstance(getProject()).select(facet, true);
+ }
+
+ @Override
+ public void selectModule(@NotNull Module module) {
+ ProjectStructureConfigurable.getInstance(getProject()).select(module.getName(), null, true);
+ }
+
+ @Override
+ public void selectLibrary(@NotNull Library library) {
+ final LibraryTable table = library.getTable();
+ if (table != null) {
+ ProjectStructureConfigurable.getInstance(getProject()).selectProjectOrGlobalLibrary(library, true);
+ }
+ else {
+ final Module module = ((LibraryImpl)library).getModule();
+ if (module != null) {
+ final ModuleRootModel rootModel = myParent.getModulesProvider().getRootModel(module);
+ final String libraryName = library.getName();
+ for (OrderEntry entry : rootModel.getOrderEntries()) {
+ if (entry instanceof ModuleLibraryOrderEntryImpl) {
+ final ModuleLibraryOrderEntryImpl libraryEntry = (ModuleLibraryOrderEntryImpl)entry;
+ if (libraryName != null && libraryName.equals(libraryEntry.getLibraryName())
+ || libraryName == null && library.equals(libraryEntry.getLibrary())) {
+ ProjectStructureConfigurable.getInstance(getProject()).selectOrderEntry(module, libraryEntry);
+ return;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ @Override
+ public List<Artifact> chooseArtifacts(final List<? extends Artifact> artifacts, final String title) {
+ ChooseArtifactsDialog dialog = new ChooseArtifactsDialog(getProject(), artifacts, title, null);
+ dialog.show();
+ return dialog.isOK() ? dialog.getChosenElements() : Collections.<Artifact>emptyList();
+ }
+
+
+ @Override
+ @NotNull
+ public ArtifactModel getArtifactModel() {
+ return myParent.getArtifactModel();
+ }
+
+ @Override
+ @NotNull
+ public ModulesProvider getModulesProvider() {
+ return myParent.getModulesProvider();
+ }
+
+ @Override
+ @NotNull
+ public FacetsProvider getFacetsProvider() {
+ return myParent.getFacetsProvider();
+ }
+
+ @Override
+ public Library findLibrary(@NotNull String level, @NotNull String libraryName) {
+ return myParent.findLibrary(level, libraryName);
+ }
+
+ @NotNull
+ @Override
+ public ManifestFileProvider getManifestFileProvider() {
+ return myParent.getManifestFileProvider();
+ }
+
+ @Override
+ public void queueValidation() {
+ myParent.queueValidation(getArtifact());
+ }
+
+ @Override
+ @NotNull
+ public ArtifactType getArtifactType() {
+ return myEditor.getArtifact().getArtifactType();
+ }
+
+ @Override
+ public List<Module> chooseModules(final List<Module> modules, final String title) {
+ return new ChooseModulesDialog(getProject(), modules, title, null).showAndGetResult();
+ }
+
+ @Override
+ public List<Library> chooseLibraries(final String title) {
+ final ChooseLibrariesFromTablesDialog dialog = ChooseLibrariesFromTablesDialog.createDialog(title, getProject(), false);
+ dialog.show();
+ return dialog.isOK() ? dialog.getSelectedLibraries() : Collections.<Library>emptyList();
+ }
+
+ @Override
+ public Artifact getArtifact() {
+ return myEditor.getArtifact();
+ }
+
+ public ArtifactsStructureConfigurableContext getParent() {
+ return myParent;
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/ArtifactEditorEx.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/ArtifactEditorEx.java
new file mode 100644
index 0000000..a9a3f4f
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/ArtifactEditorEx.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2000-2009 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.artifacts;
+
+import com.intellij.openapi.Disposable;
+import com.intellij.openapi.actionSystem.DataKey;
+import com.intellij.packaging.artifacts.Artifact;
+import com.intellij.packaging.elements.CompositePackagingElement;
+import com.intellij.packaging.elements.PackagingElement;
+import com.intellij.packaging.elements.PackagingElementType;
+import com.intellij.packaging.ui.ArtifactEditor;
+import com.intellij.packaging.ui.ArtifactEditorContext;
+import org.jetbrains.annotations.NotNull;
+
+import javax.swing.*;
+
+/**
+ * @author nik
+ */
+public interface ArtifactEditorEx extends ArtifactEditor, Disposable {
+ DataKey<ArtifactEditorEx> ARTIFACTS_EDITOR_KEY = DataKey.create("artifactsEditor");
+
+
+ void addNewPackagingElement(@NotNull PackagingElementType<?> type);
+
+ void removeSelectedElements();
+
+ void removePackagingElement(@NotNull String pathToParent, @NotNull PackagingElement<?> element);
+
+ void replacePackagingElement(@NotNull String pathToParent, @NotNull PackagingElement<?> element, @NotNull PackagingElement<?> replacement);
+
+ LayoutTreeComponent getLayoutTreeComponent();
+
+ Artifact getArtifact();
+
+ CompositePackagingElement<?> getRootElement();
+
+ ArtifactEditorContext getContext();
+
+ JComponent getMainComponent();
+
+ ComplexElementSubstitutionParameters getSubstitutionParameters();
+
+ void queueValidation();
+
+ void rebuildTries();
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/ArtifactEditorImpl.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/ArtifactEditorImpl.java
new file mode 100644
index 0000000..b2ea31a
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/ArtifactEditorImpl.java
@@ -0,0 +1,540 @@
+/*
+ * Copyright 2000-2009 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.artifacts;
+
+import com.intellij.codeInsight.hint.HintManager;
+import com.intellij.codeInsight.hint.HintUtil;
+import com.intellij.icons.AllIcons;
+import com.intellij.ide.CommonActionsManager;
+import com.intellij.ide.DataManager;
+import com.intellij.ide.DefaultTreeExpander;
+import com.intellij.ide.impl.TypeSafeDataProviderAdapter;
+import com.intellij.openapi.actionSystem.*;
+import com.intellij.openapi.compiler.CompilerBundle;
+import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.project.ProjectBundle;
+import com.intellij.openapi.roots.libraries.Library;
+import com.intellij.openapi.roots.ui.configuration.artifacts.actions.*;
+import com.intellij.openapi.roots.ui.configuration.artifacts.sourceItems.LibrarySourceItem;
+import com.intellij.openapi.roots.ui.configuration.artifacts.sourceItems.ModuleOutputSourceItem;
+import com.intellij.openapi.roots.ui.configuration.artifacts.sourceItems.SourceItemsTree;
+import com.intellij.openapi.ui.FixedSizeButton;
+import com.intellij.openapi.ui.Splitter;
+import com.intellij.openapi.ui.TextFieldWithBrowseButton;
+import com.intellij.openapi.util.Comparing;
+import com.intellij.openapi.util.Disposer;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.packaging.artifacts.Artifact;
+import com.intellij.packaging.artifacts.ArtifactType;
+import com.intellij.packaging.artifacts.ModifiableArtifact;
+import com.intellij.packaging.elements.*;
+import com.intellij.packaging.impl.artifacts.ArtifactUtil;
+import com.intellij.packaging.impl.elements.ArchivePackagingElement;
+import com.intellij.packaging.impl.elements.ManifestFileUtil;
+import com.intellij.packaging.ui.ManifestFileConfiguration;
+import com.intellij.ui.HyperlinkLabel;
+import com.intellij.ui.PopupHandler;
+import com.intellij.ui.ScrollPaneFactory;
+import com.intellij.ui.TabbedPaneWrapper;
+import com.intellij.ui.awt.RelativePoint;
+import com.intellij.util.EventDispatcher;
+import com.intellij.util.IconUtil;
+import com.intellij.util.ui.ThreeStateCheckBox;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import javax.swing.event.HyperlinkEvent;
+import javax.swing.event.HyperlinkListener;
+import java.awt.*;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * @author nik
+ */
+public class ArtifactEditorImpl implements ArtifactEditorEx {
+ private JPanel myMainPanel;
+ private JCheckBox myBuildOnMakeCheckBox;
+ private TextFieldWithBrowseButton myOutputDirectoryField;
+ private JPanel myEditorPanel;
+ private JPanel myErrorPanelPlace;
+ private ThreeStateCheckBox myShowContentCheckBox;
+ private FixedSizeButton myShowSpecificContentOptionsButton;
+ private final ActionGroup myShowSpecificContentOptionsGroup;
+ private final Project myProject;
+ private final ComplexElementSubstitutionParameters mySubstitutionParameters = new ComplexElementSubstitutionParameters();
+ private final EventDispatcher<ArtifactEditorListener> myDispatcher = EventDispatcher.create(ArtifactEditorListener.class);
+ private final ArtifactEditorContextImpl myContext;
+ private final SourceItemsTree mySourceItemsTree;
+ private final Artifact myOriginalArtifact;
+ private final LayoutTreeComponent myLayoutTreeComponent;
+ private TabbedPaneWrapper myTabbedPane;
+ private ArtifactPropertiesEditors myPropertiesEditors;
+ private final ArtifactValidationManagerImpl myValidationManager;
+ private boolean myDisposed;
+
+ public ArtifactEditorImpl(final @NotNull ArtifactsStructureConfigurableContext context, @NotNull Artifact artifact, @NotNull ArtifactEditorSettings settings) {
+ myContext = createArtifactEditorContext(context);
+ myOriginalArtifact = artifact;
+ myProject = context.getProject();
+ mySubstitutionParameters.setTypesToShowContent(settings.getTypesToShowContent());
+ mySourceItemsTree = new SourceItemsTree(myContext, this);
+ myLayoutTreeComponent = new LayoutTreeComponent(this, mySubstitutionParameters, myContext, myOriginalArtifact, settings.isSortElements());
+ myPropertiesEditors = new ArtifactPropertiesEditors(myContext, myOriginalArtifact, myOriginalArtifact);
+ Disposer.register(this, mySourceItemsTree);
+ Disposer.register(this, myLayoutTreeComponent);
+ myBuildOnMakeCheckBox.setSelected(artifact.isBuildOnMake());
+ final String outputPath = artifact.getOutputPath();
+ myOutputDirectoryField.addBrowseFolderListener(CompilerBundle.message("dialog.title.output.directory.for.artifact"),
+ CompilerBundle.message("chooser.description.select.output.directory.for.0.artifact",
+ getArtifact().getName()), myProject,
+ FileChooserDescriptorFactory.createSingleFolderDescriptor());
+ myShowSpecificContentOptionsGroup = createShowSpecificContentOptionsGroup();
+ myShowSpecificContentOptionsButton.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ ActionManager.getInstance().createActionPopupMenu(ActionPlaces.UNKNOWN, myShowSpecificContentOptionsGroup).getComponent().show(myShowSpecificContentOptionsButton, 0, 0);
+ }
+ });
+ setOutputPath(outputPath);
+ myValidationManager = new ArtifactValidationManagerImpl(this);
+ updateShowContentCheckbox();
+ }
+
+ protected ArtifactEditorContextImpl createArtifactEditorContext(ArtifactsStructureConfigurableContext parentContext) {
+ return new ArtifactEditorContextImpl(parentContext, this);
+ }
+
+ private ActionGroup createShowSpecificContentOptionsGroup() {
+ final DefaultActionGroup group = new DefaultActionGroup();
+ for (ComplexPackagingElementType<?> type : PackagingElementFactory.getInstance().getComplexElementTypes()) {
+ group.add(new ToggleShowElementContentAction(type, this));
+ }
+ return group;
+ }
+
+ private void setOutputPath(@Nullable String outputPath) {
+ myOutputDirectoryField.setText(outputPath != null ? FileUtil.toSystemDependentName(outputPath) : null);
+ }
+
+ public void apply() {
+ final ModifiableArtifact modifiableArtifact = myContext.getOrCreateModifiableArtifactModel().getOrCreateModifiableArtifact(myOriginalArtifact);
+ modifiableArtifact.setBuildOnMake(myBuildOnMakeCheckBox.isSelected());
+ modifiableArtifact.setOutputPath(getConfiguredOutputPath());
+ myPropertiesEditors.applyProperties();
+ myLayoutTreeComponent.saveElementProperties();
+ }
+
+ @Nullable
+ private String getConfiguredOutputPath() {
+ String outputPath = FileUtil.toSystemIndependentName(myOutputDirectoryField.getText().trim());
+ if (outputPath.length() == 0) {
+ outputPath = null;
+ }
+ return outputPath;
+ }
+
+ public SourceItemsTree getSourceItemsTree() {
+ return mySourceItemsTree;
+ }
+
+ public void addListener(@NotNull final ArtifactEditorListener listener) {
+ myDispatcher.addListener(listener);
+ }
+
+ @Override
+ public ArtifactEditorContextImpl getContext() {
+ return myContext;
+ }
+
+ public void removeListener(@NotNull final ArtifactEditorListener listener) {
+ myDispatcher.removeListener(listener);
+ }
+
+ @Override
+ public Artifact getArtifact() {
+ return myContext.getArtifactModel().getArtifactByOriginal(myOriginalArtifact);
+ }
+
+ @Override
+ public CompositePackagingElement<?> getRootElement() {
+ return myLayoutTreeComponent.getRootElement();
+ }
+
+ @Override
+ public void rebuildTries() {
+ myLayoutTreeComponent.rebuildTree();
+ mySourceItemsTree.rebuildTree();
+ }
+
+ @Override
+ public void queueValidation() {
+ myContext.queueValidation();
+ }
+
+ public JComponent createMainComponent() {
+ mySourceItemsTree.initTree();
+ myLayoutTreeComponent.initTree();
+ DataManager.registerDataProvider(myMainPanel, new TypeSafeDataProviderAdapter(new MyDataProvider()));
+
+ myErrorPanelPlace.add(myValidationManager.getMainErrorPanel(), BorderLayout.CENTER);
+
+ Splitter splitter = new Splitter(false);
+ final JPanel leftPanel = new JPanel(new BorderLayout());
+ leftPanel.add(myLayoutTreeComponent.getTreePanel(), BorderLayout.CENTER);
+ leftPanel.setBorder(BorderFactory.createEmptyBorder(3, 3, 3, 0));
+ splitter.setFirstComponent(leftPanel);
+
+ final JPanel rightPanel = new JPanel(new BorderLayout());
+ final JPanel rightTopPanel = new JPanel(new BorderLayout());
+ final JPanel labelPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 0, 0));
+ labelPanel.add(new JLabel("Available Elements"));
+ final HyperlinkLabel link = new HyperlinkLabel("");
+ link.setIcon(AllIcons.General.Help);
+ link.setUseIconAsLink(true);
+ link.addHyperlinkListener(new HyperlinkListener() {
+ @Override
+ public void hyperlinkUpdate(HyperlinkEvent e) {
+ if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) {
+ final JLabel label = new JLabel(ProjectBundle.message("artifact.source.items.tree.tooltip"));
+ label.setBorder(HintUtil.createHintBorder());
+ label.setBackground(HintUtil.INFORMATION_COLOR);
+ label.setOpaque(true);
+ HintManager.getInstance().showHint(label, RelativePoint.getSouthEastOf(link), HintManager.HIDE_BY_ANY_KEY | HintManager.HIDE_BY_TEXT_CHANGE, -1);
+ }
+ }
+ });
+ labelPanel.add(link);
+ rightTopPanel.add(labelPanel, BorderLayout.SOUTH);
+ rightPanel.add(rightTopPanel, BorderLayout.NORTH);
+ rightPanel.add(ScrollPaneFactory.createScrollPane(mySourceItemsTree), BorderLayout.CENTER);
+ rightPanel.setBorder(BorderFactory.createEmptyBorder(3, 0, 3, 3));
+ splitter.setSecondComponent(rightPanel);
+
+
+ myShowContentCheckBox.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ final ThreeStateCheckBox.State state = myShowContentCheckBox.getState();
+ if (state == ThreeStateCheckBox.State.SELECTED) {
+ mySubstitutionParameters.setSubstituteAll();
+ }
+ else if (state == ThreeStateCheckBox.State.NOT_SELECTED) {
+ mySubstitutionParameters.setSubstituteNone();
+ }
+ myShowContentCheckBox.setThirdStateEnabled(false);
+ myLayoutTreeComponent.rebuildTree();
+ onShowContentSettingsChanged();
+ }
+ });
+
+ ActionToolbar toolbar = ActionManager.getInstance().createActionToolbar(ActionPlaces.UNKNOWN, createToolbarActionGroup(), true);
+ leftPanel.add(toolbar.getComponent(), BorderLayout.NORTH);
+ toolbar.updateActionsImmediately();
+ rightTopPanel.setPreferredSize(new Dimension(-1, toolbar.getComponent().getPreferredSize().height));
+
+ myTabbedPane = new TabbedPaneWrapper(this);
+ myTabbedPane.addTab("Output Layout", splitter);
+ myPropertiesEditors.addTabs(myTabbedPane);
+ myEditorPanel.add(myTabbedPane.getComponent(), BorderLayout.CENTER);
+
+ final LayoutTree tree = myLayoutTreeComponent.getLayoutTree();
+ new ShowAddPackagingElementPopupAction(this).registerCustomShortcutSet(CommonShortcuts.getNew(), tree);
+ PopupHandler.installPopupHandler(tree, createPopupActionGroup(), ActionPlaces.UNKNOWN, ActionManager.getInstance());
+ ToolTipManager.sharedInstance().registerComponent(tree);
+ rebuildTries();
+ return getMainComponent();
+ }
+
+ private void onShowContentSettingsChanged() {
+ myContext.getParent().getDefaultSettings().setTypesToShowContent(mySubstitutionParameters.getTypesToSubstitute());
+ }
+
+ public void updateShowContentCheckbox() {
+ final ThreeStateCheckBox.State state;
+ if (mySubstitutionParameters.isAllSubstituted()) {
+ state = ThreeStateCheckBox.State.SELECTED;
+ }
+ else if (mySubstitutionParameters.isNoneSubstituted()) {
+ state = ThreeStateCheckBox.State.NOT_SELECTED;
+ }
+ else {
+ state = ThreeStateCheckBox.State.DONT_CARE;
+ }
+ myShowContentCheckBox.setThirdStateEnabled(state == ThreeStateCheckBox.State.DONT_CARE);
+ myShowContentCheckBox.setState(state);
+ onShowContentSettingsChanged();
+ }
+
+ public ArtifactEditorSettings createSettings() {
+ return new ArtifactEditorSettings(myLayoutTreeComponent.isSortElements(), mySubstitutionParameters.getTypesToSubstitute());
+ }
+
+ private DefaultActionGroup createToolbarActionGroup() {
+ final DefaultActionGroup toolbarActionGroup = new DefaultActionGroup();
+
+ final List<AnAction> createActions = new ArrayList<AnAction>(createNewElementActions());
+ for (AnAction createAction : createActions) {
+ toolbarActionGroup.add(createAction);
+ }
+
+ toolbarActionGroup.add(new RemovePackagingElementAction(this));
+ toolbarActionGroup.add(Separator.getInstance());
+ toolbarActionGroup.add(new SortElementsToggleAction(this.getLayoutTreeComponent()));
+ toolbarActionGroup.add(new MovePackagingElementAction(myLayoutTreeComponent, "Move Up", "", IconUtil.getMoveUpIcon(), -1));
+ toolbarActionGroup.add(new MovePackagingElementAction(myLayoutTreeComponent, "Move Down", "", IconUtil.getMoveDownIcon(), 1));
+ return toolbarActionGroup;
+ }
+
+ public List<AnAction> createNewElementActions() {
+ final List<AnAction> createActions = new ArrayList<AnAction>();
+ AddCompositeElementAction.addCompositeCreateActions(createActions, this);
+ createActions.add(createAddNonCompositeElementGroup());
+ return createActions;
+ }
+
+ private DefaultActionGroup createPopupActionGroup() {
+ final LayoutTree tree = myLayoutTreeComponent.getLayoutTree();
+
+ DefaultActionGroup popupActionGroup = new DefaultActionGroup();
+ final List<AnAction> createActions = new ArrayList<AnAction>();
+ AddCompositeElementAction.addCompositeCreateActions(createActions, this);
+ for (AnAction createAction : createActions) {
+ popupActionGroup.add(createAction);
+ }
+ popupActionGroup.add(createAddNonCompositeElementGroup());
+ final RemovePackagingElementAction removeAction = new RemovePackagingElementAction(this);
+ removeAction.registerCustomShortcutSet(CommonShortcuts.DELETE, tree);
+ popupActionGroup.add(removeAction);
+ popupActionGroup.add(new ExtractArtifactAction(this));
+ popupActionGroup.add(new InlineArtifactAction(this));
+ popupActionGroup.add(new RenamePackagingElementAction(this));
+ popupActionGroup.add(new SurroundElementWithAction(this));
+ popupActionGroup.add(Separator.getInstance());
+ popupActionGroup.add(new HideContentAction(this));
+ popupActionGroup.add(new LayoutTreeNavigateAction(myLayoutTreeComponent));
+ popupActionGroup.add(new LayoutTreeFindUsagesAction(myLayoutTreeComponent, myProject, myContext.getParent()));
+
+ popupActionGroup.add(Separator.getInstance());
+ CommonActionsManager actionsManager = CommonActionsManager.getInstance();
+ DefaultTreeExpander treeExpander = new DefaultTreeExpander(tree);
+ popupActionGroup.add(actionsManager.createExpandAllAction(treeExpander, tree));
+ popupActionGroup.add(actionsManager.createCollapseAllAction(treeExpander, tree));
+ return popupActionGroup;
+ }
+
+ @Override
+ public ComplexElementSubstitutionParameters getSubstitutionParameters() {
+ return mySubstitutionParameters;
+ }
+
+ private ActionGroup createAddNonCompositeElementGroup() {
+ DefaultActionGroup group = new DefaultActionGroup(ProjectBundle.message("artifacts.add.copy.action"), true);
+ group.getTemplatePresentation().setIcon(IconUtil.getAddIcon());
+ for (PackagingElementType<?> type : PackagingElementFactory.getInstance().getNonCompositeElementTypes()) {
+ group.add(new AddNewPackagingElementAction(type, this));
+ }
+ return group;
+ }
+
+ @Override
+ public JComponent getMainComponent() {
+ return myMainPanel;
+ }
+
+ @Override
+ public void addNewPackagingElement(@NotNull PackagingElementType<?> type) {
+ myLayoutTreeComponent.addNewPackagingElement(type);
+ mySourceItemsTree.rebuildTree();
+ }
+
+ @Override
+ public void removeSelectedElements() {
+ myLayoutTreeComponent.removeSelectedElements();
+ }
+
+ @Override
+ public void removePackagingElement(@NotNull final String pathToParent, @NotNull final PackagingElement<?> element) {
+ doReplaceElement(pathToParent, element, null);
+ }
+
+ @Override
+ public void replacePackagingElement(@NotNull final String pathToParent,
+ @NotNull final PackagingElement<?> element,
+ @NotNull final PackagingElement<?> replacement) {
+ doReplaceElement(pathToParent, element, replacement);
+ }
+
+ private void doReplaceElement(final @NotNull String pathToParent, final @NotNull PackagingElement<?> element, final @Nullable PackagingElement replacement) {
+ myLayoutTreeComponent.editLayout(new Runnable() {
+ @Override
+ public void run() {
+ final CompositePackagingElement<?> parent = findCompositeElementByPath(pathToParent);
+ if (parent == null) return;
+ for (PackagingElement<?> child : parent.getChildren()) {
+ if (child.isEqualTo(element)) {
+ parent.removeChild(child);
+ if (replacement != null) {
+ parent.addOrFindChild(replacement);
+ }
+ break;
+ }
+ }
+ }
+ });
+ myLayoutTreeComponent.rebuildTree();
+ }
+
+ @Nullable
+ private CompositePackagingElement<?> findCompositeElementByPath(String pathToElement) {
+ CompositePackagingElement<?> element = getRootElement();
+ for (String name : StringUtil.split(pathToElement, "/")) {
+ element = element.findCompositeChild(name);
+ if (element == null) return null;
+ }
+ return element;
+ }
+
+ public boolean isModified() {
+ return myBuildOnMakeCheckBox.isSelected() != myOriginalArtifact.isBuildOnMake()
+ || !Comparing.equal(getConfiguredOutputPath(), myOriginalArtifact.getOutputPath())
+ || myPropertiesEditors.isModified()
+ || myLayoutTreeComponent.isPropertiesModified();
+ }
+
+ @Override
+ public void dispose() {
+ myDisposed = true;
+ }
+
+ @Override
+ public boolean isDisposed() {
+ return myDisposed;
+ }
+
+ @Override
+ public LayoutTreeComponent getLayoutTreeComponent() {
+ return myLayoutTreeComponent;
+ }
+
+ public void updateOutputPath(@NotNull String oldArtifactName, @NotNull final String newArtifactName) {
+ final String oldDefaultPath = ArtifactUtil.getDefaultArtifactOutputPath(oldArtifactName, myProject);
+ if (Comparing.equal(oldDefaultPath, getConfiguredOutputPath())) {
+ setOutputPath(ArtifactUtil.getDefaultArtifactOutputPath(newArtifactName, myProject));
+ final CompositePackagingElement<?> root = getRootElement();
+ if (root instanceof ArchivePackagingElement) {
+ String oldFileName = ArtifactUtil.suggestArtifactFileName(oldArtifactName);
+ final String name = ((ArchivePackagingElement)root).getArchiveFileName();
+ final String fileName = FileUtil.getNameWithoutExtension(name);
+ final String extension = FileUtil.getExtension(name);
+ if (fileName.equals(oldFileName) && extension.length() > 0) {
+ myLayoutTreeComponent.editLayout(new Runnable() {
+ @Override
+ public void run() {
+ ((ArchivePackagingElement)getRootElement()).setArchiveFileName(ArtifactUtil.suggestArtifactFileName(newArtifactName) + "." + extension);
+ }
+ });
+ myLayoutTreeComponent.updateRootNode();
+ }
+ }
+ }
+ }
+
+ @Override
+ public void updateLayoutTree() {
+ myLayoutTreeComponent.rebuildTree();
+ }
+
+ @Override
+ public void putLibraryIntoDefaultLocation(@NotNull Library library) {
+ myLayoutTreeComponent.putIntoDefaultLocations(Collections.singletonList(new LibrarySourceItem(library)));
+ }
+
+ @Override
+ public void putModuleIntoDefaultLocation(@NotNull Module module) {
+ myLayoutTreeComponent.putIntoDefaultLocations(Collections.singletonList(new ModuleOutputSourceItem(module)));
+ }
+
+ @Override
+ public void addToClasspath(final CompositePackagingElement<?> element, List<String> classpath) {
+ myLayoutTreeComponent.saveElementProperties();
+ ManifestFileConfiguration manifest = myContext.getManifestFile(element, getArtifact().getArtifactType());
+ if (manifest == null) {
+ final VirtualFile file = ManifestFileUtil.showDialogAndCreateManifest(myContext, element);
+ if (file == null) {
+ return;
+ }
+
+ ManifestFileUtil.addManifestFileToLayout(file.getPath(), myContext, element);
+ manifest = myContext.getManifestFile(element, getArtifact().getArtifactType());
+ }
+
+ if (manifest != null) {
+ manifest.addToClasspath(classpath);
+ }
+ myLayoutTreeComponent.resetElementProperties();
+ }
+
+ public void setArtifactType(ArtifactType artifactType) {
+ final ModifiableArtifact modifiableArtifact = myContext.getOrCreateModifiableArtifactModel().getOrCreateModifiableArtifact(myOriginalArtifact);
+ modifiableArtifact.setArtifactType(artifactType);
+
+ myPropertiesEditors.removeTabs(myTabbedPane);
+ myPropertiesEditors = new ArtifactPropertiesEditors(myContext, myOriginalArtifact, getArtifact());
+ myPropertiesEditors.addTabs(myTabbedPane);
+
+ final CompositePackagingElement<?> oldRootElement = getRootElement();
+ final CompositePackagingElement<?> newRootElement = artifactType.createRootElement(getArtifact().getName());
+ ArtifactUtil.copyChildren(oldRootElement, newRootElement, myProject);
+ myLayoutTreeComponent.setRootElement(newRootElement);
+ }
+
+ public ArtifactValidationManagerImpl getValidationManager() {
+ return myValidationManager;
+ }
+
+ private void createUIComponents() {
+ myShowContentCheckBox = new ThreeStateCheckBox();
+ myShowSpecificContentOptionsButton = new FixedSizeButton(16);
+ }
+
+ public String getHelpTopic() {
+ final int tab = myTabbedPane.getSelectedIndex();
+ if (tab == 0) {
+ return "reference.project.structure.artifacts.output";
+ }
+ String helpId = ArtifactPropertiesEditors.getHelpId(myTabbedPane.getSelectedTitle());
+ return helpId != null ? helpId : "reference.settingsdialog.project.structure.artifacts";
+ }
+
+ private class MyDataProvider implements TypeSafeDataProvider {
+ @Override
+ public void calcData(DataKey key, DataSink sink) {
+ if (ARTIFACTS_EDITOR_KEY.equals(key)) {
+ sink.put(ARTIFACTS_EDITOR_KEY, ArtifactEditorImpl.this);
+ }
+ }
+ }
+
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/ArtifactEditorListener.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/ArtifactEditorListener.java
new file mode 100644
index 0000000..5d04425
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/ArtifactEditorListener.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2000-2009 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.artifacts;
+
+import java.util.EventListener;
+
+/**
+ * @author nik
+ */
+public interface ArtifactEditorListener extends EventListener {
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/ArtifactEditorManifestFileProvider.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/ArtifactEditorManifestFileProvider.java
new file mode 100644
index 0000000..1735ce9
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/ArtifactEditorManifestFileProvider.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2000-2011 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.artifacts;
+
+import com.intellij.packaging.artifacts.ArtifactType;
+import com.intellij.packaging.elements.CompositePackagingElement;
+import com.intellij.packaging.elements.ManifestFileProvider;
+import com.intellij.packaging.ui.ManifestFileConfiguration;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.List;
+
+/**
+* @author nik
+*/
+public class ArtifactEditorManifestFileProvider implements ManifestFileProvider {
+ private ArtifactsStructureConfigurableContext myArtifactsStructurContext;
+
+ public ArtifactEditorManifestFileProvider(ArtifactsStructureConfigurableContext artifactsStructurContext) {
+ myArtifactsStructurContext = artifactsStructurContext;
+ }
+
+ @Override
+ public List<String> getClasspathFromManifest(@NotNull CompositePackagingElement<?> archiveRoot, @NotNull ArtifactType artifactType) {
+ final ManifestFileConfiguration manifestFile = myArtifactsStructurContext.getManifestFile(archiveRoot, artifactType);
+ return manifestFile != null ? manifestFile.getClasspath() : null;
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/ArtifactEditorSettings.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/ArtifactEditorSettings.java
new file mode 100644
index 0000000..5f2e5ad
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/ArtifactEditorSettings.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2000-2009 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.artifacts;
+
+import com.intellij.openapi.components.PersistentStateComponent;
+import com.intellij.packaging.elements.ComplexPackagingElementType;
+import com.intellij.packaging.elements.PackagingElementFactory;
+import com.intellij.packaging.elements.PackagingElementType;
+import com.intellij.util.xmlb.annotations.AbstractCollection;
+import com.intellij.util.xmlb.annotations.Tag;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * @author nik
+ */
+public class ArtifactEditorSettings implements PersistentStateComponent<ArtifactEditorSettings.ArtifactEditorSettingsState> {
+ private boolean mySortElements = true;
+ private final List<ComplexPackagingElementType<?>> myTypesToShowContent = new ArrayList<ComplexPackagingElementType<?>>();
+
+ public ArtifactEditorSettings() {
+ }
+
+ public ArtifactEditorSettings(boolean sortElements, Collection<ComplexPackagingElementType<?>> typesToShowContent) {
+ mySortElements = sortElements;
+ myTypesToShowContent.addAll(typesToShowContent);
+ }
+
+ @Override
+ public ArtifactEditorSettingsState getState() {
+ final ArtifactEditorSettingsState state = new ArtifactEditorSettingsState();
+ state.mySortElements = mySortElements;
+ for (ComplexPackagingElementType<?> type : myTypesToShowContent) {
+ state.myTypesToShowContentIds.add(type.getId());
+ }
+ return state;
+ }
+
+ @Override
+ public void loadState(ArtifactEditorSettingsState state) {
+ mySortElements = state.mySortElements;
+ myTypesToShowContent.clear();
+ for (String id : state.myTypesToShowContentIds) {
+ final PackagingElementType<?> type = PackagingElementFactory.getInstance().findElementType(id);
+ if (type instanceof ComplexPackagingElementType<?>) {
+ myTypesToShowContent.add((ComplexPackagingElementType<?>)type);
+ }
+ }
+ }
+
+ public boolean isSortElements() {
+ return mySortElements;
+ }
+
+ public List<ComplexPackagingElementType<?>> getTypesToShowContent() {
+ return myTypesToShowContent;
+ }
+
+ public void setSortElements(boolean sortElements) {
+ mySortElements = sortElements;
+ }
+
+ public void setTypesToShowContent(Collection<ComplexPackagingElementType<?>> typesToShowContent) {
+ myTypesToShowContent.clear();
+ myTypesToShowContent.addAll(typesToShowContent);
+ }
+
+ @Tag("artifact-editor")
+ public static class ArtifactEditorSettingsState {
+ @Tag("show-sorted")
+ public boolean mySortElements = true;
+ @Tag("show-content")
+ @AbstractCollection(surroundWithTag = false, elementTag = "type", elementValueAttribute = "id")
+ public List<String> myTypesToShowContentIds = new ArrayList<String>();
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/ArtifactErrorPanel.form b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/ArtifactErrorPanel.form
new file mode 100644
index 0000000..6991fd7
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/ArtifactErrorPanel.form
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.intellij.openapi.roots.ui.configuration.artifacts.ArtifactErrorPanel">
+ <grid id="27dc6" binding="myMainPanel" layout-manager="GridLayoutManager" row-count="1" column-count="3" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+ <margin top="0" left="0" bottom="0" right="0"/>
+ <constraints>
+ <xy x="20" y="20" width="500" height="56"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <component id="6f1fe" class="javax.swing.JLabel" binding="myErrorLabel">
+ <constraints>
+ <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties>
+ <text value="Label"/>
+ </properties>
+ </component>
+ <hspacer id="2859f">
+ <constraints>
+ <grid row="0" column="1" row-span="1" col-span="1" vsize-policy="1" hsize-policy="6" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
+ </constraints>
+ </hspacer>
+ <component id="a4714" class="javax.swing.JButton" binding="myFixButton">
+ <constraints>
+ <grid row="0" column="2" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties>
+ <text value="Button"/>
+ </properties>
+ </component>
+ </children>
+ </grid>
+</form>
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/ArtifactErrorPanel.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/ArtifactErrorPanel.java
new file mode 100644
index 0000000..73749bb
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/ArtifactErrorPanel.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright 2000-2009 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.artifacts;
+
+import com.intellij.icons.AllIcons;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.daemon.ConfigurationErrorQuickFix;
+import com.intellij.openapi.ui.popup.JBPopupFactory;
+import com.intellij.openapi.ui.popup.PopupStep;
+import com.intellij.openapi.ui.popup.util.BaseListPopupStep;
+import com.intellij.util.containers.ContainerUtil;
+import com.intellij.util.ui.update.Activatable;
+import com.intellij.util.ui.update.UiNotifyConnector;
+import org.jetbrains.annotations.NotNull;
+
+import javax.swing.*;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.util.List;
+
+/**
+ * @author nik
+ */
+public class ArtifactErrorPanel {
+ private JPanel myMainPanel;
+ private JButton myFixButton;
+ private JLabel myErrorLabel;
+ private List<? extends ConfigurationErrorQuickFix> myCurrentQuickFixes;
+ private String myErrorText;
+
+ public ArtifactErrorPanel(final ArtifactEditorImpl artifactEditor) {
+ myErrorLabel.setIcon(AllIcons.RunConfigurations.ConfigurationWarning);
+ new UiNotifyConnector(myMainPanel, new Activatable.Adapter() {
+ @Override
+ public void showNotify() {
+ if (myErrorText != null) {
+ myErrorLabel.setText(myErrorText);
+ myErrorText = null;
+ }
+ }
+ });
+ myFixButton.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ if (!myCurrentQuickFixes.isEmpty()) {
+ if (myCurrentQuickFixes.size() == 1) {
+ performFix(ContainerUtil.getFirstItem(myCurrentQuickFixes, null), artifactEditor);
+ }
+ else {
+ JBPopupFactory.getInstance().createListPopup(new BaseListPopupStep<ConfigurationErrorQuickFix>(null, myCurrentQuickFixes) {
+ @NotNull
+ @Override
+ public String getTextFor(ConfigurationErrorQuickFix value) {
+ return value.getActionName();
+ }
+
+ @Override
+ public PopupStep onChosen(ConfigurationErrorQuickFix selectedValue, boolean finalChoice) {
+ performFix(selectedValue, artifactEditor);
+ return FINAL_CHOICE;
+ }
+ }).showUnderneathOf(myFixButton);
+ }
+ }
+ }
+ });
+ clearError();
+ }
+
+ private static void performFix(ConfigurationErrorQuickFix quickFix, ArtifactEditorImpl artifactEditor) {
+ quickFix.performFix();
+ artifactEditor.queueValidation();
+ }
+
+ public void showError(@NotNull String message, @NotNull List<? extends ConfigurationErrorQuickFix> quickFixes) {
+ myErrorLabel.setVisible(true);
+ final String errorText = "<html>" + message + "</html>";
+ if (myErrorLabel.isShowing()) {
+ myErrorLabel.setText(errorText);
+ }
+ else {
+ myErrorText = errorText;
+ }
+ myMainPanel.setVisible(true);
+ myCurrentQuickFixes = quickFixes;
+ myFixButton.setVisible(!quickFixes.isEmpty());
+ if (!quickFixes.isEmpty()) {
+ myFixButton.setText(quickFixes.size() == 1 ? ContainerUtil.getFirstItem(quickFixes, null).getActionName() : "Fix...");
+ }
+ }
+
+ public void clearError() {
+ myErrorText = null;
+ myMainPanel.setVisible(false);
+ myErrorLabel.setVisible(false);
+ myFixButton.setVisible(false);
+ }
+
+ public JComponent getMainPanel() {
+ return myMainPanel;
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/ArtifactProblemDescription.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/ArtifactProblemDescription.java
new file mode 100644
index 0000000..9bdac52
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/ArtifactProblemDescription.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2000-2009 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.artifacts;
+
+import com.intellij.openapi.roots.ui.configuration.projectRoot.daemon.ConfigurationErrorQuickFix;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.daemon.ProjectStructureProblemDescription;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.daemon.ProjectStructureProblemType;
+import com.intellij.packaging.elements.PackagingElement;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.List;
+
+/**
+ * @author nik
+ */
+public class ArtifactProblemDescription extends ProjectStructureProblemDescription {
+ private final List<PackagingElement<?>> myPathToPlace;
+
+ public ArtifactProblemDescription(@NotNull String message, @NotNull ProjectStructureProblemType problemType,
+ @Nullable List<PackagingElement<?>> pathToPlace, @NotNull PlaceInArtifact place,
+ final List<ConfigurationErrorQuickFix> quickFixList) {
+ super(message, null, place, problemType, quickFixList);
+ myPathToPlace = pathToPlace;
+ }
+
+ @Nullable
+ public List<PackagingElement<?>> getPathToPlace() {
+ return myPathToPlace;
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/ArtifactProblemsHolderImpl.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/ArtifactProblemsHolderImpl.java
new file mode 100644
index 0000000..6ba75a0
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/ArtifactProblemsHolderImpl.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2000-2009 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.artifacts;
+
+import com.intellij.openapi.roots.ui.configuration.projectRoot.daemon.ConfigurationErrorQuickFix;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.daemon.ProjectStructureProblemType;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.daemon.ProjectStructureProblemsHolder;
+import com.intellij.packaging.artifacts.Artifact;
+import com.intellij.packaging.elements.PackagingElement;
+import com.intellij.packaging.impl.artifacts.PackagingElementPath;
+import com.intellij.packaging.impl.ui.ArtifactProblemsHolderBase;
+import com.intellij.packaging.ui.ArtifactEditor;
+import com.intellij.packaging.ui.ArtifactProblemQuickFix;
+import com.intellij.util.SmartList;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.List;
+
+/**
+ * @author nik
+ */
+public class ArtifactProblemsHolderImpl extends ArtifactProblemsHolderBase {
+ private final ArtifactsStructureConfigurableContext myContext;
+ private final Artifact myOriginalArtifact;
+ private final ProjectStructureProblemsHolder myProblemsHolder;
+
+ public ArtifactProblemsHolderImpl(ArtifactsStructureConfigurableContext context,
+ Artifact originalArtifact,
+ ProjectStructureProblemsHolder problemsHolder) {
+ super(context);
+ myContext = context;
+ myOriginalArtifact = originalArtifact;
+ myProblemsHolder = problemsHolder;
+ }
+
+ @Override
+ public void registerError(@NotNull String message,
+ @NotNull String problemTypeId,
+ @Nullable List<PackagingElement<?>> pathToPlace,
+ @NotNull ArtifactProblemQuickFix... quickFixes) {
+ registerProblem(message, pathToPlace, ProjectStructureProblemType.error(problemTypeId), quickFixes);
+ }
+
+ @Override
+ public void registerWarning(@NotNull String message,
+ @NotNull String problemTypeId, @Nullable List<PackagingElement<?>> pathToPlace,
+ @NotNull ArtifactProblemQuickFix... quickFixes) {
+ registerProblem(message, pathToPlace, ProjectStructureProblemType.warning(problemTypeId), quickFixes);
+ }
+
+ private void registerProblem(@NotNull String message, @Nullable List<PackagingElement<?>> pathToPlace,
+ final ProjectStructureProblemType problemType, @NotNull ArtifactProblemQuickFix... quickFixes) {
+ String parentPath;
+ PackagingElement<?> element;
+ if (pathToPlace != null && !pathToPlace.isEmpty()) {
+ parentPath = PackagingElementPath.createPath(pathToPlace.subList(1, pathToPlace.size()-1)).getPathString();
+ element = pathToPlace.get(pathToPlace.size() - 1);
+ }
+ else {
+ parentPath = null;
+ element = null;
+ }
+ final Artifact artifact = myContext.getArtifactModel().getArtifactByOriginal(myOriginalArtifact);
+ final PlaceInArtifact place = new PlaceInArtifact(artifact, myContext, parentPath, element);
+ myProblemsHolder.registerProblem(new ArtifactProblemDescription(message, problemType, pathToPlace, place, convertQuickFixes(quickFixes)));
+ }
+
+ private List<ConfigurationErrorQuickFix> convertQuickFixes(ArtifactProblemQuickFix[] quickFixes) {
+ final List<ConfigurationErrorQuickFix> result = new SmartList<ConfigurationErrorQuickFix>();
+ for (final ArtifactProblemQuickFix fix : quickFixes) {
+ result.add(new ConfigurationErrorQuickFix(fix.getActionName()) {
+ @Override
+ public void performFix() {
+ final ArtifactEditor editor = myContext.getOrCreateEditor(myOriginalArtifact);
+ fix.performFix(((ArtifactEditorEx)editor).getContext());
+ }
+ });
+ }
+ return result;
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/ArtifactProjectStructureElement.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/ArtifactProjectStructureElement.java
new file mode 100644
index 0000000..9704d3a
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/ArtifactProjectStructureElement.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright 2000-2009 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.artifacts;
+
+import com.intellij.facet.Facet;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.roots.libraries.Library;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.StructureConfigurableContext;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.daemon.*;
+import com.intellij.packaging.artifacts.Artifact;
+import com.intellij.packaging.elements.CompositePackagingElement;
+import com.intellij.packaging.elements.PackagingElement;
+import com.intellij.packaging.impl.artifacts.ArtifactUtil;
+import com.intellij.packaging.impl.artifacts.PackagingElementPath;
+import com.intellij.packaging.impl.artifacts.PackagingElementProcessor;
+import com.intellij.packaging.impl.elements.ArtifactPackagingElement;
+import com.intellij.packaging.impl.elements.FacetBasedPackagingElement;
+import com.intellij.packaging.impl.elements.LibraryPackagingElement;
+import com.intellij.packaging.impl.elements.ModuleOutputPackagingElement;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author nik
+ */
+public class ArtifactProjectStructureElement extends ProjectStructureElement {
+ private final ArtifactsStructureConfigurableContext myArtifactsStructureContext;
+ private final Artifact myOriginalArtifact;
+
+ ArtifactProjectStructureElement(StructureConfigurableContext context,
+ ArtifactsStructureConfigurableContext artifactsStructureContext, Artifact artifact) {
+ super(context);
+ myArtifactsStructureContext = artifactsStructureContext;
+ myOriginalArtifact = artifactsStructureContext.getOriginalArtifact(artifact);
+ }
+
+ @Override
+ public void check(final ProjectStructureProblemsHolder problemsHolder) {
+ final Artifact artifact = myArtifactsStructureContext.getArtifactModel().getArtifactByOriginal(myOriginalArtifact);
+ final ArtifactProblemsHolderImpl artifactProblemsHolder = new ArtifactProblemsHolderImpl(myArtifactsStructureContext, myOriginalArtifact, problemsHolder);
+ artifact.getArtifactType().checkRootElement(myArtifactsStructureContext.getRootElement(myOriginalArtifact), artifact, artifactProblemsHolder);
+ }
+
+ public Artifact getOriginalArtifact() {
+ return myOriginalArtifact;
+ }
+
+ @Override
+ public List<ProjectStructureElementUsage> getUsagesInElement() {
+ final Artifact artifact = myArtifactsStructureContext.getArtifactModel().getArtifactByOriginal(myOriginalArtifact);
+ final List<ProjectStructureElementUsage> usages = new ArrayList<ProjectStructureElementUsage>();
+ final CompositePackagingElement<?> rootElement = myArtifactsStructureContext.getRootElement(artifact);
+ ArtifactUtil.processPackagingElements(rootElement, null, new PackagingElementProcessor<PackagingElement<?>>() {
+ @Override
+ public boolean process(@NotNull PackagingElement<?> packagingElement, @NotNull PackagingElementPath path) {
+ ProjectStructureElement element = getProjectStructureElementFor(packagingElement, ArtifactProjectStructureElement.this.myContext,
+ ArtifactProjectStructureElement.this.myArtifactsStructureContext);
+ if (element != null) {
+ usages.add(createUsage(packagingElement, element, path.getPathStringFrom("/", rootElement)));
+ }
+ return true;
+ }
+ }, myArtifactsStructureContext, false, artifact.getArtifactType());
+ return usages;
+ }
+
+ @Nullable
+ public static ProjectStructureElement getProjectStructureElementFor(PackagingElement<?> packagingElement,
+ final StructureConfigurableContext context,
+ final ArtifactsStructureConfigurableContext artifactsStructureContext) {
+ if (packagingElement instanceof ModuleOutputPackagingElement) {
+ final Module module = ((ModuleOutputPackagingElement)packagingElement).findModule(artifactsStructureContext);
+ if (module != null) {
+ return new ModuleProjectStructureElement(context, module);
+ }
+ }
+ else if (packagingElement instanceof LibraryPackagingElement) {
+ final Library library = ((LibraryPackagingElement)packagingElement).findLibrary(artifactsStructureContext);
+ if (library != null) {
+ return new LibraryProjectStructureElement(context, library);
+ }
+ }
+ else if (packagingElement instanceof ArtifactPackagingElement) {
+ final Artifact usedArtifact = ((ArtifactPackagingElement)packagingElement).findArtifact(artifactsStructureContext);
+ if (usedArtifact != null) {
+ return artifactsStructureContext.getOrCreateArtifactElement(usedArtifact);
+ }
+ }
+ else if (packagingElement instanceof FacetBasedPackagingElement) {
+ Facet facet = ((FacetBasedPackagingElement)packagingElement).findFacet(artifactsStructureContext);
+ if (facet != null) {
+ return new FacetProjectStructureElement(context, facet);
+ }
+ }
+ return null;
+ }
+
+ private UsageInArtifact createUsage(PackagingElement<?> packagingElement, final ProjectStructureElement element, final String parentPath) {
+ return new UsageInArtifact(myOriginalArtifact, myArtifactsStructureContext, element, this, parentPath, packagingElement);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof ArtifactProjectStructureElement)) return false;
+
+ return myOriginalArtifact.equals(((ArtifactProjectStructureElement)o).myOriginalArtifact);
+
+ }
+
+ @Override
+ public int hashCode() {
+ return myOriginalArtifact.hashCode();
+ }
+
+ @Override
+ public String getPresentableName() {
+ return "Artifact '" + getActualArtifactName() + "'";
+ }
+
+ @Override
+ public String getTypeName() {
+ return "Artifact";
+ }
+
+ @Override
+ public String getId() {
+ return "artifact:" + getActualArtifactName();
+ }
+
+ private String getActualArtifactName() {
+ return myArtifactsStructureContext.getArtifactModel().getArtifactByOriginal(myOriginalArtifact).getName();
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/ArtifactPropertiesEditors.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/ArtifactPropertiesEditors.java
new file mode 100644
index 0000000..53b0bc1
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/ArtifactPropertiesEditors.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright 2000-2009 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.artifacts;
+
+import com.intellij.openapi.ui.VerticalFlowLayout;
+import com.intellij.packaging.artifacts.Artifact;
+import com.intellij.packaging.artifacts.ArtifactProperties;
+import com.intellij.packaging.artifacts.ArtifactPropertiesProvider;
+import com.intellij.packaging.artifacts.ModifiableArtifact;
+import com.intellij.packaging.impl.artifacts.ArtifactUtil;
+import com.intellij.packaging.ui.ArtifactEditorContext;
+import com.intellij.packaging.ui.ArtifactPropertiesEditor;
+import com.intellij.ui.TabbedPaneWrapper;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import java.util.*;
+
+/**
+ * @author nik
+ */
+public class ArtifactPropertiesEditors {
+ private static final List<String> STANDARD_TABS_ORDER = Arrays.asList(
+ ArtifactPropertiesEditor.VALIDATION_TAB, ArtifactPropertiesEditor.PRE_PROCESSING_TAB, ArtifactPropertiesEditor.POST_PROCESSING_TAB
+ );
+ private final Map<String, JPanel> myMainPanels;
+ private final ArtifactEditorContext myContext;
+ private final Artifact myOriginalArtifact;
+ private final List<PropertiesEditorInfo> myEditors;
+
+ public ArtifactPropertiesEditors(ArtifactEditorContext context, Artifact originalArtifact, Artifact artifact) {
+ myContext = context;
+ myOriginalArtifact = originalArtifact;
+ myMainPanels = new HashMap<String, JPanel>();
+ myEditors = new ArrayList<PropertiesEditorInfo>();
+ for (ArtifactPropertiesProvider provider : artifact.getPropertiesProviders()) {
+ final PropertiesEditorInfo editorInfo = new PropertiesEditorInfo(provider);
+ myEditors.add(editorInfo);
+ final String tabName = editorInfo.myEditor.getTabName();
+ JPanel panel = myMainPanels.get(tabName);
+ if (panel == null) {
+ panel = new JPanel(new VerticalFlowLayout());
+ myMainPanels.put(tabName, panel);
+ }
+ panel.add(editorInfo.myEditor.createComponent());
+ }
+ }
+
+ public void applyProperties() {
+ for (PropertiesEditorInfo editor : myEditors) {
+ if (editor.isModified()) {
+ editor.applyProperties();
+ }
+ }
+ }
+
+ public void addTabs(TabbedPaneWrapper tabbedPane) {
+ List<String> sortedTabs = new ArrayList<String>(myMainPanels.keySet());
+ Collections.sort(sortedTabs, new Comparator<String>() {
+ @Override
+ public int compare(String o1, String o2) {
+ int i1 = STANDARD_TABS_ORDER.indexOf(o1);
+ if (i1 == -1) i1 = STANDARD_TABS_ORDER.size();
+ int i2 = STANDARD_TABS_ORDER.indexOf(o2);
+ if (i2 == -1) i2 = STANDARD_TABS_ORDER.size();
+ if (i1 != i2) {
+ return i1 - i2;
+ }
+ return o1.compareTo(o2);
+ }
+ });
+ for (String tab : sortedTabs) {
+ tabbedPane.addTab(tab, myMainPanels.get(tab));
+ }
+ }
+
+ public boolean isModified() {
+ for (PropertiesEditorInfo editor : myEditors) {
+ if (editor.isModified()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public void removeTabs(TabbedPaneWrapper tabbedPane) {
+ for (String tabName : myMainPanels.keySet()) {
+ for (int i = 0; i < tabbedPane.getTabCount(); i++) {
+ if (tabName.equals(tabbedPane.getTitleAt(i))) {
+ tabbedPane.removeTabAt(i);
+ break;
+ }
+ }
+ }
+ }
+
+ @Nullable
+ public static String getHelpId(String title) {
+ if (ArtifactPropertiesEditor.VALIDATION_TAB.equals(title)) {
+ return "reference.project.structure.artifacts.validation";
+ }
+ else if (ArtifactPropertiesEditor.PRE_PROCESSING_TAB.equals(title)) {
+ return "reference.project.structure.artifacts.preprocessing";
+ }
+ else if (ArtifactPropertiesEditor.POST_PROCESSING_TAB.equals(title)) {
+ return "reference.project.structure.artifacts.postprocessing";
+ }
+ return null;
+ }
+
+ private class PropertiesEditorInfo {
+ private final ArtifactPropertiesEditor myEditor;
+ private final ArtifactProperties<?> myProperties;
+ private final ArtifactPropertiesProvider myProvider;
+
+ private PropertiesEditorInfo(ArtifactPropertiesProvider provider) {
+ myProvider = provider;
+ myProperties = provider.createProperties(myOriginalArtifact.getArtifactType());
+ final ArtifactProperties<?> originalProperties = myOriginalArtifact.getProperties(provider);
+ if (originalProperties != null) {
+ ArtifactUtil.copyProperties(originalProperties, myProperties);
+ }
+ myEditor = myProperties.createEditor(myContext);
+ myEditor.reset();
+ }
+
+ public void applyProperties() {
+ myEditor.apply();
+ final ModifiableArtifact artifact = myContext.getOrCreateModifiableArtifactModel().getOrCreateModifiableArtifact(myOriginalArtifact);
+ artifact.setProperties(myProvider, myProperties);
+ }
+
+ public boolean isModified() {
+ return myEditor.isModified();
+ }
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/ArtifactStructureConfigurableState.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/ArtifactStructureConfigurableState.java
new file mode 100644
index 0000000..cd94f36
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/ArtifactStructureConfigurableState.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2000-2010 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.artifacts;
+
+import com.intellij.openapi.ui.MasterDetailsState;
+import com.intellij.util.xmlb.annotations.Property;
+
+/**
+ * @author nik
+ */
+public class ArtifactStructureConfigurableState extends MasterDetailsState {
+ private ArtifactEditorSettings.ArtifactEditorSettingsState myDefaultArtifactSettings = new ArtifactEditorSettings.ArtifactEditorSettingsState();
+
+ @Property(surroundWithTag = false)
+ public ArtifactEditorSettings.ArtifactEditorSettingsState getDefaultArtifactSettings() {
+ return myDefaultArtifactSettings;
+ }
+
+ public void setDefaultArtifactSettings(ArtifactEditorSettings.ArtifactEditorSettingsState defaultArtifactSettings) {
+ myDefaultArtifactSettings = defaultArtifactSettings;
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/ArtifactTypeCellRenderer.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/ArtifactTypeCellRenderer.java
new file mode 100644
index 0000000..7817a38
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/ArtifactTypeCellRenderer.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2000-2012 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.artifacts;
+
+import com.intellij.ui.ListCellRendererWrapper;
+import com.intellij.packaging.artifacts.ArtifactType;
+
+import javax.swing.*;
+
+/**
+ * @author nik
+ */
+public class ArtifactTypeCellRenderer extends ListCellRendererWrapper<ArtifactType> {
+ public ArtifactTypeCellRenderer(final ListCellRenderer listCellRenderer) {
+ super();
+ }
+
+ @Override
+ public void customize(JList list, ArtifactType type, int index, boolean selected, boolean hasFocus) {
+ setIcon(type.getIcon());
+ setText(type.getPresentableName());
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/ArtifactValidationManagerImpl.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/ArtifactValidationManagerImpl.java
new file mode 100644
index 0000000..cca4555
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/ArtifactValidationManagerImpl.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2000-2009 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.artifacts;
+
+import com.intellij.openapi.Disposable;
+import com.intellij.openapi.roots.ui.configuration.artifacts.nodes.PackagingElementNode;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.daemon.ConfigurationErrorQuickFix;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.daemon.ProjectStructureProblemDescription;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.daemon.ProjectStructureProblemsHolderImpl;
+import com.intellij.openapi.util.Disposer;
+import com.intellij.openapi.util.MultiValuesMap;
+import com.intellij.packaging.elements.PackagingElement;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+/**
+* @author nik
+*/
+public class ArtifactValidationManagerImpl implements Disposable {
+ private final ArtifactErrorPanel myErrorPanel;
+ private final ArtifactEditorImpl myArtifactEditor;
+ private final MultiValuesMap<PackagingElementNode<?>, ArtifactProblemDescription> myProblemsForNodes = new MultiValuesMap<PackagingElementNode<?>, ArtifactProblemDescription>(true);
+ private final List<ArtifactProblemDescription> myProblems = new ArrayList<ArtifactProblemDescription>();
+
+ ArtifactValidationManagerImpl(ArtifactEditorImpl artifactEditor) {
+ Disposer.register(artifactEditor, this);
+ myArtifactEditor = artifactEditor;
+ myErrorPanel = new ArtifactErrorPanel(artifactEditor);
+ }
+
+ @Override
+ public void dispose() {
+ }
+
+ public JComponent getMainErrorPanel() {
+ return myErrorPanel.getMainPanel();
+ }
+
+ public void onNodesAdded() {
+ for (ArtifactProblemDescription problem : myProblems) {
+ showProblemInTree(problem);
+ }
+ }
+
+ @Nullable
+ public Collection<ArtifactProblemDescription> getProblems(PackagingElementNode<?> node) {
+ return myProblemsForNodes.get(node);
+ }
+
+ public void updateProblems(@Nullable ProjectStructureProblemsHolderImpl holder) {
+ myErrorPanel.clearError();
+ myProblemsForNodes.clear();
+ myProblems.clear();
+ if (holder != null) {
+ final List<ProjectStructureProblemDescription> problemDescriptions = holder.getProblemDescriptions();
+ if (problemDescriptions != null) {
+ for (ProjectStructureProblemDescription description : problemDescriptions) {
+ final String message = description.getMessage(false);
+ List<? extends ConfigurationErrorQuickFix> quickFixes = Collections.emptyList();
+ if (description instanceof ArtifactProblemDescription) {
+ final ArtifactProblemDescription artifactProblem = (ArtifactProblemDescription)description;
+ quickFixes = artifactProblem.getFixes();
+ if (artifactProblem.getPathToPlace() != null) {
+ myProblems.add(artifactProblem);
+ showProblemInTree(artifactProblem);
+ }
+ }
+ myErrorPanel.showError(message, quickFixes);
+ }
+ }
+ }
+ myArtifactEditor.getLayoutTreeComponent().updateTreeNodesPresentation();
+ }
+
+ private void showProblemInTree(ArtifactProblemDescription problem) {
+ final LayoutTree layoutTree = myArtifactEditor.getLayoutTreeComponent().getLayoutTree();
+ PackagingElementNode<?> node = layoutTree.getRootPackagingNode();
+ final List<PackagingElement<?>> pathToPlace = problem.getPathToPlace();
+ if (node != null && pathToPlace != null) {
+ List<PackagingElementNode<?>> nodes = node.getNodesByPath(pathToPlace.subList(1, pathToPlace.size()));
+ for (PackagingElementNode<?> elementNode : nodes) {
+ myProblemsForNodes.put(elementNode, problem);
+ }
+ }
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/ArtifactsEditorImpl.form b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/ArtifactsEditorImpl.form
new file mode 100644
index 0000000..d3edac7
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/ArtifactsEditorImpl.form
@@ -0,0 +1,107 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.intellij.openapi.roots.ui.configuration.artifacts.ArtifactEditorImpl">
+ <grid id="27dc6" binding="myMainPanel" layout-manager="GridLayoutManager" row-count="4" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+ <margin top="0" left="0" bottom="0" right="0"/>
+ <constraints>
+ <xy x="20" y="20" width="500" height="400"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <grid id="6e544" layout-manager="GridLayoutManager" row-count="2" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+ <margin top="0" left="0" bottom="0" right="0"/>
+ <constraints>
+ <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="1" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <component id="4f4d6" class="javax.swing.JLabel">
+ <constraints>
+ <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties>
+ <text resource-bundle="messages/ProjectBundle" key="label.text.output.directory"/>
+ </properties>
+ </component>
+ <component id="a8018" class="com.intellij.openapi.ui.TextFieldWithBrowseButton" binding="myOutputDirectoryField">
+ <constraints>
+ <grid row="0" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties/>
+ </component>
+ <grid id="a20c9" layout-manager="GridLayoutManager" row-count="1" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+ <margin top="0" left="0" bottom="0" right="0"/>
+ <constraints>
+ <grid row="1" column="0" row-span="1" col-span="2" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <component id="44b97" class="javax.swing.JCheckBox" binding="myBuildOnMakeCheckBox">
+ <constraints>
+ <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties>
+ <text resource-bundle="messages/ProjectBundle" key="checkbox.text.build.on.make"/>
+ </properties>
+ </component>
+ <hspacer id="7bcaa">
+ <constraints>
+ <grid row="0" column="1" row-span="1" col-span="1" vsize-policy="1" hsize-policy="6" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
+ </constraints>
+ </hspacer>
+ </children>
+ </grid>
+ </children>
+ </grid>
+ <grid id="b1ae" binding="myEditorPanel" layout-manager="BorderLayout" hgap="0" vgap="0">
+ <constraints>
+ <grid row="1" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties>
+ <enabled value="true"/>
+ </properties>
+ <border type="none"/>
+ <children/>
+ </grid>
+ <grid id="5c105" binding="myErrorPanelPlace" layout-manager="BorderLayout" hgap="0" vgap="0">
+ <constraints>
+ <grid row="3" column="0" row-span="1" col-span="1" vsize-policy="1" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children/>
+ </grid>
+ <grid id="7b56f" layout-manager="GridLayoutManager" row-count="1" column-count="3" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+ <margin top="0" left="0" bottom="0" right="0"/>
+ <constraints>
+ <grid row="2" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <component id="4157d" class="com.intellij.util.ui.ThreeStateCheckBox" binding="myShowContentCheckBox" custom-create="true">
+ <constraints>
+ <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties>
+ <text value="Show content of elements"/>
+ </properties>
+ </component>
+ <component id="d5f3d" class="com.intellij.openapi.ui.FixedSizeButton" binding="myShowSpecificContentOptionsButton" custom-create="true">
+ <constraints>
+ <grid row="0" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="0" fill="0" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties/>
+ </component>
+ <hspacer id="eeea0">
+ <constraints>
+ <grid row="0" column="2" row-span="1" col-span="1" vsize-policy="1" hsize-policy="6" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
+ </constraints>
+ </hspacer>
+ </children>
+ </grid>
+ </children>
+ </grid>
+</form>
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
new file mode 100644
index 0000000..52b601e
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/ArtifactsStructureConfigurable.java
@@ -0,0 +1,390 @@
+/*
+ * Copyright 2000-2009 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.artifacts;
+
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.actionSystem.DefaultActionGroup;
+import com.intellij.openapi.application.Result;
+import com.intellij.openapi.application.WriteAction;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.options.ConfigurationException;
+import com.intellij.openapi.project.DumbAwareAction;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.project.ProjectBundle;
+import com.intellij.openapi.roots.ModifiableRootModel;
+import com.intellij.openapi.roots.impl.libraries.LibraryTableImplUtil;
+import com.intellij.openapi.roots.libraries.Library;
+import com.intellij.openapi.roots.libraries.LibraryTable;
+import com.intellij.openapi.roots.ui.configuration.ModuleEditor;
+import com.intellij.openapi.roots.ui.configuration.libraryEditor.LibraryEditorListener;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.*;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.daemon.ProjectStructureElement;
+import com.intellij.openapi.ui.MasterDetailsState;
+import com.intellij.openapi.ui.NamedConfigurable;
+import com.intellij.packaging.artifacts.*;
+import com.intellij.packaging.impl.artifacts.ArtifactUtil;
+import com.intellij.packaging.impl.artifacts.InvalidArtifact;
+import com.intellij.packaging.impl.artifacts.PackagingElementPath;
+import com.intellij.packaging.impl.artifacts.PackagingElementProcessor;
+import com.intellij.packaging.impl.elements.LibraryElementType;
+import com.intellij.packaging.impl.elements.LibraryPackagingElement;
+import org.jetbrains.annotations.Nls;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * @author nik
+ */
+public class ArtifactsStructureConfigurable extends BaseStructureConfigurable {
+ private ArtifactsStructureConfigurableContextImpl myPackagingEditorContext;
+ private final ArtifactEditorSettings myDefaultSettings = new ArtifactEditorSettings();
+
+ public ArtifactsStructureConfigurable(@NotNull Project project) {
+ super(project, new ArtifactStructureConfigurableState());
+ }
+
+ @Override
+ protected String getComponentStateKey() {
+ return "ArtifactsStructureConfigurable.UI";
+ }
+
+ public void init(StructureConfigurableContext context, ModuleStructureConfigurable moduleStructureConfigurable,
+ ProjectLibrariesConfigurable projectLibrariesConfig, GlobalLibrariesConfigurable globalLibrariesConfig) {
+ super.init(context);
+ myPackagingEditorContext = new ArtifactsStructureConfigurableContextImpl(myContext, myProject, myDefaultSettings, new ArtifactAdapter() {
+ @Override
+ public void artifactAdded(@NotNull Artifact artifact) {
+ final MyNode node = addArtifactNode(artifact);
+ selectNodeInTree(node);
+ myContext.getDaemonAnalyzer().queueUpdate(myPackagingEditorContext.getOrCreateArtifactElement(artifact));
+ }
+ });
+
+ context.getModulesConfigurator().addAllModuleChangeListener(new ModuleEditor.ChangeListener() {
+ @Override
+ public void moduleStateChanged(ModifiableRootModel moduleRootModel) {
+ for (ProjectStructureElement element : getProjectStructureElements()) {
+ myContext.getDaemonAnalyzer().queueUpdate(element);
+ }
+ }
+ });
+
+ final ItemsChangeListener listener = new ItemsChangeListener() {
+ @Override
+ public void itemChanged(@Nullable Object deletedItem) {
+ if (deletedItem instanceof Library || deletedItem instanceof Module) {
+ onElementDeleted();
+ }
+ }
+
+ @Override
+ public void itemsExternallyChanged() {
+ }
+ };
+ moduleStructureConfigurable.addItemsChangeListener(listener);
+ projectLibrariesConfig.addItemsChangeListener(listener);
+ globalLibrariesConfig.addItemsChangeListener(listener);
+
+ context.addLibraryEditorListener(new LibraryEditorListener() {
+ @Override
+ public void libraryRenamed(@NotNull Library library, String oldName, String newName) {
+ final Artifact[] artifacts = myPackagingEditorContext.getArtifactModel().getArtifacts();
+ for (Artifact artifact : artifacts) {
+ updateLibraryElements(artifact, library, oldName, newName);
+ }
+ }
+
+ });
+ }
+
+ private void updateLibraryElements(final Artifact artifact, final Library library, final String oldName, final String newName) {
+ if (ArtifactUtil.processPackagingElements(myPackagingEditorContext.getRootElement(artifact), LibraryElementType.LIBRARY_ELEMENT_TYPE,
+ new PackagingElementProcessor<LibraryPackagingElement>() {
+ @Override
+ public boolean process(@NotNull LibraryPackagingElement element,
+ @NotNull PackagingElementPath path) {
+ return !isResolvedToLibrary(element, library, oldName);
+ }
+ }, myPackagingEditorContext, false, artifact.getArtifactType())) {
+ return;
+ }
+ myPackagingEditorContext.editLayout(artifact, new Runnable() {
+ @Override
+ public void run() {
+ final ModifiableArtifact modifiableArtifact = myPackagingEditorContext.getOrCreateModifiableArtifactModel().getOrCreateModifiableArtifact(artifact);
+ ArtifactUtil.processPackagingElements(modifiableArtifact, LibraryElementType.LIBRARY_ELEMENT_TYPE, new PackagingElementProcessor<LibraryPackagingElement>() {
+ @Override
+ public boolean process(@NotNull LibraryPackagingElement element, @NotNull PackagingElementPath path) {
+ if (isResolvedToLibrary(element, library, oldName)) {
+ element.setLibraryName(newName);
+ }
+ return true;
+ }
+ }, myPackagingEditorContext, false);
+ }
+ });
+ final ArtifactEditorImpl artifactEditor = myPackagingEditorContext.getArtifactEditor(artifact);
+ if (artifactEditor != null) {
+ artifactEditor.rebuildTries();
+ }
+ }
+
+ private static boolean isResolvedToLibrary(LibraryPackagingElement element, Library library, String name) {
+ if (!element.getLibraryName().equals(name)) {
+ return false;
+ }
+
+ final LibraryTable table = library.getTable();
+ if (table != null) {
+ return table.getTableLevel().equals(element.getLevel());
+ }
+ return element.getLevel().equals(LibraryTableImplUtil.MODULE_LEVEL);
+ }
+
+ private void onElementDeleted() {
+ for (ArtifactEditorImpl editor : myPackagingEditorContext.getArtifactEditors()) {
+ editor.getSourceItemsTree().rebuildTree();
+ editor.queueValidation();
+ }
+ }
+
+ @Override
+ protected MasterDetailsState getState() {
+ ((ArtifactStructureConfigurableState)myState).setDefaultArtifactSettings(myDefaultSettings.getState());
+ return super.getState();
+ }
+
+ @Override
+ public void loadState(MasterDetailsState object) {
+ super.loadState(object);
+ myDefaultSettings.loadState(((ArtifactStructureConfigurableState)myState).getDefaultArtifactSettings());
+ }
+
+ @Override
+ @Nls
+ public String getDisplayName() {
+ return ProjectBundle.message("display.name.artifacts");
+ }
+
+ @Override
+ protected void loadTree() {
+ myTree.setRootVisible(false);
+ myTree.setShowsRootHandles(false);
+ for (Artifact artifact : myPackagingEditorContext.getArtifactModel().getAllArtifactsIncludingInvalid()) {
+ addArtifactNode(artifact);
+ }
+ }
+
+ @NotNull
+ @Override
+ protected Collection<? extends ProjectStructureElement> getProjectStructureElements() {
+ final List<ProjectStructureElement> elements = new ArrayList<ProjectStructureElement>();
+ for (Artifact artifact : myPackagingEditorContext.getArtifactModel().getAllArtifactsIncludingInvalid()) {
+ elements.add(myPackagingEditorContext.getOrCreateArtifactElement(artifact));
+ }
+ return elements;
+ }
+
+ private MyNode addArtifactNode(final Artifact artifact) {
+ final NamedConfigurable<Artifact> configurable;
+ if (artifact instanceof InvalidArtifact) {
+ configurable = new InvalidArtifactConfigurable((InvalidArtifact)artifact, myPackagingEditorContext, TREE_UPDATER);
+ }
+ else {
+ configurable = new ArtifactConfigurable(artifact, myPackagingEditorContext, TREE_UPDATER);
+ }
+ final MyNode node = new MyNode(configurable);
+ addNode(node, myRoot);
+ return node;
+ }
+
+ @Override
+ public void reset() {
+ loadComponentState();
+ myPackagingEditorContext.resetModifiableModel();
+ super.reset();
+ }
+
+ @Override
+ public boolean isModified() {
+ final ModifiableArtifactModel modifiableModel = myPackagingEditorContext.getActualModifiableModel();
+ if (modifiableModel != null && modifiableModel.isModified()) {
+ return true;
+ }
+ return myPackagingEditorContext.getManifestFilesInfo().isManifestFilesModified() || super.isModified();
+ }
+
+ public ArtifactsStructureConfigurableContext getArtifactsStructureContext() {
+ return myPackagingEditorContext;
+ }
+
+ public ModifiableArtifactModel getModifiableArtifactModel() {
+ return myPackagingEditorContext.getOrCreateModifiableArtifactModel();
+ }
+
+ @Override
+ protected AbstractAddGroup createAddAction() {
+ return new AbstractAddGroup(ProjectBundle.message("add.new.header.text")) {
+ @NotNull
+ @Override
+ public AnAction[] getChildren(@Nullable AnActionEvent e) {
+ final ArtifactType[] types = ArtifactType.getAllTypes();
+ final AnAction[] actions = new AnAction[types.length];
+ for (int i = 0; i < types.length; i++) {
+ actions[i] = createAddArtifactAction(types[i]);
+ }
+ return actions;
+ }
+ };
+ }
+
+ private AnAction createAddArtifactAction(@NotNull final ArtifactType type) {
+ final List<? extends ArtifactTemplate> templates = type.getNewArtifactTemplates(myPackagingEditorContext);
+ final ArtifactTemplate emptyTemplate = new ArtifactTemplate() {
+ @Override
+ public String getPresentableName() {
+ return "Empty";
+ }
+
+ @Override
+ public NewArtifactConfiguration createArtifact() {
+ final String name = "unnamed";
+ return new NewArtifactConfiguration(type.createRootElement(name), name, type);
+ }
+ };
+
+ if (templates.isEmpty()) {
+ return new AddArtifactAction(type, emptyTemplate, type.getPresentableName(), type.getIcon());
+ }
+ final DefaultActionGroup group = new DefaultActionGroup(type.getPresentableName(), true);
+ group.getTemplatePresentation().setIcon(type.getIcon());
+ group.add(new AddArtifactAction(type, emptyTemplate, emptyTemplate.getPresentableName(), null));
+ group.addSeparator();
+ for (ArtifactTemplate template : templates) {
+ group.add(new AddArtifactAction(type, template, template.getPresentableName(), null));
+ }
+ return group;
+ }
+
+ private void addArtifact(@NotNull ArtifactType type, @NotNull ArtifactTemplate artifactTemplate) {
+ final ArtifactTemplate.NewArtifactConfiguration configuration = artifactTemplate.createArtifact();
+ if (configuration == null) {
+ return;
+ }
+
+ final String baseName = configuration.getArtifactName();
+ String name = baseName;
+ int i = 2;
+ while (myPackagingEditorContext.getArtifactModel().findArtifact(name) != null) {
+ name = baseName + i;
+ i++;
+ }
+
+ ArtifactType actualType = configuration.getArtifactType();
+ if (actualType == null) {
+ actualType = type;
+ }
+ final ModifiableArtifact artifact = myPackagingEditorContext.getOrCreateModifiableArtifactModel().addArtifact(name, actualType, configuration.getRootElement());
+ artifactTemplate.setUpArtifact(artifact, configuration);
+ selectNodeInTree(findNodeByObject(myRoot, artifact));
+ }
+
+ @Override
+ public void apply() throws ConfigurationException {
+ myPackagingEditorContext.saveEditorSettings();
+ super.apply();
+
+ myPackagingEditorContext.getManifestFilesInfo().saveManifestFiles();
+ final ModifiableArtifactModel modifiableModel = myPackagingEditorContext.getActualModifiableModel();
+ if (modifiableModel != null) {
+ new WriteAction() {
+ @Override
+ protected void run(final Result result) {
+ modifiableModel.commit();
+ }
+ }.execute();
+ myPackagingEditorContext.resetModifiableModel();
+ }
+
+ reset(); // TODO: fix to not reset on apply!
+ }
+
+ @Override
+ public void disposeUIResources() {
+ myPackagingEditorContext.saveEditorSettings();
+ super.disposeUIResources();
+ myPackagingEditorContext.disposeUIResources();
+ }
+
+ @Override
+ public String getHelpTopic() {
+ final String topic = super.getHelpTopic();
+ return topic != null ? topic : "reference.settingsdialog.project.structure.artifacts";
+ }
+
+ @Override
+ protected void removeArtifact(Artifact artifact) {
+ myPackagingEditorContext.getOrCreateModifiableArtifactModel().removeArtifact(artifact);
+ myContext.getDaemonAnalyzer().removeElement(myPackagingEditorContext.getOrCreateArtifactElement(artifact));
+ }
+
+ @Override
+ protected void processRemovedItems() {
+ }
+
+ @Override
+ protected boolean wasObjectStored(Object editableObject) {
+ return false;
+ }
+
+ @Override
+ @NotNull
+ public String getId() {
+ return "project.artifacts";
+ }
+
+ @Override
+ public Runnable enableSearch(String option) {
+ return null;
+ }
+
+ @Override
+ public void dispose() {
+ }
+
+ private class AddArtifactAction extends DumbAwareAction {
+ private final ArtifactType myType;
+ private final ArtifactTemplate myArtifactTemplate;
+
+ public AddArtifactAction(@NotNull ArtifactType type, @NotNull ArtifactTemplate artifactTemplate, final @NotNull String actionText,
+ final Icon icon) {
+ super(actionText, null, icon);
+ myType = type;
+ myArtifactTemplate = artifactTemplate;
+ }
+
+ @Override
+ public void actionPerformed(AnActionEvent e) {
+ addArtifact(myType, myArtifactTemplate);
+ }
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/ArtifactsStructureConfigurableContext.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/ArtifactsStructureConfigurableContext.java
new file mode 100644
index 0000000..4374842
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/ArtifactsStructureConfigurableContext.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2000-2009 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.artifacts;
+
+import com.intellij.openapi.module.ModifiableModuleModel;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.roots.ModifiableRootModel;
+import com.intellij.packaging.artifacts.Artifact;
+import com.intellij.packaging.artifacts.ArtifactType;
+import com.intellij.packaging.artifacts.ModifiableArtifactModel;
+import com.intellij.packaging.elements.CompositePackagingElement;
+import com.intellij.packaging.elements.PackagingElementResolvingContext;
+import com.intellij.packaging.ui.ArtifactEditor;
+import com.intellij.packaging.ui.ManifestFileConfiguration;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * @author nik
+ */
+public interface ArtifactsStructureConfigurableContext extends PackagingElementResolvingContext {
+ @NotNull
+ ModifiableArtifactModel getOrCreateModifiableArtifactModel();
+
+ @Nullable
+ ManifestFileConfiguration getManifestFile(CompositePackagingElement<?> element, ArtifactType artifactType);
+
+ CompositePackagingElement<?> getRootElement(@NotNull Artifact artifact);
+
+ void editLayout(@NotNull Artifact artifact, Runnable action);
+
+ ArtifactEditor getOrCreateEditor(Artifact artifact);
+
+ @NotNull
+ Artifact getOriginalArtifact(@NotNull Artifact artifact);
+
+ @Nullable
+ ModifiableModuleModel getModifiableModuleModel();
+
+ void queueValidation(Artifact artifact);
+
+ @NotNull
+ ArtifactProjectStructureElement getOrCreateArtifactElement(@NotNull Artifact artifact);
+
+ ModifiableRootModel getOrCreateModifiableRootModel(Module module);
+
+ ArtifactEditorSettings getDefaultSettings();
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/ArtifactsStructureConfigurableContextImpl.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/ArtifactsStructureConfigurableContextImpl.java
new file mode 100644
index 0000000..5ff6700
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/ArtifactsStructureConfigurableContextImpl.java
@@ -0,0 +1,272 @@
+/*
+ * Copyright 2000-2009 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.artifacts;
+
+import com.intellij.openapi.application.Result;
+import com.intellij.openapi.application.WriteAction;
+import com.intellij.openapi.module.ModifiableModuleModel;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.roots.ModifiableRootModel;
+import com.intellij.openapi.roots.libraries.Library;
+import com.intellij.openapi.roots.ui.configuration.FacetsProvider;
+import com.intellij.openapi.roots.ui.configuration.ModuleEditor;
+import com.intellij.openapi.roots.ui.configuration.ModulesProvider;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.StructureConfigurableContext;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.daemon.ProjectStructureDaemonAnalyzerListener;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.daemon.ProjectStructureElement;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.daemon.ProjectStructureProblemsHolderImpl;
+import com.intellij.openapi.util.Disposer;
+import com.intellij.packaging.artifacts.*;
+import com.intellij.packaging.elements.CompositePackagingElement;
+import com.intellij.packaging.elements.ManifestFileProvider;
+import com.intellij.packaging.impl.artifacts.ArtifactUtil;
+import com.intellij.packaging.impl.artifacts.DefaultPackagingElementResolvingContext;
+import com.intellij.packaging.ui.ManifestFileConfiguration;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.*;
+
+/**
+* @author nik
+*/
+public class ArtifactsStructureConfigurableContextImpl implements ArtifactsStructureConfigurableContext {
+ private ModifiableArtifactModel myModifiableModel;
+ private final ManifestFilesInfo myManifestFilesInfo = new ManifestFilesInfo();
+ private final ArtifactAdapter myModifiableModelListener;
+ private final StructureConfigurableContext myContext;
+ private final Project myProject;
+ private final Map<Artifact, CompositePackagingElement<?>> myModifiableRoots = new HashMap<Artifact, CompositePackagingElement<?>>();
+ private final Map<Artifact, ArtifactEditorImpl> myArtifactEditors = new HashMap<Artifact, ArtifactEditorImpl>();
+ private final Map<ArtifactPointer, ArtifactEditorSettings> myEditorSettings = new HashMap<ArtifactPointer, ArtifactEditorSettings>();
+ private final Map<Artifact, ArtifactProjectStructureElement> myArtifactElements = new HashMap<Artifact, ArtifactProjectStructureElement>();
+ private final ArtifactEditorSettings myDefaultSettings;
+ private final ManifestFileProvider myManifestFileProvider = new ArtifactEditorManifestFileProvider(this);
+
+ public ArtifactsStructureConfigurableContextImpl(StructureConfigurableContext context, Project project,
+ ArtifactEditorSettings defaultSettings, final ArtifactAdapter modifiableModelListener) {
+ myDefaultSettings = defaultSettings;
+ myModifiableModelListener = modifiableModelListener;
+ myContext = context;
+ myProject = project;
+ context.getDaemonAnalyzer().addListener(new ProjectStructureDaemonAnalyzerListener() {
+ @Override
+ public void problemsChanged(@NotNull ProjectStructureElement element) {
+ if (element instanceof ArtifactProjectStructureElement) {
+ final Artifact originalArtifact = ((ArtifactProjectStructureElement)element).getOriginalArtifact();
+ final ArtifactEditorImpl artifactEditor = myArtifactEditors.get(originalArtifact);
+ if (artifactEditor != null) {
+ updateProblems(originalArtifact, artifactEditor);
+ }
+ }
+ }
+ });
+ }
+
+ private void updateProblems(Artifact originalArtifact, ArtifactEditorImpl artifactEditor) {
+ final ProjectStructureProblemsHolderImpl holder = myContext.getDaemonAnalyzer().getProblemsHolder(getOrCreateArtifactElement(originalArtifact));
+ artifactEditor.getValidationManager().updateProblems(holder);
+ }
+
+ @Override
+ @NotNull
+ public Project getProject() {
+ return myProject;
+ }
+
+ @Override
+ @NotNull
+ public ArtifactModel getArtifactModel() {
+ if (myModifiableModel != null) {
+ return myModifiableModel;
+ }
+ return ArtifactManager.getInstance(myProject);
+ }
+
+ @Override
+ @NotNull
+ public Artifact getOriginalArtifact(@NotNull Artifact artifact) {
+ if (myModifiableModel != null) {
+ return myModifiableModel.getOriginalArtifact(artifact);
+ }
+ return artifact;
+ }
+
+ @Override
+ public ModifiableModuleModel getModifiableModuleModel() {
+ return myContext.getModulesConfigurator().getModuleModel();
+ }
+
+ @Override
+ public void queueValidation(Artifact artifact) {
+ myContext.getDaemonAnalyzer().queueUpdate(getOrCreateArtifactElement(artifact));
+ }
+
+ @Override
+ public CompositePackagingElement<?> getRootElement(@NotNull Artifact artifact) {
+ artifact = getOriginalArtifact(artifact);
+ if (myModifiableModel != null) {
+ final Artifact modifiableArtifact = myModifiableModel.getModifiableCopy(artifact);
+ if (modifiableArtifact != null) {
+ myModifiableRoots.put(artifact, modifiableArtifact.getRootElement());
+ }
+ }
+ return getOrCreateModifiableRootElement(artifact);
+ }
+
+ private CompositePackagingElement<?> getOrCreateModifiableRootElement(Artifact originalArtifact) {
+ CompositePackagingElement<?> root = myModifiableRoots.get(originalArtifact);
+ if (root == null) {
+ root = ArtifactUtil.copyFromRoot(originalArtifact.getRootElement(), myProject);
+ myModifiableRoots.put(originalArtifact, root);
+ }
+ return root;
+ }
+
+ @Override
+ public void editLayout(@NotNull final Artifact artifact, final Runnable action) {
+ final Artifact originalArtifact = getOriginalArtifact(artifact);
+ new WriteAction() {
+ @Override
+ protected void run(final Result result) {
+ final ModifiableArtifact modifiableArtifact = getOrCreateModifiableArtifactModel().getOrCreateModifiableArtifact(originalArtifact);
+ if (modifiableArtifact.getRootElement() == originalArtifact.getRootElement()) {
+ modifiableArtifact.setRootElement(getOrCreateModifiableRootElement(originalArtifact));
+ }
+ action.run();
+ }
+ }.execute();
+ myContext.getDaemonAnalyzer().queueUpdate(getOrCreateArtifactElement(originalArtifact));
+ }
+
+ @Nullable
+ public ArtifactEditorImpl getArtifactEditor(Artifact artifact) {
+ return myArtifactEditors.get(getOriginalArtifact(artifact));
+ }
+
+ @Override
+ public ArtifactEditorImpl getOrCreateEditor(Artifact artifact) {
+ artifact = getOriginalArtifact(artifact);
+ ArtifactEditorImpl artifactEditor = myArtifactEditors.get(artifact);
+ if (artifactEditor == null) {
+ final ArtifactEditorSettings settings = myEditorSettings.get(ArtifactPointerManager.getInstance(myProject).createPointer(artifact, getArtifactModel()));
+ artifactEditor = new ArtifactEditorImpl(this, artifact, settings != null ? settings : myDefaultSettings);
+ myArtifactEditors.put(artifact, artifactEditor);
+ }
+ return artifactEditor;
+ }
+
+ @Nullable
+ public ModifiableArtifactModel getActualModifiableModel() {
+ return myModifiableModel;
+ }
+
+ @Override
+ @NotNull
+ public ModifiableArtifactModel getOrCreateModifiableArtifactModel() {
+ if (myModifiableModel == null) {
+ myModifiableModel = ArtifactManager.getInstance(myProject).createModifiableModel();
+ myModifiableModel.addListener(myModifiableModelListener);
+ }
+ return myModifiableModel;
+ }
+
+ @Override
+ public ArtifactEditorSettings getDefaultSettings() {
+ return myDefaultSettings;
+ }
+
+ @Override
+ @NotNull
+ public ModulesProvider getModulesProvider() {
+ return myContext.getModulesConfigurator();
+ }
+
+ @Override
+ @NotNull
+ public FacetsProvider getFacetsProvider() {
+ return myContext.getModulesConfigurator().getFacetsConfigurator();
+ }
+
+ @Override
+ public Library findLibrary(@NotNull String level, @NotNull String libraryName) {
+ final Library library = DefaultPackagingElementResolvingContext.findLibrary(myProject, level, libraryName);
+ return library != null ? myContext.getLibraryModel(library) : myContext.getLibrary(libraryName, level);
+ }
+
+ @NotNull
+ @Override
+ public ManifestFileProvider getManifestFileProvider() {
+ return myManifestFileProvider;
+ }
+
+ @Override
+ public ManifestFileConfiguration getManifestFile(CompositePackagingElement<?> element, ArtifactType artifactType) {
+ return myManifestFilesInfo.getManifestFile(element, artifactType, this);
+ }
+
+ public ManifestFilesInfo getManifestFilesInfo() {
+ return myManifestFilesInfo;
+ }
+
+ public void resetModifiableModel() {
+ disposeUIResources();
+ myModifiableModel = null;
+ myModifiableRoots.clear();
+ myManifestFilesInfo.clear();
+ }
+
+ public void disposeUIResources() {
+ for (ArtifactEditorImpl editor : myArtifactEditors.values()) {
+ Disposer.dispose(editor);
+ }
+ myArtifactEditors.clear();
+ if (myModifiableModel != null) {
+ myModifiableModel.dispose();
+ }
+ myArtifactElements.clear();
+ }
+
+ public Collection<? extends ArtifactEditorImpl> getArtifactEditors() {
+ return Collections.unmodifiableCollection(myArtifactEditors.values());
+ }
+
+ public void saveEditorSettings() {
+ myEditorSettings.clear();
+ for (ArtifactEditorImpl artifactEditor : myArtifactEditors.values()) {
+ final ArtifactPointer pointer = ArtifactPointerManager.getInstance(myProject).createPointer(artifactEditor.getArtifact(), getArtifactModel());
+ myEditorSettings.put(pointer, artifactEditor.createSettings());
+ }
+ }
+
+ @Override
+ @NotNull
+ public ArtifactProjectStructureElement getOrCreateArtifactElement(@NotNull Artifact artifact) {
+ ArtifactProjectStructureElement element = myArtifactElements.get(getOriginalArtifact(artifact));
+ if (element == null) {
+ element = new ArtifactProjectStructureElement(myContext, this, artifact);
+ myArtifactElements.put(artifact, element);
+ }
+ return element;
+ }
+
+ @Override
+ public ModifiableRootModel getOrCreateModifiableRootModel(Module module) {
+ final ModuleEditor editor = myContext.getModulesConfigurator().getOrCreateModuleEditor(module);
+ return editor.getModifiableRootModelProxy();
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/ComplexElementSubstitutionParameters.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/ComplexElementSubstitutionParameters.java
new file mode 100644
index 0000000..1845308
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/ComplexElementSubstitutionParameters.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2000-2009 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.artifacts;
+
+import com.intellij.openapi.roots.ui.configuration.artifacts.nodes.ComplexPackagingElementNode;
+import com.intellij.packaging.elements.ComplexPackagingElement;
+import com.intellij.packaging.elements.ComplexPackagingElementType;
+import com.intellij.packaging.elements.PackagingElementFactory;
+import com.intellij.util.containers.ContainerUtil;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.*;
+
+/**
+ * @author nik
+ */
+public class ComplexElementSubstitutionParameters {
+ private final Set<ComplexPackagingElementType<?>> myTypesToSubstitute = new HashSet<ComplexPackagingElementType<?>>();
+ private final Set<ComplexPackagingElement<?>> mySubstituted = new HashSet<ComplexPackagingElement<?>>();
+
+ public void setSubstituteAll() {
+ ContainerUtil.addAll(myTypesToSubstitute, PackagingElementFactory.getInstance().getComplexElementTypes());
+ mySubstituted.clear();
+ }
+
+ public void setSubstituteNone() {
+ myTypesToSubstitute.clear();
+ mySubstituted.clear();
+ }
+
+ public boolean shouldSubstitute(@NotNull ComplexPackagingElement<?> element) {
+ final ComplexPackagingElementType<?> type = (ComplexPackagingElementType<?>)element.getType();
+ return myTypesToSubstitute.contains(type) || mySubstituted.contains(element);
+ }
+
+ public void setShowContent(ComplexPackagingElementType<?> type, boolean showContent) {
+ if (showContent) {
+ myTypesToSubstitute.add(type);
+ }
+ else {
+ myTypesToSubstitute.remove(type);
+ }
+ final Iterator<ComplexPackagingElement<?>> iterator = mySubstituted.iterator();
+ while (iterator.hasNext()) {
+ if (iterator.next().getType().equals(type)) {
+ iterator.remove();
+ }
+ }
+ }
+
+ public Set<ComplexPackagingElementType<?>> getTypesToSubstitute() {
+ return Collections.unmodifiableSet(myTypesToSubstitute);
+ }
+
+ public void setShowContent(ComplexPackagingElementNode complexNode) {
+ mySubstituted.addAll(complexNode.getPackagingElements());
+ }
+
+ public void doNotSubstitute(ComplexPackagingElement<?> element) {
+ mySubstituted.remove(element);
+ }
+
+ public boolean isShowContentForType(@NotNull ComplexPackagingElementType type) {
+ return myTypesToSubstitute.contains(type);
+ }
+
+ public boolean isAllSubstituted() {
+ return myTypesToSubstitute.containsAll(Arrays.asList(PackagingElementFactory.getInstance().getComplexElementTypes()));
+ }
+
+ public boolean isNoneSubstituted() {
+ return myTypesToSubstitute.isEmpty() && mySubstituted.isEmpty();
+ }
+
+ public void setTypesToShowContent(Collection<ComplexPackagingElementType<?>> types) {
+ myTypesToSubstitute.clear();
+ myTypesToSubstitute.addAll(types);
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/InvalidArtifactComponent.form b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/InvalidArtifactComponent.form
new file mode 100644
index 0000000..d291652
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/InvalidArtifactComponent.form
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.intellij.openapi.roots.ui.configuration.artifacts.InvalidArtifactConfigurable.InvalidArtifactComponent">
+ <grid id="27dc6" binding="myMainPanel" layout-manager="GridLayoutManager" row-count="3" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+ <margin top="0" left="0" bottom="0" right="0"/>
+ <constraints>
+ <xy x="20" y="20" width="500" height="400"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <vspacer id="b67c4">
+ <constraints>
+ <grid row="2" column="1" row-span="1" col-span="1" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/>
+ </constraints>
+ </vspacer>
+ <component id="2bf31" class="javax.swing.JLabel">
+ <constraints>
+ <grid row="0" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties>
+ <text value="Cannot load artifact"/>
+ </properties>
+ </component>
+ <component id="b6c83" class="javax.swing.JLabel" binding="myIconLabel">
+ <constraints>
+ <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties>
+ <text value=""/>
+ </properties>
+ </component>
+ <component id="f350" class="com.intellij.openapi.ui.ex.MultiLineLabel" binding="myDescriptionLabel">
+ <constraints>
+ <grid row="1" column="1" row-span="1" col-span="1" vsize-policy="3" hsize-policy="7" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties/>
+ </component>
+ </children>
+ </grid>
+</form>
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/InvalidArtifactConfigurable.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/InvalidArtifactConfigurable.java
new file mode 100644
index 0000000..3e90ded
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/InvalidArtifactConfigurable.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2000-2010 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.artifacts;
+
+import com.intellij.icons.AllIcons;
+import com.intellij.openapi.options.ConfigurationException;
+import com.intellij.openapi.ui.ex.MultiLineLabel;
+import com.intellij.packaging.impl.artifacts.InvalidArtifact;
+
+import javax.swing.*;
+
+/**
+ * @author nik
+ */
+public class InvalidArtifactConfigurable extends ArtifactConfigurableBase {
+ private String myErrorMessage;
+
+ public InvalidArtifactConfigurable(InvalidArtifact originalArtifact,
+ ArtifactsStructureConfigurableContextImpl artifactsStructureContext,
+ Runnable updateTree) {
+ super(originalArtifact, artifactsStructureContext, updateTree, false);
+ myErrorMessage = originalArtifact.getErrorMessage();
+ }
+
+ @Override
+ public void setDisplayName(String name) {
+ }
+
+ @Override
+ public JComponent createOptionsPanel() {
+ return new InvalidArtifactComponent(myErrorMessage).myMainPanel;
+ }
+
+ @Override
+ public String getHelpTopic() {
+ return null;
+ }
+
+ @Override
+ public boolean isModified() {
+ return false;
+ }
+
+ @Override
+ public void apply() throws ConfigurationException {
+ }
+
+ @Override
+ public void reset() {
+ }
+
+ private static class InvalidArtifactComponent {
+ private JPanel myMainPanel;
+ private MultiLineLabel myDescriptionLabel;
+ private JLabel myIconLabel;
+
+ private InvalidArtifactComponent(String errorMessage) {
+ myIconLabel.setIcon(AllIcons.RunConfigurations.ConfigurationWarning);
+ myDescriptionLabel.setText(errorMessage);
+ }
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/LayoutNodesDraggingObject.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/LayoutNodesDraggingObject.java
new file mode 100644
index 0000000..8ccf7ed
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/LayoutNodesDraggingObject.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2000-2009 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.artifacts;
+
+import com.intellij.openapi.roots.ui.configuration.artifacts.nodes.PackagingElementNode;
+import com.intellij.packaging.elements.PackagingElement;
+import com.intellij.packaging.ui.ArtifactEditorContext;
+import com.intellij.packaging.impl.artifacts.ArtifactUtil;
+import com.intellij.ui.treeStructure.SimpleNode;
+import org.jetbrains.annotations.NotNull;
+
+import javax.swing.tree.TreePath;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author nik
+ */
+public class LayoutNodesDraggingObject extends PackagingElementDraggingObject {
+ private final ArtifactEditorEx myArtifactsEditor;
+ private final List<PackagingElementNode<?>> myNodes;
+
+ public LayoutNodesDraggingObject(ArtifactEditorEx artifactsEditor, List<PackagingElementNode<?>> nodes) {
+ myArtifactsEditor = artifactsEditor;
+ myNodes = nodes;
+ }
+
+ @Override
+ public List<PackagingElement<?>> createPackagingElements(ArtifactEditorContext context) {
+ final List<PackagingElement<?>> result = new ArrayList<PackagingElement<?>>();
+
+ for (PackagingElementNode<?> node : myNodes) {
+ final List<? extends PackagingElement<?>> elements = node.getPackagingElements();
+ for (PackagingElement<?> element : elements) {
+ result.add(ArtifactUtil.copyWithChildren(element, myArtifactsEditor.getContext().getProject()));
+ }
+ }
+
+ return result;
+ }
+
+ @Override
+ public boolean checkCanDrop() {
+ return myArtifactsEditor.getLayoutTreeComponent().checkCanRemove(myNodes);
+ }
+
+ @Override
+ public void beforeDrop() {
+ myArtifactsEditor.getLayoutTreeComponent().removeNodes(myNodes);
+ }
+
+ @Override
+ public boolean canDropInto(@NotNull PackagingElementNode node) {
+ final LayoutTree tree = myArtifactsEditor.getLayoutTreeComponent().getLayoutTree();
+ final TreePath path = tree.getPathFor(node);
+ if (path != null) {
+ for (PackagingElementNode<?> selectedNode : myNodes) {
+ if (pathContains(path, selectedNode, tree)) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ private static boolean pathContains(TreePath path, PackagingElementNode<?> node, LayoutTree tree) {
+ while (path != null) {
+ final SimpleNode pathNode = tree.getNodeFor(path);
+ if (pathNode == node) {
+ return true;
+ }
+ path = path.getParentPath();
+ }
+ return false;
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/LayoutTree.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/LayoutTree.java
new file mode 100644
index 0000000..9ac88ac
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/LayoutTree.java
@@ -0,0 +1,247 @@
+/*
+ * Copyright 2000-2009 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.artifacts;
+
+import com.intellij.ide.dnd.AdvancedDnDSource;
+import com.intellij.ide.dnd.DnDAction;
+import com.intellij.ide.dnd.DnDDragStartBean;
+import com.intellij.ide.dnd.DnDManager;
+import com.intellij.ide.dnd.aware.DnDAwareTree;
+import com.intellij.ide.util.treeView.AbstractTreeBuilder;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.project.ProjectBundle;
+import com.intellij.openapi.roots.ui.configuration.artifacts.nodes.PackagingElementNode;
+import com.intellij.openapi.util.Pair;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.openapi.wm.IdeFocusManager;
+import com.intellij.packaging.elements.PackagingElement;
+import com.intellij.packaging.elements.RenameablePackagingElement;
+import com.intellij.ui.TreeSpeedSearch;
+import com.intellij.ui.TreeUIHelper;
+import com.intellij.ui.treeStructure.SimpleNode;
+import com.intellij.util.containers.ContainerUtil;
+import com.intellij.util.containers.Convertor;
+import com.intellij.util.ui.tree.TreeUtil;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import javax.swing.tree.DefaultMutableTreeNode;
+import javax.swing.tree.TreePath;
+import java.awt.*;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+/**
+* @author nik
+*/
+public class LayoutTree extends SimpleDnDAwareTree implements AdvancedDnDSource {
+ private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.roots.ui.configuration.artifacts.LayoutTree");
+ private final ArtifactEditorImpl myArtifactsEditor;
+
+ public LayoutTree(ArtifactEditorImpl artifactsEditor) {
+ myArtifactsEditor = artifactsEditor;
+ setRootVisible(true);
+ setShowsRootHandles(false);
+ setCellEditor(new LayoutTreeCellEditor());
+ if (!ApplicationManager.getApplication().isUnitTestMode()) {
+ DnDManager.getInstance().registerSource(this);
+ }
+
+ //todo[nik,pegov] fix for tooltips in the tree. Otherwise tooltips will be ignored by DnDEnabled
+ setToolTipText("");
+ }
+
+ public void addSubtreeToUpdate(DefaultMutableTreeNode newNode) {
+ AbstractTreeBuilder.getBuilderFor(this).addSubtreeToUpdate(newNode);
+ }
+
+ @Override
+ protected void configureUiHelper(TreeUIHelper helper) {
+ final Convertor<TreePath, String> convertor = new Convertor<TreePath, String>() {
+ @Override
+ public String convert(final TreePath path) {
+ final SimpleNode node = getNodeFor(path);
+ if (node instanceof PackagingElementNode) {
+ return ((PackagingElementNode<?>)node).getElementPresentation().getSearchName();
+ }
+ return "";
+ }
+ };
+ new TreeSpeedSearch(this, convertor, true);
+ }
+
+ private List<PackagingElementNode<?>> getNodesToDrag() {
+ return getSelection().getNodes();
+ }
+
+ @Override
+ public boolean canStartDragging(DnDAction action, Point dragOrigin) {
+ return !getNodesToDrag().isEmpty();
+ }
+
+ @Override
+ public DnDDragStartBean startDragging(DnDAction action, Point dragOrigin) {
+ return new DnDDragStartBean(new LayoutNodesDraggingObject(myArtifactsEditor, getNodesToDrag()));
+ }
+
+ @Override
+ public Pair<Image, Point> createDraggedImage(DnDAction action, Point dragOrigin) {
+ final List<PackagingElementNode<?>> nodes = getNodesToDrag();
+ if (nodes.size() == 1) {
+ return DnDAwareTree.getDragImage(this, getPathFor(nodes.get(0)), dragOrigin);
+ }
+ return DnDAwareTree.getDragImage(this, ProjectBundle.message("drag.n.drop.text.0.packaging.elements", nodes.size()), dragOrigin);
+ }
+
+ @Override
+ public void dragDropEnd() {
+ }
+
+ @Override
+ public void dropActionChanged(int gestureModifiers) {
+ }
+
+ @Override
+ public void dispose() {
+ if (!ApplicationManager.getApplication().isUnitTestMode()) {
+ DnDManager.getInstance().unregisterSource(this);
+ }
+ }
+
+ public LayoutTreeSelection getSelection() {
+ return new LayoutTreeSelection(this);
+ }
+
+ @Nullable
+ public PackagingElement<?> getElementByPath(TreePath path) {
+ final SimpleNode node = getNodeFor(path);
+ if (node instanceof PackagingElementNode) {
+ final List<? extends PackagingElement<?>> elements = ((PackagingElementNode<?>)node).getPackagingElements();
+ if (!elements.isEmpty()) {
+ return elements.get(0);
+ }
+ }
+ return null;
+ }
+
+ public PackagingElementNode<?> getRootPackagingNode() {
+ final SimpleNode node = getNodeFor(new TreePath(getRootNode()));
+ return node instanceof PackagingElementNode ? (PackagingElementNode<?>)node : null;
+ }
+
+ public DefaultMutableTreeNode getRootNode() {
+ return (DefaultMutableTreeNode)getModel().getRoot();
+ }
+
+ public List<PackagingElementNode<?>> findNodes(final Collection<? extends PackagingElement<?>> elements) {
+ final List<PackagingElementNode<?>> nodes = new ArrayList<PackagingElementNode<?>>();
+ TreeUtil.traverseDepth(getRootNode(), new TreeUtil.Traverse() {
+ @Override
+ public boolean accept(Object node) {
+ final Object userObject = ((DefaultMutableTreeNode)node).getUserObject();
+ if (userObject instanceof PackagingElementNode) {
+ final PackagingElementNode<?> packagingNode = (PackagingElementNode<?>)userObject;
+ final List<? extends PackagingElement<?>> nodeElements = packagingNode.getPackagingElements();
+ if (ContainerUtil.intersects(nodeElements, elements)) {
+ nodes.add(packagingNode);
+ }
+ }
+ return true;
+ }
+ });
+ return nodes;
+ }
+
+ public void addSubtreeToUpdate(final PackagingElementNode elementNode) {
+ final DefaultMutableTreeNode node = TreeUtil.findNodeWithObject(getRootNode(), elementNode);
+ if (node != null) {
+ addSubtreeToUpdate(node);
+ }
+ }
+
+ @Nullable
+ public PackagingElementNode<?> findCompositeNodeByPath(String parentPath) {
+ PackagingElementNode<?> node = getRootPackagingNode();
+ for (String name : StringUtil.split(parentPath, "/")) {
+ if (node == null) {
+ return null;
+ }
+ node = node.findCompositeChild(name);
+ }
+ return node;
+ }
+
+ private class LayoutTreeCellEditor extends DefaultCellEditor {
+ public LayoutTreeCellEditor() {
+ super(new JTextField());
+ }
+
+ @Override
+ public Component getTreeCellEditorComponent(JTree tree, Object value, boolean isSelected, boolean expanded, boolean leaf, int row) {
+ final JTextField field = (JTextField)super.getTreeCellEditorComponent(tree, value, isSelected, expanded, leaf, row);
+ final Object node = ((DefaultMutableTreeNode)value).getUserObject();
+ final PackagingElement<?> element = ((PackagingElementNode)node).getElementIfSingle();
+ LOG.assertTrue(element != null);
+ final String name = ((RenameablePackagingElement)element).getName();
+ field.setText(name);
+ int i = name.lastIndexOf('.');
+ field.setSelectionStart(0);
+ field.setSelectionEnd(i != -1 ? i : name.length());
+ return field;
+ }
+
+ @Override
+ public boolean stopCellEditing() {
+ final String newValue = ((JTextField)editorComponent).getText();
+ final TreePath path = getEditingPath();
+ final Object node = getNodeFor(path);
+ RenameablePackagingElement currentElement = null;
+ if (node instanceof PackagingElementNode) {
+ final PackagingElement<?> element = ((PackagingElementNode)node).getElementIfSingle();
+ if (element instanceof RenameablePackagingElement) {
+ currentElement = (RenameablePackagingElement)element;
+ }
+ }
+ final boolean stopped = super.stopCellEditing();
+ if (stopped && currentElement != null) {
+ final RenameablePackagingElement finalCurrentElement = currentElement;
+ myArtifactsEditor.getLayoutTreeComponent().editLayout(new Runnable() {
+ @Override
+ public void run() {
+ finalCurrentElement.rename(newValue);
+ }
+ });
+ myArtifactsEditor.queueValidation();
+ myArtifactsEditor.getLayoutTreeComponent().updatePropertiesPanel(true);
+ addSubtreeToUpdate((DefaultMutableTreeNode)path.getLastPathComponent());
+ requestFocusToTree();
+ }
+ return stopped;
+ }
+
+ @Override
+ public void cancelCellEditing() {
+ super.cancelCellEditing();
+ requestFocusToTree();
+ }
+
+ private void requestFocusToTree() {
+ IdeFocusManager.getInstance(myArtifactsEditor.getContext().getProject()).requestFocus(LayoutTree.this, true);
+ }
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/LayoutTreeComponent.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/LayoutTreeComponent.java
new file mode 100644
index 0000000..ca055ed
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/LayoutTreeComponent.java
@@ -0,0 +1,669 @@
+/*
+ * Copyright 2000-2009 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.artifacts;
+
+import com.intellij.ide.dnd.DnDEvent;
+import com.intellij.ide.dnd.DnDManager;
+import com.intellij.ide.dnd.DnDTarget;
+import com.intellij.openapi.Disposable;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.roots.ui.configuration.artifacts.nodes.ArtifactRootNode;
+import com.intellij.openapi.roots.ui.configuration.artifacts.nodes.PackagingElementNode;
+import com.intellij.openapi.roots.ui.configuration.artifacts.nodes.PackagingNodeSource;
+import com.intellij.openapi.roots.ui.configuration.artifacts.nodes.PackagingTreeNodeFactory;
+import com.intellij.openapi.ui.Messages;
+import com.intellij.openapi.util.Comparing;
+import com.intellij.openapi.util.Disposer;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.packaging.artifacts.Artifact;
+import com.intellij.packaging.artifacts.ArtifactType;
+import com.intellij.packaging.elements.CompositePackagingElement;
+import com.intellij.packaging.elements.PackagingElement;
+import com.intellij.packaging.elements.PackagingElementFactory;
+import com.intellij.packaging.elements.PackagingElementType;
+import com.intellij.packaging.impl.elements.DirectoryPackagingElement;
+import com.intellij.packaging.ui.ArtifactEditorContext;
+import com.intellij.packaging.ui.PackagingElementPropertiesPanel;
+import com.intellij.packaging.ui.PackagingSourceItem;
+import com.intellij.ui.ScrollPaneFactory;
+import com.intellij.ui.awt.RelativeRectangle;
+import com.intellij.ui.treeStructure.SimpleNode;
+import com.intellij.ui.treeStructure.SimpleTreeBuilder;
+import com.intellij.ui.treeStructure.SimpleTreeStructure;
+import com.intellij.ui.treeStructure.WeightBasedComparator;
+import com.intellij.util.ArrayUtil;
+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 org.jetbrains.annotations.TestOnly;
+
+import javax.swing.*;
+import javax.swing.event.TreeSelectionEvent;
+import javax.swing.event.TreeSelectionListener;
+import javax.swing.tree.DefaultMutableTreeNode;
+import javax.swing.tree.TreePath;
+import java.awt.*;
+import java.util.*;
+import java.util.List;
+
+public class LayoutTreeComponent implements DnDTarget, Disposable {
+ @NonNls private static final String EMPTY_CARD = "<empty>";
+ @NonNls private static final String PROPERTIES_CARD = "properties";
+ private final ArtifactEditorImpl myArtifactsEditor;
+ private final LayoutTree myTree;
+ private final JPanel myTreePanel;
+ private final ComplexElementSubstitutionParameters mySubstitutionParameters;
+ private final ArtifactEditorContext myContext;
+ private final Artifact myOriginalArtifact;
+ private SelectedElementInfo<?> mySelectedElementInfo = new SelectedElementInfo<PackagingElement<?>>(null);
+ private JPanel myPropertiesPanelWrapper;
+ private JPanel myPropertiesPanel;
+ private final LayoutTreeBuilder myBuilder;
+ private boolean mySortElements;
+ private final LayoutTreeStructure myTreeStructure;
+
+ public LayoutTreeComponent(ArtifactEditorImpl artifactsEditor, ComplexElementSubstitutionParameters substitutionParameters,
+ ArtifactEditorContext context, Artifact originalArtifact, boolean sortElements) {
+ myArtifactsEditor = artifactsEditor;
+ mySubstitutionParameters = substitutionParameters;
+ myContext = context;
+ myOriginalArtifact = originalArtifact;
+ mySortElements = sortElements;
+ myTree = new LayoutTree(myArtifactsEditor);
+ myTreeStructure = new LayoutTreeStructure();
+ myBuilder = new LayoutTreeBuilder();
+ Disposer.register(this, myTree);
+ Disposer.register(this, myBuilder);
+
+ myTree.addTreeSelectionListener(new TreeSelectionListener() {
+ @Override
+ public void valueChanged(TreeSelectionEvent e) {
+ updatePropertiesPanel(false);
+ }
+ });
+ createPropertiesPanel();
+ myTreePanel = new JPanel(new BorderLayout());
+ myTreePanel.add(ScrollPaneFactory.createScrollPane(myTree), BorderLayout.CENTER);
+ myTreePanel.add(myPropertiesPanelWrapper, BorderLayout.SOUTH);
+ if (!ApplicationManager.getApplication().isUnitTestMode()) {
+ DnDManager.getInstance().registerTarget(this, myTree);
+ }
+ }
+
+ @Nullable
+ private WeightBasedComparator getComparator() {
+ return mySortElements ? new WeightBasedComparator(true) : null;
+ }
+
+ public void setSortElements(boolean sortElements) {
+ mySortElements = sortElements;
+ myBuilder.setNodeDescriptorComparator(getComparator());
+ myArtifactsEditor.getContext().getParent().getDefaultSettings().setSortElements(sortElements);
+ }
+
+ @Nullable
+ private static PackagingElementNode getNode(Object value) {
+ if (!(value instanceof DefaultMutableTreeNode)) return null;
+ final Object userObject = ((DefaultMutableTreeNode)value).getUserObject();
+ return userObject instanceof PackagingElementNode ? (PackagingElementNode)userObject : null;
+ }
+
+ private void createPropertiesPanel() {
+ myPropertiesPanel = new JPanel(new BorderLayout());
+ final JPanel emptyPanel = new JPanel();
+ emptyPanel.setMinimumSize(new Dimension(0, 0));
+ emptyPanel.setPreferredSize(new Dimension(0, 0));
+
+ myPropertiesPanelWrapper = new JPanel(new CardLayout());
+ myPropertiesPanelWrapper.add(EMPTY_CARD, emptyPanel);
+ myPropertiesPanelWrapper.add(PROPERTIES_CARD, myPropertiesPanel);
+ }
+
+ public Artifact getArtifact() {
+ return myArtifactsEditor.getArtifact();
+ }
+
+ public LayoutTree getLayoutTree() {
+ return myTree;
+ }
+
+ public void updatePropertiesPanel(final boolean force) {
+ final PackagingElement<?> selected = getSelection().getElementIfSingle();
+ if (!force && Comparing.equal(selected, mySelectedElementInfo.myElement)) {
+ return;
+ }
+ mySelectedElementInfo.save();
+ mySelectedElementInfo = new SelectedElementInfo<PackagingElement<?>>(selected);
+ mySelectedElementInfo.showPropertiesPanel();
+ }
+
+ public void saveElementProperties() {
+ mySelectedElementInfo.save();
+ }
+
+ public void rebuildTree() {
+ myBuilder.updateFromRoot(true);
+ updatePropertiesPanel(true);
+ myArtifactsEditor.queueValidation();
+ }
+
+ public LayoutTreeSelection getSelection() {
+ return myTree.getSelection();
+ }
+
+ public void addNewPackagingElement(@NotNull PackagingElementType<?> type) {
+ PackagingElementNode<?> parentNode = getParentNode(myTree.getSelection());
+ final PackagingElement<?> element = parentNode.getFirstElement();
+ final CompositePackagingElement<?> parent;
+ if (element instanceof CompositePackagingElement<?>) {
+ parent = (CompositePackagingElement<?>)element;
+ }
+ else {
+ parent = getArtifact().getRootElement();
+ parentNode = myTree.getRootPackagingNode();
+ }
+ if (!checkCanAdd(parent, parentNode)) return;
+
+ final List<? extends PackagingElement<?>> children = type.chooseAndCreate(myContext, getArtifact(), parent);
+ final PackagingElementNode<?> finalParentNode = parentNode;
+ editLayout(new Runnable() {
+ @Override
+ public void run() {
+ CompositePackagingElement<?> actualParent = getOrCreateModifiableParent(parent, finalParentNode);
+ for (PackagingElement<?> child : children) {
+ actualParent.addOrFindChild(child);
+ }
+ }
+ });
+ updateAndSelect(parentNode, children);
+ }
+
+ private static CompositePackagingElement<?> getOrCreateModifiableParent(CompositePackagingElement<?> parentElement, PackagingElementNode<?> node) {
+ PackagingElementNode<?> current = node;
+ List<String> dirNames = new ArrayList<String>();
+ while (current != null && !(current instanceof ArtifactRootNode)) {
+ final PackagingElement<?> packagingElement = current.getFirstElement();
+ if (!(packagingElement instanceof DirectoryPackagingElement)) {
+ return parentElement;
+ }
+ dirNames.add(((DirectoryPackagingElement)packagingElement).getDirectoryName());
+ current = current.getParentNode();
+ }
+
+ if (current == null) return parentElement;
+ final PackagingElement<?> rootElement = current.getElementIfSingle();
+ if (!(rootElement instanceof CompositePackagingElement<?>)) return parentElement;
+
+ Collections.reverse(dirNames);
+ String path = StringUtil.join(dirNames, "/");
+ return PackagingElementFactory.getInstance().getOrCreateDirectory((CompositePackagingElement<?>)rootElement, path);
+ }
+
+ public boolean checkCanModify(@NotNull PackagingElement<?> element, @NotNull PackagingElementNode<?> node) {
+ return checkCanModify(node.getNodeSource(element));
+ }
+
+ public boolean checkCanModifyChildren(@NotNull PackagingElement<?> parentElement,
+ @NotNull PackagingElementNode<?> parentNode,
+ @NotNull Collection<? extends PackagingElementNode<?>> children) {
+ final List<PackagingNodeSource> sources = new ArrayList<PackagingNodeSource>(parentNode.getNodeSource(parentElement));
+ for (PackagingElementNode<?> child : children) {
+ sources.addAll(child.getNodeSources());
+ }
+ return checkCanModify(sources);
+ }
+
+ public boolean checkCanModify(final Collection<PackagingNodeSource> nodeSources) {
+ if (nodeSources.isEmpty()) {
+ return true;
+ }
+
+ if (nodeSources.size() > 1) {
+ Messages.showErrorDialog(myArtifactsEditor.getMainComponent(),
+ "The selected node consist of several elements so it cannot be edited.\nSwitch off 'Show content of elements' checkbox to edit the output layout.");
+ }
+ else {
+ final PackagingNodeSource source = ContainerUtil.getFirstItem(nodeSources, null);
+ if (source != null) {
+ Messages.showErrorDialog(myArtifactsEditor.getMainComponent(),
+ "The selected node belongs to '" + source.getPresentableName() + "' element so it cannot be edited.\nSwitch off 'Show content of elements' checkbox to edit the output layout.");
+ }
+ }
+ return false;
+
+ }
+
+ public boolean checkCanAdd(CompositePackagingElement<?> parentElement, PackagingElementNode<?> parentNode) {
+ boolean allParentsAreDirectories = true;
+ PackagingElementNode<?> current = parentNode;
+ while (current != null && !(current instanceof ArtifactRootNode)) {
+ final PackagingElement<?> element = current.getFirstElement();
+ if (!(element instanceof DirectoryPackagingElement)) {
+ allParentsAreDirectories = false;
+ break;
+ }
+ current = current.getParentNode();
+ }
+
+ return allParentsAreDirectories || checkCanModify(parentElement, parentNode);
+ }
+
+ public boolean checkCanRemove(final List<? extends PackagingElementNode<?>> nodes) {
+ Set<PackagingNodeSource> rootSources = new HashSet<PackagingNodeSource>();
+ for (PackagingElementNode<?> node : nodes) {
+ rootSources.addAll(getRootNodeSources(node.getNodeSources()));
+ }
+
+ if (!rootSources.isEmpty()) {
+ final String message;
+ if (rootSources.size() == 1) {
+ final String name = rootSources.iterator().next().getPresentableName();
+ message = "The selected node belongs to '" + name + "' element. Do you want to remove the whole '" + name + "' element from the artifact?";
+ }
+ else {
+ message = "The selected node belongs to " + nodes.size() + " elements. Do you want to remove all these elements from the artifact?";
+ }
+ final int answer = Messages.showYesNoDialog(myArtifactsEditor.getMainComponent(), message, "Remove Elements", null);
+ if (answer != 0) return false;
+ }
+ return true;
+ }
+
+ public void updateAndSelect(PackagingElementNode<?> node, final List<? extends PackagingElement<?>> toSelect) {
+ myArtifactsEditor.queueValidation();
+ final DefaultMutableTreeNode treeNode = TreeUtil.findNodeWithObject(myTree.getRootNode(), node);
+ myTreeStructure.clearCaches();
+ myBuilder.addSubtreeToUpdate(treeNode, new Runnable() {
+ @Override
+ public void run() {
+ List<PackagingElementNode<?>> nodes = myTree.findNodes(toSelect);
+ myBuilder.select(ArrayUtil.toObjectArray(nodes), null);
+ }
+ });
+ }
+
+ public void selectNode(@NotNull String parentPath, @NotNull PackagingElement<?> element) {
+ final PackagingElementNode<?> parent = myTree.findCompositeNodeByPath(parentPath);
+ if (parent == null) return;
+
+ for (SimpleNode node : parent.getChildren()) {
+ if (node instanceof PackagingElementNode) {
+ final List<? extends PackagingElement<?>> elements = ((PackagingElementNode<?>)node).getPackagingElements();
+ for (PackagingElement<?> packagingElement : elements) {
+ if (packagingElement.isEqualTo(element)) {
+ myBuilder.select(node);
+ return;
+ }
+ }
+ }
+ }
+ }
+
+ @TestOnly
+ public void selectNode(@NotNull String parentPath, @NotNull String nodeName) {
+ final PackagingElementNode<?> parent = myTree.findCompositeNodeByPath(parentPath);
+ if (parent == null) return;
+
+ for (SimpleNode node : parent.getChildren()) {
+ if (node instanceof PackagingElementNode) {
+ if (nodeName.equals(((PackagingElementNode)node).getElementPresentation().getSearchName())) {
+ myBuilder.select(node);
+ return;
+ }
+ }
+ }
+ }
+
+ public void editLayout(Runnable action) {
+ myContext.editLayout(myOriginalArtifact, action);
+ }
+
+ public void removeSelectedElements() {
+ final LayoutTreeSelection selection = myTree.getSelection();
+ if (!checkCanRemove(selection.getNodes())) return;
+
+ editLayout(new Runnable() {
+ @Override
+ public void run() {
+ removeNodes(selection.getNodes());
+ }
+ });
+
+ myArtifactsEditor.rebuildTries();
+ }
+
+ public void removeNodes(final List<PackagingElementNode<?>> nodes) {
+ Set<PackagingElement<?>> parents = new HashSet<PackagingElement<?>>();
+ for (PackagingElementNode<?> node : nodes) {
+ final List<? extends PackagingElement<?>> toDelete = node.getPackagingElements();
+ for (PackagingElement<?> element : toDelete) {
+ final Collection<PackagingNodeSource> nodeSources = node.getNodeSource(element);
+ if (nodeSources.isEmpty()) {
+ final CompositePackagingElement<?> parent = node.getParentElement(element);
+ if (parent != null) {
+ parents.add(parent);
+ parent.removeChild(element);
+ }
+ }
+ else {
+ Collection<PackagingNodeSource> rootSources = getRootNodeSources(nodeSources);
+ for (PackagingNodeSource source : rootSources) {
+ parents.add(source.getSourceParentElement());
+ source.getSourceParentElement().removeChild(source.getSourceElement());
+ }
+ }
+ }
+ }
+ final List<PackagingElementNode<?>> parentNodes = myTree.findNodes(parents);
+ for (PackagingElementNode<?> parentNode : parentNodes) {
+ myTree.addSubtreeToUpdate(parentNode);
+ }
+ }
+
+ private static Collection<PackagingNodeSource> getRootNodeSources(Collection<PackagingNodeSource> nodeSources) {
+ Set<PackagingNodeSource> result = new HashSet<PackagingNodeSource>();
+ collectRootNodeSources(nodeSources, result);
+ return result;
+ }
+
+ private static void collectRootNodeSources(Collection<PackagingNodeSource> nodeSources, Set<PackagingNodeSource> result) {
+ for (PackagingNodeSource nodeSource : nodeSources) {
+ final Collection<PackagingNodeSource> parentSources = nodeSource.getParentSources();
+ if (parentSources.isEmpty()) {
+ result.add(nodeSource);
+ }
+ else {
+ collectRootNodeSources(parentSources, result);
+ }
+ }
+ }
+
+ private PackagingElementNode<?> getParentNode(final LayoutTreeSelection selection) {
+ final PackagingElementNode<?> node = selection.getNodeIfSingle();
+ if (node != null) {
+ if (node.getFirstElement() instanceof CompositePackagingElement) {
+ return node;
+ }
+ final PackagingElementNode<?> parent = node.getParentNode();
+ if (parent != null) {
+ return parent;
+ }
+ }
+ return myTree.getRootPackagingNode();
+ }
+
+ public JPanel getTreePanel() {
+ return myTreePanel;
+ }
+
+ @Override
+ public void dispose() {
+ if (!ApplicationManager.getApplication().isUnitTestMode()) {
+ DnDManager.getInstance().unregisterTarget(this, myTree);
+ }
+ }
+
+ @Override
+ public boolean update(DnDEvent aEvent) {
+ aEvent.setDropPossible(false);
+ aEvent.hideHighlighter();
+ final Object object = aEvent.getAttachedObject();
+ if (object instanceof PackagingElementDraggingObject) {
+ final DefaultMutableTreeNode parent = findParentCompositeElementNode(aEvent.getRelativePoint().getPoint(myTree));
+ if (parent != null) {
+ final PackagingElementDraggingObject draggingObject = (PackagingElementDraggingObject)object;
+ final PackagingElementNode node = getNode(parent);
+ if (node != null && draggingObject.canDropInto(node)) {
+ final PackagingElement element = node.getFirstElement();
+ if (element instanceof CompositePackagingElement) {
+ draggingObject.setTargetNode(node);
+ draggingObject.setTargetElement((CompositePackagingElement<?>)element);
+ final Rectangle bounds = myTree.getPathBounds(TreeUtil.getPathFromRoot(parent));
+ aEvent.setHighlighting(new RelativeRectangle(myTree, bounds), DnDEvent.DropTargetHighlightingType.RECTANGLE);
+ aEvent.setDropPossible(true);
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public void drop(DnDEvent aEvent) {
+ final Object object = aEvent.getAttachedObject();
+ if (object instanceof PackagingElementDraggingObject) {
+ final PackagingElementDraggingObject draggingObject = (PackagingElementDraggingObject)object;
+ final PackagingElementNode<?> targetNode = draggingObject.getTargetNode();
+ final CompositePackagingElement<?> targetElement = draggingObject.getTargetElement();
+ if (targetElement == null || targetNode == null || !draggingObject.checkCanDrop()) return;
+ if (!checkCanAdd(targetElement, targetNode)) {
+ return;
+ }
+ final List<PackagingElement<?>> toSelect = new ArrayList<PackagingElement<?>>();
+ editLayout(new Runnable() {
+ @Override
+ public void run() {
+ draggingObject.beforeDrop();
+ final CompositePackagingElement<?> parent = getOrCreateModifiableParent(targetElement, targetNode);
+ for (PackagingElement<?> element : draggingObject.createPackagingElements(myContext)) {
+ toSelect.add(element);
+ parent.addOrFindChild(element);
+ }
+ }
+ });
+ updateAndSelect(targetNode, toSelect);
+ myArtifactsEditor.getSourceItemsTree().rebuildTree();
+ }
+ }
+
+ @Nullable
+ private DefaultMutableTreeNode findParentCompositeElementNode(Point point) {
+ TreePath path = myTree.getPathForLocation(point.x, point.y);
+ while (path != null) {
+ final PackagingElement<?> element = myTree.getElementByPath(path);
+ if (element instanceof CompositePackagingElement) {
+ return (DefaultMutableTreeNode)path.getLastPathComponent();
+ }
+ path = path.getParentPath();
+ }
+ return null;
+ }
+
+ @Override
+ public void cleanUpOnLeave() {
+ }
+
+ @Override
+ public void updateDraggedImage(Image image, Point dropPoint, Point imageOffset) {
+ }
+
+ public void startRenaming(TreePath path) {
+ myTree.startEditingAtPath(path);
+ }
+
+ public boolean isEditing() {
+ return myTree.isEditing();
+ }
+
+ public void setRootElement(CompositePackagingElement<?> rootElement) {
+ myContext.getOrCreateModifiableArtifactModel().getOrCreateModifiableArtifact(myOriginalArtifact).setRootElement(rootElement);
+ myTreeStructure.updateRootElement();
+ final DefaultMutableTreeNode node = myTree.getRootNode();
+ node.setUserObject(myTreeStructure.getRootElement());
+ myBuilder.updateNode(node);
+ rebuildTree();
+ myArtifactsEditor.getSourceItemsTree().rebuildTree();
+ }
+
+ public CompositePackagingElement<?> getRootElement() {
+ return myContext.getRootElement(myOriginalArtifact);
+ }
+
+ public void updateTreeNodesPresentation() {
+ myBuilder.updateFromRoot(false);
+ }
+
+ public void updateRootNode() {
+ myBuilder.updateNode(myTree.getRootNode());
+ }
+
+ public void initTree() {
+ myBuilder.initRootNode();
+ mySelectedElementInfo.showPropertiesPanel();
+ }
+
+ public void putIntoDefaultLocations(@NotNull final List<? extends PackagingSourceItem> items) {
+ final List<PackagingElement<?>> toSelect = new ArrayList<PackagingElement<?>>();
+ editLayout(new Runnable() {
+ @Override
+ public void run() {
+ final CompositePackagingElement<?> rootElement = getArtifact().getRootElement();
+ final ArtifactType artifactType = getArtifact().getArtifactType();
+ for (PackagingSourceItem item : items) {
+ final String path = artifactType.getDefaultPathFor(item);
+ if (path != null) {
+ final CompositePackagingElement<?> directory = PackagingElementFactory.getInstance().getOrCreateDirectory(rootElement, path);
+ final List<? extends PackagingElement<?>> elements = item.createElements(myContext);
+ toSelect.addAll(directory.addOrFindChildren(elements));
+ }
+ }
+ }
+ });
+
+ myArtifactsEditor.getSourceItemsTree().rebuildTree();
+ updateAndSelect(myTree.getRootPackagingNode(), toSelect);
+ }
+
+ public void putElements(@NotNull final String path, @NotNull final List<? extends PackagingElement<?>> elements) {
+ final List<PackagingElement<?>> toSelect = new ArrayList<PackagingElement<?>>();
+ editLayout(new Runnable() {
+ @Override
+ public void run() {
+ final CompositePackagingElement<?> directory =
+ PackagingElementFactory.getInstance().getOrCreateDirectory(getArtifact().getRootElement(), path);
+ toSelect.addAll(directory.addOrFindChildren(elements));
+ }
+ });
+ myArtifactsEditor.getSourceItemsTree().rebuildTree();
+ updateAndSelect(myTree.getRootPackagingNode(), toSelect);
+ }
+
+ public void packInto(@NotNull final List<? extends PackagingSourceItem> items, final String pathToJar) {
+ final List<PackagingElement<?>> toSelect = new ArrayList<PackagingElement<?>>();
+ final CompositePackagingElement<?> rootElement = getArtifact().getRootElement();
+ editLayout(new Runnable() {
+ @Override
+ public void run() {
+ final CompositePackagingElement<?> archive = PackagingElementFactory.getInstance().getOrCreateArchive(rootElement, pathToJar);
+ for (PackagingSourceItem item : items) {
+ final List<? extends PackagingElement<?>> elements = item.createElements(myContext);
+ archive.addOrFindChildren(elements);
+ }
+ toSelect.add(archive);
+ }
+ });
+
+ myArtifactsEditor.getSourceItemsTree().rebuildTree();
+ updateAndSelect(myTree.getRootPackagingNode(), toSelect);
+ }
+
+ public boolean isPropertiesModified() {
+ final PackagingElementPropertiesPanel panel = mySelectedElementInfo.myCurrentPanel;
+ return panel != null && panel.isModified();
+ }
+
+ public void resetElementProperties() {
+ final PackagingElementPropertiesPanel panel = mySelectedElementInfo.myCurrentPanel;
+ if (panel != null) {
+ panel.reset();
+ }
+ }
+
+ public boolean isSortElements() {
+ return mySortElements;
+ }
+
+ private class SelectedElementInfo<E extends PackagingElement<?>> {
+ private final E myElement;
+ private PackagingElementPropertiesPanel myCurrentPanel;
+
+ private SelectedElementInfo(@Nullable E element) {
+ myElement = element;
+ if (myElement != null) {
+ //noinspection unchecked
+ myCurrentPanel = element.getType().createElementPropertiesPanel(myElement, myContext);
+ myPropertiesPanel.removeAll();
+ if (myCurrentPanel != null) {
+ myPropertiesPanel.add(BorderLayout.CENTER, ScrollPaneFactory.createScrollPane(myCurrentPanel.createComponent()));
+ myCurrentPanel.reset();
+ myPropertiesPanel.revalidate();
+ }
+ }
+ }
+
+ public void save() {
+ if (myCurrentPanel != null && myCurrentPanel.isModified()) {
+ editLayout(new Runnable() {
+ @Override
+ public void run() {
+ myCurrentPanel.apply();
+ }
+ });
+ }
+ }
+
+ public void showPropertiesPanel() {
+ final CardLayout cardLayout = (CardLayout)myPropertiesPanelWrapper.getLayout();
+ if (myCurrentPanel != null) {
+ cardLayout.show(myPropertiesPanelWrapper, PROPERTIES_CARD);
+ }
+ else {
+ cardLayout.show(myPropertiesPanelWrapper, EMPTY_CARD);
+ }
+ myPropertiesPanelWrapper.repaint();
+ }
+ }
+
+ private class LayoutTreeStructure extends SimpleTreeStructure {
+ private ArtifactRootNode myRootNode;
+
+ @Override
+ public Object getRootElement() {
+ if (myRootNode == null) {
+ myRootNode = PackagingTreeNodeFactory.createRootNode(myArtifactsEditor, myContext, mySubstitutionParameters, getArtifact().getArtifactType());
+ }
+ return myRootNode;
+ }
+
+ public void updateRootElement() {
+ myRootNode = null;
+ }
+ }
+
+ private class LayoutTreeBuilder extends SimpleTreeBuilder {
+ public LayoutTreeBuilder() {
+ super(LayoutTreeComponent.this.myTree, LayoutTreeComponent.this.myTree.getBuilderModel(), LayoutTreeComponent.this.myTreeStructure,
+ LayoutTreeComponent.this.getComparator());
+ }
+
+ @Override
+ public void updateNode(DefaultMutableTreeNode node) {
+ super.updateNode(node);
+ }
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/LayoutTreeSelection.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/LayoutTreeSelection.java
new file mode 100644
index 0000000..a575c29
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/LayoutTreeSelection.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2000-2009 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.artifacts;
+
+import com.intellij.openapi.roots.ui.configuration.artifacts.nodes.PackagingElementNode;
+import com.intellij.packaging.elements.PackagingElement;
+import com.intellij.packaging.elements.CompositePackagingElement;
+import com.intellij.ui.treeStructure.SimpleNode;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.tree.TreePath;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author nik
+ */
+public class LayoutTreeSelection {
+ private final List<PackagingElementNode<?>> mySelectedNodes = new ArrayList<PackagingElementNode<?>>();
+ private final List<PackagingElement<?>> mySelectedElements = new ArrayList<PackagingElement<?>>();
+ private final Map<PackagingElement<?>, PackagingElementNode<?>> myElement2Node = new HashMap<PackagingElement<?>, PackagingElementNode<?>>();
+ private final Map<PackagingElementNode<?>, TreePath> myNode2Path = new HashMap<PackagingElementNode<?>, TreePath>();
+
+ public LayoutTreeSelection(@NotNull LayoutTree tree) {
+ final TreePath[] paths = tree.getSelectionPaths();
+ if (paths == null) {
+ return;
+ }
+
+ for (TreePath path : paths) {
+ final SimpleNode node = tree.getNodeFor(path);
+ if (node instanceof PackagingElementNode) {
+ final PackagingElementNode<?> elementNode = (PackagingElementNode<?>)node;
+ mySelectedNodes.add(elementNode);
+ myNode2Path.put(elementNode, path);
+ for (PackagingElement<?> element : elementNode.getPackagingElements()) {
+ mySelectedElements.add(element);
+ myElement2Node.put(element, elementNode);
+ }
+ }
+ }
+ }
+
+ public List<PackagingElementNode<?>> getNodes() {
+ return mySelectedNodes;
+ }
+
+ public List<PackagingElement<?>> getElements() {
+ return mySelectedElements;
+ }
+
+ public PackagingElementNode<?> getNode(@NotNull PackagingElement<?> element) {
+ return myElement2Node.get(element);
+ }
+
+ public TreePath getPath(@NotNull PackagingElementNode<?> node) {
+ return myNode2Path.get(node);
+ }
+
+ @Nullable
+ public CompositePackagingElement<?> getCommonParentElement() {
+ CompositePackagingElement<?> commonParent = null;
+ for (PackagingElementNode<?> selectedNode : mySelectedNodes) {
+ final PackagingElement<?> element = selectedNode.getElementIfSingle();
+ if (element == null) return null;
+
+ final CompositePackagingElement<?> parentElement = selectedNode.getParentElement(element);
+ if (parentElement == null || commonParent != null && !commonParent.equals(parentElement)) {
+ return null;
+ }
+ commonParent = parentElement;
+ }
+ return commonParent;
+ }
+
+ @Nullable
+ public PackagingElement<?> getElementIfSingle() {
+ return mySelectedElements.size() == 1 ? mySelectedElements.get(0) : null;
+ }
+
+ @Nullable
+ public PackagingElementNode<?> getNodeIfSingle() {
+ return mySelectedNodes.size() == 1 ? mySelectedNodes.get(0) : null;
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/ManifestFilesInfo.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/ManifestFilesInfo.java
new file mode 100644
index 0000000..8f49ab1
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/ManifestFilesInfo.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2000-2009 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.artifacts;
+
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.vfs.LocalFileSystem;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.packaging.artifacts.ArtifactType;
+import com.intellij.packaging.elements.CompositePackagingElement;
+import com.intellij.packaging.elements.PackagingElementResolvingContext;
+import com.intellij.packaging.impl.elements.ManifestFileUtil;
+import com.intellij.packaging.ui.ManifestFileConfiguration;
+import org.jetbrains.annotations.Nullable;
+
+import java.io.File;
+import java.util.HashMap;
+import java.util.Map;
+
+public class ManifestFilesInfo {
+ private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.roots.ui.configuration.artifacts.ManifestFilesInfo");
+ private final Map<VirtualFile, ManifestFileConfiguration> myManifestFiles = new HashMap<VirtualFile, ManifestFileConfiguration>();
+ private final Map<VirtualFile, ManifestFileConfiguration> myOriginalManifestFiles = new HashMap<VirtualFile, ManifestFileConfiguration>();
+
+ @Nullable
+ public ManifestFileConfiguration getManifestFile(CompositePackagingElement<?> element, ArtifactType artifactType,
+ final PackagingElementResolvingContext context) {
+ final VirtualFile manifestFile = ManifestFileUtil.findManifestFile(element, context, artifactType);
+ if (manifestFile == null) {
+ return null;
+ }
+
+ ManifestFileConfiguration configuration = myManifestFiles.get(manifestFile);
+ if (configuration == null) {
+ configuration = ManifestFileUtil.createManifestFileConfiguration(manifestFile);
+ myOriginalManifestFiles.put(manifestFile, new ManifestFileConfiguration(configuration));
+ myManifestFiles.put(manifestFile, configuration);
+ }
+ return configuration;
+ }
+
+ public void saveManifestFiles() {
+ for (Map.Entry<VirtualFile, ManifestFileConfiguration> entry : myManifestFiles.entrySet()) {
+ final ManifestFileConfiguration configuration = entry.getValue();
+ final String path = configuration.getManifestFilePath();
+ if (path == null) continue;
+
+ final ManifestFileConfiguration original = myOriginalManifestFiles.get(entry.getKey());
+ if (original != null && original.equals(configuration)) {
+ continue;
+ }
+
+ VirtualFile file = LocalFileSystem.getInstance().findFileByPath(path);
+ if (file == null) {
+ final File ioFile = new File(FileUtil.toSystemDependentName(path));
+ FileUtil.createIfDoesntExist(ioFile);
+ file = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(ioFile);
+ if (file == null) {
+ //todo[nik] improve
+ LOG.error("cannot create file: " + ioFile);
+ }
+ }
+
+ ManifestFileUtil.updateManifest(file, configuration.getMainClass(), configuration.getClasspath(), true);
+ }
+ }
+
+ public boolean isManifestFilesModified() {
+ return !myOriginalManifestFiles.equals(myManifestFiles);
+ }
+
+ public void clear() {
+ myManifestFiles.clear();
+ myOriginalManifestFiles.clear();
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/PackagingElementDraggingObject.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/PackagingElementDraggingObject.java
new file mode 100644
index 0000000..d8064cc
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/PackagingElementDraggingObject.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2000-2009 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.artifacts;
+
+import com.intellij.openapi.roots.ui.configuration.artifacts.nodes.PackagingElementNode;
+import com.intellij.packaging.elements.CompositePackagingElement;
+import com.intellij.packaging.elements.PackagingElement;
+import com.intellij.packaging.ui.ArtifactEditorContext;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.List;
+
+/**
+ * @author nik
+ */
+public abstract class PackagingElementDraggingObject {
+ private PackagingElementNode<?> myTargetNode;
+ private CompositePackagingElement<?> myTargetElement;
+
+ public abstract List<PackagingElement<?>> createPackagingElements(ArtifactEditorContext context);
+
+ public void setTargetNode(PackagingElementNode<?> targetNode) {
+ myTargetNode = targetNode;
+ }
+
+ public PackagingElementNode<?> getTargetNode() {
+ return myTargetNode;
+ }
+
+ public CompositePackagingElement<?> getTargetElement() {
+ return myTargetElement;
+ }
+
+ public void setTargetElement(CompositePackagingElement<?> targetElement) {
+ myTargetElement = targetElement;
+ }
+
+ public boolean checkCanDrop() {
+ return true;
+ }
+
+ public void beforeDrop() {
+ }
+
+ public boolean canDropInto(@NotNull PackagingElementNode node) {
+ return true;
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/PlaceInArtifact.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/PlaceInArtifact.java
new file mode 100644
index 0000000..7e451e7
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/PlaceInArtifact.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2000-2011 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.artifacts;
+
+import com.intellij.openapi.roots.ui.configuration.ProjectStructureConfigurable;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.daemon.PlaceInProjectStructure;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.daemon.ProjectStructureElement;
+import com.intellij.openapi.util.ActionCallback;
+import com.intellij.packaging.artifacts.Artifact;
+import com.intellij.packaging.elements.PackagingElement;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * @author nik
+ */
+public class PlaceInArtifact extends PlaceInProjectStructure {
+ private final Artifact myArtifact;
+ private final ArtifactsStructureConfigurableContext myContext;
+ private final String myParentPath;
+ private final PackagingElement<?> myPackagingElement;
+
+ public PlaceInArtifact(Artifact artifact, ArtifactsStructureConfigurableContext context, @Nullable String parentPath,
+ @Nullable PackagingElement<?> packagingElement) {
+ myArtifact = artifact;
+ myContext = context;
+ myParentPath = parentPath;
+ myPackagingElement = packagingElement;
+ }
+
+ @NotNull
+ @Override
+ public ProjectStructureElement getContainingElement() {
+ return myContext.getOrCreateArtifactElement(myArtifact);
+ }
+
+ @Override
+ public String getPlacePath() {
+ if (myParentPath != null && myPackagingElement != null) {
+ //todo[nik] use id of element?
+ return myParentPath + "/" + myPackagingElement.getType().getId();
+ }
+ return null;
+ }
+
+ @NotNull
+ @Override
+ public ActionCallback navigate() {
+ final Artifact artifact = myContext.getArtifactModel().getArtifactByOriginal(myArtifact);
+ return ProjectStructureConfigurable.getInstance(myContext.getProject()).select(myArtifact, true).doWhenDone(new Runnable() {
+ @Override
+ public void run() {
+ final ArtifactEditorEx artifactEditor = (ArtifactEditorEx)myContext.getOrCreateEditor(artifact);
+ if (myParentPath != null && myPackagingElement != null) {
+ artifactEditor.getLayoutTreeComponent().selectNode(myParentPath, myPackagingElement);
+ }
+ }
+ });
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/SimpleDnDAwareTree.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/SimpleDnDAwareTree.java
new file mode 100644
index 0000000..de9e11d
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/SimpleDnDAwareTree.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2000-2009 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.artifacts;
+
+import com.intellij.ide.dnd.DnDAware;
+import com.intellij.ui.treeStructure.SimpleTree;
+import com.intellij.util.ui.tree.TreeUtil;
+import org.jetbrains.annotations.NotNull;
+
+import javax.swing.*;
+import java.awt.*;
+import java.awt.event.MouseEvent;
+
+/**
+ * @author nik
+ */
+public class SimpleDnDAwareTree extends SimpleTree implements DnDAware {
+ @Override
+ public void processMouseEvent(MouseEvent e) {
+ if (getToolTipText() == null && e.getID() == MouseEvent.MOUSE_ENTERED) return;
+ super.processMouseEvent(e);
+ }
+
+ @Override
+ public boolean isOverSelection(Point point) {
+ return TreeUtil.isOverSelection(this, point);
+ }
+
+ @Override
+ public void dropSelectionButUnderPoint(Point point) {
+ TreeUtil.dropSelectionButUnderPoint(this, point);
+ }
+
+ @Override
+ @NotNull
+ public JComponent getComponent() {
+ return this;
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/SourceItemsDraggingObject.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/SourceItemsDraggingObject.java
new file mode 100644
index 0000000..1d12599
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/SourceItemsDraggingObject.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2000-2009 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.artifacts;
+
+import com.intellij.packaging.elements.PackagingElement;
+import com.intellij.packaging.ui.PackagingSourceItem;
+import com.intellij.packaging.ui.ArtifactEditorContext;
+
+import java.util.List;
+import java.util.ArrayList;
+
+/**
+ * @author nik
+ */
+public class SourceItemsDraggingObject extends PackagingElementDraggingObject {
+ private final PackagingSourceItem[] mySourceItems;
+
+ public SourceItemsDraggingObject(PackagingSourceItem[] sourceItems) {
+ mySourceItems = sourceItems;
+ }
+
+ @Override
+ public List<PackagingElement<?>> createPackagingElements(ArtifactEditorContext context) {
+ final List<PackagingElement<?>> result = new ArrayList<PackagingElement<?>>();
+ for (PackagingSourceItem item : mySourceItems) {
+ result.addAll(item.createElements(context));
+ }
+ return result;
+ }
+
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/ToggleShowElementContentAction.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/ToggleShowElementContentAction.java
new file mode 100644
index 0000000..01bafac
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/ToggleShowElementContentAction.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2000-2009 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.artifacts;
+
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.actionSystem.ToggleAction;
+import com.intellij.openapi.project.DumbAware;
+import com.intellij.packaging.elements.ComplexPackagingElementType;
+
+/**
+ * @author nik
+ */
+public class ToggleShowElementContentAction extends ToggleAction implements DumbAware {
+ private final ComplexPackagingElementType<?> myType;
+ private final ArtifactEditorImpl myEditor;
+
+ public ToggleShowElementContentAction(ComplexPackagingElementType<?> type, ArtifactEditorImpl editor) {
+ super(type.getShowContentActionText());
+ myType = type;
+ myEditor = editor;
+ }
+
+ @Override
+ public boolean isSelected(AnActionEvent e) {
+ return myEditor.getSubstitutionParameters().isShowContentForType(myType);
+ }
+
+ @Override
+ public void setSelected(AnActionEvent e, boolean state) {
+ myEditor.getSubstitutionParameters().setShowContent(myType, state);
+ myEditor.updateShowContentCheckbox();
+ myEditor.getLayoutTreeComponent().rebuildTree();
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/UsageInArtifact.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/UsageInArtifact.java
new file mode 100644
index 0000000..c46b399
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/UsageInArtifact.java
@@ -0,0 +1,103 @@
+package com.intellij.openapi.roots.ui.configuration.artifacts;
+
+import com.intellij.openapi.roots.libraries.Library;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.daemon.LibraryProjectStructureElement;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.daemon.PlaceInProjectStructure;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.daemon.ProjectStructureElement;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.daemon.ProjectStructureElementUsage;
+import com.intellij.packaging.artifacts.Artifact;
+import com.intellij.packaging.elements.PackagingElement;
+import com.intellij.packaging.elements.PackagingElementFactory;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+
+/**
+ * @author nik
+ */
+public class UsageInArtifact extends ProjectStructureElementUsage {
+ private final Artifact myOriginalArtifact;
+ private final ArtifactsStructureConfigurableContext myContext;
+ private final ProjectStructureElement mySourceElement;
+ private final ProjectStructureElement myContainingElement;
+ private final String myParentPath;
+ private final PackagingElement<?> myPackagingElement;
+
+ public UsageInArtifact(Artifact originalArtifact, ArtifactsStructureConfigurableContext context, ProjectStructureElement sourceElement,
+ ArtifactProjectStructureElement containingElement,
+ String parentPath, PackagingElement<?> packagingElement) {
+ myOriginalArtifact = originalArtifact;
+ myContext = context;
+ mySourceElement = sourceElement;
+ myContainingElement = containingElement;
+ myParentPath = parentPath;
+ myPackagingElement = packagingElement;
+ }
+
+ @Override
+ public ProjectStructureElement getSourceElement() {
+ return mySourceElement;
+ }
+
+ @Override
+ public ProjectStructureElement getContainingElement() {
+ return myContainingElement;
+ }
+
+ public void removeElement() {
+ getOrCreateEditor().removePackagingElement(myParentPath, myPackagingElement);
+ }
+
+ private ArtifactEditorEx getOrCreateEditor() {
+ return (ArtifactEditorEx)myContext.getOrCreateEditor(myOriginalArtifact);
+ }
+
+ public void replaceElement(PackagingElement<?> replacement) {
+ getOrCreateEditor().replacePackagingElement(myParentPath, myPackagingElement, replacement);
+ }
+
+ @Override
+ public String getPresentableName() {
+ return myOriginalArtifact.getName();
+ }
+
+ @Override
+ public PlaceInProjectStructure getPlace() {
+ return new PlaceInArtifact(myOriginalArtifact, myContext, myParentPath, myPackagingElement);
+ }
+
+ @Override
+ public int hashCode() {
+ return myOriginalArtifact.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return obj instanceof UsageInArtifact && ((UsageInArtifact)obj).myOriginalArtifact.equals(myOriginalArtifact);
+ }
+
+ @Override
+ public Icon getIcon() {
+ return myOriginalArtifact.getArtifactType().getIcon();
+ }
+
+ @Nullable
+ @Override
+ public String getPresentableLocationInElement() {
+ return "[" + myParentPath + "]";
+ }
+
+ @Override
+ public void removeSourceElement() {
+ removeElement();
+ }
+
+ @Override
+ public void replaceElement(final ProjectStructureElement newElement) {
+ Library library = ((LibraryProjectStructureElement)newElement).getLibrary();
+ PackagingElement<?> newLibraryElement = PackagingElementFactory.getInstance().createLibraryFiles(library.getName(),
+ library.getTable().getTableLevel(),
+ null);
+ replaceElement(newLibraryElement);
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/actions/AddCompositeElementAction.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/actions/AddCompositeElementAction.java
new file mode 100644
index 0000000..0d0c79c
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/actions/AddCompositeElementAction.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2000-2009 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.artifacts.actions;
+
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.project.DumbAwareAction;
+import com.intellij.openapi.project.ProjectBundle;
+import com.intellij.openapi.roots.ui.configuration.artifacts.ArtifactEditorEx;
+import com.intellij.packaging.elements.CompositePackagingElementType;
+import com.intellij.packaging.elements.PackagingElementFactory;
+
+import java.util.List;
+
+/**
+ * @author nik
+ */
+public class AddCompositeElementAction extends DumbAwareAction {
+ private final ArtifactEditorEx myArtifactEditor;
+ private final CompositePackagingElementType<?> myElementType;
+
+ public AddCompositeElementAction(ArtifactEditorEx artifactEditor, CompositePackagingElementType elementType) {
+ super(ProjectBundle.message("artifacts.create.action", elementType.getPresentableName()));
+ myArtifactEditor = artifactEditor;
+ myElementType = elementType;
+ getTemplatePresentation().setIcon(elementType.getCreateElementIcon());
+ }
+
+ @Override
+ public void actionPerformed(AnActionEvent e) {
+ myArtifactEditor.addNewPackagingElement(myElementType);
+ }
+
+ public static void addCompositeCreateActions(List<AnAction> actions, final ArtifactEditorEx artifactEditor) {
+ for (CompositePackagingElementType packagingElementType : PackagingElementFactory.getInstance().getCompositeElementTypes()) {
+ actions.add(new AddCompositeElementAction(artifactEditor, packagingElementType));
+ }
+ }
+}
\ No newline at end of file
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/actions/AddNewPackagingElementAction.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/actions/AddNewPackagingElementAction.java
new file mode 100644
index 0000000..2b1bdd1
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/actions/AddNewPackagingElementAction.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2000-2009 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.artifacts.actions;
+
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.project.DumbAwareAction;
+import com.intellij.openapi.roots.ui.configuration.artifacts.ArtifactEditorEx;
+import com.intellij.packaging.elements.PackagingElementType;
+
+/**
+* @author nik
+*/
+public class AddNewPackagingElementAction extends DumbAwareAction {
+ private final PackagingElementType<?> myType;
+ private final ArtifactEditorEx myArtifactEditor;
+
+ public AddNewPackagingElementAction(PackagingElementType<?> type, ArtifactEditorEx artifactEditor) {
+ super(type.getPresentableName(), null, type.getCreateElementIcon());
+ myType = type;
+ myArtifactEditor = artifactEditor;
+ }
+
+ @Override
+ public void update(AnActionEvent e) {
+ e.getPresentation().setVisible(myType.canCreate(myArtifactEditor.getContext(), myArtifactEditor.getArtifact()));
+ }
+
+ @Override
+ public void actionPerformed(AnActionEvent e) {
+ myArtifactEditor.addNewPackagingElement(myType);
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/actions/ArtifactEditorFindUsagesActionBase.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/actions/ArtifactEditorFindUsagesActionBase.java
new file mode 100644
index 0000000..a23609c
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/actions/ArtifactEditorFindUsagesActionBase.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2000-2009 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.artifacts.actions;
+
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.roots.ui.configuration.artifacts.ArtifactsStructureConfigurableContext;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.FindUsagesInProjectStructureActionBase;
+import com.intellij.ui.awt.RelativePoint;
+import com.intellij.ui.treeStructure.Tree;
+
+import java.awt.*;
+
+/**
+ * @author nik
+ */
+public abstract class ArtifactEditorFindUsagesActionBase extends FindUsagesInProjectStructureActionBase {
+ private final Tree myTree;
+ protected final ArtifactsStructureConfigurableContext myArtifactContext;
+
+ public ArtifactEditorFindUsagesActionBase(Tree tree, Project project, ArtifactsStructureConfigurableContext artifactContext) {
+ super(tree, project);
+ myTree = tree;
+ myArtifactContext = artifactContext;
+ }
+
+ @Override
+ protected boolean isEnabled() {
+ return getSelectedElement() != null;
+ }
+
+ @Override
+ protected RelativePoint getPointToShowResults() {
+ final int selectedRow = myTree.getSelectionRows()[0];
+ final Rectangle rowBounds = myTree.getRowBounds(selectedRow);
+ final Point location = rowBounds.getLocation();
+ location.y += rowBounds.height;
+ return new RelativePoint(myTree, location);
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/actions/ArtifactEditorNavigateActionBase.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/actions/ArtifactEditorNavigateActionBase.java
new file mode 100644
index 0000000..8bebb6d
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/actions/ArtifactEditorNavigateActionBase.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2000-2009 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.artifacts.actions;
+
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.actionSystem.CommonShortcuts;
+import com.intellij.openapi.project.DumbAwareAction;
+import com.intellij.openapi.project.ProjectBundle;
+import com.intellij.packaging.ui.TreeNodePresentation;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+
+/**
+ * @author nik
+ */
+public abstract class ArtifactEditorNavigateActionBase extends DumbAwareAction {
+ public ArtifactEditorNavigateActionBase(JComponent contextComponent) {
+ super(ProjectBundle.message("action.name.facet.navigate"));
+ registerCustomShortcutSet(CommonShortcuts.getEditSource(), contextComponent);
+ }
+
+ @Override
+ public void update(final AnActionEvent e) {
+ final TreeNodePresentation presentation = getPresentation();
+ e.getPresentation().setEnabled(presentation != null && presentation.canNavigateToSource());
+ }
+
+ @Nullable
+ protected abstract TreeNodePresentation getPresentation();
+
+ @Override
+ public void actionPerformed(final AnActionEvent e) {
+ final TreeNodePresentation presentation = getPresentation();
+ if (presentation != null) {
+ presentation.navigateToSource();
+ }
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/actions/ExtractArtifactAction.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/actions/ExtractArtifactAction.java
new file mode 100644
index 0000000..bbd0be5
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/actions/ExtractArtifactAction.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2000-2009 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.artifacts.actions;
+
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.project.ProjectBundle;
+import com.intellij.openapi.roots.ui.configuration.artifacts.ArtifactEditorEx;
+import com.intellij.openapi.roots.ui.configuration.artifacts.LayoutTreeComponent;
+import com.intellij.openapi.roots.ui.configuration.artifacts.LayoutTreeSelection;
+import com.intellij.openapi.roots.ui.configuration.artifacts.nodes.PackagingElementNode;
+import com.intellij.packaging.artifacts.ArtifactPointerManager;
+import com.intellij.packaging.artifacts.ModifiableArtifact;
+import com.intellij.packaging.artifacts.ModifiableArtifactModel;
+import com.intellij.packaging.elements.CompositePackagingElement;
+import com.intellij.packaging.elements.PackagingElement;
+import com.intellij.packaging.impl.artifacts.ArtifactUtil;
+import com.intellij.packaging.impl.elements.ArtifactPackagingElement;
+import com.intellij.util.PathUtil;
+import com.intellij.util.containers.ContainerUtil;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.Collection;
+
+/**
+ * @author nik
+ */
+public class ExtractArtifactAction extends LayoutTreeActionBase {
+ public ExtractArtifactAction(ArtifactEditorEx editor) {
+ super(ProjectBundle.message("action.name.extract.artifact"), editor);
+ }
+
+ @Override
+ protected boolean isEnabled() {
+ return myArtifactEditor.getLayoutTreeComponent().getSelection().getCommonParentElement() != null;
+ }
+
+ @Override
+ public void actionPerformed(AnActionEvent e) {
+ final LayoutTreeComponent treeComponent = myArtifactEditor.getLayoutTreeComponent();
+ final LayoutTreeSelection selection = treeComponent.getSelection();
+ final CompositePackagingElement<?> parent = selection.getCommonParentElement();
+ if (parent == null) return;
+ final PackagingElementNode<?> parentNode = selection.getNodes().get(0).getParentNode();
+ if (parentNode == null) return;
+
+ if (!treeComponent.checkCanModifyChildren(parent, parentNode, selection.getNodes())) {
+ return;
+ }
+
+
+ final Collection<? extends PackagingElement> selectedElements = selection.getElements();
+ String initialName = "artifact";
+ if (selectedElements.size() == 1) {
+ initialName = PathUtil.suggestFileName(ContainerUtil.getFirstItem(selectedElements, null).createPresentation(myArtifactEditor.getContext()).getPresentableName());
+ }
+ IExtractArtifactDialog dialog = showDialog(treeComponent, initialName);
+ if (dialog == null) return;
+
+ final Project project = myArtifactEditor.getContext().getProject();
+ final ModifiableArtifactModel model = myArtifactEditor.getContext().getOrCreateModifiableArtifactModel();
+ final ModifiableArtifact artifact = model.addArtifact(dialog.getArtifactName(), dialog.getArtifactType());
+ treeComponent.editLayout(new Runnable() {
+ @Override
+ public void run() {
+ for (PackagingElement<?> element : selectedElements) {
+ artifact.getRootElement().addOrFindChild(ArtifactUtil.copyWithChildren(element, project));
+ }
+ for (PackagingElement element : selectedElements) {
+ parent.removeChild(element);
+ }
+ parent.addOrFindChild(new ArtifactPackagingElement(project, ArtifactPointerManager.getInstance(project).createPointer(artifact, myArtifactEditor.getContext().getArtifactModel())));
+ }
+ });
+ treeComponent.rebuildTree();
+ }
+
+ @Nullable
+ protected IExtractArtifactDialog showDialog(LayoutTreeComponent treeComponent, String initialName) {
+ final ExtractArtifactDialog dialog = new ExtractArtifactDialog(myArtifactEditor.getContext(), treeComponent, initialName);
+ dialog.show();
+ if (!dialog.isOK()) {
+ return null;
+ }
+ return dialog;
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/actions/ExtractArtifactDialog.form b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/actions/ExtractArtifactDialog.form
new file mode 100644
index 0000000..d7499bf
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/actions/ExtractArtifactDialog.form
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.intellij.openapi.roots.ui.configuration.artifacts.actions.ExtractArtifactDialog">
+ <grid id="27dc6" binding="myMainPanel" layout-manager="GridLayoutManager" row-count="5" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+ <margin top="0" left="0" bottom="0" right="0"/>
+ <constraints>
+ <xy x="20" y="20" width="500" height="400"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <component id="7d6b1" class="javax.swing.JLabel">
+ <constraints>
+ <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties>
+ <labelFor value="e88c4"/>
+ <text resource-bundle="messages/ProjectBundle" key="label.text.specify.artifact.name"/>
+ </properties>
+ </component>
+ <vspacer id="d3681">
+ <constraints>
+ <grid row="4" column="0" row-span="1" col-span="1" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/>
+ </constraints>
+ </vspacer>
+ <component id="e88c4" class="javax.swing.JTextField" binding="myNameField">
+ <constraints>
+ <grid row="1" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="8" fill="1" indent="1" use-parent-layout="false">
+ <preferred-size width="150" height="-1"/>
+ </grid>
+ </constraints>
+ <properties/>
+ </component>
+ <component id="facb9" class="javax.swing.JLabel">
+ <constraints>
+ <grid row="2" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties>
+ <labelFor value="b2bb9"/>
+ <text value="Select artifact &type:"/>
+ </properties>
+ </component>
+ <component id="b2bb9" class="javax.swing.JComboBox" binding="myTypeBox">
+ <constraints>
+ <grid row="3" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="2" anchor="8" fill="0" indent="1" use-parent-layout="false"/>
+ </constraints>
+ <properties/>
+ </component>
+ </children>
+ </grid>
+</form>
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/actions/ExtractArtifactDialog.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/actions/ExtractArtifactDialog.java
new file mode 100644
index 0000000..478e483
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/actions/ExtractArtifactDialog.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2000-2011 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.artifacts.actions;
+
+import com.intellij.CommonBundle;
+import com.intellij.openapi.project.ProjectBundle;
+import com.intellij.openapi.roots.ui.configuration.artifacts.ArtifactTypeCellRenderer;
+import com.intellij.openapi.roots.ui.configuration.artifacts.LayoutTreeComponent;
+import com.intellij.openapi.ui.DialogWrapper;
+import com.intellij.openapi.ui.Messages;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.packaging.artifacts.ArtifactType;
+import com.intellij.packaging.impl.artifacts.PlainArtifactType;
+import com.intellij.packaging.ui.ArtifactEditorContext;
+import com.intellij.ui.DocumentAdapter;
+
+import javax.swing.*;
+import javax.swing.event.DocumentEvent;
+
+/**
+ * @author nik
+ */
+public class ExtractArtifactDialog extends DialogWrapper implements IExtractArtifactDialog {
+ private JPanel myMainPanel;
+ private JTextField myNameField;
+ private JComboBox myTypeBox;
+ private final ArtifactEditorContext myContext;
+
+ public ExtractArtifactDialog(ArtifactEditorContext context, LayoutTreeComponent treeComponent, String initialName) {
+ super(treeComponent.getLayoutTree(), true);
+ myContext = context;
+ setTitle(ProjectBundle.message("dialog.title.extract.artifact"));
+ for (ArtifactType type : ArtifactType.getAllTypes()) {
+ myTypeBox.addItem(type);
+ }
+ myTypeBox.setSelectedItem(PlainArtifactType.getInstance());
+ myTypeBox.setRenderer(new ArtifactTypeCellRenderer(myTypeBox.getRenderer()));
+ myNameField.getDocument().addDocumentListener(new DocumentAdapter() {
+ @Override
+ protected void textChanged(DocumentEvent e) {
+ setOKActionEnabled(!StringUtil.isEmptyOrSpaces(getArtifactName()));
+ }
+ });
+ myNameField.setText(initialName);
+ init();
+ }
+
+ @Override
+ protected void doOKAction() {
+ final String artifactName = getArtifactName();
+ if (myContext.getArtifactModel().findArtifact(artifactName) != null) {
+ Messages.showErrorDialog(myContext.getProject(), "Artifact '" + artifactName + "' already exists!", CommonBundle.getErrorTitle());
+ return;
+ }
+ super.doOKAction();
+ }
+
+ @Override
+ protected JComponent createCenterPanel() {
+ return myMainPanel;
+ }
+
+ @Override
+ public String getArtifactName() {
+ return myNameField.getText();
+ }
+
+ @Override
+ public ArtifactType getArtifactType() {
+ return (ArtifactType)myTypeBox.getSelectedItem();
+ }
+
+ @Override
+ public JComponent getPreferredFocusedComponent() {
+ return myNameField;
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/actions/HideContentAction.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/actions/HideContentAction.java
new file mode 100644
index 0000000..42662d1
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/actions/HideContentAction.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2000-2009 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.artifacts.actions;
+
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.project.DumbAwareAction;
+import com.intellij.openapi.roots.ui.configuration.artifacts.ArtifactEditorEx;
+import com.intellij.openapi.roots.ui.configuration.artifacts.LayoutTreeSelection;
+import com.intellij.openapi.roots.ui.configuration.artifacts.nodes.PackagingElementNode;
+import com.intellij.openapi.roots.ui.configuration.artifacts.nodes.PackagingNodeSource;
+
+import java.util.Collection;
+
+/**
+ * @author nik
+ */
+public class HideContentAction extends DumbAwareAction {
+ private final ArtifactEditorEx myArtifactEditor;
+
+ public HideContentAction(ArtifactEditorEx artifactEditor) {
+ super("Hide Content");
+ myArtifactEditor = artifactEditor;
+ }
+
+ @Override
+ public void update(AnActionEvent e) {
+ final LayoutTreeSelection selection = myArtifactEditor.getLayoutTreeComponent().getSelection();
+ final PackagingElementNode<?> node = selection.getNodeIfSingle();
+ if (node != null) {
+ final Collection<PackagingNodeSource> sources = node.getNodeSources();
+ if (!sources.isEmpty()) {
+ String description;
+ if (sources.size() == 1) {
+ description = "Hide Content of '" + sources.iterator().next().getPresentableName() + "'";
+ }
+ else {
+ description = "Hide Content";
+ }
+ e.getPresentation().setVisible(true);
+ e.getPresentation().setText(description);
+ return;
+ }
+ }
+ e.getPresentation().setVisible(false);
+ }
+
+ @Override
+ public void actionPerformed(AnActionEvent e) {
+ final LayoutTreeSelection selection = myArtifactEditor.getLayoutTreeComponent().getSelection();
+ final PackagingElementNode<?> node = selection.getNodeIfSingle();
+ if (node == null) return;
+
+ final Collection<PackagingNodeSource> sources = node.getNodeSources();
+ for (PackagingNodeSource source : sources) {
+ myArtifactEditor.getSubstitutionParameters().doNotSubstitute(source.getSourceElement());
+ myArtifactEditor.getLayoutTreeComponent().getLayoutTree().addSubtreeToUpdate(source.getSourceParentNode());
+ }
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/actions/IExtractArtifactDialog.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/actions/IExtractArtifactDialog.java
new file mode 100644
index 0000000..dd73177
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/actions/IExtractArtifactDialog.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2000-2010 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.artifacts.actions;
+
+import com.intellij.packaging.artifacts.ArtifactType;
+
+/**
+ * @author nik
+ */
+public interface IExtractArtifactDialog {
+ String getArtifactName();
+
+ ArtifactType getArtifactType();
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/actions/InlineArtifactAction.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/actions/InlineArtifactAction.java
new file mode 100644
index 0000000..b54b57c
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/actions/InlineArtifactAction.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2000-2009 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.artifacts.actions;
+
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.project.DumbAwareAction;
+import com.intellij.openapi.project.ProjectBundle;
+import com.intellij.openapi.roots.ui.configuration.artifacts.ArtifactEditorEx;
+import com.intellij.openapi.roots.ui.configuration.artifacts.LayoutTreeComponent;
+import com.intellij.openapi.roots.ui.configuration.artifacts.LayoutTreeSelection;
+import com.intellij.openapi.roots.ui.configuration.artifacts.nodes.CompositePackagingElementNode;
+import com.intellij.openapi.roots.ui.configuration.artifacts.nodes.PackagingElementNode;
+import com.intellij.packaging.artifacts.Artifact;
+import com.intellij.packaging.elements.ArtifactRootElement;
+import com.intellij.packaging.elements.CompositePackagingElement;
+import com.intellij.packaging.elements.PackagingElement;
+import com.intellij.packaging.impl.artifacts.ArtifactUtil;
+import com.intellij.packaging.impl.elements.ArtifactPackagingElement;
+import com.intellij.packaging.ui.ArtifactEditorContext;
+
+import java.util.Collections;
+
+/**
+ * @author nik
+ */
+public class InlineArtifactAction extends DumbAwareAction {
+ private final ArtifactEditorEx myEditor;
+
+ public InlineArtifactAction(ArtifactEditorEx editor) {
+ super(ProjectBundle.message("action.name.inline.artifact"));
+ myEditor = editor;
+ }
+
+ @Override
+ public void update(AnActionEvent e) {
+ final LayoutTreeSelection selection = myEditor.getLayoutTreeComponent().getSelection();
+ final PackagingElementNode<?> node = selection.getNodeIfSingle();
+ PackagingElement<?> element = selection.getElementIfSingle();
+ e.getPresentation().setEnabled(element instanceof ArtifactPackagingElement && node != null && node.getParentElement(element) != null);
+ }
+
+ @Override
+ public void actionPerformed(AnActionEvent e) {
+ final LayoutTreeComponent treeComponent = myEditor.getLayoutTreeComponent();
+ final LayoutTreeSelection selection = treeComponent.getSelection();
+ final PackagingElement<?> element = selection.getElementIfSingle();
+ final PackagingElementNode<?> node = selection.getNodeIfSingle();
+ if (node == null || !(element instanceof ArtifactPackagingElement)) return;
+
+ final CompositePackagingElement<?> parent = node.getParentElement(element);
+ final CompositePackagingElementNode parentNode = node.getParentNode();
+ if (parent == null || parentNode == null) {
+ return;
+ }
+ if (!treeComponent.checkCanModifyChildren(parent, parentNode, Collections.singletonList(node))) return;
+
+ treeComponent.editLayout(new Runnable() {
+ @Override
+ public void run() {
+ parent.removeChild(element);
+ final ArtifactEditorContext context = myEditor.getContext();
+ final Artifact artifact = ((ArtifactPackagingElement)element).findArtifact(context);
+ if (artifact != null) {
+ final CompositePackagingElement<?> rootElement = artifact.getRootElement();
+ if (rootElement instanceof ArtifactRootElement<?>) {
+ for (PackagingElement<?> child : rootElement.getChildren()) {
+ parent.addOrFindChild(ArtifactUtil.copyWithChildren(child, context.getProject()));
+ }
+ }
+ else {
+ parent.addOrFindChild(ArtifactUtil.copyWithChildren(rootElement, context.getProject()));
+ }
+ }
+ }
+ });
+ treeComponent.rebuildTree();
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/actions/LayoutTreeActionBase.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/actions/LayoutTreeActionBase.java
new file mode 100644
index 0000000..944c959
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/actions/LayoutTreeActionBase.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2000-2009 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.artifacts.actions;
+
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.project.DumbAwareAction;
+import com.intellij.openapi.roots.ui.configuration.artifacts.ArtifactEditorEx;
+
+import javax.swing.*;
+
+/**
+ * @author nik
+ */
+public abstract class LayoutTreeActionBase extends DumbAwareAction {
+ protected final ArtifactEditorEx myArtifactEditor;
+
+ protected LayoutTreeActionBase(String text, String description, Icon icon, ArtifactEditorEx artifactEditor) {
+ super(text, description, icon);
+ myArtifactEditor = artifactEditor;
+ }
+
+ protected LayoutTreeActionBase(String text, ArtifactEditorEx artifactEditor) {
+ super(text);
+ myArtifactEditor = artifactEditor;
+ }
+
+ @Override
+ public void update(AnActionEvent e) {
+ e.getPresentation().setEnabled(isEnabled());
+ }
+
+ protected abstract boolean isEnabled();
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/actions/LayoutTreeFindUsagesAction.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/actions/LayoutTreeFindUsagesAction.java
new file mode 100644
index 0000000..4a9d29f
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/actions/LayoutTreeFindUsagesAction.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2000-2009 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.artifacts.actions;
+
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.roots.ui.configuration.artifacts.ArtifactProjectStructureElement;
+import com.intellij.openapi.roots.ui.configuration.artifacts.ArtifactsStructureConfigurableContext;
+import com.intellij.openapi.roots.ui.configuration.artifacts.LayoutTreeComponent;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.daemon.ProjectStructureElement;
+import com.intellij.packaging.elements.PackagingElement;
+
+/**
+* @author nik
+*/
+public class LayoutTreeFindUsagesAction extends ArtifactEditorFindUsagesActionBase {
+ private final LayoutTreeComponent myLayoutTreeComponent;
+
+ public LayoutTreeFindUsagesAction(LayoutTreeComponent layoutTreeComponent, Project project, ArtifactsStructureConfigurableContext artifactContext) {
+ super(layoutTreeComponent.getLayoutTree(), project, artifactContext);
+ myLayoutTreeComponent = layoutTreeComponent;
+ }
+
+ @Override
+ protected ProjectStructureElement getSelectedElement() {
+ PackagingElement<?> packagingElement = myLayoutTreeComponent.getSelection().getElementIfSingle();
+ if (packagingElement == null) return null;
+
+ return ArtifactProjectStructureElement.getProjectStructureElementFor(packagingElement, getContext(), myArtifactContext);
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/actions/LayoutTreeNavigateAction.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/actions/LayoutTreeNavigateAction.java
new file mode 100644
index 0000000..951950e
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/actions/LayoutTreeNavigateAction.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2000-2009 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.artifacts.actions;
+
+import com.intellij.openapi.roots.ui.configuration.artifacts.LayoutTreeComponent;
+import com.intellij.openapi.roots.ui.configuration.artifacts.nodes.PackagingElementNode;
+import com.intellij.packaging.ui.TreeNodePresentation;
+import org.jetbrains.annotations.Nullable;
+
+/**
+* @author nik
+*/
+public class LayoutTreeNavigateAction extends ArtifactEditorNavigateActionBase {
+ private final LayoutTreeComponent myLayoutTreeComponent;
+
+ public LayoutTreeNavigateAction(LayoutTreeComponent layoutTreeComponent) {
+ super(layoutTreeComponent.getLayoutTree());
+ myLayoutTreeComponent = layoutTreeComponent;
+ }
+
+ @Override
+ @Nullable
+ protected TreeNodePresentation getPresentation() {
+ PackagingElementNode<?> node = myLayoutTreeComponent.getSelection().getNodeIfSingle();
+ return node != null ? node.getElementPresentation() : null;
+ }
+
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/actions/MovePackagingElementAction.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/actions/MovePackagingElementAction.java
new file mode 100644
index 0000000..85e316d
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/actions/MovePackagingElementAction.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2000-2009 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.artifacts.actions;
+
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.project.DumbAwareAction;
+import com.intellij.openapi.roots.ui.configuration.artifacts.LayoutTreeComponent;
+import com.intellij.openapi.roots.ui.configuration.artifacts.nodes.CompositePackagingElementNode;
+import com.intellij.openapi.roots.ui.configuration.artifacts.nodes.PackagingElementNode;
+import com.intellij.packaging.elements.CompositePackagingElement;
+import com.intellij.packaging.elements.PackagingElement;
+
+import javax.swing.*;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * @author nik
+ */
+public class MovePackagingElementAction extends DumbAwareAction {
+ private final LayoutTreeComponent myLayoutTreeComponent;
+ private final int myDirection;
+
+ public MovePackagingElementAction(LayoutTreeComponent layoutTreeComponent, String text, String description, Icon icon, int direction) {
+ super(text, description, icon);
+ myLayoutTreeComponent = layoutTreeComponent;
+ myDirection = direction;
+ }
+
+ @Override
+ public void update(AnActionEvent e) {
+ final boolean b = isEnabled();
+ e.getPresentation().setEnabled(b);
+ e.getPresentation().setText(getTemplatePresentation().getText() + " (disabled if elements are sorted)");
+ }
+
+ private boolean isEnabled() {
+ if (myLayoutTreeComponent.isSortElements()) {
+ return false;
+ }
+ final PackagingElementNode<?> node = myLayoutTreeComponent.getSelection().getNodeIfSingle();
+ if (node == null) {
+ return false;
+ }
+ final CompositePackagingElementNode parent = node.getParentNode();
+ if (parent == null) return false;
+
+ final PackagingElement<?> element = node.getElementIfSingle();
+ final CompositePackagingElement<?> parentElement = parent.getElementIfSingle();
+ if (parentElement == null || element == null) return false;
+ final List<PackagingElement<?>> children = parentElement.getChildren();
+ final int index = children.indexOf(element);
+ return index != -1 && 0 <= index + myDirection && index + myDirection < children.size();
+ }
+
+ @Override
+ public void actionPerformed(AnActionEvent e) {
+ final PackagingElementNode<?> node = myLayoutTreeComponent.getSelection().getNodeIfSingle();
+ if (node == null) return;
+ final CompositePackagingElementNode parent = node.getParentNode();
+ if (parent == null) return;
+
+ final PackagingElement<?> element = node.getElementIfSingle();
+ final CompositePackagingElement<?> parentElement = parent.getElementIfSingle();
+ if (parentElement == null || element == null) return;
+
+
+ if (!myLayoutTreeComponent.checkCanModifyChildren(parentElement, parent, Arrays.asList(node))) return;
+
+ final List<PackagingElement<?>> toSelect = new ArrayList<PackagingElement<?>>();
+ myLayoutTreeComponent.editLayout(new Runnable() {
+ @Override
+ public void run() {
+ final int index = parentElement.getChildren().indexOf(element);
+ final PackagingElement<?> moved = parentElement.moveChild(index, myDirection);
+ if (moved != null) {
+ toSelect.add(moved);
+ }
+ }
+ });
+ if (!toSelect.isEmpty()) {
+ myLayoutTreeComponent.updateAndSelect(parent, toSelect);
+ }
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/actions/RemovePackagingElementAction.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/actions/RemovePackagingElementAction.java
new file mode 100644
index 0000000..4593ff5
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/actions/RemovePackagingElementAction.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2000-2009 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.artifacts.actions;
+
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.project.ProjectBundle;
+import com.intellij.openapi.roots.ui.configuration.artifacts.ArtifactEditorEx;
+import com.intellij.openapi.roots.ui.configuration.artifacts.LayoutTreeSelection;
+import com.intellij.openapi.roots.ui.configuration.artifacts.nodes.PackagingElementNode;
+import com.intellij.util.PlatformIcons;
+
+/**
+ * @author nik
+ */
+public class RemovePackagingElementAction extends LayoutTreeActionBase {
+
+ public RemovePackagingElementAction(ArtifactEditorEx artifactEditor) {
+ super(ProjectBundle.message("action.name.remove.packaging.element"), ProjectBundle.message("action.description.remove.packaging.elements"), PlatformIcons.DELETE_ICON,
+ artifactEditor);
+ }
+
+ @Override
+ protected boolean isEnabled() {
+ final LayoutTreeSelection selection = myArtifactEditor.getLayoutTreeComponent().getSelection();
+ if (selection.getElements().isEmpty() || myArtifactEditor.getLayoutTreeComponent().isEditing()) {
+ return false;
+ }
+ for (PackagingElementNode<?> node : selection.getNodes()) {
+ if (node.getParentNode() == null) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public void actionPerformed(AnActionEvent e) {
+ myArtifactEditor.removeSelectedElements();
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/actions/RenamePackagingElementAction.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/actions/RenamePackagingElementAction.java
new file mode 100644
index 0000000..dd95cc3
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/actions/RenamePackagingElementAction.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2000-2009 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.artifacts.actions;
+
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.actionSystem.CommonShortcuts;
+import com.intellij.openapi.project.DumbAwareAction;
+import com.intellij.openapi.project.ProjectBundle;
+import com.intellij.openapi.roots.ui.configuration.artifacts.ArtifactEditorEx;
+import com.intellij.openapi.roots.ui.configuration.artifacts.LayoutTreeSelection;
+import com.intellij.openapi.roots.ui.configuration.artifacts.nodes.PackagingElementNode;
+import com.intellij.packaging.elements.PackagingElement;
+import com.intellij.packaging.elements.RenameablePackagingElement;
+
+import javax.swing.tree.TreePath;
+
+/**
+ * @author nik
+ */
+public class RenamePackagingElementAction extends DumbAwareAction {
+ private final ArtifactEditorEx myArtifactEditor;
+
+ public RenamePackagingElementAction(ArtifactEditorEx artifactEditor) {
+ super(ProjectBundle.message("action.name.rename.packaging.element"));
+ registerCustomShortcutSet(CommonShortcuts.getRename(), artifactEditor.getLayoutTreeComponent().getTreePanel());
+ myArtifactEditor = artifactEditor;
+ }
+
+ @Override
+ public void update(AnActionEvent e) {
+ final LayoutTreeSelection selection = myArtifactEditor.getLayoutTreeComponent().getSelection();
+ final PackagingElement<?> element = selection.getElementIfSingle();
+ final boolean visible = element instanceof RenameablePackagingElement && ((RenameablePackagingElement)element).canBeRenamed();
+ e.getPresentation().setEnabled(visible);
+ e.getPresentation().setVisible(visible);
+ }
+
+ @Override
+ public void actionPerformed(AnActionEvent e) {
+ final LayoutTreeSelection selection = myArtifactEditor.getLayoutTreeComponent().getSelection();
+ final PackagingElementNode<?> node = selection.getNodeIfSingle();
+ final PackagingElement<?> element = selection.getElementIfSingle();
+ if (node == null || element == null) return;
+ if (!myArtifactEditor.getLayoutTreeComponent().checkCanModify(element, node)) return;
+
+ final TreePath path = selection.getPath(node);
+ myArtifactEditor.getLayoutTreeComponent().startRenaming(path);
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/actions/ShowAddPackagingElementPopupAction.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/actions/ShowAddPackagingElementPopupAction.java
new file mode 100644
index 0000000..5966375
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/actions/ShowAddPackagingElementPopupAction.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2000-2009 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.artifacts.actions;
+
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.actionSystem.DataContext;
+import com.intellij.openapi.actionSystem.DefaultActionGroup;
+import com.intellij.openapi.project.DumbAwareAction;
+import com.intellij.openapi.roots.ui.configuration.artifacts.ArtifactEditorEx;
+import com.intellij.openapi.ui.popup.JBPopupFactory;
+import com.intellij.openapi.ui.popup.ListPopup;
+import com.intellij.packaging.elements.PackagingElementFactory;
+import com.intellij.packaging.elements.PackagingElementType;
+
+/**
+ * @author nik
+ */
+public class ShowAddPackagingElementPopupAction extends DumbAwareAction {
+ private final ArtifactEditorEx myArtifactEditor;
+
+ public ShowAddPackagingElementPopupAction(ArtifactEditorEx artifactEditor) {
+ super("Add...");
+ myArtifactEditor = artifactEditor;
+ }
+
+ @Override
+ public void actionPerformed(AnActionEvent e) {
+ final DefaultActionGroup group = new DefaultActionGroup();
+ for (PackagingElementType type : PackagingElementFactory.getInstance().getAllElementTypes()) {
+ group.add(new AddNewPackagingElementAction((PackagingElementType<?>)type, myArtifactEditor));
+ }
+ final DataContext dataContext = e.getDataContext();
+ final ListPopup popup = JBPopupFactory.getInstance().createActionGroupPopup("Add", group, dataContext, JBPopupFactory.ActionSelectionAid.SPEEDSEARCH, false);
+ popup.showInBestPositionFor(dataContext);
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/actions/SortElementsToggleAction.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/actions/SortElementsToggleAction.java
new file mode 100644
index 0000000..c5dd2a7
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/actions/SortElementsToggleAction.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2000-2009 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.artifacts.actions;
+
+import com.intellij.icons.AllIcons;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.actionSystem.ToggleAction;
+import com.intellij.openapi.project.DumbAware;
+import com.intellij.openapi.roots.ui.configuration.artifacts.LayoutTreeComponent;
+
+/**
+ * @author nik
+ */
+public class SortElementsToggleAction extends ToggleAction implements DumbAware {
+ private final LayoutTreeComponent myLayoutTreeComponent;
+
+ public SortElementsToggleAction(final LayoutTreeComponent layoutTreeComponent) {
+ super("Sort Elements by Names and Types", "Sort Elements by Names and Types", AllIcons.ObjectBrowser.Sorted);
+ myLayoutTreeComponent = layoutTreeComponent;
+ }
+
+ @Override
+ public boolean isSelected(AnActionEvent e) {
+ return myLayoutTreeComponent.isSortElements();
+ }
+
+ @Override
+ public void setSelected(AnActionEvent e, boolean state) {
+ myLayoutTreeComponent.setSortElements(state);
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/actions/SurroundElementWithAction.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/actions/SurroundElementWithAction.java
new file mode 100644
index 0000000..108eb7c
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/actions/SurroundElementWithAction.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright 2000-2010 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.artifacts.actions;
+
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.actionSystem.CustomShortcutSet;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.keymap.KeymapManager;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.roots.ui.configuration.artifacts.ArtifactEditorEx;
+import com.intellij.openapi.roots.ui.configuration.artifacts.LayoutTreeComponent;
+import com.intellij.openapi.roots.ui.configuration.artifacts.LayoutTreeSelection;
+import com.intellij.openapi.roots.ui.configuration.artifacts.nodes.PackagingElementNode;
+import com.intellij.openapi.ui.popup.JBPopupFactory;
+import com.intellij.openapi.ui.popup.PopupStep;
+import com.intellij.openapi.ui.popup.util.BaseListPopupStep;
+import com.intellij.packaging.elements.CompositePackagingElement;
+import com.intellij.packaging.elements.CompositePackagingElementType;
+import com.intellij.packaging.elements.PackagingElement;
+import com.intellij.packaging.elements.PackagingElementFactory;
+import com.intellij.packaging.impl.artifacts.ArtifactUtil;
+import com.intellij.util.PathUtil;
+import com.intellij.util.containers.ContainerUtil;
+import org.jetbrains.annotations.NotNull;
+
+import javax.swing.*;
+import java.util.List;
+
+/**
+ * @author nik
+ */
+public class SurroundElementWithAction extends LayoutTreeActionBase {
+ public SurroundElementWithAction(ArtifactEditorEx artifactEditor) {
+ super("Surround With...", artifactEditor);
+ final CustomShortcutSet shortcutSet = new CustomShortcutSet(KeymapManager.getInstance().getActiveKeymap().getShortcuts("SurroundWith"));
+ registerCustomShortcutSet(shortcutSet, artifactEditor.getLayoutTreeComponent().getLayoutTree());
+ }
+
+ @Override
+ protected boolean isEnabled() {
+ return myArtifactEditor.getLayoutTreeComponent().getSelection().getCommonParentElement() != null;
+ }
+
+ @Override
+ public void actionPerformed(AnActionEvent e) {
+ final LayoutTreeComponent treeComponent = myArtifactEditor.getLayoutTreeComponent();
+ final LayoutTreeSelection selection = treeComponent.getSelection();
+ final CompositePackagingElement<?> parent = selection.getCommonParentElement();
+ if (parent == null) return;
+ final PackagingElementNode<?> parentNode = selection.getNodes().get(0).getParentNode();
+ if (parentNode == null) return;
+
+ if (!treeComponent.checkCanModifyChildren(parent, parentNode, selection.getNodes())) {
+ return;
+ }
+
+ final CompositePackagingElementType<?>[] types = PackagingElementFactory.getInstance().getCompositeElementTypes();
+ final List<PackagingElement<?>> selected = selection.getElements();
+ if (types.length == 1) {
+ surroundWith(types[0], parent, selected, treeComponent);
+ }
+ else {
+ JBPopupFactory.getInstance().createListPopup(new BaseListPopupStep<CompositePackagingElementType>("Surround With...", types) {
+ @Override
+ public Icon getIconFor(CompositePackagingElementType aValue) {
+ return aValue.getCreateElementIcon();
+ }
+
+ @NotNull
+ @Override
+ public String getTextFor(CompositePackagingElementType value) {
+ return value.getPresentableName();
+ }
+
+ @Override
+ public PopupStep onChosen(final CompositePackagingElementType selectedValue, boolean finalChoice) {
+ ApplicationManager.getApplication().invokeLater(new Runnable() {
+ @Override
+ public void run() {
+ surroundWith(selectedValue, parent, selected, treeComponent);
+ }
+ });
+ return FINAL_CHOICE;
+ }
+ }).showInBestPositionFor(e.getDataContext());
+ }
+ }
+
+ private void surroundWith(final CompositePackagingElementType<?> type, final CompositePackagingElement<?> parent, final List<PackagingElement<?>> selected,
+ LayoutTreeComponent treeComponent) {
+ if (myArtifactEditor.isDisposed() || selected.isEmpty()) return;
+
+ final Project project = myArtifactEditor.getContext().getProject();
+ final String elementName = ContainerUtil.getFirstItem(selected, null).createPresentation(myArtifactEditor.getContext()).getPresentableName();
+ final String baseName = PathUtil.suggestFileName(elementName);
+ final CompositePackagingElement<?> newParent = type.createComposite(parent, baseName, myArtifactEditor.getContext());
+ if (newParent != null) {
+ treeComponent.editLayout(new Runnable() {
+ @Override
+ public void run() {
+ for (PackagingElement<?> element : selected) {
+ newParent.addOrFindChild(ArtifactUtil.copyWithChildren(element, project));
+ }
+ for (PackagingElement<?> element : selected) {
+ parent.removeChild(element);
+ }
+ parent.addOrFindChild(newParent);
+ }
+ });
+ treeComponent.rebuildTree();
+ }
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/nodes/ArtifactRootNode.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/nodes/ArtifactRootNode.java
new file mode 100644
index 0000000..9499dc7
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/nodes/ArtifactRootNode.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2000-2009 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.artifacts.nodes;
+
+import com.intellij.openapi.roots.ui.configuration.artifacts.ArtifactEditorEx;
+import com.intellij.openapi.roots.ui.configuration.artifacts.ComplexElementSubstitutionParameters;
+import com.intellij.packaging.artifacts.ArtifactType;
+import com.intellij.packaging.ui.ArtifactEditorContext;
+
+import java.util.Collections;
+
+/**
+ * @author nik
+ */
+public class ArtifactRootNode extends CompositePackagingElementNode {
+ public ArtifactRootNode(ArtifactEditorEx artifactEditor, ArtifactEditorContext context,
+ ComplexElementSubstitutionParameters substitutionParameters, ArtifactType artifactType) {
+ super(artifactEditor.getRootElement(), context, null, null, substitutionParameters, Collections.<PackagingNodeSource>emptyList(),
+ artifactType);
+ }
+
+ @Override
+ public boolean isAutoExpandNode() {
+ return true;
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/nodes/ArtifactsTreeNode.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/nodes/ArtifactsTreeNode.java
new file mode 100644
index 0000000..7cb57f2
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/nodes/ArtifactsTreeNode.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2000-2009 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.artifacts.nodes;
+
+import com.intellij.ide.projectView.PresentationData;
+import com.intellij.ide.util.treeView.NodeDescriptor;
+import com.intellij.packaging.ui.ArtifactEditorContext;
+import com.intellij.packaging.ui.TreeNodePresentation;
+import com.intellij.ui.SimpleTextAttributes;
+import com.intellij.ui.treeStructure.CachingSimpleNode;
+
+/**
+ * @author nik
+ */
+public abstract class ArtifactsTreeNode extends CachingSimpleNode {
+ private final TreeNodePresentation myPresentation;
+ protected final ArtifactEditorContext myContext;
+
+ protected ArtifactsTreeNode(ArtifactEditorContext context, NodeDescriptor parentDescriptor, final TreeNodePresentation presentation) {
+ super(context.getProject(), parentDescriptor);
+ myContext = context;
+ myPresentation = presentation;
+ }
+
+ @Override
+ protected void update(PresentationData presentation) {
+ myPresentation.render(presentation, SimpleTextAttributes.REGULAR_ATTRIBUTES, SimpleTextAttributes.GRAY_ATTRIBUTES);
+ presentation.setTooltip(myPresentation.getTooltipText());
+ }
+
+ public TreeNodePresentation getElementPresentation() {
+ return myPresentation;
+ }
+
+ @Override
+ public int getWeight() {
+ return myPresentation.getWeight();
+ }
+
+ @Override
+ public String getName() {
+ return myPresentation.getPresentableName();
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/nodes/ComplexPackagingElementNode.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/nodes/ComplexPackagingElementNode.java
new file mode 100644
index 0000000..aa8faed
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/nodes/ComplexPackagingElementNode.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2000-2009 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.artifacts.nodes;
+
+import com.intellij.openapi.roots.ui.configuration.artifacts.ComplexElementSubstitutionParameters;
+import com.intellij.openapi.roots.ui.configuration.artifacts.LayoutTree;
+import com.intellij.packaging.elements.ComplexPackagingElement;
+import com.intellij.packaging.elements.CompositePackagingElement;
+import com.intellij.packaging.ui.ArtifactEditorContext;
+import com.intellij.ui.treeStructure.SimpleTree;
+
+import java.awt.event.InputEvent;
+import java.util.Collection;
+
+/**
+ * @author nik
+ */
+public class ComplexPackagingElementNode extends PackagingElementNode<ComplexPackagingElement<?>> {
+ private final ComplexElementSubstitutionParameters mySubstitutionParameters;
+
+ public ComplexPackagingElementNode(ComplexPackagingElement<?> element, ArtifactEditorContext context, CompositePackagingElementNode parentNode,
+ CompositePackagingElement<?> parentElement,
+ ComplexElementSubstitutionParameters substitutionParameters, Collection<PackagingNodeSource> nodeSources) {
+ super(element, context, parentNode, parentElement, nodeSources);
+ mySubstitutionParameters = substitutionParameters;
+ }
+
+ @Override
+ public void handleDoubleClickOrEnter(SimpleTree tree, InputEvent inputEvent) {
+ mySubstitutionParameters.setShowContent(this);
+ final LayoutTree layoutTree = (LayoutTree)tree;
+ final CompositePackagingElementNode parentNode = getParentNode();
+ if (parentNode != null) {
+ layoutTree.addSubtreeToUpdate(parentNode);
+ }
+ }
+
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/nodes/CompositePackagingElementNode.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/nodes/CompositePackagingElementNode.java
new file mode 100644
index 0000000..03a9827
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/nodes/CompositePackagingElementNode.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2000-2009 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.artifacts.nodes;
+
+import com.intellij.openapi.roots.ui.configuration.artifacts.ArtifactEditorImpl;
+import com.intellij.openapi.roots.ui.configuration.artifacts.ComplexElementSubstitutionParameters;
+import com.intellij.packaging.artifacts.ArtifactType;
+import com.intellij.packaging.elements.CompositePackagingElement;
+import com.intellij.packaging.elements.PackagingElement;
+import com.intellij.packaging.ui.ArtifactEditorContext;
+import com.intellij.ui.treeStructure.SimpleNode;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+
+/**
+ * @author nik
+ */
+public class CompositePackagingElementNode extends PackagingElementNode<CompositePackagingElement<?>> {
+ private final ComplexElementSubstitutionParameters mySubstitutionParameters;
+ private final ArtifactType myArtifactType;
+
+ public CompositePackagingElementNode(CompositePackagingElement<?> packagingElement, ArtifactEditorContext context,
+ CompositePackagingElementNode parentNode, CompositePackagingElement<?> parentElement,
+ ComplexElementSubstitutionParameters substitutionParameters,
+ Collection<PackagingNodeSource> nodeSources, ArtifactType artifactType) {
+ super(packagingElement, context, parentNode, parentElement, nodeSources);
+ mySubstitutionParameters = substitutionParameters;
+ myArtifactType = artifactType;
+ }
+
+ @Override
+ protected SimpleNode[] buildChildren() {
+ List<PackagingElementNode<?>> children = new ArrayList<PackagingElementNode<?>>();
+ for (CompositePackagingElement<?> element : getPackagingElements()) {
+ PackagingTreeNodeFactory.addNodes(element.getChildren(), this, element, myContext, mySubstitutionParameters, getNodeSource(element), children,
+ myArtifactType, new HashSet<PackagingElement<?>>());
+ }
+ return children.isEmpty() ? NO_CHILDREN : children.toArray(new SimpleNode[children.size()]);
+ }
+
+ @Override
+ protected void onChildrenBuilt() {
+ ((ArtifactEditorImpl)myContext.getThisArtifactEditor()).getValidationManager().onNodesAdded();
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/nodes/PackagingElementNode.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/nodes/PackagingElementNode.java
new file mode 100644
index 0000000..064ec57
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/nodes/PackagingElementNode.java
@@ -0,0 +1,208 @@
+/*
+ * Copyright 2000-2012 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.artifacts.nodes;
+
+import com.intellij.ide.projectView.PresentationData;
+import com.intellij.openapi.editor.markup.EffectType;
+import com.intellij.openapi.editor.markup.TextAttributes;
+import com.intellij.openapi.roots.ui.configuration.artifacts.ArtifactEditorImpl;
+import com.intellij.openapi.roots.ui.configuration.artifacts.ArtifactProblemDescription;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.daemon.ProjectStructureProblemType;
+import com.intellij.openapi.util.MultiValuesMap;
+import com.intellij.packaging.elements.CompositePackagingElement;
+import com.intellij.packaging.elements.PackagingElement;
+import com.intellij.packaging.ui.ArtifactEditorContext;
+import com.intellij.ui.JBColor;
+import com.intellij.ui.SimpleTextAttributes;
+import com.intellij.ui.treeStructure.SimpleNode;
+import com.intellij.util.ArrayUtil;
+import com.intellij.util.SmartList;
+import com.intellij.util.StringBuilderSpinAllocator;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.*;
+
+/**
+ * @author nik
+ */
+public class PackagingElementNode<E extends PackagingElement<?>> extends ArtifactsTreeNode {
+ private final List<E> myPackagingElements;
+ private final Map<PackagingElement<?>, CompositePackagingElement<?>> myParentElements = new HashMap<PackagingElement<?>, CompositePackagingElement<?>>(1);
+ private final MultiValuesMap<PackagingElement<?>, PackagingNodeSource> myNodeSources = new MultiValuesMap<PackagingElement<?>, PackagingNodeSource>();
+ private final CompositePackagingElementNode myParentNode;
+
+ public PackagingElementNode(@NotNull E packagingElement, ArtifactEditorContext context, @Nullable CompositePackagingElementNode parentNode,
+ @Nullable CompositePackagingElement<?> parentElement,
+ @NotNull Collection<PackagingNodeSource> nodeSources) {
+ super(context, parentNode, packagingElement.createPresentation(context));
+ myParentNode = parentNode;
+ myParentElements.put(packagingElement, parentElement);
+ myNodeSources.putAll(packagingElement, nodeSources);
+ myPackagingElements = new SmartList<E>();
+ doAddElement(packagingElement);
+ }
+
+ private void doAddElement(E packagingElement) {
+ myPackagingElements.add(packagingElement);
+ }
+
+ @Nullable
+ public CompositePackagingElement<?> getParentElement(PackagingElement<?> element) {
+ return myParentElements.get(element);
+ }
+
+ @Nullable
+ public CompositePackagingElementNode getParentNode() {
+ return myParentNode;
+ }
+
+ public List<E> getPackagingElements() {
+ return myPackagingElements;
+ }
+
+ @Nullable
+ public E getElementIfSingle() {
+ return myPackagingElements.size() == 1 ? myPackagingElements.get(0) : null;
+ }
+
+ @NotNull
+ @Override
+ public Object[] getEqualityObjects() {
+ return ArrayUtil.toObjectArray(myPackagingElements);
+ }
+
+ @Override
+ protected SimpleNode[] buildChildren() {
+ return NO_CHILDREN;
+ }
+
+ public E getFirstElement() {
+ return myPackagingElements.get(0);
+ }
+
+ @Override
+ protected void update(PresentationData presentation) {
+ final Collection<ArtifactProblemDescription> problems = ((ArtifactEditorImpl)myContext.getThisArtifactEditor()).getValidationManager().getProblems(this);
+ if (problems == null || problems.isEmpty()) {
+ super.update(presentation);
+ return;
+ }
+ StringBuilder buffer = StringBuilderSpinAllocator.alloc();
+ final String tooltip;
+ boolean isError = false;
+ try {
+ buffer.append("<html>");
+ for (ArtifactProblemDescription problem : problems) {
+ isError |= problem.getSeverity() == ProjectStructureProblemType.Severity.ERROR;
+ buffer.append(problem.getMessage(false)).append("<br>");
+ }
+ buffer.append("</html>");
+ tooltip = buffer.toString();
+ }
+ finally {
+ StringBuilderSpinAllocator.dispose(buffer);
+ }
+
+ getElementPresentation().render(presentation, addErrorHighlighting(isError, SimpleTextAttributes.REGULAR_ATTRIBUTES),
+ addErrorHighlighting(isError, SimpleTextAttributes.GRAY_ATTRIBUTES));
+ presentation.setTooltip(tooltip);
+ }
+
+ private static SimpleTextAttributes addErrorHighlighting(boolean error, SimpleTextAttributes attributes) {
+ final TextAttributes textAttributes = attributes.toTextAttributes();
+ textAttributes.setEffectType(EffectType.WAVE_UNDERSCORE);
+ textAttributes.setEffectColor(error ? JBColor.RED : JBColor.GRAY);
+ return SimpleTextAttributes.fromTextAttributes(textAttributes);
+ }
+
+ void addElement(PackagingElement<?> element, CompositePackagingElement parentElement, Collection<PackagingNodeSource> nodeSource) {
+ doAddElement((E)element);
+ myParentElements.put(element, parentElement);
+ myNodeSources.putAll(element, nodeSource);
+ }
+
+ @NotNull
+ public Collection<PackagingNodeSource> getNodeSources() {
+ return myNodeSources.values();
+ }
+
+ @NotNull
+ public Collection<PackagingNodeSource> getNodeSource(@NotNull PackagingElement<?> element) {
+ final Collection<PackagingNodeSource> nodeSources = myNodeSources.get(element);
+ return nodeSources != null ? nodeSources : Collections.<PackagingNodeSource>emptyList();
+ }
+
+ public ArtifactEditorContext getContext() {
+ return myContext;
+ }
+
+ @Nullable
+ public CompositePackagingElementNode findCompositeChild(@NotNull String name) {
+ final SimpleNode[] children = getChildren();
+ for (SimpleNode child : children) {
+ if (child instanceof CompositePackagingElementNode) {
+ final CompositePackagingElementNode composite = (CompositePackagingElementNode)child;
+ if (name.equals(composite.getFirstElement().getName())) {
+ return composite;
+ }
+ }
+ }
+ return null;
+ }
+
+
+ public List<PackagingElementNode<?>> getNodesByPath(List<PackagingElement<?>> pathToPlace) {
+ List<PackagingElementNode<?>> result = new ArrayList<PackagingElementNode<?>>();
+ PackagingElementNode<?> current = this;
+ int i = 0;
+ result.add(current);
+ while (current != null && i < pathToPlace.size()) {
+ final SimpleNode[] children = current.getCached();
+ if (children == null) {
+ break;
+ }
+
+ PackagingElementNode<?> next = null;
+ final PackagingElement<?> element = pathToPlace.get(i);
+
+ search:
+ for (SimpleNode child : children) {
+ if (child instanceof PackagingElementNode<?>) {
+ PackagingElementNode<?> childNode = (PackagingElementNode<?>)child;
+ for (PackagingElement<?> childElement : childNode.getPackagingElements()) {
+ if (childElement.isEqualTo(element)) {
+ next = childNode;
+ break search;
+ }
+ }
+ for (PackagingNodeSource nodeSource : childNode.getNodeSources()) {
+ if (nodeSource.getSourceElement().isEqualTo(element)) {
+ next = current;
+ break search;
+ }
+ }
+ }
+ }
+ current = next;
+ if (current != null) {
+ result.add(current);
+ }
+ i++;
+ }
+ return result;
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/nodes/PackagingNodeSource.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/nodes/PackagingNodeSource.java
new file mode 100644
index 0000000..bc095ed
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/nodes/PackagingNodeSource.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2000-2009 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.artifacts.nodes;
+
+import com.intellij.packaging.elements.ComplexPackagingElement;
+import com.intellij.packaging.elements.CompositePackagingElement;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.Collection;
+
+/**
+ * @author nik
+ */
+public class PackagingNodeSource {
+ private final ComplexPackagingElement<?> mySourceElement;
+ private final PackagingElementNode<?> mySourceParentNode;
+ private final CompositePackagingElement<?> mySourceParentElement;
+ private final Collection<PackagingNodeSource> myParentSources;
+
+ public PackagingNodeSource(@NotNull ComplexPackagingElement<?> sourceElement,
+ @NotNull PackagingElementNode<?> sourceParentNode,
+ @NotNull CompositePackagingElement<?> sourceParentElement,
+ @Nullable Collection<PackagingNodeSource> parentSources) {
+ mySourceElement = sourceElement;
+ mySourceParentNode = sourceParentNode;
+ mySourceParentElement = sourceParentElement;
+ myParentSources = parentSources;
+ }
+
+ @NotNull
+ public ComplexPackagingElement<?> getSourceElement() {
+ return mySourceElement;
+ }
+
+ @NotNull
+ public PackagingElementNode<?> getSourceParentNode() {
+ return mySourceParentNode;
+ }
+
+ @NotNull
+ public CompositePackagingElement<?> getSourceParentElement() {
+ return mySourceParentElement;
+ }
+
+ public Collection<PackagingNodeSource> getParentSources() {
+ return myParentSources;
+ }
+
+ public String getPresentableName() {
+ return mySourceElement.createPresentation(mySourceParentNode.getContext()).getPresentableName();
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/nodes/PackagingTreeNodeFactory.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/nodes/PackagingTreeNodeFactory.java
new file mode 100644
index 0000000..a600f98
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/nodes/PackagingTreeNodeFactory.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2000-2009 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.artifacts.nodes;
+
+import com.intellij.openapi.roots.ui.configuration.artifacts.ArtifactEditorImpl;
+import com.intellij.openapi.roots.ui.configuration.artifacts.ComplexElementSubstitutionParameters;
+import com.intellij.packaging.artifacts.ArtifactType;
+import com.intellij.packaging.elements.ArtifactRootElement;
+import com.intellij.packaging.elements.ComplexPackagingElement;
+import com.intellij.packaging.elements.CompositePackagingElement;
+import com.intellij.packaging.elements.PackagingElement;
+import com.intellij.packaging.ui.ArtifactEditorContext;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * @author nik
+ */
+public class PackagingTreeNodeFactory {
+ private PackagingTreeNodeFactory() {
+ }
+
+ public static void addNodes(@NotNull List<? extends PackagingElement<?>> elements, @NotNull CompositePackagingElementNode parentNode,
+ @NotNull CompositePackagingElement parentElement, @NotNull ArtifactEditorContext context,
+ @NotNull ComplexElementSubstitutionParameters substitutionParameters, @NotNull Collection<PackagingNodeSource> nodeSources,
+ @NotNull List<PackagingElementNode<?>> nodes,
+ ArtifactType artifactType,
+ Set<PackagingElement<?>> processed) {
+ for (PackagingElement<?> element : elements) {
+ final PackagingElementNode<?> prev = findEqual(nodes, element);
+ if (prev != null) {
+ prev.addElement(element, parentElement, nodeSources);
+ continue;
+ }
+
+ if (element instanceof ArtifactRootElement) {
+ throw new AssertionError("artifact root not expected here");
+ }
+ else if (element instanceof CompositePackagingElement) {
+ nodes.add(new CompositePackagingElementNode((CompositePackagingElement<?>)element, context, parentNode, parentElement, substitutionParameters, nodeSources,
+ artifactType));
+ }
+ else if (element instanceof ComplexPackagingElement) {
+ final ComplexPackagingElement<?> complexElement = (ComplexPackagingElement<?>)element;
+ if (processed.add(element) && substitutionParameters.shouldSubstitute(complexElement)) {
+ final List<? extends PackagingElement<?>> substitution = complexElement.getSubstitution(context, artifactType);
+ if (substitution != null) {
+ final PackagingNodeSource source = new PackagingNodeSource(complexElement, parentNode, parentElement, nodeSources);
+ addNodes(substitution, parentNode, parentElement, context, substitutionParameters, Collections.singletonList(source), nodes,
+ artifactType, processed);
+ continue;
+ }
+ }
+ nodes.add(new ComplexPackagingElementNode(complexElement, context, parentNode, parentElement, substitutionParameters, nodeSources));
+ }
+ else {
+ nodes.add(new PackagingElementNode<PackagingElement<?>>(element, context, parentNode, parentElement, nodeSources));
+ }
+ }
+ }
+
+ @Nullable
+ private static PackagingElementNode<?> findEqual(List<PackagingElementNode<?>> children, PackagingElement<?> element) {
+ for (PackagingElementNode<?> node : children) {
+ if (node.getFirstElement().isEqualTo(element)) {
+ return node;
+ }
+ }
+ return null;
+ }
+
+ @NotNull
+ public static ArtifactRootNode createRootNode(ArtifactEditorImpl artifactsEditor, ArtifactEditorContext context,
+ ComplexElementSubstitutionParameters substitutionParameters, final ArtifactType artifactType) {
+ return new ArtifactRootNode(artifactsEditor, context, substitutionParameters, artifactType);
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/sourceItems/ArtifactSourceItem.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/sourceItems/ArtifactSourceItem.java
new file mode 100644
index 0000000..f6c8f6b
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/sourceItems/ArtifactSourceItem.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2000-2009 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.artifacts.sourceItems;
+
+import com.intellij.openapi.project.Project;
+import com.intellij.packaging.artifacts.Artifact;
+import com.intellij.packaging.artifacts.ArtifactPointer;
+import com.intellij.packaging.artifacts.ArtifactPointerManager;
+import com.intellij.packaging.elements.PackagingElement;
+import com.intellij.packaging.elements.PackagingElementFactory;
+import com.intellij.packaging.elements.PackagingElementOutputKind;
+import com.intellij.packaging.impl.artifacts.JarArtifactType;
+import com.intellij.packaging.impl.ui.ArtifactElementPresentation;
+import com.intellij.packaging.ui.ArtifactEditorContext;
+import com.intellij.packaging.ui.PackagingSourceItem;
+import com.intellij.packaging.ui.SourceItemPresentation;
+import com.intellij.packaging.ui.SourceItemWeights;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * @author nik
+ */
+public class ArtifactSourceItem extends PackagingSourceItem {
+ private final Artifact myArtifact;
+
+ public ArtifactSourceItem(@NotNull Artifact artifact) {
+ myArtifact = artifact;
+ }
+
+ @Override
+ public SourceItemPresentation createPresentation(@NotNull ArtifactEditorContext context) {
+ final ArtifactPointer pointer = ArtifactPointerManager.getInstance(context.getProject()).createPointer(myArtifact, context.getArtifactModel());
+ return new DelegatedSourceItemPresentation(new ArtifactElementPresentation(pointer, context)) {
+ @Override
+ public int getWeight() {
+ return SourceItemWeights.ARTIFACT_WEIGHT;
+ }
+ };
+ }
+
+ @Override
+ @NotNull
+ public List<? extends PackagingElement<?>> createElements(@NotNull ArtifactEditorContext context) {
+ final Project project = context.getProject();
+ final ArtifactPointer pointer = ArtifactPointerManager.getInstance(project).createPointer(myArtifact, context.getArtifactModel());
+ return Collections.singletonList(PackagingElementFactory.getInstance().createArtifactElement(pointer, project));
+ }
+
+ public boolean equals(Object obj) {
+ return obj instanceof ArtifactSourceItem && myArtifact.equals(((ArtifactSourceItem)obj).myArtifact);
+ }
+
+ @NotNull
+ @Override
+ public PackagingElementOutputKind getKindOfProducedElements() {
+ return myArtifact.getArtifactType() instanceof JarArtifactType ? PackagingElementOutputKind.JAR_FILES : PackagingElementOutputKind.OTHER;
+ }
+
+ public Artifact getArtifact() {
+ return myArtifact;
+ }
+
+ public int hashCode() {
+ return myArtifact.hashCode();
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/sourceItems/ArtifactsSourceItemsProvider.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/sourceItems/ArtifactsSourceItemsProvider.java
new file mode 100644
index 0000000..cfb66ac
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/sourceItems/ArtifactsSourceItemsProvider.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2000-2009 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.artifacts.sourceItems;
+
+import com.intellij.icons.AllIcons;
+import com.intellij.ide.projectView.PresentationData;
+import com.intellij.packaging.artifacts.Artifact;
+import com.intellij.packaging.elements.PackagingElement;
+import com.intellij.packaging.impl.elements.ArtifactElementType;
+import com.intellij.packaging.ui.*;
+import com.intellij.ui.SimpleTextAttributes;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * @author nik
+ */
+public class ArtifactsSourceItemsProvider extends PackagingSourceItemsProvider {
+ @Override
+ @NotNull
+ public Collection<? extends PackagingSourceItem> getSourceItems(@NotNull ArtifactEditorContext editorContext,
+ @NotNull Artifact artifact,
+ @Nullable PackagingSourceItem parent) {
+ if (parent == null) {
+ if (!ArtifactElementType.getAvailableArtifacts(editorContext, artifact, true).isEmpty()) {
+ return Collections.singletonList(new ArtifactsGroupSourceItem());
+ }
+ }
+ else if (parent instanceof ArtifactsGroupSourceItem) {
+ List<PackagingSourceItem> items = new ArrayList<PackagingSourceItem>();
+ for (Artifact another : ArtifactElementType.getAvailableArtifacts(editorContext, artifact, true)) {
+ items.add(new ArtifactSourceItem(another));
+ }
+ return items;
+ }
+ return Collections.emptyList();
+ }
+
+ private static class ArtifactsGroupSourceItem extends PackagingSourceItem {
+ private ArtifactsGroupSourceItem() {
+ super(false);
+ }
+
+ public boolean equals(Object obj) {
+ return obj instanceof ArtifactsGroupSourceItem;
+ }
+
+ public int hashCode() {
+ return 0;
+ }
+
+ @Override
+ public SourceItemPresentation createPresentation(@NotNull ArtifactEditorContext context) {
+ return new ArtifactsGroupPresentation();
+ }
+
+ @Override
+ @NotNull
+ public List<? extends PackagingElement<?>> createElements(@NotNull ArtifactEditorContext context) {
+ return Collections.emptyList();
+ }
+
+ private static class ArtifactsGroupPresentation extends SourceItemPresentation {
+ @Override
+ public String getPresentableName() {
+ return "Artifacts";
+ }
+
+ @Override
+ public void render(@NotNull PresentationData presentationData, SimpleTextAttributes mainAttributes,
+ SimpleTextAttributes commentAttributes) {
+ presentationData.setIcon(AllIcons.Nodes.Artifact);
+ presentationData.addText("Artifacts", mainAttributes);
+ }
+
+ @Override
+ public int getWeight() {
+ return SourceItemWeights.ARTIFACTS_GROUP_WEIGHT;
+ }
+ }
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/sourceItems/DelegatedSourceItemPresentation.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/sourceItems/DelegatedSourceItemPresentation.java
new file mode 100644
index 0000000..2bc452d
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/sourceItems/DelegatedSourceItemPresentation.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2000-2009 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.artifacts.sourceItems;
+
+import com.intellij.packaging.ui.SourceItemPresentation;
+import com.intellij.packaging.ui.TreeNodePresentation;
+import com.intellij.ide.projectView.PresentationData;
+import com.intellij.ui.SimpleTextAttributes;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * @author nik
+ */
+public class DelegatedSourceItemPresentation extends SourceItemPresentation {
+ private final TreeNodePresentation myPresentation;
+
+ public DelegatedSourceItemPresentation(TreeNodePresentation presentation) {
+ myPresentation = presentation;
+ }
+
+ @Override
+ public String getPresentableName() {
+ return myPresentation.getPresentableName();
+ }
+
+ @Override
+ public String getSearchName() {
+ return myPresentation.getSearchName();
+ }
+
+ @Override
+ public void render(@NotNull PresentationData presentationData, SimpleTextAttributes mainAttributes, SimpleTextAttributes commentAttributes) {
+ myPresentation.render(presentationData, mainAttributes, commentAttributes);
+ }
+
+ @Override
+ @Nullable
+ public String getTooltipText() {
+ return myPresentation.getTooltipText();
+ }
+
+ @Override
+ public boolean canNavigateToSource() {
+ return myPresentation.canNavigateToSource();
+ }
+
+ @Override
+ public void navigateToSource() {
+ myPresentation.navigateToSource();
+ }
+
+ @Override
+ public int getWeight() {
+ return myPresentation.getWeight();
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/sourceItems/FacetBasedPackagingSourceItemsProvider.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/sourceItems/FacetBasedPackagingSourceItemsProvider.java
new file mode 100644
index 0000000..3d15845
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/sourceItems/FacetBasedPackagingSourceItemsProvider.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright 2000-2009 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.artifacts.sourceItems;
+
+import com.intellij.facet.Facet;
+import com.intellij.facet.FacetTypeId;
+import com.intellij.openapi.module.Module;
+import com.intellij.packaging.artifacts.Artifact;
+import com.intellij.packaging.elements.PackagingElement;
+import com.intellij.packaging.elements.PackagingElementOutputKind;
+import com.intellij.packaging.elements.PackagingElementType;
+import com.intellij.packaging.impl.artifacts.ArtifactUtil;
+import com.intellij.packaging.ui.*;
+import com.intellij.util.Processor;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.*;
+
+/**
+ * @author nik
+ */
+public abstract class FacetBasedPackagingSourceItemsProvider<F extends Facet, E extends PackagingElement<?>> extends PackagingSourceItemsProvider {
+ private final FacetTypeId<F> myFacetTypeId;
+ private final PackagingElementType<E> myElementType;
+
+ protected FacetBasedPackagingSourceItemsProvider(FacetTypeId<F> facetTypeId, PackagingElementType<E> elementType) {
+ myFacetTypeId = facetTypeId;
+ myElementType = elementType;
+ }
+
+ @NotNull
+ @Override
+ public Collection<? extends PackagingSourceItem> getSourceItems(@NotNull ArtifactEditorContext editorContext, @NotNull Artifact artifact,
+ @Nullable PackagingSourceItem parent) {
+ if (parent instanceof ModuleSourceItemGroup) {
+ final Module module = ((ModuleSourceItemGroup)parent).getModule();
+ final Set<F> facets = new HashSet<F>(editorContext.getFacetsProvider().getFacetsByType(module, myFacetTypeId));
+ ArtifactUtil.processPackagingElements(artifact, myElementType, new Processor<E>() {
+ @Override
+ public boolean process(E e) {
+ F facet = getFacet(e);
+ if (facet != null) {
+ facets.remove(facet);
+ }
+ return true;
+ }
+ }, editorContext, true);
+
+ if (!facets.isEmpty()) {
+ return Collections.singletonList(new FacetBasedSourceItem<F>(this, facets.iterator().next()));
+ }
+ }
+ return Collections.emptyList();
+ }
+
+ protected PackagingElementOutputKind getKindOfProducedElements() {
+ return PackagingElementOutputKind.OTHER;
+ }
+
+ @Nullable
+ protected abstract F getFacet(E element);
+
+ protected abstract TreeNodePresentation createPresentation(F facet);
+
+ protected abstract PackagingElement<?> createElement(ArtifactEditorContext context, F facet);
+
+ protected static class FacetBasedSourceItem<F extends Facet> extends PackagingSourceItem {
+ private final FacetBasedPackagingSourceItemsProvider<F, ?> myProvider;
+ private final F myFacet;
+
+ public FacetBasedSourceItem(FacetBasedPackagingSourceItemsProvider<F, ?> provider, F facet) {
+ myProvider = provider;
+ myFacet = facet;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return obj instanceof FacetBasedSourceItem && myFacet.equals(((FacetBasedSourceItem)obj).myFacet)
+ && myProvider.equals(((FacetBasedSourceItem)obj).myProvider);
+ }
+
+ @Override
+ public int hashCode() {
+ return myFacet.hashCode() + 31*myProvider.hashCode();
+ }
+
+ @Override
+ public SourceItemPresentation createPresentation(@NotNull ArtifactEditorContext context) {
+ return new DelegatedSourceItemPresentation(myProvider.createPresentation(myFacet));
+ }
+
+ @NotNull
+ @Override
+ public List<? extends PackagingElement<?>> createElements(@NotNull ArtifactEditorContext context) {
+ return Collections.singletonList(myProvider.createElement(context, myFacet));
+ }
+
+ @NotNull
+ @Override
+ public PackagingElementOutputKind getKindOfProducedElements() {
+ return myProvider.getKindOfProducedElements();
+ }
+ }
+
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/sourceItems/LibrarySourceItem.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/sourceItems/LibrarySourceItem.java
new file mode 100644
index 0000000..c726c9f
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/sourceItems/LibrarySourceItem.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright 2000-2009 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.artifacts.sourceItems;
+
+import com.intellij.ide.presentation.VirtualFilePresentation;
+import com.intellij.ide.projectView.PresentationData;
+import com.intellij.openapi.roots.OrderRootType;
+import com.intellij.openapi.roots.impl.libraries.LibraryEx;
+import com.intellij.openapi.roots.libraries.Library;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.packaging.elements.PackagingElement;
+import com.intellij.packaging.elements.PackagingElementFactory;
+import com.intellij.packaging.elements.PackagingElementOutputKind;
+import com.intellij.packaging.impl.elements.LibraryPackagingElement;
+import com.intellij.packaging.impl.ui.LibraryElementPresentation;
+import com.intellij.packaging.ui.ArtifactEditorContext;
+import com.intellij.packaging.ui.PackagingSourceItem;
+import com.intellij.packaging.ui.SourceItemPresentation;
+import com.intellij.packaging.ui.SourceItemWeights;
+import com.intellij.ui.SimpleTextAttributes;
+import com.intellij.util.PlatformIcons;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.List;
+
+/**
+ * @author nik
+ */
+public class LibrarySourceItem extends PackagingSourceItem {
+ private final Library myLibrary;
+
+ public LibrarySourceItem(@NotNull Library library) {
+ myLibrary = library;
+ }
+
+ @Override
+ public SourceItemPresentation createPresentation(@NotNull ArtifactEditorContext context) {
+ return new LibrarySourceItemPresentation(myLibrary, context);
+ }
+
+ public boolean equals(Object obj) {
+ return obj instanceof LibrarySourceItem && myLibrary.equals(((LibrarySourceItem)obj).myLibrary);
+ }
+
+ public int hashCode() {
+ return myLibrary.hashCode();
+ }
+
+ @NotNull
+ public Library getLibrary() {
+ return myLibrary;
+ }
+
+ @NotNull
+ @Override
+ public PackagingElementOutputKind getKindOfProducedElements() {
+ return LibraryPackagingElement.getKindForLibrary(myLibrary);
+ }
+
+ @Override
+ @NotNull
+ public List<? extends PackagingElement<?>> createElements(@NotNull ArtifactEditorContext context) {
+ return PackagingElementFactory.getInstance().createLibraryElements(myLibrary);
+ }
+
+ private static class LibrarySourceItemPresentation extends SourceItemPresentation {
+ private final Library myLibrary;
+ private final ArtifactEditorContext myContext;
+
+ public LibrarySourceItemPresentation(Library library, ArtifactEditorContext context) {
+ myLibrary = library;
+ myContext = context;
+ }
+
+ @Override
+ public boolean canNavigateToSource() {
+ return myLibrary != null;
+ }
+
+ @Override
+ public void navigateToSource() {
+ myContext.selectLibrary(myLibrary);
+ }
+
+ @Override
+ public String getPresentableName() {
+ final String name = myLibrary.getName();
+ if (name != null) {
+ return name;
+ }
+ final VirtualFile[] files = myLibrary.getFiles(OrderRootType.CLASSES);
+ return files.length > 0 ? files[0].getName() : "Empty Library";
+ }
+
+ @Override
+ public void render(@NotNull PresentationData presentationData, SimpleTextAttributes mainAttributes,
+ SimpleTextAttributes commentAttributes) {
+ final String name = myLibrary.getName();
+ if (name != null) {
+ presentationData.setIcon(PlatformIcons.LIBRARY_ICON);
+ presentationData.addText(name, mainAttributes);
+ presentationData.addText(LibraryElementPresentation.getLibraryTableComment(myLibrary), commentAttributes);
+ }
+ else {
+ if (((LibraryEx)myLibrary).isDisposed()) {
+ //todo[nik] disposed library should not be shown in the tree
+ presentationData.addText("Invalid Library", SimpleTextAttributes.ERROR_ATTRIBUTES);
+ return;
+ }
+ final VirtualFile[] files = myLibrary.getFiles(OrderRootType.CLASSES);
+ if (files.length > 0) {
+ final VirtualFile file = files[0];
+ presentationData.setIcon(VirtualFilePresentation.getIcon(file));
+ presentationData.addText(file.getName(), mainAttributes);
+ }
+ else {
+ presentationData.addText("Empty Library", SimpleTextAttributes.ERROR_ATTRIBUTES);
+ }
+ }
+ }
+
+ @Override
+ public int getWeight() {
+ return SourceItemWeights.LIBRARY_WEIGHT;
+ }
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/sourceItems/ModuleGroupItem.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/sourceItems/ModuleGroupItem.java
new file mode 100644
index 0000000..b31d7a8
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/sourceItems/ModuleGroupItem.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2000-2009 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.artifacts.sourceItems;
+
+import com.intellij.ide.projectView.PresentationData;
+import com.intellij.openapi.util.Comparing;
+import com.intellij.packaging.elements.PackagingElement;
+import com.intellij.packaging.ui.ArtifactEditorContext;
+import com.intellij.packaging.ui.PackagingSourceItem;
+import com.intellij.packaging.ui.SourceItemPresentation;
+import com.intellij.packaging.ui.SourceItemWeights;
+import com.intellij.ui.SimpleTextAttributes;
+import com.intellij.util.PlatformIcons;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * @author nik
+ */
+public class ModuleGroupItem extends PackagingSourceItem {
+ private final String myGroupName;
+ private final String[] myPath;
+
+ public ModuleGroupItem(String[] path) {
+ super(false);
+ myGroupName = path[path.length - 1];
+ myPath = path;
+ }
+
+ public boolean equals(Object obj) {
+ return obj instanceof ModuleGroupItem && Comparing.equal(myPath, ((ModuleGroupItem)obj).myPath);
+ }
+
+ public int hashCode() {
+ return Arrays.hashCode(myPath);
+ }
+
+ @Override
+ public SourceItemPresentation createPresentation(@NotNull ArtifactEditorContext context) {
+ return new ModuleGroupSourceItemPresentation(myGroupName);
+ }
+
+ @NotNull
+ @Override
+ public List<? extends PackagingElement<?>> createElements(@NotNull ArtifactEditorContext context) {
+ return Collections.emptyList();
+ }
+
+ public String[] getPath() {
+ return myPath;
+ }
+
+ private static class ModuleGroupSourceItemPresentation extends SourceItemPresentation {
+ private final String myGroupName;
+
+ public ModuleGroupSourceItemPresentation(String groupName) {
+ myGroupName = groupName;
+ }
+
+ @Override
+ public String getPresentableName() {
+ return myGroupName;
+ }
+
+ @Override
+ public void render(@NotNull PresentationData presentationData, SimpleTextAttributes mainAttributes,
+ SimpleTextAttributes commentAttributes) {
+ presentationData.setIcon(PlatformIcons.CLOSED_MODULE_GROUP_ICON);
+ presentationData.addText(myGroupName, mainAttributes);
+ }
+
+ @Override
+ public int getWeight() {
+ return SourceItemWeights.MODULE_GROUP_WEIGHT;
+ }
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/sourceItems/ModuleOutputSourceItem.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/sourceItems/ModuleOutputSourceItem.java
new file mode 100644
index 0000000..0a210bf
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/sourceItems/ModuleOutputSourceItem.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2000-2009 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.artifacts.sourceItems;
+
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.module.ModulePointer;
+import com.intellij.openapi.module.ModulePointerManager;
+import com.intellij.packaging.elements.PackagingElement;
+import com.intellij.packaging.elements.PackagingElementOutputKind;
+import com.intellij.packaging.impl.elements.ProductionModuleOutputPackagingElement;
+import com.intellij.packaging.impl.ui.ModuleElementPresentation;
+import com.intellij.packaging.ui.*;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * @author nik
+ */
+public class ModuleOutputSourceItem extends PackagingSourceItem {
+ private final Module myModule;
+
+ public ModuleOutputSourceItem(@NotNull Module module) {
+ myModule = module;
+ }
+
+ public Module getModule() {
+ return myModule;
+ }
+
+ public boolean equals(Object obj) {
+ return obj instanceof ModuleOutputSourceItem && myModule.equals(((ModuleOutputSourceItem)obj).myModule);
+ }
+
+ public int hashCode() {
+ return myModule.hashCode();
+ }
+
+ @Override
+ public SourceItemPresentation createPresentation(@NotNull ArtifactEditorContext context) {
+ final ModulePointer modulePointer = ModulePointerManager.getInstance(context.getProject()).create(myModule);
+ return new DelegatedSourceItemPresentation(new ModuleElementPresentation(modulePointer, context, false)) {
+ @Override
+ public int getWeight() {
+ return SourceItemWeights.MODULE_OUTPUT_WEIGHT;
+ }
+ };
+ }
+
+ @Override
+ @NotNull
+ public List<? extends PackagingElement<?>> createElements(@NotNull ArtifactEditorContext context) {
+ final ModulePointer modulePointer = ModulePointerManager.getInstance(context.getProject()).create(myModule);
+ return Collections.singletonList(new ProductionModuleOutputPackagingElement(context.getProject(), modulePointer));
+ }
+
+ @NotNull
+ @Override
+ public PackagingElementOutputKind getKindOfProducedElements() {
+ return PackagingElementOutputKind.DIRECTORIES_WITH_CLASSES;
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/sourceItems/ModuleSourceItemGroup.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/sourceItems/ModuleSourceItemGroup.java
new file mode 100644
index 0000000..c8acdd8
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/sourceItems/ModuleSourceItemGroup.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright 2000-2009 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.artifacts.sourceItems;
+
+import com.intellij.ide.projectView.PresentationData;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.module.ModuleType;
+import com.intellij.openapi.roots.DependencyScope;
+import com.intellij.openapi.roots.ModuleOrderEntry;
+import com.intellij.openapi.roots.OrderEntry;
+import com.intellij.packaging.artifacts.Artifact;
+import com.intellij.packaging.artifacts.ArtifactType;
+import com.intellij.packaging.elements.PackagingElement;
+import com.intellij.packaging.elements.PackagingElementFactory;
+import com.intellij.packaging.ui.*;
+import com.intellij.ui.SimpleTextAttributes;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.ArrayList;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * @author nik
+ */
+public class ModuleSourceItemGroup extends PackagingSourceItem {
+ private final Module myModule;
+
+ public ModuleSourceItemGroup(@NotNull Module module) {
+ super(true);
+ myModule = module;
+ }
+
+ @Override
+ public SourceItemPresentation createPresentation(@NotNull ArtifactEditorContext context) {
+ return new ModuleSourceItemPresentation(myModule, context);
+ }
+
+ public boolean equals(Object obj) {
+ return obj instanceof ModuleSourceItemGroup && myModule.equals(((ModuleSourceItemGroup)obj).myModule);
+ }
+
+ public int hashCode() {
+ return myModule.hashCode();
+ }
+
+ @Override
+ @NotNull
+ public List<? extends PackagingElement<?>> createElements(@NotNull ArtifactEditorContext context) {
+ final Set<Module> modules = new LinkedHashSet<Module>();
+ collectDependentModules(myModule, modules, context);
+
+ final Artifact artifact = context.getArtifact();
+ final ArtifactType artifactType = artifact.getArtifactType();
+ Set<PackagingSourceItem> items = new LinkedHashSet<PackagingSourceItem>();
+ for (Module module : modules) {
+ for (PackagingSourceItemsProvider provider : PackagingSourceItemsProvider.EP_NAME.getExtensions()) {
+ final ModuleSourceItemGroup parent = new ModuleSourceItemGroup(module);
+ for (PackagingSourceItem sourceItem : provider.getSourceItems(context, artifact, parent)) {
+ if (artifactType.isSuitableItem(sourceItem) && sourceItem.isProvideElements()) {
+ items.add(sourceItem);
+ }
+ }
+ }
+ }
+
+ List<PackagingElement<?>> result = new ArrayList<PackagingElement<?>>();
+ final PackagingElementFactory factory = PackagingElementFactory.getInstance();
+ for (PackagingSourceItem item : items) {
+ final String path = artifactType.getDefaultPathFor(item.getKindOfProducedElements());
+ if (path != null) {
+ result.addAll(factory.createParentDirectories(path, item.createElements(context)));
+ }
+ }
+ return result;
+ }
+
+ private static void collectDependentModules(final Module module, Set<Module> modules, ArtifactEditorContext context) {
+ if (!modules.add(module)) return;
+
+ for (OrderEntry entry : context.getModulesProvider().getRootModel(module).getOrderEntries()) {
+ if (entry instanceof ModuleOrderEntry) {
+ final ModuleOrderEntry moduleEntry = (ModuleOrderEntry)entry;
+ final Module dependency = moduleEntry.getModule();
+ final DependencyScope scope = moduleEntry.getScope();
+ if (dependency != null && scope.isForProductionRuntime()) {
+ collectDependentModules(dependency, modules, context);
+ }
+ }
+ }
+ }
+
+ public Module getModule() {
+ return myModule;
+ }
+
+ private static class ModuleSourceItemPresentation extends SourceItemPresentation {
+ private final Module myModule;
+ private final ArtifactEditorContext myContext;
+
+ public ModuleSourceItemPresentation(@NotNull Module module, ArtifactEditorContext context) {
+ myModule = module;
+ myContext = context;
+ }
+
+ @Override
+ public String getPresentableName() {
+ return myModule.getName();
+ }
+
+ @Override
+ public void render(@NotNull PresentationData presentationData, SimpleTextAttributes mainAttributes,
+ SimpleTextAttributes commentAttributes) {
+ presentationData.setIcon(ModuleType.get(myModule).getIcon());
+ presentationData.addText(myModule.getName(), mainAttributes);
+ }
+
+ @Override
+ public boolean canNavigateToSource() {
+ return true;
+ }
+
+ @Override
+ public void navigateToSource() {
+ myContext.selectModule(myModule);
+ }
+
+ @Override
+ public int getWeight() {
+ return SourceItemWeights.MODULE_WEIGHT;
+ }
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/sourceItems/ModulesAndLibrariesSourceItemsProvider.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/sourceItems/ModulesAndLibrariesSourceItemsProvider.java
new file mode 100644
index 0000000..2f3b0f3
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/sourceItems/ModulesAndLibrariesSourceItemsProvider.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright 2000-2009 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.artifacts.sourceItems;
+
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.module.ModuleManager;
+import com.intellij.openapi.roots.*;
+import com.intellij.openapi.roots.libraries.Library;
+import com.intellij.openapi.util.Comparing;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.packaging.artifacts.Artifact;
+import com.intellij.packaging.impl.artifacts.ArtifactUtil;
+import com.intellij.packaging.impl.elements.FileCopyPackagingElement;
+import com.intellij.packaging.impl.elements.ProductionModuleOutputElementType;
+import com.intellij.packaging.impl.elements.ModuleOutputPackagingElement;
+import com.intellij.packaging.impl.elements.PackagingElementFactoryImpl;
+import com.intellij.packaging.ui.ArtifactEditorContext;
+import com.intellij.packaging.ui.PackagingSourceItem;
+import com.intellij.packaging.ui.PackagingSourceItemsProvider;
+import com.intellij.util.ArrayUtil;
+import com.intellij.util.Processor;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.*;
+
+/**
+ * @author nik
+ */
+public class ModulesAndLibrariesSourceItemsProvider extends PackagingSourceItemsProvider {
+
+ @Override
+ @NotNull
+ public Collection<? extends PackagingSourceItem> getSourceItems(@NotNull ArtifactEditorContext editorContext, @NotNull Artifact artifact,
+ PackagingSourceItem parent) {
+ if (parent == null) {
+ return createModuleItems(editorContext, artifact, ArrayUtil.EMPTY_STRING_ARRAY);
+ }
+ else if (parent instanceof ModuleGroupItem) {
+ return createModuleItems(editorContext, artifact, ((ModuleGroupItem)parent).getPath());
+ }
+ else if (parent instanceof ModuleSourceItemGroup) {
+ return createClasspathItems(editorContext, artifact, ((ModuleSourceItemGroup)parent).getModule());
+ }
+ return Collections.emptyList();
+ }
+
+ private static Collection<? extends PackagingSourceItem> createClasspathItems(ArtifactEditorContext editorContext,
+ Artifact artifact, @NotNull Module module) {
+ final List<PackagingSourceItem> items = new ArrayList<PackagingSourceItem>();
+ final ModuleRootModel rootModel = editorContext.getModulesProvider().getRootModel(module);
+ List<Library> libraries = new ArrayList<Library>();
+ for (OrderEntry orderEntry : rootModel.getOrderEntries()) {
+ if (orderEntry instanceof LibraryOrderEntry) {
+ final LibraryOrderEntry libraryEntry = (LibraryOrderEntry)orderEntry;
+ final Library library = libraryEntry.getLibrary();
+ final DependencyScope scope = libraryEntry.getScope();
+ if (library != null && scope.isForProductionRuntime()) {
+ libraries.add(library);
+ }
+ }
+ }
+
+ for (Module toAdd : getNotAddedModules(editorContext, artifact, module)) {
+ items.add(new ModuleOutputSourceItem(toAdd));
+ }
+
+ for (Library library : getNotAddedLibraries(editorContext, artifact, libraries)) {
+ items.add(new LibrarySourceItem(library));
+ }
+ return items;
+ }
+
+ private static Collection<? extends PackagingSourceItem> createModuleItems(ArtifactEditorContext editorContext, Artifact artifact, @NotNull String[] groupPath) {
+ final Module[] modules = editorContext.getModulesProvider().getModules();
+ final List<PackagingSourceItem> items = new ArrayList<PackagingSourceItem>();
+ Set<String> groups = new HashSet<String>();
+ for (Module module : modules) {
+ String[] path = ModuleManager.getInstance(editorContext.getProject()).getModuleGroupPath(module);
+ if (path == null) {
+ path = ArrayUtil.EMPTY_STRING_ARRAY;
+ }
+
+ if (Comparing.equal(path, groupPath)) {
+ items.add(new ModuleSourceItemGroup(module));
+ }
+ else if (ArrayUtil.startsWith(path, groupPath)) {
+ groups.add(path[groupPath.length]);
+ }
+ }
+ for (String group : groups) {
+ items.add(0, new ModuleGroupItem(ArrayUtil.append(groupPath, group)));
+ }
+ return items;
+ }
+
+ @NotNull
+ private static List<? extends Module> getNotAddedModules(@NotNull final ArtifactEditorContext context, @NotNull Artifact artifact,
+ final Module... allModules) {
+ final Set<Module> modules = new HashSet<Module>(Arrays.asList(allModules));
+ ArtifactUtil.processPackagingElements(artifact, ProductionModuleOutputElementType.ELEMENT_TYPE, new Processor<ModuleOutputPackagingElement>() {
+ @Override
+ public boolean process(ModuleOutputPackagingElement moduleOutputPackagingElement) {
+ modules.remove(moduleOutputPackagingElement.findModule(context));
+ return true;
+ }
+ }, context, true);
+ return new ArrayList<Module>(modules);
+ }
+
+ private static List<? extends Library> getNotAddedLibraries(@NotNull final ArtifactEditorContext context, @NotNull Artifact artifact,
+ List<Library> librariesList) {
+ final Set<VirtualFile> roots = new HashSet<VirtualFile>();
+ ArtifactUtil.processPackagingElements(artifact, PackagingElementFactoryImpl.FILE_COPY_ELEMENT_TYPE, new Processor<FileCopyPackagingElement>() {
+ @Override
+ public boolean process(FileCopyPackagingElement fileCopyPackagingElement) {
+ final VirtualFile root = fileCopyPackagingElement.getLibraryRoot();
+ if (root != null) {
+ roots.add(root);
+ }
+ return true;
+ }
+ }, context, true);
+ final List<Library> result = new ArrayList<Library>();
+ for (Library library : librariesList) {
+ if (!roots.containsAll(Arrays.asList(library.getFiles(OrderRootType.CLASSES)))) {
+ result.add(library);
+ }
+ }
+ return result;
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/sourceItems/SourceItemNode.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/sourceItems/SourceItemNode.java
new file mode 100644
index 0000000..36e221c
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/sourceItems/SourceItemNode.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2000-2009 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.artifacts.sourceItems;
+
+import com.intellij.ide.util.treeView.NodeDescriptor;
+import com.intellij.openapi.roots.ui.configuration.artifacts.ArtifactEditorEx;
+import com.intellij.packaging.ui.ArtifactEditorContext;
+import com.intellij.packaging.ui.PackagingSourceItem;
+import com.intellij.ui.treeStructure.SimpleTree;
+import org.jetbrains.annotations.NotNull;
+
+import java.awt.event.InputEvent;
+import java.util.Collections;
+
+/**
+ * @author nik
+ */
+public class SourceItemNode extends SourceItemNodeBase {
+ private final PackagingSourceItem mySourceItem;
+
+ public SourceItemNode(ArtifactEditorContext context, NodeDescriptor parentDescriptor, PackagingSourceItem sourceItem, ArtifactEditorEx artifactEditor) {
+ super(context, parentDescriptor, sourceItem.createPresentation(context), artifactEditor);
+ mySourceItem = sourceItem;
+ }
+
+ @NotNull
+ @Override
+ public Object[] getEqualityObjects() {
+ return new Object[]{mySourceItem};
+ }
+
+ @Override
+ public void handleDoubleClickOrEnter(SimpleTree tree, InputEvent inputEvent) {
+ if (mySourceItem.isProvideElements() && getChildren().length == 0) {
+ getArtifactEditor().getLayoutTreeComponent().putIntoDefaultLocations(Collections.singletonList(mySourceItem));
+ }
+ }
+
+ @Override
+ public PackagingSourceItem getSourceItem() {
+ return mySourceItem;
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/sourceItems/SourceItemNodeBase.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/sourceItems/SourceItemNodeBase.java
new file mode 100644
index 0000000..359ee78
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/sourceItems/SourceItemNodeBase.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2000-2009 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.artifacts.sourceItems;
+
+import com.intellij.ide.projectView.PresentationData;
+import com.intellij.ide.util.treeView.NodeDescriptor;
+import com.intellij.openapi.extensions.Extensions;
+import com.intellij.openapi.roots.ui.configuration.artifacts.ArtifactEditorEx;
+import com.intellij.openapi.roots.ui.configuration.artifacts.nodes.ArtifactsTreeNode;
+import com.intellij.packaging.artifacts.Artifact;
+import com.intellij.packaging.ui.ArtifactEditorContext;
+import com.intellij.packaging.ui.PackagingSourceItem;
+import com.intellij.packaging.ui.PackagingSourceItemsProvider;
+import com.intellij.packaging.ui.TreeNodePresentation;
+import com.intellij.ui.treeStructure.SimpleNode;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * @author nik
+ */
+public abstract class SourceItemNodeBase extends ArtifactsTreeNode {
+ private Artifact myArtifact;
+ private final ArtifactEditorEx myArtifactEditor;
+
+ public SourceItemNodeBase(ArtifactEditorContext context, NodeDescriptor parentDescriptor, final TreeNodePresentation presentation,
+ ArtifactEditorEx artifactEditor) {
+ super(context, parentDescriptor, presentation);
+ myArtifact = artifactEditor.getArtifact();
+ myArtifactEditor = artifactEditor;
+ }
+
+ protected ArtifactEditorEx getArtifactEditor() {
+ return myArtifactEditor;
+ }
+
+ @Override
+ protected void update(PresentationData presentation) {
+ final Artifact artifact = myArtifactEditor.getArtifact();
+ if (!myArtifact.equals(artifact)) {
+ myArtifact = artifact;
+ }
+ super.update(presentation);
+ }
+
+ @Override
+ protected SimpleNode[] buildChildren() {
+ final PackagingSourceItemsProvider[] providers = Extensions.getExtensions(PackagingSourceItemsProvider.EP_NAME);
+ List<SimpleNode> children = new ArrayList<SimpleNode>();
+ for (PackagingSourceItemsProvider provider : providers) {
+ final Collection<? extends PackagingSourceItem> items = provider.getSourceItems(myContext, myArtifact, getSourceItem());
+ for (PackagingSourceItem item : items) {
+ if (myArtifact.getArtifactType().isSuitableItem(item)) {
+ children.add(new SourceItemNode(myContext, this, item, myArtifactEditor));
+ }
+ }
+ }
+ return children.isEmpty() ? NO_CHILDREN : children.toArray(new SimpleNode[children.size()]);
+ }
+
+ @Nullable
+ protected abstract PackagingSourceItem getSourceItem();
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/sourceItems/SourceItemsTree.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/sourceItems/SourceItemsTree.java
new file mode 100644
index 0000000..82af315
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/sourceItems/SourceItemsTree.java
@@ -0,0 +1,177 @@
+/*
+ * Copyright 2000-2009 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.artifacts.sourceItems;
+
+import com.intellij.ide.CommonActionsManager;
+import com.intellij.ide.DefaultTreeExpander;
+import com.intellij.ide.dnd.AdvancedDnDSource;
+import com.intellij.ide.dnd.DnDAction;
+import com.intellij.ide.dnd.DnDDragStartBean;
+import com.intellij.ide.dnd.DnDManager;
+import com.intellij.ide.dnd.aware.DnDAwareTree;
+import com.intellij.openapi.Disposable;
+import com.intellij.openapi.actionSystem.*;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.project.ProjectBundle;
+import com.intellij.openapi.roots.ui.configuration.artifacts.ArtifactEditorImpl;
+import com.intellij.openapi.roots.ui.configuration.artifacts.SimpleDnDAwareTree;
+import com.intellij.openapi.roots.ui.configuration.artifacts.SourceItemsDraggingObject;
+import com.intellij.openapi.roots.ui.configuration.artifacts.sourceItems.actions.*;
+import com.intellij.openapi.util.Disposer;
+import com.intellij.openapi.util.Pair;
+import com.intellij.packaging.ui.ArtifactEditorContext;
+import com.intellij.packaging.ui.PackagingSourceItem;
+import com.intellij.ui.PopupHandler;
+import com.intellij.ui.treeStructure.SimpleTreeBuilder;
+import com.intellij.ui.treeStructure.SimpleTreeStructure;
+import com.intellij.ui.treeStructure.WeightBasedComparator;
+import com.intellij.util.ui.tree.TreeUtil;
+
+import javax.swing.tree.DefaultMutableTreeNode;
+import java.awt.*;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author nik
+ */
+public class SourceItemsTree extends SimpleDnDAwareTree implements AdvancedDnDSource, Disposable{
+ private final ArtifactEditorImpl myArtifactsEditor;
+ private final SimpleTreeBuilder myBuilder;
+
+ public SourceItemsTree(ArtifactEditorContext editorContext, ArtifactEditorImpl artifactsEditor) {
+ myArtifactsEditor = artifactsEditor;
+ myBuilder = new SimpleTreeBuilder(this, this.getBuilderModel(), new SourceItemsTreeStructure(editorContext, artifactsEditor), new WeightBasedComparator(true));
+ setRootVisible(false);
+ setShowsRootHandles(true);
+ Disposer.register(this, myBuilder);
+ PopupHandler.installPopupHandler(this, createPopupGroup(), ActionPlaces.UNKNOWN, ActionManager.getInstance());
+ installDnD();
+ }
+
+ private void installDnD() {
+ if (!ApplicationManager.getApplication().isUnitTestMode()) {
+ DnDManager.getInstance().registerSource(this);
+ }
+ }
+
+ private ActionGroup createPopupGroup() {
+ final DefaultActionGroup group = new DefaultActionGroup();
+ group.add(new PutSourceItemIntoDefaultLocationAction(this, myArtifactsEditor));
+ group.add(new PackAndPutIntoDefaultLocationAction(this, myArtifactsEditor));
+ group.add(new PutSourceItemIntoParentAndLinkViaManifestAction(this, myArtifactsEditor));
+ group.add(new ExtractIntoDefaultLocationAction(this, myArtifactsEditor));
+
+ group.add(Separator.getInstance());
+ group.add(new SourceItemNavigateAction(this));
+ group.add(new SourceItemFindUsagesAction(this, myArtifactsEditor.getContext().getProject(), myArtifactsEditor.getContext().getParent()));
+
+ DefaultTreeExpander expander = new DefaultTreeExpander(this);
+ final CommonActionsManager commonActionsManager = CommonActionsManager.getInstance();
+ group.add(Separator.getInstance());
+ group.addAction(commonActionsManager.createExpandAllAction(expander, this));
+ group.addAction(commonActionsManager.createCollapseAllAction(expander, this));
+ return group;
+ }
+
+ public void rebuildTree() {
+ myBuilder.updateFromRoot(true);
+ }
+
+ public void initTree() {
+ myBuilder.initRootNode();
+ }
+
+ @Override
+ public void dispose() {
+ if (!ApplicationManager.getApplication().isUnitTestMode()) {
+ DnDManager.getInstance().unregisterSource(this);
+ }
+ }
+
+ private DefaultMutableTreeNode[] getSelectedTreeNodes() {
+ return getSelectedNodes(DefaultMutableTreeNode.class, null);
+ }
+
+ @Override
+ public boolean canStartDragging(DnDAction action, Point dragOrigin) {
+ return !getSelectedItems().isEmpty();
+ }
+
+ @Override
+ public DnDDragStartBean startDragging(DnDAction action, Point dragOrigin) {
+ List<PackagingSourceItem> items = getSelectedItems();
+ return new DnDDragStartBean(new SourceItemsDraggingObject(items.toArray(new PackagingSourceItem[items.size()])));
+ }
+
+ public List<SourceItemNode> getSelectedSourceItemNodes() {
+ final List<SourceItemNode> nodes = new ArrayList<SourceItemNode>();
+ for (DefaultMutableTreeNode treeNode : getSelectedTreeNodes()) {
+ final Object userObject = treeNode.getUserObject();
+ if (userObject instanceof SourceItemNode) {
+ nodes.add((SourceItemNode)userObject);
+ }
+ }
+ return nodes;
+ }
+
+ public List<PackagingSourceItem> getSelectedItems() {
+ List<PackagingSourceItem> items = new ArrayList<PackagingSourceItem>();
+ for (SourceItemNode node : getSelectedSourceItemNodes()) {
+ final PackagingSourceItem sourceItem = node.getSourceItem();
+ if (sourceItem != null && sourceItem.isProvideElements()) {
+ items.add(sourceItem);
+ }
+ }
+ return items;
+ }
+
+ @Override
+ public Pair<Image, Point> createDraggedImage(DnDAction action, Point dragOrigin) {
+ final DefaultMutableTreeNode[] nodes = getSelectedTreeNodes();
+ if (nodes.length == 1) {
+ return DnDAwareTree.getDragImage(this, TreeUtil.getPathFromRoot(nodes[0]), dragOrigin);
+ }
+ return DnDAwareTree.getDragImage(this, ProjectBundle.message("drag.n.drop.text.0.packaging.elements", nodes.length), dragOrigin);
+ }
+
+ @Override
+ public void dragDropEnd() {
+ }
+
+ @Override
+ public void dropActionChanged(int gestureModifiers) {
+ }
+
+ private static class SourceItemsTreeStructure extends SimpleTreeStructure {
+ private final ArtifactEditorContext myEditorContext;
+ private final ArtifactEditorImpl myArtifactsEditor;
+ private SourceItemsTreeRoot myRoot;
+
+ public SourceItemsTreeStructure(ArtifactEditorContext editorContext, ArtifactEditorImpl artifactsEditor) {
+ myEditorContext = editorContext;
+ myArtifactsEditor = artifactsEditor;
+ }
+
+ @Override
+ public Object getRootElement() {
+ if (myRoot == null) {
+ myRoot = new SourceItemsTreeRoot(myEditorContext, myArtifactsEditor);
+ }
+ return myRoot;
+ }
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/sourceItems/SourceItemsTreeRoot.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/sourceItems/SourceItemsTreeRoot.java
new file mode 100644
index 0000000..7c79071
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/sourceItems/SourceItemsTreeRoot.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2000-2009 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.artifacts.sourceItems;
+
+import com.intellij.ide.projectView.PresentationData;
+import com.intellij.openapi.roots.ui.configuration.artifacts.ArtifactEditorImpl;
+import com.intellij.packaging.ui.ArtifactEditorContext;
+import com.intellij.packaging.ui.PackagingSourceItem;
+import com.intellij.packaging.ui.TreeNodePresentation;
+import com.intellij.ui.SimpleTextAttributes;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * @author nik
+ */
+public class SourceItemsTreeRoot extends SourceItemNodeBase {
+ public SourceItemsTreeRoot(ArtifactEditorContext context, ArtifactEditorImpl artifactsEditor) {
+ super(context, null, new RootNodePresentation(), artifactsEditor);
+ }
+
+ @Override
+ protected PackagingSourceItem getSourceItem() {
+ return null;
+ }
+
+ @NotNull
+ @Override
+ public Object[] getEqualityObjects() {
+ return new Object[]{"root"};
+ }
+
+ @Override
+ public boolean isAutoExpandNode() {
+ return true;
+ }
+
+ private static class RootNodePresentation extends TreeNodePresentation {
+ @Override
+ public String getPresentableName() {
+ return "";
+ }
+
+ @Override
+ public void render(@NotNull PresentationData presentationData, SimpleTextAttributes mainAttributes,
+ SimpleTextAttributes commentAttributes) {
+ }
+
+ @Override
+ public int getWeight() {
+ return 0;
+ }
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/sourceItems/actions/ExtractIntoDefaultLocationAction.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/sourceItems/actions/ExtractIntoDefaultLocationAction.java
new file mode 100644
index 0000000..f373216
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/sourceItems/actions/ExtractIntoDefaultLocationAction.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2000-2012 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.artifacts.sourceItems.actions;
+
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.actionSystem.Presentation;
+import com.intellij.openapi.roots.ui.configuration.artifacts.ArtifactEditorEx;
+import com.intellij.openapi.roots.ui.configuration.artifacts.sourceItems.SourceItemsTree;
+import com.intellij.openapi.vfs.JarFileSystem;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.packaging.elements.PackagingElement;
+import com.intellij.packaging.elements.PackagingElementFactory;
+import com.intellij.packaging.elements.PackagingElementOutputKind;
+import com.intellij.packaging.impl.artifacts.ArtifactUtil;
+import com.intellij.packaging.impl.artifacts.PackagingElementPath;
+import com.intellij.packaging.impl.artifacts.PackagingElementProcessor;
+import com.intellij.packaging.impl.elements.FileCopyPackagingElement;
+import com.intellij.packaging.ui.ArtifactEditorContext;
+import com.intellij.packaging.ui.PackagingSourceItem;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * @author nik
+ */
+public class ExtractIntoDefaultLocationAction extends PutIntoDefaultLocationActionBase {
+ public ExtractIntoDefaultLocationAction(SourceItemsTree sourceItemsTree, ArtifactEditorEx artifactEditor) {
+ super(sourceItemsTree, artifactEditor);
+ }
+
+ @Override
+ public void update(AnActionEvent e) {
+ final String pathForClasses = myArtifactEditor.getArtifact().getArtifactType().getDefaultPathFor(PackagingElementOutputKind.DIRECTORIES_WITH_CLASSES);
+ final Presentation presentation = e.getPresentation();
+ if (onlyJarsSelected() && pathForClasses != null) {
+ presentation.setText("Extract Into " + getTargetLocationText(Collections.singleton(pathForClasses)));
+ presentation.setVisible(true);
+ }
+ else {
+ presentation.setVisible(false);
+ }
+ }
+
+ private boolean onlyJarsSelected() {
+ for (PackagingSourceItem item : mySourceItemsTree.getSelectedItems()) {
+ if (item.isProvideElements() && (!item.getKindOfProducedElements().containsJarFiles() || item.getKindOfProducedElements().containsDirectoriesWithClasses())) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public void actionPerformed(AnActionEvent e) {
+ final String pathForClasses = myArtifactEditor.getArtifact().getArtifactType().getDefaultPathFor(PackagingElementOutputKind.DIRECTORIES_WITH_CLASSES);
+ if (pathForClasses != null) {
+ final List<PackagingElement<?>> extracted = new ArrayList<PackagingElement<?>>();
+ for (PackagingSourceItem item : mySourceItemsTree.getSelectedItems()) {
+ final ArtifactEditorContext context = myArtifactEditor.getContext();
+ final List<? extends PackagingElement<?>> elements = item.createElements(context);
+ ArtifactUtil.processElementsWithSubstitutions(elements, context, context.getArtifactType(), PackagingElementPath.EMPTY, new PackagingElementProcessor<PackagingElement<?>>() {
+ @Override
+ public boolean process(@NotNull PackagingElement<?> element, @NotNull PackagingElementPath path) {
+ if (element instanceof FileCopyPackagingElement) {
+ final VirtualFile file = ((FileCopyPackagingElement)element).findFile();
+ if (file != null) {
+ final VirtualFile jarRoot = JarFileSystem.getInstance().getJarRootForLocalFile(file);
+ if (jarRoot != null) {
+ extracted.add(PackagingElementFactory.getInstance().createExtractedDirectory(jarRoot));
+ }
+ }
+ }
+ return true;
+ }
+ });
+ }
+ myArtifactEditor.getLayoutTreeComponent().putElements(pathForClasses, extracted);
+ }
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/sourceItems/actions/PackAndPutIntoDefaultLocationAction.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/sourceItems/actions/PackAndPutIntoDefaultLocationAction.java
new file mode 100644
index 0000000..b54cbe7
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/sourceItems/actions/PackAndPutIntoDefaultLocationAction.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2000-2009 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.artifacts.sourceItems.actions;
+
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.actionSystem.Presentation;
+import com.intellij.openapi.deployment.DeploymentUtil;
+import com.intellij.openapi.roots.ui.configuration.artifacts.ArtifactEditorEx;
+import com.intellij.openapi.roots.ui.configuration.artifacts.sourceItems.SourceItemsTree;
+import com.intellij.packaging.elements.PackagingElementOutputKind;
+import com.intellij.packaging.ui.PackagingSourceItem;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.List;
+
+/**
+ * @author nik
+ */
+public class PackAndPutIntoDefaultLocationAction extends PutIntoDefaultLocationActionBase {
+ public PackAndPutIntoDefaultLocationAction(SourceItemsTree sourceItemsTree, ArtifactEditorEx artifactEditor) {
+ super(sourceItemsTree, artifactEditor);
+ }
+
+ @Override
+ public void update(AnActionEvent e) {
+ final String jarName = suggestJarName();
+ final String pathForJars = myArtifactEditor.getArtifact().getArtifactType().getDefaultPathFor(PackagingElementOutputKind.JAR_FILES);
+ final Presentation presentation = e.getPresentation();
+ if (jarName != null && pathForJars != null) {
+ presentation.setText("Pack Into " + DeploymentUtil.appendToPath(pathForJars, jarName + ".jar"));
+ presentation.setVisible(true);
+ }
+ else {
+ presentation.setVisible(false);
+ }
+ }
+
+ @Nullable
+ private String suggestJarName() {
+ final List<PackagingSourceItem> items = mySourceItemsTree.getSelectedItems();
+ for (PackagingSourceItem item : items) {
+ if (item.isProvideElements() && item.getKindOfProducedElements().containsDirectoriesWithClasses()) {
+ return item.createPresentation(myArtifactEditor.getContext()).getPresentableName();
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public void actionPerformed(AnActionEvent e) {
+ final String pathForJars = myArtifactEditor.getArtifact().getArtifactType().getDefaultPathFor(PackagingElementOutputKind.JAR_FILES);
+ final String jarName = suggestJarName();
+ if (pathForJars != null) {
+ myArtifactEditor.getLayoutTreeComponent().packInto(mySourceItemsTree.getSelectedItems(),
+ DeploymentUtil.appendToPath(pathForJars, jarName + ".jar"));
+ }
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/sourceItems/actions/PutIntoDefaultLocationActionBase.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/sourceItems/actions/PutIntoDefaultLocationActionBase.java
new file mode 100644
index 0000000..a0f1c9f
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/sourceItems/actions/PutIntoDefaultLocationActionBase.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2000-2009 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.artifacts.sourceItems.actions;
+
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.roots.ui.configuration.artifacts.ArtifactEditorEx;
+import com.intellij.openapi.roots.ui.configuration.artifacts.sourceItems.SourceItemsTree;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.packaging.ui.PackagingSourceItem;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.Set;
+
+/**
+ * @author nik
+ */
+public abstract class PutIntoDefaultLocationActionBase extends AnAction {
+ protected final SourceItemsTree mySourceItemsTree;
+ protected final ArtifactEditorEx myArtifactEditor;
+
+ public PutIntoDefaultLocationActionBase(SourceItemsTree sourceItemsTree, ArtifactEditorEx artifactEditor) {
+ mySourceItemsTree = sourceItemsTree;
+ myArtifactEditor = artifactEditor;
+ }
+
+ @Nullable
+ protected String getDefaultPath(PackagingSourceItem item) {
+ return myArtifactEditor.getArtifact().getArtifactType().getDefaultPathFor(item);
+ }
+
+ protected static String getTargetLocationText(Set<String> paths) {
+ String target;
+ if (paths.size() == 1) {
+ final String path = StringUtil.trimStart(StringUtil.trimEnd(paths.iterator().next(), "/"), "/");
+ if (path.length() > 0) {
+ target = "/" + path;
+ }
+ else {
+ target = "Output Root";
+ }
+ }
+ else {
+ target = "Default Locations";
+ }
+ return target;
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/sourceItems/actions/PutSourceItemIntoDefaultLocationAction.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/sourceItems/actions/PutSourceItemIntoDefaultLocationAction.java
new file mode 100644
index 0000000..7e6504e
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/sourceItems/actions/PutSourceItemIntoDefaultLocationAction.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2000-2009 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.artifacts.sourceItems.actions;
+
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.actionSystem.Presentation;
+import com.intellij.openapi.roots.ui.configuration.artifacts.ArtifactEditorEx;
+import com.intellij.openapi.roots.ui.configuration.artifacts.sourceItems.SourceItemsTree;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.packaging.ui.PackagingSourceItem;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * @author nik
+ */
+public class PutSourceItemIntoDefaultLocationAction extends PutIntoDefaultLocationActionBase {
+ public PutSourceItemIntoDefaultLocationAction(SourceItemsTree sourceItemsTree, ArtifactEditorEx artifactEditor) {
+ super(sourceItemsTree, artifactEditor);
+ }
+
+ @Override
+ public void update(AnActionEvent e) {
+ final List<PackagingSourceItem> items = mySourceItemsTree.getSelectedItems();
+ boolean enabled = false;
+ final Presentation presentation = e.getPresentation();
+ if (!items.isEmpty()) {
+ enabled = true;
+ Set<String> paths = new HashSet<String>();
+ for (PackagingSourceItem item : items) {
+ final String path = getDefaultPath(item);
+ if (path == null) {
+ enabled = false;
+ break;
+ }
+ paths.add(StringUtil.trimStart(StringUtil.trimEnd(path, "/"), "/"));
+ }
+ presentation.setText("Put into " + getTargetLocationText(paths));
+ }
+ presentation.setVisible(enabled);
+ presentation.setEnabled(enabled);
+ }
+
+ @Override
+ public void actionPerformed(AnActionEvent e) {
+ myArtifactEditor.getLayoutTreeComponent().putIntoDefaultLocations(mySourceItemsTree.getSelectedItems());
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/sourceItems/actions/PutSourceItemIntoParentAndLinkViaManifestAction.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/sourceItems/actions/PutSourceItemIntoParentAndLinkViaManifestAction.java
new file mode 100644
index 0000000..96e99ff
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/sourceItems/actions/PutSourceItemIntoParentAndLinkViaManifestAction.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright 2000-2009 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.artifacts.sourceItems.actions;
+
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.actionSystem.Presentation;
+import com.intellij.openapi.roots.ui.configuration.artifacts.ArtifactEditorEx;
+import com.intellij.openapi.roots.ui.configuration.artifacts.ArtifactEditorImpl;
+import com.intellij.openapi.roots.ui.configuration.artifacts.sourceItems.SourceItemsTree;
+import com.intellij.openapi.util.EmptyRunnable;
+import com.intellij.openapi.util.Pair;
+import com.intellij.openapi.util.Ref;
+import com.intellij.packaging.artifacts.Artifact;
+import com.intellij.packaging.elements.CompositePackagingElement;
+import com.intellij.packaging.elements.PackagingElement;
+import com.intellij.packaging.impl.artifacts.ArtifactUtil;
+import com.intellij.packaging.impl.artifacts.ParentElementProcessor;
+import com.intellij.packaging.impl.elements.ManifestFileUtil;
+import com.intellij.packaging.ui.ArtifactEditor;
+import com.intellij.packaging.ui.ArtifactEditorContext;
+import com.intellij.packaging.ui.PackagingSourceItem;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author nik
+ */
+public class PutSourceItemIntoParentAndLinkViaManifestAction extends PutIntoDefaultLocationActionBase {
+ public PutSourceItemIntoParentAndLinkViaManifestAction(SourceItemsTree sourceItemsTree, ArtifactEditorEx artifactEditor) {
+ super(sourceItemsTree, artifactEditor);
+ }
+
+ @Override
+ public void update(AnActionEvent e) {
+ final Presentation presentation = e.getPresentation();
+ final Artifact artifact = myArtifactEditor.getArtifact();
+
+ final ParentElementsInfo parentInfo = findParentAndGrandParent(artifact);
+ if (parentInfo != null) {
+ presentation.setText("Put Into '" + parentInfo.getGrandparentArtifact().getName() + "' and link via manifest");
+ }
+
+ boolean enable = parentInfo != null;
+ boolean isProvideElements = false;
+ for (PackagingSourceItem item : mySourceItemsTree.getSelectedItems()) {
+ isProvideElements |= item.isProvideElements();
+ if (!item.getKindOfProducedElements().containsJarFiles()) {
+ enable = false;
+ break;
+ }
+ }
+ enable &= isProvideElements;
+ presentation.setVisible(enable);
+ presentation.setEnabled(enable);
+ }
+
+ @Nullable
+ private ParentElementsInfo findParentAndGrandParent(Artifact artifact) {
+ final Ref<ParentElementsInfo> result = Ref.create(null);
+ ArtifactUtil.processParents(artifact, myArtifactEditor.getContext(), new ParentElementProcessor() {
+ @Override
+ public boolean process(@NotNull CompositePackagingElement<?> element,
+ @NotNull List<Pair<Artifact,CompositePackagingElement<?>>> parents,
+ @NotNull Artifact artifact) {
+ if (parents.size() == 1) {
+ final Pair<Artifact, CompositePackagingElement<?>> parent = parents.get(0);
+ result.set(new ParentElementsInfo(parent.getFirst(), parent.getSecond(), artifact, element));
+ return false;
+ }
+ return true;
+ }
+ }, 1);
+
+ return result.get();
+ }
+
+ @Override
+ public void actionPerformed(AnActionEvent e) {
+ final List<PackagingSourceItem> items = mySourceItemsTree.getSelectedItems();
+ ParentElementsInfo parentsInfo = findParentAndGrandParent(myArtifactEditor.getArtifact());
+ if (parentsInfo == null) {
+ return;
+ }
+
+ final Artifact artifact = parentsInfo.getGrandparentArtifact();
+ final ArtifactEditorContext context = myArtifactEditor.getContext();
+ //todo[nik] improve
+ final Runnable emptyRunnable = EmptyRunnable.getInstance();
+ context.editLayout(artifact, emptyRunnable);
+ context.editLayout(parentsInfo.getParentArtifact(), emptyRunnable);
+ parentsInfo = findParentAndGrandParent(myArtifactEditor.getArtifact());//find elements under modifiable root
+ if (parentsInfo == null) {
+ return;
+ }
+
+ final CompositePackagingElement<?> grandParent = parentsInfo.getGrandparentElement();
+ final List<String> classpath = new ArrayList<String>();
+ context.editLayout(artifact, new Runnable() {
+ @Override
+ public void run() {
+ for (PackagingSourceItem item : items) {
+ final List<? extends PackagingElement<?>> elements = item.createElements(context);
+ grandParent.addOrFindChildren(elements);
+ classpath.addAll(ManifestFileUtil.getClasspathForElements(elements, context, artifact.getArtifactType()));
+ }
+ }
+ });
+
+ final ArtifactEditor parentArtifactEditor = context.getOrCreateEditor(parentsInfo.getParentArtifact());
+ parentArtifactEditor.addToClasspath(parentsInfo.getParentElement(), classpath);
+ ((ArtifactEditorImpl)context.getOrCreateEditor(parentsInfo.getGrandparentArtifact())).rebuildTries();
+ }
+
+ private static class ParentElementsInfo {
+ private final Artifact myParentArtifact;
+ private final CompositePackagingElement<?> myParentElement;
+ private final Artifact myGrandparentArtifact;
+ private final CompositePackagingElement<?> myGrandparentElement;
+
+ private ParentElementsInfo(Artifact parentArtifact,
+ CompositePackagingElement<?> parentElement,
+ Artifact grandparentArtifact,
+ CompositePackagingElement<?> grandparentElement) {
+ myParentArtifact = parentArtifact;
+ myParentElement = parentElement;
+ myGrandparentArtifact = grandparentArtifact;
+ myGrandparentElement = grandparentElement;
+ }
+
+ public Artifact getParentArtifact() {
+ return myParentArtifact;
+ }
+
+ public CompositePackagingElement<?> getParentElement() {
+ return myParentElement;
+ }
+
+ public Artifact getGrandparentArtifact() {
+ return myGrandparentArtifact;
+ }
+
+ public CompositePackagingElement<?> getGrandparentElement() {
+ return myGrandparentElement;
+ }
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/sourceItems/actions/SourceItemFindUsagesAction.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/sourceItems/actions/SourceItemFindUsagesAction.java
new file mode 100644
index 0000000..7a588a3
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/sourceItems/actions/SourceItemFindUsagesAction.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2000-2009 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.artifacts.sourceItems.actions;
+
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.roots.ui.configuration.artifacts.ArtifactsStructureConfigurableContext;
+import com.intellij.openapi.roots.ui.configuration.artifacts.actions.ArtifactEditorFindUsagesActionBase;
+import com.intellij.openapi.roots.ui.configuration.artifacts.nodes.ArtifactsTreeNode;
+import com.intellij.openapi.roots.ui.configuration.artifacts.sourceItems.*;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.StructureConfigurableContext;
+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.ProjectStructureElement;
+import com.intellij.packaging.ui.PackagingSourceItem;
+
+import java.util.List;
+
+/**
+ * @author nik
+ */
+public class SourceItemFindUsagesAction extends ArtifactEditorFindUsagesActionBase {
+ private final SourceItemsTree myTree;
+
+ public SourceItemFindUsagesAction(SourceItemsTree tree, Project project, ArtifactsStructureConfigurableContext artifactContext) {
+ super(tree, project, artifactContext);
+ myTree = tree;
+ }
+
+ @Override
+ protected ProjectStructureElement getSelectedElement() {
+ final List<SourceItemNode> nodes = myTree.getSelectedSourceItemNodes();
+ if (nodes.size() != 1) return null;
+ ArtifactsTreeNode node = nodes.get(0);
+ if (!(node instanceof SourceItemNode)) {
+ return null;
+ }
+
+ PackagingSourceItem sourceItem = ((SourceItemNode)node).getSourceItem();
+ if (sourceItem == null) return null;
+
+ final StructureConfigurableContext context = getContext();
+ if (sourceItem instanceof ModuleOutputSourceItem) {
+ return new ModuleProjectStructureElement(context, ((ModuleOutputSourceItem)sourceItem).getModule());
+ }
+ else if (sourceItem instanceof LibrarySourceItem) {
+ return new LibraryProjectStructureElement(context, ((LibrarySourceItem)sourceItem).getLibrary());
+ }
+ else if (sourceItem instanceof ArtifactSourceItem) {
+ return myArtifactContext.getOrCreateArtifactElement(((ArtifactSourceItem)sourceItem).getArtifact());
+ }
+ return null;
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/sourceItems/actions/SourceItemNavigateAction.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/sourceItems/actions/SourceItemNavigateAction.java
new file mode 100644
index 0000000..e066771
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/sourceItems/actions/SourceItemNavigateAction.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2000-2009 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.artifacts.sourceItems.actions;
+
+import com.intellij.openapi.roots.ui.configuration.artifacts.actions.ArtifactEditorNavigateActionBase;
+import com.intellij.openapi.roots.ui.configuration.artifacts.sourceItems.SourceItemNode;
+import com.intellij.openapi.roots.ui.configuration.artifacts.sourceItems.SourceItemsTree;
+import com.intellij.packaging.ui.TreeNodePresentation;
+
+import java.util.List;
+
+/**
+ * @author nik
+ */
+public class SourceItemNavigateAction extends ArtifactEditorNavigateActionBase {
+ private final SourceItemsTree mySourceItemsTree;
+
+ public SourceItemNavigateAction(SourceItemsTree sourceItemsTree) {
+ super(sourceItemsTree);
+ mySourceItemsTree = sourceItemsTree;
+ }
+
+ @Override
+ protected TreeNodePresentation getPresentation() {
+ final List<SourceItemNode> nodes = mySourceItemsTree.getSelectedSourceItemNodes();
+ if (nodes.size() == 1) {
+ return nodes.get(0).getElementPresentation();
+ }
+ return null;
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/classpath/AddItemPopupAction.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/classpath/AddItemPopupAction.java
new file mode 100644
index 0000000..d8a2388
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/classpath/AddItemPopupAction.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2000-2010 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.classpath;
+
+import com.intellij.openapi.ui.popup.PopupStep;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+
+/**
+* @author nik
+*/
+abstract class AddItemPopupAction<ItemType> extends ChooseAndAddAction<ItemType> {
+ private final String myTitle;
+ private final Icon myIcon;
+ private final int myIndex;
+
+ public AddItemPopupAction(ClasspathPanel classpathPanel, int index, String title, Icon icon) {
+ super(classpathPanel);
+ myTitle = title;
+ myIcon = icon;
+ myIndex = index;
+ }
+
+ public boolean hasSubStep() {
+ return false;
+ }
+
+ @Nullable
+ public PopupStep createSubStep() {
+ return null;
+ }
+
+ public String getTitle() {
+ return myTitle;
+ }
+
+ public Icon getIcon() {
+ return myIcon;
+ }
+
+ public int getIndex() {
+ return myIndex;
+ }
+
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/classpath/AddLibraryDependencyAction.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/classpath/AddLibraryDependencyAction.java
new file mode 100644
index 0000000..045609b
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/classpath/AddLibraryDependencyAction.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright 2000-2010 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.classpath;
+
+import com.intellij.openapi.project.ProjectBundle;
+import com.intellij.openapi.roots.*;
+import com.intellij.openapi.roots.libraries.Library;
+import com.intellij.openapi.roots.libraries.LibraryTable;
+import com.intellij.openapi.roots.libraries.LibraryType;
+import com.intellij.openapi.roots.ui.configuration.libraries.LibraryEditingUtil;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.LibrariesModifiableModel;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.StructureConfigurableContext;
+import com.intellij.openapi.ui.Messages;
+import com.intellij.openapi.ui.popup.PopupStep;
+import com.intellij.util.ParameterizedRunnable;
+import com.intellij.util.PlatformIcons;
+import com.intellij.util.containers.Predicate;
+import com.intellij.util.ui.classpath.ChooseLibrariesFromTablesDialog;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.List;
+
+/**
+* @author nik
+*/
+class AddLibraryDependencyAction extends AddItemPopupAction<Library> {
+ private final StructureConfigurableContext myContext;
+
+ public AddLibraryDependencyAction(ClasspathPanel classpathPanel, final int index, final String title,
+ final StructureConfigurableContext context) {
+ super(classpathPanel, index, title, PlatformIcons.LIBRARY_ICON);
+ myContext = context;
+ }
+
+ @Override
+ public boolean hasSubStep() {
+ return !hasLibraries() && LibraryEditingUtil.hasSuitableTypes(myClasspathPanel);
+ }
+
+ @Override
+ public PopupStep createSubStep() {
+ return LibraryEditingUtil.createChooseTypeStep(myClasspathPanel, new ParameterizedRunnable<LibraryType>() {
+ @Override
+ public void run(LibraryType libraryType) {
+ new AddNewLibraryDependencyAction(myClasspathPanel, myContext, libraryType).execute();
+ }
+ });
+ }
+
+ @Override
+ public void run() {
+ if (hasLibraries()) {
+ super.run();
+ }
+ else {
+ new AddNewLibraryDependencyAction(myClasspathPanel, myContext, null).run();
+ }
+ }
+
+ private boolean hasLibraries() {
+ final Predicate<Library> condition = LibraryEditingUtil.getNotAddedLibrariesCondition(myClasspathPanel.getRootModel());
+ for (LibraryTable table : ChooseLibrariesFromTablesDialog.getLibraryTables(myClasspathPanel.getProject(), true)) {
+ final LibrariesModifiableModel model = myContext.myLevel2Providers.get(table.getTableLevel());
+ if (model != null) {
+ for (Library library : model.getLibraries()) {
+ if (condition.apply(library)) {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ @Override
+ @Nullable
+ protected ClasspathTableItem<?> createTableItem(final Library item) {
+ // clear invalid order entry corresponding to added library if any
+ final ModifiableRootModel rootModel = myClasspathPanel.getRootModel();
+ final OrderEntry[] orderEntries = rootModel.getOrderEntries();
+ for (OrderEntry orderEntry : orderEntries) {
+ if (orderEntry instanceof LibraryOrderEntry) {
+ final LibraryOrderEntry libraryOrderEntry = (LibraryOrderEntry)orderEntry;
+ if (item.equals(libraryOrderEntry.getLibrary())) {
+ return ClasspathTableItem.createLibItem(libraryOrderEntry, myContext);
+ }
+ String name = item.getName();
+ if (name != null && name.equals(libraryOrderEntry.getLibraryName())) {
+ if (orderEntry.isValid()) {
+ Messages.showErrorDialog(ProjectBundle.message("classpath.message.library.already.added", item.getName()),
+ ProjectBundle.message("classpath.title.adding.dependency"));
+ return null;
+ }
+ else {
+ rootModel.removeOrderEntry(orderEntry);
+ }
+ }
+ }
+ }
+ final LibraryOrderEntry orderEntry = rootModel.addLibraryEntry(item);
+ DependencyScope defaultScope = getDefaultScope(item);
+ if (defaultScope != null) {
+ orderEntry.setScope(defaultScope);
+ }
+ return ClasspathTableItem.createLibItem(orderEntry, myContext);
+ }
+
+ @Nullable
+ private static DependencyScope getDefaultScope(Library item) {
+ for (LibraryDependencyScopeSuggester suggester : LibraryDependencyScopeSuggester.EP_NAME.getExtensions()) {
+ DependencyScope scope = suggester.getDefaultDependencyScope(item);
+ if (scope != null) {
+ return scope;
+ }
+ }
+ return null;
+ }
+
+ @Override
+ protected ClasspathElementChooser<Library> createChooser() {
+ return new ExistingLibraryChooser();
+ }
+
+ class ExistingLibraryChooser implements ClasspathElementChooser<Library> {
+ @Override
+ @NotNull
+ public List<Library> chooseElements() {
+ final Predicate<Library> condition = LibraryEditingUtil.getNotAddedLibrariesCondition(myClasspathPanel.getRootModel());
+ ProjectStructureChooseLibrariesDialog dialog = new ProjectStructureChooseLibrariesDialog(myClasspathPanel, myContext,
+ condition);
+ dialog.show();
+ return dialog.getSelectedLibraries();
+ }
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/classpath/AddModuleDependencyAction.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/classpath/AddModuleDependencyAction.java
new file mode 100644
index 0000000..e7f6376
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/classpath/AddModuleDependencyAction.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2000-2011 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.classpath;
+
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.module.StdModuleTypes;
+import com.intellij.openapi.project.ProjectBundle;
+import com.intellij.openapi.roots.ModifiableRootModel;
+import com.intellij.openapi.roots.ui.configuration.ChooseModulesDialog;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.StructureConfigurableContext;
+import com.intellij.openapi.ui.Messages;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.*;
+
+/**
+* @author nik
+*/
+class AddModuleDependencyAction extends AddItemPopupAction<Module> {
+ private final StructureConfigurableContext myContext;
+ private final ClasspathPanel myClasspathPanel;
+
+ public AddModuleDependencyAction(final ClasspathPanel classpathPanel,
+ int actionIndex,
+ StructureConfigurableContext context) {
+ super(classpathPanel, actionIndex, ProjectBundle.message("classpath.add.module.dependency.action"),
+ StdModuleTypes.JAVA.getIcon());
+ myContext = context;
+ myClasspathPanel = classpathPanel;
+ }
+
+ @Override
+ protected ClasspathTableItem<?> createTableItem(final Module item) {
+ return ClasspathTableItem.createItem(myClasspathPanel.getRootModel().addModuleOrderEntry(item), myContext);
+ }
+
+ private List<Module> getNotAddedModules() {
+ final ModifiableRootModel rootModel = myClasspathPanel.getRootModel();
+ Set<Module> addedModules = new HashSet<Module>(Arrays.asList(rootModel.getModuleDependencies(true)));
+ addedModules.add(rootModel.getModule());
+
+ final Module[] modules = myClasspathPanel.getModuleConfigurationState().getModulesProvider().getModules();
+ final List<Module> elements = new ArrayList<Module>();
+ for (final Module module : modules) {
+ if (!addedModules.contains(module)) {
+ elements.add(module);
+ }
+ }
+ return elements;
+ }
+
+ @Override
+ protected ClasspathElementChooser<Module> createChooser() {
+ final List<Module> chooseItems = getNotAddedModules();
+ if (chooseItems.isEmpty()) {
+ Messages.showMessageDialog(myClasspathPanel.getComponent(), ProjectBundle.message("message.no.module.dependency.candidates"), getTitle(),
+ Messages.getInformationIcon());
+ return null;
+ }
+ return new ModuleChooser(myClasspathPanel, chooseItems, ProjectBundle.message("classpath.chooser.title.add.module.dependency"),
+ ProjectBundle.message("classpath.chooser.description.add.module.dependency"));
+ }
+
+ private static class ModuleChooser implements ClasspathElementChooser<Module> {
+ private final List<Module> myItems;
+ private final String myTitle;
+ private final String myDescription;
+ private final ClasspathPanel myClasspathPanel;
+
+ public ModuleChooser(final ClasspathPanel classpathPanel,
+ final List<Module> items,
+ final String title,
+ String description) {
+ myItems = items;
+ myTitle = title;
+ myDescription = description;
+ myClasspathPanel = classpathPanel;
+ }
+
+ @Override
+ @NotNull
+ public List<Module> chooseElements() {
+ ChooseModulesDialog dialog = new ChooseModulesDialog(myClasspathPanel.getComponent(), myItems, myTitle, myDescription);
+ dialog.show();
+ return dialog.getChosenElements();
+ }
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/classpath/AddNewLibraryDependencyAction.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/classpath/AddNewLibraryDependencyAction.java
new file mode 100644
index 0000000..b35b469
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/classpath/AddNewLibraryDependencyAction.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2000-2010 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.classpath;
+
+import com.intellij.openapi.roots.LibraryOrderEntry;
+import com.intellij.openapi.roots.OrderEntry;
+import com.intellij.openapi.roots.libraries.Library;
+import com.intellij.openapi.roots.libraries.LibraryType;
+import com.intellij.openapi.roots.ui.configuration.libraries.LibraryEditingUtil;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.StructureConfigurableContext;
+import com.intellij.openapi.ui.popup.JBPopupFactory;
+import com.intellij.openapi.ui.popup.ListPopup;
+import com.intellij.util.ParameterizedRunnable;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+
+/**
+* @author nik
+*/
+class AddNewLibraryDependencyAction extends ChooseAndAddAction<Library> {
+ private final StructureConfigurableContext myContext;
+ private final LibraryType myLibraryType;
+
+ public AddNewLibraryDependencyAction(final ClasspathPanel classpathPanel,
+ StructureConfigurableContext context, LibraryType libraryType) {
+ super(classpathPanel);
+ myContext = context;
+ myLibraryType = libraryType;
+ }
+
+ @Override
+ protected ClasspathTableItem<?> createTableItem(final Library item) {
+ final OrderEntry[] entries = myClasspathPanel.getRootModel().getOrderEntries();
+ for (OrderEntry entry : entries) {
+ if (entry instanceof LibraryOrderEntry) {
+ final LibraryOrderEntry libraryOrderEntry = (LibraryOrderEntry)entry;
+ if (item.equals(libraryOrderEntry.getLibrary())) {
+ return ClasspathTableItem.createLibItem(libraryOrderEntry, myContext);
+ }
+ }
+ }
+ return ClasspathTableItem.createLibItem(myClasspathPanel.getRootModel().addLibraryEntry(item), myContext);
+ }
+
+ @Override
+ protected ClasspathElementChooser<Library> createChooser() {
+ return new NewLibraryChooser(myClasspathPanel.getProject(), myClasspathPanel.getRootModel(), myLibraryType, myContext, myClasspathPanel.getComponent());
+ }
+
+ public static void chooseTypeAndCreate(final ClasspathPanel classpathPanel,
+ final StructureConfigurableContext context,
+ final JButton contextButton, @NotNull final LibraryCreatedCallback callback) {
+ if (LibraryEditingUtil.hasSuitableTypes(classpathPanel)) {
+ final ListPopup popup = JBPopupFactory.getInstance().createListPopup(LibraryEditingUtil.createChooseTypeStep(classpathPanel, new ParameterizedRunnable<LibraryType>() {
+ @Override
+ public void run(LibraryType libraryType) {
+ doCreateLibrary(classpathPanel, context, callback, contextButton, libraryType);
+ }
+ }));
+ popup.showUnderneathOf(contextButton);
+ }
+ else {
+ doCreateLibrary(classpathPanel, context, callback, contextButton, null);
+ }
+ }
+
+ private static void doCreateLibrary(ClasspathPanel classpathPanel,
+ StructureConfigurableContext context,
+ LibraryCreatedCallback callback, final JComponent component, final @Nullable LibraryType libraryType) {
+ final NewLibraryChooser chooser = new NewLibraryChooser(classpathPanel.getProject(), classpathPanel.getRootModel(), libraryType, context, component);
+ final Library library = chooser.createLibrary();
+ if (library != null) {
+ callback.libraryCreated(library);
+ }
+ }
+
+ interface LibraryCreatedCallback {
+ void libraryCreated(@NotNull Library library);
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/classpath/AddNewModuleLibraryAction.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/classpath/AddNewModuleLibraryAction.java
new file mode 100644
index 0000000..51abf3e
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/classpath/AddNewModuleLibraryAction.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2000-2010 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.classpath;
+
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.project.ProjectBundle;
+import com.intellij.openapi.roots.LibraryOrderEntry;
+import com.intellij.openapi.roots.OrderEntry;
+import com.intellij.openapi.roots.libraries.Library;
+import com.intellij.openapi.roots.libraries.LibraryTable;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.StructureConfigurableContext;
+import com.intellij.util.PlatformIcons;
+
+/**
+* @author nik
+*/
+class AddNewModuleLibraryAction extends AddItemPopupAction<Library> {
+ private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.roots.ui.configuration.classpath.AddNewModuleLibraryAction");
+ private final StructureConfigurableContext myContext;
+
+ public AddNewModuleLibraryAction(final ClasspathPanel classpathPanel,
+ int actionIndex,
+ StructureConfigurableContext context) {
+ super(classpathPanel, actionIndex, ProjectBundle.message("classpath.add.simple.module.library.action"), PlatformIcons.JAR_ICON);
+ myContext = context;
+ }
+
+ @Override
+ protected ClasspathTableItem<?> createTableItem(final Library item) {
+ final OrderEntry[] entries = myClasspathPanel.getRootModel().getOrderEntries();
+ for (OrderEntry entry : entries) {
+ if (entry instanceof LibraryOrderEntry) {
+ final LibraryOrderEntry libraryOrderEntry = (LibraryOrderEntry)entry;
+ if (item.equals(libraryOrderEntry.getLibrary())) {
+ return ClasspathTableItem.createLibItem(libraryOrderEntry, myContext);
+ }
+ }
+ }
+ LOG.error("Unknown library " + item);
+ return null;
+ }
+
+ @Override
+ protected ClasspathElementChooser<Library> createChooser() {
+ final LibraryTable.ModifiableModel moduleLibraryModel = myClasspathPanel.getRootModel().getModuleLibraryTable().getModifiableModel();
+ return new CreateModuleLibraryChooser(myClasspathPanel, moduleLibraryModel);
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/classpath/ChangeLibraryLevelActionBase.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/classpath/ChangeLibraryLevelActionBase.java
new file mode 100644
index 0000000..c8f8b97
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/classpath/ChangeLibraryLevelActionBase.java
@@ -0,0 +1,202 @@
+/*
+ * Copyright 2000-2012 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.classpath;
+
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.actionSystem.Presentation;
+import com.intellij.openapi.application.AccessToken;
+import com.intellij.openapi.application.Result;
+import com.intellij.openapi.application.WriteAction;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.progress.ProgressIndicator;
+import com.intellij.openapi.progress.Task;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.roots.OrderRootType;
+import com.intellij.openapi.roots.impl.libraries.LibraryEx;
+import com.intellij.openapi.roots.impl.libraries.LibraryTableBase;
+import com.intellij.openapi.roots.impl.libraries.LibraryTableImplUtil;
+import com.intellij.openapi.roots.impl.libraries.LibraryTypeServiceImpl;
+import com.intellij.openapi.roots.libraries.Library;
+import com.intellij.openapi.roots.ui.configuration.LibraryTableModifiableModelProvider;
+import com.intellij.openapi.roots.ui.configuration.libraries.LibraryEditingUtil;
+import com.intellij.openapi.roots.ui.configuration.libraryEditor.ChangeLibraryLevelDialog;
+import com.intellij.openapi.ui.Messages;
+import com.intellij.openapi.util.Ref;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.openapi.vfs.*;
+import com.intellij.util.PathUtil;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import java.io.File;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.LinkedHashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * @author nik
+ */
+public abstract class ChangeLibraryLevelActionBase extends AnAction {
+ private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.roots.ui.configuration.classpath.ChangeLibraryLevelActionBase");
+ protected final Project myProject;
+ protected final String myTargetTableLevel;
+ protected final boolean myCopy;
+
+ public ChangeLibraryLevelActionBase(@NotNull Project project, @NotNull String targetTableName, @NotNull String targetTableLevel, boolean copy) {
+ myProject = project;
+ myTargetTableLevel = targetTableLevel;
+ myCopy = copy;
+ getTemplatePresentation().setText(getActionName() + " to " + targetTableName + "...");
+ }
+
+ protected abstract LibraryTableModifiableModelProvider getModifiableTableModelProvider();
+
+ protected abstract JComponent getParentComponent();
+
+ @Nullable
+ protected Library doCopy(LibraryEx library) {
+ final VirtualFile baseDir = getBaseDir();
+ final String libPath = baseDir != null ? baseDir.getPath() + "/lib" : "";
+ final VirtualFile[] classesRoots = library.getFiles(OrderRootType.CLASSES);
+ boolean allowEmptyName = isConvertingToModuleLibrary() && classesRoots.length == 1;
+ final String libraryName = allowEmptyName ? "" : StringUtil.notNullize(library.getName(), LibraryTypeServiceImpl.suggestLibraryName(classesRoots));
+ final LibraryTableModifiableModelProvider provider = getModifiableTableModelProvider();
+ final ChangeLibraryLevelDialog dialog = new ChangeLibraryLevelDialog(getParentComponent(), myProject, myCopy,
+ libraryName, libPath, allowEmptyName, provider);
+ dialog.show();
+ if (!dialog.isOK()) {
+ return null;
+ }
+
+ final Set<File> fileToCopy = new LinkedHashSet<File>();
+ final Map<String, String> copiedFiles = new HashMap<String, String>();
+ final String targetDirectoryPath = dialog.getDirectoryForFilesPath();
+ if (targetDirectoryPath != null) {
+ for (OrderRootType type : OrderRootType.getAllTypes()) {
+ for (VirtualFile root : library.getFiles(type)) {
+ fileToCopy.add(VfsUtil.virtualToIoFile(PathUtil.getLocalFile(root)));
+ }
+ }
+ if (!copyOrMoveFiles(fileToCopy, targetDirectoryPath, copiedFiles)) {
+ return null;
+ }
+ }
+
+ final Library copied = ((LibraryTableBase.ModifiableModelEx)provider.getModifiableModel()).createLibrary(dialog.getLibraryName(), library.getKind());
+ final LibraryEx.ModifiableModelEx model = (LibraryEx.ModifiableModelEx)copied.getModifiableModel();
+ LibraryEditingUtil.copyLibrary(library, copiedFiles, model);
+
+ AccessToken token = WriteAction.start();
+ try {
+ model.commit();
+ }
+ finally {
+ token.finish();
+ }
+ return copied;
+ }
+
+ private boolean copyOrMoveFiles(final Set<File> filesToProcess,
+ @NotNull final String targetDirPath,
+ final Map<String, String> copiedFiles) {
+ final Ref<Boolean> finished = Ref.create(false);
+ new Task.Modal(myProject, (myCopy ? "Copying" : "Moving") + " Library Files", true) {
+ @Override
+ public void run(@NotNull ProgressIndicator indicator) {
+ final File targetDir = new File(FileUtil.toSystemDependentName(targetDirPath));
+ for (final File from : filesToProcess) {
+ indicator.checkCanceled();
+ final File to = FileUtil.findSequentNonexistentFile(targetDir, FileUtil.getNameWithoutExtension(from),
+ FileUtil.getExtension(from.getName()));
+ try {
+ if (from.isDirectory()) {
+ if (myCopy) {
+ FileUtil.copyDir(from, to);
+ }
+ else {
+ FileUtil.moveDirWithContent(from, to);
+ }
+ }
+ else {
+ if (myCopy) {
+ FileUtil.copy(from, to);
+ }
+ else {
+ FileUtil.rename(from, to);
+ }
+ }
+ }
+ catch (IOException e) {
+ final String actionName = getActionName();
+ final String message = "Cannot " + actionName.toLowerCase() + " file " + from.getAbsolutePath() + ": " + e.getMessage();
+ Messages.showErrorDialog(ChangeLibraryLevelActionBase.this.myProject, message, "Cannot " + actionName);
+ LOG.info(e);
+ return;
+ }
+
+ new WriteAction() {
+ @Override
+ protected void run(Result result) throws Throwable {
+ final VirtualFile virtualTo = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(to);
+ if (virtualTo != null) {
+ copiedFiles.put(FileUtil.toSystemIndependentName(from.getAbsolutePath()), virtualTo.getPath());
+ }
+ if (!myCopy) {
+ final VirtualFile parent = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(from.getParentFile());
+ if (parent != null) {
+ parent.refresh(false, false);
+ }
+ }
+ }
+ }.execute();
+ }
+ finished.set(true);
+ }
+ }.queue();
+ if (!finished.get()) {
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public void update(AnActionEvent e) {
+ final Presentation presentation = e.getPresentation();
+ boolean enabled = isEnabled();
+ presentation.setVisible(enabled);
+ presentation.setEnabled(enabled);
+ }
+
+ private String getActionName() {
+ return myCopy ? "Copy" : "Move";
+ }
+
+ @Nullable
+ protected VirtualFile getBaseDir() {
+ return myProject.getBaseDir();
+ }
+
+ protected boolean isConvertingToModuleLibrary() {
+ return myTargetTableLevel.equals(LibraryTableImplUtil.MODULE_LEVEL);
+ }
+
+ protected abstract boolean isEnabled();
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/classpath/ChangeLibraryLevelInClasspathAction.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/classpath/ChangeLibraryLevelInClasspathAction.java
new file mode 100644
index 0000000..2b42fd3
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/classpath/ChangeLibraryLevelInClasspathAction.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright 2000-2011 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.classpath;
+
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.roots.LibraryOrderEntry;
+import com.intellij.openapi.roots.OrderEntry;
+import com.intellij.openapi.roots.impl.libraries.LibraryEx;
+import com.intellij.openapi.roots.impl.libraries.LibraryTableImplUtil;
+import com.intellij.openapi.roots.libraries.Library;
+import com.intellij.openapi.roots.ui.configuration.LibraryTableModifiableModelProvider;
+import com.intellij.openapi.vfs.VirtualFile;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+
+/**
+ * @author nik
+ */
+class ChangeLibraryLevelInClasspathAction extends ChangeLibraryLevelActionBase {
+ private final ClasspathPanel myPanel;
+
+ public ChangeLibraryLevelInClasspathAction(@NotNull ClasspathPanel panel, final @NotNull String targetTableName, @NotNull String targetTableLevel) {
+ super(panel.getProject(), targetTableName, targetTableLevel, targetTableLevel.equals(LibraryTableImplUtil.MODULE_LEVEL));
+ myPanel = panel;
+ }
+
+ @Override
+ public void actionPerformed(AnActionEvent event) {
+ final OrderEntry entry = myPanel.getSelectedEntry();
+ if (!(entry instanceof LibraryOrderEntry)) return;
+ final LibraryEx library = (LibraryEx)((LibraryOrderEntry)entry).getLibrary();
+ if (library == null) return;
+
+ final Library copied = doCopy(library);
+ if (copied == null) return;
+
+ myPanel.getRootModel().removeOrderEntry(entry);
+ if (!isConvertingToModuleLibrary()) {
+ myPanel.getRootModel().addLibraryEntry(copied);
+ }
+ }
+
+ @Override
+ protected boolean isEnabled() {
+ final OrderEntry entry = myPanel.getSelectedEntry();
+ boolean enabled = false;
+ if (entry instanceof LibraryOrderEntry) {
+ final LibraryOrderEntry libraryOrderEntry = (LibraryOrderEntry)entry;
+ if (libraryOrderEntry.getLibrary() != null) {
+ boolean isFromModuleLibrary = libraryOrderEntry.isModuleLevel();
+ boolean isToModuleLibrary = isConvertingToModuleLibrary();
+ enabled = isFromModuleLibrary != isToModuleLibrary;
+ }
+ }
+ return enabled;
+ }
+
+ @Override
+ protected LibraryTableModifiableModelProvider getModifiableTableModelProvider() {
+ return myPanel.getModifiableModelProvider(myTargetTableLevel);
+ }
+
+ @Override
+ protected JComponent getParentComponent() {
+ return myPanel.getComponent();
+ }
+
+ @Override
+ @Nullable
+ protected VirtualFile getBaseDir() {
+ if (isConvertingToModuleLibrary()) {
+ final VirtualFile[] roots = myPanel.getRootModel().getContentRoots();
+ if (roots.length > 0) {
+ return roots[0];
+ }
+ final VirtualFile moduleFile = myPanel.getRootModel().getModule().getModuleFile();
+ if (moduleFile != null) {
+ return moduleFile.getParent();
+ }
+ }
+ return super.getBaseDir();
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/classpath/ChooseAndAddAction.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/classpath/ChooseAndAddAction.java
new file mode 100644
index 0000000..6e5eefb
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/classpath/ChooseAndAddAction.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2000-2010 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.classpath;
+
+import org.jetbrains.annotations.Nullable;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author nik
+ */
+public abstract class ChooseAndAddAction<ItemType> extends ClasspathPanelAction {
+ protected ChooseAndAddAction(ClasspathPanel classpathPanel) {
+ super(classpathPanel);
+ }
+
+ @Override
+ public void run() {
+ final ClasspathElementChooser<ItemType> dialog = createChooser();
+ if (dialog == null) {
+ return;
+ }
+ final List<ItemType> chosen = dialog.chooseElements();
+ if (chosen.isEmpty()) {
+ return;
+ }
+ List<ClasspathTableItem<?>> toAdd = new ArrayList<ClasspathTableItem<?>>();
+ for (ItemType item : chosen) {
+ final ClasspathTableItem<?> tableItem = createTableItem(item);
+ if (tableItem != null) {
+ toAdd.add(tableItem);
+ }
+ }
+ myClasspathPanel.addItems(toAdd);
+ }
+
+ @Nullable
+ protected abstract ClasspathTableItem<?> createTableItem(final ItemType item);
+
+ @Nullable
+ protected abstract ClasspathElementChooser<ItemType> createChooser();
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/classpath/ClasspathElementChooser.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/classpath/ClasspathElementChooser.java
new file mode 100644
index 0000000..1ac8aca
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/classpath/ClasspathElementChooser.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2000-2010 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.classpath;
+
+import org.jetbrains.annotations.NotNull;
+
+import java.util.List;
+
+/**
+* @author nik
+*/
+interface ClasspathElementChooser<T> {
+ @NotNull
+ List<T> chooseElements();
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/classpath/ClasspathPanel.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/classpath/ClasspathPanel.java
new file mode 100644
index 0000000..a195e91
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/classpath/ClasspathPanel.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2000-2010 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.classpath;
+
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.roots.ModifiableRootModel;
+import com.intellij.openapi.roots.OrderEntry;
+import com.intellij.openapi.roots.ui.configuration.LibraryTableModifiableModelProvider;
+import com.intellij.openapi.roots.ui.configuration.ModuleConfigurationState;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import java.util.List;
+
+/**
+ * @author nik
+ */
+public interface ClasspathPanel {
+ void runClasspathPanelAction(Runnable action);
+
+ void addItems(List<ClasspathTableItem<?>> toAdd);
+
+ ModifiableRootModel getRootModel();
+
+ Project getProject();
+
+ JComponent getComponent();
+
+ ModuleConfigurationState getModuleConfigurationState();
+
+ void navigate(boolean openLibraryEditor);
+
+ @Nullable
+ OrderEntry getSelectedEntry();
+
+ @NotNull
+ LibraryTableModifiableModelProvider getModifiableModelProvider(@NotNull String tableLevel);
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/classpath/ClasspathPanelAction.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/classpath/ClasspathPanelAction.java
new file mode 100644
index 0000000..3392075
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/classpath/ClasspathPanelAction.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2000-2010 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.classpath;
+
+import org.jetbrains.annotations.Nullable;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+
+/**
+ * @author nik
+ */
+abstract class ClasspathPanelAction implements ActionListener, Runnable {
+ protected final ClasspathPanel myClasspathPanel;
+
+ protected ClasspathPanelAction(ClasspathPanel classpathPanel) {
+ myClasspathPanel = classpathPanel;
+ }
+
+ @Override
+ public void actionPerformed(@Nullable ActionEvent e) {
+ execute();
+ }
+
+ public final void execute() {
+ myClasspathPanel.runClasspathPanelAction(this);
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/classpath/ClasspathPanelImpl.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/classpath/ClasspathPanelImpl.java
new file mode 100644
index 0000000..5d1ea06
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/classpath/ClasspathPanelImpl.java
@@ -0,0 +1,790 @@
+/*
+ * Copyright 2000-2011 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.classpath;
+
+import com.intellij.CommonBundle;
+import com.intellij.analysis.AnalysisScope;
+import com.intellij.find.FindBundle;
+import com.intellij.openapi.actionSystem.*;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.module.impl.scopes.LibraryScope;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.project.ProjectBundle;
+import com.intellij.openapi.projectRoots.Sdk;
+import com.intellij.openapi.roots.*;
+import com.intellij.openapi.roots.impl.libraries.LibraryTableImplUtil;
+import com.intellij.openapi.roots.libraries.Library;
+import com.intellij.openapi.roots.libraries.LibraryTable;
+import com.intellij.openapi.roots.libraries.LibraryTablePresentation;
+import com.intellij.openapi.roots.libraries.LibraryTablesRegistrar;
+import com.intellij.openapi.roots.ui.CellAppearanceEx;
+import com.intellij.openapi.roots.ui.OrderEntryAppearanceService;
+import com.intellij.openapi.roots.ui.configuration.LibraryTableModifiableModelProvider;
+import com.intellij.openapi.roots.ui.configuration.ModuleConfigurationState;
+import com.intellij.openapi.roots.ui.configuration.ProjectStructureConfigurable;
+import com.intellij.openapi.roots.ui.configuration.dependencyAnalysis.AnalyzeDependenciesDialog;
+import com.intellij.openapi.roots.ui.configuration.libraries.LibraryEditingUtil;
+import com.intellij.openapi.roots.ui.configuration.libraryEditor.EditExistingLibraryDialog;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.FindUsagesInProjectStructureActionBase;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.ModuleStructureConfigurable;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.StructureConfigurableContext;
+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.ProjectStructureElement;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.daemon.SdkProjectStructureElement;
+import com.intellij.openapi.ui.ComboBoxTableRenderer;
+import com.intellij.openapi.ui.DialogWrapper;
+import com.intellij.openapi.ui.Messages;
+import com.intellij.openapi.ui.popup.JBPopup;
+import com.intellij.openapi.ui.popup.JBPopupFactory;
+import com.intellij.openapi.ui.popup.PopupStep;
+import com.intellij.openapi.ui.popup.util.BaseListPopupStep;
+import com.intellij.openapi.wm.IdeFocusManager;
+import com.intellij.openapi.wm.ToolWindowId;
+import com.intellij.packageDependencies.DependenciesBuilder;
+import com.intellij.packageDependencies.actions.AnalyzeDependenciesOnSpecifiedTargetHandler;
+import com.intellij.psi.PsiFile;
+import com.intellij.psi.search.GlobalSearchScope;
+import com.intellij.ui.*;
+import com.intellij.ui.awt.RelativePoint;
+import com.intellij.ui.table.JBTable;
+import com.intellij.util.EventDispatcher;
+import com.intellij.util.IconUtil;
+import com.intellij.util.ui.UIUtil;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import javax.swing.border.Border;
+import javax.swing.border.LineBorder;
+import javax.swing.event.ListSelectionEvent;
+import javax.swing.event.ListSelectionListener;
+import javax.swing.table.TableCellRenderer;
+import javax.swing.table.TableColumn;
+import java.awt.*;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.KeyEvent;
+import java.awt.event.MouseEvent;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+public class ClasspathPanelImpl extends JPanel implements ClasspathPanel {
+ private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.roots.ui.configuration.classpath.ClasspathPanelImpl");
+ private final JBTable myEntryTable;
+ private final ClasspathTableModel myModel;
+ private final EventDispatcher<OrderPanelListener> myListeners = EventDispatcher.create(OrderPanelListener.class);
+ private List<AddItemPopupAction<?>> myPopupActions = null;
+ private AnActionButton myEditButton;
+ private final ModuleConfigurationState myState;
+ private AnActionButton myRemoveButton;
+
+ public ClasspathPanelImpl(ModuleConfigurationState state) {
+ super(new BorderLayout());
+
+ myState = state;
+ myModel = new ClasspathTableModel(state, getStructureConfigurableContext());
+ myEntryTable = new JBTable(myModel);
+ myEntryTable.setShowGrid(false);
+ myEntryTable.setDragEnabled(false);
+ myEntryTable.setIntercellSpacing(new Dimension(0, 0));
+
+ myEntryTable.setDefaultRenderer(ClasspathTableItem.class, new TableItemRenderer(getStructureConfigurableContext()));
+ myEntryTable.setDefaultRenderer(Boolean.class, new ExportFlagRenderer(myEntryTable.getDefaultRenderer(Boolean.class)));
+
+ JComboBox scopeEditor = new JComboBox(new EnumComboBoxModel<DependencyScope>(DependencyScope.class));
+ myEntryTable.setDefaultEditor(DependencyScope.class, new DefaultCellEditor(scopeEditor));
+ myEntryTable.setDefaultRenderer(DependencyScope.class, new ComboBoxTableRenderer<DependencyScope>(DependencyScope.values()) {
+ @Override
+ protected String getTextFor(@NotNull final DependencyScope value) {
+ return value.getDisplayName();
+ }
+ });
+
+ myEntryTable.getSelectionModel().setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
+
+ new SpeedSearchBase<JBTable>(myEntryTable) {
+ @Override
+ public int getSelectedIndex() {
+ return myEntryTable.getSelectedRow();
+ }
+
+ @Override
+ protected int convertIndexToModel(int viewIndex) {
+ return myEntryTable.convertRowIndexToModel(viewIndex);
+ }
+
+ @Override
+ public Object[] getAllElements() {
+ final int count = myModel.getRowCount();
+ Object[] elements = new Object[count];
+ for (int idx = 0; idx < count; idx++) {
+ elements[idx] = myModel.getItemAt(idx);
+ }
+ return elements;
+ }
+
+ @Override
+ public String getElementText(Object element) {
+ return getCellAppearance((ClasspathTableItem<?>)element, getStructureConfigurableContext(), false).getText();
+ }
+
+ @Override
+ public void selectElement(Object element, String selectedText) {
+ final int count = myModel.getRowCount();
+ for (int row = 0; row < count; row++) {
+ if (element.equals(myModel.getItemAt(row))) {
+ final int viewRow = myEntryTable.convertRowIndexToView(row);
+ myEntryTable.getSelectionModel().setSelectionInterval(viewRow, viewRow);
+ TableUtil.scrollSelectionToVisible(myEntryTable);
+ break;
+ }
+ }
+ }
+ };
+ setFixedColumnWidth(ClasspathTableModel.EXPORT_COLUMN, ClasspathTableModel.EXPORT_COLUMN_NAME);
+ setFixedColumnWidth(ClasspathTableModel.SCOPE_COLUMN, DependencyScope.COMPILE.toString() + " "); // leave space for combobox border
+
+ myEntryTable.registerKeyboardAction(
+ new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ final int[] selectedRows = myEntryTable.getSelectedRows();
+ boolean currentlyMarked = true;
+ for (final int selectedRow : selectedRows) {
+ final ClasspathTableItem<?> item = myModel.getItemAt(myEntryTable.convertRowIndexToModel(selectedRow));
+ if (selectedRow < 0 || !item.isExportable()) {
+ return;
+ }
+ currentlyMarked &= item.isExported();
+ }
+ for (final int selectedRow : selectedRows) {
+ myModel.getItemAt(myEntryTable.convertRowIndexToModel(selectedRow)).setExported(!currentlyMarked);
+ }
+ myModel.fireTableDataChanged();
+ TableUtil.selectRows(myEntryTable, selectedRows);
+ }
+ },
+ KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, 0),
+ WHEN_FOCUSED
+ );
+
+ myEditButton = new AnActionButton(ProjectBundle.message("module.classpath.button.edit"), null, IconUtil.getEditIcon()) {
+ @Override
+ public void actionPerformed(AnActionEvent e) {
+ doEdit();
+ }
+
+ @Override
+ public boolean isDumbAware() {
+ return true;
+ }
+ };
+ add(createTableWithButtons(), BorderLayout.CENTER);
+
+ if (myEntryTable.getRowCount() > 0) {
+ myEntryTable.getSelectionModel().setSelectionInterval(0,0);
+ }
+
+ new DoubleClickListener() {
+ @Override
+ protected boolean onDoubleClick(MouseEvent e) {
+ navigate(true);
+ return true;
+ }
+ }.installOn(myEntryTable);
+
+ DefaultActionGroup actionGroup = new DefaultActionGroup();
+ final AnAction navigateAction = new AnAction(ProjectBundle.message("classpath.panel.navigate.action.text")) {
+ @Override
+ public void actionPerformed(AnActionEvent e) {
+ navigate(false);
+ }
+
+ @Override
+ public void update(AnActionEvent e) {
+ final Presentation presentation = e.getPresentation();
+ presentation.setEnabled(false);
+ final OrderEntry entry = getSelectedEntry();
+ if (entry != null && entry.isValid()){
+ if (!(entry instanceof ModuleSourceOrderEntry)){
+ presentation.setEnabled(true);
+ }
+ }
+ }
+ };
+ navigateAction.registerCustomShortcutSet(ActionManager.getInstance().getAction(IdeActions.ACTION_EDIT_SOURCE).getShortcutSet(),
+ myEntryTable);
+ actionGroup.add(myEditButton);
+ actionGroup.add(myRemoveButton);
+ actionGroup.add(navigateAction);
+ actionGroup.add(new MyFindUsagesAction());
+ actionGroup.add(new AnalyzeDependencyAction());
+ addChangeLibraryLevelAction(actionGroup, LibraryTablesRegistrar.PROJECT_LEVEL);
+ addChangeLibraryLevelAction(actionGroup, LibraryTablesRegistrar.APPLICATION_LEVEL);
+ addChangeLibraryLevelAction(actionGroup, LibraryTableImplUtil.MODULE_LEVEL);
+ PopupHandler.installPopupHandler(myEntryTable, actionGroup, ActionPlaces.UNKNOWN, ActionManager.getInstance());
+ }
+
+ private void addChangeLibraryLevelAction(DefaultActionGroup actionGroup, String tableLevel) {
+ final LibraryTablePresentation presentation = LibraryEditingUtil.getLibraryTablePresentation(getProject(), tableLevel);
+ actionGroup.add(new ChangeLibraryLevelInClasspathAction(this, presentation.getDisplayName(true), tableLevel));
+ }
+
+ @Override
+ @Nullable
+ public OrderEntry getSelectedEntry() {
+ if (myEntryTable.getSelectedRowCount() != 1) return null;
+ return myModel.getItemAt(myEntryTable.getSelectedRow()).getEntry();
+ }
+
+ private void setFixedColumnWidth(final int columnIndex, final String textToMeasure) {
+ final TableColumn column = myEntryTable.getTableHeader().getColumnModel().getColumn(columnIndex);
+ column.setResizable(false);
+ column.setMaxWidth(column.getPreferredWidth());
+ }
+
+ @Override
+ public void navigate(boolean openLibraryEditor) {
+ final OrderEntry entry = getSelectedEntry();
+ final ProjectStructureConfigurable rootConfigurable = ProjectStructureConfigurable.getInstance(myState.getProject());
+ if (entry instanceof ModuleOrderEntry){
+ Module module = ((ModuleOrderEntry)entry).getModule();
+ if (module != null) {
+ rootConfigurable.select(module.getName(), null, true);
+ }
+ }
+ else if (entry instanceof LibraryOrderEntry){
+ if (!openLibraryEditor) {
+ rootConfigurable.select((LibraryOrderEntry)entry, true);
+ }
+ else {
+ myEditButton.actionPerformed(null);
+ }
+ }
+ else if (entry instanceof JdkOrderEntry) {
+ Sdk jdk = ((JdkOrderEntry)entry).getJdk();
+ if (jdk != null) {
+ rootConfigurable.select(jdk, true);
+ }
+ }
+ }
+
+
+ private JComponent createTableWithButtons() {
+ final boolean isAnalyzeShown = false;
+
+ final ClasspathPanelAction removeAction = new ClasspathPanelAction(this) {
+ @Override
+ public void run() {
+ removeSelectedItems(TableUtil.removeSelectedItems(myEntryTable));
+ }
+ };
+
+ final AnActionButton analyzeButton = new AnActionButton(ProjectBundle.message("classpath.panel.analyze"), null, IconUtil.getAnalyzeIcon()) {
+ @Override
+ public void actionPerformed(AnActionEvent e) {
+ AnalyzeDependenciesDialog.show(getRootModel().getModule());
+ }
+ };
+
+ //addButton.setShortcut(CustomShortcutSet.fromString("alt A", "INSERT"));
+ //removeButton.setShortcut(CustomShortcutSet.fromString("alt DELETE"));
+ //upButton.setShortcut(CustomShortcutSet.fromString("alt UP"));
+ //downButton.setShortcut(CustomShortcutSet.fromString("alt DOWN"));
+ myEntryTable.setBorder(new LineBorder(UIUtil.getBorderColor()));
+
+ // we need to register our listener before ToolbarDecorator registers its own. Otherwise
+ myEntryTable.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
+ @Override
+ public void valueChanged(ListSelectionEvent e) {
+ if (e.getValueIsAdjusting()) {
+ return;
+ }
+ updateButtons();
+ }
+ });
+
+ final ToolbarDecorator decorator = ToolbarDecorator.createDecorator(myEntryTable);
+ decorator.setAddAction(new AnActionButtonRunnable() {
+ @Override
+ public void run(AnActionButton button) {
+ initPopupActions();
+ final JBPopup popup = JBPopupFactory.getInstance().createListPopup(
+ new BaseListPopupStep<AddItemPopupAction<?>>(null, myPopupActions) {
+ @Override
+ public Icon getIconFor(AddItemPopupAction<?> aValue) {
+ return aValue.getIcon();
+ }
+
+ @Override
+ public boolean hasSubstep(AddItemPopupAction<?> selectedValue) {
+ return selectedValue.hasSubStep();
+ }
+
+ @Override
+ public boolean isMnemonicsNavigationEnabled() {
+ return true;
+ }
+
+ @Override
+ public PopupStep onChosen(final AddItemPopupAction<?> selectedValue, final boolean finalChoice) {
+ if (selectedValue.hasSubStep()) {
+ return selectedValue.createSubStep();
+ }
+ return doFinalStep(new Runnable() {
+ @Override
+ public void run() {
+ selectedValue.execute();
+ }
+ });
+ }
+
+ @Override
+ @NotNull
+ public String getTextFor(AddItemPopupAction<?> value) {
+ return "&" + value.getIndex() + " " + value.getTitle();
+ }
+ });
+ popup.show(button.getPreferredPopupPoint());
+ }
+ })
+ .setRemoveAction(new AnActionButtonRunnable() {
+ @Override
+ public void run(AnActionButton button) {
+ removeAction.actionPerformed(null);
+ }
+ })
+ .setMoveUpAction(new AnActionButtonRunnable() {
+ @Override
+ public void run(AnActionButton button) {
+ moveSelectedRows(-1);
+ }
+ })
+ .setMoveDownAction(new AnActionButtonRunnable() {
+ @Override
+ public void run(AnActionButton button) {
+ moveSelectedRows(+1);
+ }
+ })
+ .addExtraAction(myEditButton);
+ if (isAnalyzeShown) {
+ decorator.addExtraAction(analyzeButton);
+ }
+
+ final JPanel panel = decorator.createPanel();
+ myRemoveButton = ToolbarDecorator.findRemoveButton(panel);
+ return panel;
+ }
+
+ private void doEdit() {
+ final OrderEntry entry = getSelectedEntry();
+ if (!(entry instanceof LibraryOrderEntry)) return;
+
+ final Library library = ((LibraryOrderEntry)entry).getLibrary();
+ if (library == null) {
+ return;
+ }
+ final LibraryTable table = library.getTable();
+ final String tableLevel = table != null ? table.getTableLevel() : LibraryTableImplUtil.MODULE_LEVEL;
+ final LibraryTablePresentation presentation = LibraryEditingUtil.getLibraryTablePresentation(getProject(), tableLevel);
+ final LibraryTableModifiableModelProvider provider = getModifiableModelProvider(tableLevel);
+ EditExistingLibraryDialog dialog = EditExistingLibraryDialog.createDialog(this, provider, library, myState.getProject(),
+ presentation, getStructureConfigurableContext());
+ dialog.setContextModule(getRootModel().getModule());
+ dialog.show();
+ myEntryTable.repaint();
+ ModuleStructureConfigurable.getInstance(myState.getProject()).getTree().repaint();
+ }
+
+ @Override
+ public void addNotify() {
+ super.addNotify();
+ updateButtons();
+ }
+
+ private void updateButtons() {
+ final int[] selectedRows = myEntryTable.getSelectedRows();
+ boolean removeButtonEnabled = true;
+ int minRow = myEntryTable.getRowCount() + 1;
+ int maxRow = -1;
+ for (final int selectedRow : selectedRows) {
+ minRow = Math.min(minRow, selectedRow);
+ maxRow = Math.max(maxRow, selectedRow);
+ final ClasspathTableItem<?> item = myModel.getItemAt(selectedRow);
+ if (!item.isRemovable()) {
+ removeButtonEnabled = false;
+ }
+ }
+ if (myRemoveButton != null) {
+ myRemoveButton.setEnabled(removeButtonEnabled && selectedRows.length > 0);
+ }
+ ClasspathTableItem<?> selectedItem = selectedRows.length == 1 ? myModel.getItemAt(selectedRows[0]) : null;
+ myEditButton.setEnabled(selectedItem != null && selectedItem.isEditable());
+ }
+
+ private void removeSelectedItems(final List removedRows) {
+ if (removedRows.isEmpty()) {
+ return;
+ }
+ for (final Object removedRow : removedRows) {
+ final ClasspathTableItem<?> item = (ClasspathTableItem<?>)((Object[])removedRow)[ClasspathTableModel.ITEM_COLUMN];
+ final OrderEntry orderEntry = item.getEntry();
+ if (orderEntry == null) {
+ continue;
+ }
+
+ getRootModel().removeOrderEntry(orderEntry);
+ }
+ final int[] selectedRows = myEntryTable.getSelectedRows();
+ myModel.fireTableDataChanged();
+ TableUtil.selectRows(myEntryTable, selectedRows);
+ final StructureConfigurableContext context = ModuleStructureConfigurable.getInstance(myState.getProject()).getContext();
+ context.getDaemonAnalyzer().queueUpdate(new ModuleProjectStructureElement(context, getRootModel().getModule()));
+ }
+
+ @Override
+ @NotNull
+ public LibraryTableModifiableModelProvider getModifiableModelProvider(@NotNull String tableLevel) {
+ if (LibraryTableImplUtil.MODULE_LEVEL.equals(tableLevel)) {
+ final LibraryTable moduleLibraryTable = getRootModel().getModuleLibraryTable();
+ return new LibraryTableModifiableModelProvider() {
+ @Override
+ public LibraryTable.ModifiableModel getModifiableModel() {
+ return moduleLibraryTable.getModifiableModel();
+ }
+ };
+ }
+ else {
+ return getStructureConfigurableContext().createModifiableModelProvider(tableLevel);
+ }
+ }
+
+ @Override
+ public void runClasspathPanelAction(Runnable action) {
+ try {
+ disableModelUpdate();
+ action.run();
+ }
+ finally {
+ enableModelUpdate();
+ myEntryTable.requestFocus();
+ }
+ }
+
+ @Override
+ public void addItems(List<ClasspathTableItem<?>> toAdd) {
+ for (ClasspathTableItem<?> item : toAdd) {
+ myModel.addItem(item);
+ }
+ myModel.fireTableDataChanged();
+ final ListSelectionModel selectionModel = myEntryTable.getSelectionModel();
+ selectionModel.setSelectionInterval(myModel.getRowCount() - toAdd.size(), myModel.getRowCount() - 1);
+ TableUtil.scrollSelectionToVisible(myEntryTable);
+
+ final StructureConfigurableContext context = ModuleStructureConfigurable.getInstance(myState.getProject()).getContext();
+ context.getDaemonAnalyzer().queueUpdate(new ModuleProjectStructureElement(context, getRootModel().getModule()));
+ }
+
+ @Override
+ public ModifiableRootModel getRootModel() {
+ return myState.getRootModel();
+ }
+
+ @Override
+ public Project getProject() {
+ return myState.getProject();
+ }
+
+ @Override
+ public ModuleConfigurationState getModuleConfigurationState() {
+ return myState;
+ }
+
+ @Override
+ public JComponent getComponent() {
+ return this;
+ }
+
+ public void rootsChanged() {
+ forceInitFromModel();
+ }
+
+ private void initPopupActions() {
+ if (myPopupActions == null) {
+ int actionIndex = 1;
+ final List<AddItemPopupAction<?>> actions = new ArrayList<AddItemPopupAction<?>>();
+ final StructureConfigurableContext context = getStructureConfigurableContext();
+ actions.add(new AddNewModuleLibraryAction(this, actionIndex++, context));
+ actions.add(new AddLibraryDependencyAction(this, actionIndex++, ProjectBundle.message("classpath.add.library.action"), context));
+ actions.add(new AddModuleDependencyAction(this, actionIndex, context)
+ );
+
+ myPopupActions = actions;
+ }
+ }
+
+ private StructureConfigurableContext getStructureConfigurableContext() {
+ return ProjectStructureConfigurable.getInstance(myState.getProject()).getContext();
+ }
+
+
+ private void enableModelUpdate() {
+ myInsideChange--;
+ }
+
+ private void disableModelUpdate() {
+ myInsideChange++;
+ }
+
+ public void addListener(OrderPanelListener listener) {
+ myListeners.addListener(listener);
+ }
+
+ public void removeListener(OrderPanelListener listener) {
+ myListeners.removeListener(listener);
+ }
+
+ private void moveSelectedRows(int increment) {
+ if (increment == 0) {
+ return;
+ }
+ if (myEntryTable.isEditing()){
+ myEntryTable.getCellEditor().stopCellEditing();
+ }
+ final ListSelectionModel selectionModel = myEntryTable.getSelectionModel();
+ for(int row = increment < 0? 0 : myModel.getRowCount() - 1; increment < 0? row < myModel.getRowCount() : row >= 0; row +=
+ increment < 0? +1 : -1){
+ if (selectionModel.isSelectedIndex(row)) {
+ final int newRow = moveRow(row, increment);
+ selectionModel.removeSelectionInterval(row, row);
+ selectionModel.addSelectionInterval(newRow, newRow);
+ }
+ }
+ myModel.fireTableRowsUpdated(0, myModel.getRowCount() - 1);
+ Rectangle cellRect = myEntryTable.getCellRect(selectionModel.getMinSelectionIndex(), 0, true);
+ if (cellRect != null) {
+ myEntryTable.scrollRectToVisible(cellRect);
+ }
+ myEntryTable.repaint();
+ myListeners.getMulticaster().entryMoved();
+ }
+
+ public void selectOrderEntry(@NotNull OrderEntry entry) {
+ for (int row = 0; row < myModel.getRowCount(); row++) {
+ final OrderEntry orderEntry = myModel.getItemAt(row).getEntry();
+ if (orderEntry != null && entry.getPresentableName().equals(orderEntry.getPresentableName())) {
+ myEntryTable.getSelectionModel().setSelectionInterval(row, row);
+ TableUtil.scrollSelectionToVisible(myEntryTable);
+ }
+ }
+ IdeFocusManager.getInstance(myState.getProject()).requestFocus(myEntryTable, true);
+ }
+
+ private int moveRow(final int row, final int increment) {
+ int newIndex = Math.abs(row + increment) % myModel.getRowCount();
+ final ClasspathTableItem<?> item = myModel.removeDataRow(row);
+ myModel.addItemAt(item, newIndex);
+ return newIndex;
+ }
+
+ public void stopEditing() {
+ TableUtil.stopEditing(myEntryTable);
+ }
+
+ public List<OrderEntry> getEntries() {
+ final int count = myModel.getRowCount();
+ final List<OrderEntry> entries = new ArrayList<OrderEntry>(count);
+ for (int row = 0; row < count; row++) {
+ final OrderEntry entry = myModel.getItemAt(row).getEntry();
+ if (entry != null) {
+ entries.add(entry);
+ }
+ }
+ return entries;
+ }
+
+ private int myInsideChange = 0;
+ public void initFromModel() {
+ if (myInsideChange == 0) {
+ forceInitFromModel();
+ }
+ }
+
+ public void forceInitFromModel() {
+ final int[] selection = myEntryTable.getSelectedRows();
+ myModel.clear();
+ myModel.init();
+ myModel.fireTableDataChanged();
+ TableUtil.selectRows(myEntryTable, selection);
+ }
+
+ private static CellAppearanceEx getCellAppearance(final ClasspathTableItem<?> item,
+ final StructureConfigurableContext context,
+ final boolean selected) {
+ final OrderEntryAppearanceService service = OrderEntryAppearanceService.getInstance();
+ if (item instanceof InvalidJdkItem) {
+ return service.forJdk(null, false, selected, true);
+ }
+ else {
+ final OrderEntry entry = item.getEntry();
+ assert entry != null : item;
+ return service.forOrderEntry(context.getProject(), entry, selected);
+ }
+ }
+
+ private static class TableItemRenderer extends ColoredTableCellRenderer {
+ private final Border NO_FOCUS_BORDER = BorderFactory.createEmptyBorder(1, 1, 1, 1);
+ private StructureConfigurableContext myContext;
+
+ public TableItemRenderer(StructureConfigurableContext context) {
+ myContext = context;
+ }
+
+ @Override
+ protected void customizeCellRenderer(JTable table, Object value, boolean selected, boolean hasFocus, int row, int column) {
+ setPaintFocusBorder(false);
+ setFocusBorderAroundIcon(true);
+ setBorder(NO_FOCUS_BORDER);
+ if (value instanceof ClasspathTableItem<?>) {
+ final ClasspathTableItem<?> tableItem = (ClasspathTableItem<?>)value;
+ getCellAppearance(tableItem, myContext, selected).customize(this);
+ setToolTipText(tableItem.getTooltipText());
+ }
+ }
+ }
+
+ private static class ExportFlagRenderer implements TableCellRenderer {
+ private final TableCellRenderer myDelegate;
+ private final JPanel myBlankPanel;
+
+ public ExportFlagRenderer(TableCellRenderer delegate) {
+ myDelegate = delegate;
+ myBlankPanel = new JPanel();
+ }
+
+ @Override
+ public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
+ if (!table.isCellEditable(row, column)) {
+ myBlankPanel.setBackground(isSelected ? table.getSelectionBackground() : table.getBackground());
+ return myBlankPanel;
+ }
+ return myDelegate.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
+ }
+ }
+
+ private class MyFindUsagesAction extends FindUsagesInProjectStructureActionBase {
+ private MyFindUsagesAction() {
+ super(myEntryTable, myState.getProject());
+ }
+
+ @Override
+ protected boolean isEnabled() {
+ return getSelectedElement() != null;
+ }
+
+ @Override
+ protected ProjectStructureElement getSelectedElement() {
+ final OrderEntry entry = getSelectedEntry();
+ if (entry instanceof LibraryOrderEntry) {
+ final Library library = ((LibraryOrderEntry)entry).getLibrary();
+ if (library != null) {
+ return new LibraryProjectStructureElement(getContext(), library);
+ }
+ }
+ else if (entry instanceof ModuleOrderEntry) {
+ final Module module = ((ModuleOrderEntry)entry).getModule();
+ if (module != null) {
+ return new ModuleProjectStructureElement(getContext(), module);
+ }
+ }
+ else if (entry instanceof JdkOrderEntry) {
+ final Sdk jdk = ((JdkOrderEntry)entry).getJdk();
+ if (jdk != null) {
+ return new SdkProjectStructureElement(getContext(), jdk);
+ }
+ }
+ return null;
+ }
+
+ @Override
+ protected RelativePoint getPointToShowResults() {
+ Rectangle rect = myEntryTable.getCellRect(myEntryTable.getSelectedRow(), 1, false);
+ Point location = rect.getLocation();
+ location.y += rect.height;
+ return new RelativePoint(myEntryTable, location);
+ }
+ }
+
+ private class AnalyzeDependencyAction extends AnAction {
+ private AnalyzeDependencyAction() {
+ super("Analyze This Dependency");
+ }
+
+ @Override
+ public void actionPerformed(AnActionEvent e) {
+ final OrderEntry selectedEntry = getSelectedEntry();
+ GlobalSearchScope targetScope;
+ if (selectedEntry instanceof ModuleOrderEntry) {
+ final Module module = ((ModuleOrderEntry)selectedEntry).getModule();
+ LOG.assertTrue(module != null);
+ targetScope = GlobalSearchScope.moduleScope(module);
+ }
+ else {
+ Library library = ((LibraryOrderEntry)selectedEntry).getLibrary();
+ LOG.assertTrue(library != null);
+ targetScope = new LibraryScope(getProject(), library);
+ }
+ new AnalyzeDependenciesOnSpecifiedTargetHandler(getProject(), new AnalysisScope(myState.getRootModel().getModule()),
+ targetScope) {
+ @Override
+ protected boolean canStartInBackground() {
+ return false;
+ }
+
+ @Override
+ protected boolean shouldShowDependenciesPanel(List<DependenciesBuilder> builders) {
+ for (DependenciesBuilder builder : builders) {
+ for (Set<PsiFile> files : builder.getDependencies().values()) {
+ if (!files.isEmpty()) {
+ Messages.showInfoMessage(myEntryTable,
+ "Dependencies were successfully collected in \"" +
+ ToolWindowId.DEPENDENCIES + "\" toolwindow",
+ FindBundle.message("find.pointcut.applications.not.found.title"));
+ return true;
+ }
+ }
+ }
+ if (Messages.showOkCancelDialog(myEntryTable,
+ "No code dependencies were found. Would you like to remove the dependency?",
+ CommonBundle.getWarningTitle(), Messages.getWarningIcon()) == DialogWrapper.OK_EXIT_CODE) {
+ removeSelectedItems(TableUtil.removeSelectedItems(myEntryTable));
+ }
+ return false;
+ }
+ }.analyze();
+ }
+
+ @Override
+ public void update(AnActionEvent e) {
+ final OrderEntry entry = getSelectedEntry();
+ e.getPresentation().setVisible(entry instanceof ModuleOrderEntry && ((ModuleOrderEntry)entry).getModule() != null
+ || entry instanceof LibraryOrderEntry && ((LibraryOrderEntry)entry).getLibrary() != null);
+ }
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/classpath/ClasspathTableItem.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/classpath/ClasspathTableItem.java
new file mode 100644
index 0000000..f11e229
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/classpath/ClasspathTableItem.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright 2000-2010 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.classpath;
+
+import com.intellij.openapi.roots.*;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.StructureConfigurableContext;
+import org.jetbrains.annotations.Nullable;
+
+/**
+* @author nik
+*/
+class ClasspathTableItem<T extends OrderEntry> {
+ @Nullable protected final T myEntry;
+ private final boolean myRemovable;
+
+ @Nullable
+ public static ClasspathTableItem<?> createItem(OrderEntry orderEntry, StructureConfigurableContext context) {
+ if (orderEntry instanceof JdkOrderEntry) {
+ return new ClasspathTableItem<OrderEntry>(orderEntry, false);
+ }
+ else if (orderEntry instanceof LibraryOrderEntry) {
+ return createLibItem((LibraryOrderEntry)orderEntry, context);
+ }
+ else if (orderEntry instanceof ModuleOrderEntry) {
+ return new ClasspathTableItem<OrderEntry>(orderEntry, true);
+ }
+ else if (orderEntry instanceof ModuleSourceOrderEntry) {
+ return new ClasspathTableItem<OrderEntry>(orderEntry, false);
+ }
+ return null;
+ }
+
+ public static ClasspathTableItem<LibraryOrderEntry> createLibItem(final LibraryOrderEntry orderEntry, final StructureConfigurableContext context) {
+ return new LibraryItem(orderEntry, context);
+ }
+
+ protected ClasspathTableItem(@Nullable T entry, boolean removable) {
+ myEntry = entry;
+ myRemovable = removable;
+ }
+
+ public final boolean isExportable() {
+ return myEntry instanceof ExportableOrderEntry;
+ }
+
+ public final boolean isExported() {
+ return myEntry instanceof ExportableOrderEntry && ((ExportableOrderEntry)myEntry).isExported();
+ }
+
+ public final void setExported(boolean isExported) {
+ if (myEntry instanceof ExportableOrderEntry) {
+ ((ExportableOrderEntry)myEntry).setExported(isExported);
+ }
+ }
+
+ @Nullable
+ public final DependencyScope getScope() {
+ return myEntry instanceof ExportableOrderEntry ? ((ExportableOrderEntry) myEntry).getScope() : null;
+ }
+
+ public final void setScope(DependencyScope scope) {
+ if (myEntry instanceof ExportableOrderEntry) {
+ ((ExportableOrderEntry) myEntry).setScope(scope);
+ }
+ }
+
+ @Nullable
+ public final T getEntry() {
+ return myEntry;
+ }
+
+ public boolean isRemovable() {
+ return myRemovable;
+ }
+
+ public boolean isEditable() {
+ return false;
+ }
+
+ @Nullable
+ public String getTooltipText() {
+ return null;
+ }
+
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/classpath/ClasspathTableModel.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/classpath/ClasspathTableModel.java
new file mode 100644
index 0000000..8ffb161
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/classpath/ClasspathTableModel.java
@@ -0,0 +1,166 @@
+/*
+ * Copyright 2000-2011 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.classpath;
+
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.project.ProjectBundle;
+import com.intellij.openapi.roots.DependencyScope;
+import com.intellij.openapi.roots.JdkOrderEntry;
+import com.intellij.openapi.roots.ModifiableRootModel;
+import com.intellij.openapi.roots.OrderEntry;
+import com.intellij.openapi.roots.ui.configuration.ModuleConfigurationState;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.StructureConfigurableContext;
+import com.intellij.util.ui.ItemRemovable;
+
+import javax.swing.table.AbstractTableModel;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+* @author nik
+*/
+class ClasspathTableModel extends AbstractTableModel implements ItemRemovable {
+ private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.roots.ui.configuration.classpath.ClasspathTableModel");
+ public static final String EXPORT_COLUMN_NAME = ProjectBundle.message("modules.order.export.export.column");
+ private static final String SCOPE_COLUMN_NAME = ProjectBundle.message("modules.order.export.scope.column");
+ public static final int EXPORT_COLUMN = 0;
+ public static final int ITEM_COLUMN = 1;
+ public static final int SCOPE_COLUMN = 2;
+ private final List<ClasspathTableItem<?>> myItems = new ArrayList<ClasspathTableItem<?>>();
+ private final ModuleConfigurationState myState;
+ private StructureConfigurableContext myContext;
+
+ public ClasspathTableModel(final ModuleConfigurationState state, StructureConfigurableContext context) {
+ myState = state;
+ myContext = context;
+ init();
+ }
+
+ private ModifiableRootModel getModel() {
+ return myState.getRootModel();
+ }
+
+ public void init() {
+ final OrderEntry[] orderEntries = getModel().getOrderEntries();
+ boolean hasJdkOrderEntry = false;
+ for (final OrderEntry orderEntry : orderEntries) {
+ if (orderEntry instanceof JdkOrderEntry) {
+ hasJdkOrderEntry = true;
+ }
+ addItem(ClasspathTableItem.createItem(orderEntry, myContext));
+ }
+ if (!hasJdkOrderEntry) {
+ addItemAt(new InvalidJdkItem(), 0);
+ }
+ }
+
+ public ClasspathTableItem<?> getItemAt(int row) {
+ return myItems.get(row);
+ }
+
+ public void addItem(ClasspathTableItem<?> item) {
+ myItems.add(item);
+ }
+
+ public void addItemAt(ClasspathTableItem<?> item, int row) {
+ myItems.add(row, item);
+ }
+
+ public ClasspathTableItem<?> removeDataRow(int row) {
+ return myItems.remove(row);
+ }
+
+
+ @Override
+ public void removeRow(int row) {
+ removeDataRow(row);
+ }
+
+ public void clear() {
+ myItems.clear();
+ }
+
+ @Override
+ public int getRowCount() {
+ return myItems.size();
+ }
+
+ @Override
+ public Object getValueAt(int rowIndex, int columnIndex) {
+ final ClasspathTableItem<?> item = myItems.get(rowIndex);
+ if (columnIndex == EXPORT_COLUMN) {
+ return item.isExported();
+ }
+ if (columnIndex == SCOPE_COLUMN) {
+ return item.getScope();
+ }
+ if (columnIndex == ITEM_COLUMN) {
+ return item;
+ }
+ LOG.error("Incorrect column index: " + columnIndex);
+ return null;
+ }
+
+ @Override
+ public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
+ final ClasspathTableItem<?> item = myItems.get(rowIndex);
+ if (columnIndex == EXPORT_COLUMN) {
+ item.setExported(((Boolean)aValue).booleanValue());
+ }
+ else if (columnIndex == SCOPE_COLUMN && aValue instanceof DependencyScope) {
+ item.setScope((DependencyScope) aValue);
+ }
+ }
+
+ @Override
+ public String getColumnName(int column) {
+ if (column == EXPORT_COLUMN) {
+ return EXPORT_COLUMN_NAME;
+ }
+ if (column == SCOPE_COLUMN) {
+ return SCOPE_COLUMN_NAME;
+ }
+ return "";
+ }
+
+ @Override
+ public Class getColumnClass(int column) {
+ if (column == EXPORT_COLUMN) {
+ return Boolean.class;
+ }
+ if (column == SCOPE_COLUMN) {
+ return DependencyScope.class;
+ }
+ if (column == ITEM_COLUMN) {
+ return ClasspathTableItem.class;
+ }
+ return super.getColumnClass(column);
+ }
+
+ @Override
+ public int getColumnCount() {
+ return 3;
+ }
+
+ @Override
+ public boolean isCellEditable(int row, int column) {
+ if (column == EXPORT_COLUMN || column == SCOPE_COLUMN) {
+ final ClasspathTableItem<?> item = myItems.get(row);
+ return item != null && item.isExportable();
+ }
+ return false;
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/classpath/CreateModuleLibraryChooser.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/classpath/CreateModuleLibraryChooser.java
new file mode 100644
index 0000000..c8da8d5
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/classpath/CreateModuleLibraryChooser.java
@@ -0,0 +1,212 @@
+/*
+ * Copyright 2000-2012 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.classpath;
+
+import com.intellij.openapi.actionSystem.LangDataKeys;
+import com.intellij.openapi.fileChooser.FileChooser;
+import com.intellij.openapi.fileChooser.FileChooserDescriptor;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.roots.OrderRootType;
+import com.intellij.openapi.roots.impl.libraries.LibraryEx;
+import com.intellij.openapi.roots.impl.libraries.LibraryTableBase;
+import com.intellij.openapi.roots.libraries.*;
+import com.intellij.openapi.roots.libraries.ui.LibraryRootsComponentDescriptor;
+import com.intellij.openapi.roots.libraries.ui.OrderRoot;
+import com.intellij.openapi.roots.libraries.ui.impl.RootDetectionUtil;
+import com.intellij.openapi.roots.ui.configuration.libraries.LibraryEditingUtil;
+import com.intellij.openapi.roots.ui.configuration.libraryEditor.DefaultLibraryRootsComponentDescriptor;
+import com.intellij.openapi.util.Pair;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.util.ArrayUtil;
+import com.intellij.util.Function;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import java.util.*;
+
+/**
+* @author nik
+*/
+public class CreateModuleLibraryChooser implements ClasspathElementChooser<Library> {
+ private final JComponent myParentComponent;
+ private final Module myModule;
+ private final LibraryTable.ModifiableModel myModuleLibrariesModel;
+ @Nullable private final Function<LibraryType, LibraryProperties> myDefaultPropertiesFactory;
+ private final HashMap<LibraryRootsComponentDescriptor,LibraryType> myLibraryTypes;
+ private final DefaultLibraryRootsComponentDescriptor myDefaultDescriptor;
+
+ public CreateModuleLibraryChooser(ClasspathPanel classpathPanel, LibraryTable.ModifiableModel moduleLibraryModel) {
+ this(LibraryEditingUtil.getSuitableTypes(classpathPanel), classpathPanel.getComponent(), classpathPanel.getRootModel().getModule(),
+ moduleLibraryModel, null);
+ }
+
+ public CreateModuleLibraryChooser(List<? extends LibraryType> libraryTypes, JComponent parentComponent,
+ Module module,
+ final LibraryTable.ModifiableModel moduleLibrariesModel,
+ @Nullable final Function<LibraryType, LibraryProperties> defaultPropertiesFactory) {
+ myParentComponent = parentComponent;
+ myModule = module;
+ myModuleLibrariesModel = moduleLibrariesModel;
+ myDefaultPropertiesFactory = defaultPropertiesFactory;
+ myLibraryTypes = new HashMap<LibraryRootsComponentDescriptor, LibraryType>();
+ myDefaultDescriptor = new DefaultLibraryRootsComponentDescriptor();
+ for (LibraryType<?> libraryType : libraryTypes) {
+ LibraryRootsComponentDescriptor descriptor = null;
+ if (libraryType != null) {
+ descriptor = libraryType.createLibraryRootsComponentDescriptor();
+ }
+ if (descriptor == null) {
+ descriptor = myDefaultDescriptor;
+ }
+ if (!myLibraryTypes.containsKey(descriptor)) {
+ myLibraryTypes.put(descriptor, libraryType);
+ }
+ }
+ }
+
+ private Library createLibraryFromRoots(List<OrderRoot> roots, @Nullable final LibraryType libraryType) {
+ final PersistentLibraryKind kind = libraryType == null ? null : libraryType.getKind();
+ final Library library = ((LibraryTableBase.ModifiableModelEx)myModuleLibrariesModel).createLibrary(null, kind);
+ final LibraryEx.ModifiableModelEx libModel = (LibraryEx.ModifiableModelEx)library.getModifiableModel();
+ if (myDefaultPropertiesFactory != null) {
+ libModel.setProperties(myDefaultPropertiesFactory.fun(libraryType));
+ }
+ for (OrderRoot root : roots) {
+ if (root.isJarDirectory()) {
+ libModel.addJarDirectory(root.getFile(), false, root.getType());
+ }
+ else {
+ libModel.addRoot(root.getFile(), root.getType());
+ }
+ }
+ libModel.commit();
+ return library;
+ }
+
+ private List<OrderRoot> filterAlreadyAdded(final List<OrderRoot> roots) {
+ if (roots == null || roots.isEmpty()) {
+ return Collections.emptyList();
+ }
+
+ final List<OrderRoot> result = new ArrayList<OrderRoot>();
+ final Library[] libraries = myModuleLibrariesModel.getLibraries();
+ for (OrderRoot root : roots) {
+ if (!isIncluded(root, libraries)) {
+ result.add(root);
+ }
+ }
+ return result;
+ }
+
+ private static boolean isIncluded(OrderRoot root, Library[] libraries) {
+ for (Library library : libraries) {
+ if (ArrayUtil.contains(root.getFile(), library.getFiles(root.getType()))) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ @NotNull
+ public List<Library> chooseElements() {
+ final FileChooserDescriptor chooserDescriptor;
+ final List<Pair<LibraryRootsComponentDescriptor, FileChooserDescriptor>> descriptors = new ArrayList<Pair<LibraryRootsComponentDescriptor, FileChooserDescriptor>>();
+ for (LibraryRootsComponentDescriptor componentDescriptor : myLibraryTypes.keySet()) {
+ descriptors.add(Pair.create(componentDescriptor, componentDescriptor.createAttachFilesChooserDescriptor(null)));
+ }
+ if (descriptors.size() == 1) {
+ chooserDescriptor = descriptors.get(0).getSecond();
+ }
+ else {
+ chooserDescriptor = new FileChooserDescriptor(true, true, true, false, true, false) {
+ @Override
+ public boolean isFileSelectable(VirtualFile file) {
+ for (Pair<LibraryRootsComponentDescriptor, FileChooserDescriptor> pair : descriptors) {
+ if (pair.getSecond().isFileSelectable(file)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public boolean isFileVisible(VirtualFile file, boolean showHiddenFiles) {
+ for (Pair<LibraryRootsComponentDescriptor, FileChooserDescriptor> pair : descriptors) {
+ if (pair.getSecond().isFileVisible(file, showHiddenFiles)) {
+ return true;
+ }
+ }
+ return false;
+ }
+ };
+ }
+ chooserDescriptor.putUserData(LangDataKeys.MODULE_CONTEXT, myModule);
+
+ final Project project = myModule.getProject();
+ final VirtualFile[] files = FileChooser.chooseFiles(chooserDescriptor, myParentComponent, project, project.getBaseDir());
+ if (files.length == 0) return Collections.emptyList();
+
+ List<LibraryRootsComponentDescriptor> suitableDescriptors = new ArrayList<LibraryRootsComponentDescriptor>();
+ for (Pair<LibraryRootsComponentDescriptor, FileChooserDescriptor> pair : descriptors) {
+ if (acceptAll(pair.getSecond(), files)) {
+ suitableDescriptors.add(pair.getFirst());
+ }
+ }
+
+ final LibraryRootsComponentDescriptor rootsComponentDescriptor;
+ LibraryType libraryType = null;
+ if (suitableDescriptors.size() == 1) {
+ rootsComponentDescriptor = suitableDescriptors.get(0);
+ libraryType = myLibraryTypes.get(rootsComponentDescriptor);
+ }
+ else {
+ rootsComponentDescriptor = myDefaultDescriptor;
+ }
+ List<OrderRoot> chosenRoots = RootDetectionUtil.detectRoots(Arrays.asList(files), myParentComponent, project, rootsComponentDescriptor);
+
+ final List<OrderRoot> roots = filterAlreadyAdded(chosenRoots);
+ if (roots.isEmpty()) {
+ return Collections.emptyList();
+ }
+
+ final List<Library> addedLibraries = new ArrayList<Library>();
+ boolean onlyClasses = true;
+ for (OrderRoot root : roots) {
+ onlyClasses &= root.getType() == OrderRootType.CLASSES;
+ }
+ if (onlyClasses) {
+ for (OrderRoot root : roots) {
+ addedLibraries.add(createLibraryFromRoots(Collections.singletonList(root), libraryType));
+ }
+ }
+ else {
+ addedLibraries.add(createLibraryFromRoots(roots, libraryType));
+ }
+ return addedLibraries;
+ }
+
+ private static boolean acceptAll(FileChooserDescriptor descriptor, VirtualFile[] files) {
+ for (VirtualFile file : files) {
+ if (!descriptor.isFileSelectable(file) || !descriptor.isFileVisible(file, true)) {
+ return false;
+ }
+ }
+ return true;
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/classpath/InvalidJdkItem.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/classpath/InvalidJdkItem.java
new file mode 100644
index 0000000..551a308
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/classpath/InvalidJdkItem.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2000-2010 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.classpath;
+
+import com.intellij.openapi.roots.OrderEntry;
+
+/**
+ * @author nik
+ */
+public class InvalidJdkItem extends ClasspathTableItem<OrderEntry> {
+ public InvalidJdkItem() {
+ super(null, false);
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/classpath/LibraryItem.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/classpath/LibraryItem.java
new file mode 100644
index 0000000..54a43d8
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/classpath/LibraryItem.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2000-2010 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.classpath;
+
+import com.intellij.openapi.project.ProjectBundle;
+import com.intellij.openapi.roots.LibraryOrderEntry;
+import com.intellij.openapi.roots.OrderRootType;
+import com.intellij.openapi.roots.impl.libraries.LibraryEx;
+import com.intellij.openapi.roots.libraries.Library;
+import com.intellij.openapi.roots.ui.configuration.libraries.LibraryPresentationManager;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.StructureConfigurableContext;
+import com.intellij.openapi.util.text.StringUtil;
+
+import java.util.List;
+
+/**
+* @author nik
+*/
+class LibraryItem extends ClasspathTableItem<LibraryOrderEntry> {
+ private final StructureConfigurableContext myContext;
+
+ public LibraryItem(LibraryOrderEntry orderEntry, StructureConfigurableContext context) {
+ super(orderEntry, true);
+ myContext = context;
+ }
+
+ @Override
+ public boolean isEditable() {
+ return myEntry != null && myEntry.isValid();
+ }
+
+ @Override
+ public String getTooltipText() {
+ if (myEntry == null) return null;
+
+ final Library library = myEntry.getLibrary();
+ if (library == null) return null;
+
+ final String name = library.getName();
+ if (name != null) {
+ final List<String> invalidUrls = ((LibraryEx)library).getInvalidRootUrls(OrderRootType.CLASSES);
+ if (!invalidUrls.isEmpty()) {
+ return ProjectBundle.message("project.roots.tooltip.library.has.broken.paths", name, invalidUrls.size());
+ }
+ }
+
+ final List<String> descriptions = LibraryPresentationManager.getInstance().getDescriptions(library, myContext);
+ if (descriptions.isEmpty()) return null;
+
+ return "<html>" + StringUtil.join(descriptions, "<br>") + "</html>";
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/classpath/NewLibraryChooser.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/classpath/NewLibraryChooser.java
new file mode 100644
index 0000000..13edc89
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/classpath/NewLibraryChooser.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2000-2010 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.classpath;
+
+import com.intellij.ide.DataManager;
+import com.intellij.openapi.actionSystem.LangDataKeys;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.roots.ModifiableRootModel;
+import com.intellij.openapi.roots.libraries.*;
+import com.intellij.openapi.roots.ui.configuration.libraryEditor.CreateNewLibraryAction;
+import com.intellij.openapi.roots.ui.configuration.libraryEditor.CreateNewLibraryDialog;
+import com.intellij.openapi.roots.ui.configuration.libraryEditor.NewLibraryEditor;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.StructureConfigurableContext;
+import com.intellij.util.containers.ContainerUtil;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+* @author nik
+*/
+class NewLibraryChooser implements ClasspathElementChooser<Library> {
+ private final ModifiableRootModel myRootModel;
+ private final StructureConfigurableContext myContext;
+ private final JComponent myParentComponent;
+ private final Project myProject;
+ private final LibraryType myLibraryType;
+
+ public NewLibraryChooser(final Project project,
+ final ModifiableRootModel rootModel,
+ LibraryType libraryType, StructureConfigurableContext context, final JComponent parentComponent) {
+ myRootModel = rootModel;
+ myLibraryType = libraryType;
+ myContext = context;
+ myParentComponent = parentComponent;
+ myProject = project;
+ }
+
+ @Override
+ @NotNull
+ public List<Library> chooseElements() {
+ return ContainerUtil.createMaybeSingletonList(createLibrary());
+ }
+
+ @Nullable
+ public Library createLibrary() {
+ final NewLibraryConfiguration configuration = CreateNewLibraryAction.createNewLibraryConfiguration(myLibraryType, myParentComponent, myProject);
+ if (configuration == null) return null;
+
+ final NewLibraryEditor libraryEditor = new NewLibraryEditor(configuration.getLibraryType(), configuration.getProperties());
+ libraryEditor.setName(configuration.getDefaultLibraryName());
+ configuration.addRoots(libraryEditor);
+
+ final LibraryTablesRegistrar registrar = LibraryTablesRegistrar.getInstance();
+ List<LibraryTable> tables = Arrays.asList(myRootModel.getModuleLibraryTable(),
+ registrar.getLibraryTable(myProject),
+ registrar.getLibraryTable());
+
+ CreateNewLibraryDialog dialog = new CreateNewLibraryDialog(myParentComponent, myContext, libraryEditor, tables, 1);
+ final Module contextModule = LangDataKeys.MODULE_CONTEXT.getData(DataManager.getInstance().getDataContext(myParentComponent));
+ dialog.setContextModule(contextModule);
+ dialog.show();
+ if (dialog.isOK()) {
+ return dialog.createLibrary();
+ }
+ return null;
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/classpath/ProjectStructureChooseLibrariesDialog.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/classpath/ProjectStructureChooseLibrariesDialog.java
new file mode 100644
index 0000000..a5bfdd1
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/classpath/ProjectStructureChooseLibrariesDialog.java
@@ -0,0 +1,183 @@
+/*
+ * Copyright 2000-2010 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.classpath;
+
+import com.intellij.ide.projectView.PresentationData;
+import com.intellij.ide.util.treeView.NodeDescriptor;
+import com.intellij.openapi.application.Application;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.roots.impl.libraries.LibraryTableImplUtil;
+import com.intellij.openapi.roots.libraries.Library;
+import com.intellij.openapi.roots.libraries.LibraryTable;
+import com.intellij.openapi.roots.ui.configuration.libraries.LibraryPresentationManager;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.LibrariesModifiableModel;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.StructureConfigurableContext;
+import com.intellij.openapi.util.SystemInfo;
+import com.intellij.ui.SimpleTextAttributes;
+import com.intellij.util.containers.Predicate;
+import com.intellij.util.ui.classpath.ChooseLibrariesFromTablesDialog;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import java.awt.event.ActionEvent;
+import java.awt.event.KeyEvent;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * @author nik
+ */
+public class ProjectStructureChooseLibrariesDialog extends ChooseLibrariesFromTablesDialog {
+ private final ClasspathPanel myClasspathPanel;
+ private final StructureConfigurableContext myContext;
+ private final Predicate<Library> myAcceptedLibraries;
+ private final List<Library> myCreatedModuleLibraries = new ArrayList<Library>();
+ private JButton myCreateLibraryButton;
+
+ public ProjectStructureChooseLibrariesDialog(ClasspathPanel classpathPanel,
+ StructureConfigurableContext context,
+ Predicate<Library> acceptedLibraries) {
+ super(classpathPanel.getComponent(), "Choose Libraries", classpathPanel.getProject(), true);
+ myClasspathPanel = classpathPanel;
+ myContext = context;
+ myAcceptedLibraries = acceptedLibraries;
+ setOKButtonText("Add Selected");
+ init();
+ }
+
+ @Override
+ protected void doOKAction() {
+ super.doOKAction();
+ removeCreatedModuleLibraries(getSelectedLibraries());
+ }
+
+ @Override
+ public void doCancelAction() {
+ super.doCancelAction();
+ removeCreatedModuleLibraries(Collections.<Library>emptyList());
+ }
+
+ private void removeCreatedModuleLibraries(Collection<Library> selected) {
+ for (Library library : myCreatedModuleLibraries) {
+ if (!selected.contains(library)) {
+ myClasspathPanel.getRootModel().getModuleLibraryTable().removeLibrary(library);
+ }
+ }
+ }
+
+ @Override
+ protected void collectChildren(Object element, List<Object> result) {
+ if (element instanceof Application && !myCreatedModuleLibraries.isEmpty()) {
+ result.add(myClasspathPanel.getRootModel().getModuleLibraryTable());
+ }
+ super.collectChildren(element, result);
+ }
+
+ @NotNull
+ @Override
+ protected Library[] getLibraries(@NotNull LibraryTable table) {
+ if (table.getTableLevel().equals(LibraryTableImplUtil.MODULE_LEVEL)) {
+ return myCreatedModuleLibraries.toArray(new Library[myCreatedModuleLibraries.size()]);
+ }
+ final LibrariesModifiableModel model = getLibrariesModifiableModel(table);
+ if (model == null) return Library.EMPTY_ARRAY;
+ return model.getLibraries();
+ }
+
+ @Nullable
+ private LibrariesModifiableModel getLibrariesModifiableModel(LibraryTable table) {
+ return table != null ? myContext.myLevel2Providers.get(table.getTableLevel()) : null;
+ }
+
+ @Override
+ protected boolean acceptsElement(Object element) {
+ if (element instanceof Library) {
+ final Library library = (Library)element;
+ return myAcceptedLibraries.apply(library);
+ }
+ return true;
+ }
+
+ @NotNull
+ private String getLibraryName(@NotNull Library library) {
+ final LibrariesModifiableModel model = getLibrariesModifiableModel(library.getTable());
+ if (model != null) {
+ if (model.hasLibraryEditor(library)) {
+ return model.getLibraryEditor(library).getName();
+ }
+ }
+ return library.getName();
+ }
+
+ @Override
+ protected Action[] createActions() {
+ if (SystemInfo.isMac) {
+ return new Action[]{getCancelAction(), new CreateNewLibraryAction(), getOKAction()};
+ }
+ return new Action[]{getOKAction(), new CreateNewLibraryAction(), getCancelAction()};
+ }
+
+ @Override
+ protected JButton createJButtonForAction(Action action) {
+ final JButton button = super.createJButtonForAction(action);
+ if (action instanceof CreateNewLibraryAction) {
+ myCreateLibraryButton = button;
+ }
+ return button;
+ }
+
+ @Override
+ protected LibrariesTreeNodeBase<Library> createLibraryDescriptor(NodeDescriptor parentDescriptor,
+ Library library) {
+ final String libraryName = getLibraryName(library);
+ return new LibraryEditorDescriptor(getProject(), parentDescriptor, library, libraryName, myContext);
+ }
+
+ private static class LibraryEditorDescriptor extends LibrariesTreeNodeBase<Library> {
+ protected LibraryEditorDescriptor(final Project project, final NodeDescriptor parentDescriptor, final Library element,
+ String libraryName, StructureConfigurableContext context) {
+ super(project, parentDescriptor, element);
+ final PresentationData templatePresentation = getTemplatePresentation();
+ Icon icon = LibraryPresentationManager.getInstance().getNamedLibraryIcon(element, context);
+ templatePresentation.setIcon(icon);
+ templatePresentation.addText(libraryName, SimpleTextAttributes.REGULAR_ATTRIBUTES);
+ }
+ }
+
+ private class CreateNewLibraryAction extends DialogWrapperAction {
+ private CreateNewLibraryAction() {
+ super("New Library...");
+ putValue(MNEMONIC_KEY, KeyEvent.VK_N);
+ }
+
+ @Override
+ protected void doAction(ActionEvent e) {
+ AddNewLibraryDependencyAction.chooseTypeAndCreate(myClasspathPanel, myContext, myCreateLibraryButton,
+ new AddNewLibraryDependencyAction.LibraryCreatedCallback() {
+ @Override
+ public void libraryCreated(@NotNull Library library) {
+ if (library.getTable() == null) {
+ myCreatedModuleLibraries.add(library);
+ }
+ queueUpdateAndSelect(library);
+ }
+ });
+ }
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/dependencyAnalysis/AnalyzeDependenciesComponent.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/dependencyAnalysis/AnalyzeDependenciesComponent.java
new file mode 100644
index 0000000..25b49da
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/dependencyAnalysis/AnalyzeDependenciesComponent.java
@@ -0,0 +1,847 @@
+/*
+ * Copyright 2000-2012 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.dependencyAnalysis;
+
+import com.intellij.ProjectTopics;
+import com.intellij.icons.AllIcons;
+import com.intellij.ide.DataManager;
+import com.intellij.openapi.actionSystem.*;
+import com.intellij.openapi.actionSystem.ex.ComboBoxAction;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.module.ModuleType;
+import com.intellij.openapi.options.ConfigurationException;
+import com.intellij.openapi.project.DumbAwareAction;
+import com.intellij.openapi.roots.ModuleRootAdapter;
+import com.intellij.openapi.roots.ModuleRootEvent;
+import com.intellij.openapi.roots.ModuleSourceOrderEntry;
+import com.intellij.openapi.roots.OrderEntry;
+import com.intellij.openapi.roots.ui.CellAppearanceEx;
+import com.intellij.openapi.roots.ui.OrderEntryAppearanceService;
+import com.intellij.openapi.roots.ui.configuration.ProjectStructureConfigurable;
+import com.intellij.openapi.ui.MasterDetailsComponent;
+import com.intellij.openapi.ui.NamedConfigurable;
+import com.intellij.openapi.util.Pair;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.ui.*;
+import com.intellij.ui.components.JBScrollPane;
+import com.intellij.ui.treeStructure.Tree;
+import com.intellij.util.PathUtil;
+import com.intellij.util.messages.MessageBusConnection;
+import com.intellij.util.ui.UIUtil;
+import com.intellij.util.ui.tree.TreeUtil;
+import org.jetbrains.annotations.Nls;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+
+import javax.swing.*;
+import javax.swing.tree.DefaultMutableTreeNode;
+import javax.swing.tree.DefaultTreeModel;
+import javax.swing.tree.TreePath;
+import javax.swing.tree.TreeSelectionModel;
+import java.awt.*;
+import java.util.ArrayList;
+import java.util.HashMap;
+
+/**
+ * The classpath details component
+ */
+public class AnalyzeDependenciesComponent extends MasterDetailsComponent {
+ /**
+ * Data key for order path element
+ */
+ public static DataKey<ModuleDependenciesAnalyzer.OrderPathElement> ORDER_PATH_ELEMENT_KEY = DataKey.create("ORDER_PATH_ELEMENT");
+ /**
+ * The module being analyzed
+ */
+ private final Module myModule;
+ /**
+ * The settings for UI mode
+ */
+ private final AnalyzeDependenciesSettings mySettings;
+ /**
+ * The cached analyzed classpaths for this module
+ */
+ private final HashMap<Pair<ClasspathType, Boolean>, ModuleDependenciesAnalyzer> myClasspaths =
+ new HashMap<Pair<ClasspathType, Boolean>, ModuleDependenciesAnalyzer>();
+
+ /**
+ * The message bus connection to use
+ */
+ private MessageBusConnection myMessageBusConnection;
+
+ /**
+ * The constructor
+ *
+ * @param module the module to analyze
+ */
+ public AnalyzeDependenciesComponent(Module module) {
+ myModule = module;
+ mySettings = AnalyzeDependenciesSettings.getInstance(myModule.getProject());
+ initTree();
+ init();
+ getSplitter().setProportion(0.3f);
+ myMessageBusConnection = myModule.getProject().getMessageBus().connect();
+ myMessageBusConnection.subscribe(ProjectTopics.PROJECT_ROOTS, new ModuleRootAdapter() {
+ @Override
+ public void rootsChanged(ModuleRootEvent event) {
+ myClasspaths.clear();
+ updateTree();
+ }
+ });
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void disposeUIResources() {
+ if (myMessageBusConnection != null) {
+ myMessageBusConnection.disconnect();
+ }
+ }
+
+ /**
+ * Initialize components
+ */
+ private void init() {
+ myTree.setCellRenderer(new ColoredTreeCellRenderer() {
+ @Override
+ public void customizeCellRenderer(JTree tree,
+ Object value,
+ boolean selected,
+ boolean expanded,
+ boolean leaf,
+ int row,
+ boolean hasFocus) {
+ //if(getBackground() == null) {
+ // setBackground(UIUtil.getTreeTextBackground());
+ //}
+ if (value instanceof MyNode && !(value instanceof MyRootNode)) {
+ final MyNode node = (MyNode)value;
+ PathNode<?> n = (PathNode<?>)node.getUserObject();
+ CellAppearanceEx a = n.getAppearance(selected, node.isDisplayInBold());
+ a.customize(this);
+ }
+ }
+ });
+ myTree.setShowsRootHandles(false);
+ myTree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
+ reloadTree();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected ArrayList<AnAction> createActions(boolean fromPopup) {
+ if (!fromPopup) {
+ ArrayList<AnAction> rc = new ArrayList<AnAction>();
+ rc.add(new ClasspathTypeAction());
+ rc.add(new SdkFilterAction());
+ rc.add(new UrlModeAction());
+ return rc;
+ }
+ else {
+ return super.createActions(fromPopup);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected void processRemovedItems() {
+ // no remove action so far
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected boolean wasObjectStored(Object editableObject) {
+ // no modifications so far
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Nls
+ @Override
+ public String getDisplayName() {
+ return "Classpath Details";
+ }
+
+ /**
+ * Reload tree
+ */
+ public void reloadTree() {
+ myRoot.removeAllChildren();
+ ModuleDependenciesAnalyzer a = getAnalyzer();
+ if (mySettings.isUrlMode()) {
+ for (ModuleDependenciesAnalyzer.UrlExplanation urlExplanation : a.getUrls()) {
+ myRoot.add(new MyNode(new UrlNode(urlExplanation)));
+ }
+ }
+ else {
+ for (ModuleDependenciesAnalyzer.OrderEntryExplanation explanation : a.getOrderEntries()) {
+ myRoot.add(new MyNode(new OrderEntryNode(explanation)));
+ }
+ }
+ ((DefaultTreeModel)myTree.getModel()).reload(myRoot);
+ }
+
+ /**
+ * @return the analyzer for the current settings
+ */
+ public ModuleDependenciesAnalyzer getAnalyzer() {
+ final Pair<ClasspathType, Boolean> key = Pair.create(getClasspathType(), mySettings.isSdkIncluded());
+ ModuleDependenciesAnalyzer a = myClasspaths.get(key);
+ if (a == null) {
+ a = new ModuleDependenciesAnalyzer(myModule, !mySettings.isTest(), !mySettings.isRuntime(), mySettings.isSdkIncluded());
+ myClasspaths.put(key, a);
+ }
+ return a;
+ }
+
+ /**
+ * @return the current classpath type from settings
+ */
+ private ClasspathType getClasspathType() {
+ return mySettings.isRuntime() ?
+ (mySettings.isTest() ? ClasspathType.TEST_RUNTIME : ClasspathType.PRODUCTION_RUNTIME) :
+ (mySettings.isTest() ? ClasspathType.TEST_COMPILE : ClasspathType.PRODUCTION_COMPILE);
+ }
+
+
+ /**
+ * Schedule updating the tree
+ */
+ void updateTree() {
+ // TODO make loading in the background if there will be significant delays on big projects
+ reloadTree();
+ }
+
+ /**
+ * The action that allows navigating to the path element
+ */
+ static class NavigateAction extends DumbAwareAction {
+
+ /**
+ * The constructor
+ */
+ NavigateAction() {
+ super("Navigate to ...", "Navigate to place where path element is defined", null);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void actionPerformed(AnActionEvent e) {
+ final Module module = e.getData(LangDataKeys.MODULE);
+ if (module == null) {
+ return;
+ }
+ final ModuleDependenciesAnalyzer.OrderPathElement element = e.getData(ORDER_PATH_ELEMENT_KEY);
+ if (element != null && element instanceof ModuleDependenciesAnalyzer.OrderEntryPathElement) {
+ final ModuleDependenciesAnalyzer.OrderEntryPathElement o = (ModuleDependenciesAnalyzer.OrderEntryPathElement)element;
+ final OrderEntry entry = o.entry();
+ final Module m = entry.getOwnerModule();
+ ProjectStructureConfigurable.getInstance(module.getProject()).selectOrderEntry(m, entry);
+ }
+ }
+ }
+
+ /**
+ * Base class for nodes
+ *
+ * @param <T> the actual explanation type
+ */
+ abstract class PathNode<T extends ModuleDependenciesAnalyzer.Explanation> extends NamedConfigurable<T> implements DataProvider {
+ /**
+ * The cut off length, after which URLs are not shown (only suffix)
+ */
+ public static final int CUTOFF_LENGTH = 80;
+
+ /**
+ * The explanation
+ */
+ protected final T myExplanation;
+ /**
+ * The tree with explanation
+ */
+ private Tree myExplanationTree;
+
+ /**
+ * The constructor
+ *
+ * @param explanation the wrapped explanation
+ */
+ public PathNode(T explanation) {
+ myExplanation = explanation;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public T getEditableObject() {
+ return myExplanation;
+ }
+
+ /**
+ * @return a created tree component (to be used as
+ */
+ private JComponent createTreeComponent() {
+ myExplanationTree = new Tree(new DefaultTreeModel(buildTree()));
+ myExplanationTree.setRootVisible(false);
+ myExplanationTree.setCellRenderer(new ExplanationTreeRenderer());
+ DataManager.registerDataProvider(myExplanationTree, this);
+ TreeUtil.expandAll(myExplanationTree);
+ final NavigateAction navigateAction = new NavigateAction();
+ navigateAction.registerCustomShortcutSet(new CustomShortcutSet(CommonShortcuts.DOUBLE_CLICK_1.getShortcuts()[0]), myExplanationTree);
+ DefaultActionGroup group = new DefaultActionGroup();
+ group.addAction(navigateAction);
+ PopupHandler.installUnknownPopupHandler(myExplanationTree, group, ActionManager.getInstance());
+ return new JBScrollPane(myExplanationTree);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Object getData(@NonNls String dataId) {
+ if (PlatformDataKeys.PROJECT.is(dataId)) {
+ return myModule.getProject();
+ }
+ if (LangDataKeys.MODULE.is(dataId)) {
+ return myModule;
+ }
+ TreePath selectionPath = myExplanationTree.getSelectionPath();
+ DefaultMutableTreeNode node = selectionPath == null ? null : (DefaultMutableTreeNode)selectionPath.getLastPathComponent();
+ Object o = node == null ? null : node.getUserObject();
+ if (o instanceof ModuleDependenciesAnalyzer.OrderPathElement) {
+ if (ORDER_PATH_ELEMENT_KEY.is(dataId)) {
+ return o;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Build tree for the dependencies
+ *
+ * @return a tree model
+ */
+ private DefaultMutableTreeNode buildTree() {
+ DefaultMutableTreeNode root = new DefaultMutableTreeNode("ROOT");
+ for (ModuleDependenciesAnalyzer.OrderPath orderPath : myExplanation.paths()) {
+ addDependencyPath(root, orderPath, 0);
+ }
+ return root;
+ }
+
+ /**
+ * Add the dependency path
+ *
+ * @param parent the parent to which path is added
+ * @param orderPath the order entry path
+ * @param i the position in the path
+ */
+ private void addDependencyPath(DefaultMutableTreeNode parent,
+ ModuleDependenciesAnalyzer.OrderPath orderPath,
+ int i) {
+ if (i >= orderPath.entries().size()) {
+ return;
+ }
+ ModuleDependenciesAnalyzer.OrderPathElement e = orderPath.entries().get(i);
+ int sz = parent.getChildCount();
+ DefaultMutableTreeNode n;
+ if (sz == 0) {
+ n = null;
+ }
+ else {
+ n = (DefaultMutableTreeNode)parent.getChildAt(sz - 1);
+ if (!n.getUserObject().equals(e)) {
+ n = null;
+ }
+ }
+ if (n == null) {
+ n = new DefaultMutableTreeNode(e);
+ parent.add(n);
+ }
+ addDependencyPath(n, orderPath, i + 1);
+ }
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void setDisplayName(String name) {
+ // do nothing
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public JComponent createOptionsPanel() {
+ final JComponent tree = createTreeComponent();
+ JPanel panel = new JPanel(new BorderLayout());
+ JLabel paths = new JLabel("Available Through Paths:");
+ paths.setDisplayedMnemonic('P');
+ paths.setLabelFor(tree);
+ panel.add(paths, BorderLayout.NORTH);
+ panel.add(tree, BorderLayout.CENTER);
+ return panel;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void disposeUIResources() {
+ //Do nothing
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getHelpTopic() {
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isModified() {
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void apply() throws ConfigurationException {
+ // Do nothing
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void reset() {
+ //Do nothing
+ }
+
+ /**
+ * Get appearance for rendering in master list
+ *
+ * @param selected true if selected
+ * @param bold true if bold
+ * @return the result appearance
+ */
+ public abstract CellAppearanceEx getAppearance(boolean selected, boolean bold);
+
+ /**
+ * @retrun the string cut so it would fit the banner (the prefix is dropped)
+ */
+ protected String suffixForBanner(String p) {
+ if (p.length() > CUTOFF_LENGTH) {
+ p = "..." + p.substring(p.length() - CUTOFF_LENGTH);
+ }
+ return p;
+ }
+
+ /**
+ * @retrun the string cut so it would fit the banner (the suffix is dropped)
+ */
+ protected String prefixForBanner(String p) {
+ if (p.length() > CUTOFF_LENGTH) {
+ p = p.substring(0, CUTOFF_LENGTH) + "...";
+ }
+ return p;
+ }
+ }
+
+ /**
+ * Cell renderer for explanation tree
+ */
+ static class ExplanationTreeRenderer extends ColoredTreeCellRenderer {
+
+ @Override
+ public void customizeCellRenderer(JTree tree,
+ Object value,
+ boolean selected,
+ boolean expanded,
+ boolean leaf,
+ int row,
+ boolean hasFocus) {
+ DefaultMutableTreeNode n = (DefaultMutableTreeNode)value;
+ final Object userObject = n.getUserObject();
+ if (!(userObject instanceof ModuleDependenciesAnalyzer.OrderPathElement)) {
+ return;
+ }
+ ModuleDependenciesAnalyzer.OrderPathElement e = (ModuleDependenciesAnalyzer.OrderPathElement)userObject;
+ final CellAppearanceEx appearance = e.getAppearance(selected);
+ appearance.customize(this);
+ }
+ }
+
+ /**
+ * The entry node in URL node
+ */
+ class UrlNode extends PathNode<ModuleDependenciesAnalyzer.UrlExplanation> {
+
+ /**
+ * The constructor
+ *
+ * @param url the wrapped explanation
+ */
+ public UrlNode(ModuleDependenciesAnalyzer.UrlExplanation url) {
+ super(url);
+ setNameFieldShown(false);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public CellAppearanceEx getAppearance(boolean selected, final boolean isBold) {
+ return new CellAppearanceEx() {
+ @Override
+ public void customize(@NotNull SimpleColoredComponent component) {
+ component.setIcon(getIcon());
+ final Font font = UIUtil.getTreeFont();
+ if (isBold) {
+ component.setFont(font.deriveFont(Font.BOLD));
+ }
+ else {
+ component.setFont(font.deriveFont(Font.PLAIN));
+ }
+ final String p = PathUtil.toPresentableUrl(getEditableObject().url());
+ component.append(PathUtil.getFileName(p),
+ isBold ? SimpleTextAttributes.REGULAR_BOLD_ATTRIBUTES : SimpleTextAttributes.REGULAR_ATTRIBUTES);
+ component.append(" (" + PathUtil.getParentPath(p) + ")", SimpleTextAttributes.GRAYED_ATTRIBUTES);
+ }
+
+ @Override
+ public void customize(@NotNull final HtmlListCellRenderer renderer) {
+ throw new UnsupportedOperationException("Rendering in combo box not supported yet.");
+ }
+
+ @NotNull
+ @Override
+ public String getText() {
+ return getDisplayName();
+ }
+ };
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getBannerSlogan() {
+ final VirtualFile f = myExplanation.getLocalFile();
+ String p = f == null ? myExplanation.url() : f.getPath();
+ p = suffixForBanner(p);
+ return p;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ private Icon getIcon() {
+ return myExplanation.getIcon();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Nls
+ @Override
+ public String getDisplayName() {
+ return myExplanation.url();
+ }
+ }
+
+ /**
+ * The wrapper for order entries
+ */
+ class OrderEntryNode extends PathNode<ModuleDependenciesAnalyzer.OrderEntryExplanation> {
+
+ /**
+ * The constructor
+ *
+ * @param orderEntryExplanation the explanation to wrap
+ */
+ public OrderEntryNode(ModuleDependenciesAnalyzer.OrderEntryExplanation orderEntryExplanation) {
+ super(orderEntryExplanation);
+ setNameFieldShown(false);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public CellAppearanceEx getAppearance(boolean selected, final boolean isBold) {
+ if (myExplanation.entry() instanceof ModuleSourceOrderEntry) {
+ ModuleSourceOrderEntry e = (ModuleSourceOrderEntry)myExplanation.entry();
+ if (e.getOwnerModule() == myModule) {
+ return new CellAppearanceEx() {
+ @Override
+ public void customize(@NotNull SimpleColoredComponent component) {
+ component.setIcon(ModuleType.get(myModule).getIcon());
+ component.append("<This Module>", SimpleTextAttributes.SYNTHETIC_ATTRIBUTES);
+ }
+
+ @Override
+ public void customize(@NotNull final HtmlListCellRenderer renderer) {
+ throw new UnsupportedOperationException("Rendering in combo box not supported yet.");
+ }
+
+ @NotNull
+ @Override
+ public String getText() {
+ return "<This Module>";
+ }
+ };
+ }
+ else {
+ return OrderEntryAppearanceService.getInstance().forModule(e.getOwnerModule());
+ }
+ }
+ return OrderEntryAppearanceService.getInstance().forOrderEntry(myModule.getProject(), myExplanation.entry(), selected);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getBannerSlogan() {
+ if (myExplanation.entry() instanceof ModuleSourceOrderEntry) {
+ ModuleSourceOrderEntry e = (ModuleSourceOrderEntry)myExplanation.entry();
+ return prefixForBanner("Module " + e.getOwnerModule().getName());
+ }
+ else {
+ final String p = myExplanation.entry().getPresentableName() + " in module " + myExplanation.entry().getOwnerModule().getName();
+ return suffixForBanner(p);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Nls
+ @Override
+ public String getDisplayName() {
+ return myExplanation.entry().getPresentableName();
+ }
+ }
+
+ /**
+ * The action that allows including and excluding SDK entries from analysis
+ */
+ private class SdkFilterAction extends ToggleAction {
+
+ /**
+ * The constructor
+ */
+ public SdkFilterAction() {
+ super("Include SDK", "If selected, the SDK classes are included", AllIcons.General.Jdk);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isSelected(AnActionEvent e) {
+ return mySettings.isSdkIncluded();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void setSelected(AnActionEvent e, boolean state) {
+ mySettings.setIncludeSdk(state);
+ updateTree();
+ }
+ }
+
+ /**
+ * The action that allows switching class path between URL and order entry modes
+ */
+ private class UrlModeAction extends ToggleAction {
+ /**
+ * The constructor
+ */
+ public UrlModeAction() {
+ super("Use URL mode", "If selected, the URLs are displayed, otherwise order entries", AllIcons.Nodes.PpFile);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isSelected(AnActionEvent e) {
+ return mySettings.isUrlMode();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void setSelected(AnActionEvent e, boolean state) {
+ mySettings.setUrlMode(state);
+ updateTree();
+ }
+ }
+
+ /**
+ * Classpath type action for the analyze classpath
+ */
+ private class ClasspathTypeAction extends ComboBoxAction {
+ /**
+ * The filter action group
+ */
+ DefaultActionGroup myItems;
+
+ /**
+ * {@inheritDoc}
+ */
+ @NotNull
+ @Override
+ protected DefaultActionGroup createPopupActionGroup(JComponent button) {
+ if (myItems == null) {
+ myItems = new DefaultActionGroup(null, true);
+ for (final ClasspathType classpathType : ClasspathType.values()) {
+ myItems.addAction(new DumbAwareAction(classpathType.getDescription()) {
+ @Override
+ public void actionPerformed(AnActionEvent e) {
+ mySettings.setRuntime(classpathType.isRuntime());
+ mySettings.setTest(classpathType.isTest());
+ updateTree();
+ }
+ });
+ }
+ }
+ return myItems;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void update(AnActionEvent e) {
+ final Presentation presentation = e.getPresentation();
+ updateText(presentation);
+ }
+
+ /**
+ * Update the text for the combobox
+ *
+ * @param presentation the presentaiton to update
+ */
+ private void updateText(Presentation presentation) {
+ final ClasspathType classpathType = getClasspathType();
+ String t = classpathType.getDescription();
+ presentation.setText(t);
+ }
+ }
+
+ /**
+ * The enumeration type that represents classpath entry filter
+ */
+ private static enum ClasspathType {
+ /**
+ * The production compile mode
+ */
+ PRODUCTION_COMPILE(false, false, "Production Compile"),
+ /**
+ * The production runtime mode
+ */
+ PRODUCTION_RUNTIME(false, true, "Production Runtime"),
+ /**
+ * The test runtime mode
+ */
+ TEST_RUNTIME(true, true, "Test Runtime"),
+ /**
+ * The test compile mode
+ */
+ TEST_COMPILE(true, false, "Test Compile");
+
+ /**
+ * true, if test mode
+ */
+ final private boolean myIsTest;
+ /**
+ * true, if runtime mode
+ */
+ final private boolean myIsRuntime;
+ /**
+ * The description text
+ */
+ final private String myDescription;
+
+ /**
+ * The constructor
+ *
+ * @param isTest true if the test mode
+ * @param isRuntime true if the runtime ode
+ * @param description the description text
+ */
+ ClasspathType(boolean isTest, boolean isRuntime, String description) {
+ myIsTest = isTest;
+ myIsRuntime = isRuntime;
+ myDescription = description;
+ }
+
+ /**
+ * @return true if the test mode
+ */
+ public boolean isTest() {
+ return myIsTest;
+ }
+
+ /**
+ * @return true if the runtime mode
+ */
+ public boolean isRuntime() {
+ return myIsRuntime;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String toString() {
+ return myDescription;
+ }
+
+ /**
+ * @return the description for the entry
+ */
+ public String getDescription() {
+ return myDescription;
+ }
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/dependencyAnalysis/AnalyzeDependenciesDialog.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/dependencyAnalysis/AnalyzeDependenciesDialog.java
new file mode 100644
index 0000000..afae2c5
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/dependencyAnalysis/AnalyzeDependenciesDialog.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2000-2010 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.dependencyAnalysis;
+
+import com.intellij.openapi.Disposable;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.ui.DialogWrapper;
+import com.intellij.openapi.util.Disposer;
+
+import javax.swing.*;
+
+/**
+ * The dialog that allows examining dependencies
+ */
+public class AnalyzeDependenciesDialog extends DialogWrapper {
+ /**
+ * The analyzer component
+ */
+ private final AnalyzeDependenciesComponent myComponent;
+
+ /**
+ * The constructor
+ *
+ * @param module the dialog that allows analyzing dependencies
+ */
+ protected AnalyzeDependenciesDialog(Module module) {
+ super(module.getProject(), true);
+ setTitle("Analyze Dependencies for " + module.getName());
+ setModal(false);
+ myComponent = new AnalyzeDependenciesComponent(module);
+ Disposer.register(myDisposable, new Disposable() {
+ @Override
+ public void dispose() {
+ myComponent.disposeUIResources();
+ }
+ });
+ setOKButtonText("Close");
+ init();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected Action[] createActions() {
+ return new Action[]{getOKAction()};
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected JComponent createCenterPanel() {
+ return myComponent.createComponent();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected String getDimensionServiceKey() {
+ return getClass().getName();
+ }
+
+ /**
+ * Show the dialog
+ *
+ * @param module the module to use
+ */
+ public static void show(Module module) {
+ new AnalyzeDependenciesDialog(module).show();
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/dependencyAnalysis/AnalyzeDependenciesSettings.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/dependencyAnalysis/AnalyzeDependenciesSettings.java
new file mode 100644
index 0000000..b652960
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/dependencyAnalysis/AnalyzeDependenciesSettings.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright 2000-2012 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.dependencyAnalysis;
+
+import com.intellij.openapi.components.*;
+import com.intellij.openapi.components.StoragePathMacros;
+import com.intellij.openapi.project.Project;
+
+/**
+ * The default mode for classpath details settings
+ */
+@State(
+ name = "AnalyzeDependenciesSettings",
+ storages = {@Storage(
+ file = StoragePathMacros.WORKSPACE_FILE)})
+public class AnalyzeDependenciesSettings implements PersistentStateComponent<AnalyzeDependenciesSettings.State> {
+ /**
+ * The current state
+ */
+ private State myState = new State();
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public State getState() {
+ return myState;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void loadState(State state) {
+ if (state == null) {
+ state = new State();
+ }
+ myState = state;
+ }
+
+ /**
+ * Set runtime flag
+ *
+ * @param value the value to se
+ */
+ public void setRuntime(boolean value) {
+ myState.IS_RUNTIME = value;
+ }
+
+ /**
+ * Set test flag
+ *
+ * @param value the value to se
+ */
+ public void setTest(boolean value) {
+ myState.IS_TEST = value;
+ }
+
+ /**
+ * Set include sdk flag
+ *
+ * @param value the value to se
+ */
+ public void setIncludeSdk(boolean value) {
+ myState.INCLUDE_SDK = value;
+ }
+
+ /**
+ * @return true, if runtime classpath should be analyzed
+ */
+ public boolean isRuntime() {
+ return myState.IS_RUNTIME;
+ }
+
+ /**
+ * @return true, if sdk classes should be also checked
+ */
+ public boolean isSdkIncluded() {
+ return myState.INCLUDE_SDK;
+ }
+
+ /**
+ * @return true, if test classes should be included
+ */
+ public boolean isTest() {
+ return myState.IS_TEST;
+ }
+
+ /**
+ * @return true, if classpath analysis is done in term or urls. false if in term of order entries
+ */
+ public boolean isUrlMode() {
+ return myState.IS_URL_MODE;
+ }
+
+ /**
+ * Set url mode
+ *
+ * @param value true, if classpath analysis is done in term or urls. false if in term of order entries
+ */
+ public void setUrlMode(boolean value) {
+ myState.IS_URL_MODE = value;
+ }
+
+
+ /**
+ * Get project instance
+ *
+ * @param project the context project
+ * @return the created instance
+ */
+ public static AnalyzeDependenciesSettings getInstance(Project project) {
+ return ServiceManager.getService(project, AnalyzeDependenciesSettings.class);
+ }
+
+ /**
+ * The state object for settings
+ */
+ public static class State {
+ /**
+ * If true, runtime dependencies are shown, otherwise compile-time
+ */
+ boolean IS_RUNTIME = true;
+ /**
+ * If true, test dependencies are shown, otherwise production dependencies only
+ */
+ boolean IS_TEST = true;
+ /**
+ * If true, the JDK entries are included as well
+ */
+ boolean INCLUDE_SDK = false;
+ /**
+ * Are urls or order entry used
+ */
+ boolean IS_URL_MODE = true;
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/dependencyAnalysis/ModuleDependenciesAnalyzer.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/dependencyAnalysis/ModuleDependenciesAnalyzer.java
new file mode 100644
index 0000000..2660029
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/dependencyAnalysis/ModuleDependenciesAnalyzer.java
@@ -0,0 +1,479 @@
+/*
+ * Copyright 2000-2011 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.dependencyAnalysis;
+
+import com.intellij.icons.AllIcons;
+import com.intellij.ide.presentation.VirtualFilePresentation;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.roots.*;
+import com.intellij.openapi.roots.ui.CellAppearanceEx;
+import com.intellij.openapi.roots.ui.OrderEntryAppearanceService;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.openapi.vfs.VirtualFileManager;
+import com.intellij.util.PathUtil;
+import com.intellij.util.Processor;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import java.util.*;
+
+/**
+ * Analyzer for module classpath. It uses order enumerator to get classpath details.
+ */
+public class ModuleDependenciesAnalyzer {
+ /**
+ * The current module
+ */
+ private final Module myModule;
+ /**
+ * If true production classpath is analyzed. If false, the test classpath.
+ */
+ private final boolean myProduction;
+ /**
+ * If true the compilation classpath is analyzed. If false, the test classpath.
+ */
+ private final boolean myCompile;
+ /**
+ * If true, SDK classes are included in the classpath.
+ */
+ private final boolean mySdk;
+ /**
+ * The order entry explanations
+ */
+ private final List<OrderEntryExplanation> myOrderEntries = new ArrayList<OrderEntryExplanation>();
+ /**
+ * The url explanations
+ */
+ private final List<UrlExplanation> myUrls = new ArrayList<UrlExplanation>();
+
+ /**
+ * The constructor (it creates explanations immediately
+ *
+ * @param module the context module
+ * @param production the production/test flag
+ * @param compile the compile/runtime flag
+ * @param sdk the include sdk paths
+ */
+ public ModuleDependenciesAnalyzer(Module module, boolean production, boolean compile, boolean sdk) {
+ myModule = module;
+ myProduction = production;
+ myCompile = compile;
+ mySdk = sdk;
+ analyze();
+ }
+
+ /**
+ * @return url explanations
+ */
+ public List<UrlExplanation> getUrls() {
+ return Collections.unmodifiableList(myUrls);
+ }
+
+ /**
+ * @return order entry explanations
+ */
+ public List<OrderEntryExplanation> getOrderEntries() {
+ return Collections.unmodifiableList(myOrderEntries);
+ }
+
+ /**
+ * Analyze module classpath
+ */
+ private void analyze() {
+ OrderEnumerator e = ModuleRootManager.getInstance(myModule).orderEntries();
+ e.recursively();
+ if (!mySdk) {
+ e.withoutSdk();
+ }
+ if (myCompile) {
+ e.compileOnly();
+ }
+ else {
+ e.runtimeOnly();
+ }
+ if (myProduction) {
+ e.productionOnly();
+ }
+ final Map<String, List<OrderPath>> urlExplanations = new LinkedHashMap<String, List<OrderPath>>();
+ final OrderRootsEnumerator classes = e.classes();
+ if (myCompile) {
+ classes.withoutSelfModuleOutput();
+ }
+ for (String url : classes.getUrls()) {
+ if (!urlExplanations.containsKey(url)) {
+ urlExplanations.put(url, new ArrayList<OrderPath>());
+ }
+ }
+ final Map<OrderEntry, List<OrderPath>> orderExplanations = new LinkedHashMap<OrderEntry, List<OrderPath>>();
+ new PathWalker(urlExplanations, orderExplanations).examine(myModule, 0);
+ for (Map.Entry<OrderEntry, List<OrderPath>> entry : orderExplanations.entrySet()) {
+ myOrderEntries.add(new OrderEntryExplanation(entry.getKey(), entry.getValue()));
+ }
+ for (Map.Entry<String, List<OrderPath>> entry : urlExplanations.entrySet()) {
+ myUrls.add(new UrlExplanation(entry.getKey(), entry.getValue()));
+ }
+ }
+
+ /**
+ * The walker for the class paths. It walks the entire module classpath
+ */
+ private class PathWalker {
+ /**
+ * The explanations for urls
+ */
+ private final Map<String, List<OrderPath>> myUrlExplanations;
+ /**
+ * The explanations for order entries
+ */
+ private final Map<OrderEntry, List<OrderPath>> myOrderExplanations;
+ /**
+ * The current stack
+ */
+ private final ArrayList<OrderPathElement> myStack = new ArrayList<OrderPathElement>();
+ /**
+ * Visited modules (in order to detect cyclic dependencies)
+ */
+ private final HashSet<Module> myVisited = new HashSet<Module>();
+
+ /**
+ * The constructor
+ *
+ * @param urlExplanations the url explanations to accumulate
+ * @param orderExplanations the explanations for order entries
+ */
+ public PathWalker(Map<String, List<OrderPath>> urlExplanations,
+ Map<OrderEntry, List<OrderPath>> orderExplanations) {
+ myUrlExplanations = urlExplanations;
+ myOrderExplanations = orderExplanations;
+ }
+
+ /**
+ * Examine the specified module
+ *
+ * @param m the module to examine
+ * @param level the level of the examination
+ */
+ void examine(final Module m, final int level) {
+ if (myVisited.contains(m)) {
+ return;
+ }
+ myVisited.add(m);
+ try {
+ final OrderEnumerator e = ModuleRootManager.getInstance(m).orderEntries();
+ if (!mySdk || level != 0) {
+ e.withoutSdk();
+ }
+ if (myCompile && level != 0) {
+ e.exportedOnly();
+ }
+ if (myProduction) {
+ e.productionOnly();
+ }
+ if (myCompile) {
+ e.compileOnly();
+ }
+ else {
+ e.runtimeOnly();
+ }
+ e.forEach(new Processor<OrderEntry>() {
+ @Override
+ public boolean process(OrderEntry orderEntry) {
+ myStack.add(new OrderEntryPathElement(orderEntry));
+ try {
+ if (orderEntry instanceof ModuleOrderEntry) {
+ ModuleOrderEntry o = (ModuleOrderEntry)orderEntry;
+ examine(o.getModule(), level + 1);
+ }
+ else if (orderEntry instanceof ModuleSourceOrderEntry) {
+ if (!myProduction || !myCompile) {
+ CompilerModuleExtension e = CompilerModuleExtension.getInstance(m);
+ final OrderPath p = new OrderPath(myStack);
+ for (String u : e.getOutputRootUrls(!myCompile ? !myProduction : level > 0 && !myProduction)) {
+ addUrlPath(p, u);
+ }
+ addEntryPath(orderEntry, p);
+ }
+ }
+ else {
+ final OrderPath p = new OrderPath(myStack);
+ for (String u : orderEntry.getUrls(OrderRootType.CLASSES)) {
+ addUrlPath(p, u);
+ }
+ addEntryPath(orderEntry, p);
+ }
+ }
+ finally {
+ myStack.remove(myStack.size() - 1);
+ }
+ return true;
+ }
+ });
+ }
+ finally {
+ myVisited.remove(m);
+ }
+ }
+
+ /**
+ * Add url path
+ *
+ * @param p the path to add
+ * @param u the url to update
+ */
+ private void addUrlPath(OrderPath p, String u) {
+ final List<OrderPath> orderPaths = myUrlExplanations.get(u);
+ if (orderPaths != null) {
+ orderPaths.add(p);
+ }
+ }
+
+ /**
+ * Add order entry explanation
+ *
+ * @param orderEntry the order entry to explain
+ * @param p the path that explain order entry
+ */
+ private void addEntryPath(OrderEntry orderEntry, OrderPath p) {
+ List<OrderPath> paths = myOrderExplanations.get(orderEntry);
+ if (paths == null) {
+ paths = new ArrayList<OrderPath>();
+ myOrderExplanations.put(orderEntry, paths);
+ }
+ paths.add(p);
+ }
+ }
+
+ /**
+ * The path consisting of order entry path.
+ */
+ public static class OrderPath {
+ /**
+ * The immutable list of path elements. The first element in the list is an order entry actually included in the current module.
+ */
+ private final List<OrderPathElement> myEntries;
+
+ /**
+ * The constructor
+ *
+ * @param entries the list of entries (will be copied and wrapped)
+ */
+ public OrderPath(List<OrderPathElement> entries) {
+ this.myEntries = Collections.unmodifiableList(new ArrayList<OrderPathElement>(entries));
+ }
+
+ /**
+ * @return the immutable list of path elements. The first element in the list is an order entry actually included in the current module.
+ */
+ public List<OrderPathElement> entries() {
+ return myEntries;
+ }
+
+ @Override
+ public int hashCode() {
+ return myEntries.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof OrderPath)) {
+ return false;
+ }
+ return myEntries.equals(((OrderPath)obj).myEntries);
+ }
+ }
+
+ /**
+ * The a entry in the path. The implementation should support {@link #equals(Object)} and {@link #hashCode()} methods.
+ */
+ public static abstract class OrderPathElement {
+ /**
+ * Get appearance for path element
+ *
+ * @param isSelected true if the element is selected
+ * @return the appearance to use for rendering
+ */
+ @NotNull
+ public abstract CellAppearanceEx getAppearance(boolean isSelected);
+ }
+
+ /**
+ * The order entry path element
+ */
+ public static class OrderEntryPathElement extends OrderPathElement {
+ /**
+ * The order entry
+ */
+ private final OrderEntry myEntry;
+
+ /**
+ * The constructor
+ *
+ * @param entry the order entry
+ */
+ public OrderEntryPathElement(OrderEntry entry) {
+ this.myEntry = entry;
+ }
+
+ /**
+ * @return the order entry
+ */
+ public OrderEntry entry() {
+ return myEntry;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public int hashCode() {
+ return myEntry.hashCode();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof OrderEntryPathElement)) {
+ return false;
+ }
+ OrderEntryPathElement o = (OrderEntryPathElement)obj;
+ return o.myEntry == myEntry;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String toString() {
+ return myEntry.getPresentableName();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @NotNull
+ @Override
+ public CellAppearanceEx getAppearance(boolean isSelected) {
+ return OrderEntryAppearanceService.getInstance().forOrderEntry(myEntry.getOwnerModule().getProject(), myEntry, isSelected);
+ }
+ }
+
+ /**
+ * The base class for explanations
+ */
+ public static class Explanation {
+ /**
+ * The paths that refer to the path element
+ */
+ public final List<OrderPath> myPaths;
+
+ /**
+ * The explanation for the path
+ *
+ * @param paths the paths to analyze (the list is wrapped)
+ */
+ Explanation(List<OrderPath> paths) {
+ this.myPaths = Collections.unmodifiableList(paths);
+ }
+
+ /**
+ * @return the paths that refer to the path element
+ */
+ public List<OrderPath> paths() {
+ return myPaths;
+ }
+ }
+
+ /**
+ * The explanation for
+ */
+ public static class OrderEntryExplanation extends Explanation {
+ /**
+ * The URL in the path
+ */
+ private final OrderEntry myEntry;
+
+ /**
+ * The explanation for the path
+ *
+ * @param entry the explained order entry
+ * @param paths the paths to analyze
+ */
+ OrderEntryExplanation(OrderEntry entry, List<OrderPath> paths) {
+ super(paths);
+ myEntry = entry;
+ }
+
+ /**
+ * @return the explained entry
+ */
+ public OrderEntry entry() {
+ return myEntry;
+ }
+ }
+
+
+ /**
+ * The explanation for url
+ */
+ public static class UrlExplanation extends Explanation {
+ /**
+ * The URL in the path
+ */
+ private final String myUrl;
+
+ /**
+ * The explanation for the path
+ *
+ * @param url the url for the order entry
+ * @param paths the paths to analyze
+ */
+ UrlExplanation(String url, List<OrderPath> paths) {
+ super(paths);
+ myUrl = url;
+ }
+
+ /**
+ * @return the explained url
+ */
+ public String url() {
+ return myUrl;
+ }
+
+ /**
+ * @return icon for the classpath
+ */
+ @Nullable
+ public Icon getIcon() {
+ VirtualFile file = getLocalFile();
+ return file == null ? AllIcons.General.Error : VirtualFilePresentation.getIcon(file);
+ }
+
+ /**
+ * @return the local file
+ */
+ @Nullable
+ public VirtualFile getLocalFile() {
+ VirtualFile file = VirtualFileManager.getInstance().findFileByUrl(myUrl);
+ if (file != null) {
+ file = PathUtil.getLocalFile(file);
+ }
+ return file;
+ }
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraries/AddCustomLibraryDialog.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraries/AddCustomLibraryDialog.java
new file mode 100644
index 0000000..72239c8
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraries/AddCustomLibraryDialog.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2000-2010 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.libraries;
+
+import com.intellij.facet.impl.ui.libraries.LibraryCompositionSettings;
+import com.intellij.facet.impl.ui.libraries.LibraryOptionsPanel;
+import com.intellij.framework.library.FrameworkLibraryVersionFilter;
+import com.intellij.ide.IdeBundle;
+import com.intellij.openapi.application.Result;
+import com.intellij.openapi.application.WriteAction;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.roots.ModifiableRootModel;
+import com.intellij.openapi.roots.ModuleRootManager;
+import com.intellij.openapi.roots.libraries.Library;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.LibrariesContainer;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.LibrariesContainerFactory;
+import com.intellij.openapi.ui.DialogWrapper;
+import com.intellij.openapi.util.Disposer;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.util.ParameterizedRunnable;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+* @author nik
+*/
+public class AddCustomLibraryDialog extends DialogWrapper {
+ private final LibraryOptionsPanel myPanel;
+ private final LibrariesContainer myLibrariesContainer;
+ private final Module myModule;
+ private final ModifiableRootModel myModifiableRootModel;
+ private final @Nullable ParameterizedRunnable<ModifiableRootModel> myBeforeLibraryAdded;
+ private final List<Library> myAddedLibraries = new ArrayList<Library>();
+
+ private AddCustomLibraryDialog(CustomLibraryDescription description, LibrariesContainer librariesContainer,
+ Module module,
+ ModifiableRootModel modifiableRootModel,
+ @Nullable ParameterizedRunnable<ModifiableRootModel> beforeLibraryAdded) {
+ super(module.getProject(), true);
+ myLibrariesContainer = librariesContainer;
+ myModule = module;
+ myModifiableRootModel = modifiableRootModel;
+ myBeforeLibraryAdded = beforeLibraryAdded;
+ setTitle(IdeBundle.message("setup.library.dialog.title"));
+ VirtualFile baseDir = myModule.getProject().getBaseDir();
+ final String baseDirPath = baseDir != null ? baseDir.getPath() : "";
+ myPanel = new LibraryOptionsPanel(description, baseDirPath, FrameworkLibraryVersionFilter.ALL, myLibrariesContainer, false);
+ Disposer.register(myDisposable, myPanel);
+ init();
+ }
+
+ public static AddCustomLibraryDialog createDialog(@NotNull CustomLibraryDescription description,
+ final @NotNull Module module,
+ final ParameterizedRunnable<ModifiableRootModel> beforeLibraryAdded) {
+ return createDialog(description, LibrariesContainerFactory.createContainer(module), module, null, beforeLibraryAdded);
+ }
+
+ public static AddCustomLibraryDialog createDialog(CustomLibraryDescription description,
+ final @NotNull LibrariesContainer librariesContainer, final @NotNull Module module,
+ final @Nullable ModifiableRootModel modifiableRootModel,
+ @Nullable ParameterizedRunnable<ModifiableRootModel> beforeLibraryAdded) {
+ return new AddCustomLibraryDialog(description, librariesContainer, module, modifiableRootModel, beforeLibraryAdded);
+ }
+
+ @Override
+ protected JComponent createCenterPanel() {
+ return myPanel.getMainPanel();
+ }
+
+ @Override
+ protected void doOKAction() {
+ final LibraryCompositionSettings settings = myPanel.apply();
+ if (settings != null && settings.downloadFiles(myPanel.getMainPanel())) {
+ if (myModifiableRootModel == null) {
+ final ModifiableRootModel model = ModuleRootManager.getInstance(myModule).getModifiableModel();
+ new WriteAction() {
+ @Override
+ protected void run(final Result result) {
+ addLibraries(model, settings);
+ model.commit();
+ }
+ }.execute();
+ }
+ else {
+ addLibraries(myModifiableRootModel, settings);
+ }
+ super.doOKAction();
+ }
+ }
+
+ private void addLibraries(ModifiableRootModel model, final LibraryCompositionSettings settings) {
+ if (myBeforeLibraryAdded != null) {
+ myBeforeLibraryAdded.run(model);
+ }
+ settings.addLibraries(model, myAddedLibraries, myLibrariesContainer);
+ }
+
+ public List<Library> getAddedLibraries() {
+ return myAddedLibraries;
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraries/CustomLibraryDescription.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraries/CustomLibraryDescription.java
new file mode 100644
index 0000000..f375edf
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraries/CustomLibraryDescription.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2000-2012 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.libraries;
+
+import com.intellij.framework.library.DownloadableLibraryType;
+import com.intellij.openapi.roots.libraries.LibraryKind;
+import com.intellij.openapi.roots.libraries.NewLibraryConfiguration;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.LibrariesContainer;
+import com.intellij.openapi.vfs.VirtualFile;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import java.util.Set;
+
+/**
+ * @author nik
+ */
+public abstract class CustomLibraryDescription {
+ @Nullable
+ public DownloadableLibraryType getDownloadableLibraryType() {
+ return null;
+ }
+
+ @NotNull
+ public abstract Set<? extends LibraryKind> getSuitableLibraryKinds();
+
+ @Nullable
+ public abstract NewLibraryConfiguration createNewLibrary(@NotNull JComponent parentComponent, @Nullable VirtualFile contextDirectory);
+
+ @NotNull
+ public LibrariesContainer.LibraryLevel getDefaultLevel() {
+ return LibrariesContainer.LibraryLevel.PROJECT;
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraries/LibraryEditingUtil.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraries/LibraryEditingUtil.java
new file mode 100644
index 0000000..2c5374a
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraries/LibraryEditingUtil.java
@@ -0,0 +1,218 @@
+/*
+ * Copyright 2000-2012 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.libraries;
+
+import com.intellij.ide.IdeBundle;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.roots.LibraryOrderEntry;
+import com.intellij.openapi.roots.ModuleRootModel;
+import com.intellij.openapi.roots.OrderEntry;
+import com.intellij.openapi.roots.OrderRootType;
+import com.intellij.openapi.roots.impl.ModuleLibraryTable;
+import com.intellij.openapi.roots.impl.libraries.LibraryEx;
+import com.intellij.openapi.roots.impl.libraries.LibraryImpl;
+import com.intellij.openapi.roots.impl.libraries.LibraryTableImplUtil;
+import com.intellij.openapi.roots.libraries.*;
+import com.intellij.openapi.roots.ui.configuration.classpath.ClasspathPanel;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.LibrariesModifiableModel;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.ModuleStructureConfigurable;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.daemon.ProjectStructureValidator;
+import com.intellij.openapi.ui.popup.PopupStep;
+import com.intellij.openapi.ui.popup.util.BaseListPopupStep;
+import com.intellij.openapi.vfs.JarFileSystem;
+import com.intellij.openapi.vfs.VirtualFileManager;
+import com.intellij.util.ParameterizedRunnable;
+import com.intellij.util.PlatformIcons;
+import com.intellij.util.containers.Predicate;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import java.util.*;
+
+/**
+ * @author nik
+ */
+public class LibraryEditingUtil {
+ private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.roots.ui.configuration.libraries.LibraryEditingUtil");
+
+ private LibraryEditingUtil() {
+ }
+
+ public static boolean libraryAlreadyExists(LibraryTable.ModifiableModel table, String libraryName) {
+ for (Iterator<Library> it = table.getLibraryIterator(); it.hasNext(); ) {
+ final Library library = it.next();
+ final String libName;
+ if (table instanceof LibrariesModifiableModel){
+ libName = ((LibrariesModifiableModel)table).getLibraryEditor(library).getName();
+ }
+ else {
+ libName = library.getName();
+ }
+ if (libraryName.equals(libName)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public static String suggestNewLibraryName(LibraryTable.ModifiableModel table,
+ final String baseName) {
+ String candidateName = baseName;
+ int idx = 1;
+ while (libraryAlreadyExists(table, candidateName)) {
+ candidateName = baseName + (idx++);
+ }
+ return candidateName;
+ }
+
+ public static Predicate<Library> getNotAddedLibrariesCondition(final ModuleRootModel rootModel) {
+ final OrderEntry[] orderEntries = rootModel.getOrderEntries();
+ final Set<Library> result = new HashSet<Library>(orderEntries.length);
+ for (OrderEntry orderEntry : orderEntries) {
+ if (orderEntry instanceof LibraryOrderEntry && orderEntry.isValid()) {
+ final LibraryImpl library = (LibraryImpl)((LibraryOrderEntry)orderEntry).getLibrary();
+ if (library != null) {
+ final Library source = library.getSource();
+ result.add(source != null ? source : library);
+ }
+ }
+ }
+ return new Predicate<Library>() {
+ @Override
+ public boolean apply(Library library) {
+ if (result.contains(library)) return false;
+ if (library instanceof LibraryImpl) {
+ final Library source = ((LibraryImpl)library).getSource();
+ if (source != null && result.contains(source)) return false;
+ }
+ return true;
+ }
+ };
+ }
+
+ public static void copyLibrary(LibraryEx from, Map<String, String> rootMapping, LibraryEx.ModifiableModelEx target) {
+ target.setProperties(from.getProperties());
+ for (OrderRootType type : OrderRootType.getAllTypes()) {
+ final String[] urls = from.getUrls(type);
+ for (String url : urls) {
+ final String protocol = VirtualFileManager.extractProtocol(url);
+ if (protocol == null) continue;
+ final String fullPath = VirtualFileManager.extractPath(url);
+ final int sep = fullPath.indexOf(JarFileSystem.JAR_SEPARATOR);
+ String localPath;
+ String pathInJar;
+ if (sep != -1) {
+ localPath = fullPath.substring(0, sep);
+ pathInJar = fullPath.substring(sep);
+ }
+ else {
+ localPath = fullPath;
+ pathInJar = "";
+ }
+ final String targetPath = rootMapping.get(localPath);
+ String targetUrl = targetPath != null ? VirtualFileManager.constructUrl(protocol, targetPath + pathInJar) : url;
+
+ if (from.isJarDirectory(url, type)) {
+ target.addJarDirectory(targetUrl, false, type);
+ }
+ else {
+ target.addRoot(targetUrl, type);
+ }
+ }
+ }
+ }
+
+ public static LibraryTablePresentation getLibraryTablePresentation(@NotNull Project project, @NotNull String level) {
+ if (level.equals(LibraryTableImplUtil.MODULE_LEVEL)) {
+ return ModuleLibraryTable.MODULE_LIBRARY_TABLE_PRESENTATION;
+ }
+ final LibraryTable table = LibraryTablesRegistrar.getInstance().getLibraryTableByLevel(level, project);
+ LOG.assertTrue(table != null, level);
+ return table.getPresentation();
+ }
+
+ public static List<LibraryType> getSuitableTypes(ClasspathPanel classpathPanel) {
+ List<LibraryType> suitableTypes = new ArrayList<LibraryType>();
+ suitableTypes.add(null);
+ final Module module = classpathPanel.getRootModel().getModule();
+ for (LibraryType libraryType : LibraryType.EP_NAME.getExtensions()) {
+ if (libraryType.getCreateActionName() != null && libraryType.isSuitableModule(module, classpathPanel.getModuleConfigurationState().getFacetsProvider())) {
+ suitableTypes.add(libraryType);
+ }
+ }
+ return suitableTypes;
+ }
+
+ public static boolean hasSuitableTypes(ClasspathPanel panel) {
+ return getSuitableTypes(panel).size() > 1;
+ }
+
+ public static BaseListPopupStep<LibraryType> createChooseTypeStep(final ClasspathPanel classpathPanel,
+ final ParameterizedRunnable<LibraryType> action) {
+ return new BaseListPopupStep<LibraryType>(IdeBundle.message("popup.title.select.library.type"), getSuitableTypes(classpathPanel)) {
+ @NotNull
+ @Override
+ public String getTextFor(LibraryType value) {
+ return value != null ? value.getCreateActionName() : IdeBundle.message("create.default.library.type.action.name");
+ }
+
+ @Override
+ public Icon getIconFor(LibraryType aValue) {
+ return aValue != null ? aValue.getIcon() : PlatformIcons.LIBRARY_ICON;
+ }
+
+ @Override
+ public PopupStep onChosen(final LibraryType selectedValue, boolean finalChoice) {
+ return doFinalStep(new Runnable() {
+ @Override
+ public void run() {
+ action.run(selectedValue);
+ }
+ });
+ }
+ };
+ }
+
+ public static List<Module> getSuitableModules(@NotNull ModuleStructureConfigurable rootConfigurable,
+ final @Nullable LibraryKind kind, @Nullable Library library) {
+ final List<Module> modules = new ArrayList<Module>();
+ LibraryType type = kind == null ? null : LibraryType.findByKind(kind);
+ for (Module module : rootConfigurable.getModules()) {
+ if (type != null && !type.isSuitableModule(module, rootConfigurable.getFacetConfigurator())) {
+ continue;
+ }
+
+ if (library != null) {
+ final ModuleRootModel rootModel = rootConfigurable.getContext().getModulesConfigurator().getRootModel(module);
+ if (!getNotAddedLibrariesCondition(rootModel).apply(library)) {
+ continue;
+ }
+ }
+
+ modules.add(module);
+ }
+ return modules;
+ }
+
+ public static void showDialogAndAddLibraryToDependencies(@NotNull Library library,
+ @NotNull Project project,
+ boolean allowEmptySelection) {
+ ProjectStructureValidator.showDialogAndAddLibraryToDependencies(library, project, allowEmptySelection);
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraries/LibraryPresentationManager.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraries/LibraryPresentationManager.java
new file mode 100644
index 0000000..67584fd
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraries/LibraryPresentationManager.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2000-2012 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.libraries;
+
+import com.intellij.openapi.components.ServiceManager;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.roots.libraries.Library;
+import com.intellij.openapi.roots.libraries.LibraryKind;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.LibrariesContainer;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.StructureConfigurableContext;
+import com.intellij.openapi.vfs.VirtualFile;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * @author nik
+ */
+public abstract class LibraryPresentationManager {
+ public static LibraryPresentationManager getInstance() {
+ return ServiceManager.getService(LibraryPresentationManager.class);
+ }
+
+ @NotNull
+ public abstract Icon getNamedLibraryIcon(@NotNull Library library, @Nullable StructureConfigurableContext context);
+
+ @Nullable
+ public abstract Icon getCustomIcon(@NotNull Library library, @Nullable StructureConfigurableContext context);
+
+ @NotNull
+ public abstract List<Icon> getCustomIcons(@NotNull Library library, @Nullable StructureConfigurableContext context);
+
+ @NotNull
+ public abstract List<String> getDescriptions(@NotNull Library library, StructureConfigurableContext context);
+
+ @NotNull
+ public abstract List<String> getDescriptions(@NotNull VirtualFile[] classRoots, Set<LibraryKind> excludedKinds);
+
+ public abstract List<Library> getLibraries(@NotNull Set<LibraryKind> kinds, @NotNull Project project, @Nullable StructureConfigurableContext context);
+
+ public abstract boolean isLibraryOfKind(@NotNull List<VirtualFile> files, @NotNull LibraryKind kind);
+
+ public abstract boolean isLibraryOfKind(@NotNull Library library, @NotNull LibrariesContainer librariesContainer,
+ @NotNull Set<? extends LibraryKind> acceptedKinds);
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraries/impl/LibraryPresentationManagerImpl.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraries/impl/LibraryPresentationManagerImpl.java
new file mode 100644
index 0000000..d5db34b
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraries/impl/LibraryPresentationManagerImpl.java
@@ -0,0 +1,194 @@
+/*
+ * Copyright 2000-2012 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.libraries.impl;
+
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.roots.OrderRootType;
+import com.intellij.openapi.roots.impl.libraries.LibraryEx;
+import com.intellij.openapi.roots.libraries.*;
+import com.intellij.openapi.roots.ui.configuration.libraries.LibraryPresentationManager;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.LibrariesContainer;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.StructureConfigurableContext;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.util.PlatformIcons;
+import com.intellij.util.SmartList;
+import com.intellij.util.containers.ContainerUtil;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import java.util.*;
+
+/**
+ * @author nik
+ */
+public class LibraryPresentationManagerImpl extends LibraryPresentationManager {
+ private Map<LibraryKind, LibraryPresentationProvider<?>> myPresentationProviders;
+
+ private <P extends LibraryProperties> LibraryPresentationProvider<P> getPresentationProvider(LibraryKind kind) {
+ if (myPresentationProviders == null) {
+ final Map<LibraryKind, LibraryPresentationProvider<?>> providers = new HashMap<LibraryKind, LibraryPresentationProvider<?>>();
+ for (LibraryType<?> type : LibraryType.EP_NAME.getExtensions()) {
+ providers.put(type.getKind(), type);
+ }
+ for (LibraryPresentationProvider provider : LibraryPresentationProvider.EP_NAME.getExtensions()) {
+ providers.put(provider.getKind(), provider);
+ }
+ myPresentationProviders = providers;
+ }
+ //noinspection unchecked
+ return (LibraryPresentationProvider<P>)myPresentationProviders.get(kind);
+ }
+
+ @NotNull
+ @Override
+ public Icon getNamedLibraryIcon(@NotNull Library library, @Nullable StructureConfigurableContext context) {
+ final Icon icon = getCustomIcon(library, context);
+ return icon != null ? icon : PlatformIcons.LIBRARY_ICON;
+ }
+
+ @Override
+ public Icon getCustomIcon(@NotNull Library library, StructureConfigurableContext context) {
+ final LibraryKind kind = ((LibraryEx)library).getKind();
+ if (kind != null) {
+ return LibraryType.findByKind(kind).getIcon();
+ }
+ final List<Icon> icons = getCustomIcons(library, context);
+ if (icons.size() == 1) {
+ return icons.get(0);
+ }
+ return null;
+ }
+
+ @NotNull
+ @Override
+ public List<Icon> getCustomIcons(@NotNull Library library, StructureConfigurableContext context) {
+ final VirtualFile[] files = getLibraryFiles(library, context);
+ final List<Icon> icons = new SmartList<Icon>();
+ LibraryDetectionManager.getInstance().processProperties(Arrays.asList(files), new LibraryDetectionManager.LibraryPropertiesProcessor() {
+ @Override
+ public <P extends LibraryProperties> boolean processProperties(@NotNull LibraryKind kind, @NotNull P properties) {
+ final LibraryPresentationProvider<P> provider = getPresentationProvider(kind);
+ if (provider != null) {
+ ContainerUtil.addIfNotNull(icons, provider.getIcon());
+ }
+ return true;
+ }
+ });
+ return icons;
+ }
+
+ @Override
+ public boolean isLibraryOfKind(@NotNull List<VirtualFile> files, @NotNull final LibraryKind kind) {
+ return !LibraryDetectionManager.getInstance().processProperties(files, new LibraryDetectionManager.LibraryPropertiesProcessor() {
+ @Override
+ public <P extends LibraryProperties> boolean processProperties(@NotNull LibraryKind processedKind, @NotNull P properties) {
+ return !kind.equals(processedKind);
+ }
+ });
+ }
+
+ @Override
+ public boolean isLibraryOfKind(@NotNull Library library,
+ @NotNull LibrariesContainer librariesContainer,
+ @NotNull final Set<? extends LibraryKind> acceptedKinds) {
+ final LibraryKind type = ((LibraryEx)library).getKind();
+ if (type != null && acceptedKinds.contains(type)) return true;
+
+ final VirtualFile[] files = librariesContainer.getLibraryFiles(library, OrderRootType.CLASSES);
+ return !LibraryDetectionManager.getInstance().processProperties(Arrays.asList(files), new LibraryDetectionManager.LibraryPropertiesProcessor() {
+ @Override
+ public <P extends LibraryProperties> boolean processProperties(@NotNull LibraryKind processedKind, @NotNull P properties) {
+ return !acceptedKinds.contains(processedKind);
+ }
+ });
+ }
+
+ public static List<LibraryKind> getLibraryKinds(@NotNull Library library, @Nullable StructureConfigurableContext context) {
+ final List<LibraryKind> result = new SmartList<LibraryKind>();
+ final LibraryKind kind = ((LibraryEx)library).getKind();
+ if (kind != null) {
+ result.add(kind);
+ }
+ final VirtualFile[] files = getLibraryFiles(library, context);
+ LibraryDetectionManager.getInstance().processProperties(Arrays.asList(files), new LibraryDetectionManager.LibraryPropertiesProcessor() {
+ @Override
+ public <P extends LibraryProperties> boolean processProperties(@NotNull LibraryKind kind, @NotNull P properties) {
+ result.add(kind);
+ return true;
+ }
+ });
+ return result;
+ }
+
+ @NotNull
+ @Override
+ public List<String> getDescriptions(@NotNull Library library, StructureConfigurableContext context) {
+ final VirtualFile[] files = getLibraryFiles(library, context);
+ return getDescriptions(files, Collections.<LibraryKind>emptySet());
+ }
+
+ @NotNull
+ private static VirtualFile[] getLibraryFiles(@NotNull Library library, @Nullable StructureConfigurableContext context) {
+ if (((LibraryEx)library).isDisposed()) {
+ return VirtualFile.EMPTY_ARRAY;
+ }
+ return context != null ? context.getLibraryFiles(library, OrderRootType.CLASSES) : library.getFiles(OrderRootType.CLASSES);
+ }
+
+ @NotNull
+ @Override
+ public List<String> getDescriptions(@NotNull VirtualFile[] classRoots, final Set<LibraryKind> excludedKinds) {
+ final SmartList<String> result = new SmartList<String>();
+ LibraryDetectionManager.getInstance().processProperties(Arrays.asList(classRoots), new LibraryDetectionManager.LibraryPropertiesProcessor() {
+ @Override
+ public <P extends LibraryProperties> boolean processProperties(@NotNull LibraryKind kind, @NotNull P properties) {
+ if (!excludedKinds.contains(kind)) {
+ final LibraryPresentationProvider<P> provider = getPresentationProvider(kind);
+ if (provider != null) {
+ ContainerUtil.addIfNotNull(result, provider.getDescription(properties));
+ }
+ }
+ return true;
+ }
+ });
+ return result;
+ }
+
+ @Override
+ public List<Library> getLibraries(@NotNull Set<LibraryKind> kinds, @NotNull Project project, @Nullable StructureConfigurableContext context) {
+ List<Library> libraries = new ArrayList<Library>();
+ if (context != null) {
+ Collections.addAll(libraries, context.getProjectLibrariesProvider().getModifiableModel().getLibraries());
+ Collections.addAll(libraries, context.getGlobalLibrariesProvider().getModifiableModel().getLibraries());
+ }
+ else {
+ final LibraryTablesRegistrar registrar = LibraryTablesRegistrar.getInstance();
+ Collections.addAll(libraries, registrar.getLibraryTable(project).getLibraries());
+ Collections.addAll(libraries, registrar.getLibraryTable().getLibraries());
+ }
+
+ final Iterator<Library> iterator = libraries.iterator();
+ while (iterator.hasNext()) {
+ Library library = iterator.next();
+ final List<LibraryKind> libraryKinds = getLibraryKinds(library, context);
+ if (!ContainerUtil.intersects(libraryKinds, kinds)) {
+ iterator.remove();
+ }
+ }
+ return libraries;
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraries/impl/LibraryUsageCollector.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraries/impl/LibraryUsageCollector.java
new file mode 100644
index 0000000..2b8f425
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraries/impl/LibraryUsageCollector.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2000-2012 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.libraries.impl;
+
+import com.intellij.internal.statistic.AbstractApplicationUsagesCollector;
+import com.intellij.internal.statistic.beans.GroupDescriptor;
+import com.intellij.internal.statistic.beans.UsageDescriptor;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.module.ModuleManager;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.roots.ModuleRootManager;
+import com.intellij.openapi.roots.libraries.Library;
+import com.intellij.openapi.roots.libraries.LibraryKind;
+import com.intellij.util.Processor;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * @author nik
+ */
+public class LibraryUsageCollector extends AbstractApplicationUsagesCollector {
+
+ @NonNls private static final String GROUP_ID = "libraries";
+
+ @NotNull
+ @Override
+ public Set<UsageDescriptor> getProjectUsages(@NotNull Project project) {
+ final Set<LibraryKind> usedKinds = new HashSet<LibraryKind>();
+ final Processor<Library> processor = new Processor<Library>() {
+ @Override
+ public boolean process(Library library) {
+ usedKinds.addAll(LibraryPresentationManagerImpl.getLibraryKinds(library, null));
+ return true;
+ }
+ };
+ for (Module module : ModuleManager.getInstance(project).getModules()) {
+ ModuleRootManager.getInstance(module).orderEntries().librariesOnly().forEachLibrary(processor);
+ }
+
+ final HashSet<UsageDescriptor> usageDescriptors = new HashSet<UsageDescriptor>();
+ for (LibraryKind kind : usedKinds) {
+ usageDescriptors.add(new UsageDescriptor(kind.getKindId(), 1));
+ }
+ return usageDescriptors;
+ }
+
+ @NotNull
+ @Override
+ public GroupDescriptor getGroupId() {
+ return GroupDescriptor.create(GROUP_ID); }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/AnnotationsOrderRootTypeUIFactory.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/AnnotationsOrderRootTypeUIFactory.java
new file mode 100644
index 0000000..2e0aeca
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/AnnotationsOrderRootTypeUIFactory.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2000-2009 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.
+ */
+
+/*
+ * User: anna
+ * Date: 26-Dec-2007
+ */
+package com.intellij.openapi.roots.ui.configuration.libraryEditor;
+
+import com.intellij.icons.AllIcons;
+import com.intellij.openapi.fileChooser.FileChooserDescriptor;
+import com.intellij.openapi.project.ProjectBundle;
+import com.intellij.openapi.projectRoots.Sdk;
+import com.intellij.openapi.projectRoots.ui.SdkPathEditor;
+import com.intellij.openapi.roots.AnnotationOrderRootType;
+import com.intellij.openapi.roots.ui.OrderRootTypeUIFactory;
+
+import javax.swing.*;
+
+public class AnnotationsOrderRootTypeUIFactory implements OrderRootTypeUIFactory {
+
+ @Override
+ public Icon getIcon() {
+ return AllIcons.Modules.Annotation;
+ }
+
+ @Override
+ public String getNodeText() {
+ return ProjectBundle.message("sdk.configure.annotations.tab");
+ }
+
+ @Override
+ public SdkPathEditor createPathEditor(Sdk sdk) {
+ return new SdkPathEditor(ProjectBundle.message("sdk.configure.annotations.tab"), AnnotationOrderRootType.getInstance(),
+ new FileChooserDescriptor(false, true, true, false, true, false));
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/ChangeLibraryLevelDialog.form b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/ChangeLibraryLevelDialog.form
new file mode 100644
index 0000000..4a7da9a
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/ChangeLibraryLevelDialog.form
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.intellij.openapi.roots.ui.configuration.libraryEditor.ChangeLibraryLevelDialog">
+ <grid id="27dc6" binding="myMainPanel" layout-manager="GridLayoutManager" row-count="5" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+ <margin top="0" left="0" bottom="0" right="0"/>
+ <constraints>
+ <xy x="20" y="20" width="500" height="124"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <component id="2f5ca" class="javax.swing.JLabel">
+ <constraints>
+ <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties>
+ <labelFor value="d8845"/>
+ <text value="&Name:"/>
+ </properties>
+ </component>
+ <component id="d8845" class="javax.swing.JTextField" binding="myNameField">
+ <constraints>
+ <grid row="1" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="8" fill="1" indent="1" use-parent-layout="false">
+ <preferred-size width="150" height="-1"/>
+ </grid>
+ </constraints>
+ <properties/>
+ </component>
+ <component id="d55d7" class="javax.swing.JCheckBox" binding="myCopyFilesCheckBox">
+ <constraints>
+ <grid row="2" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties>
+ <text value="&Copy library files to:"/>
+ </properties>
+ </component>
+ <component id="594b6" class="com.intellij.openapi.ui.TextFieldWithBrowseButton" binding="myDirectoryForFilesField">
+ <constraints>
+ <grid row="3" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="0" fill="1" indent="1" use-parent-layout="false">
+ <preferred-size width="350" height="-1"/>
+ </grid>
+ </constraints>
+ <properties/>
+ </component>
+ <vspacer id="4db7d">
+ <constraints>
+ <grid row="4" column="0" row-span="1" col-span="1" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/>
+ </constraints>
+ </vspacer>
+ </children>
+ </grid>
+</form>
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/ChangeLibraryLevelDialog.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/ChangeLibraryLevelDialog.java
new file mode 100644
index 0000000..6ec7377
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/ChangeLibraryLevelDialog.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright 2000-2011 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.libraryEditor;
+
+import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.roots.libraries.LibraryTable;
+import com.intellij.openapi.roots.ui.configuration.LibraryTableModifiableModelProvider;
+import com.intellij.openapi.roots.ui.configuration.libraries.LibraryEditingUtil;
+import com.intellij.openapi.ui.DialogWrapper;
+import com.intellij.openapi.ui.TextFieldWithBrowseButton;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.ui.DocumentAdapter;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import javax.swing.event.DocumentEvent;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+
+/**
+ * @author nik
+ */
+public class ChangeLibraryLevelDialog extends DialogWrapper {
+ private JTextField myNameField;
+ private JCheckBox myCopyFilesCheckBox;
+ private TextFieldWithBrowseButton myDirectoryForFilesField;
+ private JPanel myMainPanel;
+ private final boolean myAllowEmptyName;
+ private LibraryTable.ModifiableModel myModifiableModel;
+
+ public ChangeLibraryLevelDialog(JComponent parent,
+ Project project,
+ boolean copy,
+ String libraryName,
+ String path,
+ boolean allowEmptyName,
+ LibraryTableModifiableModelProvider provider) {
+ super(parent, true);
+ myAllowEmptyName = allowEmptyName;
+ final String actionName = copy ? "Copy" : "Move";
+ setTitle(actionName + " Library");
+ myCopyFilesCheckBox.setText(actionName + " library files to:");
+ myCopyFilesCheckBox.setMnemonic(copy ? 'C' : 'M');
+ myCopyFilesCheckBox.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ updateDirectoriesForFilesField();
+ }
+ });
+ myModifiableModel = provider.getModifiableModel();
+ myNameField.setText(libraryName);
+ myDirectoryForFilesField.addBrowseFolderListener("Directory for Library Files", null, project,
+ FileChooserDescriptorFactory.createSingleFolderDescriptor());
+ myDirectoryForFilesField.setText(FileUtil.toSystemDependentName(path));
+ myNameField.selectAll();
+ init();
+ checkName();
+ updateDirectoriesForFilesField();
+ myNameField.getDocument().addDocumentListener(new DocumentAdapter() {
+ @Override
+ protected void textChanged(DocumentEvent e) {
+ checkName();
+ }
+ });
+ }
+
+ private void checkName() {
+ final String name = getLibraryName();
+ if (name.isEmpty()) {
+ if (!myAllowEmptyName) {
+ setErrorText("Library name is not specified");
+ }
+ return;
+ }
+ if (LibraryEditingUtil.libraryAlreadyExists(myModifiableModel, name)) {
+ setErrorText("Library '" + name + "' already exists");
+ return;
+ }
+ setErrorText(null);
+ }
+
+ private void updateDirectoriesForFilesField() {
+ myDirectoryForFilesField.setEnabled(myCopyFilesCheckBox.isSelected());
+ }
+
+ @Override
+ public JComponent getPreferredFocusedComponent() {
+ return myNameField;
+ }
+
+ @Override
+ protected JComponent createCenterPanel() {
+ return myMainPanel;
+ }
+
+ public String getLibraryName() {
+ return myNameField.getText().trim();
+ }
+
+ @Nullable
+ public String getDirectoryForFilesPath() {
+ if (!myCopyFilesCheckBox.isSelected()) {
+ return null;
+ }
+ return FileUtil.toSystemIndependentName(myDirectoryForFilesField.getText());
+ }
+
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/ClassesOrderRootTypeUIFactory.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/ClassesOrderRootTypeUIFactory.java
new file mode 100644
index 0000000..92b110c
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/ClassesOrderRootTypeUIFactory.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2000-2009 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.
+ */
+
+/*
+ * User: anna
+ * Date: 26-Dec-2007
+ */
+package com.intellij.openapi.roots.ui.configuration.libraryEditor;
+
+import com.intellij.icons.AllIcons;
+import com.intellij.openapi.fileChooser.FileChooserDescriptor;
+import com.intellij.openapi.project.ProjectBundle;
+import com.intellij.openapi.projectRoots.Sdk;
+import com.intellij.openapi.projectRoots.ui.SdkPathEditor;
+import com.intellij.openapi.roots.OrderRootType;
+import com.intellij.openapi.roots.ui.OrderRootTypeUIFactory;
+
+import javax.swing.*;
+
+public class ClassesOrderRootTypeUIFactory implements OrderRootTypeUIFactory {
+
+ @Override
+ public SdkPathEditor createPathEditor(Sdk sdk) {
+ return new SdkPathEditor(ProjectBundle.message("sdk.configure.classpath.tab"), OrderRootType.CLASSES, new FileChooserDescriptor(true, true, true, false, true, true));
+ }
+
+ @Override
+ public Icon getIcon() {
+ return AllIcons.Nodes.CompiledClassesFolder;
+ }
+
+ @Override
+ public String getNodeText() {
+ return ProjectBundle.message("library.classes.node");
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/CreateNewLibraryAction.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/CreateNewLibraryAction.java
new file mode 100644
index 0000000..ff1b722
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/CreateNewLibraryAction.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright 2000-2012 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.libraryEditor;
+
+import com.intellij.ide.IdeBundle;
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.application.AccessToken;
+import com.intellij.openapi.application.WriteAction;
+import com.intellij.openapi.project.DumbAwareAction;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.roots.impl.libraries.LibraryEx;
+import com.intellij.openapi.roots.libraries.Library;
+import com.intellij.openapi.roots.libraries.LibraryType;
+import com.intellij.openapi.roots.libraries.LibraryTypeService;
+import com.intellij.openapi.roots.libraries.NewLibraryConfiguration;
+import com.intellij.openapi.roots.ui.configuration.ProjectStructureConfigurable;
+import com.intellij.openapi.roots.ui.configuration.libraries.LibraryEditingUtil;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.BaseLibrariesConfigurable;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.LibrariesModifiableModel;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.ModuleStructureConfigurable;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.ProjectLibrariesConfigurable;
+import com.intellij.openapi.ui.MasterDetailsComponent;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.util.PlatformIcons;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import javax.swing.tree.DefaultMutableTreeNode;
+import javax.swing.tree.TreeNode;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+* @author nik
+*/
+public class CreateNewLibraryAction extends DumbAwareAction {
+ private final @Nullable LibraryType myType;
+ private final BaseLibrariesConfigurable myLibrariesConfigurable;
+ private final Project myProject;
+
+ private CreateNewLibraryAction(@NotNull String text, @Nullable Icon icon, @Nullable LibraryType type, @NotNull BaseLibrariesConfigurable librariesConfigurable, final @NotNull Project project) {
+ super(text, null, icon);
+ myType = type;
+ myLibrariesConfigurable = librariesConfigurable;
+ myProject = project;
+ }
+
+ @Override
+ public void actionPerformed(AnActionEvent e) {
+ Library library =
+ createLibrary(myType, myLibrariesConfigurable.getTree(), myProject, myLibrariesConfigurable.getModelProvider().getModifiableModel());
+ if (library == null) return;
+
+ final BaseLibrariesConfigurable rootConfigurable = ProjectStructureConfigurable.getInstance(myProject).getConfigurableFor(library);
+ final DefaultMutableTreeNode
+ libraryNode = MasterDetailsComponent.findNodeByObject((TreeNode)rootConfigurable.getTree().getModel().getRoot(), library);
+ rootConfigurable.selectNodeInTree(libraryNode);
+ LibraryEditingUtil.showDialogAndAddLibraryToDependencies(library, myProject, true);
+ }
+
+
+ @Nullable
+ public static Library createLibrary(@Nullable final LibraryType type, @NotNull final JComponent parentComponent,
+ @NotNull final Project project, @NotNull final LibrariesModifiableModel modifiableModel) {
+ final NewLibraryConfiguration configuration = createNewLibraryConfiguration(type, parentComponent, project);
+ if (configuration == null) return null;
+ final LibraryType<?> libraryType = configuration.getLibraryType();
+ final Library library = modifiableModel.createLibrary(
+ LibraryEditingUtil.suggestNewLibraryName(modifiableModel, configuration.getDefaultLibraryName()), libraryType != null ? libraryType.getKind() : null);
+
+ final NewLibraryEditor editor = new NewLibraryEditor(libraryType, configuration.getProperties());
+ configuration.addRoots(editor);
+ final Library.ModifiableModel model = library.getModifiableModel();
+ editor.applyTo((LibraryEx.ModifiableModelEx)model);
+ AccessToken token = WriteAction.start();
+ try {
+ model.commit();
+ }
+ finally {
+ token.finish();
+ }
+ return library;
+ }
+
+ @Nullable
+ public static NewLibraryConfiguration createNewLibraryConfiguration(@Nullable LibraryType type, @NotNull JComponent parentComponent, @NotNull Project project) {
+ final NewLibraryConfiguration configuration;
+ final VirtualFile baseDir = project.getBaseDir();
+ if (type != null) {
+ configuration = type.createNewLibrary(parentComponent, baseDir, project);
+ }
+ else {
+ configuration = LibraryTypeService
+ .getInstance().createLibraryFromFiles(new DefaultLibraryRootsComponentDescriptor(), parentComponent, baseDir, null, project);
+ }
+ if (configuration == null) return null;
+ return configuration;
+ }
+
+ public static AnAction[] createActionOrGroup(@NotNull String text, @NotNull BaseLibrariesConfigurable librariesConfigurable, final @NotNull Project project) {
+ final LibraryType<?>[] extensions = LibraryType.EP_NAME.getExtensions();
+ List<LibraryType<?>> suitableTypes = new ArrayList<LibraryType<?>>();
+ if (librariesConfigurable instanceof ProjectLibrariesConfigurable) {
+ final ModuleStructureConfigurable configurable = ModuleStructureConfigurable.getInstance(project);
+ for (LibraryType<?> extension : extensions) {
+ if (!LibraryEditingUtil.getSuitableModules(configurable, extension.getKind(), null).isEmpty()) {
+ suitableTypes.add(extension);
+ }
+ }
+ }
+ else {
+ Collections.addAll(suitableTypes, extensions);
+ }
+
+ if (suitableTypes.isEmpty()) {
+ return new AnAction[]{new CreateNewLibraryAction(text, PlatformIcons.LIBRARY_ICON, null, librariesConfigurable, project)};
+ }
+ List<AnAction> actions = new ArrayList<AnAction>();
+ actions.add(new CreateNewLibraryAction(IdeBundle.message("create.default.library.type.action.name"), PlatformIcons.LIBRARY_ICON, null, librariesConfigurable, project));
+ for (LibraryType<?> type : suitableTypes) {
+ final String actionName = type.getCreateActionName();
+ if (actionName != null) {
+ actions.add(new CreateNewLibraryAction(actionName, type.getIcon(), type, librariesConfigurable, project));
+ }
+ }
+ return actions.toArray(new AnAction[actions.size()]);
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/CreateNewLibraryDialog.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/CreateNewLibraryDialog.java
new file mode 100644
index 0000000..c7d8570
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/CreateNewLibraryDialog.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2000-2012 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.libraryEditor;
+
+import com.intellij.ui.ListCellRendererWrapper;
+import com.intellij.openapi.application.Result;
+import com.intellij.openapi.application.WriteAction;
+import com.intellij.openapi.roots.impl.libraries.LibraryEx;
+import com.intellij.openapi.roots.impl.libraries.LibraryTableBase;
+import com.intellij.openapi.roots.libraries.Library;
+import com.intellij.openapi.roots.libraries.LibraryTable;
+import com.intellij.openapi.roots.libraries.LibraryType;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.StructureConfigurableContext;
+import com.intellij.openapi.ui.ComboBox;
+import com.intellij.util.ui.FormBuilder;
+import org.jetbrains.annotations.NotNull;
+
+import javax.swing.*;
+import java.util.List;
+
+/**
+ * @author nik
+ */
+public class CreateNewLibraryDialog extends LibraryEditorDialogBase {
+ private final StructureConfigurableContext myContext;
+ private final NewLibraryEditor myLibraryEditor;
+ private final ComboBox myLibraryLevelCombobox;
+
+ public CreateNewLibraryDialog(@NotNull JComponent parent, @NotNull StructureConfigurableContext context, @NotNull NewLibraryEditor libraryEditor,
+ @NotNull List<LibraryTable> libraryTables, int selectedTable) {
+ super(parent, new LibraryRootsComponent(context.getProject(), libraryEditor));
+ myContext = context;
+ myLibraryEditor = libraryEditor;
+ final DefaultComboBoxModel model = new DefaultComboBoxModel();
+ for (LibraryTable table : libraryTables) {
+ model.addElement(table);
+ }
+ myLibraryLevelCombobox = new ComboBox(model);
+ myLibraryLevelCombobox.setSelectedIndex(selectedTable);
+ myLibraryLevelCombobox.setRenderer(new ListCellRendererWrapper() {
+ @Override
+ public void customize(JList list, Object value, int index, boolean selected, boolean hasFocus) {
+ if (value instanceof LibraryTable) {
+ setText(((LibraryTable)value).getPresentation().getDisplayName(false));
+ }
+ }
+ });
+ init();
+ }
+
+ @NotNull @Override
+ protected LibraryTable.ModifiableModel getTableModifiableModel() {
+ final LibraryTable selectedTable = (LibraryTable)myLibraryLevelCombobox.getSelectedItem();
+ return myContext.getModifiableLibraryTable(selectedTable);
+ }
+
+ @NotNull
+ public Library createLibrary() {
+ final LibraryTableBase.ModifiableModelEx modifiableModel = (LibraryTableBase.ModifiableModelEx)getTableModifiableModel();
+ final LibraryType<?> type = myLibraryEditor.getType();
+ final Library library = modifiableModel.createLibrary(myLibraryEditor.getName(), type != null ? type.getKind() : null);
+ final LibraryEx.ModifiableModelEx model = (LibraryEx.ModifiableModelEx)library.getModifiableModel();
+ myLibraryEditor.applyTo(model);
+ new WriteAction() {
+ @Override
+ protected void run(final Result result) {
+ model.commit();
+ }
+ }.execute();
+ return library;
+ }
+
+ @Override
+ protected void addNorthComponents(FormBuilder formBuilder) {
+ formBuilder.addLabeledComponent("&Level:", myLibraryLevelCombobox);
+ }
+
+ @Override
+ protected boolean shouldCheckName(String newName) {
+ return true;
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/DefaultLibraryRootsComponentDescriptor.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/DefaultLibraryRootsComponentDescriptor.java
new file mode 100644
index 0000000..0763407
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/DefaultLibraryRootsComponentDescriptor.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright 2000-2012 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.libraryEditor;
+
+import com.intellij.codeInsight.ExternalAnnotationsManager;
+import com.intellij.openapi.fileTypes.StdFileTypes;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.progress.ProgressIndicator;
+import com.intellij.openapi.project.ProjectBundle;
+import com.intellij.openapi.projectRoots.ui.Util;
+import com.intellij.openapi.roots.AnnotationOrderRootType;
+import com.intellij.openapi.roots.JavadocOrderRootType;
+import com.intellij.openapi.roots.OrderRootType;
+import com.intellij.openapi.roots.libraries.ui.*;
+import com.intellij.openapi.roots.ui.OrderRootTypeUIFactory;
+import com.intellij.openapi.roots.ui.configuration.PathUIUtils;
+import com.intellij.openapi.vfs.VfsUtilCore;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.openapi.vfs.VirtualFileVisitor;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import java.util.*;
+
+/**
+ * @author nik
+ */
+public class DefaultLibraryRootsComponentDescriptor extends LibraryRootsComponentDescriptor {
+ @Override
+ public OrderRootTypePresentation getRootTypePresentation(@NotNull OrderRootType type) {
+ return getDefaultPresentation(type);
+ }
+
+ @NotNull
+ @Override
+ public List<? extends AttachRootButtonDescriptor> createAttachButtons() {
+ return Arrays.asList(new AttachUrlJavadocDescriptor());
+ }
+
+ @NotNull
+ @Override
+ public List<? extends RootDetector> getRootDetectors() {
+ return Arrays.asList(new FileTypeBasedRootFilter(OrderRootType.CLASSES, false, StdFileTypes.CLASS, "classes"),
+ new FileTypeBasedRootFilter(OrderRootType.CLASSES, true, StdFileTypes.CLASS, "jar directory"),
+ PathUIUtils.JAVA_SOURCE_ROOT_DETECTOR,
+ new FileTypeBasedRootFilter(OrderRootType.SOURCES, true, StdFileTypes.JAVA, "source archive directory"),
+ new JavadocRootDetector(),
+ new AnnotationsRootFilter());
+ }
+
+ public static OrderRootTypePresentation getDefaultPresentation(OrderRootType type) {
+ final OrderRootTypeUIFactory factory = OrderRootTypeUIFactory.FACTORY.getByKey(type);
+ return new OrderRootTypePresentation(factory.getNodeText(), factory.getIcon());
+ }
+
+ private static class JavadocRootDetector extends RootDetector {
+ private JavadocRootDetector() {
+ super(JavadocOrderRootType.getInstance(), false, "JavaDocs");
+ }
+
+ @NotNull
+ @Override
+ public Collection<VirtualFile> detectRoots(@NotNull VirtualFile rootCandidate, @NotNull ProgressIndicator progressIndicator) {
+ List<VirtualFile> result = new ArrayList<VirtualFile>();
+ collectJavadocRoots(rootCandidate, result, progressIndicator);
+ return result;
+ }
+
+ private static void collectJavadocRoots(VirtualFile file, final List<VirtualFile> result, final ProgressIndicator progressIndicator) {
+ VfsUtilCore.visitChildrenRecursively(file, new VirtualFileVisitor() {
+ @Override
+ public boolean visitFile(@NotNull VirtualFile file) {
+ progressIndicator.checkCanceled();
+ if (file.isDirectory() && file.findChild("allclasses-frame.html") != null && file.findChild("allclasses-noframe.html") != null) {
+ result.add(file);
+ return false;
+ }
+ return true;
+ }
+ });
+ }
+ }
+
+ private static class AnnotationsRootFilter extends FileTypeBasedRootFilter {
+ private AnnotationsRootFilter() {
+ super(AnnotationOrderRootType.getInstance(), false, StdFileTypes.XML, "external annotations");
+ }
+
+ @Override
+ protected boolean isFileAccepted(VirtualFile virtualFile) {
+ return super.isFileAccepted(virtualFile) && virtualFile.getName().equals(ExternalAnnotationsManager.ANNOTATIONS_XML);
+ }
+ }
+
+ private static class AttachUrlJavadocDescriptor extends AttachRootButtonDescriptor {
+ private AttachUrlJavadocDescriptor() {
+ super(JavadocOrderRootType.getInstance(), ProjectBundle.message("module.libraries.javadoc.url.button"));
+ }
+
+ @Override
+ public VirtualFile[] selectFiles(@NotNull JComponent parent,
+ @Nullable VirtualFile initialSelection,
+ @Nullable Module contextModule,
+ @NotNull LibraryEditor libraryEditor) {
+ final VirtualFile vFile = Util.showSpecifyJavadocUrlDialog(parent);
+ if (vFile != null) {
+ return new VirtualFile[]{vFile};
+ }
+ return VirtualFile.EMPTY_ARRAY;
+ }
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/EditExistingLibraryDialog.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/EditExistingLibraryDialog.java
new file mode 100644
index 0000000..5723938
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/EditExistingLibraryDialog.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2000-2010 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.libraryEditor;
+
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.roots.libraries.Library;
+import com.intellij.openapi.roots.libraries.LibraryTable;
+import com.intellij.openapi.roots.libraries.LibraryTablePresentation;
+import com.intellij.openapi.roots.ui.configuration.LibraryTableModifiableModelProvider;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.LibrariesModifiableModel;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.StructureConfigurableContext;
+import com.intellij.openapi.util.Comparing;
+import com.intellij.openapi.util.Disposer;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.awt.*;
+
+/**
+ * @author nik
+ */
+public class EditExistingLibraryDialog extends LibraryEditorDialogBase {
+ private ExistingLibraryEditor myLibraryEditor;
+ private boolean myCommitChanges;
+ private LibraryTable.ModifiableModel myTableModifiableModel;
+
+ public static EditExistingLibraryDialog createDialog(Component parent,
+ LibraryTableModifiableModelProvider modelProvider,
+ Library library,
+ @Nullable Project project,
+ LibraryTablePresentation presentation,
+ StructureConfigurableContext context) {
+ LibraryTable.ModifiableModel modifiableModel = modelProvider.getModifiableModel();
+ boolean commitChanges = false;
+ ExistingLibraryEditor libraryEditor;
+ if (modifiableModel instanceof LibrariesModifiableModel) {
+ libraryEditor = ((LibrariesModifiableModel)modifiableModel).getLibraryEditor(library);
+ }
+ else {
+ libraryEditor = new ExistingLibraryEditor(library, context);
+ commitChanges = true;
+ }
+ return new EditExistingLibraryDialog(parent, modifiableModel, project, libraryEditor, commitChanges, presentation, context);
+ }
+
+ private EditExistingLibraryDialog(Component parent,
+ LibraryTable.ModifiableModel tableModifiableModel,
+ @Nullable Project project,
+ ExistingLibraryEditor libraryEditor,
+ boolean commitChanges,
+ LibraryTablePresentation presentation, StructureConfigurableContext context) {
+ super(parent, new LibraryRootsComponent(project, libraryEditor));
+ setTitle("Configure " + presentation.getDisplayName(false));
+ myTableModifiableModel = tableModifiableModel;
+ myLibraryEditor = libraryEditor;
+ myCommitChanges = commitChanges;
+ if (commitChanges) {
+ Disposer.register(getDisposable(), libraryEditor);
+ }
+ context.addLibraryEditorListener(new LibraryEditorListener() {
+ @Override
+ public void libraryRenamed(@NotNull Library library, String oldName, String newName) {
+ if (library.equals(myLibraryEditor.getLibrary())) {
+ myNameField.setText(newName);
+ }
+ }
+ }, getDisposable());
+ init();
+ }
+
+ @Override
+ protected boolean validateAndApply() {
+ if (!super.validateAndApply()) {
+ return false;
+ }
+
+ if (myCommitChanges) {
+ myLibraryEditor.commit();
+ }
+ return true;
+ }
+
+ @Override
+ protected LibraryTable.ModifiableModel getTableModifiableModel() {
+ return myTableModifiableModel;
+ }
+
+ @Override
+ protected boolean shouldCheckName(String newName) {
+ return !Comparing.equal(newName, getLibraryRootsComponent().getLibraryEditor().getName());
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/ExistingLibraryEditor.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/ExistingLibraryEditor.java
new file mode 100644
index 0000000..996a546
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/ExistingLibraryEditor.java
@@ -0,0 +1,216 @@
+/*
+ * Copyright 2000-2012 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.libraryEditor;
+
+import com.intellij.openapi.Disposable;
+import com.intellij.openapi.roots.OrderRootType;
+import com.intellij.openapi.roots.impl.libraries.LibraryEx;
+import com.intellij.openapi.roots.libraries.*;
+import com.intellij.openapi.util.Disposer;
+import com.intellij.openapi.util.Pair;
+import com.intellij.openapi.vfs.VirtualFile;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.Arrays;
+import java.util.Collection;
+
+public class ExistingLibraryEditor extends LibraryEditorBase implements Disposable {
+ private final Library myLibrary;
+ private final LibraryEditorListener myListener;
+ private String myLibraryName = null;
+ private LibraryProperties myLibraryProperties;
+ private LibraryProperties myDetectedLibraryProperties;
+ private Library.ModifiableModel myModel = null;
+ private LibraryType<?> myDetectedType;
+ private boolean myDetectedTypeComputed;
+
+ public ExistingLibraryEditor(@NotNull Library library, @Nullable LibraryEditorListener listener) {
+ myLibrary = library;
+ myListener = listener;
+ }
+
+ public Library getLibrary() {
+ return myLibrary;
+ }
+
+ @Override
+ public String getName() {
+ if (myLibraryName != null) {
+ return myLibraryName;
+ }
+ return myLibrary.getName();
+ }
+
+ @Override
+ public LibraryType<?> getType() {
+ final LibraryKind kind = ((LibraryEx)myLibrary).getKind();
+ if (kind != null) {
+ return LibraryType.findByKind(kind);
+ }
+ return detectType();
+ }
+
+ @Override
+ public void setType(@NotNull LibraryType<?> type) {
+ ((LibraryEx.ModifiableModelEx)getModel()).setKind(type.getKind());
+ }
+
+ private LibraryType detectType() {
+ if (!myDetectedTypeComputed) {
+ final Pair<LibraryType<?>,LibraryProperties<?>> pair = LibraryDetectionManager.getInstance().detectType(Arrays.asList(getFiles(OrderRootType.CLASSES)));
+ if (pair != null) {
+ myDetectedType = pair.getFirst();
+ myDetectedLibraryProperties = pair.getSecond();
+ }
+ myDetectedTypeComputed = true;
+ }
+ return myDetectedType;
+ }
+
+ @Override
+ public LibraryProperties getProperties() {
+ final LibraryType type = getType();
+ if (type == null) return null;
+
+ if (myDetectedType != null) {
+ return myDetectedLibraryProperties;
+ }
+
+ if (myLibraryProperties == null) {
+ myLibraryProperties = type.getKind().createDefaultProperties();
+ //noinspection unchecked
+ myLibraryProperties.loadState(getOriginalProperties().getState());
+ }
+ return myLibraryProperties;
+ }
+
+ @Override
+ public void setProperties(LibraryProperties properties) {
+ myLibraryProperties = properties;
+ }
+
+ private LibraryProperties getOriginalProperties() {
+ return ((LibraryEx)myLibrary).getProperties();
+ }
+
+ @Override
+ public void dispose() {
+ if (myModel != null) {
+ // dispose if wasn't committed
+ Disposer.dispose(myModel);
+ }
+ }
+
+ @Override
+ public String[] getUrls(OrderRootType rootType) {
+ if (myModel != null) {
+ return myModel.getUrls(rootType);
+ }
+ return myLibrary.getUrls(rootType);
+ }
+
+ @Override
+ public VirtualFile[] getFiles(OrderRootType rootType) {
+ if (myModel != null) {
+ return myModel.getFiles(rootType);
+ }
+ return myLibrary.getFiles(rootType);
+ }
+
+ @Override
+ public void setName(String name) {
+ String oldName = getModel().getName();
+ myLibraryName = name;
+ getModel().setName(name);
+ if (myListener != null) {
+ myListener.libraryRenamed(myLibrary, oldName, name);
+ }
+ }
+
+ @Override
+ public void addRoot(VirtualFile file, OrderRootType rootType) {
+ getModel().addRoot(file, rootType);
+ }
+
+ @Override
+ public void addRoot(String url, OrderRootType rootType) {
+ getModel().addRoot(url, rootType);
+ }
+
+ @Override
+ public void addJarDirectory(VirtualFile file, boolean recursive, OrderRootType rootType) {
+ getModel().addJarDirectory(file, recursive, rootType);
+ }
+
+ @Override
+ public void addJarDirectory(String url, boolean recursive, OrderRootType rootType) {
+ getModel().addJarDirectory(url, recursive, rootType);
+ }
+
+ @Override
+ public void removeRoot(String url, OrderRootType rootType) {
+ while (getModel().removeRoot(url, rootType)) ;
+ }
+
+ public void commit() {
+ if (myModel != null) {
+ if (myLibraryProperties != null) {
+ ((LibraryEx.ModifiableModelEx)myModel).setProperties(myLibraryProperties);
+ }
+ myModel.commit();
+ myModel = null;
+ myLibraryName = null;
+ myLibraryProperties = null;
+ }
+ }
+
+ public Library.ModifiableModel getModel() {
+ if (myModel == null) {
+ myModel = myLibrary.getModifiableModel();
+ }
+ return myModel;
+ }
+
+ @Override
+ public boolean hasChanges() {
+ if (myModel != null && myModel.isChanged()) {
+ return true;
+ }
+ return myLibraryProperties != null && !myLibraryProperties.equals(getOriginalProperties());
+ }
+
+ @Override
+ public boolean isJarDirectory(String url, OrderRootType rootType) {
+ if (myModel != null) {
+ return myModel.isJarDirectory(url, rootType);
+ }
+ return myLibrary.isJarDirectory(url, rootType);
+ }
+
+ @Override
+ public boolean isValid(final String url, final OrderRootType orderRootType) {
+ if (myModel != null) {
+ return myModel.isValid(url, orderRootType);
+ }
+ return myLibrary.isValid(url, orderRootType);
+ }
+
+ @Override
+ public Collection<OrderRootType> getOrderRootTypes() {
+ return Arrays.asList(OrderRootType.getAllTypes());
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/ItemElement.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/ItemElement.java
new file mode 100644
index 0000000..c78bef1
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/ItemElement.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2000-2009 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.libraryEditor;
+
+import com.intellij.ide.util.treeView.NodeDescriptor;
+import com.intellij.openapi.roots.OrderRootType;
+
+
+class ItemElement extends LibraryTableTreeContentElement {
+ private final OrderRootTypeElement myParent;
+ private final String myUrl;
+ private final OrderRootType myRootType;
+ private final boolean myIsJarDirectory;
+ private final boolean myValid;
+
+ public ItemElement(OrderRootTypeElement parent, String url, OrderRootType rootType, final boolean isJarDirectory,
+ boolean isValid) {
+ myParent = parent;
+ myUrl = url;
+ myRootType = rootType;
+ myIsJarDirectory = isJarDirectory;
+ myValid = isValid;
+ }
+
+ public OrderRootTypeElement getParent() {
+ return myParent;
+ }
+
+ @Override
+ public NodeDescriptor createDescriptor(final NodeDescriptor parentDescriptor, final LibraryRootsComponent parentEditor) {
+ return new ItemElementDescriptor(parentDescriptor, this);
+ }
+
+ public String getUrl() {
+ return myUrl;
+ }
+
+ public boolean isJarDirectory() {
+ return myIsJarDirectory;
+ }
+
+ public boolean isValid() {
+ return myValid;
+ }
+
+ public OrderRootType getRootType() {
+ return myRootType;
+ }
+
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof ItemElement)) return false;
+
+ final ItemElement itemElement = (ItemElement)o;
+
+ if (!myParent.equals(itemElement.myParent)) return false;
+ if (!myRootType.equals(itemElement.myRootType)) return false;
+ if (!myUrl.equals(itemElement.myUrl)) return false;
+
+ return true;
+ }
+
+ public int hashCode() {
+ int result;
+ result = myParent.hashCode();
+ result = 29 * result + myUrl.hashCode();
+ result = 29 * result + myRootType.hashCode();
+ return result;
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/ItemElementDescriptor.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/ItemElementDescriptor.java
new file mode 100644
index 0000000..b8e5924
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/ItemElementDescriptor.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright 2000-2012 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.libraryEditor;
+
+import com.intellij.icons.AllIcons;
+import com.intellij.ide.IconUtilEx;
+import com.intellij.ide.util.treeView.NodeDescriptor;
+import com.intellij.openapi.vfs.JarFileSystem;
+import com.intellij.openapi.vfs.LocalFileSystem;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.openapi.vfs.VirtualFileManager;
+import com.intellij.openapi.vfs.ex.http.HttpFileSystem;
+import com.intellij.ui.JBColor;
+import com.intellij.util.PlatformIcons;
+import com.intellij.util.ui.UIUtil;
+
+import javax.swing.*;
+import java.awt.*;
+import java.io.File;
+
+class ItemElementDescriptor extends NodeDescriptor<ItemElement> {
+ private final ItemElement myElement;
+
+ public ItemElementDescriptor(NodeDescriptor parentDescriptor, ItemElement element) {
+ super(null, parentDescriptor);
+ myElement = element;
+ final String url = element.getUrl();
+ myName = getPresentablePath(url).replace('/', File.separatorChar);
+ setIcon(getIconForUrl(url, element.isValid(), element.isJarDirectory()));
+ }
+
+ @Override
+ public boolean update() {
+ Color color = myElement.isValid()? UIUtil.getListForeground() : JBColor.RED;
+ final boolean changes = !color.equals(myColor);
+ myColor = color;
+ return changes;
+ }
+
+ @Override
+ public ItemElement getElement() {
+ return myElement;
+ }
+
+ private static Icon getIconForUrl(final String url, final boolean isValid, final boolean isJarDirectory) {
+ final Icon icon;
+ if (isValid) {
+ VirtualFile presentableFile;
+ if (isJarFileRoot(url)) {
+ presentableFile = LocalFileSystem.getInstance().findFileByPath(getPresentablePath(url));
+ }
+ else {
+ presentableFile = VirtualFileManager.getInstance().findFileByUrl(url);
+ }
+ if (presentableFile != null && presentableFile.isValid()) {
+ if (presentableFile.getFileSystem() instanceof HttpFileSystem) {
+ icon = PlatformIcons.WEB_ICON;
+ }
+ else {
+ if (presentableFile.isDirectory()) {
+ if (isJarDirectory) {
+ icon = AllIcons.Nodes.JarDirectory;
+ }
+ else {
+ icon = PlatformIcons.DIRECTORY_CLOSED_ICON;
+ }
+ }
+ else {
+ icon = IconUtilEx.getIcon(presentableFile, 0, null);
+ }
+ }
+ }
+ else {
+ icon = AllIcons.Nodes.PpInvalid;
+ }
+ }
+ else {
+ icon = AllIcons.Nodes.PpInvalid;
+ }
+ return icon;
+ }
+
+ static String getPresentablePath(final String url) {
+ String presentablePath = VirtualFileManager.extractPath(url);
+ if (isJarFileRoot(url)) {
+ presentablePath = presentablePath.substring(0, presentablePath.length() - JarFileSystem.JAR_SEPARATOR.length());
+ }
+ return presentablePath;
+ }
+
+ private static boolean isJarFileRoot(final String url) {
+ return VirtualFileManager.extractPath(url).endsWith(JarFileSystem.JAR_SEPARATOR);
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/JavadocOrderRootTypeUIFactory.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/JavadocOrderRootTypeUIFactory.java
new file mode 100644
index 0000000..802df79
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/JavadocOrderRootTypeUIFactory.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2000-2012 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.
+ */
+
+/*
+ * User: anna
+ * Date: 26-Dec-2007
+ */
+package com.intellij.openapi.roots.ui.configuration.libraryEditor;
+
+import com.intellij.icons.AllIcons;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.actionSystem.CustomShortcutSet;
+import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory;
+import com.intellij.openapi.project.ProjectBundle;
+import com.intellij.openapi.projectRoots.Sdk;
+import com.intellij.openapi.projectRoots.SdkType;
+import com.intellij.openapi.projectRoots.ui.SdkPathEditor;
+import com.intellij.openapi.projectRoots.ui.Util;
+import com.intellij.openapi.roots.JavadocOrderRootType;
+import com.intellij.openapi.roots.ui.OrderRootTypeUIFactory;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.ui.AnActionButton;
+import com.intellij.ui.AnActionButtonUpdater;
+import com.intellij.ui.ToolbarDecorator;
+import com.intellij.util.IconUtil;
+
+import javax.swing.*;
+
+public class JavadocOrderRootTypeUIFactory implements OrderRootTypeUIFactory {
+
+ @Override
+ public SdkPathEditor createPathEditor(Sdk sdk) {
+ return new JavadocPathsEditor(sdk);
+ }
+
+ @Override
+ public Icon getIcon() {
+ return AllIcons.Nodes.JavaDocFolder;
+ }
+
+ @Override
+ public String getNodeText() {
+ return ProjectBundle.message("library.javadocs.node");
+ }
+
+ static class JavadocPathsEditor extends SdkPathEditor {
+ private final Sdk mySdk;
+
+ public JavadocPathsEditor(Sdk sdk) {
+ super(ProjectBundle.message("sdk.configure.javadoc.tab"),
+ JavadocOrderRootType.getInstance(),
+ FileChooserDescriptorFactory.createMultipleJavaPathDescriptor());
+ mySdk = sdk;
+ }
+
+ @Override
+ protected void addToolbarButtons(ToolbarDecorator toolbarDecorator) {
+ AnActionButton specifyUrlButton = new AnActionButton(ProjectBundle.message("sdk.paths.specify.url.button"), IconUtil.getAddLinkIcon()) {
+ @Override
+ public void actionPerformed(AnActionEvent e) {
+ onSpecifyUrlButtonClicked();
+ }
+ };
+ specifyUrlButton.setShortcut(CustomShortcutSet.fromString("alt S"));
+ specifyUrlButton.addCustomUpdater(new AnActionButtonUpdater() {
+ @Override
+ public boolean isEnabled(AnActionEvent e) {
+ return myEnabled && !isUrlInserted();
+ }
+ });
+ toolbarDecorator.addExtraAction(specifyUrlButton);
+ }
+
+ private void onSpecifyUrlButtonClicked() {
+ final String defaultDocsUrl = mySdk == null ? "" : StringUtil.notNullize(((SdkType) mySdk.getSdkType()).getDefaultDocumentationUrl(mySdk), "");
+ VirtualFile virtualFile = Util.showSpecifyJavadocUrlDialog(myPanel, defaultDocsUrl);
+ if(virtualFile != null){
+ addElement(virtualFile);
+ setModified(true);
+ requestDefaultFocus();
+ setSelectedRoots(new Object[]{virtualFile});
+ }
+ }
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/LibraryEditorBase.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/LibraryEditorBase.java
new file mode 100644
index 0000000..a9c62e4
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/LibraryEditorBase.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2000-2011 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.libraryEditor;
+
+import com.intellij.openapi.roots.OrderRootType;
+import com.intellij.openapi.roots.libraries.LibraryProperties;
+import com.intellij.openapi.roots.libraries.LibraryType;
+import com.intellij.openapi.roots.libraries.ui.OrderRoot;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * @author nik
+ */
+public abstract class LibraryEditorBase implements LibraryEditor {
+ @Override
+ public void removeAllRoots() {
+ final List<OrderRootType> types = new ArrayList<OrderRootType>(getOrderRootTypes());
+ for (OrderRootType type : types) {
+ final String[] urls = getUrls(type);
+ for (String url : urls) {
+ removeRoot(url, type);
+ }
+ }
+ }
+
+ protected abstract Collection<OrderRootType> getOrderRootTypes();
+
+ public abstract void setProperties(LibraryProperties properties);
+
+ public abstract void setType(@NotNull LibraryType<?> type);
+
+ @Override
+ public void addRoots(Collection<? extends OrderRoot> roots) {
+ for (OrderRoot root : roots) {
+ if (root.isJarDirectory()) {
+ addJarDirectory(root.getFile(), false, root.getType());
+ }
+ else {
+ addRoot(root.getFile(), root.getType());
+ }
+ }
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/LibraryEditorDialogBase.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/LibraryEditorDialogBase.java
new file mode 100644
index 0000000..bc59d6f
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/LibraryEditorDialogBase.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright 2000-2010 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.libraryEditor;
+
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.project.ProjectBundle;
+import com.intellij.openapi.roots.impl.ModuleLibraryTable;
+import com.intellij.openapi.roots.libraries.LibraryTable;
+import com.intellij.openapi.roots.ui.configuration.libraries.LibraryEditingUtil;
+import com.intellij.openapi.ui.DialogWrapper;
+import com.intellij.openapi.ui.Messages;
+import com.intellij.openapi.util.Disposer;
+import com.intellij.util.ui.FormBuilder;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import java.awt.*;
+
+/**
+* @author nik
+*/
+public abstract class LibraryEditorDialogBase extends DialogWrapper {
+ protected JTextField myNameField;
+ private LibraryRootsComponent myLibraryRootsComponent;
+
+ public LibraryEditorDialogBase(final Component parent, final LibraryRootsComponent libraryRootsComponent) {
+ super(parent, true);
+ myLibraryRootsComponent = libraryRootsComponent;
+ libraryRootsComponent.resetProperties();
+ setTitle(ProjectBundle.message("library.configure.title"));
+ Disposer.register(getDisposable(), myLibraryRootsComponent);
+ }
+
+ public void setContextModule(Module module) {
+ myLibraryRootsComponent.setContextModule(module);
+ }
+
+ @Override
+ protected String getDimensionServiceKey() {
+ return "#com.intellij.openapi.roots.ui.configuration.libraryEditor.LibraryEditorDialog";
+ }
+
+ @Override
+ public JComponent getPreferredFocusedComponent() {
+ return myNameField;
+ }
+
+ @Override
+ protected final void doOKAction() {
+ if (!validateAndApply()) {
+ return;
+ }
+ super.doOKAction();
+ }
+
+ protected boolean validateAndApply() {
+ String newName = myNameField.getText().trim();
+ if (newName.length() == 0) {
+ newName = null;
+ }
+ if (shouldCheckName(newName)) {
+ final LibraryTable.ModifiableModel tableModifiableModel = getTableModifiableModel();
+ if (tableModifiableModel != null && !(tableModifiableModel instanceof ModuleLibraryTable)) {
+ if (newName == null) {
+ Messages.showErrorDialog(ProjectBundle.message("library.name.not.specified.error", newName), ProjectBundle.message("library.name.not.specified.title"));
+ return false;
+ }
+ if (LibraryEditingUtil.libraryAlreadyExists(tableModifiableModel, newName)) {
+ Messages.showErrorDialog(ProjectBundle.message("library.name.already.exists.error", newName), ProjectBundle.message("library.name.already.exists.title"));
+ return false;
+ }
+ }
+ myLibraryRootsComponent.renameLibrary(newName);
+ }
+ myLibraryRootsComponent.applyProperties();
+ return true;
+ }
+
+ protected abstract boolean shouldCheckName(String newName);
+
+ @Nullable
+ protected LibraryTable.ModifiableModel getTableModifiableModel() {
+ return null;
+ }
+
+ protected LibraryRootsComponent getLibraryRootsComponent() {
+ return myLibraryRootsComponent;
+ }
+
+ @Override
+ protected JComponent createNorthPanel() {
+ String currentName = myLibraryRootsComponent.getLibraryEditor().getName();
+ myNameField = new JTextField(currentName);
+ myNameField.selectAll();
+
+ FormBuilder formBuilder = FormBuilder.createFormBuilder().addLabeledComponent("&Name:", myNameField);
+ addNorthComponents(formBuilder);
+ return formBuilder.addVerticalGap(10).getPanel();
+ }
+
+ protected void addNorthComponents(FormBuilder formBuilder) {
+ }
+
+ @Override
+ protected JComponent createCenterPanel() {
+ return myLibraryRootsComponent.getComponent();
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/LibraryEditorListener.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/LibraryEditorListener.java
new file mode 100644
index 0000000..a42929d
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/LibraryEditorListener.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2000-2009 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.libraryEditor;
+
+import com.intellij.openapi.roots.libraries.Library;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.EventListener;
+
+/**
+ * @author nik
+ */
+public interface LibraryEditorListener extends EventListener {
+ void libraryRenamed(@NotNull Library library, String oldName, String newName);
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/LibraryNameAndLevelPanel.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/LibraryNameAndLevelPanel.java
new file mode 100644
index 0000000..c55904c
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/LibraryNameAndLevelPanel.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2000-2012 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.libraryEditor;
+
+import com.intellij.ui.ListCellRendererWrapper;
+import com.intellij.openapi.project.ProjectBundle;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.LibrariesContainer;
+import com.intellij.ui.CollectionComboBoxModel;
+import com.intellij.util.ui.FormBuilder;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import java.util.*;
+
+/**
+ * @author Dmitry Avdeev
+ */
+public class LibraryNameAndLevelPanel {
+ private JTextField myLibraryNameField;
+ private JComboBox myLevelComboBox;
+ private String myDefaultLibraryName;
+
+ public LibraryNameAndLevelPanel(@NotNull FormBuilder formBuilder, @NotNull String libraryName, @Nullable LibrariesContainer.LibraryLevel level) {
+ this(formBuilder, libraryName, Arrays.asList(LibrariesContainer.LibraryLevel.values()), level);
+ }
+
+ public LibraryNameAndLevelPanel(@NotNull FormBuilder formBuilder, @NotNull String libraryName, @NotNull List<LibrariesContainer.LibraryLevel> availableLevels,
+ @Nullable LibrariesContainer.LibraryLevel level) {
+ myLibraryNameField = new JTextField(25);
+ formBuilder.addLabeledComponent("&Name:", myLibraryNameField);
+ myLibraryNameField.setText(libraryName);
+ myLevelComboBox = new JComboBox();
+ if (level != null && !availableLevels.isEmpty()) {
+ formBuilder.addLabeledComponent("&Level:", myLevelComboBox);
+ final Map<LibrariesContainer.LibraryLevel, String> levels = new HashMap<LibrariesContainer.LibraryLevel, String>();
+ levels.put(LibrariesContainer.LibraryLevel.GLOBAL, ProjectBundle.message("combobox.item.global.library"));
+ levels.put(LibrariesContainer.LibraryLevel.PROJECT, ProjectBundle.message("combobox.item.project.library"));
+ levels.put(LibrariesContainer.LibraryLevel.MODULE, ProjectBundle.message("combobox.item.module.library"));
+ myLevelComboBox.setRenderer(new ListCellRendererWrapper() {
+ @Override
+ public void customize(JList list, Object value, int index, boolean selected, boolean hasFocus) {
+ if (value instanceof LibrariesContainer.LibraryLevel) {
+ final LibrariesContainer.LibraryLevel level = (LibrariesContainer.LibraryLevel)value;
+ setText(levels.get(level));
+ }
+ }
+ });
+ myLevelComboBox.setModel(new CollectionComboBoxModel(availableLevels, level));
+ }
+ }
+
+ public String getLibraryName() {
+ return myLibraryNameField.getText();
+ }
+
+ public LibrariesContainer.LibraryLevel getLibraryLevel() {
+ return (LibrariesContainer.LibraryLevel)myLevelComboBox.getSelectedItem();
+ }
+
+ public JTextField getLibraryNameField() {
+ return myLibraryNameField;
+ }
+
+ public JComboBox getLevelComboBox() {
+ return myLevelComboBox;
+ }
+
+ public void setDefaultName(@NotNull String defaultLibraryName) {
+ if (myDefaultLibraryName != null && myDefaultLibraryName.equals(getLibraryName())) {
+ myLibraryNameField.setText(defaultLibraryName);
+ }
+ myDefaultLibraryName = defaultLibraryName;
+ }
+
+ public static FormBuilder createFormBuilder() {
+ return FormBuilder.createFormBuilder();
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/LibraryPropertiesEditorBase.form b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/LibraryPropertiesEditorBase.form
new file mode 100644
index 0000000..342f8c1
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/LibraryPropertiesEditorBase.form
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.intellij.openapi.roots.ui.configuration.libraryEditor.LibraryPropertiesEditorBase">
+ <grid id="27dc6" binding="myMainPanel" layout-manager="GridLayoutManager" row-count="1" column-count="3" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+ <margin top="0" left="0" bottom="0" right="0"/>
+ <constraints>
+ <xy x="20" y="20" width="500" height="29"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <component id="969a8" class="javax.swing.JLabel" binding="myDescriptionLabel">
+ <constraints>
+ <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties>
+ <text value="Label"/>
+ </properties>
+ </component>
+ <hspacer id="3d26a">
+ <constraints>
+ <grid row="0" column="1" row-span="1" col-span="1" vsize-policy="1" hsize-policy="6" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
+ </constraints>
+ </hspacer>
+ <component id="979f7" class="javax.swing.JButton" binding="myEditButton">
+ <constraints>
+ <grid row="0" column="2" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties>
+ <text value="&Edit..."/>
+ </properties>
+ </component>
+ </children>
+ </grid>
+</form>
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/LibraryPropertiesEditorBase.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/LibraryPropertiesEditorBase.java
new file mode 100644
index 0000000..7467d12
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/LibraryPropertiesEditorBase.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2000-2011 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.libraryEditor;
+
+import com.intellij.openapi.roots.libraries.LibraryProperties;
+import com.intellij.openapi.roots.libraries.LibraryType;
+import com.intellij.openapi.roots.libraries.ui.LibraryEditorComponent;
+import com.intellij.openapi.roots.libraries.ui.LibraryPropertiesEditor;
+import com.intellij.util.ui.UIUtil;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+
+/**
+ * @author nik
+ */
+public abstract class LibraryPropertiesEditorBase<P extends LibraryProperties, T extends LibraryType<P>> extends LibraryPropertiesEditor {
+ private JPanel myMainPanel;
+ private JLabel myDescriptionLabel;
+ private JButton myEditButton;
+ private boolean myModified;
+ protected final LibraryEditorComponent<P> myEditorComponent;
+ protected final T myLibraryType;
+
+ protected LibraryPropertiesEditorBase(final LibraryEditorComponent<P> editorComponent,
+ T libraryType, @Nullable String editButtonText) {
+ myEditorComponent = editorComponent;
+ myLibraryType = libraryType;
+ updateDescription();
+ if (editButtonText != null) {
+ myEditButton.setText(UIUtil.replaceMnemonicAmpersand(editButtonText));
+ }
+ myEditButton.setVisible(!myEditorComponent.isNewLibrary());
+ myEditButton.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ edit();
+ }
+ });
+ }
+
+ protected JPanel getMainPanel() {
+ return myMainPanel;
+ }
+
+ protected void updateDescription() {
+ myDescriptionLabel.setText(myLibraryType.getDescription(myEditorComponent.getProperties()));
+ }
+
+ protected abstract void edit();
+
+ protected void setModified() {
+ myModified = true;
+ updateDescription();
+ }
+
+ @NotNull
+ @Override
+ public JComponent createComponent() {
+ return myMainPanel;
+ }
+
+ @Override
+ public boolean isModified() {
+ return myModified;
+ }
+
+ @Override
+ public void reset() {
+ updateDescription();
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/LibraryRootsComponent.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/LibraryRootsComponent.java
new file mode 100644
index 0000000..7497e5d
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/LibraryRootsComponent.java
@@ -0,0 +1,495 @@
+/*
+ * Copyright 2000-2012 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.libraryEditor;
+
+import com.intellij.ide.DataManager;
+import com.intellij.ide.util.treeView.AbstractTreeStructure;
+import com.intellij.ide.util.treeView.NodeDescriptor;
+import com.intellij.openapi.Disposable;
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.actionSystem.DefaultActionGroup;
+import com.intellij.openapi.actionSystem.LangDataKeys;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.fileChooser.FileChooser;
+import com.intellij.openapi.fileChooser.FileChooserDescriptor;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.project.DumbAwareAction;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.project.ProjectBundle;
+import com.intellij.openapi.roots.OrderRootType;
+import com.intellij.openapi.roots.libraries.LibraryKind;
+import com.intellij.openapi.roots.libraries.LibraryProperties;
+import com.intellij.openapi.roots.libraries.LibraryType;
+import com.intellij.openapi.roots.libraries.ui.*;
+import com.intellij.openapi.roots.libraries.ui.impl.RootDetectionUtil;
+import com.intellij.openapi.roots.ui.configuration.libraries.LibraryPresentationManager;
+import com.intellij.openapi.ui.ex.MultiLineLabel;
+import com.intellij.openapi.ui.popup.JBPopupFactory;
+import com.intellij.openapi.util.Computable;
+import com.intellij.openapi.util.Disposer;
+import com.intellij.openapi.vfs.JarFileSystem;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.ui.AnActionButton;
+import com.intellij.ui.AnActionButtonRunnable;
+import com.intellij.ui.AnActionButtonUpdater;
+import com.intellij.ui.ToolbarDecorator;
+import com.intellij.ui.treeStructure.Tree;
+import com.intellij.util.ArrayUtil;
+import com.intellij.util.containers.ContainerUtil;
+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.TreePath;
+import java.awt.*;
+import java.util.*;
+import java.util.List;
+
+/**
+ * @author Eugene Zhuravlev
+ * Date: Jan 11, 2004
+ */
+public class LibraryRootsComponent implements Disposable, LibraryEditorComponent {
+ static final UrlComparator ourUrlComparator = new UrlComparator();
+
+ private JPanel myPanel;
+ private JPanel myTreePanel;
+ private MultiLineLabel myPropertiesLabel;
+ private JPanel myPropertiesPanel;
+ private LibraryPropertiesEditor myPropertiesEditor;
+ private Tree myTree;
+ private LibraryTableTreeBuilder myTreeBuilder;
+
+ private final Collection<Runnable> myListeners = new ArrayList<Runnable>();
+ @Nullable private final Project myProject;
+
+ private final Computable<LibraryEditor> myLibraryEditorComputable;
+ private LibraryRootsComponentDescriptor myDescriptor;
+ private Module myContextModule;
+
+ public LibraryRootsComponent(@Nullable Project project, @NotNull LibraryEditor libraryEditor) {
+ this(project, new Computable.PredefinedValueComputable<LibraryEditor>(libraryEditor));
+ }
+
+ public LibraryRootsComponent(@Nullable Project project, @NotNull Computable<LibraryEditor> libraryEditorComputable) {
+ myProject = project;
+ myLibraryEditorComputable = libraryEditorComputable;
+ final LibraryEditor editor = getLibraryEditor();
+ final LibraryType type = editor.getType();
+ if (type != null) {
+ myDescriptor = type.createLibraryRootsComponentDescriptor();
+ //noinspection unchecked
+ myPropertiesEditor = type.createPropertiesEditor(this);
+ if (myPropertiesEditor != null) {
+ myPropertiesPanel.add(myPropertiesEditor.createComponent(), BorderLayout.CENTER);
+ }
+ }
+ if (myDescriptor == null) {
+ myDescriptor = new DefaultLibraryRootsComponentDescriptor();
+ }
+ init(new LibraryTreeStructure(this, myDescriptor));
+ updatePropertiesLabel();
+ }
+
+ @NotNull
+ @Override
+ public LibraryProperties getProperties() {
+ return getLibraryEditor().getProperties();
+ }
+
+ @Override
+ public boolean isNewLibrary() {
+ return getLibraryEditor() instanceof NewLibraryEditor;
+ }
+
+ public void updatePropertiesLabel() {
+ StringBuilder text = new StringBuilder();
+ final LibraryType<?> type = getLibraryEditor().getType();
+ final Set<LibraryKind> excluded =
+ type != null ? Collections.<LibraryKind>singleton(type.getKind()) : Collections.<LibraryKind>emptySet();
+ for (String description : LibraryPresentationManager.getInstance().getDescriptions(getLibraryEditor().getFiles(OrderRootType.CLASSES),
+ excluded)) {
+ if (text.length() > 0) {
+ text.append("\n");
+ }
+ text.append(description);
+ }
+ myPropertiesLabel.setText(text.toString());
+ }
+
+ private void init(AbstractTreeStructure treeStructure) {
+ myTree = new Tree(new DefaultTreeModel(new DefaultMutableTreeNode()));
+ myTree.setRootVisible(false);
+ myTree.setShowsRootHandles(true);
+ new LibraryRootsTreeSpeedSearch(myTree);
+ myTree.setCellRenderer(new LibraryTreeRenderer());
+ myTreeBuilder = new LibraryTableTreeBuilder(myTree, (DefaultTreeModel)myTree.getModel(), treeStructure);
+ myTreePanel.setLayout(new BorderLayout());
+
+ ToolbarDecorator toolbarDecorator = ToolbarDecorator.createDecorator(myTree).disableUpDownActions().disableAddAction()
+ .setRemoveActionName(ProjectBundle.message("library.detach.action"))
+ .setRemoveAction(new AnActionButtonRunnable() {
+ @Override
+ public void run(AnActionButton button) {
+ final Object[] selectedElements = getSelectedElements();
+ if (selectedElements.length == 0) {
+ return;
+ }
+ ApplicationManager.getApplication().runWriteAction(new Runnable() {
+ @Override
+ public void run() {
+ for (Object selectedElement : selectedElements) {
+ if (selectedElement instanceof ItemElement) {
+ final ItemElement itemElement = (ItemElement)selectedElement;
+ getLibraryEditor().removeRoot(itemElement.getUrl(), itemElement.getRootType());
+ }
+ else if (selectedElement instanceof OrderRootTypeElement) {
+ final OrderRootType rootType = ((OrderRootTypeElement)selectedElement).getOrderRootType();
+ final String[] urls = getLibraryEditor().getUrls(rootType);
+ for (String url : urls) {
+ getLibraryEditor().removeRoot(url, rootType);
+ }
+ }
+ }
+ }
+ });
+ librariesChanged(true);
+ }
+ });
+
+ toolbarDecorator.setAddAction(new AnActionButtonRunnable() {
+ @Override
+ public void run(AnActionButton button) {
+ final AnAction[] children = getActions();
+ if (children.length == 0) return;
+ final DefaultActionGroup actions = new DefaultActionGroup(children);
+ JBPopupFactory.getInstance().createActionGroupPopup(null, actions,
+ DataManager.getInstance().getDataContext(button.getContextComponent()),
+ JBPopupFactory.ActionSelectionAid.SPEEDSEARCH, true)
+ .show(button.getPreferredPopupPoint());
+ }
+
+ private AnAction[] getActions() {
+ List<AnAction> actions = new ArrayList<AnAction>();
+ actions.add(new AttachFilesAction(myDescriptor.getAttachFilesActionName()));
+ for (AttachRootButtonDescriptor descriptor : myDescriptor.createAttachButtons()) {
+ actions.add(new AttachItemAction(descriptor, descriptor.getButtonText()));
+ }
+ return actions.toArray(new AnAction[actions.size()]);
+ }
+ });
+
+
+
+ myTreePanel.add(toolbarDecorator.createPanel(), BorderLayout.CENTER);
+ ToolbarDecorator.findRemoveButton(myTreePanel).addCustomUpdater(new AnActionButtonUpdater() {
+ @Override
+ public boolean isEnabled(AnActionEvent e) {
+ final Object[] selectedElements = getSelectedElements();
+ for (Object element : selectedElements) {
+ if (element instanceof ItemElement) {
+ return true;
+ }
+ if (element instanceof OrderRootTypeElement && getLibraryEditor().getUrls(((OrderRootTypeElement)element).getOrderRootType()).length > 0) {
+ return true;
+ }
+ }
+ return false;
+ }
+ });
+
+ Disposer.register(this, myTreeBuilder);
+ }
+
+ public JComponent getComponent() {
+ return myPanel;
+ }
+
+ @Override
+ @Nullable
+ public Project getProject() {
+ return myProject;
+ }
+
+ public void setContextModule(Module module) {
+ myContextModule = module;
+ }
+
+ @Override
+ @Nullable
+ public VirtualFile getExistingRootDirectory() {
+ for (OrderRootType orderRootType : OrderRootType.getAllPersistentTypes()) {
+ final VirtualFile[] existingRoots = getLibraryEditor().getFiles(orderRootType);
+ if (existingRoots.length > 0) {
+ VirtualFile existingRoot = existingRoots[0];
+ if (existingRoot.getFileSystem() instanceof JarFileSystem) {
+ existingRoot = JarFileSystem.getInstance().getVirtualFileForJar(existingRoot);
+ }
+ if (existingRoot != null) {
+ if (existingRoot.isDirectory()) {
+ return existingRoot;
+ }
+ else {
+ return existingRoot.getParent();
+ }
+ }
+ }
+ }
+ return null;
+ }
+
+ @Override
+ @Nullable
+ public VirtualFile getBaseDirectory() {
+ if (myProject != null) {
+ //todo[nik] perhaps we shouldn't select project base dir if global library is edited
+ return myProject.getBaseDir();
+ }
+ return null;
+ }
+
+ @Override
+ public LibraryEditor getLibraryEditor() {
+ return myLibraryEditorComputable.compute();
+ }
+
+ public boolean hasChanges() {
+ if (myPropertiesEditor != null && myPropertiesEditor.isModified()) {
+ return true;
+ }
+ return getLibraryEditor().hasChanges();
+ }
+
+ private Object[] getSelectedElements() {
+ if (myTreeBuilder == null || myTreeBuilder.isDisposed()) return ArrayUtil.EMPTY_OBJECT_ARRAY;
+ final TreePath[] selectionPaths = myTreeBuilder.getTree().getSelectionPaths();
+ if (selectionPaths == null) {
+ return ArrayUtil.EMPTY_OBJECT_ARRAY;
+ }
+ List<Object> elements = new ArrayList<Object>();
+ for (TreePath selectionPath : selectionPaths) {
+ final Object pathElement = getPathElement(selectionPath);
+ if (pathElement != null) {
+ elements.add(pathElement);
+ }
+ }
+ return ArrayUtil.toObjectArray(elements);
+ }
+
+ @Nullable
+ private static Object getPathElement(final TreePath selectionPath) {
+ if (selectionPath == null) {
+ return null;
+ }
+ final DefaultMutableTreeNode lastPathComponent = (DefaultMutableTreeNode)selectionPath.getLastPathComponent();
+ if (lastPathComponent == null) {
+ return null;
+ }
+ final Object userObject = lastPathComponent.getUserObject();
+ if (!(userObject instanceof NodeDescriptor)) {
+ return null;
+ }
+ final Object element = ((NodeDescriptor)userObject).getElement();
+ if (!(element instanceof LibraryTableTreeContentElement)) {
+ return null;
+ }
+ return element;
+ }
+
+ @Override
+ public void renameLibrary(String newName) {
+ final LibraryEditor libraryEditor = getLibraryEditor();
+ libraryEditor.setName(newName);
+ librariesChanged(false);
+ }
+
+ @Override
+ public void dispose() {
+ if (myPropertiesEditor != null) {
+ myPropertiesEditor.disposeUIResources();
+ }
+ myTreeBuilder = null;
+ }
+
+ public void resetProperties() {
+ if (myPropertiesEditor != null) {
+ myPropertiesEditor.reset();
+ }
+ }
+
+ public void applyProperties() {
+ if (myPropertiesEditor != null && myPropertiesEditor.isModified()) {
+ myPropertiesEditor.apply();
+ }
+ }
+
+ @Override
+ public void updateRootsTree() {
+ if (myTreeBuilder != null) {
+ myTreeBuilder.queueUpdate();
+ }
+ }
+
+ private class AttachFilesAction extends AttachItemActionBase {
+ public AttachFilesAction(String title) {
+ super(title);
+ }
+
+ @Override
+ protected List<OrderRoot> selectRoots(@Nullable VirtualFile initialSelection) {
+ final String name = getLibraryEditor().getName();
+ final FileChooserDescriptor chooserDescriptor = myDescriptor.createAttachFilesChooserDescriptor(name);
+ if (myContextModule != null) {
+ chooserDescriptor.putUserData(LangDataKeys.MODULE_CONTEXT, myContextModule);
+ }
+ final VirtualFile[] files = FileChooser.chooseFiles(chooserDescriptor, myPanel, myProject, initialSelection);
+ if (files.length == 0) return Collections.emptyList();
+
+ return RootDetectionUtil.detectRoots(Arrays.asList(files), myPanel, myProject, myDescriptor);
+ }
+ }
+
+ public abstract class AttachItemActionBase extends DumbAwareAction {
+ private VirtualFile myLastChosen = null;
+
+ protected AttachItemActionBase(String text) {
+ super(text);
+ }
+
+ @Nullable
+ protected VirtualFile getFileToSelect() {
+ if (myLastChosen != null) {
+ return myLastChosen;
+ }
+
+ final VirtualFile directory = getExistingRootDirectory();
+ if (directory != null) {
+ return directory;
+ }
+ return getBaseDirectory();
+ }
+
+ @Override
+ public void actionPerformed(@Nullable AnActionEvent e) {
+ VirtualFile toSelect = getFileToSelect();
+ List<OrderRoot> roots = selectRoots(toSelect);
+ if (roots.isEmpty()) return;
+
+ final List<OrderRoot> attachedRoots = attachFiles(roots);
+ final OrderRoot first = ContainerUtil.getFirstItem(attachedRoots);
+ if (first != null) {
+ myLastChosen = first.getFile();
+ }
+ fireLibrariesChanged();
+ myTree.requestFocus();
+ }
+
+ protected abstract List<OrderRoot> selectRoots(@Nullable VirtualFile initialSelection);
+ }
+
+ private class AttachItemAction extends AttachItemActionBase {
+ private final AttachRootButtonDescriptor myDescriptor;
+
+ protected AttachItemAction(AttachRootButtonDescriptor descriptor, String title) {
+ super(title);
+ myDescriptor = descriptor;
+ }
+
+ @Override
+ protected List<OrderRoot> selectRoots(@Nullable VirtualFile initialSelection) {
+ final VirtualFile[] files = myDescriptor.selectFiles(myPanel, initialSelection, myContextModule, getLibraryEditor());
+ if (files.length == 0) return Collections.emptyList();
+
+ List<OrderRoot> roots = new ArrayList<OrderRoot>();
+ for (VirtualFile file : myDescriptor.scanForActualRoots(files, myPanel)) {
+ roots.add(new OrderRoot(file, myDescriptor.getRootType(), myDescriptor.addAsJarDirectories()));
+ }
+ return roots;
+ }
+ }
+
+ private List<OrderRoot> attachFiles(List<OrderRoot> roots) {
+ final List<OrderRoot> rootsToAttach = filterAlreadyAdded(roots);
+ if (!rootsToAttach.isEmpty()) {
+ ApplicationManager.getApplication().runWriteAction(new Runnable() {
+ @Override
+ public void run() {
+ getLibraryEditor().addRoots(rootsToAttach);
+ }
+ });
+ updatePropertiesLabel();
+ myTreeBuilder.queueUpdate();
+ }
+ return rootsToAttach;
+ }
+
+ private List<OrderRoot> filterAlreadyAdded(@NotNull List<OrderRoot> roots) {
+ List<OrderRoot> result = new ArrayList<OrderRoot>();
+ for (OrderRoot root : roots) {
+ final VirtualFile[] libraryFiles = getLibraryEditor().getFiles(root.getType());
+ if (!ArrayUtil.contains(root.getFile(), libraryFiles)) {
+ result.add(root);
+ }
+ }
+ return result;
+ }
+
+ private void librariesChanged(boolean putFocusIntoTree) {
+ updatePropertiesLabel();
+ myTreeBuilder.queueUpdate();
+ if (putFocusIntoTree) {
+ myTree.requestFocus();
+ }
+ fireLibrariesChanged();
+ }
+
+ private void fireLibrariesChanged() {
+ Runnable[] listeners = myListeners.toArray(new Runnable[myListeners.size()]);
+ for (Runnable listener : listeners) {
+ listener.run();
+ }
+ }
+
+ public void addListener(Runnable listener) {
+ myListeners.add(listener);
+ }
+
+ public void removeListener(Runnable listener) {
+ myListeners.remove(listener);
+ }
+
+ @Nullable
+ private static Class<?> getElementsClass(Object[] elements) {
+ if (elements.length == 0) {
+ return null;
+ }
+ Class<?> cls = null;
+ for (Object element : elements) {
+ if (cls == null) {
+ cls = element.getClass();
+ }
+ else {
+ if (!cls.equals(element.getClass())) {
+ return null;
+ }
+ }
+ }
+ return cls;
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/LibraryRootsTreeSpeedSearch.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/LibraryRootsTreeSpeedSearch.java
new file mode 100644
index 0000000..02dc421
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/LibraryRootsTreeSpeedSearch.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2000-2011 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.libraryEditor;
+
+import com.intellij.ui.TreeSpeedSearch;
+import com.intellij.ui.treeStructure.Tree;
+
+import javax.swing.tree.DefaultMutableTreeNode;
+import javax.swing.tree.TreePath;
+import java.io.File;
+import java.util.StringTokenizer;
+
+/**
+* @author nik
+*/
+class LibraryRootsTreeSpeedSearch extends TreeSpeedSearch {
+ public LibraryRootsTreeSpeedSearch(final Tree tree) {
+ super(tree);
+ }
+
+ @Override
+ public boolean isMatchingElement(Object element, String pattern) {
+ Object userObject = ((DefaultMutableTreeNode)((TreePath)element).getLastPathComponent()).getUserObject();
+ if (userObject instanceof ItemElementDescriptor) {
+ String str = getElementText(element);
+ if (str == null) {
+ return false;
+ }
+ if (!hasCapitals(pattern)) { // be case-sensitive only if user types capitals
+ str = str.toLowerCase();
+ }
+ if (pattern.contains(File.separator)) {
+ return compare(str,pattern);
+ }
+ final StringTokenizer tokenizer = new StringTokenizer(str, File.separator);
+ while (tokenizer.hasMoreTokens()) {
+ final String token = tokenizer.nextToken();
+ if (compare(token,pattern)) {
+ return true;
+ }
+ }
+ return false;
+ }
+ else {
+ return super.isMatchingElement(element, pattern);
+ }
+ }
+
+ private static boolean hasCapitals(String str) {
+ for (int idx = 0; idx < str.length(); idx++) {
+ if (Character.isUpperCase(str.charAt(idx))) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/LibraryTableTreeBuilder.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/LibraryTableTreeBuilder.java
new file mode 100644
index 0000000..63f224d
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/LibraryTableTreeBuilder.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2000-2009 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.libraryEditor;
+
+import com.intellij.ide.util.treeView.AbstractTreeBuilder;
+import com.intellij.ide.util.treeView.AbstractTreeStructure;
+import com.intellij.ide.util.treeView.IndexComparator;
+import com.intellij.ide.util.treeView.NodeDescriptor;
+import com.intellij.openapi.progress.ProgressIndicator;
+import com.intellij.openapi.progress.util.StatusBarProgress;
+import org.jetbrains.annotations.NotNull;
+
+import javax.swing.*;
+import javax.swing.tree.DefaultTreeModel;
+
+class LibraryTableTreeBuilder extends AbstractTreeBuilder {
+ public LibraryTableTreeBuilder(JTree tree, DefaultTreeModel treeModel, AbstractTreeStructure treeStructure) {
+ super(tree, treeModel, treeStructure, IndexComparator.INSTANCE);
+ initRootNode();
+ }
+
+ @Override
+ protected boolean isAlwaysShowPlus(NodeDescriptor nodeDescriptor) {
+ return false;
+ }
+
+ @Override
+ protected boolean isAutoExpandNode(NodeDescriptor nodeDescriptor) {
+ final Object element = nodeDescriptor.getElement();
+ final Object rootElement = getTreeStructure().getRootElement();
+ return rootElement.equals(element) || element instanceof OrderRootTypeElement;
+ }
+
+ @Override
+ protected boolean isSmartExpand() {
+ return false;
+ }
+
+ @Override
+ @NotNull
+ protected ProgressIndicator createProgressIndicator() {
+ return new StatusBarProgress();
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/LibraryTableTreeContentElement.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/LibraryTableTreeContentElement.java
new file mode 100644
index 0000000..d942de7
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/LibraryTableTreeContentElement.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2000-2009 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.libraryEditor;
+
+import com.intellij.ide.util.treeView.NodeDescriptor;
+
+public abstract class LibraryTableTreeContentElement {
+
+ public abstract NodeDescriptor createDescriptor(NodeDescriptor parentDescriptor, final LibraryRootsComponent parentEditor);
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/LibraryTreeRenderer.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/LibraryTreeRenderer.java
new file mode 100644
index 0000000..d79b9b5
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/LibraryTreeRenderer.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2000-2012 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.libraryEditor;
+
+import com.intellij.ide.util.treeView.NodeDescriptor;
+import com.intellij.ui.ColoredTreeCellRenderer;
+import com.intellij.ui.SimpleTextAttributes;
+import com.intellij.util.ui.UIUtil;
+
+import javax.swing.*;
+import javax.swing.tree.DefaultMutableTreeNode;
+import java.awt.*;
+
+public class LibraryTreeRenderer extends ColoredTreeCellRenderer {
+ @Override
+ public void customizeCellRenderer(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) {
+ DefaultMutableTreeNode node = (DefaultMutableTreeNode)value;
+ Object userObject = node.getUserObject();
+ if (userObject instanceof NodeDescriptor) {
+ final NodeDescriptor descriptor = (NodeDescriptor)userObject;
+ setIcon(descriptor.getIcon());
+ append(descriptor.toString(), new SimpleTextAttributes(SimpleTextAttributes.STYLE_PLAIN, descriptor.getColor()));
+ }
+ }
+
+ @Override
+ public Font getFont() {
+ Font font = super.getFont();
+ if (font == null) {
+ font = UIUtil.getLabelFont();
+ }
+ return font;
+ }
+ }
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/LibraryTreeStructure.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/LibraryTreeStructure.java
new file mode 100644
index 0000000..acf20c0
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/LibraryTreeStructure.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright 2000-2009 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.libraryEditor;
+
+import com.intellij.ide.util.treeView.AbstractTreeStructure;
+import com.intellij.ide.util.treeView.NodeDescriptor;
+import com.intellij.openapi.project.ProjectBundle;
+import com.intellij.openapi.roots.OrderRootType;
+import com.intellij.openapi.roots.libraries.ui.LibraryRootsComponentDescriptor;
+import com.intellij.openapi.roots.libraries.ui.OrderRootTypePresentation;
+import com.intellij.util.ArrayUtil;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+
+public class LibraryTreeStructure extends AbstractTreeStructure {
+ private final Object myRootElement;
+ private final NodeDescriptor myRootElementDescriptor;
+ private final LibraryRootsComponent myParentEditor;
+ private final LibraryRootsComponentDescriptor myComponentDescriptor;
+
+ public LibraryTreeStructure(LibraryRootsComponent parentElement, LibraryRootsComponentDescriptor componentDescriptor) {
+ myParentEditor = parentElement;
+ myComponentDescriptor = componentDescriptor;
+ myRootElement = new Object();
+ myRootElementDescriptor = new NodeDescriptor(null, null) {
+ @Override
+ public boolean update() {
+ myName = ProjectBundle.message("library.root.node");
+ return false;
+ }
+ @Override
+ public Object getElement() {
+ return myRootElement;
+ }
+ };
+ }
+
+ @Override
+ public Object getRootElement() {
+ return myRootElement;
+ }
+
+ @Override
+ public Object[] getChildElements(Object element) {
+ if (element == myRootElement) {
+ ArrayList<LibraryTableTreeContentElement> elements = new ArrayList<LibraryTableTreeContentElement>(3);
+ final LibraryEditor parentEditor = myParentEditor.getLibraryEditor();
+ for (OrderRootType type : myComponentDescriptor.getRootTypes()) {
+ final String[] urls = parentEditor.getUrls(type);
+ if (urls.length > 0) {
+ OrderRootTypePresentation presentation = myComponentDescriptor.getRootTypePresentation(type);
+ if (presentation == null) {
+ presentation = DefaultLibraryRootsComponentDescriptor.getDefaultPresentation(type);
+ }
+ elements.add(new OrderRootTypeElement(type, presentation.getNodeText(), presentation.getIcon()));
+ }
+ }
+ return elements.toArray();
+ }
+
+ if (element instanceof OrderRootTypeElement) {
+ OrderRootTypeElement rootTypeElement = (OrderRootTypeElement)element;
+ OrderRootType orderRootType = rootTypeElement.getOrderRootType();
+ ArrayList<ItemElement> items = new ArrayList<ItemElement>();
+ final LibraryEditor libraryEditor = myParentEditor.getLibraryEditor();
+ final String[] urls = libraryEditor.getUrls(orderRootType).clone();
+ Arrays.sort(urls, LibraryRootsComponent.ourUrlComparator);
+ for (String url : urls) {
+ items.add(new ItemElement(rootTypeElement, url, orderRootType, libraryEditor.isJarDirectory(url, orderRootType), libraryEditor.isValid(url, orderRootType)));
+ }
+ return items.toArray();
+ }
+ return ArrayUtil.EMPTY_OBJECT_ARRAY;
+ }
+
+ @Override
+ public void commit() {
+ }
+
+ @Override
+ public boolean hasSomethingToCommit() {
+ return false;
+ }
+
+ @Override
+ public Object getParentElement(Object element) {
+ Object rootElement = getRootElement();
+ if (element == rootElement) {
+ return null;
+ }
+ if (element instanceof ItemElement) {
+ return ((ItemElement)element).getParent();
+ }
+ return rootElement;
+ }
+
+ @Override
+ @NotNull
+ public NodeDescriptor createDescriptor(Object element, NodeDescriptor parentDescriptor) {
+ if (element == myRootElement) {
+ return myRootElementDescriptor;
+ }
+ if (element instanceof LibraryTableTreeContentElement) {
+ return ((LibraryTableTreeContentElement)element).createDescriptor(parentDescriptor, myParentEditor);
+ }
+ return null;
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/NewLibraryEditor.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/NewLibraryEditor.java
new file mode 100644
index 0000000..fc5e6db
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/NewLibraryEditor.java
@@ -0,0 +1,202 @@
+/*
+ * Copyright 2000-2010 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.libraryEditor;
+
+import com.intellij.openapi.roots.OrderRootType;
+import com.intellij.openapi.roots.impl.libraries.JarDirectories;
+import com.intellij.openapi.roots.impl.libraries.LibraryEx;
+import com.intellij.openapi.roots.impl.libraries.LibraryImpl;
+import com.intellij.openapi.roots.libraries.LibraryProperties;
+import com.intellij.openapi.roots.libraries.LibraryType;
+import com.intellij.openapi.roots.ui.LightFilePointer;
+import com.intellij.openapi.vfs.VfsUtil;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.util.ArrayUtil;
+import com.intellij.util.containers.MultiMap;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * @author nik
+ */
+public class NewLibraryEditor extends LibraryEditorBase {
+ private String myLibraryName;
+ private final MultiMap<OrderRootType, LightFilePointer> myRoots;
+ private final JarDirectories myJarDirectories = new JarDirectories();
+ private LibraryType myType;
+ private LibraryProperties myProperties;
+
+ public NewLibraryEditor() {
+ this(null, null);
+ }
+
+ public NewLibraryEditor(@Nullable LibraryType type, @Nullable LibraryProperties properties) {
+ myType = type;
+ myProperties = properties;
+ myRoots = new MultiMap<OrderRootType, LightFilePointer>();
+ }
+
+ @Override
+ public Collection<OrderRootType> getOrderRootTypes() {
+ return myRoots.keySet();
+ }
+
+ @Override
+ @Nullable
+ public LibraryType<?> getType() {
+ return myType;
+ }
+
+ @Override
+ public void setType(@NotNull LibraryType<?> type) {
+ myType = type;
+ }
+
+ @Override
+ public LibraryProperties getProperties() {
+ return myProperties;
+ }
+
+ @Override
+ public void setProperties(LibraryProperties properties) {
+ myProperties = properties;
+ }
+
+ @Override
+ public String getName() {
+ return myLibraryName;
+ }
+
+ @Override
+ public String[] getUrls(OrderRootType rootType) {
+ final Collection<LightFilePointer> pointers = myRoots.get(rootType);
+ List<String> urls = new ArrayList<String>();
+ for (LightFilePointer pointer : pointers) {
+ urls.add(pointer.getUrl());
+ }
+ return ArrayUtil.toStringArray(urls);
+ }
+
+ @Override
+ public VirtualFile[] getFiles(OrderRootType rootType) {
+ List<VirtualFile> result = new ArrayList<VirtualFile>();
+ for (LightFilePointer pointer : myRoots.get(rootType)) {
+ final VirtualFile file = pointer.getFile();
+ if (file == null) {
+ continue;
+ }
+
+ if (file.isDirectory()) {
+ final String url = file.getUrl();
+ if (myJarDirectories.contains(rootType, url)) {
+ LibraryImpl.collectJarFiles(file, result, myJarDirectories.isRecursive(rootType, url));
+ continue;
+ }
+ }
+ result.add(file);
+ }
+ return VfsUtil.toVirtualFileArray(result);
+ }
+
+ @Override
+ public void setName(String name) {
+ myLibraryName = name;
+ }
+
+ @Override
+ public void addRoot(VirtualFile file, OrderRootType rootType) {
+ myRoots.putValue(rootType, new LightFilePointer(file));
+ }
+
+ @Override
+ public void addRoot(String url, OrderRootType rootType) {
+ myRoots.putValue(rootType, new LightFilePointer(url));
+ }
+
+ @Override
+ public void addJarDirectory(VirtualFile file, boolean recursive, OrderRootType rootType) {
+ addJarDirectory(file.getUrl(), recursive, rootType);
+ }
+
+ @Override
+ public void addJarDirectory(final String url, boolean recursive, OrderRootType rootType) {
+ addRoot(url, rootType);
+ myJarDirectories.add(rootType, url, recursive);
+ }
+
+ @Override
+ public void removeRoot(String url, OrderRootType rootType) {
+ myRoots.removeValue(rootType, new LightFilePointer(url));
+ myJarDirectories.remove(rootType, url);
+ }
+
+ @Override
+ public boolean hasChanges() {
+ return true;
+ }
+
+ @Override
+ public boolean isJarDirectory(String url, OrderRootType rootType) {
+ return myJarDirectories.contains(rootType, url);
+ }
+
+ @Override
+ public boolean isValid(String url, OrderRootType orderRootType) {
+ final Collection<LightFilePointer> pointers = myRoots.get(orderRootType);
+ for (LightFilePointer pointer : pointers) {
+ if (pointer.getUrl().equals(url)) {
+ return pointer.isValid();
+ }
+ }
+ return false;
+ }
+
+ public void applyTo(LibraryEx.ModifiableModelEx model) {
+ model.setProperties(myProperties);
+ for (OrderRootType type : myRoots.keySet()) {
+ for (LightFilePointer pointer : myRoots.get(type)) {
+ if (!myJarDirectories.contains(type, pointer.getUrl())) {
+ model.addRoot(pointer.getUrl(), type);
+ }
+ }
+ }
+ for (OrderRootType rootType : myJarDirectories.getRootTypes()) {
+ for (String url : myJarDirectories.getDirectories(rootType)) {
+ model.addJarDirectory(url, myJarDirectories.isRecursive(rootType, url), rootType);
+ }
+ }
+ }
+
+ public void applyTo(LibraryEditorBase editor) {
+ editor.setProperties(myProperties);
+ for (OrderRootType type : myRoots.keySet()) {
+ for (LightFilePointer pointer : myRoots.get(type)) {
+ if (!myJarDirectories.contains(type, pointer.getUrl())) {
+ editor.addRoot(pointer.getUrl(), type);
+ }
+ }
+ }
+ for (OrderRootType rootType : myJarDirectories.getRootTypes()) {
+ for (String url : myJarDirectories.getDirectories(rootType)) {
+ editor.addJarDirectory(url, myJarDirectories.isRecursive(rootType, url), rootType);
+ }
+ }
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/OrderRootTypeElement.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/OrderRootTypeElement.java
new file mode 100644
index 0000000..3db9881
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/OrderRootTypeElement.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2000-2010 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.libraryEditor;
+
+import com.intellij.ide.util.treeView.NodeDescriptor;
+import com.intellij.openapi.roots.OrderRootType;
+import org.jetbrains.annotations.NotNull;
+
+import javax.swing.*;
+
+/**
+ * @author nik
+ */
+public class OrderRootTypeElement extends LibraryTableTreeContentElement {
+ private final OrderRootType myRootType;
+ private String myNodeText;
+ private Icon myIcon;
+
+ public OrderRootTypeElement(@NotNull OrderRootType rootType, final String nodeText, final Icon icon) {
+ myRootType = rootType;
+ myNodeText = nodeText;
+ myIcon = icon;
+ }
+
+ @NotNull
+ public OrderRootType getOrderRootType() {
+ return myRootType;
+ }
+
+ @Override
+ public int hashCode() {
+ return myRootType.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return obj instanceof OrderRootTypeElement && ((OrderRootTypeElement)obj).getOrderRootType().equals(myRootType);
+ }
+
+ @Override
+ public NodeDescriptor createDescriptor(final NodeDescriptor parentDescriptor, final LibraryRootsComponent parentEditor) {
+ return new OrderRootTypeElementDescriptor(parentDescriptor, this, myNodeText, myIcon);
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/OrderRootTypeElementDescriptor.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/OrderRootTypeElementDescriptor.java
new file mode 100644
index 0000000..788d2d3
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/OrderRootTypeElementDescriptor.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2000-2010 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.libraryEditor;
+
+import com.intellij.ide.util.treeView.NodeDescriptor;
+
+import javax.swing.*;
+
+/**
+ * @author nik
+ */
+public class OrderRootTypeElementDescriptor extends NodeDescriptor<OrderRootTypeElement> {
+ private final OrderRootTypeElement myElement;
+
+ public OrderRootTypeElementDescriptor(NodeDescriptor parentDescriptor,
+ OrderRootTypeElement element, String text, Icon icon) {
+ super(null, parentDescriptor);
+ myElement = element;
+ setIcon(icon);
+ myName = text;
+ }
+
+ @Override
+ public boolean update() {
+ return false;
+ }
+
+ @Override
+ public OrderRootTypeElement getElement() {
+ return myElement;
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/SourcesOrderRootTypeUIFactory.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/SourcesOrderRootTypeUIFactory.java
new file mode 100644
index 0000000..7d2f766
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/SourcesOrderRootTypeUIFactory.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2000-2009 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.
+ */
+
+/*
+ * User: anna
+ * Date: 26-Dec-2007
+ */
+package com.intellij.openapi.roots.ui.configuration.libraryEditor;
+
+import com.intellij.icons.AllIcons;
+import com.intellij.openapi.fileChooser.FileChooserDescriptor;
+import com.intellij.openapi.project.ProjectBundle;
+import com.intellij.openapi.projectRoots.JavaSdkType;
+import com.intellij.openapi.projectRoots.Sdk;
+import com.intellij.openapi.projectRoots.ui.SdkPathEditor;
+import com.intellij.openapi.roots.OrderRootType;
+import com.intellij.openapi.roots.ui.OrderRootTypeUIFactory;
+import com.intellij.openapi.roots.ui.configuration.PathUIUtils;
+import com.intellij.openapi.vfs.VirtualFile;
+
+import javax.swing.*;
+import java.awt.*;
+
+public class SourcesOrderRootTypeUIFactory implements OrderRootTypeUIFactory {
+
+ @Override
+ public SdkPathEditor createPathEditor(final Sdk sdk) {
+ return new SdkPathEditor(ProjectBundle.message("sdk.configure.sourcepath.tab"), OrderRootType.SOURCES, new FileChooserDescriptor(true, true, true, false, true, true)) {
+ @Override
+ protected VirtualFile[] adjustAddedFileSet(final Component component, final VirtualFile[] files) {
+ if (sdk.getSdkType() instanceof JavaSdkType) {
+ return PathUIUtils.scanAndSelectDetectedJavaSourceRoots(component, files);
+ }
+ return super.adjustAddedFileSet(component, files);
+ }
+ };
+ }
+
+ @Override
+ public Icon getIcon() {
+ return AllIcons.Nodes.SourceFolder;
+ }
+
+ @Override
+ public String getNodeText() {
+ return ProjectBundle.message("library.sources.node");
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/UrlComparator.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/UrlComparator.java
new file mode 100644
index 0000000..14d25c9
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/UrlComparator.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2000-2009 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.libraryEditor;
+
+import java.util.Comparator;
+
+class UrlComparator implements Comparator<String> {
+ @Override
+ public int compare(String url1, String url2) {
+ return url1.compareToIgnoreCase(url2);
+ /*
+ url1 = removeJarSeparator(url1);
+ url2 = removeJarSeparator(url2);
+ String name1 = url1.substring(url1.lastIndexOf('/') + 1);
+ String name2 = url2.substring(url2.lastIndexOf('/') + 1);
+ return name1.compareToIgnoreCase(name2);
+ */
+ }
+
+ /*
+ private String removeJarSeparator(String url) {
+ return url.endsWith(JarFileSystem.JAR_SEPARATOR)? url.substring(0, url.length() - JarFileSystem.JAR_SEPARATOR.length()) : url;
+ }
+ */
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/packaging/PackagingEditorUtil.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/packaging/PackagingEditorUtil.java
new file mode 100644
index 0000000..87b4140
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/packaging/PackagingEditorUtil.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2000-2009 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.packaging;
+
+import com.intellij.openapi.project.ProjectBundle;
+import com.intellij.openapi.roots.OrderRootType;
+import com.intellij.openapi.roots.libraries.Library;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.packaging.impl.ui.LibraryElementPresentation;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * @author nik
+ */
+public class PackagingEditorUtil {
+ private PackagingEditorUtil() {
+ }
+
+ public static String getLibraryItemText(final @NotNull Library library, final boolean includeTableName) {
+ String name = library.getName();
+ VirtualFile[] files = library.getFiles(OrderRootType.CLASSES);
+ if (name != null) {
+ return name + (includeTableName ? LibraryElementPresentation.getLibraryTableComment(library) : "");
+ }
+ else if (files.length > 0) {
+ return files[0].getName() + (includeTableName ? LibraryElementPresentation.getLibraryTableComment(library) : "");
+ }
+ else {
+ return ProjectBundle.message("library.empty.item");
+ }
+ }
+
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/AddFacetOfTypeAction.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/AddFacetOfTypeAction.java
new file mode 100644
index 0000000..dbc467a
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/AddFacetOfTypeAction.java
@@ -0,0 +1,166 @@
+/*
+ * Copyright 2000-2010 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.facet.Facet;
+import com.intellij.facet.FacetType;
+import com.intellij.facet.FacetTypeId;
+import com.intellij.facet.FacetTypeRegistry;
+import com.intellij.facet.impl.ProjectFacetsConfigurator;
+import com.intellij.ide.util.ChooseElementsDialog;
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.module.ModuleType;
+import com.intellij.openapi.project.DumbAwareAction;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.roots.ui.configuration.ChooseModulesDialog;
+import com.intellij.openapi.roots.ui.configuration.ProjectStructureConfigurable;
+import com.intellij.openapi.ui.Messages;
+import org.jetbrains.annotations.NotNull;
+
+import javax.swing.*;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+* @author nik
+*/
+class AddFacetOfTypeAction extends DumbAwareAction {
+ private final FacetType myFacetType;
+ private final StructureConfigurableContext myContext;
+
+ AddFacetOfTypeAction(final FacetType type, final StructureConfigurableContext context) {
+ super(type.getPresentableName(), null, type.getIcon());
+ myFacetType = type;
+ myContext = context;
+ }
+
+ @Override
+ public void actionPerformed(AnActionEvent e) {
+ final FacetType type = myFacetType;
+ if (type == null) return;
+
+ final FacetTypeId underlyingFacetType = type.getUnderlyingFacetType();
+ if (underlyingFacetType == null) {
+ addFacetToModule(type);
+ }
+ else {
+ addSubFacet(type, underlyingFacetType);
+ }
+ }
+
+ private void addSubFacet(FacetType type, FacetTypeId<?> underlyingType) {
+ final ProjectFacetsConfigurator facetsConfigurator = myContext.getModulesConfigurator().getFacetsConfigurator();
+ List<Facet> suitableParents = new ArrayList<Facet>();
+ for (Module module : myContext.getModules()) {
+ if (type.isSuitableModuleType(ModuleType.get(module))) {
+ suitableParents.addAll(facetsConfigurator.getFacetsByType(module, underlyingType));
+ }
+ }
+
+ final Iterator<Facet> iterator = suitableParents.iterator();
+ while (iterator.hasNext()) {
+ Facet parent = iterator.next();
+ if (type.isOnlyOneFacetAllowed() && facetsConfigurator.hasFacetOfType(parent.getModule(), parent, type.getId())) {
+ iterator.remove();
+ }
+ }
+
+ final Project project = myContext.getProject();
+ if (suitableParents.isEmpty()) {
+ final String parentType = FacetTypeRegistry.getInstance().findFacetType(underlyingType).getPresentableName();
+ Messages.showErrorDialog(project, "No suitable parent " + parentType + " facets found", "Cannot Create " + type.getPresentableName() + " Facet");
+ return;
+ }
+
+ ChooseParentFacetDialog dialog = new ChooseParentFacetDialog(project, suitableParents);
+ dialog.show();
+ final List<Facet> chosen = dialog.getChosenElements();
+ if (!dialog.isOK() || chosen.size() != 1) return;
+ final Facet parent = chosen.get(0);
+ final Facet facet =
+ ModuleStructureConfigurable.getInstance(project).getFacetEditorFacade().createAndAddFacet(type, parent.getModule(), parent);
+ ProjectStructureConfigurable.getInstance(project).select(facet, true);
+ }
+
+ private void addFacetToModule(@NotNull FacetType type) {
+ final ProjectFacetsConfigurator facetsConfigurator = myContext.getModulesConfigurator().getFacetsConfigurator();
+ List<Module> suitableModules = new ArrayList<Module>(Arrays.asList(myContext.getModules()));
+ final Iterator<Module> iterator = suitableModules.iterator();
+ while (iterator.hasNext()) {
+ Module module = iterator.next();
+ if (!type.isSuitableModuleType(ModuleType.get(module)) || (type.isOnlyOneFacetAllowed() && facetsConfigurator.hasFacetOfType(module, null, type.getId()))) {
+ iterator.remove();
+ }
+ }
+ final Project project = myContext.getProject();
+ if (suitableModules.isEmpty()) {
+ Messages.showErrorDialog(project, "No suitable modules for " + type.getPresentableName() + " facet found.", "Cannot Create Facet");
+ return;
+ }
+
+ final ChooseModulesDialog dialog = new ChooseModulesDialog(project, suitableModules, "Choose Module",
+ type.getPresentableName() + " facet will be added to selected module");
+ dialog.setSingleSelectionMode();
+ dialog.show();
+ final List<Module> elements = dialog.getChosenElements();
+ if (!dialog.isOK() || elements.size() != 1) return;
+
+ final Module module = elements.get(0);
+ final Facet facet = ModuleStructureConfigurable.getInstance(project).getFacetEditorFacade().createAndAddFacet(type, module, null);
+ ProjectStructureConfigurable.getInstance(project).select(facet, true);
+ }
+
+ public static AnAction[] createAddFacetActions(FacetStructureConfigurable configurable) {
+ final List<AnAction> result = new ArrayList<AnAction>();
+ final StructureConfigurableContext context = configurable.myContext;
+ for (FacetType type : FacetTypeRegistry.getInstance().getSortedFacetTypes()) {
+ if (hasSuitableModules(context, type)) {
+ result.add(new AddFacetOfTypeAction(type, context));
+ }
+ }
+ return result.toArray(new AnAction[result.size()]);
+ }
+
+ private static boolean hasSuitableModules(StructureConfigurableContext context, FacetType type) {
+ for (Module module : context.getModules()) {
+ if (type.isSuitableModuleType(ModuleType.get(module))) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private static class ChooseParentFacetDialog extends ChooseElementsDialog<Facet> {
+ private ChooseParentFacetDialog(Project project, List<? extends Facet> items) {
+ super(project, items, "Select Parent Facet", null, true);
+ myChooser.setSingleSelectionMode();
+ }
+
+ @Override
+ protected String getItemText(Facet item) {
+ return item.getName() + " (module " + item.getModule().getName() + ")";
+ }
+
+ @Override
+ protected Icon getItemIcon(Facet item) {
+ return item.getType().getIcon();
+ }
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/AddLibraryToModuleDependenciesAction.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/AddLibraryToModuleDependenciesAction.java
new file mode 100644
index 0000000..03e8e4c
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/AddLibraryToModuleDependenciesAction.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2000-2012 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.actionSystem.AnActionEvent;
+import com.intellij.openapi.project.DumbAwareAction;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.roots.impl.libraries.LibraryEx;
+import com.intellij.openapi.roots.libraries.Library;
+import com.intellij.openapi.roots.ui.configuration.libraries.LibraryEditingUtil;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.daemon.LibraryProjectStructureElement;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.daemon.ProjectStructureElement;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * @author nik
+ */
+public class AddLibraryToModuleDependenciesAction extends DumbAwareAction {
+ @NotNull private final Project myProject;
+ @NotNull private final BaseLibrariesConfigurable myConfigurable;
+
+ public AddLibraryToModuleDependenciesAction(@NotNull Project project, @NotNull BaseLibrariesConfigurable configurable) {
+ super("Add to Modules...", "Add the library to the dependencies list of chosen modules", null);
+ myProject = project;
+ myConfigurable = configurable;
+ }
+
+ @Override
+ public void update(AnActionEvent e) {
+ final ProjectStructureElement element = myConfigurable.getSelectedElement();
+ boolean visible = false;
+ if (element instanceof LibraryProjectStructureElement) {
+ final LibraryEx library = (LibraryEx)((LibraryProjectStructureElement)element).getLibrary();
+ visible = !LibraryEditingUtil.getSuitableModules(ModuleStructureConfigurable.getInstance(myProject), library.getKind(), library).isEmpty();
+ }
+ e.getPresentation().setVisible(visible);
+ }
+
+ @Override
+ public void actionPerformed(AnActionEvent e) {
+ final LibraryProjectStructureElement element = (LibraryProjectStructureElement)myConfigurable.getSelectedElement();
+ if (element == null) return;
+ final Library library = element.getLibrary();
+ LibraryEditingUtil.showDialogAndAddLibraryToDependencies(library, myProject, false);
+ }
+}
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
new file mode 100644
index 0000000..0e522a8
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/BaseLibrariesConfigurable.java
@@ -0,0 +1,366 @@
+/*
+ * Copyright 2000-2012 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.openapi.actionSystem.AnAction;
+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.roots.impl.libraries.LibraryEx;
+import com.intellij.openapi.roots.impl.libraries.LibraryImpl;
+import com.intellij.openapi.roots.libraries.Library;
+import com.intellij.openapi.roots.libraries.LibraryTable;
+import com.intellij.openapi.roots.libraries.LibraryTablePresentation;
+import com.intellij.openapi.roots.libraries.LibraryTablesRegistrar;
+import com.intellij.openapi.roots.ui.configuration.libraries.LibraryEditingUtil;
+import com.intellij.openapi.roots.ui.configuration.libraryEditor.CreateNewLibraryAction;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.daemon.LibraryProjectStructureElement;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.daemon.ProjectStructureDaemonAnalyzer;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.daemon.ProjectStructureElement;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.daemon.ProjectStructureElementUsage;
+import com.intellij.openapi.ui.DialogWrapper;
+import com.intellij.openapi.ui.Messages;
+import com.intellij.openapi.ui.NamedConfigurable;
+import com.intellij.openapi.ui.NonEmptyInputValidator;
+import com.intellij.openapi.util.Comparing;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.util.containers.MultiMap;
+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.tree.DefaultTreeModel;
+import javax.swing.tree.TreeNode;
+import java.util.*;
+
+public abstract class BaseLibrariesConfigurable extends BaseStructureConfigurable {
+ protected String myLevel;
+
+ protected BaseLibrariesConfigurable(final @NotNull Project project) {
+ super(project);
+ }
+
+ public static BaseLibrariesConfigurable getInstance(@NotNull Project project, @NotNull String tableLevel) {
+ if (tableLevel.equals(LibraryTablesRegistrar.PROJECT_LEVEL)) {
+ return ProjectLibrariesConfigurable.getInstance(project);
+ }
+ else {
+ return GlobalLibrariesConfigurable.getInstance(project);
+ }
+ }
+
+ public abstract LibraryTablePresentation getLibraryTablePresentation();
+
+ @Override
+ protected void processRemovedItems() {
+ }
+
+ @Override
+ protected boolean wasObjectStored(final Object editableObject) {
+ return false;
+ }
+
+ @Override
+ @Nullable
+ public Runnable enableSearch(final String option) {
+ return null;
+ }
+
+ @Override
+ @Nullable
+ @NonNls
+ public String getHelpTopic() {
+ return "reference.settingsdialog.project.structure.library";
+ }
+
+ @Override
+ public boolean isModified() {
+ boolean isModified = false;
+ for (final LibrariesModifiableModel provider : myContext.myLevel2Providers.values()) {
+ isModified |= provider.isChanged();
+ }
+ return isModified || super.isModified();
+ }
+
+ @Override
+ public void checkCanApply() throws ConfigurationException {
+ super.checkCanApply();
+ for (LibraryConfigurable configurable : getLibraryConfigurables()) {
+ if (configurable.getDisplayName().isEmpty()) {
+ ((LibraryProjectStructureElement)configurable.getProjectStructureElement()).navigate();
+ throw new ConfigurationException("Library name is not specified");
+ }
+ }
+ }
+
+ @Override
+ public void reset() {
+ super.reset();
+ myTree.setRootVisible(false);
+ }
+
+ @Override
+ protected void loadTree() {
+ createLibrariesNode(myContext.createModifiableModelProvider(myLevel));
+ }
+
+ @NotNull
+ @Override
+ protected Collection<? extends ProjectStructureElement> getProjectStructureElements() {
+ final List<ProjectStructureElement> result = new ArrayList<ProjectStructureElement>();
+ for (LibraryConfigurable libraryConfigurable : getLibraryConfigurables()) {
+ result.add(new LibraryProjectStructureElement(myContext, libraryConfigurable.getEditableObject()));
+ }
+ return result;
+ }
+
+ private List<LibraryConfigurable> getLibraryConfigurables() {
+ //todo[nik] improve
+ List<LibraryConfigurable> libraryConfigurables = new ArrayList<LibraryConfigurable>();
+ for (int i = 0; i < myRoot.getChildCount(); i++) {
+ final TreeNode node = myRoot.getChildAt(i);
+ if (node instanceof MyNode) {
+ final NamedConfigurable configurable = ((MyNode)node).getConfigurable();
+ if (configurable instanceof LibraryConfigurable) {
+ libraryConfigurables.add((LibraryConfigurable)configurable);
+ }
+ }
+ }
+ return libraryConfigurables;
+ }
+
+ private void createLibrariesNode(final StructureLibraryTableModifiableModelProvider modelProvider) {
+ final Library[] libraries = modelProvider.getModifiableModel().getLibraries();
+ for (Library library : libraries) {
+ myRoot.add(new MyNode(new LibraryConfigurable(modelProvider, library, myContext, TREE_UPDATER)));
+ }
+ TreeUtil.sort(myRoot, new Comparator() {
+ @Override
+ public int compare(final Object o1, final Object o2) {
+ MyNode node1 = (MyNode)o1;
+ MyNode node2 = (MyNode)o2;
+ return node1.getDisplayName().compareToIgnoreCase(node2.getDisplayName());
+ }
+ });
+ ((DefaultTreeModel)myTree.getModel()).reload(myRoot);
+ }
+
+ @Override
+ public void apply() throws ConfigurationException {
+ super.apply();
+ ApplicationManager.getApplication().runWriteAction(new Runnable() {
+ @Override
+ public void run() {
+ for (final LibrariesModifiableModel provider : myContext.myLevel2Providers.values()) {
+ provider.deferredCommit();
+ }
+ }
+ });
+ }
+
+ public String getLevel() {
+ return myLevel;
+ }
+
+ public void createLibraryNode(Library library) {
+ final LibraryTable table = library.getTable();
+ if (table != null) {
+ final String level = table.getTableLevel();
+ final LibraryConfigurable configurable =
+ new LibraryConfigurable(myContext.createModifiableModelProvider(level), library, myContext, TREE_UPDATER);
+ final MyNode node = new MyNode(configurable);
+ addNode(node, myRoot);
+ final ProjectStructureDaemonAnalyzer daemonAnalyzer = myContext.getDaemonAnalyzer();
+ daemonAnalyzer.queueUpdate(new LibraryProjectStructureElement(myContext, library));
+ daemonAnalyzer.queueUpdateForAllElementsWithErrors();
+ }
+ }
+
+ @Override
+ public void dispose() {
+ if (myContext != null) {
+ for (final LibrariesModifiableModel provider : myContext.myLevel2Providers.values()) {
+ provider.disposeUncommittedLibraries();
+ }
+ }
+ }
+
+ @Override
+ @NotNull
+ protected List<? extends AnAction> createCopyActions(boolean fromPopup) {
+ final ArrayList<AnAction> actions = new ArrayList<AnAction>();
+ actions.add(new CopyLibraryAction());
+ if (fromPopup) {
+ final BaseLibrariesConfigurable targetGroup = getOppositeGroup();
+ actions.add(new ChangeLibraryLevelAction(myProject, myTree, this, targetGroup));
+ actions.add(new AddLibraryToModuleDependenciesAction(myProject, this));
+ }
+ return actions;
+ }
+
+ @Override
+ protected AbstractAddGroup createAddAction() {
+ return new AbstractAddGroup(getAddText()) {
+ @Override
+ @NotNull
+ public AnAction[] getChildren(@Nullable final AnActionEvent e) {
+ return CreateNewLibraryAction.createActionOrGroup(getAddText(), BaseLibrariesConfigurable.this, myProject);
+ }
+ };
+ }
+
+ protected abstract String getAddText();
+
+ public abstract StructureLibraryTableModifiableModelProvider getModelProvider();
+
+ public abstract BaseLibrariesConfigurable getOppositeGroup();
+
+ @Override
+ protected void updateSelection(@Nullable NamedConfigurable configurable) {
+ boolean selectionChanged = !Comparing.equal(myCurrentConfigurable, configurable);
+ if (myCurrentConfigurable != null && selectionChanged) {
+ ((LibraryConfigurable)myCurrentConfigurable).onUnselected();
+ }
+ super.updateSelection(configurable);
+ if (myCurrentConfigurable != null && selectionChanged) {
+ ((LibraryConfigurable)myCurrentConfigurable).onSelected();
+ }
+ }
+
+ @Override
+ public void onStructureUnselected() {
+ if (myCurrentConfigurable != null) {
+ ((LibraryConfigurable)myCurrentConfigurable).onUnselected();
+ }
+ }
+
+ @Override
+ public void onStructureSelected() {
+ if (myCurrentConfigurable != null) {
+ ((LibraryConfigurable)myCurrentConfigurable).onSelected();
+ }
+ }
+
+ public void removeLibrary(@NotNull LibraryProjectStructureElement element) {
+ getModelProvider().getModifiableModel().removeLibrary(element.getLibrary());
+ myContext.getDaemonAnalyzer().removeElement(element);
+ final MyNode node = findNodeByObject(myRoot, element.getLibrary());
+ if (node != null) {
+ removePaths(TreeUtil.getPathFromRoot(node));
+ }
+ }
+
+ @Override
+ protected boolean removeLibrary(final Library library) {
+ final LibraryTable table = library.getTable();
+ if (table != null) {
+ final LibraryProjectStructureElement libraryElement = new LibraryProjectStructureElement(myContext, library);
+ final Collection<ProjectStructureElementUsage> usages = new ArrayList<ProjectStructureElementUsage>(myContext.getDaemonAnalyzer().getUsages(libraryElement));
+ if (usages.size() > 0) {
+ final MultiMap<String, ProjectStructureElementUsage> containerType2Usage = new MultiMap<String, ProjectStructureElementUsage>();
+ for (final ProjectStructureElementUsage usage : usages) {
+ containerType2Usage.putValue(usage.getContainingElement().getTypeName(), usage);
+ }
+
+ List<String> types = new ArrayList<String>(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<ProjectStructureElementUsage> 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()));
+ }
+ }
+
+ sb.append(".\n\nAre you sure you want to delete this library?");
+
+ if (DialogWrapper.OK_EXIT_CODE == Messages.showOkCancelDialog(myProject, sb.toString(),
+ "Delete Library", Messages.getQuestionIcon())) {
+
+ for (final ProjectStructureElementUsage usage : usages) {
+ usage.removeSourceElement();
+ }
+
+ getModelProvider().getModifiableModel().removeLibrary(library);
+ myContext.getDaemonAnalyzer().removeElement(libraryElement);
+ return true;
+ }
+ } else {
+ getModelProvider().getModifiableModel().removeLibrary(library);
+ myContext.getDaemonAnalyzer().removeElement(libraryElement);
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ @Override
+ @Nullable
+ protected String getEmptySelectionString() {
+ return "Select a library to view or edit its details here";
+ }
+
+ private class CopyLibraryAction extends AnAction {
+ private CopyLibraryAction() {
+ super(CommonBundle.message("button.copy"), CommonBundle.message("button.copy"), COPY_ICON);
+ }
+
+ @Override
+ public void actionPerformed(final AnActionEvent e) {
+ final Object o = getSelectedObject();
+ if (o instanceof LibraryEx) {
+ final LibraryEx selected = (LibraryEx)o;
+ final String newName = Messages.showInputDialog("Enter library name:", "Copy Library", null, selected.getName() + "2", new NonEmptyInputValidator());
+ if (newName == null) return;
+
+ BaseLibrariesConfigurable configurable = BaseLibrariesConfigurable.this;
+ final LibraryEx library = (LibraryEx)myContext.getLibrary(selected.getName(), myLevel);
+ LOG.assertTrue(library != null);
+
+ final LibrariesModifiableModel libsModel = configurable.getModelProvider().getModifiableModel();
+ final Library lib = libsModel.createLibrary(newName, library.getKind());
+ final LibraryEx.ModifiableModelEx model = (LibraryEx.ModifiableModelEx)libsModel.getLibraryEditor(lib).getModel();
+ LibraryEditingUtil.copyLibrary(library, Collections.<String, String>emptyMap(), model);
+ }
+ }
+
+ @Override
+ public void update(final AnActionEvent e) {
+ if (myTree.getSelectionPaths() == null || myTree.getSelectionPaths().length != 1) {
+ e.getPresentation().setEnabled(false);
+ } else {
+ e.getPresentation().setEnabled(getSelectedObject() instanceof LibraryImpl);
+ }
+ }
+ }
+}
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
new file mode 100644
index 0000000..bf4f67c
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/BaseStructureConfigurable.java
@@ -0,0 +1,471 @@
+/*
+ * Copyright 2000-2009 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.facet.Facet;
+import com.intellij.ide.CommonActionsManager;
+import com.intellij.ide.TreeExpander;
+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;
+import com.intellij.openapi.ui.MasterDetailsState;
+import com.intellij.openapi.ui.MasterDetailsStateService;
+import com.intellij.openapi.ui.NamedConfigurable;
+import com.intellij.openapi.util.ActionCallback;
+import com.intellij.openapi.util.Condition;
+import com.intellij.openapi.util.Disposer;
+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.ui.tree.TreeUtil;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import javax.swing.tree.TreePath;
+import java.awt.*;
+import java.util.*;
+import java.util.List;
+
+public abstract class BaseStructureConfigurable extends MasterDetailsComponent implements SearchableConfigurable, Disposable, Place.Navigator {
+
+ protected StructureConfigurableContext myContext;
+
+ protected final Project myProject;
+
+ protected boolean myUiDisposed = true;
+
+ private boolean myWasTreeInitialized;
+
+ protected boolean myAutoScrollEnabled = true;
+
+ protected BaseStructureConfigurable(Project project, MasterDetailsState state) {
+ super(state);
+ myProject = project;
+ }
+
+ protected BaseStructureConfigurable(final Project project) {
+ myProject = project;
+ }
+
+ public void init(StructureConfigurableContext context) {
+ myContext = context;
+ myContext.getDaemonAnalyzer().addListener(new ProjectStructureDaemonAnalyzerListener() {
+ @Override
+ public void problemsChanged(@NotNull ProjectStructureElement element) {
+ if (!myTree.isShowing()) return;
+
+ myTree.revalidate();
+ myTree.repaint();
+ }
+ });
+ }
+
+ @Override
+ protected MasterDetailsStateService getStateService() {
+ return MasterDetailsStateService.getInstance(myProject);
+ }
+
+ @Override
+ public ActionCallback navigateTo(@Nullable final Place place, final boolean requestFocus) {
+ if (place == null) return new ActionCallback.Done();
+
+ final Object object = place.getPath(TREE_OBJECT);
+ final String byName = (String)place.getPath(TREE_NAME);
+
+ if (object == null && byName == null) return new ActionCallback.Done();
+
+ final MyNode node = object == null ? null : findNodeByObject(myRoot, object);
+ final MyNode nodeByName = byName == null ? null : findNodeByName(myRoot, byName);
+
+ if (node == null && nodeByName == null) return new ActionCallback.Done();
+
+ final NamedConfigurable config;
+ if (node != null) {
+ config = node.getConfigurable();
+ } else {
+ config = nodeByName.getConfigurable();
+ }
+
+ final ActionCallback result = new ActionCallback().doWhenDone(new Runnable() {
+ @Override
+ public void run() {
+ myAutoScrollEnabled = true;
+ }
+ });
+
+ myAutoScrollEnabled = false;
+ myAutoScrollHandler.cancelAllRequests();
+ final MyNode nodeToSelect = node != null ? node : nodeByName;
+ selectNodeInTree(nodeToSelect, requestFocus).doWhenDone(new Runnable() {
+ @Override
+ public void run() {
+ setSelectedNode(nodeToSelect);
+ Place.goFurther(config, place, requestFocus).notifyWhenDone(result);
+ }
+ });
+
+ return result;
+ }
+
+
+ @Override
+ public void queryPlace(@NotNull final Place place) {
+ if (myCurrentConfigurable != null) {
+ place.putPath(TREE_OBJECT, myCurrentConfigurable.getEditableObject());
+ Place.queryFurther(myCurrentConfigurable, place);
+ }
+ }
+
+ @Override
+ protected void initTree() {
+ if (myWasTreeInitialized) return;
+ myWasTreeInitialized = true;
+
+ super.initTree();
+ new TreeSpeedSearch(myTree, new Convertor<TreePath, String>() {
+ @Override
+ public String convert(final TreePath treePath) {
+ return ((MyNode)treePath.getLastPathComponent()).getDisplayName();
+ }
+ }, true);
+ ToolTipManager.sharedInstance().registerComponent(myTree);
+ myTree.setCellRenderer(new ProjectStructureElementRenderer(myContext));
+ }
+
+ @Override
+ public void disposeUIResources() {
+ if (myUiDisposed) return;
+
+ super.disposeUIResources();
+
+ myUiDisposed = true;
+
+ myAutoScrollHandler.cancelAllRequests();
+
+ myContext.getDaemonAnalyzer().clear();
+
+ Disposer.dispose(this);
+ }
+
+ public void checkCanApply() throws ConfigurationException {
+ }
+
+ protected void addCollapseExpandActions(final List<AnAction> result) {
+ final TreeExpander expander = new TreeExpander() {
+ @Override
+ public void expandAll() {
+ TreeUtil.expandAll(myTree);
+ }
+
+ @Override
+ public boolean canExpand() {
+ return true;
+ }
+
+ @Override
+ public void collapseAll() {
+ TreeUtil.collapseAll(myTree, 0);
+ }
+
+ @Override
+ public boolean canCollapse() {
+ return true;
+ }
+ };
+ final CommonActionsManager actionsManager = CommonActionsManager.getInstance();
+ result.add(actionsManager.createExpandAllAction(expander, myTree));
+ result.add(actionsManager.createCollapseAllAction(expander, myTree));
+ }
+
+ @Nullable
+ public ProjectStructureElement getSelectedElement() {
+ final TreePath selectionPath = myTree.getSelectionPath();
+ if (selectionPath != null && selectionPath.getLastPathComponent() instanceof MyNode) {
+ MyNode node = (MyNode)selectionPath.getLastPathComponent();
+ final NamedConfigurable configurable = node.getConfigurable();
+ if (configurable instanceof ProjectStructureElementConfigurable) {
+ return ((ProjectStructureElementConfigurable)configurable).getProjectStructureElement();
+ }
+ }
+ return null;
+ }
+
+ private class MyFindUsagesAction extends FindUsagesInProjectStructureActionBase {
+
+ public MyFindUsagesAction(JComponent parentComponent) {
+ super(parentComponent, myProject);
+ }
+
+ @Override
+ protected boolean isEnabled() {
+ final TreePath selectionPath = myTree.getSelectionPath();
+ if (selectionPath != null){
+ final MyNode node = (MyNode)selectionPath.getLastPathComponent();
+ return !node.isDisplayInBold();
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ protected StructureConfigurableContext getContext() {
+ return myContext;
+ }
+
+ @Override
+ protected ProjectStructureElement getSelectedElement() {
+ return BaseStructureConfigurable.this.getSelectedElement();
+ }
+
+ @Override
+ protected RelativePoint getPointToShowResults() {
+ final int selectedRow = myTree.getSelectionRows()[0];
+ final Rectangle rowBounds = myTree.getRowBounds(selectedRow);
+ final Point location = rowBounds.getLocation();
+ location.x += rowBounds.width;
+ return new RelativePoint(myTree, location);
+ }
+ }
+
+
+ @Override
+ public void reset() {
+ myUiDisposed = false;
+
+ if (!myWasTreeInitialized) {
+ initTree();
+ myTree.setShowsRootHandles(false);
+ loadTree();
+ } else {
+ super.disposeUIResources();
+ myTree.setShowsRootHandles(false);
+ loadTree();
+ }
+ for (ProjectStructureElement element : getProjectStructureElements()) {
+ myContext.getDaemonAnalyzer().queueUpdate(element);
+ }
+
+ super.reset();
+ }
+
+ @NotNull
+ protected Collection<? extends ProjectStructureElement> getProjectStructureElements() {
+ return Collections.emptyList();
+ }
+
+ protected abstract void loadTree();
+
+
+ @Override
+ @NotNull
+ protected ArrayList<AnAction> createActions(final boolean fromPopup) {
+ final ArrayList<AnAction> result = new ArrayList<AnAction>();
+ AbstractAddGroup addAction = createAddAction();
+ if (addAction != null) {
+ result.add(addAction);
+ }
+ result.add(new MyRemoveAction());
+
+ final List<? extends AnAction> copyActions = createCopyActions(fromPopup);
+ result.addAll(copyActions);
+ result.add(Separator.getInstance());
+
+ result.add(new MyFindUsagesAction(myTree));
+
+
+ return result;
+ }
+
+ @NotNull
+ protected List<? extends AnAction> createCopyActions(boolean fromPopup) {
+ return Collections.emptyList();
+ }
+
+ public void onStructureUnselected() {
+ }
+
+ public void onStructureSelected() {
+ }
+
+ @Nullable
+ protected abstract AbstractAddGroup createAddAction();
+
+ protected class MyRemoveAction extends MyDeleteAction {
+ public MyRemoveAction() {
+ super(new Condition<Object[]>() {
+ @Override
+ public boolean value(final Object[] objects) {
+ Object[] editableObjects = ContainerUtil.mapNotNull(objects, new Function<Object, Object>() {
+ @Override
+ public Object fun(Object object) {
+ if (object instanceof MyNode) {
+ final NamedConfigurable namedConfigurable = ((MyNode)object).getConfigurable();
+ if (namedConfigurable != null) {
+ return namedConfigurable.getEditableObject();
+ }
+ }
+ return null;
+ }
+ }, new Object[0]);
+ return editableObjects.length == objects.length && canBeRemoved(editableObjects);
+ }
+ });
+ }
+
+ @Override
+ public void actionPerformed(AnActionEvent e) {
+ final TreePath[] paths = myTree.getSelectionPaths();
+ if (paths == null) return;
+
+ final Set<TreePath> pathsToRemove = new HashSet<TreePath>();
+ for (TreePath path : paths) {
+ if (removeFromModel(path)) {
+ pathsToRemove.add(path);
+ }
+ }
+ 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);
+ }
+ }
+
+ protected boolean canBeRemoved(final Object[] editableObjects) {
+ for (Object editableObject : editableObjects) {
+ if (!canObjectBeRemoved(editableObject)) return false;
+ }
+ return true;
+ }
+
+ 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<Facet> facets) {
+ for (Facet facet : facets) {
+ MyNode node = findNodeByObject(myRoot, facet);
+ if (node != null) {
+ removePaths(TreeUtil.getPathFromRoot(node));
+ }
+ }
+ }
+
+ protected List<Facet> 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) {
+ super(text, true);
+
+ final Presentation presentation = getTemplatePresentation();
+ presentation.setIcon(icon);
+
+ final Keymap active = KeymapManager.getInstance().getActiveKeymap();
+ if (active != null) {
+ final Shortcut[] shortcuts = active.getShortcuts("NewElement");
+ setShortcutSet(new CustomShortcutSet(shortcuts));
+ }
+ }
+
+ public AbstractAddGroup(String text) {
+ this(text, IconUtil.getAddIcon());
+ }
+
+ @Override
+ public ActionGroup getActionGroup() {
+ return this;
+ }
+
+ @Override
+ public int getDefaultIndex() {
+ return 0;
+ }
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/ChangeLibraryLevelAction.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/ChangeLibraryLevelAction.java
new file mode 100644
index 0000000..9cd1fa6
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/ChangeLibraryLevelAction.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2000-2011 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.actionSystem.AnActionEvent;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.roots.impl.libraries.LibraryEx;
+import com.intellij.openapi.roots.libraries.Library;
+import com.intellij.openapi.roots.ui.configuration.LibraryTableModifiableModelProvider;
+import com.intellij.openapi.roots.ui.configuration.ProjectStructureConfigurable;
+import com.intellij.openapi.roots.ui.configuration.classpath.ChangeLibraryLevelActionBase;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.daemon.*;
+import org.jetbrains.annotations.NotNull;
+
+import javax.swing.*;
+import java.util.Collection;
+
+/**
+ * @author nik
+ */
+public class ChangeLibraryLevelAction extends ChangeLibraryLevelActionBase {
+ private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.roots.ui.configuration.projectRoot.ChangeLibraryLevelAction");
+ private final JComponent myParentComponent;
+ private final BaseLibrariesConfigurable mySourceConfigurable;
+ private final BaseLibrariesConfigurable myTargetConfigurable;
+
+ public ChangeLibraryLevelAction(@NotNull Project project, @NotNull JComponent parentComponent,
+ @NotNull BaseLibrariesConfigurable sourceConfigurable,
+ @NotNull BaseLibrariesConfigurable targetConfigurable) {
+ super(project, targetConfigurable.getLibraryTablePresentation().getDisplayName(true), targetConfigurable.getLevel(),
+ sourceConfigurable instanceof GlobalLibrariesConfigurable);
+ myParentComponent = parentComponent;
+ mySourceConfigurable = sourceConfigurable;
+ myTargetConfigurable = targetConfigurable;
+ }
+
+ @Override
+ public void actionPerformed(AnActionEvent e) {
+ final ProjectStructureElement selectedElement = mySourceConfigurable.getSelectedElement();
+ if (!(selectedElement instanceof LibraryProjectStructureElement)) return;
+ final StructureConfigurableContext context = mySourceConfigurable.myContext;
+ final LibraryProjectStructureElement libraryElement = (LibraryProjectStructureElement)selectedElement;
+ final LibraryEx oldLibrary = (LibraryEx)context.getLibrary(libraryElement.getLibrary().getName(), mySourceConfigurable.getLevel());
+ LOG.assertTrue(oldLibrary != null);
+ final Library newLibrary = doCopy(oldLibrary);
+ if (newLibrary == null) return;
+
+ final Collection<ProjectStructureElementUsage> usages = context.getDaemonAnalyzer().getUsages(libraryElement);
+ for (ProjectStructureElementUsage usage : usages) {
+ usage.replaceElement(new LibraryProjectStructureElement(context, newLibrary));
+ }
+
+ if (!myCopy) {
+ mySourceConfigurable.removeLibrary(libraryElement);
+ }
+ ProjectStructureConfigurable.getInstance(myProject).selectProjectOrGlobalLibrary(newLibrary, true);
+ }
+
+ @Override
+ protected boolean isEnabled() {
+ return mySourceConfigurable.getSelectedElement() instanceof LibraryProjectStructureElement;
+ }
+
+ @Override
+ protected LibraryTableModifiableModelProvider getModifiableTableModelProvider() {
+ return myTargetConfigurable.getModelProvider();
+ }
+
+ @Override
+ protected JComponent getParentComponent() {
+ return myParentComponent;
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/FacetConfigurable.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/FacetConfigurable.java
new file mode 100644
index 0000000..1055b3b
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/FacetConfigurable.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright 2000-2009 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.facet.Facet;
+import com.intellij.facet.impl.ProjectFacetsConfigurator;
+import com.intellij.facet.impl.invalid.InvalidFacet;
+import com.intellij.facet.impl.ui.FacetEditorImpl;
+import com.intellij.openapi.options.ConfigurationException;
+import com.intellij.openapi.project.ProjectBundle;
+import com.intellij.openapi.roots.ui.configuration.ModulesConfigurator;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.daemon.FacetProjectStructureElement;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.daemon.ProjectStructureElement;
+import org.jetbrains.annotations.Nls;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+
+/**
+ * @author nik
+ */
+public class FacetConfigurable extends ProjectStructureElementConfigurable<Facet> {
+ private final Facet myFacet;
+ private final ModulesConfigurator myModulesConfigurator;
+ private String myFacetName;
+ private FacetProjectStructureElement myProjectStructureElement;
+
+ public FacetConfigurable(final Facet facet, final StructureConfigurableContext context, final Runnable updateTree) {
+ super(!facet.getType().isOnlyOneFacetAllowed() && !(facet instanceof InvalidFacet), updateTree);
+ myFacet = facet;
+ myModulesConfigurator = context.getModulesConfigurator();
+ myFacetName = myFacet.getName();
+ myProjectStructureElement = new FacetProjectStructureElement(context, facet);
+ }
+
+
+ @Override
+ public void setDisplayName(String name) {
+ name = name.trim();
+ if (!name.equals(myFacetName)) {
+ getFacetsConfigurator().getOrCreateModifiableModel(myFacet.getModule()).rename(myFacet, name);
+ myFacetName = name;
+ }
+ }
+
+ @Override
+ public ProjectStructureElement getProjectStructureElement() {
+ return myProjectStructureElement;
+ }
+
+ private ProjectFacetsConfigurator getFacetsConfigurator() {
+ return myModulesConfigurator.getFacetsConfigurator();
+ }
+
+ @Override
+ public Facet getEditableObject() {
+ return myFacet;
+ }
+
+ @Override
+ public String getBannerSlogan() {
+ return ProjectBundle.message("facet.banner.text", myFacetName);
+ }
+
+ @Override
+ public JComponent createOptionsPanel() {
+ return getEditor().getComponent();
+ }
+
+ public FacetEditorImpl getEditor() {
+ return getFacetsConfigurator().getOrCreateEditor(myFacet);
+ }
+
+ @Override
+ @Nls
+ public String getDisplayName() {
+ return myFacetName;
+ }
+
+ @Override
+ @Nullable
+ public Icon getIcon(boolean open) {
+ return myFacet.getType().getIcon();
+ }
+
+ @Override
+ @Nullable
+ @NonNls
+ public String getHelpTopic() {
+ final FacetEditorImpl facetEditor = getFacetsConfigurator().getEditor(myFacet);
+ return facetEditor != null ? facetEditor.getHelpTopic() : null;
+ }
+
+ @Override
+ public boolean isModified() {
+ return false;
+ }
+
+ @Override
+ public void apply() throws ConfigurationException {
+ }
+
+ @Override
+ public void reset() {
+ }
+
+ @Override
+ public void disposeUIResources() {
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/FacetEditorFacadeImpl.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/FacetEditorFacadeImpl.java
new file mode 100644
index 0000000..376abb0
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/FacetEditorFacadeImpl.java
@@ -0,0 +1,207 @@
+/*
+ * Copyright 2000-2009 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.facet.*;
+import com.intellij.facet.impl.ProjectFacetsConfigurator;
+import com.intellij.facet.impl.ui.FacetEditorFacade;
+import com.intellij.facet.impl.ui.FacetTreeModel;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.module.ModuleType;
+import com.intellij.openapi.ui.MasterDetailsComponent;
+import com.intellij.openapi.ui.NamedConfigurable;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.tree.TreeNode;
+import java.util.*;
+
+/**
+ * @author nik
+ */
+public class FacetEditorFacadeImpl implements FacetEditorFacade {
+ private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.roots.ui.configuration.projectRoot.FacetEditorFacadeImpl");
+ private final ModuleStructureConfigurable myStructureConfigurable;
+ private final Runnable myTreeUpdater;
+ private final Map<Facet, MasterDetailsComponent.MyNode> myNodes = new HashMap<Facet, MasterDetailsComponent.MyNode>();
+ private final Map<Facet, FacetConfigurable> myConfigurables = new HashMap<Facet, FacetConfigurable>();
+
+ public FacetEditorFacadeImpl(final ModuleStructureConfigurable structureConfigurable, final Runnable treeUpdater) {
+ myStructureConfigurable = structureConfigurable;
+ myTreeUpdater = treeUpdater;
+ }
+
+ public boolean addFacetsNodes(final Module module, final MasterDetailsComponent.MyNode moduleNode) {
+ boolean facetsExist = false;
+
+ getFacetConfigurator().addFacetInfos(module);
+
+ final FacetModel facetModel = getFacetConfigurator().getFacetModel(module);
+ for (Facet facet : facetModel.getSortedFacets()) {
+ addFacetNode(facet, moduleNode);
+ facetsExist = true;
+ }
+
+ return facetsExist;
+ }
+
+ private void addFacetNode(Facet facet) {
+ MasterDetailsComponent.MyNode moduleNode = myStructureConfigurable.findModuleNode(facet.getModule());
+ if (moduleNode == null) return;
+ addFacetNode(facet, moduleNode);
+ final FacetStructureConfigurable facetStructureConfigurable = FacetStructureConfigurable.getInstance(myStructureConfigurable.getProject());
+ final MasterDetailsComponent.MyNode facetTypeNode = facetStructureConfigurable.getOrCreateFacetTypeNode(facet.getType());
+ LOG.assertTrue(facetTypeNode != null, "Cannot found node for " + facet.getType());
+ facetStructureConfigurable.addFacetNode(facetTypeNode, facet, this);
+ }
+
+ private MasterDetailsComponent.MyNode addFacetNode(final Facet facet, final MasterDetailsComponent.MyNode moduleNode) {
+ final MasterDetailsComponent.MyNode existing = findFacetNode(facet, moduleNode);
+ if (existing != null) return existing;
+
+ final FacetConfigurable facetConfigurable = getOrCreateConfigurable(facet);
+ final MasterDetailsComponent.MyNode facetNode = new MasterDetailsComponent.MyNode(facetConfigurable);
+ myNodes.put(facet, facetNode);
+ MasterDetailsComponent.MyNode parent = moduleNode;
+ final Facet underlyingFacet = facet.getUnderlyingFacet();
+ if (underlyingFacet != null) {
+ parent = myNodes.get(underlyingFacet);
+ LOG.assertTrue(parent != null);
+ }
+ myStructureConfigurable.addNode(facetNode, parent);
+ return facetNode;
+ }
+
+ public FacetConfigurable getOrCreateConfigurable(final Facet facet) {
+ FacetConfigurable configurable = myConfigurables.get(facet);
+ if (configurable == null) {
+ configurable = new FacetConfigurable(facet, myStructureConfigurable.getContext(), myTreeUpdater);
+ myConfigurables.put(facet, configurable);
+ }
+ return configurable;
+ }
+
+ @Nullable
+ private static MasterDetailsComponent.MyNode findFacetNode(final Facet facet, final MasterDetailsComponent.MyNode moduleNode) {
+ for (int i = 0; i < moduleNode.getChildCount(); i++) {
+ final TreeNode node = moduleNode.getChildAt(i);
+ if (node instanceof MasterDetailsComponent.MyNode) {
+ final MasterDetailsComponent.MyNode configNode = (MasterDetailsComponent.MyNode)node;
+ final NamedConfigurable config = configNode.getConfigurable();
+ if (config instanceof FacetConfigurable) {
+ final Facet existingFacet = ((FacetConfigurable)config).getEditableObject();
+ if (existingFacet != null && existingFacet.equals(facet)) {
+ return configNode;
+ }
+ }
+ }
+ }
+
+ return null;
+ }
+
+ @Override
+ public boolean nodeHasFacetOfType(final FacetInfo facet, FacetTypeId typeId) {
+ final Module selectedModule = getSelectedModule();
+ if (selectedModule == null) {
+ return false;
+ }
+ final FacetTreeModel facetTreeModel = getFacetConfigurator().getTreeModel(selectedModule);
+ return facetTreeModel.hasFacetOfType(facet, typeId);
+ }
+
+ @Override
+ public Facet createFacet(final FacetInfo parent, FacetType type) {
+ return createAndAddFacet(type, getSelectedModule(), getFacetConfigurator().getFacet(parent));
+ }
+
+ public Facet createAndAddFacet(FacetType type, Module module, final Facet underlying) {
+ final Facet facet = getFacetConfigurator().createAndAddFacet(module, type, underlying);
+ addFacetNode(facet);
+ return facet;
+ }
+
+ @Override
+ public Collection<FacetInfo> getFacetsByType(final FacetType<?,?> type) {
+ final Module selectedModule = getSelectedModule();
+ if (selectedModule == null) return Collections.emptyList();
+ final FacetModel facetModel = getFacetConfigurator().getFacetModel(selectedModule);
+ final Collection<? extends Facet> facets = facetModel.getFacetsByType(type.getId());
+
+ final ArrayList<FacetInfo> infos = new ArrayList<FacetInfo>();
+ for (Facet facet : facets) {
+ final FacetInfo facetInfo = getFacetConfigurator().getFacetInfo(facet);
+ if (facetInfo != null) {
+ infos.add(facetInfo);
+ }
+ }
+ return infos;
+ }
+
+ @Override
+ @Nullable
+ public FacetInfo getParent(final FacetInfo facetInfo) {
+ final Module module = getFacetConfigurator().getFacet(facetInfo).getModule();
+ return getFacetConfigurator().getTreeModel(module).getParent(facetInfo);
+ }
+
+ private ProjectFacetsConfigurator getFacetConfigurator() {
+ return myStructureConfigurable.getFacetConfigurator();
+ }
+
+ @Nullable
+ private Facet getSelectedFacet() {
+ final Object selectedObject = myStructureConfigurable.getSelectedObject();
+ if (selectedObject instanceof Facet) {
+ return (Facet)selectedObject;
+ }
+ return null;
+ }
+
+ @Nullable
+ private Module getSelectedModule() {
+ final Object selected = myStructureConfigurable.getSelectedObject();
+ if (selected instanceof Module) {
+ return (Module)selected;
+ }
+ if (selected instanceof Facet) {
+ return ((Facet)selected).getModule();
+ }
+ return null;
+ }
+
+ @Override
+ @Nullable
+ public ModuleType getSelectedModuleType() {
+ final Module module = getSelectedModule();
+ return module != null ? ModuleType.get(module) : null;
+ }
+
+ @Override
+ @Nullable
+ public FacetInfo getSelectedFacetInfo() {
+ final Facet facet = getSelectedFacet();
+ return facet != null ? getFacetConfigurator().getFacetInfo(facet) : null;
+ }
+
+ public void clearMaps(boolean clearNodes) {
+ myConfigurables.clear();
+ if (clearNodes) {
+ myNodes.clear();
+ }
+ }
+}
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
new file mode 100644
index 0000000..75899d3
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/FacetStructureConfigurable.java
@@ -0,0 +1,442 @@
+/*
+ * Copyright 2000-2012 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.facet.*;
+import com.intellij.facet.impl.invalid.InvalidFacetManager;
+import com.intellij.facet.impl.invalid.InvalidFacetType;
+import com.intellij.facet.impl.ui.facetType.FacetTypeEditor;
+import com.intellij.facet.ui.FacetEditor;
+import com.intellij.facet.ui.MultipleFacetSettingsEditor;
+import com.intellij.ide.DataManager;
+import com.intellij.openapi.actionSystem.*;
+import com.intellij.openapi.components.ServiceManager;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.module.ModuleManager;
+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.ui.configuration.ProjectStructureConfigurable;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.daemon.FacetProjectStructureElement;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.daemon.ProjectStructureElement;
+import com.intellij.openapi.ui.DetailsComponent;
+import com.intellij.openapi.ui.NamedConfigurable;
+import com.intellij.ui.treeStructure.filtered.FilteringTreeBuilder;
+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.TreeCellRenderer;
+import java.awt.*;
+import java.awt.event.ComponentAdapter;
+import java.awt.event.ComponentEvent;
+import java.util.*;
+import java.util.List;
+
+/**
+ * @author nik
+ */
+public class FacetStructureConfigurable extends BaseStructureConfigurable {
+ private final ModuleManager myModuleManager;
+ private final Map<FacetType<?, ?>, FacetTypeEditor> myFacetTypeEditors = new HashMap<FacetType<?,?>, FacetTypeEditor>();
+ private MultipleFacetSettingsEditor myCurrentMultipleSettingsEditor;
+ @NonNls private static final String NO_FRAMEWORKS_NODE = "No facets are configured";
+ private boolean myTreeWasInitialized;
+
+ public FacetStructureConfigurable(final Project project, ModuleManager moduleManager) {
+ super(project);
+ myModuleManager = moduleManager;
+ }
+
+ @Override
+ protected String getComponentStateKey() {
+ return "FacetStructureConfigurable.UI";
+ }
+
+ public static FacetStructureConfigurable getInstance(final @NotNull Project project) {
+ return ServiceManager.getService(project, FacetStructureConfigurable.class);
+ }
+
+ public boolean isVisible() {
+ return FacetTypeRegistry.getInstance().getFacetTypes().length > 0 || !InvalidFacetManager.getInstance(myProject).getInvalidFacets().isEmpty();
+ }
+
+ @Override
+ protected void initTree() {
+ super.initTree();
+ if (!myTreeWasInitialized) {
+ myTreeWasInitialized = true;
+ final FacetsTreeCellRenderer separatorRenderer = new FacetsTreeCellRenderer();
+ final TreeCellRenderer oldRenderer = myTree.getCellRenderer();
+ myTree.setCellRenderer(new TreeCellRenderer() {
+ @Override
+ public Component getTreeCellRendererComponent(JTree tree,
+ Object value,
+ boolean selected,
+ boolean expanded,
+ boolean leaf,
+ int row,
+ boolean hasFocus) {
+ if (value instanceof MyNode && ((MyNode)value).getConfigurable() instanceof FrameworkDetectionConfigurable) {
+ return separatorRenderer.getTreeCellRendererComponent(tree, value, selected, expanded, leaf, row, hasFocus);
+ }
+ return oldRenderer.getTreeCellRendererComponent(tree, value, selected, expanded, leaf, row, hasFocus);
+ }
+ });
+ myTree.addComponentListener(new ComponentAdapter() {
+ @Override
+ public void componentResized(ComponentEvent e) {
+ revalidateTree();
+ }
+
+ @Override
+ public void componentMoved(ComponentEvent e) {
+ revalidateTree();
+ }
+
+ @Override
+ public void componentShown(ComponentEvent e) {
+ revalidateTree();
+ }
+ });
+ }
+ }
+
+ private void revalidateTree() {
+ FilteringTreeBuilder.revalidateTree(myTree);
+ }
+
+ @Override
+ protected void loadTree() {
+ myTree.setRootVisible(false);
+ myTree.setShowsRootHandles(false);
+ boolean hasFacetTypeNodes = false;
+ for (FacetType<?,?> facetType : FacetTypeRegistry.getInstance().getFacetTypes()) {
+ if (ProjectFacetManager.getInstance(myProject).hasFacets(facetType.getId())) {
+ hasFacetTypeNodes = true;
+ addFacetTypeNode(facetType);
+ }
+ }
+ if (!InvalidFacetManager.getInstance(myProject).getInvalidFacets().isEmpty()) {
+ hasFacetTypeNodes = true;
+ addFacetTypeNode(InvalidFacetType.getInstance());
+ }
+ if (!hasFacetTypeNodes) {
+ addNode(new MyNode(new TextConfigurable<String>(NO_FRAMEWORKS_NODE, NO_FRAMEWORKS_NODE, "Facets", "Press '+' button to add a new facet",
+ null)), myRoot);
+ }
+ addNode(new MyNode(new FrameworkDetectionConfigurable(myProject)), myRoot);
+ }
+
+ @Override
+ protected Comparator<MyNode> getNodeComparator() {
+ return new Comparator<MyNode>() {
+ @Override
+ public int compare(MyNode node1, MyNode node2) {
+ final NamedConfigurable c1 = node1.getConfigurable();
+ final NamedConfigurable c2 = node2.getConfigurable();
+ if (c1 instanceof FrameworkDetectionConfigurable && !(c2 instanceof FrameworkDetectionConfigurable)) return 1;
+ if (!(c1 instanceof FrameworkDetectionConfigurable) && c2 instanceof FrameworkDetectionConfigurable) return -1;
+
+ return node1.getDisplayName().compareToIgnoreCase(node2.getDisplayName());
+ }
+ };
+ }
+
+ private MyNode addFacetTypeNode(FacetType<?, ?> facetType) {
+ final MyNode noFrameworksNode = findNodeByObject(myRoot, NO_FRAMEWORKS_NODE);
+ if (noFrameworksNode != null) {
+ removePaths(TreeUtil.getPathFromRoot(noFrameworksNode));
+ }
+
+ FacetTypeConfigurable facetTypeConfigurable = new FacetTypeConfigurable(this, facetType);
+ MyNode facetTypeNode = new MyNode(facetTypeConfigurable);
+ addNode(facetTypeNode, myRoot);
+
+ for (Module module : myModuleManager.getModules()) {
+ Collection<? extends Facet> facets = FacetManager.getInstance(module).getFacetsByType(facetType.getId());
+ FacetEditorFacadeImpl editorFacade = ModuleStructureConfigurable.getInstance(myProject).getFacetEditorFacade();
+ for (Facet facet : facets) {
+ addFacetNode(facetTypeNode, facet, editorFacade);
+ }
+ }
+ return facetTypeNode;
+ }
+
+ @NotNull
+ @Override
+ protected Collection<? extends ProjectStructureElement> getProjectStructureElements() {
+ List<ProjectStructureElement> elements = new ArrayList<ProjectStructureElement>();
+ for (Module module : myModuleManager.getModules()) {
+ Facet[] facets = FacetManager.getInstance(module).getAllFacets();
+ for (Facet facet : facets) {
+ elements.add(new FacetProjectStructureElement(myContext, facet));
+ }
+ }
+ return elements;
+ }
+
+ public MyNode getOrCreateFacetTypeNode(FacetType facetType) {
+ final MyNode node = findNodeByObject(myRoot, facetType);
+ if (node != null) {
+ return node;
+ }
+ return addFacetTypeNode(facetType);
+ }
+
+ public void addFacetNode(@NotNull MyNode facetTypeNode, @NotNull Facet facet, @NotNull FacetEditorFacadeImpl editorFacade) {
+ FacetConfigurable facetConfigurable = editorFacade.getOrCreateConfigurable(facet);
+ addNode(new FacetConfigurableNode(facetConfigurable), facetTypeNode);
+ myContext.getDaemonAnalyzer().queueUpdate(new FacetProjectStructureElement(myContext, facet));
+ }
+
+ @Nullable
+ public FacetTypeEditor getFacetTypeEditor(@NotNull FacetType<?, ?> facetType) {
+ return myFacetTypeEditors.get(facetType);
+ }
+
+ public FacetTypeEditor getOrCreateFacetTypeEditor(@NotNull FacetType<?, ?> facetType) {
+ FacetTypeEditor editor = myFacetTypeEditors.get(facetType);
+ if (editor == null) {
+ editor = new FacetTypeEditor(myProject, myContext, facetType);
+ editor.reset();
+ myFacetTypeEditors.put(facetType, editor);
+ }
+ return editor;
+ }
+
+ @Override
+ public void reset() {
+ myFacetTypeEditors.clear();
+ super.reset();
+ TreeUtil.expandAll(myTree);
+ }
+
+
+ @Override
+ public void apply() throws ConfigurationException {
+ super.apply();
+ for (FacetTypeEditor editor : myFacetTypeEditors.values()) {
+ editor.apply();
+ }
+ }
+
+ @Override
+ public boolean isModified() {
+ return super.isModified() || isEditorsModified();
+ }
+
+ private boolean isEditorsModified() {
+ for (FacetTypeEditor editor : myFacetTypeEditors.values()) {
+ if (editor.isModified()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public void disposeUIResources() {
+ super.disposeUIResources();
+
+ for (FacetTypeEditor editor : myFacetTypeEditors.values()) {
+ editor.disposeUIResources();
+ }
+ myFacetTypeEditors.clear();
+ }
+
+ @Override
+ @NotNull
+ protected ArrayList<AnAction> createActions(final boolean fromPopup) {
+ ArrayList<AnAction> actions = new ArrayList<AnAction>();
+ actions.add(new AbstractAddGroup("Add") {
+ @NotNull
+ @Override
+ public AnAction[] getChildren(@Nullable AnActionEvent e) {
+ return AddFacetOfTypeAction.createAddFacetActions(FacetStructureConfigurable.this);
+ }
+ });
+ if (fromPopup) {
+ actions.add(new MyNavigateAction());
+ }
+ actions.add(new MyRemoveAction());
+ actions.add(Separator.getInstance());
+ addCollapseExpandActions(actions);
+ return actions;
+ }
+
+ @Override
+ protected List<Facet> removeFacet(final Facet facet) {
+ List<Facet> 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<NamedConfigurable> selectedConfigurables) {
+ return updateMultiSelection(selectedConfigurables, getDetailsComponent());
+ }
+
+ public boolean updateMultiSelection(final List<NamedConfigurable> selectedConfigurables, final DetailsComponent detailsComponent) {
+ FacetType selectedFacetType = null;
+ List<FacetEditor> facetEditors = new ArrayList<FacetEditor>();
+ for (NamedConfigurable selectedConfigurable : selectedConfigurables) {
+ if (selectedConfigurable instanceof FacetConfigurable) {
+ FacetConfigurable facetConfigurable = (FacetConfigurable)selectedConfigurable;
+ FacetType facetType = facetConfigurable.getEditableObject().getType();
+ if (selectedFacetType != null && selectedFacetType != facetType) {
+ return false;
+ }
+ selectedFacetType = facetType;
+ facetEditors.add(facetConfigurable.getEditor());
+ }
+ }
+ if (facetEditors.size() <= 1 || selectedFacetType == null) {
+ return false;
+ }
+
+ FacetEditor[] selectedEditors = facetEditors.toArray(new FacetEditor[facetEditors.size()]);
+ MultipleFacetSettingsEditor editor = selectedFacetType.createMultipleConfigurationsEditor(myProject, selectedEditors);
+ if (editor == null) {
+ return false;
+ }
+
+ setSelectedNode(null);
+ myCurrentMultipleSettingsEditor = editor;
+ detailsComponent.setText(ProjectBundle.message("multiple.facets.banner.0.1.facets", selectedEditors.length,
+ selectedFacetType.getPresentableName()));
+ detailsComponent.setContent(editor.createComponent());
+ return true;
+ }
+
+ @Override
+ protected void updateSelection(@Nullable final NamedConfigurable configurable) {
+ disposeMultipleSettingsEditor();
+ if (configurable instanceof FacetTypeConfigurable) {
+ ((FacetTypeConfigurable)configurable).updateComponent();
+ }
+ super.updateSelection(configurable);
+ }
+
+ public void disposeMultipleSettingsEditor() {
+ if (myCurrentMultipleSettingsEditor != null) {
+ myCurrentMultipleSettingsEditor.disposeUIResources();
+ myCurrentMultipleSettingsEditor = null;
+ }
+ }
+
+ @Override
+ @Nullable
+ protected AbstractAddGroup createAddAction() {
+ return null;
+ }
+
+ @Override
+ protected void processRemovedItems() {
+ }
+
+ @Override
+ protected boolean wasObjectStored(final Object editableObject) {
+ return false;
+ }
+
+ @Override
+ public String getDisplayName() {
+ return ProjectBundle.message("project.facets.display.name");
+ }
+
+ @Override
+ public String getHelpTopic() {
+ final Component component = PlatformDataKeys.CONTEXT_COMPONENT.getData(DataManager.getInstance().getDataContext());
+ if (myTree.equals(component)) {
+ final NamedConfigurable selectedConfigurable = getSelectedConfigurable();
+ if (selectedConfigurable instanceof FacetTypeConfigurable) {
+ final FacetType facetType = ((FacetTypeConfigurable)selectedConfigurable).getEditableObject();
+ final String topic = facetType.getHelpTopic();
+ if (topic != null) {
+ return topic;
+ }
+ }
+ }
+ if (myCurrentMultipleSettingsEditor != null) {
+ final String topic = myCurrentMultipleSettingsEditor.getHelpTopic();
+ if (topic != null) {
+ return topic;
+ }
+ }
+ String topic = super.getHelpTopic();
+ if (topic != null) {
+ return topic;
+ }
+ return "reference.settingsdialog.project.structure.facet";
+ }
+
+ @Override
+ @NotNull
+ public String getId() {
+ return "project.facets";
+ }
+
+ @Override
+ public Runnable enableSearch(final String option) {
+ return null;
+ }
+
+ @Override
+ public void dispose() {
+ }
+
+ private class FacetConfigurableNode extends MyNode {
+ public FacetConfigurableNode(final FacetConfigurable facetConfigurable) {
+ super(facetConfigurable);
+ }
+
+ @Override
+ @NotNull
+ public String getDisplayName() {
+ FacetConfigurable facetConfigurable = (FacetConfigurable)getConfigurable();
+ String moduleName = myContext.getRealName(facetConfigurable.getEditableObject().getModule());
+ return facetConfigurable.getDisplayName() + " (" + moduleName + ")";
+ }
+ }
+
+ private class MyNavigateAction extends AnAction implements DumbAware {
+ private MyNavigateAction() {
+ super(ProjectBundle.message("action.name.facet.navigate"));
+ registerCustomShortcutSet(CommonShortcuts.getEditSource(), myTree);
+ }
+
+ @Override
+ public void update(final AnActionEvent e) {
+ NamedConfigurable selected = getSelectedConfigurable();
+ e.getPresentation().setEnabled(selected instanceof FacetConfigurable);
+ }
+
+ @Override
+ public void actionPerformed(final AnActionEvent e) {
+ NamedConfigurable selected = getSelectedConfigurable();
+ if (selected instanceof FacetConfigurable) {
+ ProjectStructureConfigurable.getInstance(myProject).select(((FacetConfigurable)selected).getEditableObject(), true);
+ }
+ }
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/FacetTypeConfigurable.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/FacetTypeConfigurable.java
new file mode 100644
index 0000000..7f653a4
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/FacetTypeConfigurable.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2000-2009 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.facet.FacetType;
+import com.intellij.facet.impl.ui.facetType.FacetTypeEditor;
+import com.intellij.openapi.options.ConfigurationException;
+import com.intellij.openapi.project.ProjectBundle;
+import com.intellij.openapi.ui.NamedConfigurable;
+
+import javax.swing.*;
+
+/**
+ * @author nik
+ */
+public class FacetTypeConfigurable extends NamedConfigurable<FacetType> {
+ private final FacetStructureConfigurable myFacetStructureConfigurable;
+ private final FacetType myFacetType;
+
+ public FacetTypeConfigurable(final FacetStructureConfigurable facetStructureConfigurable, final FacetType facetType) {
+ myFacetStructureConfigurable = facetStructureConfigurable;
+ myFacetType = facetType;
+ }
+
+ @Override
+ public void setDisplayName(final String name) {
+ }
+
+ @Override
+ public FacetType getEditableObject() {
+ return myFacetType;
+ }
+
+ @Override
+ public String getBannerSlogan() {
+ return ProjectBundle.message("facet.type.banner.text", myFacetType.getPresentableName());
+ }
+
+ @Override
+ public JComponent createOptionsPanel() {
+ return myFacetStructureConfigurable.getOrCreateFacetTypeEditor(myFacetType).createComponent();
+ }
+
+ @Override
+ public String getDisplayName() {
+ return myFacetType.getPresentableName();
+ }
+
+ @Override
+ public String getHelpTopic() {
+ final FacetTypeEditor editor = myFacetStructureConfigurable.getFacetTypeEditor(myFacetType);
+ return editor != null ? editor.getHelpTopic() : null;
+ }
+
+ @Override
+ public boolean isModified() {
+ return false;
+ }
+
+ @Override
+ public void apply() throws ConfigurationException {
+ }
+
+ @Override
+ public void reset() {
+ }
+
+ @Override
+ public void disposeUIResources() {
+ }
+
+ public void updateComponent() {
+ resetOptionsPanel();
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/FacetsTreeCellRenderer.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/FacetsTreeCellRenderer.java
new file mode 100644
index 0000000..d89fe37
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/FacetsTreeCellRenderer.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2000-2011 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.MasterDetailsComponent;
+import com.intellij.openapi.ui.NamedConfigurable;
+import com.intellij.ui.ErrorLabel;
+import com.intellij.ui.GroupedElementsRenderer;
+
+import javax.swing.*;
+import java.awt.*;
+
+/**
+ * @author nik
+ */
+public class FacetsTreeCellRenderer extends GroupedElementsRenderer.Tree {
+ @Override
+ protected JComponent createItemComponent() {
+ myTextLabel = new ErrorLabel();
+ return myTextLabel;
+ }
+
+ @Override
+ protected void layout() {
+ myRendererComponent.setOpaqueActive(false);
+ myRendererComponent.add(mySeparatorComponent, BorderLayout.NORTH);
+ myRendererComponent.add(myComponent, BorderLayout.CENTER);
+ }
+
+ @Override
+ public Component getTreeCellRendererComponent(JTree tree,
+ Object value,
+ boolean selected,
+ boolean expanded,
+ boolean leaf,
+ int row,
+ boolean hasFocus) {
+ if (value instanceof MasterDetailsComponent.MyNode) {
+ final MasterDetailsComponent.MyNode node = (MasterDetailsComponent.MyNode)value;
+ final NamedConfigurable configurable = node.getConfigurable();
+ if (configurable != null) {
+ final Icon icon = configurable.getIcon(expanded);
+ final boolean showSeparator = configurable instanceof FrameworkDetectionConfigurable;
+ int width = -1;
+ if (showSeparator && tree.isVisible()) {
+ final int treeWidth = tree.getVisibleRect().width;
+ if (treeWidth > 0) {
+ width = treeWidth;
+ }
+ }
+ final JComponent component = configureComponent(node.getDisplayName(), null, icon, icon, selected, showSeparator, null,
+ width);
+
+ myTextLabel.setOpaque(selected);
+ return component;
+ }
+ }
+ return myRendererComponent;
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/FindUsagesInProjectStructureActionBase.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/FindUsagesInProjectStructureActionBase.java
new file mode 100644
index 0000000..67b9f35
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/FindUsagesInProjectStructureActionBase.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright 2000-2009 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.find.FindBundle;
+import com.intellij.icons.AllIcons;
+import com.intellij.openapi.actionSystem.ActionManager;
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.actionSystem.IdeActions;
+import com.intellij.openapi.project.DumbAware;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.project.ProjectBundle;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.daemon.ProjectStructureElement;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.daemon.ProjectStructureElementUsage;
+import com.intellij.openapi.ui.Messages;
+import com.intellij.openapi.ui.popup.PopupStep;
+import com.intellij.openapi.ui.popup.util.BaseListPopupStep;
+import com.intellij.ui.ListCellRendererWithRightAlignedComponent;
+import com.intellij.ui.awt.RelativePoint;
+import com.intellij.ui.popup.list.ListPopupImpl;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import java.awt.*;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Comparator;
+
+/**
+ * @author nik
+ */
+public abstract class FindUsagesInProjectStructureActionBase extends AnAction implements DumbAware {
+ private final JComponent myParentComponent;
+ private final Project myProject;
+
+ public FindUsagesInProjectStructureActionBase(JComponent parentComponent, Project project) {
+ super(ProjectBundle.message("find.usages.action.text"), ProjectBundle.message("find.usages.action.text"), AllIcons.Actions.Find);
+ registerCustomShortcutSet(ActionManager.getInstance().getAction(IdeActions.ACTION_FIND_USAGES).getShortcutSet(), parentComponent);
+ myParentComponent = parentComponent;
+ myProject = project;
+ }
+
+ @Override
+ public void update(AnActionEvent e) {
+ e.getPresentation().setEnabled(isEnabled());
+ }
+
+ protected abstract boolean isEnabled();
+
+ @Override
+ public void actionPerformed(AnActionEvent e) {
+ final ProjectStructureElement selected = getSelectedElement();
+ if (selected == null) return;
+
+ final Collection<ProjectStructureElementUsage> usages = getContext().getDaemonAnalyzer().getUsages(selected);
+ if (usages.isEmpty()) {
+ Messages.showInfoMessage(myParentComponent, FindBundle.message("find.usage.view.no.usages.text"), FindBundle.message("find.pointcut.applications.not.found.title"));
+ return;
+ }
+
+ RelativePoint point = getPointToShowResults();
+ final ProjectStructureElementUsage[] usagesArray = usages.toArray(new ProjectStructureElementUsage[usages.size()]);
+ Arrays.sort(usagesArray, new Comparator<ProjectStructureElementUsage>() {
+ @Override
+ public int compare(ProjectStructureElementUsage o1, ProjectStructureElementUsage o2) {
+ return o1.getPresentableName().compareToIgnoreCase(o2.getPresentableName());
+ }
+ });
+
+ BaseListPopupStep<ProjectStructureElementUsage> step =
+ new BaseListPopupStep<ProjectStructureElementUsage>(ProjectBundle.message("dependencies.used.in.popup.title"), usagesArray) {
+ @Override
+ public PopupStep onChosen(final ProjectStructureElementUsage selected, final boolean finalChoice) {
+ selected.getPlace().navigate();
+ return FINAL_CHOICE;
+ }
+
+ @NotNull
+ @Override
+ public String getTextFor(ProjectStructureElementUsage value) {
+ return value.getPresentableName();
+ }
+
+ @Override
+ public Icon getIconFor(ProjectStructureElementUsage selection) {
+ return selection.getIcon();
+ }
+ };
+ new ListPopupImpl(step) {
+ @Override
+ protected ListCellRenderer getListElementRenderer() {
+ return new ListCellRendererWithRightAlignedComponent<ProjectStructureElementUsage>() {
+ @Override
+ protected void customize(ProjectStructureElementUsage value) {
+ setLeftText(value.getPresentableName());
+ setIcon(value.getIcon());
+ setRightForeground(Color.GRAY);
+ setRightText(value.getPresentableLocationInElement());
+ }
+ };
+ }
+ }.show(point);
+ }
+
+ @Nullable
+ protected abstract ProjectStructureElement getSelectedElement();
+
+ protected StructureConfigurableContext getContext() {
+ return ModuleStructureConfigurable.getInstance(myProject).getContext();
+ }
+
+ protected abstract RelativePoint getPointToShowResults();
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/FrameworkDetectionConfigurable.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/FrameworkDetectionConfigurable.java
new file mode 100644
index 0000000..edb6a89
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/FrameworkDetectionConfigurable.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2000-2011 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.framework.detection.DetectionExcludesConfiguration;
+import com.intellij.framework.detection.impl.exclude.DetectionExcludesConfigurable;
+import com.intellij.framework.detection.impl.exclude.DetectionExcludesConfigurationImpl;
+import com.intellij.openapi.options.ConfigurationException;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.NamedConfigurable;
+import org.jetbrains.annotations.Nls;
+import org.jetbrains.annotations.NotNull;
+
+import javax.swing.*;
+
+/**
+ * @author nik
+ */
+public class FrameworkDetectionConfigurable extends NamedConfigurable<DetectionExcludesConfiguration> {
+ private final DetectionExcludesConfiguration myExcludesConfiguration;
+ private final DetectionExcludesConfigurable myConfigurable;
+
+ public FrameworkDetectionConfigurable(@NotNull Project project) {
+ myExcludesConfiguration = DetectionExcludesConfiguration.getInstance(project);
+ myConfigurable = new DetectionExcludesConfigurable(project, (DetectionExcludesConfigurationImpl)myExcludesConfiguration);
+ }
+
+ @Override
+ public void setDisplayName(String name) {
+ }
+
+ @Override
+ public DetectionExcludesConfiguration getEditableObject() {
+ return myExcludesConfiguration;
+ }
+
+ @Override
+ public String getBannerSlogan() {
+ return "Framework Detection";
+ }
+
+ @Override
+ public JComponent createOptionsPanel() {
+ return myConfigurable.createComponent();
+ }
+
+ @Nls
+ @Override
+ public String getDisplayName() {
+ return "Detection";
+ }
+
+ @Override
+ public String getHelpTopic() {
+ return myConfigurable.getHelpTopic();
+ }
+
+ @Override
+ public boolean isModified() {
+ return myConfigurable.isModified();
+ }
+
+ @Override
+ public void apply() throws ConfigurationException {
+ myConfigurable.apply();
+ }
+
+ @Override
+ public void reset() {
+ myConfigurable.reset();
+ }
+
+ @Override
+ public void disposeUIResources() {
+ myConfigurable.disposeUIResources();
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/GlobalLibrariesConfigurable.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/GlobalLibrariesConfigurable.java
new file mode 100644
index 0000000..d3c2d6a
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/GlobalLibrariesConfigurable.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2000-2009 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.components.ServiceManager;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.project.ProjectBundle;
+import com.intellij.openapi.roots.libraries.LibraryTablePresentation;
+import com.intellij.openapi.roots.libraries.LibraryTablesRegistrar;
+import org.jetbrains.annotations.Nls;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+
+public class GlobalLibrariesConfigurable extends BaseLibrariesConfigurable {
+
+ public GlobalLibrariesConfigurable(final Project project) {
+ super(project);
+ myLevel = LibraryTablesRegistrar.APPLICATION_LEVEL;
+ }
+
+ @Override
+ protected String getComponentStateKey() {
+ return "GlobalLibrariesConfigurable.UI";
+ }
+
+ @Override
+ @Nls
+ public String getDisplayName() {
+ return "Global Libraries";
+ }
+
+ @Override
+ @NotNull
+ @NonNls
+ public String getId() {
+ return "global.libraries";
+ }
+
+
+ public static GlobalLibrariesConfigurable getInstance(final Project project) {
+ return ServiceManager.getService(project, GlobalLibrariesConfigurable.class);
+ }
+
+ @Override
+ public LibraryTablePresentation getLibraryTablePresentation() {
+ return LibraryTablesRegistrar.getInstance().getLibraryTable().getPresentation();
+ }
+
+ @Override
+ public StructureLibraryTableModifiableModelProvider getModelProvider() {
+ return myContext.getGlobalLibrariesProvider();
+ }
+
+ @Override
+ public BaseLibrariesConfigurable getOppositeGroup() {
+ return ProjectLibrariesConfigurable.getInstance(myProject);
+ }
+
+ @Override
+ protected String getAddText() {
+ return ProjectBundle.message("add.new.global.library.text");
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/JdkConfigurable.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/JdkConfigurable.java
new file mode 100644
index 0000000..0729c12
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/JdkConfigurable.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright 2000-2012 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.options.ConfigurationException;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.project.ProjectBundle;
+import com.intellij.openapi.projectRoots.Sdk;
+import com.intellij.openapi.projectRoots.SdkType;
+import com.intellij.openapi.projectRoots.impl.ProjectJdkImpl;
+import com.intellij.openapi.projectRoots.ui.SdkEditor;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.daemon.ProjectStructureElement;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.daemon.SdkProjectStructureElement;
+import com.intellij.openapi.util.ActionCallback;
+import com.intellij.ui.navigation.History;
+import com.intellij.ui.navigation.Place;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+
+/**
+ * User: anna
+ * Date: 05-Jun-2006
+ */
+public class JdkConfigurable extends ProjectStructureElementConfigurable<Sdk> implements Place.Navigator {
+ private final ProjectJdkImpl myProjectJdk;
+ private final SdkEditor mySdkEditor;
+ private final SdkProjectStructureElement myProjectStructureElement;
+
+ public JdkConfigurable(final ProjectJdkImpl projectJdk,
+ final ProjectSdksModel sdksModel,
+ final Runnable updateTree, @NotNull History history, Project project) {
+ super(true, updateTree);
+ myProjectJdk = projectJdk;
+ mySdkEditor = createSdkEditor(sdksModel, history, myProjectJdk);
+ final StructureConfigurableContext context = ModuleStructureConfigurable.getInstance(project).getContext();
+ myProjectStructureElement = new SdkProjectStructureElement(context, myProjectJdk);
+ }
+
+ protected SdkEditor createSdkEditor(ProjectSdksModel sdksModel, History history, ProjectJdkImpl projectJdk) {
+ return new SdkEditor(sdksModel, history, projectJdk);
+ }
+
+ @Override
+ public ProjectStructureElement getProjectStructureElement() {
+ return myProjectStructureElement;
+ }
+
+ @Override
+ public void setDisplayName(final String name) {
+ myProjectJdk.setName(name);
+ }
+
+ @Override
+ public Sdk getEditableObject() {
+ return myProjectJdk;
+ }
+
+ @Override
+ public String getBannerSlogan() {
+ return ProjectBundle.message("project.roots.jdk.banner.text", myProjectJdk.getName());
+ }
+
+ @Override
+ public String getDisplayName() {
+ return myProjectJdk.getName();
+ }
+
+ @Override
+ public Icon getIcon(boolean open) {
+ return ((SdkType) myProjectJdk.getSdkType()).getIcon();
+ }
+
+ @Override
+ @Nullable
+ @NonNls
+ public String getHelpTopic() {
+ return ((SdkType) myProjectJdk.getSdkType()).getHelpTopic();
+ }
+
+
+ @Override
+ public JComponent createOptionsPanel() {
+ return mySdkEditor.createComponent();
+ }
+
+ @Override
+ public boolean isModified() {
+ return mySdkEditor.isModified();
+ }
+
+ @Override
+ public void apply() throws ConfigurationException {
+ mySdkEditor.apply();
+ }
+
+ @Override
+ public void reset() {
+ mySdkEditor.reset();
+ }
+
+ @Override
+ public void disposeUIResources() {
+ mySdkEditor.disposeUIResources();
+ }
+
+ @Override
+ public void setHistory(final History history) {
+ }
+
+ @Override
+ public ActionCallback navigateTo(@Nullable final Place place, final boolean requestFocus) {
+ return mySdkEditor.navigateTo(place, requestFocus);
+ }
+
+ @Override
+ public void queryPlace(@NotNull final Place place) {
+ mySdkEditor.queryPlace(place);
+ }
+}
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
new file mode 100644
index 0000000..b605092
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/JdkListConfigurable.java
@@ -0,0 +1,225 @@
+/*
+ * Copyright 2000-2009 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.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.actionSystem.DefaultActionGroup;
+import com.intellij.openapi.components.ServiceManager;
+import com.intellij.openapi.options.ConfigurationException;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.project.ProjectBundle;
+import com.intellij.openapi.projectRoots.Sdk;
+import com.intellij.openapi.projectRoots.SdkModel;
+import com.intellij.openapi.projectRoots.impl.ProjectJdkImpl;
+import com.intellij.openapi.roots.ProjectRootManager;
+import com.intellij.openapi.roots.ui.configuration.ProjectStructureConfigurable;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.daemon.ProjectStructureElement;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.daemon.SdkProjectStructureElement;
+import com.intellij.openapi.ui.MasterDetailsComponent;
+import com.intellij.openapi.ui.NamedConfigurable;
+import com.intellij.util.Consumer;
+import org.jetbrains.annotations.Nls;
+import org.jetbrains.annotations.NonNls;
+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;
+
+public class JdkListConfigurable extends BaseStructureConfigurable {
+ private final ProjectSdksModel myJdksTreeModel;
+ private final SdkModel.Listener myListener = new SdkModel.Listener() {
+ @Override
+ public void sdkAdded(Sdk sdk) {
+ }
+
+ @Override
+ public void beforeSdkRemove(Sdk sdk) {
+ }
+
+ @Override
+ public void sdkChanged(Sdk sdk, String previousName) {
+ updateName();
+ }
+
+ @Override
+ public void sdkHomeSelected(Sdk sdk, String newSdkHome) {
+ updateName();
+ }
+
+ private void updateName() {
+ final TreePath path = myTree.getSelectionPath();
+ if (path != null) {
+ final NamedConfigurable configurable = ((MyNode)path.getLastPathComponent()).getConfigurable();
+ if (configurable != null && configurable instanceof JdkConfigurable) {
+ configurable.updateName();
+ }
+ }
+ }
+ };
+
+ public JdkListConfigurable(final Project project, ProjectStructureConfigurable root) {
+ super(project);
+ myJdksTreeModel = root.getProjectJdksModel();
+ myJdksTreeModel.addListener(myListener);
+ }
+
+ @Override
+ protected String getComponentStateKey() {
+ return "JdkListConfigurable.UI";
+ }
+
+ @Override
+ protected void processRemovedItems() {
+ }
+
+ @Override
+ protected boolean wasObjectStored(final Object editableObject) {
+ return false;
+ }
+
+ @Override
+ @Nls
+ public String getDisplayName() {
+ return "SDKs";
+ }
+
+ @Override
+ @Nullable
+ @NonNls
+ public String getHelpTopic() {
+ return myCurrentConfigurable != null ? myCurrentConfigurable.getHelpTopic() : "reference.settingsdialog.project.structure.jdk";
+ }
+
+ @Override
+ @NotNull
+ @NonNls
+ public String getId() {
+ return "jdk.list";
+ }
+
+ @Override
+ @Nullable
+ public Runnable enableSearch(final String option) {
+ return null;
+ }
+
+ @Override
+ protected void loadTree() {
+ final Map<Sdk,Sdk> sdks = myJdksTreeModel.getProjectSdks();
+ for (Sdk sdk : sdks.keySet()) {
+ final JdkConfigurable configurable = new JdkConfigurable((ProjectJdkImpl)sdks.get(sdk), myJdksTreeModel, TREE_UPDATER, myHistory,
+ myProject);
+ addNode(new MyNode(configurable), myRoot);
+ }
+ }
+
+ @NotNull
+ @Override
+ protected Collection<? extends ProjectStructureElement> getProjectStructureElements() {
+ final List<ProjectStructureElement> result = new ArrayList<ProjectStructureElement>();
+ for (Sdk sdk : myJdksTreeModel.getProjectSdks().values()) {
+ result.add(new SdkProjectStructureElement(myContext, sdk));
+ }
+ return result;
+ }
+
+ public boolean addJdkNode(final Sdk jdk, final boolean selectInTree) {
+ if (!myUiDisposed) {
+ myContext.getDaemonAnalyzer().queueUpdate(new SdkProjectStructureElement(myContext, jdk));
+ addNode(new MyNode(new JdkConfigurable((ProjectJdkImpl)jdk, myJdksTreeModel, TREE_UPDATER, myHistory, myProject)), myRoot);
+ if (selectInTree) {
+ selectNodeInTree(MasterDetailsComponent.findNodeByObject(myRoot, jdk));
+ }
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public void dispose() {
+ myJdksTreeModel.removeListener(myListener);
+ myJdksTreeModel.disposeUIResources();
+ }
+
+ public ProjectSdksModel getJdksTreeModel() {
+ return myJdksTreeModel;
+ }
+
+ @Override
+ public void reset() {
+ super.reset();
+ myTree.setRootVisible(false);
+ }
+
+ @Override
+ public void apply() throws ConfigurationException {
+ boolean modifiedJdks = false;
+ for (int i = 0; i < myRoot.getChildCount(); i++) {
+ final NamedConfigurable configurable = ((MyNode)myRoot.getChildAt(i)).getConfigurable();
+ if (configurable.isModified()) {
+ configurable.apply();
+ modifiedJdks = true;
+ }
+ }
+
+ if (myJdksTreeModel.isModified() || modifiedJdks) myJdksTreeModel.apply(this);
+ myJdksTreeModel.setProjectSdk(ProjectRootManager.getInstance(myProject).getProjectSdk());
+ }
+
+ @Override
+ public boolean isModified() {
+ return super.isModified() || myJdksTreeModel.isModified();
+ }
+
+ public static JdkListConfigurable getInstance(Project project) {
+ return ServiceManager.getService(project, JdkListConfigurable.class);
+ }
+
+ @Override
+ public AbstractAddGroup createAddAction() {
+ return new AbstractAddGroup(ProjectBundle.message("add.new.jdk.text")) {
+ @Override
+ public AnAction[] getChildren(@Nullable final AnActionEvent e) {
+ DefaultActionGroup group = new DefaultActionGroup(ProjectBundle.message("add.new.jdk.text"), true);
+ myJdksTreeModel.createAddActions(group, myTree, new Consumer<Sdk>() {
+ @Override
+ public void consume(final Sdk projectJdk) {
+ addJdkNode(projectJdk, true);
+ }
+ });
+ return group.getChildren(null);
+ }
+ };
+ }
+
+ @Override
+ protected void removeJdk(final Sdk jdk) {
+ myJdksTreeModel.removeSdk(jdk);
+ myContext.getDaemonAnalyzer().removeElement(new SdkProjectStructureElement(myContext, jdk));
+ }
+
+ @Override
+ protected
+ @Nullable
+ String getEmptySelectionString() {
+ return "Select an SDK to view or edit its details here";
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/LibrariesContainer.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/LibrariesContainer.java
new file mode 100644
index 0000000..eccb4ea
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/LibrariesContainer.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2000-2009 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.project.Project;
+import com.intellij.openapi.roots.OrderRootType;
+import com.intellij.openapi.roots.libraries.Library;
+import com.intellij.openapi.roots.libraries.ui.OrderRoot;
+import com.intellij.openapi.roots.ui.configuration.libraryEditor.ExistingLibraryEditor;
+import com.intellij.openapi.roots.ui.configuration.libraryEditor.NewLibraryEditor;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.openapi.vfs.VirtualFile;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * @author nik
+ */
+public interface LibrariesContainer {
+
+ @Nullable
+ Project getProject();
+
+ enum LibraryLevel {GLOBAL, PROJECT, MODULE;
+ public String toString() {
+ return StringUtil.capitalize(name().toLowerCase());
+ }
+ }
+
+ @NotNull
+ Library[] getLibraries(@NotNull LibraryLevel libraryLevel);
+
+ @NotNull
+ Library[] getAllLibraries();
+
+ @NotNull
+ VirtualFile[] getLibraryFiles(@NotNull Library library, @NotNull OrderRootType rootType);
+
+ boolean canCreateLibrary(@NotNull LibraryLevel level);
+
+ @NotNull
+ List<LibraryLevel> getAvailableLevels();
+
+ Library createLibrary(@NotNull @NonNls String name, @NotNull LibraryLevel level,
+ @NotNull VirtualFile[] classRoots, @NotNull VirtualFile[] sourceRoots);
+
+ Library createLibrary(@NotNull @NonNls String name, @NotNull LibraryLevel level,
+ @NotNull Collection<? extends OrderRoot> roots);
+
+ Library createLibrary(@NotNull NewLibraryEditor libraryEditor, @NotNull LibraryLevel level);
+
+ @NotNull
+ String suggestUniqueLibraryName(@NotNull String baseName);
+
+ @Nullable
+ ExistingLibraryEditor getLibraryEditor(@NotNull Library library);
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/LibrariesContainerFactory.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/LibrariesContainerFactory.java
new file mode 100644
index 0000000..993f88a
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/LibrariesContainerFactory.java
@@ -0,0 +1,376 @@
+/*
+ * Copyright 2000-2012 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.ide.util.projectWizard.WizardContext;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.roots.*;
+import com.intellij.openapi.roots.impl.libraries.LibraryEx;
+import com.intellij.openapi.roots.impl.libraries.LibraryTableBase;
+import com.intellij.openapi.roots.libraries.Library;
+import com.intellij.openapi.roots.libraries.LibraryTable;
+import com.intellij.openapi.roots.libraries.LibraryTablesRegistrar;
+import com.intellij.openapi.roots.libraries.LibraryType;
+import com.intellij.openapi.roots.libraries.ui.OrderRoot;
+import com.intellij.openapi.roots.ui.configuration.LibraryTableModifiableModelProvider;
+import com.intellij.openapi.roots.ui.configuration.ModulesConfigurator;
+import com.intellij.openapi.roots.ui.configuration.ModulesProvider;
+import com.intellij.openapi.roots.ui.configuration.libraryEditor.ExistingLibraryEditor;
+import com.intellij.openapi.roots.ui.configuration.libraryEditor.LibraryEditor;
+import com.intellij.openapi.roots.ui.configuration.libraryEditor.NewLibraryEditor;
+import com.intellij.openapi.util.Condition;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.util.ArrayUtil;
+import com.intellij.util.Function;
+import com.intellij.util.text.UniqueNameGenerator;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * @author nik
+ */
+public class LibrariesContainerFactory {
+ private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.roots.ui.configuration.projectRoot.LibrariesContainerFactory");
+ private static final Library[] EMPTY_LIBRARIES_ARRAY = new Library[0];
+
+ private LibrariesContainerFactory() {
+ }
+
+ @NotNull
+ public static LibrariesContainer createContainer(@Nullable Project project) {
+ return new LibrariesContainerImpl(project, null, null);
+ }
+
+ @NotNull
+ public static LibrariesContainer createContainer(@NotNull Module module) {
+ return new LibrariesContainerImpl(module.getProject(), module, null);
+ }
+
+ @NotNull
+ public static LibrariesContainer createContainer(@NotNull ModifiableRootModel rootModel) {
+ Module module = rootModel.getModule();
+ return new LibrariesContainerImpl(module.getProject(), module, rootModel);
+ }
+
+ public static LibrariesContainer createContainer(StructureConfigurableContext context) {
+ return new StructureConfigurableLibrariesContainer(context);
+ }
+
+ public static Library createLibrary(@Nullable LibrariesContainer container1, @NotNull LibrariesContainer container2,
+ @NotNull @NonNls final NewLibraryEditor editor, @NotNull final LibrariesContainer.LibraryLevel level) {
+ if (container1 != null && container1.canCreateLibrary(level)) {
+ return container1.createLibrary(editor, level);
+ }
+ else {
+ return container2.createLibrary(editor, level);
+ }
+ }
+
+ @NotNull
+ private static Library createLibraryInTable(final @NotNull NewLibraryEditor editor, final LibraryTable table) {
+ LibraryTableBase.ModifiableModelEx modifiableModel = (LibraryTableBase.ModifiableModelEx) table.getModifiableModel();
+ final String name = StringUtil.isEmpty(editor.getName()) ? null : getUniqueLibraryName(editor.getName(), modifiableModel);
+ final LibraryType<?> type = editor.getType();
+ Library library = modifiableModel.createLibrary(name, type == null ? null : type.getKind());
+ final LibraryEx.ModifiableModelEx model = (LibraryEx.ModifiableModelEx)library.getModifiableModel();
+ editor.applyTo(model);
+ model.commit();
+ modifiableModel.commit();
+ return library;
+ }
+
+ private static String getUniqueLibraryName(final String baseName, final LibraryTable.ModifiableModel model) {
+ return UniqueNameGenerator.generateUniqueName(baseName, "", "", " (", ")", new Condition<String>() {
+ @Override
+ public boolean value(String s) {
+ return model.getLibraryByName(s) == null;
+ }
+ });
+ }
+
+ @NotNull
+ public static LibrariesContainer createContainer(@NotNull WizardContext context, @NotNull ModulesProvider modulesProvider) {
+ final LibrariesContainer container;
+ if (modulesProvider instanceof ModulesConfigurator) {
+ ModulesConfigurator configurator = (ModulesConfigurator)modulesProvider;
+ container = createContainer(configurator.getContext());
+ }
+ else {
+ container = createContainer(context.getProject());
+ }
+ return container;
+ }
+
+
+ private abstract static class LibrariesContainerBase implements LibrariesContainer {
+ private UniqueNameGenerator myNameGenerator;
+
+ @Override
+ public Library createLibrary(@NotNull @NonNls String name,
+ @NotNull LibraryLevel level,
+ @NotNull VirtualFile[] classRoots,
+ @NotNull VirtualFile[] sourceRoots) {
+ NewLibraryEditor editor = new NewLibraryEditor();
+ editor.setName(name);
+ for (VirtualFile classRoot : classRoots) {
+ editor.addRoot(classRoot, OrderRootType.CLASSES);
+ }
+ for (VirtualFile sourceRoot : sourceRoots) {
+ editor.addRoot(sourceRoot, OrderRootType.SOURCES);
+ }
+ return createLibrary(editor, level);
+ }
+
+ @Override
+ public Library createLibrary(@NotNull @NonNls String name,
+ @NotNull LibraryLevel level,
+ @NotNull Collection<? extends OrderRoot> roots) {
+ final NewLibraryEditor editor = new NewLibraryEditor();
+ editor.setName(name);
+ editor.addRoots(roots);
+ return createLibrary(editor, level);
+ }
+
+ @Override
+ @NotNull
+ public Library[] getAllLibraries() {
+ Library[] libraries = getLibraries(LibraryLevel.GLOBAL);
+ Library[] projectLibraries = getLibraries(LibraryLevel.PROJECT);
+ if (projectLibraries.length > 0) {
+ libraries = ArrayUtil.mergeArrays(libraries, projectLibraries);
+ }
+ Library[] moduleLibraries = getLibraries(LibraryLevel.MODULE);
+ if (moduleLibraries.length > 0) {
+ libraries = ArrayUtil.mergeArrays(libraries, moduleLibraries);
+ }
+ return libraries;
+ }
+
+ @NotNull
+ @Override
+ public List<LibraryLevel> getAvailableLevels() {
+ final List<LibraryLevel> levels = new ArrayList<LibraryLevel>();
+ for (LibraryLevel level : LibraryLevel.values()) {
+ if (canCreateLibrary(level)) {
+ levels.add(level);
+ }
+ }
+ return levels;
+ }
+
+ @NotNull
+ @Override
+ public String suggestUniqueLibraryName(@NotNull String baseName) {
+ if (myNameGenerator == null) {
+ myNameGenerator = new UniqueNameGenerator(Arrays.asList(getAllLibraries()), new Function<Library, String>() {
+ @Override
+ public String fun(Library o) {
+ return o.getName();
+ }
+ });
+ }
+ return myNameGenerator.generateUniqueName(baseName, "", "", " (", ")");
+ }
+ }
+
+
+ private static class LibrariesContainerImpl extends LibrariesContainerBase {
+ private @Nullable final Project myProject;
+ @Nullable private final Module myModule;
+ @Nullable private final ModifiableRootModel myRootModel;
+
+ private LibrariesContainerImpl(final @Nullable Project project, final @Nullable Module module, final @Nullable ModifiableRootModel rootModel) {
+ myProject = project;
+ myModule = module;
+ myRootModel = rootModel;
+ }
+
+ @Override
+ @Nullable
+ public Project getProject() {
+ return myProject;
+ }
+
+ @Override
+ @NotNull
+ public Library[] getLibraries(@NotNull final LibraryLevel libraryLevel) {
+ if (libraryLevel == LibraryLevel.MODULE && myModule != null) {
+ return getModuleLibraries();
+ }
+
+ LibraryTablesRegistrar registrar = LibraryTablesRegistrar.getInstance();
+ if (libraryLevel == LibraryLevel.GLOBAL) {
+ return registrar.getLibraryTable().getLibraries();
+ }
+
+ if (libraryLevel == LibraryLevel.PROJECT && myProject != null) {
+ return registrar.getLibraryTable(myProject).getLibraries();
+ }
+
+ return EMPTY_LIBRARIES_ARRAY;
+ }
+
+ private Library[] getModuleLibraries() {
+ if (myRootModel != null) {
+ return myRootModel.getModuleLibraryTable().getLibraries();
+ }
+ OrderEntry[] orderEntries = ModuleRootManager.getInstance(myModule).getOrderEntries();
+ List<Library> libraries = new ArrayList<Library>();
+ for (OrderEntry orderEntry : orderEntries) {
+ if (orderEntry instanceof LibraryOrderEntry) {
+ final LibraryOrderEntry entry = (LibraryOrderEntry)orderEntry;
+ if (entry.isModuleLevel()) {
+ libraries.add(entry.getLibrary());
+ }
+ }
+ }
+ return libraries.toArray(new Library[libraries.size()]);
+ }
+
+ @Override
+ @NotNull
+ public VirtualFile[] getLibraryFiles(@NotNull final Library library, @NotNull final OrderRootType rootType) {
+ return library.getFiles(rootType);
+ }
+
+ @Override
+ public boolean canCreateLibrary(@NotNull final LibraryLevel level) {
+ if (level == LibraryLevel.MODULE) {
+ return myRootModel != null;
+ }
+ return level == LibraryLevel.GLOBAL || myProject != null;
+ }
+
+ @Override
+ public Library createLibrary(@NotNull NewLibraryEditor libraryEditor,
+ @NotNull LibraryLevel level) {
+ if (level == LibraryLevel.MODULE && myRootModel != null) {
+ return createLibraryInTable(libraryEditor, myRootModel.getModuleLibraryTable());
+ }
+
+ LibraryTablesRegistrar registrar = LibraryTablesRegistrar.getInstance();
+ LibraryTable table;
+ if (level == LibraryLevel.GLOBAL) {
+ table = registrar.getLibraryTable();
+ }
+ else if (level == LibraryLevel.PROJECT && myProject != null) {
+ table = registrar.getLibraryTable(myProject);
+ }
+ else {
+ return null;
+ }
+ return createLibraryInTable(libraryEditor, table);
+ }
+
+ @Override
+ public ExistingLibraryEditor getLibraryEditor(@NotNull Library library) {
+ return null;
+ }
+ }
+
+ private static class StructureConfigurableLibrariesContainer extends LibrariesContainerBase {
+ private final StructureConfigurableContext myContext;
+
+ public StructureConfigurableLibrariesContainer(final StructureConfigurableContext context) {
+ myContext = context;
+ }
+
+ @Override
+ public Library createLibrary(@NotNull NewLibraryEditor libraryEditor,
+ @NotNull LibraryLevel level) {
+ LibraryTableModifiableModelProvider provider = getProvider(level);
+ if (provider == null) {
+ LOG.error("cannot create module library in this context");
+ }
+
+ LibraryTableBase.ModifiableModelEx model = (LibraryTableBase.ModifiableModelEx)provider.getModifiableModel();
+ final LibraryType<?> type = libraryEditor.getType();
+ Library library = model.createLibrary(getUniqueLibraryName(libraryEditor.getName(), model), type == null ? null : type.getKind());
+ ExistingLibraryEditor createdLibraryEditor = ((LibrariesModifiableModel)model).getLibraryEditor(library);
+ createdLibraryEditor.setProperties(libraryEditor.getProperties());
+ libraryEditor.applyTo(createdLibraryEditor);
+ return library;
+ }
+
+ @Override
+ public ExistingLibraryEditor getLibraryEditor(@NotNull Library library) {
+ final LibraryTable table = library.getTable();
+ if (table == null) return null;
+
+ final LibraryTable.ModifiableModel model = myContext.getModifiableLibraryTable(table);
+ if (model instanceof LibrariesModifiableModel) {
+ return ((LibrariesModifiableModel)model).getLibraryEditor(library);
+ }
+ return null;
+ }
+
+ @Override
+ @Nullable
+ public Project getProject() {
+ return myContext.getProject();
+ }
+
+ @Override
+ @NotNull
+ public Library[] getLibraries(@NotNull final LibraryLevel libraryLevel) {
+ LibraryTableModifiableModelProvider provider = getProvider(libraryLevel);
+ return provider != null ? provider.getModifiableModel().getLibraries() : EMPTY_LIBRARIES_ARRAY;
+ }
+
+ @Nullable
+ private LibraryTableModifiableModelProvider getProvider(LibraryLevel libraryLevel) {
+ if (libraryLevel == LibraryLevel.PROJECT) {
+ return myContext.getProjectLibrariesProvider();
+ }
+ else if (libraryLevel == LibraryLevel.GLOBAL) {
+ return myContext.getGlobalLibrariesProvider();
+ }
+ else {
+ return null;
+ }
+ }
+
+ @Override
+ public boolean canCreateLibrary(@NotNull final LibraryLevel level) {
+ return level == LibraryLevel.GLOBAL || level == LibraryLevel.PROJECT;
+ }
+
+ @Override
+ @NotNull
+ public VirtualFile[] getLibraryFiles(@NotNull final Library library, @NotNull final OrderRootType rootType) {
+ LibrariesModifiableModel projectLibrariesModel = myContext.getProjectLibrariesProvider().getModifiableModel();
+ if (projectLibrariesModel.hasLibraryEditor(library)) {
+ LibraryEditor libraryEditor = projectLibrariesModel.getLibraryEditor(library);
+ return libraryEditor.getFiles(rootType);
+ }
+ LibrariesModifiableModel globalLibraries = myContext.getGlobalLibrariesProvider().getModifiableModel();
+ if (globalLibraries.hasLibraryEditor(library)) {
+ LibraryEditor libraryEditor = globalLibraries.getLibraryEditor(library);
+ return libraryEditor.getFiles(rootType);
+ }
+ return library.getFiles(rootType);
+ }
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/LibrariesModifiableModel.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/LibrariesModifiableModel.java
new file mode 100644
index 0000000..06ee257
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/LibrariesModifiableModel.java
@@ -0,0 +1,193 @@
+/*
+ * Copyright 2000-2012 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.project.Project;
+import com.intellij.openapi.roots.impl.libraries.LibraryImpl;
+import com.intellij.openapi.roots.impl.libraries.LibraryTableBase;
+import com.intellij.openapi.roots.libraries.Library;
+import com.intellij.openapi.roots.libraries.LibraryTable;
+import com.intellij.openapi.roots.libraries.PersistentLibraryKind;
+import com.intellij.openapi.roots.ui.configuration.ProjectStructureConfigurable;
+import com.intellij.openapi.roots.ui.configuration.libraryEditor.ExistingLibraryEditor;
+import com.intellij.openapi.roots.ui.configuration.libraryEditor.LibraryEditor;
+import com.intellij.openapi.roots.ui.configuration.libraryEditor.LibraryEditorListener;
+import com.intellij.openapi.util.Disposer;
+import com.intellij.util.containers.ContainerUtil;
+import gnu.trove.THashMap;
+import gnu.trove.TObjectHashingStrategy;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.*;
+
+/**
+ * User: anna
+ * Date: 04-Jun-2006
+ */
+
+public class LibrariesModifiableModel implements LibraryTableBase.ModifiableModelEx {
+ //todo[nik] remove LibraryImpl#equals method instead of using identity maps
+ private final Map<Library, ExistingLibraryEditor> myLibrary2EditorMap =
+ ContainerUtil.<Library, ExistingLibraryEditor>newIdentityTroveMap();
+ private final Set<Library> myRemovedLibraries = ContainerUtil.<Library>newIdentityTroveSet();
+
+ private LibraryTable.ModifiableModel myLibrariesModifiableModel;
+ private final Project myProject;
+ private final LibraryTable myTable;
+ private final LibraryEditorListener myLibraryEditorListener;
+
+ public LibrariesModifiableModel(final LibraryTable table, final Project project, LibraryEditorListener libraryEditorListener) {
+ myProject = project;
+ myTable = table;
+ myLibraryEditorListener = libraryEditorListener;
+ }
+
+ @Override
+ public Library createLibrary(String name) {
+ return createLibrary(name, null);
+ }
+
+ @Override
+ public Library createLibrary(String name, @Nullable PersistentLibraryKind type) {
+ final Library library = ((LibraryTableBase.ModifiableModelEx)getLibrariesModifiableModel()).createLibrary(name, type);
+ //createLibraryEditor(library); \
+ final BaseLibrariesConfigurable configurable = ProjectStructureConfigurable.getInstance(myProject).getConfigurableFor(library);
+ configurable.createLibraryNode(library);
+ return library;
+ }
+
+ @Override
+ public void removeLibrary(@NotNull Library library) {
+ if (getLibrariesModifiableModel().getLibraryByName(library.getName()) == null) return;
+
+ removeLibraryEditor(library);
+ final Library existingLibrary = myTable.getLibraryByName(library.getName());
+ getLibrariesModifiableModel().removeLibrary(library);
+ if (existingLibrary == library) {
+ myRemovedLibraries.add(library);
+ } else {
+ // dispose uncommitted library
+ Disposer.dispose(library);
+ }
+ }
+
+ @Override
+ public void commit() {
+ //do nothing - do deffered commit
+ }
+
+ @Override
+ @NotNull
+ public Iterator<Library> getLibraryIterator() {
+ return getLibrariesModifiableModel().getLibraryIterator();
+ }
+
+ @Override
+ public Library getLibraryByName(@NotNull String name) {
+ return getLibrariesModifiableModel().getLibraryByName(name);
+ }
+
+ @Override
+ @NotNull
+ public Library[] getLibraries() {
+ return getLibrariesModifiableModel().getLibraries();
+ }
+
+ @Override
+ public boolean isChanged() {
+ for (LibraryEditor libraryEditor : myLibrary2EditorMap.values()) {
+ if (libraryEditor.hasChanges()) return true;
+ }
+ return getLibrariesModifiableModel().isChanged();
+ }
+
+ public void deferredCommit(){
+ final List<ExistingLibraryEditor> libraryEditors = new ArrayList<ExistingLibraryEditor>(myLibrary2EditorMap.values());
+ myLibrary2EditorMap.clear();
+ for (ExistingLibraryEditor libraryEditor : libraryEditors) {
+ libraryEditor.commit(); // TODO: is seems like commit will recreate the editor, but it should not
+ Disposer.dispose(libraryEditor);
+ }
+ if (!libraryEditors.isEmpty() || !myRemovedLibraries.isEmpty() || myLibrariesModifiableModel != null && myLibrariesModifiableModel.isChanged()) {
+ getLibrariesModifiableModel().commit();
+ myLibrariesModifiableModel = null;
+ }
+ myRemovedLibraries.clear();
+ }
+
+ public boolean wasLibraryRemoved(Library library){
+ return myRemovedLibraries.contains(library);
+ }
+
+ public boolean hasLibraryEditor(Library library){
+ return myLibrary2EditorMap.containsKey(library);
+ }
+
+ public ExistingLibraryEditor getLibraryEditor(Library library){
+ final Library source = ((LibraryImpl)library).getSource();
+ if (source != null) {
+ return getLibraryEditor(source);
+ }
+ ExistingLibraryEditor libraryEditor = myLibrary2EditorMap.get(library);
+ if (libraryEditor == null){
+ libraryEditor = createLibraryEditor(library);
+ }
+ return libraryEditor;
+ }
+
+ private ExistingLibraryEditor createLibraryEditor(final Library library) {
+ final ExistingLibraryEditor libraryEditor = new ExistingLibraryEditor(library, myLibraryEditorListener);
+ myLibrary2EditorMap.put(library, libraryEditor);
+ return libraryEditor;
+ }
+
+ private void removeLibraryEditor(final Library library) {
+ final ExistingLibraryEditor libraryEditor = myLibrary2EditorMap.remove(library);
+ if (libraryEditor != null) {
+ Disposer.dispose(libraryEditor);
+ }
+ }
+
+ public Library.ModifiableModel getLibraryModifiableModel(final Library library) {
+ return getLibraryEditor(library).getModel();
+ }
+
+ private LibraryTable.ModifiableModel getLibrariesModifiableModel() {
+ if (myLibrariesModifiableModel == null) {
+ myLibrariesModifiableModel = myTable.getModifiableModel();
+ }
+
+ return myLibrariesModifiableModel;
+ }
+
+ public void disposeUncommittedLibraries() {
+ for (final Library library : new ArrayList<Library>(myLibrary2EditorMap.keySet())) {
+ final Library existingLibrary = myTable.getLibraryByName(library.getName());
+ if (existingLibrary != library) {
+ Disposer.dispose(library);
+ }
+
+ final ExistingLibraryEditor libraryEditor = myLibrary2EditorMap.get(library);
+ if (libraryEditor != null) {
+ Disposer.dispose(libraryEditor);
+ }
+ }
+
+ myLibrary2EditorMap.clear();
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/LibraryConfigurable.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/LibraryConfigurable.java
new file mode 100644
index 0000000..0408ded
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/LibraryConfigurable.java
@@ -0,0 +1,196 @@
+/*
+ * Copyright 2000-2009 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.project.Project;
+import com.intellij.openapi.project.ProjectBundle;
+import com.intellij.openapi.roots.libraries.Library;
+import com.intellij.openapi.roots.libraries.LibraryTable;
+import com.intellij.openapi.roots.ui.configuration.libraries.LibraryPresentationManager;
+import com.intellij.openapi.roots.ui.configuration.libraryEditor.LibraryEditor;
+import com.intellij.openapi.roots.ui.configuration.libraryEditor.LibraryRootsComponent;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.daemon.LibraryProjectStructureElement;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.daemon.ProjectStructureElement;
+import com.intellij.openapi.util.Computable;
+import com.intellij.openapi.util.Disposer;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+
+/**
+ * User: anna
+ * Date: 02-Jun-2006
+ */
+public class LibraryConfigurable extends ProjectStructureElementConfigurable<Library> {
+ private LibraryRootsComponent myLibraryEditorComponent;
+ private final Library myLibrary;
+ private final StructureLibraryTableModifiableModelProvider myModel;
+ private final StructureConfigurableContext myContext;
+ private final Project myProject;
+ private final LibraryProjectStructureElement myProjectStructureElement;
+ private boolean myUpdatingName;
+ private boolean myPropertiesLoaded;
+
+ protected LibraryConfigurable(final StructureLibraryTableModifiableModelProvider modelProvider,
+ final Library library,
+ final StructureConfigurableContext context,
+ final Runnable updateTree) {
+ super(true, updateTree);
+ myModel = modelProvider;
+ myContext = context;
+ myProject = context.getProject();
+ myLibrary = library;
+ myProjectStructureElement = new LibraryProjectStructureElement(context, myLibrary);
+ }
+
+ @Override
+ public JComponent createOptionsPanel() {
+ myLibraryEditorComponent = new LibraryRootsComponent(myProject, new Computable<LibraryEditor>() {
+ @Override
+ public LibraryEditor compute() {
+ return getLibraryEditor();
+ }
+ });
+ myLibraryEditorComponent.addListener(new Runnable() {
+ @Override
+ public void run() {
+ myContext.getDaemonAnalyzer().queueUpdate(myProjectStructureElement);
+ updateName();
+ }
+ });
+ return myLibraryEditorComponent.getComponent();
+ }
+
+ @Override
+ public boolean isModified() {
+ return myLibraryEditorComponent != null && myLibraryEditorComponent.hasChanges();
+ }
+
+ @Override
+ @NotNull
+ public ProjectStructureElement getProjectStructureElement() {
+ return myProjectStructureElement;
+ }
+
+ @Override
+ public void apply() {
+ applyProperties();
+ }
+
+ @Override
+ public void reset() {
+ resetProperties();
+ }
+
+ @Override
+ public void disposeUIResources() {
+ if (myLibraryEditorComponent != null) {
+ Disposer.dispose(myLibraryEditorComponent);
+ myLibraryEditorComponent = null;
+ }
+ }
+
+ @Override
+ public void setDisplayName(final String name) {
+ if (!myUpdatingName) {
+ getLibraryEditor().setName(name);
+ myContext.getDaemonAnalyzer().queueUpdateForAllElementsWithErrors();
+ }
+ }
+
+ protected LibraryEditor getLibraryEditor() {
+ return myModel.getModifiableModel().getLibraryEditor(myLibrary);
+ }
+
+ @Override
+ public void updateName() {
+ //todo[nik] pull up to NamedConfigurable
+ myUpdatingName = true;
+ try {
+ super.updateName();
+ }
+ finally {
+ myUpdatingName = false;
+ }
+ }
+
+ @Override
+ public Library getEditableObject() {
+ return myLibrary;
+ }
+
+ @Override
+ public String getBannerSlogan() {
+ final LibraryTable libraryTable = myLibrary.getTable();
+ String libraryType = libraryTable == null
+ ? ProjectBundle.message("module.library.display.name", 1)
+ : libraryTable.getPresentation().getDisplayName(false);
+ return ProjectBundle.message("project.roots.library.banner.text", getDisplayName(), libraryType);
+ }
+
+ @Override
+ public String getDisplayName() {
+ if (myModel.getModifiableModel().hasLibraryEditor(myLibrary)) {
+ return getLibraryEditor().getName();
+ }
+
+ return myLibrary.getName();
+ }
+
+ public void onSelected() {
+ resetProperties();
+ }
+
+ public void onUnselected() {
+ applyProperties();
+ }
+
+ private void resetProperties() {
+ if (myLibraryEditorComponent != null) {
+ myLibraryEditorComponent.updatePropertiesLabel();
+ myLibraryEditorComponent.resetProperties();
+ myPropertiesLoaded = true;
+ }
+ }
+
+ private void applyProperties() {
+ if (myLibraryEditorComponent != null && myPropertiesLoaded) {
+ myLibraryEditorComponent.applyProperties();
+ myPropertiesLoaded = false;
+ }
+ }
+
+ @Override
+ public Icon getIcon(boolean open) {
+ return LibraryPresentationManager.getInstance().getNamedLibraryIcon(myLibrary, myContext);
+ }
+
+ @Override
+ @Nullable
+ @NonNls
+ public String getHelpTopic() {
+ return "preferences.jdkGlobalLibs"; //todo
+ }
+
+ public void updateComponent() {
+ if (myLibraryEditorComponent != null) {
+ myLibraryEditorComponent.updateRootsTree();
+ }
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/ModuleConfigurable.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/ModuleConfigurable.java
new file mode 100644
index 0000000..8c90e73
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/ModuleConfigurable.java
@@ -0,0 +1,163 @@
+/*
+ * Copyright 2000-2009 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.module.ModifiableModuleModel;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.module.ModuleType;
+import com.intellij.openapi.module.ModuleWithNameAlreadyExists;
+import com.intellij.openapi.options.ConfigurationException;
+import com.intellij.openapi.project.ProjectBundle;
+import com.intellij.openapi.roots.ui.configuration.ModuleEditor;
+import com.intellij.openapi.roots.ui.configuration.ModulesConfigurator;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.daemon.ModuleProjectStructureElement;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.daemon.ProjectStructureElement;
+import com.intellij.openapi.util.ActionCallback;
+import com.intellij.openapi.util.Comparing;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.ui.navigation.History;
+import com.intellij.ui.navigation.Place;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+
+/**
+ * User: anna
+ * Date: 04-Jun-2006
+ */
+public class ModuleConfigurable extends ProjectStructureElementConfigurable<Module> implements Place.Navigator {
+ private final Module myModule;
+ private final ModulesConfigurator myConfigurator;
+ private String myModuleName;
+ private final ModuleProjectStructureElement myProjectStructureElement;
+ private final StructureConfigurableContext myContext;
+
+ public ModuleConfigurable(ModulesConfigurator modulesConfigurator,
+ Module module,
+ final Runnable updateTree) {
+ super(true, updateTree);
+ myModule = module;
+ myModuleName = myModule.getName();
+ myConfigurator = modulesConfigurator;
+ myContext = ModuleStructureConfigurable.getInstance(myModule.getProject()).getContext();
+ myProjectStructureElement = new ModuleProjectStructureElement(myContext, myModule);
+ }
+
+ @Override
+ public void setDisplayName(String name) {
+ name = name.trim();
+ final ModifiableModuleModel modifiableModuleModel = myConfigurator.getModuleModel();
+ if (StringUtil.isEmpty(name)) return; //empty string comes on double click on module node
+ if (Comparing.strEqual(name, myModuleName)) return; //nothing changed
+ try {
+ modifiableModuleModel.renameModule(myModule, name);
+ }
+ catch (ModuleWithNameAlreadyExists moduleWithNameAlreadyExists) {
+ //do nothing
+ }
+ myConfigurator.moduleRenamed(myModule, myModuleName, name);
+ myModuleName = name;
+ myConfigurator.setModified(!Comparing.strEqual(myModuleName, myModule.getName()));
+ myContext.getDaemonAnalyzer().queueUpdateForAllElementsWithErrors();
+ }
+
+ @Override
+ public ProjectStructureElement getProjectStructureElement() {
+ return myProjectStructureElement;
+ }
+
+ @Override
+ public Module getEditableObject() {
+ return myModule;
+ }
+
+ @Override
+ public String getBannerSlogan() {
+ return ProjectBundle.message("project.roots.module.banner.text", myModuleName);
+ }
+
+ @Override
+ public String getDisplayName() {
+ return myModuleName;
+ }
+
+ @Override
+ public Icon getIcon(final boolean open) {
+ return ModuleType.get(myModule).getIcon();
+ }
+
+ public Module getModule() {
+ return myModule;
+ }
+
+ @Override
+ @Nullable
+ @NonNls
+ public String getHelpTopic() {
+ final ModuleEditor moduleEditor = getModuleEditor();
+ return moduleEditor != null ? moduleEditor.getHelpTopic() : null;
+ }
+
+
+ @Override
+ public JComponent createOptionsPanel() {
+ return getModuleEditor().getPanel();
+ }
+
+ @Override
+ public boolean isModified() {
+ return false;
+ }
+
+ @Override
+ public void apply() throws ConfigurationException {
+ //do nothing
+ }
+
+ @Override
+ public void reset() {
+ //do nothing
+ }
+
+ @Override
+ public void disposeUIResources() {
+ //do nothing
+ }
+
+ public ModuleEditor getModuleEditor() {
+ return myConfigurator.getModuleEditor(myModule);
+ }
+
+ @Override
+ public ActionCallback navigateTo(@Nullable final Place place, final boolean requestFocus) {
+ return getModuleEditor().navigateTo(place, requestFocus);
+ }
+
+ @Override
+ public void queryPlace(@NotNull final Place place) {
+ final ModuleEditor editor = getModuleEditor();
+ if (editor != null) {
+ editor.queryPlace(place);
+ }
+ }
+
+ @Override
+ public void setHistory(final History history) {
+ }
+}
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
new file mode 100644
index 0000000..0ba8d7d
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/ModuleStructureConfigurable.java
@@ -0,0 +1,1011 @@
+/*
+ * Copyright 2000-2012 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.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 o1.getDisplayName().compareToIgnoreCase(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);
+ 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();
+ }
+ });
+ comparators.add(NODE_COMPARATOR);
+ return new MergingComparator<MyNode>(comparators);
+ }
+
+ @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()) == DialogWrapper.OK_EXIT_CODE) {
+ 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.setPreferedFocusComponent(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;
+ }
+ }
+
+}
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
new file mode 100644
index 0000000..76ba823
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/ModuleStructureExtension.java
@@ -0,0 +1,88 @@
+package com.intellij.openapi.roots.ui.configuration.projectRoot;
+
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.extensions.ExtensionPointName;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.options.ConfigurationException;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.roots.OrderEntry;
+import com.intellij.openapi.ui.MasterDetailsComponent;
+import com.intellij.openapi.ui.NamedConfigurable;
+import com.intellij.openapi.util.ActionCallback;
+import com.intellij.openapi.util.NullableComputable;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+
+public abstract class ModuleStructureExtension {
+
+ public static final ExtensionPointName<ModuleStructureExtension> EP_NAME =
+ ExtensionPointName.create("com.intellij.configuration.ModuleStructureExtension");
+
+ public void reset(Project project) {
+ }
+
+ public boolean addModuleNodeChildren(Module module, MasterDetailsComponent.MyNode moduleNode, Runnable treeNodeNameUpdater) {
+ return false;
+ }
+
+ //public void moduleAdded(final Module module, final Runnable treeNodeNameUpdater) {
+ //}
+
+ public void moduleRemoved(final Module module) {
+ }
+
+ public boolean isModified() {
+ return false;
+ }
+
+ public void apply() throws ConfigurationException {
+ }
+
+ public void disposeUIResources() {
+ }
+
+ public boolean canBeRemoved(final Object[] editableObjects) {
+ return false;
+ }
+
+ public boolean removeObject(final Object editableObject) {
+ return false;
+ }
+
+ public Collection<AnAction> createAddActions(final NullableComputable<MasterDetailsComponent.MyNode> selectedNodeRetriever,
+ final Runnable treeNodeNameUpdater,
+ final Project project,
+ final MasterDetailsComponent.MyNode root) {
+ return Collections.emptyList();
+ }
+
+ public boolean canBeCopied(final NamedConfigurable configurable) {
+ return false;
+ }
+
+ public void copy(final NamedConfigurable configurable, final Runnable treeNodeNameUpdater) {
+ }
+
+ public void addRootNodes(final MasterDetailsComponent.MyNode parent, final Project project, final Runnable treeUpdater) {
+ }
+
+ @Nullable
+ public Comparator<MasterDetailsComponent.MyNode> getNodeComparator() {
+ return null;
+ }
+
+ /**
+ * @return callback or <code>null</code> if not handled
+ */
+ @Nullable
+ public ActionCallback selectOrderEntry(@NotNull final Module module, @Nullable final OrderEntry entry) {
+ return null;
+ }
+
+ public void afterModelCommit() {
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/ProjectLibrariesConfigurable.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/ProjectLibrariesConfigurable.java
new file mode 100644
index 0000000..005d6cc
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/ProjectLibrariesConfigurable.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2000-2009 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.components.ServiceManager;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.project.ProjectBundle;
+import com.intellij.openapi.roots.libraries.LibraryTablePresentation;
+import com.intellij.openapi.roots.libraries.LibraryTablesRegistrar;
+import org.jetbrains.annotations.Nls;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+
+public class ProjectLibrariesConfigurable extends BaseLibrariesConfigurable {
+ public ProjectLibrariesConfigurable(final Project project) {
+ super(project);
+ myLevel = LibraryTablesRegistrar.PROJECT_LEVEL;
+ }
+
+ @Override
+ protected String getComponentStateKey() {
+ return "ProjectLibrariesConfigurable.UI";
+ }
+
+ @Override
+ @Nls
+ public String getDisplayName() {
+ return "Libraries";
+ }
+
+ @Override
+ @NotNull
+ @NonNls
+ public String getId() {
+ return "project.libraries";
+ }
+
+
+ @Override
+ public StructureLibraryTableModifiableModelProvider getModelProvider() {
+ return myContext.getProjectLibrariesProvider();
+ }
+
+ @Override
+ public BaseLibrariesConfigurable getOppositeGroup() {
+ return GlobalLibrariesConfigurable.getInstance(myProject);
+ }
+
+ public static ProjectLibrariesConfigurable getInstance(final Project project) {
+ return ServiceManager.getService(project, ProjectLibrariesConfigurable.class);
+ }
+
+ @Override
+ public LibraryTablePresentation getLibraryTablePresentation() {
+ return LibraryTablesRegistrar.getInstance().getLibraryTable(myProject).getPresentation();
+ }
+
+ @Override
+ protected String getAddText() {
+ return ProjectBundle.message("add.new.project.library.text");
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/ProjectStructureElementConfigurable.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/ProjectStructureElementConfigurable.java
new file mode 100644
index 0000000..ddbf9f5
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/ProjectStructureElementConfigurable.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2000-2009 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.roots.ui.configuration.projectRoot.daemon.ProjectStructureElement;
+import com.intellij.openapi.ui.NamedConfigurable;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+
+/**
+ * @author nik
+ */
+public abstract class ProjectStructureElementConfigurable<T> extends NamedConfigurable<T> {
+ protected ProjectStructureElementConfigurable() {
+ }
+
+ protected ProjectStructureElementConfigurable(boolean isNameEditable, @Nullable Runnable updateTree) {
+ super(isNameEditable, updateTree);
+ }
+
+ @Nullable
+ public abstract ProjectStructureElement getProjectStructureElement();
+
+ @Override
+ @Nullable
+ public Icon getIcon(boolean open) {
+ return null;
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/ProjectStructureElementRenderer.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/ProjectStructureElementRenderer.java
new file mode 100644
index 0000000..f90888d
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/ProjectStructureElementRenderer.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2000-2012 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.roots.ui.configuration.projectRoot.daemon.ProjectStructureDaemonAnalyzer;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.daemon.ProjectStructureElement;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.daemon.ProjectStructureProblemType;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.daemon.ProjectStructureProblemsHolderImpl;
+import com.intellij.openapi.ui.MasterDetailsComponent;
+import com.intellij.openapi.ui.NamedConfigurable;
+import com.intellij.ui.ColoredTreeCellRenderer;
+import com.intellij.ui.JBColor;
+import com.intellij.ui.SimpleTextAttributes;
+import com.intellij.util.ui.UIUtil;
+
+import javax.swing.*;
+import java.awt.*;
+
+/**
+* @author nik
+*/
+class ProjectStructureElementRenderer extends ColoredTreeCellRenderer {
+ private StructureConfigurableContext myContext;
+
+ public ProjectStructureElementRenderer(StructureConfigurableContext context) {
+ myContext = context;
+ }
+
+ @Override
+ public void customizeCellRenderer(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) {
+ if (value instanceof MasterDetailsComponent.MyNode) {
+ final MasterDetailsComponent.MyNode node = (MasterDetailsComponent.MyNode)value;
+
+ final NamedConfigurable namedConfigurable = node.getConfigurable();
+ if (namedConfigurable == null) {
+ return;
+ }
+
+ final String displayName = node.getDisplayName();
+ final Icon icon = namedConfigurable.getIcon(expanded);
+ setIcon(icon);
+ setToolTipText(null);
+ setFont(UIUtil.getTreeFont());
+
+ SimpleTextAttributes textAttributes = SimpleTextAttributes.REGULAR_ATTRIBUTES;
+ if (node.isDisplayInBold()) {
+ textAttributes = SimpleTextAttributes.REGULAR_BOLD_ATTRIBUTES;
+ }
+ else if (namedConfigurable instanceof ProjectStructureElementConfigurable) {
+ final ProjectStructureElement projectStructureElement =
+ ((ProjectStructureElementConfigurable)namedConfigurable).getProjectStructureElement();
+ if (projectStructureElement != null) {
+ final ProjectStructureDaemonAnalyzer daemonAnalyzer = myContext.getDaemonAnalyzer();
+ final ProjectStructureProblemsHolderImpl problemsHolder = daemonAnalyzer.getProblemsHolder(projectStructureElement);
+ if (problemsHolder != null && problemsHolder.containsProblems()) {
+ final boolean isUnused = problemsHolder.containsProblems(ProjectStructureProblemType.Severity.UNUSED);
+ final boolean haveWarnings = problemsHolder.containsProblems(ProjectStructureProblemType.Severity.WARNING);
+ final boolean haveErrors = problemsHolder.containsProblems(ProjectStructureProblemType.Severity.ERROR);
+ Color foreground = isUnused ? UIUtil.getInactiveTextColor() : null;
+ final int style = haveWarnings || haveErrors ? SimpleTextAttributes.STYLE_WAVED : -1;
+ final Color waveColor = haveErrors ? JBColor.RED : haveWarnings ? JBColor.GRAY : null;
+ textAttributes = textAttributes.derive(style, foreground, null, waveColor);
+ setToolTipText(problemsHolder.composeTooltipMessage());
+ }
+
+ append(displayName, textAttributes);
+ String description = projectStructureElement.getDescription();
+ if (description != null) {
+ append(" (" + description + ")", SimpleTextAttributes.GRAY_ATTRIBUTES, false);
+ }
+ return;
+ }
+ }
+ append(displayName, textAttributes);
+ }
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/StructureConfigurableContext.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/StructureConfigurableContext.java
new file mode 100644
index 0000000..09f6a3e
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/StructureConfigurableContext.java
@@ -0,0 +1,184 @@
+/*
+ * Copyright 2000-2009 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.Disposable;
+import com.intellij.openapi.module.ModifiableModuleModel;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.roots.OrderRootType;
+import com.intellij.openapi.roots.impl.libraries.LibraryTableImplUtil;
+import com.intellij.openapi.roots.libraries.Library;
+import com.intellij.openapi.roots.libraries.LibraryTable;
+import com.intellij.openapi.roots.libraries.LibraryTablesRegistrar;
+import com.intellij.openapi.roots.ui.configuration.ModulesConfigurator;
+import com.intellij.openapi.roots.ui.configuration.libraryEditor.LibraryEditorListener;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.daemon.ProjectStructureDaemonAnalyzer;
+import com.intellij.openapi.util.Disposer;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.util.EventDispatcher;
+import gnu.trove.THashMap;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.Map;
+
+public class StructureConfigurableContext implements Disposable, LibraryEditorListener {
+ private final ProjectStructureDaemonAnalyzer myDaemonAnalyzer;
+ public final ModulesConfigurator myModulesConfigurator;
+ public final Map<String, LibrariesModifiableModel> myLevel2Providers = new THashMap<String, LibrariesModifiableModel>();
+ private final EventDispatcher<LibraryEditorListener> myLibraryEditorListeners = EventDispatcher.create(LibraryEditorListener.class);
+ private final Project myProject;
+
+
+ public StructureConfigurableContext(Project project, final ModulesConfigurator modulesConfigurator) {
+ myProject = project;
+ myModulesConfigurator = modulesConfigurator;
+ Disposer.register(project, this);
+ myDaemonAnalyzer = new ProjectStructureDaemonAnalyzer(this);
+ }
+
+ public VirtualFile[] getLibraryFiles(Library library, final OrderRootType type) {
+ final LibraryTable table = library.getTable();
+ if (table != null) {
+ final LibraryTable.ModifiableModel modifiableModel = getModifiableLibraryTable(table);
+ if (modifiableModel instanceof LibrariesModifiableModel) {
+ final LibrariesModifiableModel librariesModel = (LibrariesModifiableModel)modifiableModel;
+ if (librariesModel.hasLibraryEditor(library)) {
+ return librariesModel.getLibraryEditor(library).getFiles(type);
+ }
+ }
+ }
+ return library.getFiles(type);
+ }
+
+ public Project getProject() {
+ return myProject;
+ }
+
+ public ProjectStructureDaemonAnalyzer getDaemonAnalyzer() {
+ return myDaemonAnalyzer;
+ }
+
+ @Override
+ public void dispose() {
+ }
+
+ public ModulesConfigurator getModulesConfigurator() {
+ return myModulesConfigurator;
+ }
+
+ public Module[] getModules() {
+ return myModulesConfigurator.getModules();
+ }
+
+ public String getRealName(final Module module) {
+ final ModifiableModuleModel moduleModel = myModulesConfigurator.getModuleModel();
+ String newName = moduleModel.getNewName(module);
+ return newName != null ? newName : module.getName();
+ }
+
+ public void resetLibraries() {
+ final LibraryTablesRegistrar tablesRegistrar = LibraryTablesRegistrar.getInstance();
+
+ myLevel2Providers.clear();
+ myLevel2Providers.put(LibraryTablesRegistrar.APPLICATION_LEVEL, new LibrariesModifiableModel(tablesRegistrar.getLibraryTable(), myProject, this));
+ myLevel2Providers.put(LibraryTablesRegistrar.PROJECT_LEVEL, new LibrariesModifiableModel(tablesRegistrar.getLibraryTable(myProject), myProject, this));
+ for (final LibraryTable table : tablesRegistrar.getCustomLibraryTables()) {
+ myLevel2Providers.put(table.getTableLevel(), new LibrariesModifiableModel(table, myProject, this));
+ }
+ }
+
+ public void addLibraryEditorListener(LibraryEditorListener listener) {
+ myLibraryEditorListeners.addListener(listener);
+ }
+
+ public void addLibraryEditorListener(@NotNull LibraryEditorListener listener, @NotNull Disposable parentDisposable) {
+ myLibraryEditorListeners.addListener(listener, parentDisposable);
+ }
+
+ @Override
+ public void libraryRenamed(@NotNull Library library, String oldName, String newName) {
+ myLibraryEditorListeners.getMulticaster().libraryRenamed(library, oldName, newName);
+ }
+
+ public StructureLibraryTableModifiableModelProvider getGlobalLibrariesProvider() {
+ return createModifiableModelProvider(LibraryTablesRegistrar.APPLICATION_LEVEL);
+ }
+
+ public StructureLibraryTableModifiableModelProvider createModifiableModelProvider(final String level) {
+ return new StructureLibraryTableModifiableModelProvider(level, this);
+ }
+
+ public StructureLibraryTableModifiableModelProvider getProjectLibrariesProvider() {
+ return createModifiableModelProvider(LibraryTablesRegistrar.PROJECT_LEVEL);
+ }
+
+
+ public LibraryTable.ModifiableModel getModifiableLibraryTable(@NotNull LibraryTable table) {
+ final String tableLevel = table.getTableLevel();
+ if (tableLevel.equals(LibraryTableImplUtil.MODULE_LEVEL)) {
+ return table.getModifiableModel();
+ }
+ return myLevel2Providers.get(tableLevel);
+ }
+
+ @Nullable
+ public Library getLibrary(final String libraryName, final String libraryLevel) {
+/* the null check is added only to prevent NPE when called from getLibrary */
+ final LibrariesModifiableModel model = myLevel2Providers.get(libraryLevel);
+ return model == null ? null : findLibraryModel(libraryName, model);
+ }
+
+ @Nullable
+ private static Library findLibraryModel(final @NotNull String libraryName, @NotNull LibrariesModifiableModel model) {
+ for (Library library : model.getLibraries()) {
+ final Library libraryModel = findLibraryModel(library, model);
+ if (libraryModel != null && libraryName.equals(libraryModel.getName())) {
+ return libraryModel;
+ }
+ }
+ return null;
+ }
+
+ @Nullable
+ public Library getLibraryModel(@NotNull Library library) {
+ final LibraryTable libraryTable = library.getTable();
+ if (libraryTable != null) {
+ return findLibraryModel(library, myLevel2Providers.get(libraryTable.getTableLevel()));
+ }
+ return library;
+ }
+
+ @Nullable
+ private static Library findLibraryModel(final Library library, LibrariesModifiableModel tableModel) {
+ if (tableModel == null) return library;
+ if (tableModel.wasLibraryRemoved(library)) return null;
+ return tableModel.hasLibraryEditor(library) ? (Library)tableModel.getLibraryEditor(library).getModel() : library;
+ }
+
+
+ public void reset() {
+ resetLibraries();
+ myModulesConfigurator.resetModuleEditors();
+ myDaemonAnalyzer.reset(); // should be called after resetLibraries!
+ }
+
+ public void clear() {
+ myLevel2Providers.clear();
+ }
+
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/StructureLibraryTableModifiableModelProvider.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/StructureLibraryTableModifiableModelProvider.java
new file mode 100644
index 0000000..fb36f9e
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/StructureLibraryTableModifiableModelProvider.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2000-2010 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.roots.ui.configuration.LibraryTableModifiableModelProvider;
+
+/**
+* @author nik
+*/
+public class StructureLibraryTableModifiableModelProvider implements LibraryTableModifiableModelProvider {
+ private final String myLevel;
+ private final StructureConfigurableContext myContext;
+
+ public StructureLibraryTableModifiableModelProvider(String level,
+ final StructureConfigurableContext context) {
+ myLevel = level;
+ myContext = context;
+ }
+
+ @Override
+ public LibrariesModifiableModel getModifiableModel() {
+ return myContext.myLevel2Providers.get(myLevel);
+ }
+
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/TextConfigurable.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/TextConfigurable.java
new file mode 100644
index 0000000..217fad1
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/TextConfigurable.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2000-2011 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.options.ConfigurationException;
+import com.intellij.openapi.ui.NamedConfigurable;
+import com.intellij.openapi.ui.PanelWithText;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+
+/**
+ * @author nik
+ */
+public class TextConfigurable<T> extends NamedConfigurable<T> {
+ private final T myObject;
+ private final String myBannerSlogan;
+ private final String myDisplayName;
+ private final Icon myClosedIcon;
+ private final String myDescriptionText;
+
+ public TextConfigurable(final T object,
+ final String displayName,
+ final String bannerSlogan,
+ final String descriptionText,
+ final Icon closedIcon) {
+ myDisplayName = displayName;
+ myBannerSlogan = bannerSlogan;
+ myDescriptionText = descriptionText;
+ myClosedIcon = closedIcon;
+ myObject = object;
+ }
+
+ @Override
+ public void setDisplayName(final String name) {
+ //do nothing
+ }
+
+ @Override
+ public boolean isModified() {
+ return false;
+ }
+
+ @Override
+ public void apply() throws ConfigurationException {
+ //do nothing
+ }
+
+ @Override
+ public void reset() {
+ //do nothing
+ }
+
+ @Override
+ public void disposeUIResources() {
+ //do nothing
+ }
+
+ @Override
+ @Nullable
+ @NonNls
+ public String getHelpTopic() {
+ return null;
+ }
+
+ @Override
+ public T getEditableObject() {
+ return myObject;
+ }
+
+ @Override
+ public String getBannerSlogan() {
+ return myBannerSlogan;
+ }
+
+ @Override
+ public String getDisplayName() {
+ return myDisplayName;
+ }
+
+ @Override
+ public Icon getIcon(final boolean open) {
+ return myClosedIcon;
+ }
+
+ @Override
+ public JComponent createOptionsPanel() {
+ return new PanelWithText(myDescriptionText);
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/daemon/ConfigurationErrorQuickFix.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/daemon/ConfigurationErrorQuickFix.java
new file mode 100644
index 0000000..c6ebb42
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/daemon/ConfigurationErrorQuickFix.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2000-2010 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.daemon;
+
+/**
+ * @author nik
+ */
+public abstract class ConfigurationErrorQuickFix {
+ private final String myActionName;
+
+ protected ConfigurationErrorQuickFix(String actionName) {
+ myActionName = actionName;
+ }
+
+ public String getActionName() {
+ return myActionName;
+ }
+
+ public abstract void performFix();
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/daemon/FacetProjectStructureElement.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/daemon/FacetProjectStructureElement.java
new file mode 100644
index 0000000..3d8f41e
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/daemon/FacetProjectStructureElement.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2000-2011 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.daemon;
+
+import com.intellij.facet.Facet;
+import com.intellij.facet.pointers.FacetPointersManager;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.StructureConfigurableContext;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * @author nik
+ */
+public class FacetProjectStructureElement extends ProjectStructureElement {
+ private final Facet myFacet;
+
+ public FacetProjectStructureElement(@NotNull StructureConfigurableContext context, @NotNull Facet facet) {
+ super(context);
+ myFacet = facet;
+ }
+
+ @Override
+ public void check(ProjectStructureProblemsHolder problemsHolder) {
+ }
+
+ @Override
+ public List<ProjectStructureElementUsage> getUsagesInElement() {
+ return Collections.emptyList();
+ }
+
+ @Override
+ public String getPresentableName() {
+ return "Facet '" + myFacet.getName() + "' in module '" + myFacet.getModule().getName() + "'";
+ }
+
+ @Override
+ public String getTypeName() {
+ return "Facet";
+ }
+
+ @Override
+ public String getId() {
+ return "facet:" + FacetPointersManager.constructId(myFacet);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return obj instanceof FacetProjectStructureElement && myFacet.equals(((FacetProjectStructureElement)obj).myFacet);
+ }
+
+ @Override
+ public int hashCode() {
+ return myFacet.hashCode();
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/daemon/GlobalProjectStructureProblemsSettings.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/daemon/GlobalProjectStructureProblemsSettings.java
new file mode 100644
index 0000000..373723c
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/daemon/GlobalProjectStructureProblemsSettings.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2000-2012 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.daemon;
+
+import com.intellij.openapi.components.State;
+import com.intellij.openapi.components.Storage;
+import com.intellij.openapi.components.StoragePathMacros;
+
+/**
+ * @author nik
+ */
+@State(
+ name = "ProjectStructureProblems",
+ storages = {@Storage(file = StoragePathMacros.APP_CONFIG + "/projectStructureProblems.xml")}
+)
+public class GlobalProjectStructureProblemsSettings extends ProjectStructureProblemsSettingsBase {
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/daemon/LibraryProjectStructureElement.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/daemon/LibraryProjectStructureElement.java
new file mode 100644
index 0000000..b44ab87
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/daemon/LibraryProjectStructureElement.java
@@ -0,0 +1,229 @@
+/*
+ * Copyright 2000-2012 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.daemon;
+
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.project.ProjectBundle;
+import com.intellij.openapi.roots.JavadocOrderRootType;
+import com.intellij.openapi.roots.OrderRootType;
+import com.intellij.openapi.roots.impl.libraries.LibraryEx;
+import com.intellij.openapi.roots.impl.libraries.LibraryImpl;
+import com.intellij.openapi.roots.libraries.Library;
+import com.intellij.openapi.roots.libraries.LibraryTable;
+import com.intellij.openapi.roots.libraries.LibraryTablesRegistrar;
+import com.intellij.openapi.roots.ui.configuration.ModuleEditor;
+import com.intellij.openapi.roots.ui.configuration.ProjectStructureConfigurable;
+import com.intellij.openapi.roots.ui.configuration.libraries.LibraryEditingUtil;
+import com.intellij.openapi.roots.ui.configuration.libraryEditor.ExistingLibraryEditor;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.BaseLibrariesConfigurable;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.LibrariesModifiableModel;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.LibraryConfigurable;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.StructureConfigurableContext;
+import com.intellij.openapi.ui.NamedConfigurable;
+import com.intellij.openapi.util.ActionCallback;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.util.PathUtil;
+import org.jetbrains.annotations.NotNull;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Proxy;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * @author nik
+ */
+public class LibraryProjectStructureElement extends ProjectStructureElement {
+ private final Library myLibrary;
+
+ public LibraryProjectStructureElement(@NotNull StructureConfigurableContext context, @NotNull Library library) {
+ super(context);
+ myLibrary = library;
+ }
+
+ public Library getLibrary() {
+ return myLibrary;
+ }
+
+ @Override
+ public void check(ProjectStructureProblemsHolder problemsHolder) {
+ if (((LibraryEx)myLibrary).isDisposed()) return;
+ final LibraryEx library = (LibraryEx)myContext.getLibraryModel(myLibrary);
+ if (library == null || library.isDisposed()) return;
+
+ reportInvalidRoots(problemsHolder, library, OrderRootType.CLASSES, "classes", ProjectStructureProblemType.error("library-invalid-classes-path"));
+ final String libraryName = library.getName();
+ if (libraryName == null || !libraryName.startsWith("Maven: ")) {
+ reportInvalidRoots(problemsHolder, library, OrderRootType.SOURCES, "sources",
+ ProjectStructureProblemType.warning("library-invalid-source-javadoc-path"));
+ reportInvalidRoots(problemsHolder, library, JavadocOrderRootType.getInstance(), "javadoc",
+ ProjectStructureProblemType.warning("library-invalid-source-javadoc-path"));
+ }
+ }
+
+ private void reportInvalidRoots(ProjectStructureProblemsHolder problemsHolder, LibraryEx library,
+ final OrderRootType type, String rootName, final ProjectStructureProblemType problemType) {
+ final List<String> invalidUrls = library.getInvalidRootUrls(type);
+ if (!invalidUrls.isEmpty()) {
+ final String description = createInvalidRootsDescription(invalidUrls, rootName, library.getName());
+ final PlaceInProjectStructure place = createPlace();
+ final String message = ProjectBundle.message("project.roots.error.message.invalid.roots", rootName, invalidUrls.size());
+ ProjectStructureProblemDescription.ProblemLevel level = library.getTable().getTableLevel().equals(LibraryTablesRegistrar.PROJECT_LEVEL)
+ ? ProjectStructureProblemDescription.ProblemLevel.PROJECT : ProjectStructureProblemDescription.ProblemLevel.GLOBAL;
+ problemsHolder.registerProblem(new ProjectStructureProblemDescription(message, description, place,
+ problemType, level,
+ Collections.singletonList(new RemoveInvalidRootsQuickFix(library, type, invalidUrls)),
+ true));
+ }
+ }
+
+ private static String createInvalidRootsDescription(List<String> invalidClasses, String rootName, String libraryName) {
+ StringBuilder buffer = new StringBuilder();
+ buffer.append("<html>");
+ buffer.append("Library '").append(StringUtil.escapeXml(libraryName)).append("' has broken " + rootName + " " + StringUtil.pluralize("path", invalidClasses.size()) + ":");
+ for (String url : invalidClasses) {
+ buffer.append("<br> ");
+ buffer.append(PathUtil.toPresentableUrl(url));
+ }
+ buffer.append("</html>");
+ return buffer.toString();
+ }
+
+ @NotNull
+ private PlaceInProjectStructure createPlace() {
+ final Project project = myContext.getProject();
+ return new PlaceInProjectStructureBase(project, ProjectStructureConfigurable.getInstance(project).createProjectOrGlobalLibraryPlace(myLibrary), this);
+ }
+
+ @Override
+ public List<ProjectStructureElementUsage> getUsagesInElement() {
+ return Collections.emptyList();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof LibraryProjectStructureElement)) return false;
+
+ return getSourceOrThis() == (((LibraryProjectStructureElement)o).getSourceOrThis());
+ }
+
+ public ActionCallback navigate() {
+ return createPlace().navigate();
+ }
+
+ @NotNull
+ private Library getSourceOrThis() {
+ final InvocationHandler invocationHandler = Proxy.isProxyClass(myLibrary.getClass()) ? Proxy.getInvocationHandler(myLibrary) : null;
+ final Library realLibrary = invocationHandler instanceof ModuleEditor.ProxyDelegateAccessor ?
+ (Library)((ModuleEditor.ProxyDelegateAccessor)invocationHandler).getDelegate() : myLibrary;
+ final Library source = realLibrary instanceof LibraryImpl? ((LibraryImpl)realLibrary).getSource() : null;
+ return source != null ? source : myLibrary;
+ }
+
+ @Override
+ public int hashCode() {
+ return System.identityHashCode(getSourceOrThis());
+ }
+
+ @Override
+ public boolean shouldShowWarningIfUnused() {
+ final LibraryTable libraryTable = myLibrary.getTable();
+ if (libraryTable == null) return false;
+ return LibraryTablesRegistrar.PROJECT_LEVEL.equals(libraryTable.getTableLevel());
+ }
+
+ @Override
+ public ProjectStructureProblemDescription createUnusedElementWarning() {
+ final List<ConfigurationErrorQuickFix> fixes = Arrays.asList(new AddLibraryToDependenciesFix(), new RemoveLibraryFix());
+ return new ProjectStructureProblemDescription("Library '" + StringUtil.escapeXml(myLibrary.getName()) + "'" + " is not used", null, createPlace(),
+ ProjectStructureProblemType.unused("unused-library"), ProjectStructureProblemDescription.ProblemLevel.PROJECT,
+ fixes, false);
+ }
+
+ @Override
+ public String getPresentableName() {
+ return "Library '" + myLibrary.getName() + "'";
+ }
+
+ @Override
+ public String getTypeName() {
+ return "Library";
+ }
+
+ @Override
+ public String getId() {
+ return "library:" + myLibrary.getTable().getTableLevel() + ":" + myLibrary.getName();
+ }
+
+ private class RemoveInvalidRootsQuickFix extends ConfigurationErrorQuickFix {
+ private final Library myLibrary;
+ private final OrderRootType myType;
+ private final List<String> myInvalidUrls;
+
+ public RemoveInvalidRootsQuickFix(Library library, OrderRootType type, List<String> invalidUrls) {
+ super("Remove invalid " + StringUtil.pluralize("root", invalidUrls.size()));
+ myLibrary = library;
+ myType = type;
+ myInvalidUrls = invalidUrls;
+ }
+
+ @Override
+ public void performFix() {
+ final LibraryTable.ModifiableModel libraryTable = myContext.getModifiableLibraryTable(myLibrary.getTable());
+ if (libraryTable instanceof LibrariesModifiableModel) {
+ for (String invalidRoot : myInvalidUrls) {
+ final ExistingLibraryEditor libraryEditor = ((LibrariesModifiableModel)libraryTable).getLibraryEditor(myLibrary);
+ libraryEditor.removeRoot(invalidRoot, myType);
+ }
+ myContext.getDaemonAnalyzer().queueUpdate(LibraryProjectStructureElement.this);
+ final ProjectStructureConfigurable structureConfigurable = ProjectStructureConfigurable.getInstance(myContext.getProject());
+ navigate().doWhenDone(new Runnable() {
+ @Override
+ public void run() {
+ final NamedConfigurable configurable = structureConfigurable.getConfigurableFor(myLibrary).getSelectedConfigurable();
+ if (configurable instanceof LibraryConfigurable) {
+ ((LibraryConfigurable)configurable).updateComponent();
+ }
+ }
+ });
+ }
+ }
+ }
+
+ private class AddLibraryToDependenciesFix extends ConfigurationErrorQuickFix {
+ private AddLibraryToDependenciesFix() {
+ super("Add to Dependencies...");
+ }
+
+ @Override
+ public void performFix() {
+ LibraryEditingUtil.showDialogAndAddLibraryToDependencies(myLibrary, myContext.getProject(), false);
+ }
+ }
+
+ private class RemoveLibraryFix extends ConfigurationErrorQuickFix {
+ private RemoveLibraryFix() {
+ super("Remove Library");
+ }
+
+ @Override
+ public void performFix() {
+ BaseLibrariesConfigurable.getInstance(myContext.getProject(), myLibrary.getTable().getTableLevel()).removeLibrary(LibraryProjectStructureElement.this);
+ }
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/daemon/ModuleProjectStructureElement.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/daemon/ModuleProjectStructureElement.java
new file mode 100644
index 0000000..a3242a7
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/daemon/ModuleProjectStructureElement.java
@@ -0,0 +1,155 @@
+package com.intellij.openapi.roots.ui.configuration.projectRoot.daemon;
+
+import com.intellij.openapi.module.ModifiableModuleModel;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.project.ProjectBundle;
+import com.intellij.openapi.projectRoots.Sdk;
+import com.intellij.openapi.roots.*;
+import com.intellij.openapi.roots.libraries.Library;
+import com.intellij.openapi.roots.ui.configuration.ModuleEditor;
+import com.intellij.openapi.roots.ui.configuration.ProjectStructureConfigurable;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.StructureConfigurableContext;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.util.ArrayUtil;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author nik
+ */
+public class ModuleProjectStructureElement extends ProjectStructureElement {
+ private final Module myModule;
+
+ public ModuleProjectStructureElement(@NotNull StructureConfigurableContext context, @NotNull Module module) {
+ super(context);
+ myModule = module;
+ }
+
+ public Module getModule() {
+ return myModule;
+ }
+
+ public void checkModulesNames(ProjectStructureProblemsHolder problemsHolder) {
+ final ModifiableModuleModel moduleModel = myContext.getModulesConfigurator().getModuleModel();
+ final Module[] all = moduleModel.getModules();
+ if (!ArrayUtil.contains(myModule, all)) {
+ return;//module has been deleted
+ }
+
+ for (Module each : all) {
+ if (each != myModule && myContext.getRealName(each).equals(myContext.getRealName(myModule))) {
+ problemsHolder.registerProblem(ProjectBundle.message("project.roots.module.duplicate.name.message"), null,
+ ProjectStructureProblemType.error("duplicate-module-name"), createPlace(),
+ null);
+ break;
+ }
+ }
+ }
+
+ @Override
+ public void check(ProjectStructureProblemsHolder problemsHolder) {
+ checkModulesNames(problemsHolder);
+
+ final ModuleRootModel rootModel = myContext.getModulesConfigurator().getRootModel(myModule);
+ if (rootModel == null) return; //already disposed
+ final OrderEntry[] entries = rootModel.getOrderEntries();
+ for (OrderEntry entry : entries) {
+ if (!entry.isValid()){
+ if (entry instanceof JdkOrderEntry && ((JdkOrderEntry)entry).getJdkName() == null) {
+ problemsHolder.registerProblem(ProjectBundle.message("project.roots.module.jdk.problem.message"), null, ProjectStructureProblemType.error("module-sdk-not-defined"), createPlace(entry),
+ null);
+ }
+ else {
+ problemsHolder.registerProblem(ProjectBundle.message("project.roots.library.problem.message", StringUtil.escapeXml(entry.getPresentableName())), null,
+ ProjectStructureProblemType.error("invalid-module-dependency"), createPlace(entry),
+ null);
+ }
+ }
+ //todo[nik] highlight libraries with invalid paths in ClasspathEditor
+ //else if (entry instanceof LibraryOrderEntry) {
+ // final LibraryEx library = (LibraryEx)((LibraryOrderEntry)entry).getLibrary();
+ // if (library != null) {
+ // if (!library.allPathsValid(OrderRootType.CLASSES)) {
+ // problemsHolder.registerError(ProjectBundle.message("project.roots.tooltip.library.misconfigured", entry.getName()));
+ // }
+ // else if (!library.allPathsValid(OrderRootType.SOURCES)) {
+ // problemsHolder.registerWarning(ProjectBundle.message("project.roots.tooltip.library.misconfigured", entry.getName()));
+ // }
+ // }
+ //}
+ }
+ }
+
+ private PlaceInProjectStructure createPlace() {
+ final Project project = myContext.getProject();
+ return new PlaceInProjectStructureBase(project, ProjectStructureConfigurable.getInstance(project).createModulePlace(myModule), this);
+ }
+
+ private PlaceInProjectStructure createPlace(OrderEntry entry) {
+ return new PlaceInModuleClasspath(myContext, myModule, this, entry);
+ }
+
+ @Override
+ public List<ProjectStructureElementUsage> getUsagesInElement() {
+ final List<ProjectStructureElementUsage> usages = new ArrayList<ProjectStructureElementUsage>();
+ final ModuleEditor moduleEditor = myContext.getModulesConfigurator().getModuleEditor(myModule);
+ if (moduleEditor != null) {
+ for (OrderEntry entry : moduleEditor.getOrderEntries()) {
+ if (entry instanceof ModuleOrderEntry) {
+ ModuleOrderEntry moduleOrderEntry = (ModuleOrderEntry)entry;
+ final Module module = moduleOrderEntry.getModule();
+ if (module != null) {
+ usages.add(new UsageInModuleClasspath(myContext, this, new ModuleProjectStructureElement(myContext, module), moduleOrderEntry.getScope()));
+ }
+ }
+ else if (entry instanceof LibraryOrderEntry) {
+ LibraryOrderEntry libraryOrderEntry = (LibraryOrderEntry)entry;
+ final Library library = libraryOrderEntry.getLibrary();
+ if (library != null) {
+ usages.add(new UsageInModuleClasspath(myContext, this, new LibraryProjectStructureElement(myContext, library),
+ libraryOrderEntry.getScope()));
+ }
+ }
+ else if (entry instanceof JdkOrderEntry) {
+ final Sdk jdk = ((JdkOrderEntry)entry).getJdk();
+ if (jdk != null) {
+ usages.add(new UsageInModuleClasspath(myContext, this, new SdkProjectStructureElement(myContext, jdk), null));
+ }
+ }
+ }
+ }
+ return usages;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof ModuleProjectStructureElement)) return false;
+
+ return myModule.equals(((ModuleProjectStructureElement)o).myModule);
+
+ }
+
+ @Override
+ public int hashCode() {
+ return myModule.hashCode();
+ }
+
+ @Override
+ public String getPresentableName() {
+ return "Module '" + myModule.getName() + "'";
+ }
+
+ @Override
+ public String getTypeName() {
+ return "Module";
+ }
+
+ @Override
+ public String getId() {
+ return "module:" + myModule.getName();
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/daemon/PlaceInModuleClasspath.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/daemon/PlaceInModuleClasspath.java
new file mode 100644
index 0000000..1bf4448
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/daemon/PlaceInModuleClasspath.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2000-2011 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.daemon;
+
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.roots.ModuleRootModel;
+import com.intellij.openapi.roots.OrderEntry;
+import com.intellij.openapi.roots.impl.OrderEntryUtil;
+import com.intellij.openapi.roots.ui.configuration.ProjectStructureConfigurable;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.StructureConfigurableContext;
+import com.intellij.openapi.util.ActionCallback;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * @author nik
+ */
+public class PlaceInModuleClasspath extends PlaceInProjectStructure {
+ private final StructureConfigurableContext myContext;
+ private final Module myModule;
+ private final ProjectStructureElement myElement;
+ private final OrderEntry myOrderEntry;
+
+ public PlaceInModuleClasspath(StructureConfigurableContext context, Module module, ProjectStructureElement element, OrderEntry orderEntry) {
+ myContext = context;
+ myModule = module;
+ myElement = element;
+ myOrderEntry = orderEntry;
+ }
+
+ public PlaceInModuleClasspath(@NotNull StructureConfigurableContext context, @NotNull Module module, ProjectStructureElement element, @NotNull ProjectStructureElement elementInClasspath) {
+ myContext = context;
+ myModule = module;
+ myElement = element;
+ ModuleRootModel rootModel = myContext.getModulesConfigurator().getRootModel(myModule);
+ if (elementInClasspath instanceof LibraryProjectStructureElement) {
+ myOrderEntry = OrderEntryUtil.findLibraryOrderEntry(rootModel, ((LibraryProjectStructureElement)elementInClasspath).getLibrary());
+ }
+ else if (elementInClasspath instanceof ModuleProjectStructureElement) {
+ myOrderEntry = OrderEntryUtil.findModuleOrderEntry(rootModel, ((ModuleProjectStructureElement)elementInClasspath).getModule());
+ }
+ else if (elementInClasspath instanceof SdkProjectStructureElement) {
+ myOrderEntry = OrderEntryUtil.findJdkOrderEntry(rootModel, ((SdkProjectStructureElement)elementInClasspath).getSdk());
+ }
+ else {
+ myOrderEntry = null;
+ }
+ }
+
+ @NotNull
+ @Override
+ public ProjectStructureElement getContainingElement() {
+ return myElement;
+ }
+
+ @Override
+ public String getPlacePath() {
+ return myOrderEntry != null ? myOrderEntry.getPresentableName() : null;
+ }
+
+ @NotNull
+ @Override
+ public ActionCallback navigate() {
+ return ProjectStructureConfigurable.getInstance(myContext.getProject()).selectOrderEntry(myModule, myOrderEntry);
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/daemon/PlaceInProjectStructure.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/daemon/PlaceInProjectStructure.java
new file mode 100644
index 0000000..03cf009
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/daemon/PlaceInProjectStructure.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2000-2011 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.daemon;
+
+import com.intellij.openapi.util.ActionCallback;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * @author nik
+ */
+public abstract class PlaceInProjectStructure {
+ @NotNull
+ public abstract ProjectStructureElement getContainingElement();
+
+ @Nullable
+ public abstract String getPlacePath();
+
+ @NotNull
+ public abstract ActionCallback navigate();
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/daemon/PlaceInProjectStructureBase.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/daemon/PlaceInProjectStructureBase.java
new file mode 100644
index 0000000..4331c5f
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/daemon/PlaceInProjectStructureBase.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2000-2011 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.daemon;
+
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.roots.ui.configuration.ProjectStructureConfigurable;
+import com.intellij.openapi.util.ActionCallback;
+import com.intellij.ui.navigation.Place;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * @author nik
+ */
+public class PlaceInProjectStructureBase extends PlaceInProjectStructure {
+ private final Project myProject;
+ private final Place myPlace;
+ private final ProjectStructureElement myElement;
+
+ public PlaceInProjectStructureBase(Project project, Place place, ProjectStructureElement element) {
+ myProject = project;
+ myPlace = place;
+ myElement = element;
+ }
+
+ @Override
+ public String getPlacePath() {
+ return null;
+ }
+
+ @NotNull
+ @Override
+ public ProjectStructureElement getContainingElement() {
+ return myElement;
+ }
+
+ @NotNull
+ @Override
+ public ActionCallback navigate() {
+ return ProjectStructureConfigurable.getInstance(myProject).navigateTo(myPlace, true);
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/daemon/ProjectConfigurationProblem.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/daemon/ProjectConfigurationProblem.java
new file mode 100644
index 0000000..77d29ba
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/daemon/ProjectConfigurationProblem.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2000-2010 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.daemon;
+
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.roots.ui.configuration.ConfigurationError;
+import com.intellij.openapi.ui.popup.JBPopupFactory;
+import com.intellij.openapi.ui.popup.PopupStep;
+import com.intellij.openapi.ui.popup.util.BaseListPopupStep;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.ui.awt.RelativePoint;
+import org.jetbrains.annotations.NotNull;
+
+import javax.swing.*;
+
+/**
+* @author nik
+*/
+class ProjectConfigurationProblem extends ConfigurationError {
+ private final ProjectStructureProblemDescription myDescription;
+ private final Project myProject;
+
+ public ProjectConfigurationProblem(ProjectStructureProblemDescription description, Project project) {
+ super(StringUtil.unescapeXml(description.getMessage(true)), computeDescription(description),
+ getSettings(project, description.getProblemLevel()).isIgnored(description));
+ myDescription = description;
+ myProject = project;
+ }
+
+ private static ProjectStructureProblemsSettings getSettings(Project project, ProjectStructureProblemDescription.ProblemLevel problemLevel) {
+ if (problemLevel == ProjectStructureProblemDescription.ProblemLevel.PROJECT) {
+ return ProjectStructureProblemsSettings.getProjectInstance(project);
+ }
+ else {
+ return ProjectStructureProblemsSettings.getGlobalInstance();
+ }
+ }
+
+ private static String computeDescription(ProjectStructureProblemDescription description) {
+ final String descriptionString = description.getDescription();
+ return descriptionString != null ? descriptionString : description.getMessage(true);
+ }
+
+ @Override
+ public void ignore(boolean ignored) {
+ super.ignore(ignored);
+ getSettings(myProject, myDescription.getProblemLevel()).setIgnored(myDescription, ignored);
+ }
+
+ @Override
+ public void navigate() {
+ myDescription.getPlace().navigate();
+ }
+
+ @Override
+ public boolean canBeFixed() {
+ return !myDescription.getFixes().isEmpty();
+ }
+
+ @Override
+ public void fix(final JComponent contextComponent, RelativePoint relativePoint) {
+ JBPopupFactory.getInstance().createListPopup(new BaseListPopupStep<ConfigurationErrorQuickFix>(null, myDescription.getFixes()) {
+ @NotNull
+ @Override
+ public String getTextFor(ConfigurationErrorQuickFix value) {
+ return value.getActionName();
+ }
+
+ @Override
+ public PopupStep onChosen(final ConfigurationErrorQuickFix selectedValue, boolean finalChoice) {
+ return doFinalStep(new Runnable() {
+ @Override
+ public void run() {
+ selectedValue.performFix();
+ }
+ });
+ }
+ }).show(relativePoint);
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/daemon/ProjectConfigurationProblems.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/daemon/ProjectConfigurationProblems.java
new file mode 100644
index 0000000..fbbc04d
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/daemon/ProjectConfigurationProblems.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2000-2010 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.daemon;
+
+import com.intellij.openapi.roots.ui.configuration.ConfigurationError;
+import com.intellij.openapi.roots.ui.configuration.ConfigurationErrors;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.StructureConfigurableContext;
+import com.intellij.openapi.util.MultiValuesMap;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * @author nik
+ */
+public class ProjectConfigurationProblems {
+ private final MultiValuesMap<ProjectStructureElement, ConfigurationError> myErrors = new MultiValuesMap<ProjectStructureElement, ConfigurationError>();
+ private final ProjectStructureDaemonAnalyzer myAnalyzer;
+ private final StructureConfigurableContext myContext;
+
+ public ProjectConfigurationProblems(ProjectStructureDaemonAnalyzer analyzer, StructureConfigurableContext context) {
+ myAnalyzer = analyzer;
+ myContext = context;
+ analyzer.addListener(new ProjectStructureDaemonAnalyzerListener() {
+ @Override
+ public void problemsChanged(@NotNull ProjectStructureElement element) {
+ updateErrors(element);
+ }
+ });
+ }
+
+ public void clearProblems() {
+ removeErrors(myErrors.values());
+ myErrors.clear();
+ }
+
+ private void updateErrors(ProjectStructureElement element) {
+ removeErrors(myErrors.removeAll(element));
+ final ProjectStructureProblemsHolderImpl problemsHolder = myAnalyzer.getProblemsHolder(element);
+ if (problemsHolder != null) {
+ final List<ProjectStructureProblemDescription> descriptions = problemsHolder.getProblemDescriptions();
+ if (descriptions != null) {
+ for (ProjectStructureProblemDescription description : descriptions) {
+ final ProjectConfigurationProblem error = new ProjectConfigurationProblem(description, myContext.getProject());
+ myErrors.put(element, error);
+ ConfigurationErrors.Bus.addError(error, myContext.getProject());
+ }
+ }
+ }
+ }
+
+ private void removeErrors(@Nullable Collection<ConfigurationError> errors) {
+ if (errors == null) return;
+ for (ConfigurationError error : errors) {
+ ConfigurationErrors.Bus.removeError(error, myContext.getProject());
+ }
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/daemon/ProjectStructureDaemonAnalyzer.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/daemon/ProjectStructureDaemonAnalyzer.java
new file mode 100644
index 0000000..91183b9b
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/daemon/ProjectStructureDaemonAnalyzer.java
@@ -0,0 +1,315 @@
+package com.intellij.openapi.roots.ui.configuration.projectRoot.daemon;
+
+import com.intellij.openapi.Disposable;
+import com.intellij.openapi.application.ReadAction;
+import com.intellij.openapi.application.Result;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.StructureConfigurableContext;
+import com.intellij.openapi.util.Disposer;
+import com.intellij.openapi.util.MultiValuesMap;
+import com.intellij.util.EventDispatcher;
+import com.intellij.util.ui.update.MergingUpdateQueue;
+import com.intellij.util.ui.update.Update;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import java.util.*;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * @author nik
+ */
+public class ProjectStructureDaemonAnalyzer implements Disposable {
+ private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.roots.ui.configuration.projectRoot.validation.ProjectStructureDaemonAnalyzer");
+ private final Map<ProjectStructureElement, ProjectStructureProblemsHolderImpl> myProblemHolders = new HashMap<ProjectStructureElement, ProjectStructureProblemsHolderImpl>();
+ private final MultiValuesMap<ProjectStructureElement, ProjectStructureElementUsage> mySourceElement2Usages = new MultiValuesMap<ProjectStructureElement, ProjectStructureElementUsage>();
+ private final MultiValuesMap<ProjectStructureElement, ProjectStructureElementUsage> myContainingElement2Usages = new MultiValuesMap<ProjectStructureElement, ProjectStructureElementUsage>();
+ private final Set<ProjectStructureElement> myElementWithNotCalculatedUsages = new HashSet<ProjectStructureElement>();
+ private final Set<ProjectStructureElement> myElementsToShowWarningIfUnused = new HashSet<ProjectStructureElement>();
+ private final Map<ProjectStructureElement, ProjectStructureProblemDescription> myWarningsAboutUnused = new HashMap<ProjectStructureElement, ProjectStructureProblemDescription>();
+ private final MergingUpdateQueue myAnalyzerQueue;
+ private final EventDispatcher<ProjectStructureDaemonAnalyzerListener> myDispatcher = EventDispatcher.create(ProjectStructureDaemonAnalyzerListener.class);
+ private final AtomicBoolean myStopped = new AtomicBoolean(false);
+ private final ProjectConfigurationProblems myProjectConfigurationProblems;
+
+ public ProjectStructureDaemonAnalyzer(StructureConfigurableContext context) {
+ Disposer.register(context, this);
+ myProjectConfigurationProblems = new ProjectConfigurationProblems(this, context);
+ myAnalyzerQueue = new MergingUpdateQueue("Project Structure Daemon Analyzer", 300, false, null, this, null, false);
+ }
+
+ private void doUpdate(final ProjectStructureElement element, final boolean check, final boolean collectUsages) {
+ if (myStopped.get()) return;
+
+ if (check) {
+ doCheck(element);
+ }
+ if (collectUsages) {
+ doCollectUsages(element);
+ }
+ }
+
+ private void doCheck(final ProjectStructureElement element) {
+ final ProjectStructureProblemsHolderImpl problemsHolder = new ProjectStructureProblemsHolderImpl();
+ new ReadAction() {
+ @Override
+ protected void run(final Result result) {
+ if (myStopped.get()) return;
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("checking " + element);
+ }
+ ProjectStructureValidator.check(element, problemsHolder);
+ }
+ }.execute();
+ invokeLater(new Runnable() {
+ @Override
+ public void run() {
+ if (myStopped.get()) return;
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("updating problems for " + element);
+ }
+ final ProjectStructureProblemDescription warning = myWarningsAboutUnused.get(element);
+ if (warning != null)
+ problemsHolder.registerProblem(warning);
+ myProblemHolders.put(element, problemsHolder);
+ myDispatcher.getMulticaster().problemsChanged(element);
+ }
+ });
+ }
+
+ private void doCollectUsages(final ProjectStructureElement element) {
+ final List<ProjectStructureElementUsage> usages = new ReadAction<List<ProjectStructureElementUsage>>() {
+ @Override
+ protected void run(final Result<List<ProjectStructureElementUsage>> result) {
+ if (myStopped.get()) return;
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("collecting usages in " + element);
+ }
+ result.setResult(getUsagesInElement(element));
+ }
+ }.execute().getResultObject();
+
+ invokeLater(new Runnable() {
+ @Override
+ public void run() {
+ if (myStopped.get() || usages == null) return;
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("updating usages for " + element);
+ }
+ updateUsages(element, usages);
+ }
+ });
+ }
+
+ private static List<ProjectStructureElementUsage> getUsagesInElement(final ProjectStructureElement element) {
+ return ProjectStructureValidator.getUsagesInElement(element);
+ }
+
+ private void updateUsages(ProjectStructureElement element, List<ProjectStructureElementUsage> usages) {
+ removeUsagesInElement(element);
+ for (ProjectStructureElementUsage usage : usages) {
+ addUsage(usage);
+ }
+ myElementWithNotCalculatedUsages.remove(element);
+ reportUnusedElements();
+ }
+
+ private static void invokeLater(Runnable runnable) {
+ SwingUtilities.invokeLater(runnable);
+ }
+
+ public void queueUpdate(@NotNull final ProjectStructureElement element) {
+ queueUpdate(element, true, true);
+ }
+
+ private void queueUpdate(@NotNull final ProjectStructureElement element, final boolean check, final boolean collectUsages) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("start " + (check ? "checking " : "") + (collectUsages ? "collecting usages " : "") + "for " + element);
+ }
+ if (collectUsages) {
+ myElementWithNotCalculatedUsages.add(element);
+ }
+ if (element.shouldShowWarningIfUnused()) {
+ myElementsToShowWarningIfUnused.add(element);
+ }
+ myAnalyzerQueue.queue(new AnalyzeElementUpdate(element, check, collectUsages));
+ }
+
+ public void removeElement(ProjectStructureElement element) {
+ myElementWithNotCalculatedUsages.remove(element);
+ myElementsToShowWarningIfUnused.remove(element);
+ myWarningsAboutUnused.remove(element);
+ myProblemHolders.remove(element);
+ final Collection<ProjectStructureElementUsage> usages = mySourceElement2Usages.removeAll(element);
+ if (usages != null) {
+ for (ProjectStructureElementUsage usage : usages) {
+ myProblemHolders.remove(usage.getContainingElement());
+ }
+ }
+ removeUsagesInElement(element);
+ myDispatcher.getMulticaster().problemsChanged(element);
+ reportUnusedElements();
+ }
+
+ private void reportUnusedElements() {
+ if (!myElementWithNotCalculatedUsages.isEmpty()) return;
+
+ for (ProjectStructureElement element : myElementsToShowWarningIfUnused) {
+ final ProjectStructureProblemDescription warning;
+ final Collection<ProjectStructureElementUsage> usages = mySourceElement2Usages.get(element);
+ if (usages == null || usages.isEmpty()) {
+ warning = element.createUnusedElementWarning();
+ }
+ else {
+ warning = null;
+ }
+
+ final ProjectStructureProblemDescription old = myWarningsAboutUnused.put(element, warning);
+ ProjectStructureProblemsHolderImpl holder = myProblemHolders.get(element);
+ if (holder == null) {
+ holder = new ProjectStructureProblemsHolderImpl();
+ myProblemHolders.put(element, holder);
+ }
+ if (old != null) {
+ holder.removeProblem(old);
+ }
+ if (warning != null) {
+ holder.registerProblem(warning);
+ }
+ if (old != null || warning != null) {
+ myDispatcher.getMulticaster().problemsChanged(element);
+ }
+ }
+ }
+
+ private void removeUsagesInElement(ProjectStructureElement element) {
+ final Collection<ProjectStructureElementUsage> usages = myContainingElement2Usages.removeAll(element);
+ if (usages != null) {
+ for (ProjectStructureElementUsage usage : usages) {
+ mySourceElement2Usages.remove(usage.getSourceElement(), usage);
+ }
+ }
+ }
+
+ private void addUsage(@NotNull ProjectStructureElementUsage usage) {
+ mySourceElement2Usages.put(usage.getSourceElement(), usage);
+ myContainingElement2Usages.put(usage.getContainingElement(), usage);
+ }
+
+ public void stop() {
+ LOG.debug("analyzer stopped");
+ myStopped.set(true);
+ myAnalyzerQueue.cancelAllUpdates();
+ clearCaches();
+ myAnalyzerQueue.deactivate();
+ }
+
+ public void clearCaches() {
+ LOG.debug("clear caches");
+ myProblemHolders.clear();
+ }
+
+ public void queueUpdateForAllElementsWithErrors() {
+ List<ProjectStructureElement> toUpdate = new ArrayList<ProjectStructureElement>();
+ for (Map.Entry<ProjectStructureElement, ProjectStructureProblemsHolderImpl> entry : myProblemHolders.entrySet()) {
+ if (entry.getValue().containsProblems()) {
+ toUpdate.add(entry.getKey());
+ }
+ }
+ myProblemHolders.clear();
+ LOG.debug("Adding to queue updates for " + toUpdate.size() + " problematic elements");
+ for (ProjectStructureElement element : toUpdate) {
+ queueUpdate(element);
+ }
+ }
+
+ @Override
+ public void dispose() {
+ myStopped.set(true);
+ myAnalyzerQueue.cancelAllUpdates();
+ }
+
+ @Nullable
+ public ProjectStructureProblemsHolderImpl getProblemsHolder(ProjectStructureElement element) {
+ return myProblemHolders.get(element);
+ }
+
+ public Collection<ProjectStructureElementUsage> getUsages(ProjectStructureElement selected) {
+ ProjectStructureElement[] elements = myElementWithNotCalculatedUsages.toArray(new ProjectStructureElement[myElementWithNotCalculatedUsages.size()]);
+ for (ProjectStructureElement element : elements) {
+ updateUsages(element, getUsagesInElement(element));
+ }
+ final Collection<ProjectStructureElementUsage> usages = mySourceElement2Usages.get(selected);
+ return usages != null ? usages : Collections.<ProjectStructureElementUsage>emptyList();
+ }
+
+ public void addListener(ProjectStructureDaemonAnalyzerListener listener) {
+ LOG.debug("listener added " + listener);
+ myDispatcher.addListener(listener);
+ }
+
+ public void reset() {
+ LOG.debug("analyzer started");
+ myAnalyzerQueue.activate();
+ myAnalyzerQueue.queue(new Update("reset") {
+ @Override
+ public void run() {
+ myStopped.set(false);
+ }
+ });
+ }
+
+ public void clear() {
+ myWarningsAboutUnused.clear();
+ myElementsToShowWarningIfUnused.clear();
+ mySourceElement2Usages.clear();
+ myContainingElement2Usages.clear();
+ myElementWithNotCalculatedUsages.clear();
+ if (myProjectConfigurationProblems != null) {
+ myProjectConfigurationProblems.clearProblems();
+ }
+ }
+
+ private class AnalyzeElementUpdate extends Update {
+ private final ProjectStructureElement myElement;
+ private final boolean myCheck;
+ private final boolean myCollectUsages;
+ private final Object[] myEqualityObjects;
+
+ public AnalyzeElementUpdate(ProjectStructureElement element, boolean check, boolean collectUsages) {
+ super(element);
+ myElement = element;
+ myCheck = check;
+ myCollectUsages = collectUsages;
+ myEqualityObjects = new Object[]{myElement, myCheck, myCollectUsages};
+ }
+
+ @Override
+ public boolean canEat(Update update) {
+ if (!(update instanceof AnalyzeElementUpdate)) return false;
+ final AnalyzeElementUpdate other = (AnalyzeElementUpdate)update;
+ return myElement.equals(other.myElement) && (!other.myCheck || myCheck) && (!other.myCollectUsages || myCollectUsages);
+ }
+
+ @NotNull
+ @Override
+ public Object[] getEqualityObjects() {
+ return myEqualityObjects;
+ }
+
+ @Override
+ public void run() {
+ try {
+ doUpdate(myElement, myCheck, myCollectUsages);
+ }
+ catch (Throwable t) {
+ LOG.error(t);
+ }
+ }
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/daemon/ProjectStructureDaemonAnalyzerListener.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/daemon/ProjectStructureDaemonAnalyzerListener.java
new file mode 100644
index 0000000..a946493
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/daemon/ProjectStructureDaemonAnalyzerListener.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2000-2009 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.daemon;
+
+import org.jetbrains.annotations.NotNull;
+
+import java.util.EventListener;
+
+/**
+ * @author nik
+ */
+public interface ProjectStructureDaemonAnalyzerListener extends EventListener {
+ void problemsChanged(@NotNull ProjectStructureElement element);
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/daemon/ProjectStructureElement.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/daemon/ProjectStructureElement.java
new file mode 100644
index 0000000..fed8278
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/daemon/ProjectStructureElement.java
@@ -0,0 +1,55 @@
+package com.intellij.openapi.roots.ui.configuration.projectRoot.daemon;
+
+import com.intellij.openapi.roots.ui.configuration.projectRoot.StructureConfigurableContext;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.List;
+
+/**
+ * @author nik
+ */
+public abstract class ProjectStructureElement {
+ protected final StructureConfigurableContext myContext;
+
+ protected ProjectStructureElement(@NotNull StructureConfigurableContext context) {
+ myContext = context;
+ }
+
+ public abstract String getPresentableName();
+
+ @Nullable
+ public String getDescription() {
+ return null;
+ }
+
+ public abstract String getTypeName();
+
+ public abstract String getId();
+
+ public abstract void check(ProjectStructureProblemsHolder problemsHolder);
+
+ public abstract List<ProjectStructureElementUsage> getUsagesInElement();
+
+
+ public boolean shouldShowWarningIfUnused() {
+ return false;
+ }
+
+ @Nullable
+ public ProjectStructureProblemDescription createUnusedElementWarning() {
+ return null;
+ }
+
+
+ @Override
+ public abstract boolean equals(Object obj);
+
+ @Override
+ public abstract int hashCode();
+
+ @Override
+ public String toString() {
+ return getId();
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/daemon/ProjectStructureElementUsage.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/daemon/ProjectStructureElementUsage.java
new file mode 100644
index 0000000..ad680b2
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/daemon/ProjectStructureElementUsage.java
@@ -0,0 +1,35 @@
+package com.intellij.openapi.roots.ui.configuration.projectRoot.daemon;
+
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+
+/**
+ * @author nik
+ */
+public abstract class ProjectStructureElementUsage {
+ public abstract ProjectStructureElement getSourceElement();
+
+ public abstract ProjectStructureElement getContainingElement();
+
+ public abstract String getPresentableName();
+
+ @Nullable
+ public String getPresentableLocationInElement() {
+ return null;
+ }
+
+ public abstract PlaceInProjectStructure getPlace();
+
+ @Override
+ public abstract int hashCode();
+
+ @Override
+ public abstract boolean equals(Object obj);
+
+ public abstract Icon getIcon();
+
+ public abstract void removeSourceElement();
+
+ public abstract void replaceElement(ProjectStructureElement newElement);
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/daemon/ProjectStructureProblemDescription.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/daemon/ProjectStructureProblemDescription.java
new file mode 100644
index 0000000..e39e696
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/daemon/ProjectStructureProblemDescription.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2000-2009 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.daemon;
+
+import com.intellij.openapi.util.text.StringUtil;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.List;
+
+/**
+ * @author nik
+ */
+public class ProjectStructureProblemDescription {
+ public enum ProblemLevel {PROJECT, GLOBAL}
+ private final String myMessage;
+ private final String myDescription;
+ private final PlaceInProjectStructure myPlace;
+ private final List<? extends ConfigurationErrorQuickFix> myFixes;
+ private final ProjectStructureProblemType myProblemType;
+ private final ProblemLevel myProblemLevel;
+ private final boolean myCanShowPlace;
+
+ public ProjectStructureProblemDescription(@NotNull String message,
+ @Nullable String description,
+ @NotNull PlaceInProjectStructure place,
+ @NotNull ProjectStructureProblemType problemType,
+ @NotNull List<? extends ConfigurationErrorQuickFix> fixes) {
+ this(message, description, place, problemType, ProblemLevel.PROJECT, fixes, true);
+ }
+
+ public ProjectStructureProblemDescription(@NotNull String message,
+ @Nullable String description,
+ @NotNull PlaceInProjectStructure place,
+ @NotNull ProjectStructureProblemType problemType,
+ @NotNull ProblemLevel level,
+ @NotNull List<? extends ConfigurationErrorQuickFix> fixes, final boolean canShowPlace) {
+ myMessage = message;
+ myDescription = description;
+ myPlace = place;
+ myFixes = fixes;
+ myProblemType = problemType;
+ myProblemLevel = level;
+ myCanShowPlace = canShowPlace;
+ }
+
+ public ProblemLevel getProblemLevel() {
+ return myProblemLevel;
+ }
+
+ public String getMessage(final boolean includePlace) {
+ if (includePlace && myCanShowPlace) {
+ return myPlace.getContainingElement().getPresentableName() + ": " + StringUtil.decapitalize(myMessage);
+ }
+ return myMessage;
+ }
+
+ @Nullable
+ public String getDescription() {
+ return myDescription;
+ }
+
+ public List<? extends ConfigurationErrorQuickFix> getFixes() {
+ return myFixes;
+ }
+
+ public ProjectStructureProblemType.Severity getSeverity() {
+ return myProblemType.getSeverity();
+ }
+
+ public PlaceInProjectStructure getPlace() {
+ return myPlace;
+ }
+
+ public String getId() {
+ final String placePath = myPlace.getPlacePath();
+ return myProblemType.getId() + "(" + myPlace.getContainingElement().getId() + (placePath != null ? "," + placePath : "") + ")";
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/daemon/ProjectStructureProblemType.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/daemon/ProjectStructureProblemType.java
new file mode 100644
index 0000000..83f2ef0
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/daemon/ProjectStructureProblemType.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2000-2011 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.daemon;
+
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * @author nik
+ */
+public class ProjectStructureProblemType {
+ public enum Severity { ERROR, WARNING, UNUSED }
+
+ private final String myId;
+ private final Severity mySeverity;
+
+ public ProjectStructureProblemType(@NotNull String id, @NotNull Severity severity) {
+ myId = id;
+ mySeverity = severity;
+ }
+
+ public static ProjectStructureProblemType error(@NotNull String id) {
+ return new ProjectStructureProblemType(id, Severity.ERROR);
+ }
+
+ public static ProjectStructureProblemType warning(@NotNull String id) {
+ return new ProjectStructureProblemType(id, Severity.WARNING);
+ }
+
+ public static ProjectStructureProblemType unused(@NotNull String id) {
+ return new ProjectStructureProblemType(id, Severity.UNUSED);
+ }
+
+ @NotNull
+ public String getId() {
+ return myId;
+ }
+
+ @NotNull
+ public Severity getSeverity() {
+ return mySeverity;
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/daemon/ProjectStructureProblemsHolder.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/daemon/ProjectStructureProblemsHolder.java
new file mode 100644
index 0000000..e6d6a8c
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/daemon/ProjectStructureProblemsHolder.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2000-2009 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.daemon;
+
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * @author nik
+ */
+public interface ProjectStructureProblemsHolder {
+ void registerProblem(@NotNull String message, @Nullable String description, @NotNull ProjectStructureProblemType problemType,
+ @NotNull PlaceInProjectStructure place, @Nullable ConfigurationErrorQuickFix fix);
+
+ void registerProblem(@NotNull ProjectStructureProblemDescription description);
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/daemon/ProjectStructureProblemsHolderImpl.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/daemon/ProjectStructureProblemsHolderImpl.java
new file mode 100644
index 0000000..f71939a
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/daemon/ProjectStructureProblemsHolderImpl.java
@@ -0,0 +1,83 @@
+package com.intellij.openapi.roots.ui.configuration.projectRoot.daemon;
+
+import com.intellij.util.SmartList;
+import com.intellij.util.StringBuilderSpinAllocator;
+import com.intellij.xml.util.XmlStringUtil;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * @author nik
+ */
+public class ProjectStructureProblemsHolderImpl implements ProjectStructureProblemsHolder {
+ private List<ProjectStructureProblemDescription> myProblemDescriptions;
+
+ @Override
+ public void registerProblem(@NotNull String message, @Nullable String description,
+ @NotNull ProjectStructureProblemType problemType,
+ @NotNull PlaceInProjectStructure place,
+ @Nullable ConfigurationErrorQuickFix fix) {
+ final List<ConfigurationErrorQuickFix> fixes = fix != null ? Collections.singletonList(fix) : Collections.<ConfigurationErrorQuickFix>emptyList();
+ registerProblem(new ProjectStructureProblemDescription(message, description, place, problemType, fixes));
+ }
+
+ @Override
+ public void registerProblem(final @NotNull ProjectStructureProblemDescription description) {
+ if (myProblemDescriptions == null) {
+ myProblemDescriptions = new SmartList<ProjectStructureProblemDescription>();
+ }
+ myProblemDescriptions.add(description);
+ }
+
+ public String composeTooltipMessage() {
+ final StringBuilder buf = StringBuilderSpinAllocator.alloc();
+ try {
+ buf.append("<html><body>");
+ if (myProblemDescriptions != null) {
+ int problems = 0;
+ for (ProjectStructureProblemDescription problemDescription : myProblemDescriptions) {
+ buf.append(XmlStringUtil.escapeString(problemDescription.getMessage(false))).append("<br>");
+ problems++;
+ if (problems >= 10 && myProblemDescriptions.size() > 12) {
+ buf.append(myProblemDescriptions.size() - problems).append(" more problems...<br>");
+ break;
+ }
+ }
+ }
+ buf.append("</body></html>");
+ return buf.toString();
+ }
+ finally {
+ StringBuilderSpinAllocator.dispose(buf);
+ }
+ }
+
+ public boolean containsProblems() {
+ return myProblemDescriptions != null && !myProblemDescriptions.isEmpty();
+ }
+
+ public boolean containsProblems(final ProjectStructureProblemType.Severity severity) {
+ if (myProblemDescriptions != null) {
+ for (ProjectStructureProblemDescription description : myProblemDescriptions) {
+ if (description.getSeverity() == severity) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ public void removeProblem(@NotNull ProjectStructureProblemDescription description) {
+ if (myProblemDescriptions != null) {
+ myProblemDescriptions.remove(description);
+ }
+ }
+
+ @Nullable
+ public List<ProjectStructureProblemDescription> getProblemDescriptions() {
+ return myProblemDescriptions;
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/daemon/ProjectStructureProblemsSettings.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/daemon/ProjectStructureProblemsSettings.java
new file mode 100644
index 0000000..499fca7
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/daemon/ProjectStructureProblemsSettings.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2000-2011 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.daemon;
+
+import com.intellij.openapi.components.ServiceManager;
+import com.intellij.openapi.project.Project;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * @author nik
+ */
+public abstract class ProjectStructureProblemsSettings {
+ public static ProjectStructureProblemsSettings getProjectInstance(@NotNull Project project) {
+ return ServiceManager.getService(project, ProjectStructureProblemsSettings.class);
+ }
+
+ public static ProjectStructureProblemsSettings getGlobalInstance() {
+ return ServiceManager.getService(ProjectStructureProblemsSettings.class);
+ }
+
+ public abstract boolean isIgnored(@NotNull ProjectStructureProblemDescription description);
+ public abstract void setIgnored(@NotNull ProjectStructureProblemDescription description, boolean ignored);
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/daemon/ProjectStructureProblemsSettingsBase.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/daemon/ProjectStructureProblemsSettingsBase.java
new file mode 100644
index 0000000..4f7c288
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/daemon/ProjectStructureProblemsSettingsBase.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2000-2011 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.daemon;
+
+import com.intellij.openapi.components.PersistentStateComponent;
+import com.intellij.util.containers.SortedList;
+import com.intellij.util.xmlb.XmlSerializerUtil;
+import com.intellij.util.xmlb.annotations.AbstractCollection;
+import com.intellij.util.xmlb.annotations.Tag;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.List;
+
+/**
+ * @author nik
+ */
+public class ProjectStructureProblemsSettingsBase extends ProjectStructureProblemsSettings implements PersistentStateComponent<ProjectStructureProblemsSettingsBase> {
+ @AbstractCollection(surroundWithTag = false, elementTag = "problem", elementValueAttribute = "id")
+ @Tag("ignored-problems")
+ public List<String> myIgnoredProblems = new SortedList<String>(String.CASE_INSENSITIVE_ORDER);
+
+ @Override
+ public ProjectStructureProblemsSettingsBase getState() {
+ return this;
+ }
+
+ @Override
+ public void loadState(ProjectStructureProblemsSettingsBase state) {
+ XmlSerializerUtil.copyBean(state, this);
+ }
+
+ @Override
+ public boolean isIgnored(@NotNull ProjectStructureProblemDescription description) {
+ return myIgnoredProblems.contains(description.getId());
+ }
+
+ @Override
+ public void setIgnored(@NotNull ProjectStructureProblemDescription description, boolean ignored) {
+ final String id = description.getId();
+ if (ignored) {
+ myIgnoredProblems.add(id);
+ }
+ else {
+ myIgnoredProblems.remove(id);
+ }
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/daemon/ProjectStructureProblemsSettingsImpl.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/daemon/ProjectStructureProblemsSettingsImpl.java
new file mode 100644
index 0000000..6cb37e0
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/daemon/ProjectStructureProblemsSettingsImpl.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2000-2012 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.daemon;
+
+import com.intellij.openapi.components.State;
+import com.intellij.openapi.components.Storage;
+import com.intellij.openapi.components.StoragePathMacros;
+
+/**
+ * @author nik
+ */
+@State(
+ name = "ProjectStructureProblems",
+ storages = {@Storage(file = StoragePathMacros.WORKSPACE_FILE)}
+)
+public class ProjectStructureProblemsSettingsImpl extends ProjectStructureProblemsSettingsBase {
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/daemon/ProjectStructureValidator.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/daemon/ProjectStructureValidator.java
new file mode 100644
index 0000000..32eff3a
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/daemon/ProjectStructureValidator.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright 2000-2012 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.daemon;
+
+import com.intellij.openapi.extensions.ExtensionPointName;
+import com.intellij.openapi.module.Module;
+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.libraries.Library;
+import com.intellij.openapi.roots.ui.configuration.ChooseModulesDialog;
+import com.intellij.openapi.roots.ui.configuration.libraries.LibraryEditingUtil;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.ModuleStructureConfigurable;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.List;
+
+/**
+ * User: ksafonov
+ */
+public abstract class ProjectStructureValidator {
+
+ private static final ExtensionPointName<ProjectStructureValidator> EP_NAME =
+ ExtensionPointName.create("com.intellij.projectStructureValidator");
+
+ public static List<ProjectStructureElementUsage> getUsagesInElement(final ProjectStructureElement element) {
+ for (ProjectStructureValidator validator : EP_NAME.getExtensions()) {
+ List<ProjectStructureElementUsage> usages = validator.getUsagesIn(element);
+ if (usages != null) {
+ return usages;
+ }
+ }
+ return element.getUsagesInElement();
+ }
+
+ public static void check(ProjectStructureElement element, ProjectStructureProblemsHolder problemsHolder) {
+ for (ProjectStructureValidator validator : EP_NAME.getExtensions()) {
+ if (validator.checkElement(element, problemsHolder)) {
+ return;
+ }
+ }
+ element.check(problemsHolder);
+ }
+
+ public static void showDialogAndAddLibraryToDependencies(final Library library, final Project project, boolean allowEmptySelection) {
+ for (ProjectStructureValidator validator : EP_NAME.getExtensions()) {
+ if (validator.addLibraryToDependencies(library, project, allowEmptySelection)) {
+ return;
+ }
+ }
+
+ final ModuleStructureConfigurable moduleStructureConfigurable = ModuleStructureConfigurable.getInstance(project);
+ final List<Module> modules = LibraryEditingUtil.getSuitableModules(moduleStructureConfigurable, ((LibraryEx)library).getKind(), library);
+ if (modules.isEmpty()) return;
+ final ChooseModulesDialog
+ dlg = new ChooseModulesDialog(moduleStructureConfigurable.getProject(), modules, ProjectBundle.message("choose.modules.dialog.title"),
+ ProjectBundle
+ .message("choose.modules.dialog.description", library.getName()));
+ dlg.show();
+ if (dlg.isOK()) {
+ final List<Module> chosenModules = dlg.getChosenElements();
+ for (Module module : chosenModules) {
+ moduleStructureConfigurable.addLibraryOrderEntry(module, library);
+ }
+ }
+ }
+
+ /**
+ * @return <code>true</code> if handled
+ */
+ protected boolean addLibraryToDependencies(final Library library, final Project project, final boolean allowEmptySelection) {
+ return false;
+ }
+
+
+ /**
+ * @return <code>true</code> if it handled this element
+ */
+ protected boolean checkElement(ProjectStructureElement element, ProjectStructureProblemsHolder problemsHolder) {
+ return false;
+ }
+
+ /**
+ * @return list of usages or <code>null</code> when it does not handle such element
+ */
+ @Nullable
+ protected List<ProjectStructureElementUsage> getUsagesIn(final ProjectStructureElement element) {
+ return null;
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/daemon/SdkProjectStructureElement.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/daemon/SdkProjectStructureElement.java
new file mode 100644
index 0000000..b85fde8
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/daemon/SdkProjectStructureElement.java
@@ -0,0 +1,60 @@
+package com.intellij.openapi.roots.ui.configuration.projectRoot.daemon;
+
+import com.intellij.openapi.projectRoots.Sdk;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.StructureConfigurableContext;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * @author nik
+ */
+public class SdkProjectStructureElement extends ProjectStructureElement {
+ private final Sdk mySdk;
+
+ public SdkProjectStructureElement(StructureConfigurableContext context, Sdk sdk) {
+ super(context);
+ mySdk = sdk;
+ }
+
+ public Sdk getSdk() {
+ return mySdk;
+ }
+
+ @Override
+ public void check(ProjectStructureProblemsHolder problemsHolder) {
+ }
+
+ @Override
+ public List<ProjectStructureElementUsage> getUsagesInElement() {
+ return Collections.emptyList();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof SdkProjectStructureElement)) return false;
+ return mySdk.equals(((SdkProjectStructureElement)o).mySdk);
+
+ }
+
+ @Override
+ public int hashCode() {
+ return mySdk.hashCode();
+ }
+
+ @Override
+ public String getPresentableName() {
+ return "SDK '" + mySdk.getName() + "'";
+ }
+
+ @Override
+ public String getTypeName() {
+ return "SDK";
+ }
+
+ @Override
+ public String getId() {
+ return "sdk:" + mySdk.getName();
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/daemon/UsageInModuleClasspath.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/daemon/UsageInModuleClasspath.java
new file mode 100644
index 0000000..d2ee781
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/daemon/UsageInModuleClasspath.java
@@ -0,0 +1,103 @@
+package com.intellij.openapi.roots.ui.configuration.projectRoot.daemon;
+
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.module.ModuleType;
+import com.intellij.openapi.roots.DependencyScope;
+import com.intellij.openapi.roots.ModifiableRootModel;
+import com.intellij.openapi.roots.impl.OrderEntryUtil;
+import com.intellij.openapi.roots.ui.configuration.ModuleEditor;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.ModuleStructureConfigurable;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.StructureConfigurableContext;
+import com.intellij.openapi.util.text.StringUtil;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+
+/**
+ * @author nik
+ */
+public class UsageInModuleClasspath extends ProjectStructureElementUsage {
+ private final StructureConfigurableContext myContext;
+ private final ModuleProjectStructureElement myContainingElement;
+ @Nullable private final DependencyScope myScope;
+ private final ProjectStructureElement mySourceElement;
+ private final Module myModule;
+
+ public UsageInModuleClasspath(@NotNull StructureConfigurableContext context,
+ @NotNull ModuleProjectStructureElement containingElement,
+ ProjectStructureElement sourceElement,
+ @Nullable DependencyScope scope) {
+ myContext = context;
+ myContainingElement = containingElement;
+ myScope = scope;
+ myModule = containingElement.getModule();
+ mySourceElement = sourceElement;
+ }
+
+
+ @Override
+ public ProjectStructureElement getSourceElement() {
+ return mySourceElement;
+ }
+
+ @Override
+ public ModuleProjectStructureElement getContainingElement() {
+ return myContainingElement;
+ }
+
+ public Module getModule() {
+ return myModule;
+ }
+
+ @Override
+ public String getPresentableName() {
+ return myModule.getName();
+ }
+
+ @Override
+ public PlaceInProjectStructure getPlace() {
+ return new PlaceInModuleClasspath(myContext, myModule, myContainingElement, mySourceElement);
+ }
+
+ @Override
+ public int hashCode() {
+ return myModule.hashCode()*31 + mySourceElement.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return obj instanceof UsageInModuleClasspath && myModule.equals(((UsageInModuleClasspath)obj).myModule)
+ && mySourceElement.equals(((UsageInModuleClasspath)obj).mySourceElement);
+ }
+
+ @Override
+ public Icon getIcon() {
+ return ModuleType.get(myModule).getIcon();
+ }
+
+ @Override
+ public void removeSourceElement() {
+ if (mySourceElement instanceof LibraryProjectStructureElement) {
+ ModuleStructureConfigurable.getInstance(myModule.getProject())
+ .removeLibraryOrderEntry(myModule, ((LibraryProjectStructureElement)mySourceElement).getLibrary());
+ }
+ }
+
+ @Nullable
+ @Override
+ public String getPresentableLocationInElement() {
+ return myScope != null && myScope != DependencyScope.COMPILE ? "[" + StringUtil.decapitalize(myScope.getDisplayName()) + "]" : null;
+ }
+
+ @Override
+ public void replaceElement(final ProjectStructureElement newElement) {
+ final ModuleEditor editor = myContext.getModulesConfigurator().getModuleEditor(myModule);
+ if (editor != null) {
+ final ModifiableRootModel rootModel = editor.getModifiableRootModelProxy();
+ OrderEntryUtil.replaceLibrary(rootModel, ((LibraryProjectStructureElement)mySourceElement).getLibrary(),
+ ((LibraryProjectStructureElement)newElement).getLibrary());
+ myContext.getDaemonAnalyzer().queueUpdate(new ModuleProjectStructureElement(myContext, myModule));
+ }
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/util/HttpUrlCellAppearance.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/util/HttpUrlCellAppearance.java
new file mode 100644
index 0000000..8ab9551
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/util/HttpUrlCellAppearance.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2000-2011 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.util;
+
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.util.PlatformIcons;
+
+import javax.swing.*;
+
+public class HttpUrlCellAppearance extends ValidFileCellAppearance {
+ public HttpUrlCellAppearance(VirtualFile file) {
+ super(file);
+ }
+
+ @Override
+ protected Icon getIcon() {
+ return PlatformIcons.WEB_ICON;
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/util/JarSubfileCellAppearance.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/util/JarSubfileCellAppearance.java
new file mode 100644
index 0000000..ed4fd08
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/util/JarSubfileCellAppearance.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2000-2011 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.util;
+
+import com.intellij.openapi.fileTypes.FileTypes;
+import com.intellij.openapi.vfs.JarFileSystem;
+import com.intellij.openapi.vfs.VirtualFile;
+
+import javax.swing.*;
+
+public class JarSubfileCellAppearance extends ValidFileCellAppearance {
+ public JarSubfileCellAppearance(VirtualFile file) {
+ super(file);
+ }
+
+ @Override
+ protected Icon getIcon() {
+ return FileTypes.ARCHIVE.getIcon();
+ }
+
+ @Override
+ protected int getSplitUrlIndex(String url) {
+ int jarNameEnd = url.lastIndexOf(JarFileSystem.JAR_SEPARATOR.charAt(0));
+ String jarUrl = jarNameEnd >= 0 ? url.substring(0, jarNameEnd) : url;
+ return super.getSplitUrlIndex(jarUrl);
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/util/ValidFileCellAppearance.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/util/ValidFileCellAppearance.java
new file mode 100644
index 0000000..89c7cdc
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/util/ValidFileCellAppearance.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2000-2011 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.util;
+
+import com.intellij.openapi.fileTypes.FileTypeManager;
+import com.intellij.openapi.vfs.VirtualFile;
+
+import javax.swing.*;
+import java.io.File;
+
+public class ValidFileCellAppearance extends BaseTextCommentCellAppearance {
+ private final VirtualFile myFile;
+
+ public ValidFileCellAppearance(VirtualFile file) {
+ myFile = file;
+ }
+
+ @Override
+ protected Icon getIcon() {
+ return myFile.getFileType().getIcon();
+ }
+
+ @Override
+ protected String getSecondaryText() {
+ return getSubname(true);
+ }
+
+ @Override
+ protected String getPrimaryText() {
+ return getSubname(false);
+ }
+
+ private String getSubname(boolean headOrTail) {
+ String presentableUrl = myFile.getPresentableUrl();
+ int separatorIndex = getSplitUrlIndex(presentableUrl);
+ if (headOrTail)
+ return separatorIndex >= 0 ? presentableUrl.substring(0, separatorIndex) : "";
+ else
+ return presentableUrl.substring(separatorIndex + 1);
+ }
+
+ protected int getSplitUrlIndex(String presentableUrl) {
+ return presentableUrl.lastIndexOf(File.separatorChar);
+ }
+
+ public VirtualFile getFile() {
+ return myFile;
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/vcs/checkout/NewProjectCheckoutListener.java b/java/idea-ui/src/com/intellij/openapi/vcs/checkout/NewProjectCheckoutListener.java
new file mode 100644
index 0000000..a21ae77
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/vcs/checkout/NewProjectCheckoutListener.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2000-2009 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.vcs.checkout;
+
+import com.intellij.ide.actions.ImportModuleAction;
+import com.intellij.ide.util.newProjectWizard.AddModuleWizard;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.project.ProjectManager;
+import com.intellij.openapi.ui.Messages;
+import com.intellij.openapi.vcs.ProjectLevelVcsManager;
+import com.intellij.openapi.vcs.VcsBundle;
+import com.intellij.openapi.vcs.VcsDirectoryMapping;
+import com.intellij.openapi.vcs.VcsKey;
+import com.intellij.openapi.vfs.LocalFileSystem;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.projectImport.ProjectImportProvider;
+
+import java.io.File;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * @author yole
+ */
+public class NewProjectCheckoutListener implements VcsAwareCheckoutListener {
+ @Override
+ public boolean processCheckedOutDirectory(Project project, File directory, VcsKey vcsKey) {
+ int rc = Messages.showYesNoDialog(project, VcsBundle.message("checkout.create.project.prompt",
+ ProjectCheckoutListener.getProductNameWithArticle(),
+ directory.getAbsolutePath()),
+ VcsBundle.message("checkout.title"), Messages.getQuestionIcon());
+ if (rc == 0) {
+ final ProjectManager pm = ProjectManager.getInstance();
+ final Project[] projects = pm.getOpenProjects();
+ final Set<VirtualFile> files = projectsLocationSet(projects);
+ VirtualFile file = LocalFileSystem.getInstance().findFileByIoFile(directory);
+ AddModuleWizard wizard = ImportModuleAction.createImportWizard(null, null, file, ProjectImportProvider.PROJECT_IMPORT_PROVIDER.getExtensions());
+ if (wizard.showAndGet()) {
+ ImportModuleAction.createFromWizard(null, wizard);
+ }
+ final Project[] projectsAfter = pm.getOpenProjects();
+
+ for (Project project1 : projectsAfter) {
+ if (project1.getBaseDir() != null && ! files.contains(project1.getBaseDir())) {
+ final ProjectLevelVcsManager vcsManager = ProjectLevelVcsManager.getInstance(project1);
+ vcsManager.setDirectoryMappings(Collections.singletonList(new VcsDirectoryMapping("", vcsKey.getName())));
+ break;
+ }
+ }
+ return true;
+ }
+ return false;
+ }
+
+ private Set<VirtualFile> projectsLocationSet(Project[] projects) {
+ final Set<VirtualFile> files = new HashSet<VirtualFile>();
+ for (Project project1 : projects) {
+ if (project1.getBaseDir() != null) {
+ files.add(project1.getBaseDir());
+ }
+ }
+ return files;
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/vcs/checkout/ProjectCheckoutListener.java b/java/idea-ui/src/com/intellij/openapi/vcs/checkout/ProjectCheckoutListener.java
new file mode 100644
index 0000000..5c91519
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/vcs/checkout/ProjectCheckoutListener.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2000-2009 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.vcs.checkout;
+
+import com.intellij.ide.highlighter.ProjectFileType;
+import com.intellij.ide.impl.ProjectUtil;
+import com.intellij.openapi.application.ApplicationNamesInfo;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.Messages;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.openapi.vcs.VcsBundle;
+import com.intellij.util.PlatformUtils;
+import org.apache.oro.io.GlobFilenameFilter;
+
+import java.io.File;
+import java.io.FilenameFilter;
+
+/**
+ * @author yole
+ */
+public class ProjectCheckoutListener implements CheckoutListener {
+ @Override
+ public boolean processCheckedOutDirectory(Project project, File directory) {
+ File[] files = directory.listFiles((FilenameFilter) new GlobFilenameFilter("*" + ProjectFileType.DOT_DEFAULT_EXTENSION));
+ if (files != null && files.length > 0) {
+ int rc = Messages
+ .showYesNoDialog(project, VcsBundle.message("checkout.open.project.prompt", getProductNameWithArticle(), files[0].getPath()),
+ VcsBundle.message("checkout.title"), Messages.getQuestionIcon());
+ if (rc == 0) {
+ ProjectUtil.openProject(files[0].getPath(), project, false);
+ }
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public void processOpenedProject(Project lastOpenedProject) {
+ }
+
+ static String getProductNameWithArticle() {
+ final ApplicationNamesInfo namesInfo = ApplicationNamesInfo.getInstance();
+ // example: "to create an IntelliJ IDEA project" (full product name is ok);
+ // "to create a JetBrains Astella project" (better use not full product name: "to create an Astella project")
+ final String productName = PlatformUtils.isIdea() ? namesInfo.getFullProductName() : namesInfo.getProductName();
+ final String article = StringUtil.isVowel(Character.toLowerCase(productName.charAt(0))) ? "an " : "a ";
+ return article + productName;
+ }
+}
\ No newline at end of file
diff --git a/java/idea-ui/src/com/intellij/openapi/vcs/checkout/ProjectDirCheckoutListener.java b/java/idea-ui/src/com/intellij/openapi/vcs/checkout/ProjectDirCheckoutListener.java
new file mode 100644
index 0000000..4909d76
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/vcs/checkout/ProjectDirCheckoutListener.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2000-2012 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.vcs.checkout;
+
+import com.intellij.ide.impl.ProjectUtil;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.Messages;
+import com.intellij.openapi.vcs.VcsBundle;
+
+import java.io.File;
+
+/**
+ * @author irengrig
+ * @since 5/27/11
+ */
+public class ProjectDirCheckoutListener implements CheckoutListener {
+ @Override
+ public boolean processCheckedOutDirectory(Project project, File directory) {
+ if (new File(directory, Project.DIRECTORY_STORE_FOLDER).exists()) {
+ String message = VcsBundle.message("checkout.open.project.dir.prompt",
+ ProjectCheckoutListener.getProductNameWithArticle(), directory.getPath());
+ int rc = Messages.showYesNoDialog(project, message, VcsBundle.message("checkout.title"), Messages.getQuestionIcon());
+ if (rc == 0) {
+ ProjectUtil.openProject(directory.getPath(), project, false);
+ }
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public void processOpenedProject(Project lastOpenedProject) {
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/vcs/checkout/ProjectImporterCheckoutListener.java b/java/idea-ui/src/com/intellij/openapi/vcs/checkout/ProjectImporterCheckoutListener.java
new file mode 100644
index 0000000..f80b1e3
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/vcs/checkout/ProjectImporterCheckoutListener.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2000-2011 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.vcs.checkout;
+
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.Messages;
+import com.intellij.openapi.vcs.VcsBundle;
+import com.intellij.openapi.vfs.LocalFileSystem;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.projectImport.ProjectOpenProcessor;
+
+import java.io.File;
+
+public class ProjectImporterCheckoutListener implements CheckoutListener {
+ @Override
+ public boolean processCheckedOutDirectory(Project project, File directory) {
+ final File[] files = directory.listFiles();
+ if (files != null) {
+ final LocalFileSystem localFileSystem = LocalFileSystem.getInstance();
+ for (File file : files) {
+ if (file.isDirectory()) continue;
+ final VirtualFile virtualFile = localFileSystem.findFileByIoFile(file);
+ if (virtualFile != null) {
+ final ProjectOpenProcessor openProcessor = ProjectOpenProcessor.getImportProvider(virtualFile);
+ if (openProcessor != null) {
+ int rc = Messages
+ .showYesNoDialog(project, VcsBundle.message("checkout.open.project.prompt", ProjectCheckoutListener.getProductNameWithArticle(), files[0].getPath()),
+ VcsBundle.message("checkout.title"), Messages.getQuestionIcon());
+ if (rc == 0) {
+ openProcessor.doOpenProject(virtualFile, project, false);
+ }
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public void processOpenedProject(Project lastOpenedProject) {
+ }
+}
\ No newline at end of file
diff --git a/java/idea-ui/src/com/intellij/openapi/wm/IdeaFrameTitleBuilder.java b/java/idea-ui/src/com/intellij/openapi/wm/IdeaFrameTitleBuilder.java
new file mode 100644
index 0000000..6c705aa
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/wm/IdeaFrameTitleBuilder.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2000-2012 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.wm;
+
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.project.ProjectUtil;
+import com.intellij.openapi.util.SystemInfo;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.openapi.wm.impl.PlatformFrameTitleBuilder;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * @author yole
+ */
+public class IdeaFrameTitleBuilder extends PlatformFrameTitleBuilder {
+ @Override
+ public String getFileTitle(@NotNull final Project project, @NotNull final VirtualFile file) {
+ return ProjectUtil.calcRelativeToProjectPath(file, project, !SystemInfo.isMac, true, false);
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/wm/impl/welcomeScreen/DevelopPluginsAction.java b/java/idea-ui/src/com/intellij/openapi/wm/impl/welcomeScreen/DevelopPluginsAction.java
new file mode 100644
index 0000000..3902389
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/wm/impl/welcomeScreen/DevelopPluginsAction.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2000-2009 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.wm.impl.welcomeScreen;
+
+import com.intellij.ide.BrowserUtil;
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.application.PathManager;
+import com.intellij.openapi.project.DumbAware;
+import org.jetbrains.annotations.NonNls;
+
+import java.io.File;
+
+/**
+ * @author yole
+ */
+public class DevelopPluginsAction extends AnAction implements DumbAware {
+ @NonNls private static final String PLUGIN_URL = PathManager.getHomePath() + "/Plugin Development Readme.html";
+ @NonNls private static final String PLUGIN_WEBSITE = "http://www.jetbrains.com/idea/plugins/plugin_developers.html";
+
+ @Override
+ public void actionPerformed(final AnActionEvent e) {
+ try {
+ if (new File(PLUGIN_URL).isFile()) {
+ BrowserUtil.launchBrowser(PLUGIN_URL);
+ }
+ else {
+ BrowserUtil.launchBrowser(PLUGIN_WEBSITE);
+ }
+ }
+ catch(IllegalStateException ex) {
+ // ignore
+ }
+ }
+}
\ No newline at end of file
diff --git a/java/idea-ui/src/com/intellij/peer/impl/PeerFactoryImpl.java b/java/idea-ui/src/com/intellij/peer/impl/PeerFactoryImpl.java
new file mode 100644
index 0000000..487f1d1
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/peer/impl/PeerFactoryImpl.java
@@ -0,0 +1,262 @@
+/*
+ * Copyright 2000-2012 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.peer.impl;
+
+import com.intellij.ide.ui.SplitterProportionsDataImpl;
+import com.intellij.ide.util.PackageChooserDialog;
+import com.intellij.lang.ASTNode;
+import com.intellij.lang.Language;
+import com.intellij.lang.PsiBuilder;
+import com.intellij.lang.PsiBuilderFactory;
+import com.intellij.lexer.Lexer;
+import com.intellij.openapi.components.ServiceManager;
+import com.intellij.openapi.diff.DiffRequestFactory;
+import com.intellij.openapi.editor.Document;
+import com.intellij.openapi.editor.colors.EditorColorsScheme;
+import com.intellij.openapi.editor.highlighter.EditorHighlighter;
+import com.intellij.openapi.editor.highlighter.EditorHighlighterFactory;
+import com.intellij.openapi.fileChooser.FileSystemTreeFactory;
+import com.intellij.openapi.fileTypes.StdFileTypes;
+import com.intellij.openapi.fileTypes.SyntaxHighlighter;
+import com.intellij.openapi.module.ModuleConfigurationEditor;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.projectRoots.Sdk;
+import com.intellij.openapi.projectRoots.SdkType;
+import com.intellij.openapi.projectRoots.impl.ProjectJdkImpl;
+import com.intellij.openapi.roots.ui.configuration.JavaContentEntriesEditor;
+import com.intellij.openapi.roots.ui.configuration.ModuleConfigurationState;
+import com.intellij.openapi.ui.DialogWrapperPeerFactory;
+import com.intellij.openapi.ui.PackageChooser;
+import com.intellij.openapi.ui.SplitterProportionsData;
+import com.intellij.openapi.vcs.FileStatusFactory;
+import com.intellij.openapi.vcs.actions.VcsContextFactory;
+import com.intellij.peer.PeerFactory;
+import com.intellij.psi.*;
+import com.intellij.psi.search.scope.packageSet.PackageSetFactory;
+import com.intellij.psi.util.PsiFormatUtil;
+import com.intellij.psi.util.PsiFormatUtilBase;
+import com.intellij.ui.*;
+import com.intellij.ui.TextComponent;
+import com.intellij.ui.content.ContentFactory;
+import com.intellij.ui.errorView.ErrorViewFactory;
+import com.intellij.ui.treeStructure.treetable.TreeTable;
+import com.intellij.util.EditSourceOnDoubleClickHandler;
+import com.intellij.util.EditSourceOnEnterKeyHandler;
+import com.intellij.util.Function;
+import com.intellij.util.containers.Convertor;
+import com.intellij.util.ui.Table;
+import com.intellij.util.ui.UIUtil;
+import org.apache.xmlrpc.IdeaAwareWebServer;
+import org.apache.xmlrpc.IdeaAwareXmlRpcServer;
+import org.apache.xmlrpc.WebServer;
+import org.apache.xmlrpc.XmlRpcServer;
+
+import javax.swing.*;
+import javax.swing.table.TableCellRenderer;
+import javax.swing.tree.TreeCellRenderer;
+import javax.swing.tree.TreePath;
+import java.awt.*;
+import java.net.InetAddress;
+
+public class PeerFactoryImpl extends PeerFactory {
+ private final UIHelper myUIHelper = new MyUIHelper();
+
+ public FileStatusFactory getFileStatusFactory() {
+ return FileStatusFactory.getInstance();
+ }
+
+ public DialogWrapperPeerFactory getDialogWrapperPeerFactory() {
+ return DialogWrapperPeerFactory.getInstance();
+ }
+
+ public PackageSetFactory getPackageSetFactory() {
+ return PackageSetFactory.getInstance();
+ }
+
+ public UIHelper getUIHelper() {
+ return myUIHelper;
+ }
+
+ public ErrorViewFactory getErrorViewFactory() {
+ return ErrorViewFactory.SERVICE.getInstance();
+ }
+
+ public ContentFactory getContentFactory() {
+ return ServiceManager.getService(ContentFactory.class);
+ }
+
+ public FileSystemTreeFactory getFileSystemTreeFactory() {
+ return FileSystemTreeFactory.SERVICE.getInstance();
+ }
+
+ public DiffRequestFactory getDiffRequestFactory() {
+ return DiffRequestFactory.getInstance();
+ }
+
+ private static class MyUIHelper implements UIHelper {
+ public void installToolTipHandler(JTree tree) {
+ TreeUIHelper.getInstance().installToolTipHandler(tree);
+ }
+
+ public void installToolTipHandler(JTable table) {
+ TreeUIHelper.getInstance().installToolTipHandler(table);
+ }
+
+ public void installEditSourceOnDoubleClick(JTree tree) {
+ EditSourceOnDoubleClickHandler.install(tree);
+ }
+
+ public void installEditSourceOnDoubleClick(TreeTable tree) {
+ EditSourceOnDoubleClickHandler.install(tree);
+ }
+
+ public void installEditSourceOnDoubleClick(Table table) {
+ EditSourceOnDoubleClickHandler.install(table);
+ }
+
+ public void installTreeTableSpeedSearch(TreeTable treeTable) {
+ new TreeTableSpeedSearch(treeTable);
+ }
+
+ public void installTreeTableSpeedSearch(final TreeTable treeTable, final Convertor<TreePath, String> convertor) {
+ new TreeTableSpeedSearch(treeTable, convertor);
+ }
+
+ public void installTreeSpeedSearch(JTree tree) {
+ new TreeSpeedSearch(tree);
+ }
+
+ public void installTreeSpeedSearch(final JTree tree, final Convertor<TreePath, String> convertor) {
+ new TreeSpeedSearch(tree, convertor);
+ }
+
+ public void installListSpeedSearch(JList list) {
+ new ListSpeedSearch(list);
+ }
+
+ public void installListSpeedSearch(final JList list, final Function<Object, String> elementTextDelegate) {
+ new ListSpeedSearch(list, elementTextDelegate);
+ }
+
+ public void installEditSourceOnEnterKeyHandler(JTree tree) {
+ EditSourceOnEnterKeyHandler.install(tree);
+ }
+
+ public SplitterProportionsData createSplitterProportionsData() {
+ return new SplitterProportionsDataImpl();
+ }
+
+ public TableCellRenderer createPsiElementRenderer(final PsiElement psiElement, final Project project) {
+ return new ColoredTableCellRenderer() {
+ protected void customizeCellRenderer(JTable table, Object value, boolean selected, boolean hasFocus, int row, int column) {
+ append(getPsiElementText(psiElement), SimpleTextAttributes.REGULAR_ATTRIBUTES);
+ setIcon(psiElement.getIcon(0));
+ }
+ };
+
+ }
+
+ public TreeCellRenderer createHighlightableTreeCellRenderer() {
+ return new HighlightableCellRenderer();
+ }
+
+ public void drawDottedRectangle(Graphics g, int x, int y, int i, int i1) {
+ UIUtil.drawDottedRectangle(g,x,y,i,i1);
+ }
+
+ public void installSmartExpander(JTree tree) {
+ SmartExpander.installOn(tree);
+ }
+
+ public void installSelectionSaver(JTree tree) {
+ SelectionSaver.installOn(tree);
+ }
+
+ public TextComponent createTypedTextField(final String text, PsiType type, PsiElement context, final Project project) {
+ final PsiExpressionCodeFragment fragment =
+ JavaCodeFragmentFactory.getInstance(project).createExpressionCodeFragment(text, context, type, true);
+ final Document document = PsiDocumentManager.getInstance(project).getDocument(fragment);
+ return new EditorTextField(document, project, StdFileTypes.JAVA);
+ }
+
+ public PackageChooser createPackageChooser(String title, Project project) {
+ return new PackageChooserDialog(title, project);
+ }
+
+ private static String getPsiElementText(PsiElement psiElement) {
+ if (psiElement instanceof PsiClass) {
+ return PsiFormatUtil.formatClass((PsiClass)psiElement, PsiFormatUtilBase.SHOW_NAME |
+ PsiFormatUtilBase.SHOW_FQ_NAME);
+ }
+ else if (psiElement instanceof PsiMethod) {
+ return PsiFormatUtil.formatMethod((PsiMethod)psiElement,
+ PsiSubstitutor.EMPTY,
+ PsiFormatUtilBase.SHOW_NAME |
+ PsiFormatUtilBase.SHOW_PARAMETERS |
+ PsiFormatUtilBase.SHOW_CONTAINING_CLASS,
+ 0);
+ }
+ else if (psiElement instanceof PsiField) {
+ return PsiFormatUtil.formatVariable((PsiField)psiElement,
+ PsiFormatUtilBase.SHOW_NAME |
+ PsiFormatUtilBase.SHOW_TYPE |
+ PsiFormatUtilBase.SHOW_CONTAINING_CLASS,
+ PsiSubstitutor.EMPTY);
+ }
+ else {
+ return psiElement.toString();
+ }
+
+ }
+
+ }
+
+ public VcsContextFactory getVcsContextFactory() {
+ return VcsContextFactory.SERVICE.getInstance();
+ }
+
+ public PsiBuilder createBuilder(ASTNode tree, Language lang, CharSequence seq, final Project project) {
+ return PsiBuilderFactory.getInstance().createBuilder(project, tree, null, lang, seq);
+ }
+
+ public PsiBuilder createBuilder(final ASTNode tree, final Lexer lexer, final Language lang, final CharSequence seq, final Project project) {
+ return PsiBuilderFactory.getInstance().createBuilder(project, tree, lexer, lang, seq);
+ }
+
+ public XmlRpcServer createRpcServer() {
+ return new IdeaAwareXmlRpcServer();
+ }
+
+ public WebServer createWebServer(final int port, final InetAddress addr, final XmlRpcServer xmlrpc) {
+ return new IdeaAwareWebServer(port, addr, xmlrpc);
+ }
+
+ public EditorHighlighter createEditorHighlighter(final SyntaxHighlighter syntaxHighlighter, final EditorColorsScheme colors) {
+ return EditorHighlighterFactory.getInstance().createEditorHighlighter(syntaxHighlighter, colors);
+ }
+
+ public Sdk createProjectJdk(final String name, final String version, final String homePath, final SdkType sdkType) {
+ final ProjectJdkImpl projectJdk = new ProjectJdkImpl(name, sdkType);
+ projectJdk.setHomePath(homePath);
+ projectJdk.setVersionString(version);
+ return projectJdk;
+ }
+
+ public ModuleConfigurationEditor createModuleConfigurationEditor(final String moduleName, ModuleConfigurationState state) {
+ return new JavaContentEntriesEditor(moduleName, state);
+ }
+
+}
\ No newline at end of file
diff --git a/java/idea-ui/src/com/intellij/platform/templates/ArchivedProjectTemplate.java b/java/idea-ui/src/com/intellij/platform/templates/ArchivedProjectTemplate.java
new file mode 100644
index 0000000..4e04bae
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/platform/templates/ArchivedProjectTemplate.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2000-2012 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.platform.templates;
+
+import com.intellij.ide.util.projectWizard.ModuleBuilder;
+import com.intellij.openapi.module.ModuleType;
+import com.intellij.openapi.ui.ValidationInfo;
+import com.intellij.platform.ProjectTemplate;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.io.IOException;
+import java.util.zip.ZipInputStream;
+
+/**
+ * @author Dmitry Avdeev
+ * Date: 11/14/12
+ */
+public abstract class ArchivedProjectTemplate implements ProjectTemplate {
+
+ protected final String myDisplayName;
+
+ public ArchivedProjectTemplate(String displayName) {
+ myDisplayName = displayName;
+ }
+
+ @NotNull
+ @Override
+ public String getName() {
+ return myDisplayName;
+ }
+
+ protected abstract ModuleType getModuleType();
+
+ @NotNull
+ @Override
+ public ModuleBuilder createModuleBuilder() {
+ return new TemplateModuleBuilder(this, getModuleType());
+ }
+
+ @Nullable
+ @Override
+ public ValidationInfo validateSettings() {
+ return null;
+ }
+
+ public abstract ZipInputStream getStream() throws IOException;
+}
diff --git a/java/idea-ui/src/com/intellij/platform/templates/ArchivedTemplatesFactory.java b/java/idea-ui/src/com/intellij/platform/templates/ArchivedTemplatesFactory.java
new file mode 100644
index 0000000..dfc8a1f
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/platform/templates/ArchivedTemplatesFactory.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright 2000-2012 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.platform.templates;
+
+import com.intellij.ide.fileTemplates.impl.UrlUtil;
+import com.intellij.ide.plugins.IdeaPluginDescriptor;
+import com.intellij.ide.plugins.PluginManager;
+import com.intellij.ide.util.projectWizard.WizardContext;
+import com.intellij.openapi.application.PathManager;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.util.ClearableLazyValue;
+import com.intellij.platform.ProjectTemplate;
+import com.intellij.platform.ProjectTemplatesFactory;
+import com.intellij.util.ArrayUtil;
+import com.intellij.util.containers.ContainerUtil;
+import com.intellij.util.containers.MultiMap;
+import org.jetbrains.annotations.NotNull;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.*;
+
+/**
+ * @author Dmitry Avdeev
+ * @since 10/1/12
+ */
+public class ArchivedTemplatesFactory extends ProjectTemplatesFactory {
+
+ private static final String ZIP = ".zip";
+
+ private final ClearableLazyValue<MultiMap<String, URL>> myGroups = new ClearableLazyValue<MultiMap<String, URL>>() {
+ @NotNull
+ @Override
+ protected MultiMap<String, URL> compute() {
+ MultiMap<String, URL> map = new MultiMap<String, URL>();
+ IdeaPluginDescriptor[] plugins = PluginManager.getPlugins();
+ Set<URL> urls = new HashSet<URL>();
+ for (IdeaPluginDescriptor plugin : plugins) {
+ try {
+ Enumeration<URL> resources = plugin.getPluginClassLoader().getResources("resources/projectTemplates");
+ urls.addAll(Collections.list(resources));
+ }
+ catch (IOException e) {
+ LOG.error(e);
+ }
+ }
+
+ URL configURL = getCustomTemplatesURL();
+ ContainerUtil.addIfNotNull(urls, configURL);
+
+ for (URL url : urls) {
+ try {
+ List<String> children = UrlUtil.getChildrenRelativePaths(url);
+ if (configURL == url && !children.isEmpty()) {
+ map.putValue(CUSTOM_GROUP, url);
+ continue;
+ }
+
+ for (String child : children) {
+ int index = child.indexOf('/');
+ if (index != -1) {
+ child = child.substring(0, index);
+ }
+ map.putValue(child.replace('_', ' '), new URL(url.toExternalForm() + "/" + child));
+ }
+ }
+ catch (IOException e) {
+ LOG.error(e);
+ }
+ }
+ return map;
+ }
+ };
+
+ private static URL getCustomTemplatesURL() {
+ String path = getCustomTemplatesPath();
+ try {
+ return new File(path).toURI().toURL();
+ }
+ catch (MalformedURLException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ static String getCustomTemplatesPath() {
+ return PathManager.getConfigPath() + "/projectTemplates";
+ }
+
+ public static File getTemplateFile(String name) {
+ String configURL = getCustomTemplatesPath();
+ return new File(configURL + "/" + name + ".zip");
+ }
+
+ @NotNull
+ @Override
+ public String[] getGroups() {
+ myGroups.drop();
+ Set<String> groups = myGroups.getValue().keySet();
+ return ArrayUtil.toStringArray(groups);
+ }
+
+ @NotNull
+ @Override
+ public ProjectTemplate[] createTemplates(String group, WizardContext context) {
+ Collection<URL> urls = myGroups.getValue().get(group);
+ List<ProjectTemplate> templates = new ArrayList<ProjectTemplate>();
+ for (URL url : urls) {
+ try {
+ List<String> children = UrlUtil.getChildrenRelativePaths(url);
+ for (String child : children) {
+ if (child.endsWith(ZIP)) {
+ URL templateUrl = new URL(url.toExternalForm() + "/" + child);
+ String name = child.substring(0, child.length() - ZIP.length()).replace('_', ' ');
+ templates.add(new LocalArchivedTemplate(name, templateUrl));
+ }
+ }
+ }
+ catch (IOException e) {
+ LOG.error(e);
+ }
+ }
+ return templates.toArray(new ProjectTemplate[templates.size()]);
+ }
+
+ private final static Logger LOG = Logger.getInstance(ArchivedTemplatesFactory.class);
+}
diff --git a/java/idea-ui/src/com/intellij/platform/templates/BuilderBasedTemplate.java b/java/idea-ui/src/com/intellij/platform/templates/BuilderBasedTemplate.java
new file mode 100644
index 0000000..009159c
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/platform/templates/BuilderBasedTemplate.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2000-2012 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.platform.templates;
+
+import com.intellij.ide.util.projectWizard.ModuleBuilder;
+import com.intellij.openapi.ui.ValidationInfo;
+import com.intellij.platform.ProjectTemplate;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+* @author Dmitry Avdeev
+* Date: 11/9/12
+*/
+public class BuilderBasedTemplate implements ProjectTemplate {
+ private final ModuleBuilder myBuilder;
+
+ public BuilderBasedTemplate(ModuleBuilder builder) {
+ myBuilder = builder;
+ }
+
+ @NotNull
+ @Override
+ public String getName() {
+ return myBuilder.getPresentableName();
+ }
+
+ @Nullable
+ @Override
+ public String getDescription() {
+ return myBuilder.getDescription();
+ }
+
+ @NotNull
+ @Override
+ public ModuleBuilder createModuleBuilder() {
+ return myBuilder;
+ }
+
+ @Nullable
+ @Override
+ public ValidationInfo validateSettings() {
+ return null;
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/platform/templates/IC_templates.xml b/java/idea-ui/src/com/intellij/platform/templates/IC_templates.xml
new file mode 100644
index 0000000..8da6b26
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/platform/templates/IC_templates.xml
@@ -0,0 +1,8 @@
+<templates>
+ <template>
+ <name>Java Hello World</name>
+ <description><![CDATA[ Simple Java "Hello World" application. ]]>
+ </description>
+ <path>HelloWorld.zip</path>
+ </template>
+</templates>
diff --git a/java/idea-ui/src/com/intellij/platform/templates/LocalArchivedTemplate.java b/java/idea-ui/src/com/intellij/platform/templates/LocalArchivedTemplate.java
new file mode 100644
index 0000000..76c70ed
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/platform/templates/LocalArchivedTemplate.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright 2000-2012 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.platform.templates;
+
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.module.ModuleType;
+import com.intellij.openapi.module.ModuleTypeManager;
+import com.intellij.openapi.util.Condition;
+import com.intellij.openapi.util.JDOMUtil;
+import com.intellij.openapi.util.io.StreamUtil;
+import org.jdom.Document;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.io.IOException;
+import java.net.URL;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
+
+/**
+ * @author Dmitry Avdeev
+ * Date: 10/1/12
+ */
+public class LocalArchivedTemplate extends ArchivedProjectTemplate {
+
+ static final String DESCRIPTION_PATH = ".idea/description.html";
+
+ private final URL myArchivePath;
+ private final ModuleType myModuleType;
+
+ public LocalArchivedTemplate(String displayName,
+ URL archivePath) {
+ super(displayName);
+
+ myArchivePath = archivePath;
+ myModuleType = computeModuleType(this);
+ }
+
+ @Override
+ public String getDescription() {
+ return readEntry(new Condition<ZipEntry>() {
+ @Override
+ public boolean value(ZipEntry entry) {
+ return entry.getName().endsWith(DESCRIPTION_PATH);
+ }
+ });
+ }
+
+ @Nullable
+ String readEntry(Condition<ZipEntry> condition) {
+ ZipInputStream stream = null;
+ try {
+ stream = getStream();
+ ZipEntry entry;
+ while ((entry = stream.getNextEntry()) != null) {
+ if (condition.value(entry)) {
+ return StreamUtil.readText(stream);
+ }
+ }
+ }
+ catch (IOException e) {
+ return null;
+ }
+ finally {
+ StreamUtil.closeStream(stream);
+ }
+ return null;
+ }
+
+ @NotNull
+ private static ModuleType computeModuleType(LocalArchivedTemplate template) {
+ String iml = template.readEntry(new Condition<ZipEntry>() {
+ @Override
+ public boolean value(ZipEntry entry) {
+ return entry.getName().endsWith(".iml");
+ }
+ });
+ if (iml == null) return ModuleType.EMPTY;
+ try {
+ Document document = JDOMUtil.loadDocument(iml);
+ String type = document.getRootElement().getAttributeValue(Module.ELEMENT_TYPE);
+ return ModuleTypeManager.getInstance().findByID(type);
+ }
+ catch (Exception e) {
+ return ModuleType.EMPTY;
+ }
+ }
+
+ @Override
+ protected ModuleType getModuleType() {
+ return myModuleType;
+ }
+
+ @Override
+ public ZipInputStream getStream() throws IOException {
+ return new ZipInputStream(myArchivePath.openStream());
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/platform/templates/ManageProjectTemplatesAction.java b/java/idea-ui/src/com/intellij/platform/templates/ManageProjectTemplatesAction.java
new file mode 100644
index 0000000..1327787
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/platform/templates/ManageProjectTemplatesAction.java
@@ -0,0 +1,15 @@
+package com.intellij.platform.templates;
+
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+
+/**
+ * @author Dmitry Avdeev
+ * Date: 11/13/12
+ */
+public class ManageProjectTemplatesAction extends AnAction {
+ @Override
+ public void actionPerformed(AnActionEvent e) {
+ new ManageProjectTemplatesDialog().show();
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/platform/templates/ManageProjectTemplatesDialog.java b/java/idea-ui/src/com/intellij/platform/templates/ManageProjectTemplatesDialog.java
new file mode 100644
index 0000000..b122ebb
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/platform/templates/ManageProjectTemplatesDialog.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright 2000-2012 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.platform.templates;
+
+import com.intellij.CommonBundle;
+import com.intellij.ide.util.projectWizard.WizardContext;
+import com.intellij.openapi.ui.DialogWrapper;
+import com.intellij.openapi.ui.Messages;
+import com.intellij.platform.ProjectTemplate;
+import com.intellij.platform.ProjectTemplatesFactory;
+import com.intellij.ui.*;
+import com.intellij.ui.components.JBList;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import javax.swing.event.ListDataEvent;
+import javax.swing.event.ListSelectionEvent;
+import javax.swing.event.ListSelectionListener;
+import java.awt.*;
+import java.awt.event.ActionEvent;
+import java.util.Arrays;
+
+/**
+ * @author Dmitry Avdeev
+ * Date: 11/13/12
+ */
+class ManageProjectTemplatesDialog extends DialogWrapper {
+
+ private final JPanel myPanel;
+ private final JBList myTemplatesList;
+ private final JTextPane myDescriptionPane;
+
+ ManageProjectTemplatesDialog() {
+ super(false);
+ setTitle("Manage Project Templates");
+ final ProjectTemplate[] templates =
+ new ArchivedTemplatesFactory().createTemplates(ProjectTemplatesFactory.CUSTOM_GROUP, new WizardContext(null));
+ final CollectionListModel<ProjectTemplate> model = new CollectionListModel<ProjectTemplate>(Arrays.asList(templates));
+ myTemplatesList = new JBList(model);
+ myTemplatesList.setEmptyText("No user-defined project templates");
+ myTemplatesList.setPreferredSize(new Dimension(300, 100));
+ myTemplatesList.setCellRenderer(new ColoredListCellRenderer() {
+ @Override
+ protected void customizeCellRenderer(JList list, Object value, int index, boolean selected, boolean hasFocus) {
+ append(((ProjectTemplate)value).getName());
+ }
+ });
+ myTemplatesList.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
+ @Override
+ public void valueChanged(ListSelectionEvent e) {
+ ProjectTemplate template = getSelectedTemplate();
+ myDescriptionPane.setText(template == null ? null : template.getDescription());
+ }
+ });
+ model.addListDataListener(new ListDataAdapter() {
+ @Override
+ public void intervalRemoved(ListDataEvent e) {
+ ArchivedTemplatesFactory.getTemplateFile(templates[e.getIndex0()].getName()).delete();
+ }
+ });
+
+ myPanel = new JPanel(new BorderLayout(0, 5));
+ myPanel.add(ToolbarDecorator.createDecorator(myTemplatesList).disableUpDownActions().createPanel());
+
+ myDescriptionPane = new JTextPane();
+ myDescriptionPane.setPreferredSize(new Dimension(300, 50));
+ Messages.installHyperlinkSupport(myDescriptionPane);
+ myPanel.add(ScrollPaneFactory.createScrollPane(myDescriptionPane, ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS,
+ ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED), BorderLayout.SOUTH);
+
+ if (templates.length > 0) {
+ myTemplatesList.setSelectedValue(templates[0], true);
+ }
+
+ init();
+ }
+
+ @Nullable
+ private ProjectTemplate getSelectedTemplate() {
+ return (ProjectTemplate)myTemplatesList.getSelectedValue();
+ }
+
+ @NotNull
+ @Override
+ protected Action[] createActions() {
+ return new Action[]{ new DialogWrapperAction(CommonBundle.getCloseButtonText()) {
+ @Override
+ protected void doAction(ActionEvent e) {
+ doCancelAction();
+ }
+ }};
+ }
+
+ @Nullable
+ @Override
+ protected JComponent createCenterPanel() {
+ return myPanel;
+ }
+
+ @Nullable
+ @Override
+ public JComponent getPreferredFocusedComponent() {
+ return myTemplatesList;
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/platform/templates/PlainModuleTemplatesFactory.java b/java/idea-ui/src/com/intellij/platform/templates/PlainModuleTemplatesFactory.java
new file mode 100644
index 0000000..c801325
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/platform/templates/PlainModuleTemplatesFactory.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2000-2012 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.platform.templates;
+
+import com.intellij.ide.util.projectWizard.EmptyModuleBuilder;
+import com.intellij.ide.util.projectWizard.ModuleBuilder;
+import com.intellij.ide.util.projectWizard.WizardContext;
+import com.intellij.platform.ProjectTemplate;
+import com.intellij.platform.ProjectTemplatesFactory;
+import com.intellij.util.ArrayUtil;
+import com.intellij.util.Function;
+import com.intellij.util.NullableFunction;
+import com.intellij.util.containers.ContainerUtil;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * @author Dmitry Avdeev
+ * Date: 10/9/12
+ */
+public class PlainModuleTemplatesFactory extends ProjectTemplatesFactory {
+
+ @NotNull
+ @Override
+ public String[] getGroups() {
+ List<ModuleBuilder> builders = ModuleBuilder.getAllBuilders();
+ Set<String> groups = ContainerUtil.map2Set(builders, new Function<ModuleBuilder, String>() {
+ @Override
+ public String fun(ModuleBuilder builder) {
+ return builder.getGroupName();
+ }
+ });
+ groups.add(OTHER_GROUP);
+ return ArrayUtil.toStringArray(groups);
+ }
+
+ @NotNull
+ @Override
+ public ProjectTemplate[] createTemplates(final String group, WizardContext context) {
+ if (OTHER_GROUP.equals(group)) {
+ if (!context.isCreatingNewProject()) {
+ return ProjectTemplate.EMPTY_ARRAY;
+ }
+ return new ProjectTemplate[]{new BuilderBasedTemplate(new EmptyModuleBuilder() {
+ @Override
+ public String getPresentableName() {
+ return "Empty Project";
+ }
+
+ @Override
+ public String getDescription() {
+ return "Empty project without modules. Use it to create free-style module structure.";
+ }
+ })};
+ }
+ ModuleBuilder[] builders = context.getAllBuilders();
+ return ContainerUtil.mapNotNull(builders, new NullableFunction<ModuleBuilder, ProjectTemplate>() {
+ @Nullable
+ @Override
+ public ProjectTemplate fun(ModuleBuilder builder) {
+ return builder.getGroupName().equals(group) ? new BuilderBasedTemplate(builder) : null;
+ }
+ }, ProjectTemplate.EMPTY_ARRAY);
+ }
+
+ @Override
+ public Icon getGroupIcon(String group) {
+ List<ModuleBuilder> builders = ModuleBuilder.getAllBuilders();
+ for (ModuleBuilder builder : builders) {
+ if (group.equals(builder.getGroupName())) {
+ return builder.getNodeIcon();
+ }
+ }
+ return null;
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/platform/templates/RemoteTemplatesFactory.java b/java/idea-ui/src/com/intellij/platform/templates/RemoteTemplatesFactory.java
new file mode 100644
index 0000000..33b6d84
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/platform/templates/RemoteTemplatesFactory.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright 2000-2012 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.platform.templates;
+
+import com.intellij.ide.plugins.PluginManager;
+import com.intellij.ide.util.projectWizard.WizardContext;
+import com.intellij.openapi.application.ApplicationInfo;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.extensions.PluginId;
+import com.intellij.openapi.module.ModuleType;
+import com.intellij.openapi.module.ModuleTypeManager;
+import com.intellij.openapi.util.JDOMUtil;
+import com.intellij.openapi.util.io.StreamUtil;
+import com.intellij.platform.ProjectTemplate;
+import com.intellij.platform.ProjectTemplatesFactory;
+import com.intellij.util.NullableFunction;
+import com.intellij.util.containers.ContainerUtil;
+import com.intellij.util.net.HttpConfigurable;
+import org.jdom.Element;
+import org.jdom.JDOMException;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.HttpURLConnection;
+import java.util.List;
+import java.util.zip.ZipInputStream;
+
+/**
+ * @author Dmitry Avdeev
+ * Date: 11/14/12
+ */
+public class RemoteTemplatesFactory extends ProjectTemplatesFactory {
+
+ private static final String URL = "http://download.jetbrains.com/idea/project_templates/";
+
+ @NotNull
+ @Override
+ public String[] getGroups() {
+ return new String[] { "Samples Gallery"};
+ }
+
+ @NotNull
+ @Override
+ public ProjectTemplate[] createTemplates(String group, WizardContext context) {
+ InputStream stream = null;
+ HttpURLConnection connection = null;
+ String code = ApplicationInfo.getInstance().getBuild().getProductCode();
+ try {
+ connection = getConnection(code + "_templates.xml");
+ stream = connection.getInputStream();
+ String text = StreamUtil.readText(stream);
+ return createFromText(text);
+ }
+ catch (IOException ex) { // timeouts, lost connection etc
+ LOG.info(ex);
+ return ProjectTemplate.EMPTY_ARRAY;
+ }
+ catch (Exception e) {
+ LOG.error(e);
+ return ProjectTemplate.EMPTY_ARRAY;
+ }
+ finally {
+ StreamUtil.closeStream(stream);
+ if (connection != null) {
+ connection.disconnect();
+ }
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ public static ProjectTemplate[] createFromText(String text) throws IOException, JDOMException {
+
+ List<Element> elements = JDOMUtil.loadDocument(text).getRootElement().getChildren("template");
+
+ List<ProjectTemplate> templates = ContainerUtil.mapNotNull(elements, new NullableFunction<Element, ProjectTemplate>() {
+ @Override
+ public ProjectTemplate fun(final Element element) {
+
+ List<Element> plugins = element.getChildren("requiredPlugin");
+ for (Element plugin : plugins) {
+ String id = plugin.getTextTrim();
+ if (!PluginManager.isPluginInstalled(PluginId.getId(id))) {
+ return null;
+ }
+ }
+ String type = element.getChildText("moduleType");
+ final ModuleType moduleType = ModuleTypeManager.getInstance().findByID(type);
+ return new ArchivedProjectTemplate(element.getChildTextTrim("name")) {
+ @Override
+ protected ModuleType getModuleType() {
+ return moduleType;
+ }
+
+ @Override
+ public ZipInputStream getStream() throws IOException {
+ String path = element.getChildText("path");
+ final HttpURLConnection connection = getConnection(path);
+ return new ZipInputStream(connection.getInputStream()) {
+ @Override
+ public void close() throws IOException {
+ super.close();
+ connection.disconnect();
+ }
+ };
+ }
+
+ @Nullable
+ @Override
+ public String getDescription() {
+ return element.getChildTextTrim("description");
+ }
+ };
+ }
+ });
+ return templates.toArray(new ProjectTemplate[templates.size()]);
+ }
+
+ private static HttpURLConnection getConnection(String path) throws IOException {
+ HttpURLConnection connection = HttpConfigurable.getInstance().openHttpConnection(URL + path);
+ connection.setConnectTimeout(2000);
+ connection.setReadTimeout(2000);
+ connection.connect();
+ return connection;
+ }
+
+ private final static Logger LOG = Logger.getInstance(RemoteTemplatesFactory.class);
+}
diff --git a/java/idea-ui/src/com/intellij/platform/templates/SaveProjectAsTemplateAction.java b/java/idea-ui/src/com/intellij/platform/templates/SaveProjectAsTemplateAction.java
new file mode 100644
index 0000000..f8d89ac
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/platform/templates/SaveProjectAsTemplateAction.java
@@ -0,0 +1,203 @@
+/*
+ * Copyright 2000-2012 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.platform.templates;
+
+import com.intellij.CommonBundle;
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.components.StorageScheme;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.progress.PerformInBackgroundOption;
+import com.intellij.openapi.progress.ProgressIndicator;
+import com.intellij.openapi.progress.ProgressManager;
+import com.intellij.openapi.progress.Task;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.project.ex.ProjectEx;
+import com.intellij.openapi.roots.ContentIterator;
+import com.intellij.openapi.roots.FileIndex;
+import com.intellij.openapi.roots.ModuleRootManager;
+import com.intellij.openapi.roots.ProjectRootManager;
+import com.intellij.openapi.ui.Messages;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.util.io.StreamUtil;
+import com.intellij.openapi.vfs.VfsUtil;
+import com.intellij.openapi.vfs.VfsUtilCore;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.util.io.ZipUtil;
+import com.intellij.util.ui.UIUtil;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipOutputStream;
+
+/**
+ * @author Dmitry Avdeev
+ * Date: 10/5/12
+ */
+public class SaveProjectAsTemplateAction extends AnAction {
+
+ private static final Logger LOG = Logger.getInstance(SaveProjectAsTemplateAction.class);
+
+ @Override
+ public void actionPerformed(AnActionEvent e) {
+ final Project project = getEventProject(e);
+ assert project != null;
+ StorageScheme scheme = ((ProjectEx)project).getStateStore().getStorageScheme();
+ if (scheme != StorageScheme.DIRECTORY_BASED) {
+ Messages.showErrorDialog(project, "Project templates do not support old .ipr (file-based) format.\n" +
+ "Please convert your project via File->Save as Directory-Based format.", CommonBundle.getErrorTitle());
+ return;
+ }
+
+ final VirtualFile descriptionFile = getDescriptionFile(project);
+ final SaveProjectAsTemplateDialog dialog = new SaveProjectAsTemplateDialog(project, descriptionFile);
+
+ if (dialog.showAndGet()) {
+
+ final Module moduleToSave = dialog.getModuleToSave();
+ final File file = dialog.getTemplateFile();
+ final String description = dialog.getDescription();
+
+ ProgressManager.getInstance().run(new Task.Backgroundable(project, "Saving Project as Template", true, PerformInBackgroundOption.DEAF) {
+ @Override
+ public void run(@NotNull final ProgressIndicator indicator) {
+ saveProject(project, file, moduleToSave, description, indicator);
+ }
+
+ @Override
+ public void onSuccess() {
+ Messages.showInfoMessage(FileUtil.getNameWithoutExtension(file) + " was successfully created.\n" +
+ "It's available now in Project Wizard", "Template Created");
+ }
+
+ @Override
+ public void onCancel() {
+ file.delete();
+ }
+ });
+ }
+ }
+
+ public static VirtualFile getDescriptionFile(Project project) {
+ return VfsUtil.findRelativeFile(LocalArchivedTemplate.DESCRIPTION_PATH, project.getBaseDir());
+ }
+
+ public static void saveProject(final Project project,
+ final File zipFile,
+ Module moduleToSave,
+ final String description,
+ final ProgressIndicator indicator) {
+
+ indicator.setText("Saving project...");
+ UIUtil.invokeAndWaitIfNeeded(new Runnable() {
+ @Override
+ public void run() {
+ ApplicationManager.getApplication().runWriteAction(new Runnable() {
+ public void run() {
+ project.save();
+ }
+ });
+ }
+ });
+ indicator.setText("Processing project files...");
+ ZipOutputStream stream = null;
+ try {
+ FileUtil.ensureExists(zipFile.getParentFile());
+ stream = new ZipOutputStream(new FileOutputStream(zipFile));
+
+ final VirtualFile dir = getDirectoryToSave(project, moduleToSave);
+ final VirtualFile descriptionFile = getDescriptionFile(project);
+ if (descriptionFile == null) {
+ stream.putNextEntry(new ZipEntry(dir.getName() + "/" + LocalArchivedTemplate.DESCRIPTION_PATH));
+ stream.write(description.getBytes());
+ stream.closeEntry();
+ }
+ else {
+ UIUtil.invokeAndWaitIfNeeded(new Runnable() {
+ public void run() {
+ try {
+ VfsUtil.saveText(descriptionFile, description);
+ }
+ catch (IOException e) {
+ LOG.error(e);
+ }
+ }
+ });
+ }
+
+ FileIndex index = moduleToSave == null
+ ? ProjectRootManager.getInstance(project).getFileIndex()
+ : ModuleRootManager.getInstance(moduleToSave).getFileIndex();
+ final ZipOutputStream finalStream = stream;
+ index.iterateContent(new ContentIterator() {
+ @Override
+ public boolean processFile(VirtualFile file) {
+ if (!file.isDirectory()) {
+ indicator.setText2(file.getName());
+ try {
+ String relativePath = VfsUtilCore.getRelativePath(file, dir, '/');
+ if (relativePath == null) {
+ throw new RuntimeException("Can't find relative path for " + file);
+ }
+ ZipUtil.addFileToZip(finalStream, new File(file.getPath()), dir.getName() + "/" + relativePath, null, null);
+ }
+ catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ indicator.checkCanceled();
+ // if (!".idea".equals(fileName.getParent())) return true;
+ // todo filter out some garbage from .idea
+ return true;
+ }
+ });
+ }
+ catch (Exception ex) {
+ LOG.error(ex);
+ UIUtil.invokeLaterIfNeeded(new Runnable() {
+ public void run() {
+ Messages.showErrorDialog(project, "Can't save project as template", "Internal Error");
+ }
+ });
+ }
+ finally {
+ StreamUtil.closeStream(stream);
+ }
+ }
+
+ private static VirtualFile getDirectoryToSave(Project project, @Nullable Module module) {
+ if (module == null) {
+ return project.getBaseDir();
+ }
+ else {
+ VirtualFile moduleFile = module.getModuleFile();
+ assert moduleFile != null;
+ return moduleFile.getParent();
+ }
+ }
+
+ @Override
+ public void update(AnActionEvent e) {
+ Project project = getEventProject(e);
+ e.getPresentation().setEnabled(project != null && !project.isDefault());
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/platform/templates/SaveProjectAsTemplateDialog.form b/java/idea-ui/src/com/intellij/platform/templates/SaveProjectAsTemplateDialog.form
new file mode 100644
index 0000000..ebcd029
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/platform/templates/SaveProjectAsTemplateDialog.form
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.intellij.platform.templates.SaveProjectAsTemplateDialog">
+ <grid id="27dc6" binding="myPanel" layout-manager="GridLayoutManager" row-count="4" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+ <margin top="0" left="0" bottom="0" right="0"/>
+ <constraints>
+ <xy x="20" y="20" width="370" height="170"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <component id="b149e" class="com.intellij.ui.components.JBLabel">
+ <constraints>
+ <grid row="1" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties>
+ <labelFor value="6b257"/>
+ <text value="&Name:"/>
+ </properties>
+ </component>
+ <vspacer id="bd33">
+ <constraints>
+ <grid row="3" column="0" row-span="1" col-span="1" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/>
+ </constraints>
+ </vspacer>
+ <component id="6b257" class="javax.swing.JTextField" binding="myName">
+ <constraints>
+ <grid row="1" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="8" fill="1" indent="0" use-parent-layout="false">
+ <preferred-size width="150" height="-1"/>
+ </grid>
+ </constraints>
+ <properties/>
+ </component>
+ <component id="fbd8f" class="com.intellij.ui.components.JBLabel">
+ <constraints>
+ <grid row="2" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="10" fill="0" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties>
+ <text value="&Description:"/>
+ </properties>
+ </component>
+ <component id="94559" class="com.intellij.ui.EditorTextField" binding="myDescription">
+ <constraints>
+ <grid row="2" column="1" row-span="2" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false">
+ <preferred-size width="-1" height="60"/>
+ </grid>
+ </constraints>
+ <properties/>
+ </component>
+ <component id="61a42" class="javax.swing.JComboBox" binding="myModuleCombo">
+ <constraints>
+ <grid row="0" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="2" anchor="8" fill="1" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties/>
+ </component>
+ <component id="66b82" class="javax.swing.JLabel" binding="myModuleLabel">
+ <constraints>
+ <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties>
+ <text value="&Save:"/>
+ </properties>
+ </component>
+ </children>
+ </grid>
+</form>
diff --git a/java/idea-ui/src/com/intellij/platform/templates/SaveProjectAsTemplateDialog.java b/java/idea-ui/src/com/intellij/platform/templates/SaveProjectAsTemplateDialog.java
new file mode 100644
index 0000000..860c54f
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/platform/templates/SaveProjectAsTemplateDialog.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright 2000-2012 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.platform.templates;
+
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.fileTypes.FileTypeManager;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.module.ModuleManager;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.DialogWrapper;
+import com.intellij.openapi.ui.ValidationInfo;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.openapi.vfs.VfsUtilCore;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.ui.CollectionComboBoxModel;
+import com.intellij.ui.EditorTextField;
+import com.intellij.util.Function;
+import com.intellij.util.containers.ContainerUtil;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author Dmitry Avdeev
+ * Date: 10/8/12
+ */
+public class SaveProjectAsTemplateDialog extends DialogWrapper {
+
+ private static final String WHOLE_PROJECT = "<whole project>";
+ @NotNull private final Project myProject;
+ private JPanel myPanel;
+ private JTextField myName;
+ private EditorTextField myDescription;
+ private JComboBox myModuleCombo;
+ private JLabel myModuleLabel;
+
+ protected SaveProjectAsTemplateDialog(@NotNull Project project, @Nullable VirtualFile descriptionFile) {
+ super(project);
+ myProject = project;
+
+ setTitle("Save Project As Template");
+ Module[] modules = ModuleManager.getInstance(project).getModules();
+ if (modules.length < 2) {
+ myModuleLabel.setVisible(false);
+ myModuleCombo.setVisible(false);
+ }
+ else {
+ List<String> items = new ArrayList<String>(ContainerUtil.map(modules, new Function<Module, String>() {
+ @Override
+ public String fun(Module module) {
+ return module.getName();
+ }
+ }));
+ items.add(WHOLE_PROJECT);
+ myModuleCombo.setModel(new CollectionComboBoxModel(items, WHOLE_PROJECT));
+ }
+ myDescription.setFileType(FileTypeManager.getInstance().getFileTypeByExtension(".html"));
+ if (descriptionFile != null) {
+ try {
+ String s = VfsUtilCore.loadText(descriptionFile);
+ myDescription.setText(s);
+ }
+ catch (IOException e) {
+ LOG.error(e);
+ }
+ }
+ init();
+ }
+
+ @Nullable
+ @Override
+ protected JComponent createCenterPanel() {
+ return myPanel;
+ }
+
+ @Nullable
+ @Override
+ public JComponent getPreferredFocusedComponent() {
+ return myName;
+ }
+
+ @Nullable
+ @Override
+ protected String getDimensionServiceKey() {
+ return "save.project.as.template.dialog";
+ }
+
+ @Nullable
+ @Override
+ protected ValidationInfo doValidate() {
+ if (StringUtil.isEmpty(myName.getText())) {
+ return new ValidationInfo("Template name should not be empty");
+ }
+ File file = getTemplateFile();
+ return file.exists() ? new ValidationInfo("Template already exists") : null;
+ }
+
+ File getTemplateFile() {
+ String name = myName.getText();
+ return ArchivedTemplatesFactory.getTemplateFile(name);
+ }
+
+ String getDescription() {
+ return myDescription.getText();
+ }
+
+ @Nullable
+ Module getModuleToSave() {
+ String item = (String)myModuleCombo.getSelectedItem();
+ if (item == null || item.equals(WHOLE_PROJECT)) return null;
+ return ModuleManager.getInstance(myProject).findModuleByName(item);
+ }
+
+ private final static Logger LOG = Logger.getInstance(SaveProjectAsTemplateDialog.class);
+}
diff --git a/java/idea-ui/src/com/intellij/platform/templates/TemplateModuleBuilder.java b/java/idea-ui/src/com/intellij/platform/templates/TemplateModuleBuilder.java
new file mode 100644
index 0000000..8529897
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/platform/templates/TemplateModuleBuilder.java
@@ -0,0 +1,200 @@
+/*
+ * Copyright 2000-2012 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.platform.templates;
+
+import com.intellij.execution.RunManager;
+import com.intellij.execution.configurations.ModuleBasedConfiguration;
+import com.intellij.execution.configurations.RunConfiguration;
+import com.intellij.ide.util.newProjectWizard.modes.ImportImlMode;
+import com.intellij.ide.util.projectWizard.ModuleBuilder;
+import com.intellij.ide.util.projectWizard.ModuleWizardStep;
+import com.intellij.ide.util.projectWizard.WizardContext;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.module.*;
+import com.intellij.openapi.options.ConfigurationException;
+import com.intellij.openapi.progress.ProgressManager;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.project.ex.ProjectManagerEx;
+import com.intellij.openapi.roots.ModifiableRootModel;
+import com.intellij.openapi.roots.ui.configuration.ModulesProvider;
+import com.intellij.openapi.util.Condition;
+import com.intellij.openapi.util.InvalidDataException;
+import com.intellij.openapi.util.NullableComputable;
+import com.intellij.openapi.util.io.StreamUtil;
+import com.intellij.openapi.vfs.LocalFileSystem;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.openapi.vfs.newvfs.RefreshQueue;
+import com.intellij.platform.templates.github.ZipUtil;
+import com.intellij.util.NullableFunction;
+import com.intellij.util.containers.ContainerUtil;
+import org.jdom.JDOMException;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.zip.ZipInputStream;
+
+/**
+* @author Dmitry Avdeev
+* Date: 10/19/12
+*/
+public class TemplateModuleBuilder extends ModuleBuilder {
+
+ private static final NullableFunction<String,String> PATH_CONVERTOR = new NullableFunction<String, String>() {
+ @Nullable
+ @Override
+ public String fun(String s) {
+ return s.contains(".idea") ? null : s;
+ }
+ };
+
+ private final ModuleType myType;
+ private ArchivedProjectTemplate myTemplate;
+ private boolean myProjectMode;
+
+ public TemplateModuleBuilder(ArchivedProjectTemplate template, ModuleType moduleType) {
+ myTemplate = template;
+ myType = moduleType;
+ }
+
+ @Override
+ public void setupRootModel(ModifiableRootModel modifiableRootModel) throws ConfigurationException {
+
+ }
+
+ @Override
+ public ModuleWizardStep[] createWizardSteps(WizardContext wizardContext, ModulesProvider modulesProvider) {
+ return myType.createModuleBuilder().createWizardSteps(wizardContext, modulesProvider);
+ }
+
+ @Override
+ public Module commitModule(@NotNull final Project project, ModifiableModuleModel model) {
+ if (myProjectMode) {
+ final Module[] modules = ModuleManager.getInstance(project).getModules();
+ if (modules.length > 0) {
+ final Module module = modules[0];
+ ApplicationManager.getApplication().runWriteAction(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ setupModule(module);
+ ModifiableModuleModel modifiableModuleModel = ModuleManager.getInstance(project).getModifiableModel();
+ modifiableModuleModel.renameModule(module, module.getProject().getName());
+ modifiableModuleModel.commit();
+ fixModuleName(module);
+ }
+ catch (ConfigurationException e) {
+ LOG.error(e);
+ }
+ catch (ModuleWithNameAlreadyExists exists) {
+ // do nothing
+ }
+ }
+ });
+ return module;
+ }
+ return null;
+ }
+ else {
+ return super.commitModule(project, model);
+ }
+ }
+
+ @Override
+ public ModuleType getModuleType() {
+ return myType;
+ }
+
+ @NotNull
+ @Override
+ public Module createModule(@NotNull ModifiableModuleModel moduleModel)
+ throws InvalidDataException, IOException, ModuleWithNameAlreadyExists, JDOMException, ConfigurationException {
+ final String path = getContentEntryPath();
+ unzip(path, true);
+ Module module = ImportImlMode.setUpLoader(getModuleFilePath()).createModule(moduleModel);
+ if (myProjectMode) {
+ moduleModel.renameModule(module, module.getProject().getName());
+ }
+ fixModuleName(module);
+ return module;
+ }
+
+ private static void fixModuleName(Module module) {
+ RunConfiguration[] configurations = RunManager.getInstance(module.getProject()).getAllConfigurations();
+ for (RunConfiguration configuration : configurations) {
+ if (configuration instanceof ModuleBasedConfiguration) {
+ ((ModuleBasedConfiguration)configuration).getConfigurationModule().setModule(module);
+ }
+ }
+ }
+
+ private void unzip(String path, boolean moduleMode) {
+ File dir = new File(path);
+ ZipInputStream zipInputStream = null;
+ try {
+ zipInputStream = myTemplate.getStream();
+ ZipUtil.unzip(ProgressManager.getInstance().getProgressIndicator(), dir, zipInputStream, moduleMode ? PATH_CONVERTOR : null);
+ String iml = ContainerUtil.find(dir.list(), new Condition<String>() {
+ @Override
+ public boolean value(String s) {
+ return s.endsWith(".iml");
+ }
+ });
+ if (moduleMode) {
+ File from = new File(path, iml);
+ File to = new File(getModuleFilePath());
+ if (!from.renameTo(to)) {
+ throw new IOException("Can't rename " + from + " to " + to);
+ }
+ }
+ VirtualFile virtualFile = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(dir);
+ if (virtualFile == null) {
+ throw new IOException("Can't find " + dir);
+ }
+ RefreshQueue.getInstance().refresh(false, true, null, virtualFile);
+ }
+ catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ finally {
+ StreamUtil.closeStream(zipInputStream);
+ }
+ }
+
+ @Nullable
+ @Override
+ public Project createProject(String name, final String path) {
+ myProjectMode = true;
+ unzip(path, false);
+ return ApplicationManager.getApplication().runWriteAction(new NullableComputable<Project>() {
+ @Nullable
+ @Override
+ public Project compute() {
+ try {
+ return ProjectManagerEx.getInstanceEx().convertAndLoadProject(path);
+ }
+ catch (IOException e) {
+ LOG.error(e);
+ return null;
+ }
+ }
+ });
+ }
+
+ private final static Logger LOG = Logger.getInstance(TemplateModuleBuilder.class);
+}
diff --git a/java/idea-ui/src/com/intellij/projectImport/ImportChooserStep.form b/java/idea-ui/src/com/intellij/projectImport/ImportChooserStep.form
new file mode 100644
index 0000000..790f007
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/projectImport/ImportChooserStep.form
@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.intellij.projectImport.ImportChooserStep">
+ <grid id="27dc6" binding="myPanel" layout-manager="GridLayoutManager" row-count="3" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+ <margin top="0" left="0" bottom="0" right="0"/>
+ <constraints>
+ <xy x="20" y="20" width="500" height="269"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <component id="478c3" class="com.intellij.ui.components.JBRadioButton" binding="myCreateFromSources">
+ <constraints>
+ <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties>
+ <text resource-bundle="messages/ProjectBundle" key="project.new.wizard.from.existent.sources.title"/>
+ </properties>
+ </component>
+ <component id="79a8a" class="com.intellij.ui.components.JBRadioButton" binding="myImportFrom">
+ <constraints>
+ <grid row="1" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties>
+ <text resource-bundle="messages/ProjectBundle" key="project.new.wizard.import.title"/>
+ </properties>
+ </component>
+ <grid id="b4cdc" layout-manager="GridLayoutManager" row-count="2" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+ <margin top="0" left="0" bottom="0" right="0"/>
+ <constraints>
+ <grid row="2" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties/>
+ <border type="empty">
+ <size top="0" left="20" bottom="0" right="0"/>
+ </border>
+ <children>
+ <scrollpane id="e873f" class="com.intellij.ui.components.JBScrollPane">
+ <constraints>
+ <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties/>
+ <border type="none">
+ <size top="0" left="20" bottom="0" right="0"/>
+ </border>
+ <children>
+ <component id="4daca" class="com.intellij.ui.components.JBList" binding="myList">
+ <constraints/>
+ <properties/>
+ </component>
+ </children>
+ </scrollpane>
+ <vspacer id="65df7">
+ <constraints>
+ <grid row="1" column="0" row-span="1" col-span="1" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/>
+ </constraints>
+ </vspacer>
+ </children>
+ </grid>
+ </children>
+ </grid>
+ <buttonGroups>
+ <group name="myButtonGroup">
+ <member id="478c3"/>
+ <member id="79a8a"/>
+ </group>
+ </buttonGroups>
+</form>
diff --git a/java/idea-ui/src/com/intellij/projectImport/ImportChooserStep.java b/java/idea-ui/src/com/intellij/projectImport/ImportChooserStep.java
new file mode 100644
index 0000000..e81846e
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/projectImport/ImportChooserStep.java
@@ -0,0 +1,200 @@
+/*
+ * Copyright 2000-2009 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.
+ */
+
+/*
+ * User: anna
+ * Date: 10-Jul-2007
+ */
+package com.intellij.projectImport;
+
+import com.intellij.ide.util.PropertiesComponent;
+import com.intellij.ide.util.newProjectWizard.StepSequence;
+import com.intellij.ide.util.projectWizard.ImportFromSourcesProvider;
+import com.intellij.ide.util.projectWizard.WizardContext;
+import com.intellij.openapi.project.ProjectBundle;
+import com.intellij.openapi.util.Comparing;
+import com.intellij.openapi.util.IconLoader;
+import com.intellij.openapi.wm.IdeFocusManager;
+import com.intellij.ui.DoubleClickListener;
+import com.intellij.ui.components.JBList;
+import com.intellij.ui.components.JBRadioButton;
+import org.jetbrains.annotations.NonNls;
+
+import javax.swing.*;
+import javax.swing.event.ListSelectionEvent;
+import javax.swing.event.ListSelectionListener;
+import java.awt.*;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.MouseEvent;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+public class ImportChooserStep extends ProjectImportWizardStep {
+ private static final String PREFERRED = "create.project.preferred.importer";
+
+ private final ProjectImportProvider[] myProviders;
+ private final StepSequence mySequence;
+ private ProjectImportProvider myFromSourcesProvider;
+ private JBList myList;
+ private JPanel myPanel;
+
+ private JBRadioButton myCreateFromSources;
+ private JBRadioButton myImportFrom;
+
+ public ImportChooserStep(final ProjectImportProvider[] providers, final StepSequence sequence, final WizardContext context) {
+ super(context);
+ myProviders = providers;
+ mySequence = sequence;
+
+ myImportFrom.setText(ProjectBundle.message("project.new.wizard.import.title", context.getPresentationName()));
+ myCreateFromSources.setText(ProjectBundle.message("project.new.wizard.from.existent.sources.title", context.getPresentationName()));
+ final DefaultListModel model = new DefaultListModel();
+ for (ProjectImportProvider provider : sorted(providers)) {
+ if (provider instanceof ImportFromSourcesProvider) {
+ myFromSourcesProvider = provider;
+ }
+ else {
+ model.addElement(provider);
+ }
+ }
+
+ myList.setModel(model);
+ myList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
+ myList.setCellRenderer(new DefaultListCellRenderer() {
+ public Component getListCellRendererComponent(final JList list,
+ final Object value,
+ final int index, final boolean isSelected, final boolean cellHasFocus) {
+ final Component rendererComponent = super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
+ setText(((ProjectImportProvider)value).getName());
+ Icon icon = ((ProjectImportProvider)value).getIcon();
+ setIcon(icon);
+ setDisabledIcon(IconLoader.getDisabledIcon(icon));
+ return rendererComponent;
+ }
+ });
+
+ ActionListener actionListener = new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ if (myImportFrom.isSelected()) {
+ IdeFocusManager.getInstance(context.getProject()).requestFocus(myList, false);
+ }
+ updateSteps();
+ }
+ };
+ myImportFrom.addActionListener(actionListener);
+ myCreateFromSources.addActionListener(actionListener);
+
+ myList.addListSelectionListener(new ListSelectionListener() {
+ public void valueChanged(final ListSelectionEvent e) {
+ updateSteps();
+ }
+ });
+
+ new DoubleClickListener() {
+ @Override
+ protected boolean onDoubleClick(MouseEvent e) {
+ context.requestNextStep();
+ return true;
+ }
+ }.installOn(myList);
+ }
+
+ @Override
+ public void updateStep() {
+ if (myList.getSelectedValue() != null) return;
+
+ final String id = PropertiesComponent.getInstance().getValue(PREFERRED);
+ if (id == null || id.equals(myFromSourcesProvider.getId())) {
+ myCreateFromSources.setSelected(true);
+ }
+ else {
+ myImportFrom.setSelected(true);
+ for (ProjectImportProvider provider : myProviders) {
+ if (Comparing.strEqual(provider.getId(), id)) {
+ myList.setSelectedValue(provider, true);
+ break;
+ }
+ }
+ }
+ if (myList.getSelectedValue() == null) {
+ myList.setSelectedIndex(0);
+ }
+ }
+
+ private void updateSteps() {
+ myList.setEnabled(myImportFrom.isSelected());
+ final ProjectImportProvider provider = getSelectedProvider();
+ if (provider != null) {
+ mySequence.setType(provider.getId());
+ PropertiesComponent.getInstance().setValue(PREFERRED, provider.getId());
+ getWizardContext().requestWizardButtonsUpdate();
+ }
+ }
+
+ private static List<ProjectImportProvider> sorted(ProjectImportProvider[] providers) {
+ List<ProjectImportProvider> result = new ArrayList<ProjectImportProvider>();
+ Collections.addAll(result, providers);
+ Collections.sort(result, new Comparator<ProjectImportProvider>() {
+ public int compare(ProjectImportProvider l, ProjectImportProvider r) {
+ return l.getName().compareToIgnoreCase(r.getName());
+ }
+ });
+ return result;
+ }
+
+ public JComponent getComponent() {
+ return myPanel;
+ }
+
+ public JComponent getPreferredFocusedComponent() {
+ return myCreateFromSources.isSelected() ? myCreateFromSources : myList;
+ }
+
+ public void updateDataModel() {
+ final ProjectImportProvider provider = getSelectedProvider();
+ if (provider != null) {
+ mySequence.setType(provider.getId());
+ final ProjectImportBuilder builder = provider.getBuilder();
+ getWizardContext().setProjectBuilder(builder);
+ builder.setUpdate(getWizardContext().getProject() != null);
+ }
+ }
+
+ private ProjectImportProvider getSelectedProvider() {
+ final ProjectImportProvider provider;
+ if (myCreateFromSources.isSelected()) {
+ provider = myFromSourcesProvider;
+ }
+ else {
+ provider = (ProjectImportProvider)myList.getSelectedValue();
+ }
+ return provider;
+ }
+
+ @Override
+ public String getName() {
+ return "Choose External Model";
+ }
+
+ @NonNls
+ public String getHelpId() {
+ return "reference.dialogs.new.project.import";
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/projectImport/ProjectFormatPanel.form b/java/idea-ui/src/com/intellij/projectImport/ProjectFormatPanel.form
new file mode 100644
index 0000000..85c063f
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/projectImport/ProjectFormatPanel.form
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.intellij.projectImport.ProjectFormatPanel">
+ <grid id="27dc6" binding="myWholePanel" layout-manager="GridBagLayout">
+ <constraints>
+ <xy x="20" y="20" width="140" height="29"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <component id="29c74" class="javax.swing.JLabel">
+ <constraints>
+ <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
+ <gridbag top="5" left="5" bottom="0" right="5" weightx="0.0" weighty="0.0"/>
+ </constraints>
+ <properties>
+ <text value="Project format:"/>
+ </properties>
+ </component>
+ <component id="bc339" class="javax.swing.JComboBox" binding="myStorageFormatCombo">
+ <constraints>
+ <grid row="0" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="2" anchor="8" fill="1" indent="0" use-parent-layout="false"/>
+ <gridbag top="5" left="0" bottom="0" right="0" weightx="0.0" weighty="0.0"/>
+ </constraints>
+ <properties/>
+ </component>
+ </children>
+ </grid>
+</form>
diff --git a/java/idea-ui/src/com/intellij/projectImport/ProjectFormatPanel.java b/java/idea-ui/src/com/intellij/projectImport/ProjectFormatPanel.java
new file mode 100644
index 0000000..e367ce7
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/projectImport/ProjectFormatPanel.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2000-2009 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.
+ */
+
+/*
+ * User: anna
+ * Date: 09-Feb-2009
+ */
+package com.intellij.projectImport;
+
+import com.intellij.ide.util.PropertiesComponent;
+import com.intellij.ide.util.projectWizard.WizardContext;
+import com.intellij.openapi.components.StorageScheme;
+
+import javax.swing.*;
+
+public class ProjectFormatPanel {
+
+ private static final String STORAGE_FORMAT_PROPERTY = "default.storage.format";
+ public static final String DIR_BASED = ".idea (directory based)";
+ private static final String FILE_BASED = ".ipr (file based)";
+
+ private JComboBox myStorageFormatCombo;
+ private JPanel myWholePanel;
+
+ public ProjectFormatPanel() {
+ myStorageFormatCombo.insertItemAt(DIR_BASED, 0);
+ myStorageFormatCombo.insertItemAt(FILE_BASED, 1);
+ myStorageFormatCombo.setSelectedItem(PropertiesComponent.getInstance().getOrInit(STORAGE_FORMAT_PROPERTY, DIR_BASED));
+ }
+
+ public JPanel getPanel() {
+ return myWholePanel;
+ }
+
+ public JComboBox getStorageFormatComboBox() {
+ return myStorageFormatCombo;
+ }
+
+ public void updateData(WizardContext context) {
+ StorageScheme format =
+ FILE_BASED.equals(myStorageFormatCombo.getSelectedItem()) ? StorageScheme.DEFAULT : StorageScheme.DIRECTORY_BASED;
+ context.setProjectStorageFormat(format);
+ setDefaultFormat(isDefault());
+ }
+
+ public static void setDefaultFormat(boolean aDefault) {
+ PropertiesComponent.getInstance().setValue(STORAGE_FORMAT_PROPERTY, aDefault ? FILE_BASED : DIR_BASED);
+ }
+
+ public void setVisible(boolean visible) {
+ myWholePanel.setVisible(visible);
+ }
+
+ public boolean isDefault() {
+ return FILE_BASED.equals(myStorageFormatCombo.getSelectedItem());
+ }
+}
\ No newline at end of file
diff --git a/java/idea-ui/src/com/intellij/projectImport/ProjectImportBuilder.java b/java/idea-ui/src/com/intellij/projectImport/ProjectImportBuilder.java
new file mode 100644
index 0000000..7095871
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/projectImport/ProjectImportBuilder.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2000-2009 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.projectImport;
+
+import com.intellij.ide.DataManager;
+import com.intellij.ide.IdeBundle;
+import com.intellij.ide.util.projectWizard.ProjectBuilder;
+import com.intellij.openapi.actionSystem.PlatformDataKeys;
+import com.intellij.openapi.extensions.ExtensionPointName;
+import com.intellij.openapi.module.ModifiableModuleModel;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.options.ConfigurationException;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.roots.ui.configuration.ModulesProvider;
+import com.intellij.packaging.artifacts.ModifiableArtifactModel;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import java.util.List;
+
+/**
+ * @author Vladislav.Kaznacheev
+ */
+public abstract class ProjectImportBuilder<T> extends ProjectBuilder {
+ public static final ExtensionPointName<ProjectImportBuilder> EXTENSIONS_POINT_NAME = ExtensionPointName.create("com.intellij.projectImportBuilder");
+
+ private boolean myUpdate;
+ private String myFileToImport;
+
+ @NotNull
+ public abstract String getName();
+
+ public abstract Icon getIcon();
+
+ public abstract List<T> getList();
+
+ public abstract boolean isMarked(final T element);
+
+ public abstract void setList(List<T> list) throws ConfigurationException;
+
+ public abstract void setOpenProjectSettingsAfter(boolean on);
+
+ @Override
+ public List<Module> commit(Project project, ModifiableModuleModel model, ModulesProvider modulesProvider) {
+ return commit(project, model, modulesProvider, null);
+ }
+
+ @Nullable
+ public abstract List<Module> commit(Project project, ModifiableModuleModel model, ModulesProvider modulesProvider, ModifiableArtifactModel artifactModel);
+
+ public void setFileToImport(String path) {
+ myFileToImport = path;
+ }
+
+ public String getFileToImport() {
+ return myFileToImport;
+ }
+
+ @Nullable
+ public static Project getCurrentProject() {
+ return PlatformDataKeys.PROJECT.getData(DataManager.getInstance().getDataContext());
+ }
+
+ protected String getTitle() {
+ return IdeBundle.message("project.import.wizard.title", getName());
+ }
+
+ public boolean isUpdate() {
+ return myUpdate;
+ }
+
+ public void setUpdate(final boolean update) {
+ myUpdate = update;
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/projectImport/ProjectImportProvider.java b/java/idea-ui/src/com/intellij/projectImport/ProjectImportProvider.java
new file mode 100644
index 0000000..8f79e2b
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/projectImport/ProjectImportProvider.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2000-2009 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.
+ */
+
+/*
+ * User: anna
+ * Date: 10-Jul-2007
+ */
+package com.intellij.projectImport;
+
+import com.intellij.ide.util.newProjectWizard.StepSequence;
+import com.intellij.ide.util.projectWizard.ModuleWizardStep;
+import com.intellij.ide.util.projectWizard.WizardContext;
+import com.intellij.openapi.extensions.ExtensionPointName;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.vfs.VirtualFile;
+import org.intellij.lang.annotations.Language;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+
+public abstract class ProjectImportProvider {
+ public static final ExtensionPointName<ProjectImportProvider> PROJECT_IMPORT_PROVIDER = ExtensionPointName.create("com.intellij.projectImportProvider");
+
+ protected ProjectImportBuilder myBuilder;
+
+ protected ProjectImportProvider(final ProjectImportBuilder builder) {
+ myBuilder = builder;
+ }
+
+ public ProjectImportBuilder getBuilder() {
+ return myBuilder;
+ }
+
+ @NonNls @NotNull
+ public String getId(){
+ return getBuilder().getName();
+ }
+
+ @NotNull
+ public String getName(){
+ return getBuilder().getName();
+ }
+
+ @Nullable
+ public Icon getIcon() {
+ return getBuilder().getIcon();
+ }
+
+ public boolean canImport(VirtualFile fileOrDirectory, @Nullable Project project) {
+ if (fileOrDirectory.isDirectory()) {
+ return true;
+ }
+ else {
+ return canImportFromFile(fileOrDirectory);
+ }
+ }
+
+ protected boolean canImportFromFile(VirtualFile file) {
+ return false;
+ }
+
+ public String getPathToBeImported(VirtualFile file) {
+ return getDefaultPath(file);
+ }
+
+ public static String getDefaultPath(VirtualFile file) {
+ return file.isDirectory() ? file.getPath() : file.getParent().getPath();
+ }
+
+ public boolean canCreateNewProject() {
+ return true;
+ }
+
+ public void addSteps(StepSequence sequence, WizardContext context, String id) {
+ ModuleWizardStep[] steps = createSteps(context);
+ for (ModuleWizardStep step : steps) {
+ sequence.addSpecificStep(id, step);
+ }
+ }
+
+ public ModuleWizardStep[] createSteps(WizardContext context) {
+ return ModuleWizardStep.EMPTY_ARRAY;
+ }
+
+ @Nullable
+ @Language("HTML")
+ public String getFileSample() {
+ return null;
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/projectImport/ProjectImportWizardStep.java b/java/idea-ui/src/com/intellij/projectImport/ProjectImportWizardStep.java
new file mode 100644
index 0000000..ca3e311
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/projectImport/ProjectImportWizardStep.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2000-2009 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.projectImport;
+
+import com.intellij.ide.util.projectWizard.ModuleWizardStep;
+import com.intellij.ide.util.projectWizard.WizardContext;
+import com.intellij.openapi.util.io.FileUtil;
+
+import javax.swing.*;
+
+/**
+ * @author Vladislav.Kaznacheev
+ */
+public abstract class ProjectImportWizardStep extends ModuleWizardStep {
+ private final WizardContext myContext;
+
+ public ProjectImportWizardStep(WizardContext context) {
+ myContext = context;
+ }
+
+ public Icon getIcon() {
+ return myContext.getStepIcon();
+ }
+
+ protected ProjectImportBuilder getBuilder() {
+ return (ProjectImportBuilder)myContext.getProjectBuilder();
+ }
+
+ protected WizardContext getWizardContext() {
+ return myContext;
+ }
+
+ protected void suggestProjectNameAndPath(final String alternativePath, final String path) {
+ getWizardContext().setProjectFileDirectory(alternativePath != null && alternativePath.length() > 0 ? alternativePath : path);
+ final String global = FileUtil.toSystemIndependentName(path);
+ getWizardContext().setProjectName(global.substring(global.lastIndexOf("/") + 1));
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/projectImport/ProjectOpenProcessorBase.java b/java/idea-ui/src/com/intellij/projectImport/ProjectOpenProcessorBase.java
new file mode 100644
index 0000000..7ede6a8
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/projectImport/ProjectOpenProcessorBase.java
@@ -0,0 +1,254 @@
+/*
+ * Copyright 2000-2012 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.
+ */
+
+/*
+ * User: anna
+ * Date: 12-Jul-2007
+ */
+package com.intellij.projectImport;
+
+import com.intellij.CommonBundle;
+import com.intellij.ide.IdeBundle;
+import com.intellij.ide.highlighter.ProjectFileType;
+import com.intellij.ide.impl.NewProjectUtil;
+import com.intellij.ide.impl.ProjectUtil;
+import com.intellij.ide.util.projectWizard.WizardContext;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.components.StorageScheme;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.project.ProjectManager;
+import com.intellij.openapi.project.ex.ProjectManagerEx;
+import com.intellij.openapi.projectRoots.JavaSdk;
+import com.intellij.openapi.projectRoots.ProjectJdkTable;
+import com.intellij.openapi.projectRoots.Sdk;
+import com.intellij.openapi.roots.CompilerProjectExtension;
+import com.intellij.openapi.roots.ProjectRootManager;
+import com.intellij.openapi.roots.ui.configuration.ModulesProvider;
+import com.intellij.openapi.ui.Messages;
+import com.intellij.openapi.util.InvalidDataException;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.openapi.vfs.VfsUtil;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.openapi.vfs.newvfs.NewVirtualFile;
+import org.jdom.JDOMException;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import java.io.File;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collection;
+
+public abstract class ProjectOpenProcessorBase<T extends ProjectImportBuilder> extends ProjectOpenProcessor {
+
+ private final T myBuilder;
+
+ protected ProjectOpenProcessorBase(@NotNull final T builder) {
+ myBuilder = builder;
+ }
+
+ public String getName() {
+ return getBuilder().getName();
+ }
+
+ @Nullable
+ public Icon getIcon() {
+ return getBuilder().getIcon();
+ }
+
+ public boolean canOpenProject(final VirtualFile file) {
+ final String[] supported = getSupportedExtensions();
+ if (supported != null) {
+ if (file.isDirectory()) {
+ for (VirtualFile child : getFileChildren(file)) {
+ if (canOpenFile(child, supported)) return true;
+ }
+ return false;
+ }
+ if (canOpenFile(file, supported)) return true;
+ }
+ return false;
+ }
+
+ private static Collection<VirtualFile> getFileChildren(VirtualFile file) {
+ if (file instanceof NewVirtualFile) {
+ return ((NewVirtualFile)file).getCachedChildren();
+ }
+
+ return Arrays.asList(file.getChildren());
+ }
+
+ protected static boolean canOpenFile(VirtualFile file, String[] supported) {
+ final String fileName = file.getName();
+ for (String name : supported) {
+ if (fileName.equals(name)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ protected boolean doQuickImport(VirtualFile file, final WizardContext wizardContext) {
+ return false;
+ }
+
+ @NotNull
+ public T getBuilder() {
+ return myBuilder;
+ }
+
+ @Nullable
+ public abstract String[] getSupportedExtensions();
+
+ @Nullable
+ public Project doOpenProject(@NotNull VirtualFile virtualFile, Project projectToClose, boolean forceOpenInNewFrame) {
+ try {
+ getBuilder().setUpdate(false);
+ final WizardContext wizardContext = new WizardContext(null);
+ if (virtualFile.isDirectory()) {
+ final String[] supported = getSupportedExtensions();
+ for (VirtualFile file : getFileChildren(virtualFile)) {
+ if (canOpenFile(file, supported)) {
+ virtualFile = file;
+ break;
+ }
+ }
+ }
+
+ wizardContext.setProjectFileDirectory(virtualFile.getParent().getPath());
+
+ if (!doQuickImport(virtualFile, wizardContext)) return null;
+
+ if (wizardContext.getProjectName() == null) {
+ if (wizardContext.getProjectStorageFormat() == StorageScheme.DEFAULT) {
+ wizardContext.setProjectName(IdeBundle.message("project.import.default.name", getName()) + ProjectFileType.DOT_DEFAULT_EXTENSION);
+ }
+ else {
+ wizardContext.setProjectName(IdeBundle.message("project.import.default.name.dotIdea", getName()));
+ }
+ }
+
+ Project defaultProject = ProjectManager.getInstance().getDefaultProject();
+ Sdk jdk = ProjectRootManager.getInstance(defaultProject).getProjectSdk();
+ if (jdk == null) {
+ jdk = ProjectJdkTable.getInstance().findMostRecentSdkOfType(JavaSdk.getInstance());
+ }
+ wizardContext.setProjectJdk(jdk);
+
+ final String dotIdeaFilePath = wizardContext.getProjectFileDirectory() + File.separator + Project.DIRECTORY_STORE_FOLDER;
+ final String projectFilePath = wizardContext.getProjectFileDirectory() + File.separator + wizardContext.getProjectName() +
+ ProjectFileType.DOT_DEFAULT_EXTENSION;
+
+ File dotIdeaFile = new File(dotIdeaFilePath);
+ File projectFile = new File(projectFilePath);
+
+ String pathToOpen;
+ if (wizardContext.getProjectStorageFormat() == StorageScheme.DEFAULT) {
+ pathToOpen = projectFilePath;
+ } else {
+ pathToOpen = dotIdeaFile.getParent();
+ }
+
+ boolean shouldOpenExisting = false;
+ if (!ApplicationManager.getApplication().isHeadlessEnvironment() && (projectFile.exists() || dotIdeaFile.exists())) {
+ String existingName;
+ if (dotIdeaFile.exists()) {
+ existingName = "an existing project";
+ pathToOpen = dotIdeaFile.getParent();
+ }
+ else {
+ existingName = "'" + projectFile.getName() + "'";
+ pathToOpen = projectFilePath;
+ }
+ int result = Messages.showYesNoCancelDialog(projectToClose,
+ IdeBundle.message("project.import.open.existing",
+ existingName,
+ projectFile.getParent(),
+ virtualFile.getName()),
+ IdeBundle.message("title.open.project"),
+ IdeBundle.message("project.import.open.existing.openExisting"),
+ IdeBundle.message("project.import.open.existing.reimport"),
+ CommonBundle.message("button.cancel"),
+ Messages.getQuestionIcon());
+ if (result == 2) return null;
+ shouldOpenExisting = result == 0;
+ }
+
+ final Project projectToOpen;
+ if (shouldOpenExisting) {
+ try {
+ projectToOpen = ProjectManagerEx.getInstanceEx().loadProject(pathToOpen);
+ }
+ catch (IOException e) {
+ return null;
+ }
+ catch (JDOMException e) {
+ return null;
+ }
+ catch (InvalidDataException e) {
+ return null;
+ }
+ }
+ else {
+ projectToOpen = ProjectManagerEx.getInstanceEx().newProject(wizardContext.getProjectName(), pathToOpen, true, false);
+
+ if (projectToOpen == null || !getBuilder().validate(projectToClose, projectToOpen)) {
+ return null;
+ }
+
+ projectToOpen.save();
+
+
+ ApplicationManager.getApplication().runWriteAction(new Runnable() {
+ public void run() {
+ Sdk jdk = wizardContext.getProjectJdk();
+ if (jdk != null) NewProjectUtil.applyJdkToProject(projectToOpen, jdk);
+
+ final String projectDirPath = wizardContext.getProjectFileDirectory();
+ CompilerProjectExtension.getInstance(projectToOpen).setCompilerOutputUrl(getUrl(
+ StringUtil.endsWithChar(projectDirPath, '/') ? projectDirPath + "classes" : projectDirPath + "/classes"));
+ }
+ });
+
+ getBuilder().commit(projectToOpen, null, ModulesProvider.EMPTY_MODULES_PROVIDER);
+ }
+
+ if (!forceOpenInNewFrame) {
+ NewProjectUtil.closePreviousProject(projectToClose);
+ }
+ ProjectUtil.updateLastProjectLocation(pathToOpen);
+ ProjectManagerEx.getInstanceEx().openProject(projectToOpen);
+
+ return projectToOpen;
+ }
+ finally {
+ getBuilder().cleanup();
+ }
+ }
+
+ public static String getUrl(@NonNls String path) {
+ try {
+ path = FileUtil.resolveShortWindowsName(path);
+ }
+ catch (IOException e) {
+ //file doesn't exist
+ }
+ return VfsUtil.pathToUrl(FileUtil.toSystemIndependentName(path));
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/projectImport/SelectImportedProjectsStep.java b/java/idea-ui/src/com/intellij/projectImport/SelectImportedProjectsStep.java
new file mode 100644
index 0000000..83d0685
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/projectImport/SelectImportedProjectsStep.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2000-2009 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.projectImport;
+
+import com.intellij.ide.IdeBundle;
+import com.intellij.ide.util.ElementsChooser;
+import com.intellij.ide.util.projectWizard.WizardContext;
+import com.intellij.openapi.options.ConfigurationException;
+import com.intellij.ui.IdeBorderFactory;
+import com.intellij.uiDesigner.core.GridConstraints;
+import com.intellij.uiDesigner.core.GridLayoutManager;
+import org.jetbrains.annotations.Nullable;
+import org.jetbrains.annotations.NotNull;
+
+import javax.swing.*;
+import java.awt.*;
+
+/**
+ * @author Vladislav.Kaznacheev
+ */
+public abstract class SelectImportedProjectsStep<T> extends ProjectImportWizardStep {
+ private final JPanel panel;
+ protected final ElementsChooser<T> fileChooser;
+ private final JCheckBox openModuleSettingsCheckBox;
+
+ public SelectImportedProjectsStep(WizardContext context) {
+ super(context);
+ fileChooser = new ElementsChooser<T>(true) {
+ protected String getItemText(@NotNull T item) {
+ return getElementText(item);
+ }
+
+ protected Icon getItemIcon(@NotNull final T item) {
+ return getElementIcon (item);
+ }
+ };
+
+ panel = new JPanel(new GridLayoutManager(2, 1, new Insets(0, 0, 0, 0), -1, -1));
+
+ panel.add(fileChooser, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_NORTH, GridConstraints.FILL_BOTH,
+ GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_WANT_GROW,
+ GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_WANT_GROW, null, null,
+ null));
+ openModuleSettingsCheckBox = new JCheckBox(IdeBundle.message("project.import.show.settings.after"));
+ panel.add(openModuleSettingsCheckBox, new GridConstraints(1, 0, 1, 1, GridConstraints.ANCHOR_SOUTH, GridConstraints.FILL_HORIZONTAL,
+ GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_WANT_GROW,
+ GridConstraints.SIZEPOLICY_FIXED, null, null, null));
+ }
+
+ @Nullable
+ protected Icon getElementIcon(final T item) {
+ return null;
+ }
+
+ protected abstract String getElementText(final T item);
+
+ public JComponent getComponent() {
+ return panel;
+ }
+
+ protected boolean isElementEnabled(T element) {
+ return true;
+ }
+
+ public void updateStep() {
+ fileChooser.clear();
+ for (T element : getContext().getList()) {
+ boolean isEnabled = isElementEnabled(element);
+ fileChooser.addElement(element, isEnabled && getContext().isMarked(element));
+ if (!isEnabled) {
+ fileChooser.disableElement(element);
+ }
+ }
+
+ fileChooser.setBorder(IdeBorderFactory.createTitledBorder(
+ IdeBundle.message("project.import.select.title", getContext().getName()), false));
+ openModuleSettingsCheckBox.setSelected(getBuilder().isOpenProjectSettingsAfter());
+ }
+
+ public boolean validate() throws ConfigurationException {
+ getContext().setList(fileChooser.getMarkedElements());
+ if (fileChooser.getMarkedElements().size() == 0) {
+ throw new ConfigurationException("Nothing found to import", "Unable to proceed");
+ }
+ return true;
+ }
+
+ public void updateDataModel() {}
+
+ public void onStepLeaving() {
+ super.onStepLeaving();
+ getContext().setOpenProjectSettingsAfter(openModuleSettingsCheckBox.isSelected());
+ }
+
+ public ProjectImportBuilder<T> getContext() {
+ return getBuilder();
+ }
+}
+
diff --git a/java/idea-ui/src/com/intellij/util/descriptors/impl/ConfigFileContainerImpl.java b/java/idea-ui/src/com/intellij/util/descriptors/impl/ConfigFileContainerImpl.java
new file mode 100644
index 0000000..331db27
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/util/descriptors/impl/ConfigFileContainerImpl.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright 2000-2009 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.util.descriptors.impl;
+
+import com.intellij.openapi.Disposable;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.Disposer;
+import com.intellij.openapi.util.MultiValuesMap;
+import com.intellij.openapi.vfs.*;
+import com.intellij.util.EventDispatcher;
+import com.intellij.util.descriptors.*;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * @author nik
+ */
+public class ConfigFileContainerImpl implements ConfigFileContainer {
+ private final Project myProject;
+ private final EventDispatcher<ConfigFileListener> myDispatcher = EventDispatcher.create(ConfigFileListener.class);
+ private final MultiValuesMap<ConfigFileMetaData, ConfigFile> myConfigFiles = new MultiValuesMap<ConfigFileMetaData, ConfigFile>();
+ private ConfigFile[] myCachedConfigFiles;
+ private final ConfigFileMetaDataProvider myMetaDataProvider;
+ private final ConfigFileInfoSetImpl myConfiguration;
+
+ public ConfigFileContainerImpl(final Project project, final ConfigFileMetaDataProvider descriptorMetaDataProvider,
+ final ConfigFileInfoSetImpl configuration) {
+ myConfiguration = configuration;
+ myMetaDataProvider = descriptorMetaDataProvider;
+ myProject = project;
+ VirtualFileManager.getInstance().addVirtualFileListener(new VirtualFileAdapter() {
+ public void propertyChanged(final VirtualFilePropertyEvent event) {
+ fileChanged(event.getFile());
+ }
+
+ public void fileMoved(final VirtualFileMoveEvent event) {
+ fileChanged(event.getFile());
+ }
+ }, this);
+ myConfiguration.setContainer(this);
+ }
+
+ private void fileChanged(final VirtualFile file) {
+ for (ConfigFile descriptor : myConfigFiles.values()) {
+ final VirtualFile virtualFile = descriptor.getVirtualFile();
+ if (virtualFile != null && VfsUtil.isAncestor(file, virtualFile, false)) {
+ myConfiguration.updateConfigFile(descriptor);
+ fireDescriptorChanged(descriptor);
+ }
+ }
+ }
+
+ @Nullable
+ public ConfigFile getConfigFile(ConfigFileMetaData metaData) {
+ final Collection<ConfigFile> descriptors = myConfigFiles.get(metaData);
+ if (descriptors == null || descriptors.isEmpty()) {
+ return null;
+ }
+ return descriptors.iterator().next();
+ }
+
+ public ConfigFile[] getConfigFiles() {
+ if (myCachedConfigFiles == null) {
+ final Collection<ConfigFile> descriptors = myConfigFiles.values();
+ myCachedConfigFiles = descriptors.toArray(new ConfigFile[descriptors.size()]);
+ }
+ return myCachedConfigFiles;
+ }
+
+ public Project getProject() {
+ return myProject;
+ }
+
+ public void addListener(final ConfigFileListener listener, final Disposable parentDisposable) {
+ myDispatcher.addListener(listener, parentDisposable);
+ }
+
+ public void fireDescriptorChanged(final ConfigFile descriptor) {
+ myDispatcher.getMulticaster().configFileChanged(descriptor);
+ }
+
+
+ public ConfigFileInfoSet getConfiguration() {
+ return myConfiguration;
+ }
+
+ public void dispose() {
+ int i = 0;
+ }
+
+ public void addListener(final ConfigFileListener listener) {
+ myDispatcher.addListener(listener);
+ }
+
+ public void removeListener(final ConfigFileListener listener) {
+ myDispatcher.removeListener(listener);
+ }
+
+ public ConfigFileMetaDataProvider getMetaDataProvider() {
+ return myMetaDataProvider;
+ }
+
+ public void updateDescriptors(final MultiValuesMap<ConfigFileMetaData, ConfigFileInfo> descriptorsMap) {
+ Set<ConfigFile> toDelete = new HashSet<ConfigFile>(myConfigFiles.values());
+ Set<ConfigFile> added = new HashSet<ConfigFile>();
+
+ for (Map.Entry<ConfigFileMetaData, Collection<ConfigFileInfo>> entry : descriptorsMap.entrySet()) {
+ ConfigFileMetaData metaData = entry.getKey();
+ Set<ConfigFileInfo> newDescriptors = new HashSet<ConfigFileInfo>(entry.getValue());
+ final Collection<ConfigFile> oldDescriptors = myConfigFiles.get(metaData);
+ if (oldDescriptors != null) {
+ for (ConfigFile descriptor : oldDescriptors) {
+ if (newDescriptors.contains(descriptor.getInfo())) {
+ newDescriptors.remove(descriptor.getInfo());
+ toDelete.remove(descriptor);
+ }
+ }
+ }
+ for (ConfigFileInfo configuration : newDescriptors) {
+ final ConfigFileImpl configFile = new ConfigFileImpl(this, configuration);
+ Disposer.register(this, configFile);
+ myConfigFiles.put(metaData, configFile);
+ added.add(configFile);
+ }
+ }
+
+ for (ConfigFile descriptor : toDelete) {
+ myConfigFiles.remove(descriptor.getMetaData(), descriptor);
+ Disposer.dispose(descriptor);
+ }
+
+ myCachedConfigFiles = null;
+ for (ConfigFile configFile : added) {
+ myDispatcher.getMulticaster().configFileAdded(configFile);
+ }
+ for (ConfigFile configFile : toDelete) {
+ myDispatcher.getMulticaster().configFileRemoved(configFile);
+ }
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/util/descriptors/impl/ConfigFileFactoryImpl.java b/java/idea-ui/src/com/intellij/util/descriptors/impl/ConfigFileFactoryImpl.java
new file mode 100644
index 0000000..1805567
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/util/descriptors/impl/ConfigFileFactoryImpl.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright 2000-2009 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.util.descriptors.impl;
+
+import com.intellij.ide.IdeBundle;
+import com.intellij.ide.fileTemplates.FileTemplate;
+import com.intellij.ide.fileTemplates.FileTemplateManager;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.util.FileContentUtil;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.Messages;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.vfs.LocalFileSystem;
+import com.intellij.openapi.vfs.VfsUtil;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.util.descriptors.*;
+import org.jetbrains.annotations.Nullable;
+
+import java.io.File;
+import java.io.IOException;
+
+/**
+ * @author nik
+ */
+public class ConfigFileFactoryImpl extends ConfigFileFactory {
+ private static final Logger LOG = Logger.getInstance("#com.intellij.util.descriptors.impl.ConfigFileFactoryImpl");
+
+ public ConfigFileMetaDataProvider createMetaDataProvider(final ConfigFileMetaData... metaDatas) {
+ return new ConfigFileMetaDataRegistryImpl(metaDatas);
+ }
+
+ public ConfigFileMetaDataRegistry createMetaDataRegistry() {
+ return new ConfigFileMetaDataRegistryImpl();
+ }
+
+ public ConfigFileInfoSet createConfigFileInfoSet(final ConfigFileMetaDataProvider metaDataProvider) {
+ return new ConfigFileInfoSetImpl(metaDataProvider);
+ }
+
+ public ConfigFileContainer createConfigFileContainer(final Project project, final ConfigFileMetaDataProvider metaDataProvider,
+ final ConfigFileInfoSet configuration) {
+ return new ConfigFileContainerImpl(project, metaDataProvider, (ConfigFileInfoSetImpl)configuration);
+ }
+
+ private static String getText(final String templateName) throws IOException {
+ final FileTemplateManager templateManager = FileTemplateManager.getInstance();
+ final FileTemplate template = templateManager.getJ2eeTemplate(templateName);
+ if (template == null) {
+ return "";
+ }
+ return template.getText(templateManager.getDefaultProperties());
+ }
+
+ @Nullable
+ public VirtualFile createFile(@Nullable Project project, String url, ConfigFileVersion version, final boolean forceNew) {
+ return createFileFromTemplate(project, url, version.getTemplateName(), forceNew);
+ }
+
+ @Nullable
+ private VirtualFile createFileFromTemplate(@Nullable final Project project, String url, final String templateName, final boolean forceNew) {
+ final LocalFileSystem fileSystem = LocalFileSystem.getInstance();
+ final File file = new File(VfsUtil.urlToPath(url));
+ VirtualFile existingFile = fileSystem.refreshAndFindFileByIoFile(file);
+ if (existingFile != null) {
+ existingFile.refresh(false, false);
+ if (!existingFile.isValid()) {
+ existingFile = null;
+ }
+ }
+
+ if (existingFile != null && !forceNew) {
+ return existingFile;
+ }
+ try {
+ String text = getText(templateName);
+ final VirtualFile childData;
+ if (existingFile == null || existingFile.isDirectory()) {
+ final VirtualFile virtualFile;
+ if (!FileUtil.createParentDirs(file) ||
+ (virtualFile = fileSystem.refreshAndFindFileByIoFile(file.getParentFile())) == null) {
+ throw new IOException(IdeBundle.message("error.message.unable.to.create.file", file.getPath()));
+ }
+ childData = virtualFile.createChildData(this, file.getName());
+ }
+ else {
+ childData = existingFile;
+ }
+ FileContentUtil.setFileText(project, childData, text);
+ return childData;
+ }
+ catch (final IOException e) {
+ LOG.info(e);
+ ApplicationManager.getApplication().invokeLater(new Runnable() {
+ public void run() {
+ Messages.showErrorDialog(IdeBundle.message("message.text.error.creating.deployment.descriptor", e.getLocalizedMessage()),
+ IdeBundle.message("message.text.creating.deployment.descriptor"));
+ }
+ });
+ }
+ return null;
+ }
+
+ public ConfigFileContainer createSingleFileContainer(Project project, ConfigFileMetaData metaData) {
+ final ConfigFileMetaDataProvider metaDataProvider = createMetaDataProvider(metaData);
+ return createConfigFileContainer(project, metaDataProvider, createConfigFileInfoSet(metaDataProvider));
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/util/descriptors/impl/ConfigFileImpl.java b/java/idea-ui/src/com/intellij/util/descriptors/impl/ConfigFileImpl.java
new file mode 100644
index 0000000..45edd4a
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/util/descriptors/impl/ConfigFileImpl.java
@@ -0,0 +1,133 @@
+package com.intellij.util.descriptors.impl;
+
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.openapi.vfs.pointers.VirtualFilePointer;
+import com.intellij.openapi.vfs.pointers.VirtualFilePointerListener;
+import com.intellij.openapi.vfs.pointers.VirtualFilePointerManager;
+import com.intellij.psi.PsiFile;
+import com.intellij.psi.PsiManager;
+import com.intellij.psi.xml.XmlDocument;
+import com.intellij.psi.xml.XmlFile;
+import com.intellij.util.descriptors.ConfigFile;
+import com.intellij.util.descriptors.ConfigFileInfo;
+import com.intellij.util.descriptors.ConfigFileMetaData;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.lang.ref.Reference;
+import java.lang.ref.SoftReference;
+
+/**
+ * @author nik
+ */
+public class ConfigFileImpl implements ConfigFile {
+ @NotNull private ConfigFileInfo myInfo;
+ private final VirtualFilePointer myFilePointer;
+ private volatile Reference<PsiFile> myPsiFile;
+ private final ConfigFileContainerImpl myContainer;
+ private final Project myProject;
+ private long myModificationCount;
+
+ public ConfigFileImpl(@NotNull final ConfigFileContainerImpl container, @NotNull final ConfigFileInfo configuration) {
+ myContainer = container;
+ myInfo = configuration;
+ final VirtualFilePointerManager pointerManager = VirtualFilePointerManager.getInstance();
+ myFilePointer = pointerManager.create(configuration.getUrl(), this, new VirtualFilePointerListener() {
+ @Override
+ public void beforeValidityChanged(@NotNull final VirtualFilePointer[] pointers) {
+ }
+
+ @Override
+ public void validityChanged(@NotNull final VirtualFilePointer[] pointers) {
+ myPsiFile = null;
+ onChange();
+ }
+ });
+ onChange();
+ myProject = myContainer.getProject();
+ }
+
+ private void onChange() {
+ myModificationCount++;
+ myContainer.fireDescriptorChanged(this);
+ }
+
+ @Override
+ public String getUrl() {
+ return myFilePointer.getUrl();
+ }
+
+ public void setInfo(@NotNull final ConfigFileInfo info) {
+ myInfo = info;
+ }
+
+ @Override
+ @Nullable
+ public VirtualFile getVirtualFile() {
+ return myFilePointer.isValid() ? myFilePointer.getFile() : null;
+ }
+
+ @Override
+ @Nullable
+ public PsiFile getPsiFile() {
+ Reference<PsiFile> ref = myPsiFile;
+ PsiFile psiFile = ref == null ? null : ref.get();
+
+ if (psiFile != null && psiFile.isValid()) {
+ return psiFile;
+ }
+
+ VirtualFile virtualFile = getVirtualFile();
+ if (virtualFile == null || !virtualFile.isValid()) return null;
+
+ psiFile = PsiManager.getInstance(myProject).findFile(virtualFile);
+
+ myPsiFile = new SoftReference<PsiFile>(psiFile);
+
+ return psiFile;
+ }
+
+ @Override
+ @Nullable
+ public XmlFile getXmlFile() {
+ final PsiFile file = getPsiFile();
+ return file instanceof XmlFile ? (XmlFile)file : null;
+ }
+
+ @Override
+ public void dispose() {
+ }
+
+ @Override
+ @NotNull
+ public ConfigFileInfo getInfo() {
+ return myInfo;
+ }
+
+ @Override
+ public boolean isValid() {
+ final PsiFile psiFile = getPsiFile();
+ if (psiFile == null || !psiFile.isValid()) {
+ return false;
+ }
+ if (psiFile instanceof XmlFile) {
+ final XmlDocument document = ((XmlFile)psiFile).getDocument();
+ return document != null && document.getRootTag() != null;
+ }
+ return true;
+ }
+
+
+ @Override
+ @NotNull
+ public ConfigFileMetaData getMetaData() {
+ return myInfo.getMetaData();
+ }
+
+
+ @Override
+ public long getModificationCount() {
+ return myModificationCount;
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/util/descriptors/impl/ConfigFileInfoSetImpl.java b/java/idea-ui/src/com/intellij/util/descriptors/impl/ConfigFileInfoSetImpl.java
new file mode 100644
index 0000000..d86bbe5
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/util/descriptors/impl/ConfigFileInfoSetImpl.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright 2000-2009 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.util.descriptors.impl;
+
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.util.Comparing;
+import com.intellij.openapi.util.InvalidDataException;
+import com.intellij.openapi.util.MultiValuesMap;
+import com.intellij.openapi.util.WriteExternalException;
+import com.intellij.util.descriptors.*;
+import org.jdom.Element;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.*;
+
+/**
+ * @author nik
+ */
+public class ConfigFileInfoSetImpl implements ConfigFileInfoSet {
+ private static final Logger LOG = Logger.getInstance("#com.intellij.util.descriptors.impl.ConfigFileInfoSetImpl");
+ @NonNls private static final String ELEMENT_NAME = "deploymentDescriptor";
+ @NonNls private static final String ID_ATTRIBUTE = "name";
+ @NonNls private static final String URL_ATTRIBUTE = "url";
+ private final MultiValuesMap<ConfigFileMetaData, ConfigFileInfo> myConfigFiles = new MultiValuesMap<ConfigFileMetaData, ConfigFileInfo>();
+ private @Nullable ConfigFileContainerImpl myContainer;
+ private final ConfigFileMetaDataProvider myMetaDataProvider;
+
+ public ConfigFileInfoSetImpl(final ConfigFileMetaDataProvider metaDataProvider) {
+ myMetaDataProvider = metaDataProvider;
+ }
+
+ public void addConfigFile(ConfigFileInfo descriptor) {
+ myConfigFiles.put(descriptor.getMetaData(), descriptor);
+ onChange();
+ }
+
+ public void addConfigFile(final ConfigFileMetaData metaData, final String url) {
+ addConfigFile(new ConfigFileInfo(metaData, url));
+ }
+
+ public void removeConfigFile(ConfigFileInfo descriptor) {
+ myConfigFiles.remove(descriptor.getMetaData(), descriptor);
+ onChange();
+ }
+
+ public void replaceConfigFile(final ConfigFileMetaData metaData, final String newUrl) {
+ myConfigFiles.removeAll(metaData);
+ addConfigFile(new ConfigFileInfo(metaData, newUrl));
+ }
+
+ public ConfigFileInfo updateConfigFile(ConfigFile configFile) {
+ myConfigFiles.remove(configFile.getMetaData(), configFile.getInfo());
+ ConfigFileInfo info = new ConfigFileInfo(configFile.getMetaData(), configFile.getUrl());
+ myConfigFiles.put(info.getMetaData(), info);
+ ((ConfigFileImpl)configFile).setInfo(info);
+ return info;
+ }
+
+ public void removeConfigFiles(final ConfigFileMetaData... metaData) {
+ for (ConfigFileMetaData data : metaData) {
+ myConfigFiles.removeAll(data);
+ }
+ onChange();
+ }
+
+ @Nullable
+ public ConfigFileInfo getConfigFileInfo(ConfigFileMetaData metaData) {
+ final Collection<ConfigFileInfo> descriptors = myConfigFiles.get(metaData);
+ if (descriptors == null || descriptors.isEmpty()) {
+ return null;
+ }
+ return descriptors.iterator().next();
+ }
+
+ public ConfigFileInfo[] getConfigFileInfos() {
+ final Collection<ConfigFileInfo> configurations = myConfigFiles.values();
+ return configurations.toArray(new ConfigFileInfo[configurations.size()]);
+ }
+
+ public void setConfigFileInfos(final Collection<ConfigFileInfo> descriptors) {
+ myConfigFiles.clear();
+ for (ConfigFileInfo descriptor : descriptors) {
+ myConfigFiles.put(descriptor.getMetaData(), descriptor);
+ }
+ onChange();
+ }
+
+ private void onChange() {
+ if (myContainer != null) {
+ myContainer.updateDescriptors(myConfigFiles);
+ }
+ }
+
+
+ public ConfigFileMetaDataProvider getMetaDataProvider() {
+ return myMetaDataProvider;
+ }
+
+ public void readExternal(final Element element) throws InvalidDataException {
+ myConfigFiles.clear();
+ List<Element> children = element.getChildren(ELEMENT_NAME);
+ for (Element child : children) {
+ final String id = child.getAttributeValue(ID_ATTRIBUTE);
+ if (id != null) {
+ final ConfigFileMetaData metaData = myMetaDataProvider.findMetaData(id);
+ if (metaData != null) {
+ final String url = child.getAttributeValue(URL_ATTRIBUTE);
+ myConfigFiles.put(metaData, new ConfigFileInfo(metaData, url));
+ }
+ }
+ }
+ onChange();
+ }
+
+ @SuppressWarnings({"HardCodedStringLiteral"})
+ public void writeExternal(final Element element) throws WriteExternalException {
+ final TreeSet<ConfigFileInfo> sortedConfigFiles = new TreeSet<ConfigFileInfo>(new Comparator<ConfigFileInfo>() {
+ public int compare(final ConfigFileInfo o1, final ConfigFileInfo o2) {
+ final int id = Comparing.compare(o1.getMetaData().getId(), o2.getMetaData().getId());
+ return id != 0? id : Comparing.compare(o1.getUrl(), o2.getUrl());
+ }
+ });
+ sortedConfigFiles.addAll(myConfigFiles.collectValues());
+ for (ConfigFileInfo configuration : sortedConfigFiles) {
+ final Element child = new Element(ELEMENT_NAME);
+ final ConfigFileMetaData metaData = configuration.getMetaData();
+ child.setAttribute(ID_ATTRIBUTE, metaData.getId());
+ child.setAttribute(URL_ATTRIBUTE, configuration.getUrl());
+ element.addContent(child);
+ }
+ }
+
+ public void setContainer(@NotNull ConfigFileContainerImpl container) {
+ LOG.assertTrue(myContainer == null);
+ myContainer = container;
+ myContainer.updateDescriptors(myConfigFiles);
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/util/descriptors/impl/ConfigFileMetaDataRegistryImpl.java b/java/idea-ui/src/com/intellij/util/descriptors/impl/ConfigFileMetaDataRegistryImpl.java
new file mode 100644
index 0000000..ea58217
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/util/descriptors/impl/ConfigFileMetaDataRegistryImpl.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2000-2009 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.util.descriptors.impl;
+
+import com.intellij.util.descriptors.ConfigFileMetaData;
+import com.intellij.util.descriptors.ConfigFileMetaDataRegistry;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.List;
+import java.util.ArrayList;
+
+/**
+ * @author nik
+ */
+public class ConfigFileMetaDataRegistryImpl implements ConfigFileMetaDataRegistry {
+ private final List<ConfigFileMetaData> myMetaData = new ArrayList<ConfigFileMetaData>();
+ private final Map<String, ConfigFileMetaData> myId2MetaData = new HashMap<String, ConfigFileMetaData>();
+ private ConfigFileMetaData[] myCachedMetaData;
+
+ public ConfigFileMetaDataRegistryImpl() {
+ }
+
+ public ConfigFileMetaDataRegistryImpl(ConfigFileMetaData[] metaDatas) {
+ for (ConfigFileMetaData metaData : metaDatas) {
+ registerMetaData(metaData);
+ }
+ }
+
+ @NotNull
+ public ConfigFileMetaData[] getMetaData() {
+ if (myCachedMetaData == null) {
+ myCachedMetaData = myMetaData.toArray(new ConfigFileMetaData[myMetaData.size()]);
+ }
+ return myCachedMetaData;
+ }
+
+ @Nullable
+ public ConfigFileMetaData findMetaData(@NonNls @NotNull final String id) {
+ return myId2MetaData.get(id);
+ }
+
+ public void registerMetaData(@NotNull final ConfigFileMetaData... metaData) {
+ for (ConfigFileMetaData data : metaData) {
+ myMetaData.add(data);
+ myId2MetaData.put(data.getId(), data);
+ }
+ myCachedMetaData = null;
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/util/ui/classpath/ChooseLibrariesDialogBase.java b/java/idea-ui/src/com/intellij/util/ui/classpath/ChooseLibrariesDialogBase.java
new file mode 100644
index 0000000..c567f26
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/util/ui/classpath/ChooseLibrariesDialogBase.java
@@ -0,0 +1,422 @@
+/*
+ * Copyright 2000-2011 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.util.ui.classpath;
+
+import com.intellij.ide.CommonActionsManager;
+import com.intellij.ide.DefaultTreeExpander;
+import com.intellij.ide.TreeExpander;
+import com.intellij.ide.projectView.PresentationData;
+import com.intellij.ide.util.treeView.AbstractTreeBuilder;
+import com.intellij.ide.util.treeView.AbstractTreeStructure;
+import com.intellij.ide.util.treeView.NodeDescriptor;
+import com.intellij.openapi.actionSystem.ActionManager;
+import com.intellij.openapi.actionSystem.ActionPlaces;
+import com.intellij.openapi.actionSystem.DefaultActionGroup;
+import com.intellij.openapi.application.Application;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.module.ModuleManager;
+import com.intellij.openapi.module.ModuleType;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.project.ProjectManager;
+import com.intellij.openapi.roots.LibraryOrderEntry;
+import com.intellij.openapi.roots.ModuleRootManager;
+import com.intellij.openapi.roots.OrderEntry;
+import com.intellij.openapi.roots.impl.libraries.LibraryTableImplUtil;
+import com.intellij.openapi.roots.libraries.Library;
+import com.intellij.openapi.roots.libraries.LibraryTable;
+import com.intellij.openapi.roots.libraries.LibraryTablesRegistrar;
+import com.intellij.openapi.roots.ui.CellAppearanceEx;
+import com.intellij.openapi.roots.ui.OrderEntryAppearanceService;
+import com.intellij.openapi.ui.DialogWrapper;
+import com.intellij.openapi.util.Disposer;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.ui.DoubleClickListener;
+import com.intellij.ui.ScrollPaneFactory;
+import com.intellij.ui.SimpleColoredComponent;
+import com.intellij.ui.SimpleTextAttributes;
+import com.intellij.ui.treeStructure.SimpleNode;
+import com.intellij.ui.treeStructure.SimpleTree;
+import com.intellij.ui.treeStructure.SimpleTreeBuilder;
+import com.intellij.ui.treeStructure.WeightBasedComparator;
+import com.intellij.util.CommonProcessors;
+import com.intellij.util.PlatformIcons;
+import com.intellij.util.Processor;
+import com.intellij.util.ui.UIUtil;
+import gnu.trove.THashMap;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import javax.swing.event.TreeSelectionEvent;
+import javax.swing.event.TreeSelectionListener;
+import javax.swing.tree.DefaultMutableTreeNode;
+import javax.swing.tree.DefaultTreeModel;
+import java.awt.*;
+import java.awt.event.KeyEvent;
+import java.awt.event.MouseEvent;
+import java.util.*;
+import java.util.List;
+
+/**
+ * @author Gregory.Shrago
+ */
+
+public abstract class ChooseLibrariesDialogBase extends DialogWrapper {
+ private final SimpleTree myTree = new SimpleTree();
+ private AbstractTreeBuilder myBuilder;
+ private List<Library> myResult;
+ private final Map<Object, Object> myParentsMap = new THashMap<Object, Object>();
+
+ protected ChooseLibrariesDialogBase(final JComponent parentComponent, final String title) {
+ super(parentComponent, false);
+ setTitle(title);
+ }
+
+ protected ChooseLibrariesDialogBase(Project project, String title) {
+ super(project, false);
+ setTitle(title);
+ }
+
+ @Override
+ protected void init() {
+ super.init();
+ updateOKAction();
+ }
+
+ private static String notEmpty(String nodeText) {
+ return StringUtil.isNotEmpty(nodeText) ? nodeText : "<unnamed>";
+ }
+
+ @Override
+ protected String getDimensionServiceKey() {
+ return "#com.intellij.util.ui.classpath.ChooseLibrariesDialog";
+ }
+
+ protected int getLibraryTableWeight(@NotNull LibraryTable libraryTable) {
+ return 0;
+ }
+
+ protected boolean isAutoExpandLibraryTable(@NotNull LibraryTable libraryTable) {
+ return false;
+ }
+
+ @Override
+ protected void doOKAction() {
+ processSelection(new CommonProcessors.CollectProcessor<Library>(myResult = new ArrayList<Library>()));
+ super.doOKAction();
+ }
+
+ private void updateOKAction() {
+ setOKActionEnabled(!processSelection(new CommonProcessors.FindFirstProcessor<Library>()));
+ }
+
+ @Override
+ public JComponent getPreferredFocusedComponent() {
+ return myTree;
+ }
+
+ @NotNull
+ public List<Library> getSelectedLibraries() {
+ return myResult == null? Collections.<Library>emptyList() : myResult;
+ }
+
+ protected void queueUpdateAndSelect(@NotNull final Library library) {
+ myBuilder.queueUpdate().doWhenDone(new Runnable() {
+ @Override
+ public void run() {
+ myBuilder.select(library);
+ }
+ });
+ }
+
+ private boolean processSelection(final Processor<Library> processor) {
+ for (Object element : myBuilder.getSelectedElements()) {
+ if (element instanceof Library) {
+ if (!processor.process((Library)element)) return false;
+ }
+ }
+ return true;
+ }
+
+ protected boolean acceptsElement(final Object element) {
+ return true;
+ }
+
+ @Override
+ protected JComponent createNorthPanel() {
+ final DefaultActionGroup group = new DefaultActionGroup();
+ final TreeExpander expander = new DefaultTreeExpander(myTree);
+ final CommonActionsManager actionsManager = CommonActionsManager.getInstance();
+ group.add(actionsManager.createExpandAllAction(expander, myTree));
+ group.add(actionsManager.createCollapseAllAction(expander, myTree));
+ final JComponent component = ActionManager.getInstance().createActionToolbar(ActionPlaces.PROJECT_VIEW_TOOLBAR, group, true).getComponent();
+ component.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createMatteBorder(0, 0, 1, 0, Color.darkGray), component.getBorder()));
+ return component;
+ }
+
+ @Override
+ @Nullable
+ protected JComponent createCenterPanel() {
+ myBuilder = new SimpleTreeBuilder(myTree, new DefaultTreeModel(new DefaultMutableTreeNode()),
+ new MyStructure(getProject()),
+ WeightBasedComparator.FULL_INSTANCE);
+ myBuilder.initRootNode();
+
+ myTree.setDragEnabled(false);
+
+ myTree.setShowsRootHandles(true);
+ UIUtil.setLineStyleAngled(myTree);
+
+ myTree.setRootVisible(false);
+ myTree.addTreeSelectionListener(new TreeSelectionListener() {
+ @Override
+ public void valueChanged(final TreeSelectionEvent e) {
+ updateOKAction();
+ }
+ });
+ new DoubleClickListener() {
+ @Override
+ protected boolean onDoubleClick(MouseEvent e) {
+ if (isOKActionEnabled()) {
+ doOKAction();
+ return true;
+ }
+ return false;
+ }
+ }.installOn(myTree);
+
+ myTree.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), "ENTER");
+ myTree.getActionMap().put("ENTER", getOKAction());
+ final JScrollPane pane = ScrollPaneFactory.createScrollPane(myTree);
+ pane.setPreferredSize(new Dimension(300, 80));
+ return pane;
+ }
+
+ @NotNull
+ protected Project getProject() {
+ return ProjectManager.getInstance().getDefaultProject();
+ }
+
+ protected LibrariesTreeNodeBase<Library> createLibraryDescriptor(NodeDescriptor parentDescriptor, Library library) {
+ return new LibraryDescriptor(getProject(), parentDescriptor, library);
+ }
+
+ protected void collectChildren(Object element, final List<Object> result) {
+ if (element instanceof Application) {
+ Collections.addAll(result, ProjectManager.getInstance().getOpenProjects());
+ final LibraryTablesRegistrar instance = LibraryTablesRegistrar.getInstance();
+ result.add(instance.getLibraryTable()); //1
+ result.addAll(instance.getCustomLibraryTables()); //2
+ }
+ else if (element instanceof Project) {
+ Collections.addAll(result, ModuleManager.getInstance((Project)element).getModules());
+ result.add(LibraryTablesRegistrar.getInstance().getLibraryTable((Project)element));
+ }
+ else if (element instanceof LibraryTable) {
+ Collections.addAll(result, ((LibraryTable)element).getLibraries());
+ }
+ else if (element instanceof Module) {
+ for (OrderEntry entry : ModuleRootManager.getInstance((Module)element).getOrderEntries()) {
+ if (entry instanceof LibraryOrderEntry) {
+ final LibraryOrderEntry libraryOrderEntry = (LibraryOrderEntry)entry;
+ if (LibraryTableImplUtil.MODULE_LEVEL.equals(libraryOrderEntry.getLibraryLevel())) {
+ final Library library = libraryOrderEntry.getLibrary();
+ result.add(library);
+ }
+ }
+ }
+ }
+ }
+
+ @Override
+ protected void dispose() {
+ Disposer.dispose(myBuilder);
+ super.dispose();
+ }
+
+ protected static class LibrariesTreeNodeBase<T> extends SimpleNode {
+ private final T myElement;
+
+ protected LibrariesTreeNodeBase(Project project, NodeDescriptor parentDescriptor, T element) {
+ super(project, parentDescriptor);
+ myElement = element;
+ }
+
+ @Override
+ public T getElement() {
+ return myElement;
+ }
+
+ @Override
+ public SimpleNode[] getChildren() {
+ return NO_CHILDREN;
+ }
+
+ @Override
+ public int getWeight() {
+ return 0;
+ }
+
+ @NotNull
+ @Override
+ public Object[] getEqualityObjects() {
+ return new Object[] {myElement};
+ }
+
+ @Override
+ protected void update(PresentationData presentation) {
+ //todo[nik] this is workaround for bug in getTemplatePresentation().setIcons()
+ presentation.setIcon(getTemplatePresentation().getIcon(false));
+ }
+ }
+
+ private static class RootDescriptor extends LibrariesTreeNodeBase<Object> {
+ protected RootDescriptor(final Project project) {
+ super(project, null, ApplicationManager.getApplication());
+ }
+ }
+
+ private static class ProjectDescriptor extends LibrariesTreeNodeBase<Project> {
+ protected ProjectDescriptor(final Project project, final Project element) {
+ super(project, null, element);
+ getTemplatePresentation().setIcon(PlatformIcons.PROJECT_ICON);
+ getTemplatePresentation().addText(notEmpty(getElement().getName()), SimpleTextAttributes.REGULAR_ATTRIBUTES);
+ }
+ }
+
+ private static class ModuleDescriptor extends LibrariesTreeNodeBase<Module> {
+ protected ModuleDescriptor(final Project project, final NodeDescriptor parentDescriptor, final Module element) {
+ super(project, parentDescriptor, element);
+ final PresentationData templatePresentation = getTemplatePresentation();
+ templatePresentation.setIcon(ModuleType.get(element).getIcon());
+ templatePresentation.addText(notEmpty(element.getName()), SimpleTextAttributes.REGULAR_ATTRIBUTES);
+ }
+
+ @Override
+ public int getWeight() {
+ return 1;
+ }
+ }
+
+ private static class LibraryDescriptor extends LibrariesTreeNodeBase<Library> {
+ protected LibraryDescriptor(final Project project, final NodeDescriptor parentDescriptor, final Library element) {
+ super(project, parentDescriptor, element);
+ final CellAppearanceEx appearance = OrderEntryAppearanceService.getInstance().forLibrary(project, element, false);
+ final SimpleColoredComponent coloredComponent = new SimpleColoredComponent();
+ appearance.customize(coloredComponent);
+ final PresentationData templatePresentation = getTemplatePresentation();
+ templatePresentation.setIcon(coloredComponent.getIcon());
+ templatePresentation.addText(notEmpty(appearance.getText()), SimpleTextAttributes.REGULAR_ATTRIBUTES);
+ }
+ }
+
+ private static class LibraryTableDescriptor extends LibrariesTreeNodeBase<LibraryTable> {
+ private final int myWeight;
+ private boolean myAutoExpand;
+
+ protected LibraryTableDescriptor(final Project project,
+ final NodeDescriptor parentDescriptor,
+ final LibraryTable table,
+ final int weight,
+ boolean autoExpand) {
+ super(project, parentDescriptor, table);
+ myWeight = weight;
+ myAutoExpand = autoExpand;
+ getTemplatePresentation().setIcon(PlatformIcons.LIBRARY_ICON);
+ final String nodeText = table.getPresentation().getDisplayName(true);
+ getTemplatePresentation().addText(notEmpty(nodeText), SimpleTextAttributes.REGULAR_ATTRIBUTES);
+ }
+
+ @Override
+ public boolean isAutoExpandNode() {
+ return myAutoExpand;
+ }
+
+ @Override
+ public int getWeight() {
+ return myWeight;
+ }
+ }
+
+ public boolean isEmpty() {
+ List<Object> children = new ArrayList<Object>();
+ collectChildren(myBuilder.getTreeStructure().getRootElement(), children);
+ return children.isEmpty();
+ }
+
+ private class MyStructure extends AbstractTreeStructure {
+ private final Project myProject;
+
+ public MyStructure(Project project) {
+ myProject = project;
+ }
+
+ @Override
+ public Object getRootElement() {
+ return ApplicationManager.getApplication();
+ }
+
+ @Override
+ public Object[] getChildElements(Object element) {
+ final List<Object> result = new ArrayList<Object>();
+ collectChildren(element, result);
+ final Iterator<Object> it = result.iterator();
+ while (it.hasNext()) {
+ if (!acceptsElement(it.next())) it.remove();
+ }
+ for (Object o : result) {
+ myParentsMap.put(o, element);
+ }
+ return result.toArray();
+ }
+
+ @Override
+ public Object getParentElement(Object element) {
+ if (element instanceof Application) return null;
+ if (element instanceof Project) return ApplicationManager.getApplication();
+ if (element instanceof Module) return ((Module)element).getProject();
+ if (element instanceof LibraryTable) return myParentsMap.get(element);
+ if (element instanceof Library) return myParentsMap.get(element);
+ throw new AssertionError();
+ }
+
+ @NotNull
+ @Override
+ public NodeDescriptor createDescriptor(Object element, NodeDescriptor parentDescriptor) {
+ if (element instanceof Application) return new RootDescriptor(myProject);
+ if (element instanceof Project) return new ProjectDescriptor(myProject, (Project)element);
+ if (element instanceof Module) return new ModuleDescriptor(myProject, parentDescriptor, (Module)element);
+ if (element instanceof LibraryTable) {
+ final LibraryTable libraryTable = (LibraryTable)element;
+ return new LibraryTableDescriptor(myProject, parentDescriptor, libraryTable,
+ getLibraryTableWeight(libraryTable),
+ isAutoExpandLibraryTable(libraryTable));
+ }
+ if (element instanceof Library) return createLibraryDescriptor(parentDescriptor, (Library)element);
+ throw new AssertionError();
+ }
+
+ @Override
+ public void commit() {
+ }
+
+ @Override
+ public boolean hasSomethingToCommit() {
+ return false;
+ }
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/util/ui/classpath/ChooseLibrariesFromTablesDialog.java b/java/idea-ui/src/com/intellij/util/ui/classpath/ChooseLibrariesFromTablesDialog.java
new file mode 100644
index 0000000..f60dafc
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/util/ui/classpath/ChooseLibrariesFromTablesDialog.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright 2000-2011 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.util.ui.classpath;
+
+import com.intellij.openapi.application.Application;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.roots.impl.libraries.LibraryTableImplUtil;
+import com.intellij.openapi.roots.libraries.Library;
+import com.intellij.openapi.roots.libraries.LibraryTable;
+import com.intellij.openapi.roots.libraries.LibraryTablesRegistrar;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * @author nik
+ */
+public class ChooseLibrariesFromTablesDialog extends ChooseLibrariesDialogBase {
+ private @Nullable final Project myProject;
+ private final boolean myShowCustomLibraryTables;
+
+ protected ChooseLibrariesFromTablesDialog(@NotNull String title, @NotNull Project project, final boolean showCustomLibraryTables) {
+ super(project, title);
+ myShowCustomLibraryTables = showCustomLibraryTables;
+ myProject = project;
+ }
+
+ protected ChooseLibrariesFromTablesDialog(@NotNull JComponent parentComponent,
+ @NotNull String title,
+ @Nullable Project project,
+ final boolean showCustomLibraryTables) {
+ super(parentComponent, title);
+ myShowCustomLibraryTables = showCustomLibraryTables;
+ myProject = project;
+ }
+
+ public static ChooseLibrariesFromTablesDialog createDialog(@NotNull String title,
+ @NotNull Project project,
+ final boolean showCustomLibraryTables) {
+ final ChooseLibrariesFromTablesDialog dialog = new ChooseLibrariesFromTablesDialog(title, project, showCustomLibraryTables);
+ dialog.init();
+ return dialog;
+ }
+
+ @NotNull
+ @Override
+ protected Project getProject() {
+ if (myProject != null) {
+ return myProject;
+ }
+ return super.getProject();
+ }
+
+ @Override
+ protected JComponent createNorthPanel() {
+ return null;
+ }
+
+ @Override
+ protected void collectChildren(Object element, List<Object> result) {
+ if (element instanceof Application) {
+ for (LibraryTable table : getLibraryTables(myProject, myShowCustomLibraryTables)) {
+ if (hasLibraries(table)) {
+ result.add(table);
+ }
+ }
+ }
+ else if (element instanceof LibraryTable) {
+ Collections.addAll(result, getLibraries((LibraryTable)element));
+ }
+ }
+
+ public static List<LibraryTable> getLibraryTables(final Project project, final boolean showCustomLibraryTables) {
+ final List<LibraryTable> tables = new ArrayList<LibraryTable>();
+ final LibraryTablesRegistrar registrar = LibraryTablesRegistrar.getInstance();
+ if (project != null) {
+ tables.add(registrar.getLibraryTable(project));
+ }
+ tables.add(registrar.getLibraryTable());
+ if (showCustomLibraryTables) {
+ for (LibraryTable table : registrar.getCustomLibraryTables()) {
+ tables.add(table);
+ }
+ }
+ return tables;
+ }
+
+ private boolean hasLibraries(LibraryTable table) {
+ final Library[] libraries = getLibraries(table);
+ for (Library library : libraries) {
+ if (acceptsElement(library)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ protected int getLibraryTableWeight(@NotNull LibraryTable libraryTable) {
+ if (libraryTable.getTableLevel().equals(LibraryTableImplUtil.MODULE_LEVEL)) return 0;
+ if (isProjectLibraryTable(libraryTable)) return 1;
+ if (isApplicationLibraryTable(libraryTable)) return 2;
+ return 3;
+ }
+
+ private static boolean isApplicationLibraryTable(LibraryTable libraryTable) {
+ return libraryTable.equals(LibraryTablesRegistrar.getInstance().getLibraryTable());
+ }
+
+ private boolean isProjectLibraryTable(LibraryTable libraryTable) {
+ final LibraryTablesRegistrar registrar = LibraryTablesRegistrar.getInstance();
+ return myProject != null && libraryTable.equals(registrar.getLibraryTable(myProject));
+ }
+
+ @Override
+ protected boolean isAutoExpandLibraryTable(@NotNull LibraryTable libraryTable) {
+ return isApplicationLibraryTable(libraryTable) || isProjectLibraryTable(libraryTable);
+ }
+
+ @NotNull
+ protected Library[] getLibraries(@NotNull LibraryTable table) {
+ return table.getLibraries();
+ }
+}