com.intellij.ide.projectView.impl.ProjectViewImpl.java Source code

Java tutorial

Introduction

Here is the source code for com.intellij.ide.projectView.impl.ProjectViewImpl.java

Source

/*
 * Copyright 2000-2013 JetBrains s.r.o.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.intellij.ide.projectView.impl;

import com.intellij.ProjectTopics;
import com.intellij.history.LocalHistory;
import com.intellij.history.LocalHistoryAction;
import com.intellij.icons.AllIcons;
import com.intellij.ide.*;
import com.intellij.ide.impl.ProjectViewSelectInTarget;
import com.intellij.ide.projectView.HelpID;
import com.intellij.ide.projectView.ProjectView;
import com.intellij.ide.projectView.ProjectViewNode;
import com.intellij.ide.projectView.impl.nodes.*;
import com.intellij.ide.scopeView.ScopeViewPane;
import com.intellij.ide.ui.SplitterProportionsDataImpl;
import com.intellij.ide.ui.UISettings;
import com.intellij.ide.util.DeleteHandler;
import com.intellij.ide.util.DirectoryChooserUtil;
import com.intellij.ide.util.EditorHelper;
import com.intellij.ide.util.treeView.AbstractTreeBuilder;
import com.intellij.ide.util.treeView.AbstractTreeNode;
import com.intellij.ide.util.treeView.NodeDescriptor;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.actionSystem.*;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.command.CommandProcessor;
import com.intellij.openapi.components.*;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.extensions.Extensions;
import com.intellij.openapi.fileEditor.FileEditor;
import com.intellij.openapi.fileEditor.FileEditorManager;
import com.intellij.openapi.fileEditor.TextEditor;
import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleUtil;
import com.intellij.openapi.project.DumbAware;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.*;
import com.intellij.openapi.roots.ui.configuration.actions.ModuleDeleteProvider;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.ui.SimpleToolWindowPanel;
import com.intellij.openapi.ui.SplitterProportionsData;
import com.intellij.openapi.ui.popup.PopupChooserBuilder;
import com.intellij.openapi.util.*;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.ArchiveFileSystem;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VfsUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.wm.*;
import com.intellij.openapi.wm.ex.ToolWindowEx;
import com.intellij.openapi.wm.ex.ToolWindowManagerAdapter;
import com.intellij.openapi.wm.ex.ToolWindowManagerEx;
import com.intellij.openapi.wm.impl.content.ToolWindowContentUi;
import com.intellij.psi.*;
import com.intellij.psi.impl.file.PsiPackageHelper;
import com.intellij.psi.util.PsiUtilBase;
import com.intellij.psi.util.PsiUtilCore;
import com.intellij.ui.AutoScrollFromSourceHandler;
import com.intellij.ui.AutoScrollToSourceHandler;
import com.intellij.ui.GuiUtils;
import com.intellij.ui.components.JBList;
import com.intellij.ui.content.*;
import com.intellij.ui.switcher.QuickActionProvider;
import com.intellij.util.ArrayUtil;
import com.intellij.util.IJSwingUtilities;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.messages.MessageBusConnection;
import com.intellij.util.ui.UIUtil;
import com.intellij.util.ui.tree.TreeUtil;
import gnu.trove.THashMap;
import gnu.trove.THashSet;
import org.consulo.psi.PsiPackageSupportProviders;
import org.consulo.vfs.backgroundTask.BackgroundTaskByVfsChangeManager;
import org.consulo.vfs.backgroundTask.BackgroundTaskByVfsChangeTask;
import org.jdom.Attribute;
import org.jdom.Element;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.mustbe.consulo.RequiredDispatchThread;

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;

@State(name = "ProjectView", storages = { @Storage(file = StoragePathMacros.WORKSPACE_FILE) })
public class ProjectViewImpl extends ProjectView
        implements PersistentStateComponent<Element>, Disposable, QuickActionProvider, BusyObject {
    private static final Logger LOG = Logger.getInstance("#com.intellij.ide.projectView.impl.ProjectViewImpl");
    private static final Key<String> ID_KEY = Key.create("pane-id");
    private static final Key<String> SUB_ID_KEY = Key.create("pane-sub-id");
    private final CopyPasteDelegator myCopyPasteDelegator;
    private boolean isInitialized;
    private boolean myExtensionsLoaded = false;
    private final Project myProject;

    // + options
    private final Map<String, Boolean> myFlattenPackages = new THashMap<String, Boolean>();
    private static final boolean ourFlattenPackagesDefaults = false;
    private final Map<String, Boolean> myShowMembers = new THashMap<String, Boolean>();
    private static final boolean ourShowMembersDefaults = false;
    private final Map<String, Boolean> mySortByType = new THashMap<String, Boolean>();
    private static final boolean ourSortByTypeDefaults = false;
    private final Map<String, Boolean> myShowModules = new THashMap<String, Boolean>();
    private static final boolean ourShowModulesDefaults = true;
    private final Map<String, Boolean> myShowLibraryContents = new THashMap<String, Boolean>();
    private static final boolean ourShowLibraryContentsDefaults = true;
    private final Map<String, Boolean> myHideEmptyPackages = new THashMap<String, Boolean>();
    private static final boolean ourHideEmptyPackagesDefaults = true;
    private final Map<String, Boolean> myAbbreviatePackageNames = new THashMap<String, Boolean>();
    private static final boolean ourAbbreviatePackagesDefaults = false;
    private final Map<String, Boolean> myAutoscrollToSource = new THashMap<String, Boolean>();
    private final Map<String, Boolean> myAutoscrollFromSource = new THashMap<String, Boolean>();
    private static final boolean ourAutoscrollFromSourceDefaults = false;
    private static final boolean ourShowStructureDefaults = false;

    private String myCurrentViewId;
    private String myCurrentViewSubId;
    // - options

    private final AutoScrollToSourceHandler myAutoScrollToSourceHandler;
    private final MyAutoScrollFromSourceHandler myAutoScrollFromSourceHandler;

    private final IdeView myIdeView = new MyIdeView();
    private final MyDeletePSIElementProvider myDeletePSIElementProvider = new MyDeletePSIElementProvider();
    private final ModuleDeleteProvider myDeleteModuleProvider = new ModuleDeleteProvider();

    private SimpleToolWindowPanel myPanel;
    private final Map<String, AbstractProjectViewPane> myId2Pane = new LinkedHashMap<String, AbstractProjectViewPane>();
    private final Collection<AbstractProjectViewPane> myUninitializedPanes = new THashSet<AbstractProjectViewPane>();

    static final DataKey<ProjectViewImpl> DATA_KEY = DataKey
            .create("com.intellij.ide.projectView.impl.ProjectViewImpl");

    private DefaultActionGroup myActionGroup;
    private String mySavedPaneId = ProjectViewPane.ID;
    private String mySavedPaneSubId;
    //private static final Icon COMPACT_EMPTY_MIDDLE_PACKAGES_ICON = IconLoader.getIcon("/objectBrowser/compactEmptyPackages.png");
    //private static final Icon HIDE_EMPTY_MIDDLE_PACKAGES_ICON = IconLoader.getIcon("/objectBrowser/hideEmptyPackages.png");
    @NonNls
    private static final String ELEMENT_NAVIGATOR = "navigator";
    @NonNls
    private static final String ELEMENT_PANES = "panes";
    @NonNls
    private static final String ELEMENT_PANE = "pane";
    @NonNls
    private static final String ATTRIBUTE_CURRENT_VIEW = "currentView";
    @NonNls
    private static final String ATTRIBUTE_CURRENT_SUBVIEW = "currentSubView";
    @NonNls
    private static final String ELEMENT_FLATTEN_PACKAGES = "flattenPackages";
    @NonNls
    private static final String ELEMENT_SHOW_MEMBERS = "showMembers";
    @NonNls
    private static final String ELEMENT_SHOW_MODULES = "showModules";
    @NonNls
    private static final String ELEMENT_SHOW_LIBRARY_CONTENTS = "showLibraryContents";
    @NonNls
    private static final String ELEMENT_HIDE_EMPTY_PACKAGES = "hideEmptyPackages";
    @NonNls
    private static final String ELEMENT_ABBREVIATE_PACKAGE_NAMES = "abbreviatePackageNames";
    @NonNls
    private static final String ELEMENT_AUTOSCROLL_TO_SOURCE = "autoscrollToSource";
    @NonNls
    private static final String ELEMENT_AUTOSCROLL_FROM_SOURCE = "autoscrollFromSource";
    @NonNls
    private static final String ELEMENT_SORT_BY_TYPE = "sortByType";
    @NonNls
    private static final String ELEMENT_FOLDERS_ALWAYS_ON_TOP = "foldersAlwaysOnTop";

    private static final String ATTRIBUTE_ID = "id";
    private JPanel myViewContentPanel;
    private static final Comparator<AbstractProjectViewPane> PANE_WEIGHT_COMPARATOR = new Comparator<AbstractProjectViewPane>() {
        @Override
        public int compare(final AbstractProjectViewPane o1, final AbstractProjectViewPane o2) {
            return o1.getWeight() - o2.getWeight();
        }
    };
    private final FileEditorManager myFileEditorManager;
    private final MyPanel myDataProvider;
    private final SplitterProportionsData splitterProportions = new SplitterProportionsDataImpl();
    private final MessageBusConnection myConnection;
    private final Map<String, Element> myUninitializedPaneState = new HashMap<String, Element>();
    private final Map<String, SelectInTarget> mySelectInTargets = new HashMap<String, SelectInTarget>();
    private ContentManager myContentManager;
    private boolean myFoldersAlwaysOnTop = true;

    public ProjectViewImpl(Project project, final FileEditorManager fileEditorManager,
            final ToolWindowManagerEx toolWindowManager) {
        myProject = project;

        constructUi();

        Disposer.register(myProject, this);
        myFileEditorManager = fileEditorManager;

        myConnection = project.getMessageBus().connect();
        myConnection.subscribe(ProjectTopics.PROJECT_ROOTS, new ModuleRootAdapter() {
            @Override
            public void rootsChanged(ModuleRootEvent event) {
                refresh();
            }
        });
        myConnection.subscribe(BackgroundTaskByVfsChangeManager.TOPIC,
                new BackgroundTaskByVfsChangeManager.ListenerAdapter() {
                    @Override
                    public void taskChanged(@NotNull BackgroundTaskByVfsChangeTask task) {
                        refresh();
                    }
                });

        myAutoScrollFromSourceHandler = new MyAutoScrollFromSourceHandler();

        myDataProvider = new MyPanel();
        myDataProvider.add(myPanel, BorderLayout.CENTER);
        myCopyPasteDelegator = new CopyPasteDelegator(myProject, myPanel) {
            @Override
            @NotNull
            protected PsiElement[] getSelectedElements() {
                final AbstractProjectViewPane viewPane = getCurrentProjectViewPane();
                return viewPane == null ? PsiElement.EMPTY_ARRAY : viewPane.getSelectedPSIElements();
            }
        };
        myAutoScrollToSourceHandler = new AutoScrollToSourceHandler() {
            @Override
            protected boolean isAutoScrollMode() {
                return isAutoscrollToSource(myCurrentViewId);
            }

            @Override
            protected void setAutoScrollMode(boolean state) {
                setAutoscrollToSource(state, myCurrentViewId);
            }
        };
        toolWindowManager.addToolWindowManagerListener(new ToolWindowManagerAdapter() {
            private boolean toolWindowVisible;

            @Override
            public void stateChanged() {
                ToolWindow window = toolWindowManager.getToolWindow(ToolWindowId.PROJECT_VIEW);
                if (window == null)
                    return;
                if (window.isVisible() && !toolWindowVisible) {
                    String id = getCurrentViewId();
                    if (isAutoscrollToSource(id)) {
                        AbstractProjectViewPane currentProjectViewPane = getCurrentProjectViewPane();

                        if (currentProjectViewPane != null) {
                            myAutoScrollToSourceHandler.onMouseClicked(currentProjectViewPane.getTree());
                        }
                    }
                    if (isAutoscrollFromSource(id)) {
                        myAutoScrollFromSourceHandler.setAutoScrollEnabled(true);
                    }
                }
                toolWindowVisible = window.isVisible();
            }
        });
    }

    private void constructUi() {
        myViewContentPanel = new JPanel();
        myPanel = new SimpleToolWindowPanel(true).setProvideQuickActions(false);
        myPanel.setContent(myViewContentPanel);
    }

    @Override
    public String getName() {
        return "Project";
    }

    @Override
    public List<AnAction> getActions(boolean originalProvider) {
        List<AnAction> result = new ArrayList<AnAction>();

        DefaultActionGroup views = new DefaultActionGroup("Change View", true);
        boolean lastHeaderHadKids = false;
        for (int i = 0; i < myContentManager.getContentCount(); i++) {
            Content each = myContentManager.getContent(i);
            if (each != null) {

                if (each.getUserData(SUB_ID_KEY) == null) {
                    if (lastHeaderHadKids) {
                        views.add(AnSeparator.getInstance());
                    } else {
                        if (i + 1 < myContentManager.getContentCount()) {
                            Content next = myContentManager.getContent(i + 1);
                            if (next != null) {
                                if (next.getUserData(SUB_ID_KEY) != null) {
                                    views.add(AnSeparator.getInstance());
                                }
                            }
                        }
                    }
                } else {
                    lastHeaderHadKids = true;
                }

                views.add(new ChangeViewAction(each.getUserData(ID_KEY), each.getUserData(SUB_ID_KEY)));
            }
        }
        result.add(views);
        result.add(AnSeparator.getInstance());

        List<AnAction> secondary = new ArrayList<AnAction>();
        if (myActionGroup != null) {
            AnAction[] kids = myActionGroup.getChildren(null);
            for (AnAction each : kids) {
                if (myActionGroup.isPrimary(each)) {
                    result.add(each);
                } else {
                    secondary.add(each);
                }
            }
        }
        result.add(AnSeparator.getInstance());
        result.addAll(secondary);

        return result;
    }

    private class ChangeViewAction extends AnAction {
        private String myId;
        private String mySubId;

        private ChangeViewAction(String id, String subId) {
            myId = id;
            mySubId = subId;
        }

        @Override
        public void update(AnActionEvent e) {
            AbstractProjectViewPane pane = getProjectViewPaneById(myId);
            e.getPresentation().setText(
                    pane.getTitle() + (mySubId != null ? (" - " + pane.getPresentableSubIdName(mySubId)) : ""));
        }

        @Override
        public void actionPerformed(AnActionEvent e) {
            changeView(myId, mySubId);
        }
    }

    @Override
    public boolean isCycleRoot() {
        return false;
    }

    @Override
    public synchronized void addProjectPane(final AbstractProjectViewPane pane) {
        myUninitializedPanes.add(pane);
        SelectInTarget selectInTarget = pane.createSelectInTarget();
        if (selectInTarget != null) {
            mySelectInTargets.put(pane.getId(), selectInTarget);
        }
        if (isInitialized) {
            doAddUninitializedPanes();
        }
    }

    @Override
    public synchronized void removeProjectPane(AbstractProjectViewPane pane) {
        myUninitializedPanes.remove(pane);
        //assume we are completely initialized here
        String idToRemove = pane.getId();

        if (!myId2Pane.containsKey(idToRemove))
            return;
        pane.removeTreeChangeListener();
        for (int i = myContentManager.getContentCount() - 1; i >= 0; i--) {
            Content content = myContentManager.getContent(i);
            String id = content != null ? content.getUserData(ID_KEY) : null;
            if (id != null && id.equals(idToRemove)) {
                myContentManager.removeContent(content, true);
            }
        }
        myId2Pane.remove(idToRemove);
        mySelectInTargets.remove(idToRemove);
        viewSelectionChanged();
    }

    private synchronized void doAddUninitializedPanes() {
        for (AbstractProjectViewPane pane : myUninitializedPanes) {
            doAddPane(pane);
        }
        final Content[] contents = myContentManager.getContents();
        for (int i = 1; i < contents.length; i++) {
            Content content = contents[i];
            Content prev = contents[i - 1];
            if (!StringUtil.equals(content.getUserData(ID_KEY), prev.getUserData(ID_KEY))
                    && prev.getUserData(SUB_ID_KEY) != null && content.getSeparator() == null) {
                content.setSeparator("");
            }
        }
        for (Content content : contents) {
            final String id = content.getUserData(ID_KEY);
            final String subId = content.getUserData(SUB_ID_KEY);
            if (id != null && id.equals(mySavedPaneId)
                    && StringUtil.equals(subId, content.getUserData(SUB_ID_KEY))) {
                changeView(mySavedPaneId, mySavedPaneSubId);
                mySavedPaneId = null;
                mySavedPaneSubId = null;
                break;
            }
        }
        myUninitializedPanes.clear();
    }

    private void doAddPane(final AbstractProjectViewPane newPane) {
        int index;
        final ContentManager manager = myContentManager;
        for (index = 0; index < manager.getContentCount(); index++) {
            Content content = manager.getContent(index);
            String id = content.getUserData(ID_KEY);
            AbstractProjectViewPane pane = myId2Pane.get(id);

            int comp = PANE_WEIGHT_COMPARATOR.compare(pane, newPane);
            if (comp == 0) {
                System.out.println("here");
            }
            LOG.assertTrue(comp != 0);
            if (comp > 0) {
                break;
            }
        }
        final String id = newPane.getId();
        myId2Pane.put(id, newPane);
        String[] subIds = newPane.getSubIds();
        subIds = subIds.length == 0 ? new String[] { null } : subIds;
        boolean first = true;
        for (String subId : subIds) {
            final String title = subId != null ? newPane.getPresentableSubIdName(subId) : newPane.getTitle();
            final Content content = myContentManager.getFactory().createContent(getComponent(), title, false);
            content.setTabName(title);
            content.putUserData(ID_KEY, id);
            content.putUserData(SUB_ID_KEY, subId);
            content.putUserData(ToolWindow.SHOW_CONTENT_ICON, Boolean.TRUE);
            content.setIcon(newPane.getIcon());
            content.setPopupIcon(subId != null ? AllIcons.General.Bullet : newPane.getIcon());
            content.setPreferredFocusedComponent(new Computable<JComponent>() {
                @Override
                public JComponent compute() {
                    final AbstractProjectViewPane current = getCurrentProjectViewPane();
                    return current != null ? current.getComponentToFocus() : null;
                }
            });
            content.setBusyObject(this);
            if (first && subId != null) {
                content.setSeparator(newPane.getTitle());
            }
            manager.addContent(content, index++);
            first = false;
        }
        Disposer.register(this, newPane);
    }

    private void showPane(AbstractProjectViewPane newPane) {
        AbstractProjectViewPane currentPane = getCurrentProjectViewPane();
        PsiElement selectedPsiElement = null;
        if (currentPane != null) {
            if (currentPane != newPane) {
                currentPane.saveExpandedPaths();
            }
            final PsiElement[] elements = currentPane.getSelectedPSIElements();
            if (elements.length > 0) {
                selectedPsiElement = elements[0];
            }
        }
        myViewContentPanel.removeAll();
        JComponent component = newPane.createComponent();
        UIUtil.removeScrollBorder(component);
        myViewContentPanel.setLayout(new BorderLayout());
        myViewContentPanel.add(component, BorderLayout.CENTER);
        myCurrentViewId = newPane.getId();
        String newSubId = myCurrentViewSubId = newPane.getSubId();
        myViewContentPanel.revalidate();
        myViewContentPanel.repaint();
        createToolbarActions();

        myAutoScrollToSourceHandler.install(newPane.myTree);

        IdeFocusManager.getInstance(myProject).requestFocus(newPane.getComponentToFocus(), false);

        newPane.restoreExpandedPaths();
        if (selectedPsiElement != null) {
            final VirtualFile virtualFile = PsiUtilCore.getVirtualFile(selectedPsiElement);
            if (virtualFile != null && ((ProjectViewSelectInTarget) newPane.createSelectInTarget())
                    .isSubIdSelectable(newSubId, new SelectInContext() {
                        @Override
                        @NotNull
                        public Project getProject() {
                            return myProject;
                        }

                        @Override
                        @NotNull
                        public VirtualFile getVirtualFile() {
                            return virtualFile;
                        }

                        @Override
                        public Object getSelectorInFile() {
                            return null;
                        }

                        @Override
                        public FileEditorProvider getFileEditorProvider() {
                            return null;
                        }
                    })) {
                newPane.select(selectedPsiElement, virtualFile, true);
            }
        }
        myAutoScrollToSourceHandler.onMouseClicked(newPane.myTree);
    }

    // public for tests
    public synchronized void setupImpl(final ToolWindow toolWindow) {
        setupImpl(toolWindow, true);
    }

    // public for tests
    public synchronized void setupImpl(final ToolWindow toolWindow, final boolean loadPaneExtensions) {
        myActionGroup = new DefaultActionGroup();

        myAutoScrollFromSourceHandler.install();

        if (toolWindow != null) {
            myContentManager = toolWindow.getContentManager();
            if (!ApplicationManager.getApplication().isUnitTestMode()) {
                toolWindow.setContentUiType(ToolWindowContentUiType.getInstance("combo"), null);
                ((ToolWindowEx) toolWindow).setAdditionalGearActions(myActionGroup);
                toolWindow.getComponent().putClientProperty(ToolWindowContentUi.HIDE_ID_LABEL, "true");
            }
        } else {
            final ContentFactory contentFactory = ServiceManager.getService(ContentFactory.class);
            myContentManager = contentFactory.createContentManager(false, myProject);
        }

        GuiUtils.replaceJSplitPaneWithIDEASplitter(myPanel);
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                splitterProportions.restoreSplitterProportions(myPanel);
            }
        });

        if (loadPaneExtensions) {
            ensurePanesLoaded();
        }
        isInitialized = true;
        doAddUninitializedPanes();

        myContentManager.addContentManagerListener(new ContentManagerAdapter() {
            @Override
            public void selectionChanged(ContentManagerEvent event) {
                if (event.getOperation() == ContentManagerEvent.ContentOperation.add) {
                    viewSelectionChanged();
                }
            }
        });
    }

    private void ensurePanesLoaded() {
        if (myExtensionsLoaded)
            return;
        myExtensionsLoaded = true;
        for (AbstractProjectViewPane pane : Extensions.getExtensions(AbstractProjectViewPane.EP_NAME, myProject)) {
            if (myUninitializedPaneState.containsKey(pane.getId())) {
                try {
                    pane.readExternal(myUninitializedPaneState.get(pane.getId()));
                } catch (InvalidDataException e) {
                    // ignore
                }
                myUninitializedPaneState.remove(pane.getId());
            }
            if (pane.isInitiallyVisible() && !myId2Pane.containsKey(pane.getId())) {
                addProjectPane(pane);
            }
            Disposer.register(this, pane);
        }
    }

    private boolean viewSelectionChanged() {
        Content content = myContentManager.getSelectedContent();
        if (content == null)
            return false;
        final String id = content.getUserData(ID_KEY);
        String subId = content.getUserData(SUB_ID_KEY);
        if (content.equals(Pair.create(myCurrentViewId, myCurrentViewSubId)))
            return false;
        final AbstractProjectViewPane newPane = getProjectViewPaneById(id);
        if (newPane == null)
            return false;
        newPane.setSubId(subId);
        showPane(newPane);
        if (isAutoscrollFromSource(id)) {
            myAutoScrollFromSourceHandler.scrollFromSource();
        }
        return true;
    }

    private void createToolbarActions() {
        List<AnAction> titleActions = ContainerUtil.newSmartList();
        myActionGroup.removeAll();

        myActionGroup.addAction(new PaneOptionAction(myFlattenPackages,
                IdeBundle.message("action.flatten.packages"), IdeBundle.message("action.flatten.packages"),
                AllIcons.ObjectBrowser.FlattenPackages, ourFlattenPackagesDefaults) {
            @Override
            public void setSelected(AnActionEvent event, boolean flag) {
                final AbstractProjectViewPane viewPane = getCurrentProjectViewPane();
                final SelectionInfo selectionInfo = SelectionInfo.create(viewPane);

                super.setSelected(event, flag);

                selectionInfo.apply(viewPane);
            }

            @Override
            @RequiredDispatchThread
            public void update(AnActionEvent e) {
                super.update(e);
                Project project = e.getProject();
                assert project != null;
                final Presentation presentation = e.getPresentation();
                if (!PsiPackageSupportProviders.isPackageSupported(project)) {
                    presentation.setVisible(false);
                }
            }
        }).setAsSecondary(true);

        class FlattenPackagesDependableAction extends PaneOptionAction {
            FlattenPackagesDependableAction(Map<String, Boolean> optionsMap, final String text,
                    final String description, final Icon icon, boolean optionDefaultValue) {
                super(optionsMap, text, description, icon, optionDefaultValue);
            }

            @Override
            @RequiredDispatchThread
            public void update(AnActionEvent e) {
                super.update(e);
                Project project = e.getProject();
                assert project != null;
                final Presentation presentation = e.getPresentation();
                if (!PsiPackageSupportProviders.isPackageSupported(project)) {
                    presentation.setVisible(false);
                } else {
                    presentation.setVisible(isFlattenPackages(myCurrentViewId));
                }
            }
        }
        myActionGroup.addAction(new HideEmptyMiddlePackagesAction()).setAsSecondary(true);
        myActionGroup.addAction(new FlattenPackagesDependableAction(myAbbreviatePackageNames,
                IdeBundle.message("action.abbreviate.qualified.package.names"),
                IdeBundle.message("action.abbreviate.qualified.package.names"),
                AllIcons.ObjectBrowser.AbbreviatePackageNames, ourAbbreviatePackagesDefaults) {
            @Override
            public boolean isSelected(AnActionEvent event) {
                return super.isSelected(event) && isAbbreviatePackageNames(myCurrentViewId);
            }

            @Override
            @RequiredDispatchThread
            public void update(AnActionEvent e) {
                super.update(e);
                if (ScopeViewPane.ID.equals(myCurrentViewId)) {
                    e.getPresentation().setEnabled(false);
                }
            }
        }).setAsSecondary(true);

        myActionGroup.addAction(new PaneOptionAction(myShowMembers, IdeBundle.message("action.show.members"),
                IdeBundle.message("action.show.hide.members"), AllIcons.ObjectBrowser.ShowMembers,
                ourShowMembersDefaults)).setAsSecondary(true);
        myActionGroup.addAction(myAutoScrollToSourceHandler.createToggleAction()).setAsSecondary(true);
        myActionGroup.addAction(myAutoScrollFromSourceHandler.createToggleAction()).setAsSecondary(true);
        myActionGroup.addAction(new SortByTypeAction()).setAsSecondary(true);
        myActionGroup.addAction(new FoldersAlwaysOnTopAction()).setAsSecondary(true);

        if (!myAutoScrollFromSourceHandler.isAutoScrollEnabled()) {
            titleActions.add(new ScrollFromSourceAction());
        }
        AnAction collapseAllAction = CommonActionsManager.getInstance().createCollapseAllAction(new TreeExpander() {
            @Override
            public void expandAll() {

            }

            @Override
            public boolean canExpand() {
                return false;
            }

            @Override
            public void collapseAll() {
                AbstractProjectViewPane pane = getCurrentProjectViewPane();
                JTree tree = pane.myTree;
                if (tree != null) {
                    TreeUtil.collapseAll(tree, 0);
                }
            }

            @Override
            public boolean canCollapse() {
                return true;
            }
        }, getComponent());

        collapseAllAction.getTemplatePresentation().setIcon(AllIcons.General.CollapseAll);
        collapseAllAction.getTemplatePresentation().setHoveredIcon(AllIcons.General.CollapseAllHover);
        titleActions.add(collapseAllAction);

        getCurrentProjectViewPane().addToolbarActionsImpl(myActionGroup);

        ToolWindowEx window = (ToolWindowEx) ToolWindowManager.getInstance(myProject)
                .getToolWindow(ToolWindowId.PROJECT_VIEW);
        if (window != null) {
            window.setTitleActions(
                    titleActions.toArray(ContainerUtil.toArray(titleActions, AnAction.ARRAY_FACTORY)));
        }
    }

    @Override
    public AbstractProjectViewPane getProjectViewPaneById(String id) {
        if (!ApplicationManager.getApplication().isUnitTestMode()) { // most tests don't need all panes to be loaded
            ensurePanesLoaded();
        }

        final AbstractProjectViewPane pane = myId2Pane.get(id);
        if (pane != null) {
            return pane;
        }
        for (AbstractProjectViewPane viewPane : myUninitializedPanes) {
            if (viewPane.getId().equals(id)) {
                return viewPane;
            }
        }
        return null;
    }

    @Override
    public AbstractProjectViewPane getCurrentProjectViewPane() {
        return getProjectViewPaneById(myCurrentViewId);
    }

    @Override
    public void refresh() {
        AbstractProjectViewPane currentProjectViewPane = getCurrentProjectViewPane();
        if (currentProjectViewPane != null) {
            // may be null for e.g. default project
            currentProjectViewPane.updateFromRoot(false);
        }
    }

    @Override
    public void select(final Object element, VirtualFile file, boolean requestFocus) {
        final AbstractProjectViewPane viewPane = getCurrentProjectViewPane();
        if (viewPane != null) {
            viewPane.select(element, file, requestFocus);
        }
    }

    @Override
    public ActionCallback selectCB(Object element, VirtualFile file, boolean requestFocus) {
        final AbstractProjectViewPane viewPane = getCurrentProjectViewPane();
        if (viewPane != null && viewPane instanceof AbstractProjectViewPSIPane) {
            return ((AbstractProjectViewPSIPane) viewPane).selectCB(element, file, requestFocus);
        } else {
            select(element, file, requestFocus);
            return new ActionCallback.Done();
        }
    }

    @Override
    public void dispose() {
        myConnection.disconnect();
    }

    @Override
    public JComponent getComponent() {
        return myDataProvider;
    }

    @Override
    public String getCurrentViewId() {
        return myCurrentViewId;
    }

    @Override
    public PsiElement getParentOfCurrentSelection() {
        final AbstractProjectViewPane viewPane = getCurrentProjectViewPane();
        if (viewPane == null) {
            return null;
        }
        TreePath path = viewPane.getSelectedPath();
        if (path == null) {
            return null;
        }
        path = path.getParentPath();
        if (path == null) {
            return null;
        }
        DefaultMutableTreeNode node = (DefaultMutableTreeNode) path.getLastPathComponent();
        Object userObject = node.getUserObject();
        if (userObject instanceof ProjectViewNode) {
            ProjectViewNode descriptor = (ProjectViewNode) userObject;
            Object element = descriptor.getValue();
            if (element instanceof PsiElement) {
                PsiElement psiElement = (PsiElement) element;
                if (!psiElement.isValid())
                    return null;
                return psiElement;
            } else {
                return null;
            }
        }
        return null;
    }

    private class PaneOptionAction extends ToggleAction implements DumbAware {
        private final Map<String, Boolean> myOptionsMap;
        private final boolean myOptionDefaultValue;

        PaneOptionAction(Map<String, Boolean> optionsMap, final String text, final String description,
                final Icon icon, boolean optionDefaultValue) {
            super(text, description, icon);
            myOptionsMap = optionsMap;
            myOptionDefaultValue = optionDefaultValue;
        }

        @Override
        public boolean isSelected(AnActionEvent event) {
            return getPaneOptionValue(myOptionsMap, myCurrentViewId, myOptionDefaultValue);
        }

        @Override
        public void setSelected(AnActionEvent event, boolean flag) {
            setPaneOption(myOptionsMap, flag, myCurrentViewId, true);
        }
    }

    @Override
    public void changeView() {
        final List<AbstractProjectViewPane> views = new ArrayList<AbstractProjectViewPane>(myId2Pane.values());
        views.remove(getCurrentProjectViewPane());
        Collections.sort(views, PANE_WEIGHT_COMPARATOR);

        final JList list = new JBList(ArrayUtil.toObjectArray(views));
        list.setCellRenderer(new DefaultListCellRenderer() {
            @Override
            public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected,
                    boolean cellHasFocus) {
                super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
                AbstractProjectViewPane pane = (AbstractProjectViewPane) value;
                setText(pane.getTitle());
                return this;
            }
        });

        if (!views.isEmpty()) {
            list.setSelectedValue(views.get(0), true);
        }
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                if (list.getSelectedIndex() < 0)
                    return;
                AbstractProjectViewPane pane = (AbstractProjectViewPane) list.getSelectedValue();
                changeView(pane.getId());
            }
        };

        new PopupChooserBuilder(list).setTitle(IdeBundle.message("title.popup.views"))
                .setItemChoosenCallback(runnable).createPopup().showInCenterOf(getComponent());
    }

    @Override
    public void changeView(@NotNull String viewId) {
        changeView(viewId, null);
    }

    @Override
    public void changeView(@NotNull String viewId, @Nullable String subId) {
        AbstractProjectViewPane pane = getProjectViewPaneById(viewId);
        LOG.assertTrue(pane != null, "Project view pane not found: " + viewId + "; subId:" + subId);
        if (!viewId.equals(getCurrentViewId()) || subId != null && !subId.equals(pane.getSubId())) {
            for (Content content : myContentManager.getContents()) {
                if (viewId.equals(content.getUserData(ID_KEY))
                        && StringUtil.equals(subId, content.getUserData(SUB_ID_KEY))) {
                    myContentManager.setSelectedContent(content);
                    break;
                }
            }
            viewSelectionChanged();
        }
    }

    private final class MyDeletePSIElementProvider implements DeleteProvider {
        @Override
        public boolean canDeleteElement(@NotNull DataContext dataContext) {
            final PsiElement[] elements = getElementsToDelete();
            return DeleteHandler.shouldEnableDeleteAction(elements);
        }

        @Override
        public void deleteElement(@NotNull DataContext dataContext) {
            List<PsiElement> allElements = Arrays.asList(getElementsToDelete());
            List<PsiElement> validElements = new ArrayList<PsiElement>();
            for (PsiElement psiElement : allElements) {
                if (psiElement != null && psiElement.isValid())
                    validElements.add(psiElement);
            }
            final PsiElement[] elements = PsiUtilBase.toPsiElementArray(validElements);

            LocalHistoryAction a = LocalHistory.getInstance().startAction(IdeBundle.message("progress.deleting"));
            try {
                DeleteHandler.deletePsiElement(elements, myProject);
            } finally {
                a.finish();
            }
        }

        private PsiElement[] getElementsToDelete() {
            final AbstractProjectViewPane viewPane = getCurrentProjectViewPane();
            PsiElement[] elements = viewPane.getSelectedPSIElements();
            for (int idx = 0; idx < elements.length; idx++) {
                final PsiElement element = elements[idx];
                if (element instanceof PsiDirectory) {
                    PsiDirectory directory = (PsiDirectory) element;
                    if (isHideEmptyMiddlePackages(viewPane.getId()) && directory.getChildren().length == 0
                            && !BaseProjectViewDirectoryHelper.skipDirectory(directory)) {
                        while (true) {
                            PsiDirectory parent = directory.getParentDirectory();
                            if (parent == null)
                                break;
                            if (BaseProjectViewDirectoryHelper.skipDirectory(parent) || PsiPackageHelper
                                    .getInstance(myProject).getQualifiedName(parent, false).length() == 0)
                                break;
                            PsiElement[] children = parent.getChildren();
                            if (children.length == 0 || children.length == 1 && children[0] == directory) {
                                directory = parent;
                            } else {
                                break;
                            }
                        }
                        elements[idx] = directory;
                    }
                    final VirtualFile virtualFile = directory.getVirtualFile();
                    final String path = virtualFile.getPath();
                    if (path.endsWith(ArchiveFileSystem.ARCHIVE_SEPARATOR)) { // if is jar-file root
                        final VirtualFile vFile = LocalFileSystem.getInstance().findFileByPath(
                                path.substring(0, path.length() - ArchiveFileSystem.ARCHIVE_SEPARATOR.length()));
                        if (vFile != null) {
                            final PsiFile psiFile = PsiManager.getInstance(myProject).findFile(vFile);
                            if (psiFile != null) {
                                elements[idx] = psiFile;
                            }
                        }
                    }
                }
            }
            return elements;
        }

    }

    private final class MyPanel extends JPanel implements DataProvider {
        MyPanel() {
            super(new BorderLayout());
        }

        @Nullable
        private Object getSelectedNodeElement() {
            final AbstractProjectViewPane currentProjectViewPane = getCurrentProjectViewPane();
            if (currentProjectViewPane == null) { // can happen if not initialized yet
                return null;
            }
            DefaultMutableTreeNode node = currentProjectViewPane.getSelectedNode();
            if (node == null) {
                return null;
            }
            Object userObject = node.getUserObject();
            if (userObject instanceof AbstractTreeNode) {
                return ((AbstractTreeNode) userObject).getValue();
            }
            if (!(userObject instanceof NodeDescriptor)) {
                return null;
            }
            return ((NodeDescriptor) userObject).getElement();
        }

        @Override
        public Object getData(String dataId) {
            final AbstractProjectViewPane currentProjectViewPane = getCurrentProjectViewPane();
            if (currentProjectViewPane != null) {
                final Object paneSpecificData = currentProjectViewPane.getData(dataId);
                if (paneSpecificData != null)
                    return paneSpecificData;
            }

            if (LangDataKeys.PSI_ELEMENT.is(dataId)) {
                if (currentProjectViewPane == null)
                    return null;
                final PsiElement[] elements = currentProjectViewPane.getSelectedPSIElements();
                return elements.length == 1 ? elements[0] : null;
            }
            if (LangDataKeys.PSI_ELEMENT_ARRAY.is(dataId)) {
                if (currentProjectViewPane == null) {
                    return null;
                }
                PsiElement[] elements = currentProjectViewPane.getSelectedPSIElements();
                return elements.length == 0 ? null : elements;
            }
            if (PlatformDataKeys.VIRTUAL_FILE_ARRAY.is(dataId)) {
                PsiElement[] psiElements = (PsiElement[]) getData(LangDataKeys.PSI_ELEMENT_ARRAY.getName());
                if (psiElements == null)
                    return null;
                Set<VirtualFile> files = new LinkedHashSet<VirtualFile>();
                for (PsiElement element : psiElements) {
                    final VirtualFile virtualFile = PsiUtilCore.getVirtualFile(element);
                    if (virtualFile != null) {
                        files.add(virtualFile);
                    }
                }
                return files.size() > 0 ? VfsUtil.toVirtualFileArray(files) : null;
            }
            if (LangDataKeys.MODULE.is(dataId)) {
                VirtualFile[] virtualFiles = (VirtualFile[]) getData(PlatformDataKeys.VIRTUAL_FILE_ARRAY.getName());
                if (virtualFiles == null || virtualFiles.length <= 1)
                    return null;
                final Set<Module> modules = new HashSet<Module>();
                for (VirtualFile virtualFile : virtualFiles) {
                    modules.add(ModuleUtil.findModuleForFile(virtualFile, myProject));
                }
                return modules.size() == 1 ? modules.iterator().next() : null;
            }
            if (LangDataKeys.TARGET_PSI_ELEMENT.is(dataId)) {
                return null;
            }
            if (PlatformDataKeys.CUT_PROVIDER.is(dataId)) {
                return myCopyPasteDelegator.getCutProvider();
            }
            if (PlatformDataKeys.COPY_PROVIDER.is(dataId)) {
                return myCopyPasteDelegator.getCopyProvider();
            }
            if (PlatformDataKeys.PASTE_PROVIDER.is(dataId)) {
                return myCopyPasteDelegator.getPasteProvider();
            }
            if (LangDataKeys.IDE_VIEW.is(dataId)) {
                return myIdeView;
            }
            if (PlatformDataKeys.DELETE_ELEMENT_PROVIDER.is(dataId)) {
                final Module[] modules = getSelectedModules();
                if (modules != null) {
                    return myDeleteModuleProvider;
                }
                final LibraryOrderEntry orderEntry = getSelectedLibrary();
                if (orderEntry != null) {
                    return new DeleteProvider() {
                        @Override
                        public void deleteElement(@NotNull DataContext dataContext) {
                            detachLibrary(orderEntry, myProject);
                        }

                        @Override
                        public boolean canDeleteElement(@NotNull DataContext dataContext) {
                            return true;
                        }
                    };
                }
                return myDeletePSIElementProvider;
            }
            if (PlatformDataKeys.HELP_ID.is(dataId)) {
                return HelpID.PROJECT_VIEWS;
            }
            if (ProjectViewImpl.DATA_KEY.is(dataId)) {
                return ProjectViewImpl.this;
            }
            if (PlatformDataKeys.PROJECT_CONTEXT.is(dataId)) {
                Object selected = getSelectedNodeElement();
                return selected instanceof Project ? selected : null;
            }
            if (LangDataKeys.MODULE_CONTEXT.is(dataId)) {
                Object selected = getSelectedNodeElement();
                if (selected instanceof Module) {
                    return !((Module) selected).isDisposed() ? selected : null;
                } else if (selected instanceof PsiDirectory) {
                    return moduleByContentRoot(((PsiDirectory) selected).getVirtualFile());
                } else if (selected instanceof VirtualFile) {
                    return moduleByContentRoot((VirtualFile) selected);
                } else {
                    return null;
                }
            }

            if (LangDataKeys.MODULE_CONTEXT_ARRAY.is(dataId)) {
                return getSelectedModules();
            }
            if (ModuleGroup.ARRAY_DATA_KEY.is(dataId)) {
                final List<ModuleGroup> selectedElements = getSelectedElements(ModuleGroup.class);
                return selectedElements.isEmpty() ? null
                        : selectedElements.toArray(new ModuleGroup[selectedElements.size()]);
            }
            if (LibraryGroupElement.ARRAY_DATA_KEY.is(dataId)) {
                final List<LibraryGroupElement> selectedElements = getSelectedElements(LibraryGroupElement.class);
                return selectedElements.isEmpty() ? null
                        : selectedElements.toArray(new LibraryGroupElement[selectedElements.size()]);
            }
            if (NamedLibraryElement.ARRAY_DATA_KEY.is(dataId)) {
                final List<NamedLibraryElement> selectedElements = getSelectedElements(NamedLibraryElement.class);
                return selectedElements.isEmpty() ? null
                        : selectedElements.toArray(new NamedLibraryElement[selectedElements.size()]);
            }

            if (QuickActionProvider.KEY.is(dataId)) {
                return ProjectViewImpl.this;
            }

            return null;
        }

        @Nullable
        private LibraryOrderEntry getSelectedLibrary() {
            final AbstractProjectViewPane viewPane = getCurrentProjectViewPane();
            DefaultMutableTreeNode node = viewPane != null ? viewPane.getSelectedNode() : null;
            if (node == null)
                return null;
            DefaultMutableTreeNode parent = (DefaultMutableTreeNode) node.getParent();
            if (parent == null)
                return null;
            Object userObject = parent.getUserObject();
            if (userObject instanceof LibraryGroupNode) {
                userObject = node.getUserObject();
                if (userObject instanceof NamedLibraryElementNode) {
                    NamedLibraryElement element = ((NamedLibraryElementNode) userObject).getValue();
                    OrderEntry orderEntry = element.getOrderEntry();
                    return orderEntry instanceof LibraryOrderEntry ? (LibraryOrderEntry) orderEntry : null;
                }
                PsiDirectory directory = ((PsiDirectoryNode) userObject).getValue();
                VirtualFile virtualFile = directory.getVirtualFile();
                Module module = (Module) ((AbstractTreeNode) ((DefaultMutableTreeNode) parent.getParent())
                        .getUserObject()).getValue();

                if (module == null)
                    return null;
                ModuleFileIndex index = ModuleRootManager.getInstance(module).getFileIndex();
                OrderEntry entry = index.getOrderEntryForFile(virtualFile);
                if (entry instanceof LibraryOrderEntry) {
                    return (LibraryOrderEntry) entry;
                }
            }

            return null;
        }

        private void detachLibrary(final LibraryOrderEntry orderEntry, final Project project) {
            final Module module = orderEntry.getOwnerModule();
            String message = IdeBundle.message("detach.library.from.module", orderEntry.getPresentableName(),
                    module.getName());
            String title = IdeBundle.message("detach.library");
            int ret = Messages.showOkCancelDialog(project, message, title, Messages.getQuestionIcon());
            if (ret != 0)
                return;
            CommandProcessor.getInstance().executeCommand(module.getProject(), new Runnable() {
                @Override
                public void run() {
                    final Runnable action = new Runnable() {
                        @Override
                        public void run() {
                            ModuleRootManager rootManager = ModuleRootManager.getInstance(module);
                            OrderEntry[] orderEntries = rootManager.getOrderEntries();
                            ModifiableRootModel model = rootManager.getModifiableModel();
                            OrderEntry[] modifiableEntries = model.getOrderEntries();
                            for (int i = 0; i < orderEntries.length; i++) {
                                OrderEntry entry = orderEntries[i];
                                if (entry instanceof LibraryOrderEntry
                                        && ((LibraryOrderEntry) entry).getLibrary() == orderEntry.getLibrary()) {
                                    model.removeOrderEntry(modifiableEntries[i]);
                                }
                            }
                            model.commit();
                        }
                    };
                    ApplicationManager.getApplication().runWriteAction(action);
                }
            }, title, null);
        }

        @Nullable
        private Module[] getSelectedModules() {
            final AbstractProjectViewPane viewPane = getCurrentProjectViewPane();
            if (viewPane == null)
                return null;
            final Object[] elements = viewPane.getSelectedElements();
            ArrayList<Module> result = new ArrayList<Module>();
            for (Object element : elements) {
                if (element instanceof Module) {
                    final Module module = (Module) element;
                    if (!module.isDisposed()) {
                        result.add(module);
                    }
                } else if (element instanceof ModuleGroup) {
                    Collection<Module> modules = ((ModuleGroup) element).modulesInGroup(myProject, true);
                    result.addAll(modules);
                } else if (element instanceof PsiDirectory) {
                    Module module = moduleByContentRoot(((PsiDirectory) element).getVirtualFile());
                    if (module != null)
                        result.add(module);
                } else if (element instanceof VirtualFile) {
                    Module module = moduleByContentRoot((VirtualFile) element);
                    if (module != null)
                        result.add(module);
                }
            }

            if (result.isEmpty()) {
                return null;
            } else {
                return result.toArray(new Module[result.size()]);
            }
        }
    }

    @Nullable
    private Module moduleByContentRoot(VirtualFile file) {
        if (ProjectRootsUtil.isModuleContentRoot(file, myProject)) {
            Module module = ProjectRootManager.getInstance(myProject).getFileIndex().getModuleForFile(file);
            if (module != null && !module.isDisposed()) {
                return module;
            }
        }

        return null;
    }

    private <T> List<T> getSelectedElements(Class<T> klass) {
        ArrayList<T> result = new ArrayList<T>();
        final AbstractProjectViewPane viewPane = getCurrentProjectViewPane();
        if (viewPane == null)
            return result;
        final Object[] elements = viewPane.getSelectedElements();
        for (Object element : elements) {
            //element still valid
            if (element != null && klass.isAssignableFrom(element.getClass())) {
                result.add((T) element);
            }
        }
        return result;
    }

    private final class MyIdeView implements IdeView {
        @Override
        public void selectElement(PsiElement element) {
            selectPsiElement(element, false);
            boolean requestFocus = true;
            if (element != null) {
                final boolean isDirectory = element instanceof PsiDirectory;
                if (!isDirectory) {
                    FileEditor editor = EditorHelper.openInEditor(element, false);
                    if (editor != null) {
                        ToolWindowManager.getInstance(myProject).activateEditorComponent();
                        requestFocus = false;
                    }
                }
            }

            if (requestFocus) {
                selectPsiElement(element, true);
            }
        }

        @Override
        public PsiDirectory[] getDirectories() {
            final AbstractProjectViewPane viewPane = getCurrentProjectViewPane();
            if (viewPane != null) {
                return viewPane.getSelectedDirectories();
            }

            return PsiDirectory.EMPTY_ARRAY;
        }

        @Override
        public PsiDirectory getOrChooseDirectory() {
            return DirectoryChooserUtil.getOrChooseDirectory(this);
        }
    }

    @Override
    public void selectPsiElement(PsiElement element, boolean requestFocus) {
        if (element == null)
            return;
        VirtualFile virtualFile = PsiUtilBase.getVirtualFile(element);
        select(element, virtualFile, requestFocus);
    }

    private static void readOption(Element node, Map<String, Boolean> options) {
        if (node == null)
            return;
        List attributes = node.getAttributes();
        for (final Object attribute1 : attributes) {
            Attribute attribute = (Attribute) attribute1;
            options.put(attribute.getName(),
                    Boolean.TRUE.toString().equals(attribute.getValue()) ? Boolean.TRUE : Boolean.FALSE);
        }
    }

    private static void writeOption(Element parentNode, Map<String, Boolean> optionsForPanes, String optionName) {
        Element e = new Element(optionName);
        for (Map.Entry<String, Boolean> entry : optionsForPanes.entrySet()) {
            final String key = entry.getKey();
            if (key != null) { //SCR48267
                e.setAttribute(key, Boolean.toString(entry.getValue().booleanValue()));
            }
        }

        parentNode.addContent(e);
    }

    @Override
    public void loadState(Element parentNode) {
        Element navigatorElement = parentNode.getChild(ELEMENT_NAVIGATOR);
        if (navigatorElement != null) {
            mySavedPaneId = navigatorElement.getAttributeValue(ATTRIBUTE_CURRENT_VIEW);
            mySavedPaneSubId = navigatorElement.getAttributeValue(ATTRIBUTE_CURRENT_SUBVIEW);
            if (mySavedPaneId == null) {
                mySavedPaneId = ProjectViewPane.ID;
                mySavedPaneSubId = null;
            }
            readOption(navigatorElement.getChild(ELEMENT_FLATTEN_PACKAGES), myFlattenPackages);
            readOption(navigatorElement.getChild(ELEMENT_SHOW_MEMBERS), myShowMembers);
            readOption(navigatorElement.getChild(ELEMENT_SHOW_MODULES), myShowModules);
            readOption(navigatorElement.getChild(ELEMENT_SHOW_LIBRARY_CONTENTS), myShowLibraryContents);
            readOption(navigatorElement.getChild(ELEMENT_HIDE_EMPTY_PACKAGES), myHideEmptyPackages);
            readOption(navigatorElement.getChild(ELEMENT_ABBREVIATE_PACKAGE_NAMES), myAbbreviatePackageNames);
            readOption(navigatorElement.getChild(ELEMENT_AUTOSCROLL_TO_SOURCE), myAutoscrollToSource);
            readOption(navigatorElement.getChild(ELEMENT_AUTOSCROLL_FROM_SOURCE), myAutoscrollFromSource);
            readOption(navigatorElement.getChild(ELEMENT_SORT_BY_TYPE), mySortByType);

            try {
                splitterProportions.readExternal(navigatorElement);
            } catch (InvalidDataException e) {
                // ignore
            }
        }
        Element panesElement = parentNode.getChild(ELEMENT_PANES);
        if (panesElement != null) {
            readPaneState(panesElement);
        }
    }

    private void readPaneState(Element panesElement) {
        @SuppressWarnings({ "unchecked" })
        final List<Element> paneElements = panesElement.getChildren(ELEMENT_PANE);

        for (Element paneElement : paneElements) {
            String paneId = paneElement.getAttributeValue(ATTRIBUTE_ID);
            final AbstractProjectViewPane pane = myId2Pane.get(paneId);
            if (pane != null) {
                try {
                    pane.readExternal(paneElement);
                } catch (InvalidDataException e) {
                    // ignore
                }
            } else {
                myUninitializedPaneState.put(paneId, paneElement);
            }
        }
    }

    @Override
    public Element getState() {
        Element parentNode = new Element("projectView");
        Element navigatorElement = new Element(ELEMENT_NAVIGATOR);
        AbstractProjectViewPane currentPane = getCurrentProjectViewPane();
        if (currentPane != null) {
            navigatorElement.setAttribute(ATTRIBUTE_CURRENT_VIEW, currentPane.getId());
            String subId = currentPane.getSubId();
            if (subId != null) {
                navigatorElement.setAttribute(ATTRIBUTE_CURRENT_SUBVIEW, subId);
            }
        }
        writeOption(navigatorElement, myFlattenPackages, ELEMENT_FLATTEN_PACKAGES);
        writeOption(navigatorElement, myShowMembers, ELEMENT_SHOW_MEMBERS);
        writeOption(navigatorElement, myShowModules, ELEMENT_SHOW_MODULES);
        writeOption(navigatorElement, myShowLibraryContents, ELEMENT_SHOW_LIBRARY_CONTENTS);
        writeOption(navigatorElement, myHideEmptyPackages, ELEMENT_HIDE_EMPTY_PACKAGES);
        writeOption(navigatorElement, myAbbreviatePackageNames, ELEMENT_ABBREVIATE_PACKAGE_NAMES);
        writeOption(navigatorElement, myAutoscrollToSource, ELEMENT_AUTOSCROLL_TO_SOURCE);
        writeOption(navigatorElement, myAutoscrollFromSource, ELEMENT_AUTOSCROLL_FROM_SOURCE);
        writeOption(navigatorElement, mySortByType, ELEMENT_SORT_BY_TYPE);

        splitterProportions.saveSplitterProportions(myPanel);
        try {
            splitterProportions.writeExternal(navigatorElement);
        } catch (WriteExternalException e) {
            // ignore
        }
        parentNode.addContent(navigatorElement);

        Element panesElement = new Element(ELEMENT_PANES);
        writePaneState(panesElement);
        parentNode.addContent(panesElement);
        return parentNode;
    }

    private void writePaneState(Element panesElement) {
        for (AbstractProjectViewPane pane : myId2Pane.values()) {
            Element paneElement = new Element(ELEMENT_PANE);
            paneElement.setAttribute(ATTRIBUTE_ID, pane.getId());
            try {
                pane.writeExternal(paneElement);
            } catch (WriteExternalException e) {
                continue;
            }
            panesElement.addContent(paneElement);
        }
        for (Element element : myUninitializedPaneState.values()) {
            panesElement.addContent(element.clone());
        }
    }

    @Override
    public boolean isAutoscrollToSource(String paneId) {
        return getPaneOptionValue(myAutoscrollToSource, paneId,
                UISettings.getInstance().DEFAULT_AUTOSCROLL_TO_SOURCE);
    }

    private void setAutoscrollToSource(boolean autoscrollMode, String paneId) {
        myAutoscrollToSource.put(paneId, autoscrollMode ? Boolean.TRUE : Boolean.FALSE);
    }

    @Override
    public boolean isAutoscrollFromSource(String paneId) {
        return getPaneOptionValue(myAutoscrollFromSource, paneId, ourAutoscrollFromSourceDefaults);
    }

    public void setAutoscrollFromSource(boolean autoscrollMode, String paneId) {
        setPaneOption(myAutoscrollFromSource, autoscrollMode, paneId, false);
    }

    @Override
    public boolean isFlattenPackages(String paneId) {
        return getPaneOptionValue(myFlattenPackages, paneId, ourFlattenPackagesDefaults);
    }

    public void setFlattenPackages(boolean flattenPackages, String paneId) {
        setPaneOption(myFlattenPackages, flattenPackages, paneId, true);
    }

    public boolean isFoldersAlwaysOnTop() {
        return myFoldersAlwaysOnTop;
    }

    public void setFoldersAlwaysOnTop(boolean foldersAlwaysOnTop) {
        if (myFoldersAlwaysOnTop != foldersAlwaysOnTop) {
            myFoldersAlwaysOnTop = foldersAlwaysOnTop;
            for (AbstractProjectViewPane pane : myId2Pane.values()) {
                if (pane.getTree() != null) {
                    pane.updateFromRoot(false);
                }
            }
        }
    }

    @Override
    public boolean isShowMembers(String paneId) {
        return getPaneOptionValue(myShowMembers, paneId, ourShowMembersDefaults);
    }

    public void setShowMembers(boolean showMembers, String paneId) {
        setPaneOption(myShowMembers, showMembers, paneId, true);
    }

    @Override
    public boolean isHideEmptyMiddlePackages(String paneId) {
        return getPaneOptionValue(myHideEmptyPackages, paneId, ourHideEmptyPackagesDefaults);
    }

    @Override
    public boolean isAbbreviatePackageNames(String paneId) {
        return getPaneOptionValue(myAbbreviatePackageNames, paneId, ourAbbreviatePackagesDefaults);
    }

    @Override
    public boolean isShowLibraryContents(String paneId) {
        return getPaneOptionValue(myShowLibraryContents, paneId, ourShowLibraryContentsDefaults);
    }

    @Override
    public void setShowLibraryContents(boolean showLibraryContents, String paneId) {
        setPaneOption(myShowLibraryContents, showLibraryContents, paneId, true);
    }

    public ActionCallback setShowLibraryContentsCB(boolean showLibraryContents, String paneId) {
        return setPaneOption(myShowLibraryContents, showLibraryContents, paneId, true);
    }

    @Override
    public boolean isShowModules(String paneId) {
        return getPaneOptionValue(myShowModules, paneId, ourShowModulesDefaults);
    }

    @Override
    public void setShowModules(boolean showModules, String paneId) {
        setPaneOption(myShowModules, showModules, paneId, true);
    }

    @Override
    public void setHideEmptyPackages(boolean hideEmptyPackages, String paneId) {
        setPaneOption(myHideEmptyPackages, hideEmptyPackages, paneId, true);
    }

    @Override
    public void setAbbreviatePackageNames(boolean abbreviatePackageNames, String paneId) {
        setPaneOption(myAbbreviatePackageNames, abbreviatePackageNames, paneId, true);
    }

    private ActionCallback setPaneOption(Map<String, Boolean> optionsMap, boolean value, String paneId,
            final boolean updatePane) {
        optionsMap.put(paneId, value ? Boolean.TRUE : Boolean.FALSE);
        if (updatePane) {
            final AbstractProjectViewPane pane = getProjectViewPaneById(paneId);
            if (pane != null) {
                return pane.updateFromRoot(false);
            }
        }
        return new ActionCallback.Done();
    }

    private static boolean getPaneOptionValue(Map<String, Boolean> optionsMap, String paneId,
            boolean defaultValue) {
        final Boolean value = optionsMap.get(paneId);
        return value == null ? defaultValue : value.booleanValue();
    }

    private class HideEmptyMiddlePackagesAction extends PaneOptionAction {
        private HideEmptyMiddlePackagesAction() {
            super(myHideEmptyPackages, "", "", null, ourHideEmptyPackagesDefaults);
        }

        @Override
        public void setSelected(AnActionEvent event, boolean flag) {
            final AbstractProjectViewPane viewPane = getCurrentProjectViewPane();
            final SelectionInfo selectionInfo = SelectionInfo.create(viewPane);

            super.setSelected(event, flag);

            selectionInfo.apply(viewPane);
        }

        @Override
        @RequiredDispatchThread
        public void update(AnActionEvent e) {
            super.update(e);
            final Presentation presentation = e.getPresentation();
            Project project = e.getProject();
            assert project != null;
            if (!PsiPackageSupportProviders.isPackageSupported(project)) {
                presentation.setVisible(false);
                return;
            }
            if (isFlattenPackages(myCurrentViewId)) {
                presentation.setText(IdeBundle.message("action.hide.empty.middle.packages"));
                presentation.setDescription(IdeBundle.message("action.show.hide.empty.middle.packages"));
            } else {
                presentation.setText(IdeBundle.message("action.compact.empty.middle.packages"));
                presentation.setDescription(IdeBundle.message("action.show.compact.empty.middle.packages"));
            }
        }
    }

    private static class SelectionInfo {
        private final Object[] myElements;

        private SelectionInfo(Object[] elements) {
            myElements = elements;
        }

        public void apply(final AbstractProjectViewPane viewPane) {
            if (viewPane == null) {
                return;
            }
            AbstractTreeBuilder treeBuilder = viewPane.getTreeBuilder();
            JTree tree = viewPane.myTree;
            DefaultTreeModel treeModel = (DefaultTreeModel) tree.getModel();
            List<TreePath> paths = new ArrayList<TreePath>(myElements.length);
            for (final Object element : myElements) {
                DefaultMutableTreeNode node = treeBuilder.getNodeForElement(element);
                if (node == null) {
                    treeBuilder.buildNodeForElement(element);
                    node = treeBuilder.getNodeForElement(element);
                }
                if (node != null) {
                    paths.add(new TreePath(treeModel.getPathToRoot(node)));
                }
            }
            if (!paths.isEmpty()) {
                tree.setSelectionPaths(paths.toArray(new TreePath[paths.size()]));
            }
        }

        public static SelectionInfo create(final AbstractProjectViewPane viewPane) {
            List<Object> selectedElements = Collections.emptyList();
            if (viewPane != null) {
                final TreePath[] selectionPaths = viewPane.getSelectionPaths();
                if (selectionPaths != null) {
                    selectedElements = new ArrayList<Object>();
                    for (TreePath path : selectionPaths) {
                        final DefaultMutableTreeNode node = (DefaultMutableTreeNode) path.getLastPathComponent();
                        final Object userObject = node.getUserObject();
                        if (userObject instanceof NodeDescriptor) {
                            selectedElements.add(((NodeDescriptor) userObject).getElement());
                        }
                    }
                }
            }
            return new SelectionInfo(selectedElements.toArray());
        }
    }

    private class MyAutoScrollFromSourceHandler extends AutoScrollFromSourceHandler {
        private MyAutoScrollFromSourceHandler() {
            super(ProjectViewImpl.this.myProject, ProjectViewImpl.this.myViewContentPanel, ProjectViewImpl.this);
        }

        @Override
        protected void selectElementFromEditor(@NotNull FileEditor fileEditor) {
            if (myProject.isDisposed() || !myViewContentPanel.isShowing())
                return;
            if (isAutoscrollFromSource(getCurrentViewId())) {
                if (fileEditor instanceof TextEditor) {
                    Editor editor = ((TextEditor) fileEditor).getEditor();
                    selectElementAtCaretNotLosingFocus(editor);
                } else {
                    final VirtualFile file = FileEditorManagerEx.getInstanceEx(myProject).getFile(fileEditor);
                    if (file != null) {
                        final PsiFile psiFile = PsiManager.getInstance(myProject).findFile(file);
                        if (psiFile != null) {
                            final SelectInTarget target = mySelectInTargets.get(getCurrentViewId());
                            if (target != null) {
                                final MySelectInContext selectInContext = new MySelectInContext(psiFile, null) {
                                    @Override
                                    public Object getSelectorInFile() {
                                        return psiFile;
                                    }
                                };

                                if (target.canSelect(selectInContext)) {
                                    target.selectIn(selectInContext, false);
                                }
                            }
                        }
                    }
                }
            }
        }

        public void scrollFromSource() {
            final FileEditorManager fileEditorManager = FileEditorManager.getInstance(myProject);
            final Editor selectedTextEditor = fileEditorManager.getSelectedTextEditor();
            if (selectedTextEditor != null) {
                selectElementAtCaret(selectedTextEditor);
                return;
            }
            final FileEditor[] editors = fileEditorManager.getSelectedEditors();
            for (FileEditor fileEditor : editors) {
                if (fileEditor instanceof TextEditor) {
                    Editor editor = ((TextEditor) fileEditor).getEditor();
                    selectElementAtCaret(editor);
                    return;
                }
            }
            final VirtualFile[] selectedFiles = fileEditorManager.getSelectedFiles();
            if (selectedFiles.length > 0) {
                final PsiFile file = PsiManager.getInstance(myProject).findFile(selectedFiles[0]);
                if (file != null) {
                    scrollFromFile(file, null);
                }
            }
        }

        private void selectElementAtCaretNotLosingFocus(final Editor editor) {
            if (IJSwingUtilities.hasFocus(getCurrentProjectViewPane().getComponentToFocus()))
                return;
            selectElementAtCaret(editor);
        }

        private void selectElementAtCaret(Editor editor) {
            final PsiFile file = PsiDocumentManager.getInstance(myProject).getPsiFile(editor.getDocument());
            if (file == null)
                return;

            scrollFromFile(file, editor);
        }

        private void scrollFromFile(PsiFile file, @Nullable Editor editor) {
            final MySelectInContext selectInContext = new MySelectInContext(file, editor);

            final SelectInTarget target = mySelectInTargets.get(getCurrentViewId());
            if (target != null && target.canSelect(selectInContext)) {
                target.selectIn(selectInContext, false);
            }
        }

        @Override
        protected boolean isAutoScrollEnabled() {
            return isAutoscrollFromSource(myCurrentViewId);
        }

        @Override
        protected void setAutoScrollEnabled(boolean state) {
            setAutoscrollFromSource(state, myCurrentViewId);
            if (state) {
                final Editor editor = myFileEditorManager.getSelectedTextEditor();
                if (editor != null) {
                    selectElementAtCaretNotLosingFocus(editor);
                }
            }
            createToolbarActions();
        }

        private class MySelectInContext implements SelectInContext {
            private final PsiFile myPsiFile;
            @Nullable
            private final Editor myEditor;

            private MySelectInContext(final PsiFile psiFile, @Nullable Editor editor) {
                myPsiFile = psiFile;
                myEditor = editor;
            }

            @Override
            @NotNull
            public Project getProject() {
                return myProject;
            }

            private PsiFile getPsiFile() {
                return myPsiFile;
            }

            @Override
            public FileEditorProvider getFileEditorProvider() {
                if (myPsiFile == null)
                    return null;
                return new FileEditorProvider() {
                    @Override
                    public FileEditor openFileEditor() {
                        return myFileEditorManager.openFile(myPsiFile.getContainingFile().getVirtualFile(),
                                false)[0];
                    }
                };
            }

            private PsiElement getPsiElement() {
                PsiElement e = null;
                if (myEditor != null) {
                    final int offset = myEditor.getCaretModel().getOffset();
                    PsiDocumentManager.getInstance(myProject).commitAllDocuments();
                    e = getPsiFile().findElementAt(offset);
                }
                if (e == null) {
                    e = getPsiFile();
                }
                return e;
            }

            @Override
            @NotNull
            public VirtualFile getVirtualFile() {
                return getPsiFile().getVirtualFile();
            }

            @Override
            public Object getSelectorInFile() {
                return getPsiElement();
            }
        }
    }

    @Override
    public boolean isSortByType(String paneId) {
        return getPaneOptionValue(mySortByType, paneId, ourSortByTypeDefaults);
    }

    @Override
    public void setSortByType(String paneId, final boolean sortByType) {
        setPaneOption(mySortByType, sortByType, paneId, false);
        final AbstractProjectViewPane pane = getProjectViewPaneById(paneId);
        pane.installComparator();
    }

    private class SortByTypeAction extends ToggleAction {
        private SortByTypeAction() {
            super(IdeBundle.message("action.sort.by.type"), IdeBundle.message("action.sort.by.type"),
                    AllIcons.ObjectBrowser.SortByType);
        }

        @Override
        public boolean isSelected(AnActionEvent event) {
            return isSortByType(getCurrentViewId());
        }

        @Override
        public void setSelected(AnActionEvent event, boolean flag) {
            setSortByType(getCurrentViewId(), flag);
        }

        @Override
        public void update(final AnActionEvent e) {
            super.update(e);
            final Presentation presentation = e.getPresentation();
            presentation.setVisible(getCurrentProjectViewPane() != null);
        }
    }

    private class FoldersAlwaysOnTopAction extends ToggleAction {
        private FoldersAlwaysOnTopAction() {
            super("Folders Always on Top");
        }

        @Override
        public boolean isSelected(AnActionEvent event) {
            return isFoldersAlwaysOnTop();
        }

        @Override
        public void setSelected(AnActionEvent event, boolean flag) {
            setFoldersAlwaysOnTop(flag);
        }

        @Override
        @RequiredDispatchThread
        public void update(final AnActionEvent e) {
            super.update(e);
            final Presentation presentation = e.getPresentation();
            presentation.setVisible(getCurrentProjectViewPane() != null);
        }
    }

    private class ScrollFromSourceAction extends AnAction implements DumbAware {
        private ScrollFromSourceAction() {
            super("Scroll from Source", "Select the file open in the active editor", AllIcons.General.Locate);
            getTemplatePresentation().setHoveredIcon(AllIcons.General.LocateHover);
        }

        @Override
        public void actionPerformed(AnActionEvent e) {
            myAutoScrollFromSourceHandler.scrollFromSource();
        }
    }

    @Override
    public Collection<String> getPaneIds() {
        return myId2Pane.keySet();
    }

    @Override
    public Collection<SelectInTarget> getSelectInTargets() {
        ensurePanesLoaded();
        return mySelectInTargets.values();
    }

    @Override
    public ActionCallback getReady(@NotNull Object requestor) {
        AbstractProjectViewPane pane = myId2Pane.get(myCurrentViewSubId);
        if (pane == null) {
            pane = myId2Pane.get(myCurrentViewId);
        }
        return pane != null ? pane.getReady(requestor) : new ActionCallback.Done();
    }
}