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="&amp;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 &amp;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 &amp;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 &amp;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="&amp;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 &amp;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="&amp;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="&amp;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="&amp;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="&lt;html&gt;{} jars will be downloaded into &lt;b&gt;lib&lt;/b&gt; directory&lt;br&gt; Project level library &lt;b&gt;spring&lt;/b&gt; will be created&lt;/html&gt;"/>
+                            </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="&amp;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="&lt;html&gt;&#13;&#10;  &lt;head&gt;&#13;&#10;&#13;&#10;  &lt;/head&gt;&#13;&#10;  &lt;body&gt;&#13;&#10;    &lt;p style=&quot;margin-top: 0&quot;&gt;&#13;&#10;      &#13;&#10;    &lt;/p&gt;&#13;&#10;  &lt;/body&gt;&#13;&#10;&lt;/html&gt;&#13;&#10;"/>
+                </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="&amp;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>
+ * &lt;extensions defaultExtensionNs="com.intellij"&gt;
+ * &nbsp;&nbsp;&lt;projectStructureDetector implementation="qualified-class-name"/&gt;
+ * &lt;/extensions&gt;
+ * </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>&nbsp;&nbsp;&nbsp;&nbsp;";
+        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 &amp;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="&amp;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="&amp;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="&amp;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>&nbsp;&nbsp;");
+      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="&amp;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="&amp;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="&amp;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();
+  }
+}