blob: 368d76ddb4eb20621dadf56fe626bbc1921e8226 [file] [log] [blame]
/*
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.tools.idea.navigator;
import com.android.tools.idea.navigator.nodes.AndroidViewProjectNode;
import com.android.tools.idea.navigator.nodes.DirectoryGroupNode;
import com.google.common.collect.Lists;
import com.intellij.ide.SelectInTarget;
import com.intellij.ide.impl.ProjectPaneSelectInTarget;
import com.intellij.ide.projectView.BaseProjectTreeBuilder;
import com.intellij.ide.projectView.ViewSettings;
import com.intellij.ide.projectView.impl.AbstractProjectViewPSIPane;
import com.intellij.ide.projectView.impl.ProjectAbstractTreeStructureBase;
import com.intellij.ide.projectView.impl.ProjectTreeStructure;
import com.intellij.ide.projectView.impl.ProjectViewTree;
import com.intellij.ide.projectView.impl.nodes.PackageElement;
import com.intellij.ide.util.treeView.AbstractTreeBuilder;
import com.intellij.ide.util.treeView.AbstractTreeNode;
import com.intellij.ide.util.treeView.AbstractTreeUpdater;
import com.intellij.ide.util.treeView.NodeDescriptor;
import com.intellij.openapi.actionSystem.CommonDataKeys;
import com.intellij.openapi.actionSystem.DataProvider;
import com.intellij.openapi.actionSystem.LangDataKeys;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.GeneratedSourcesFilter;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiDirectory;
import com.intellij.psi.PsiElement;
import com.intellij.psi.search.GlobalSearchScope;
import icons.AndroidIcons;
import org.jetbrains.android.facet.AndroidFacet;
import org.jetbrains.android.facet.IdeaSourceProvider;
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 java.util.ArrayList;
import java.util.List;
public class AndroidProjectViewPane extends AbstractProjectViewPSIPane {
// Note: This value is duplicated in ProjectViewImpl.java to set the default view to be the Android project view.
public static final String ID = "AndroidView";
public AndroidProjectViewPane(Project project) {
super(project);
}
@Override
public String getTitle() {
return "Android";
}
@Override
public Icon getIcon() {
return AndroidIcons.Android;
}
@NotNull
@Override
public String getId() {
return ID;
}
@Override
public int getWeight() {
// used for sorting the sequence of panes, but the weight cannot match any existing pane's weight
// IDEA's panes start with 0 (project view pane) and go up (1 for package view, favorites seems to use 4, ..)
return 142;
}
@NotNull
public static List<IdeaSourceProvider> getSourceProviders(@NotNull AndroidFacet facet) {
List<IdeaSourceProvider> sourceProviders = IdeaSourceProvider.getCurrentSourceProviders(facet);
sourceProviders.addAll(IdeaSourceProvider.getCurrentTestSourceProviders(facet));
return sourceProviders;
}
@Override
public SelectInTarget createSelectInTarget() {
return new ProjectPaneSelectInTarget(myProject);
}
@Override
protected ProjectAbstractTreeStructureBase createStructure() {
return new ProjectTreeStructure(myProject, ID) {
@Override
protected AbstractTreeNode createRoot(Project project, ViewSettings settings) {
return new AndroidViewProjectNode(project, settings, AndroidProjectViewPane.this);
}
};
}
@NotNull
@Override
protected BaseProjectTreeBuilder createBuilder(DefaultTreeModel treeModel) {
return new AndroidProjectTreeBuilder(myProject, myTree, treeModel, null, (ProjectAbstractTreeStructureBase)myTreeStructure);
}
@Override
protected ProjectViewTree createTree(final DefaultTreeModel treeModel) {
return new MyProjectViewTree(treeModel);
}
@Override
protected AbstractTreeUpdater createTreeUpdater(AbstractTreeBuilder treeBuilder) {
return new AbstractTreeUpdater(treeBuilder);
}
@Override
public PsiDirectory[] getSelectedDirectories() {
Object selectedElement = getSelectedElement();
if (selectedElement instanceof PackageElement) {
PackageElement packageElement = (PackageElement)selectedElement;
Module m = packageElement.getModule();
if (m != null) {
return packageElement.getPackage().getDirectories(GlobalSearchScope.moduleScope(m));
}
}
NodeDescriptor descriptor = getSelectedDescriptor();
if (descriptor instanceof DirectoryGroupNode) {
return ((DirectoryGroupNode)descriptor).getDirectories();
}
PsiDirectory[] selectedDirectories = super.getSelectedDirectories();
// For modules we'll include generated folders too but we don't want
// to treat these as selectable (for target output directories etc)
if (selectedElement instanceof Module && selectedDirectories.length > 0) {
List<PsiDirectory> dirs = Lists.newArrayListWithExpectedSize(selectedDirectories.length);
for (PsiDirectory dir : selectedDirectories) {
VirtualFile file = dir.getVirtualFile();
if (!GeneratedSourcesFilter.isGeneratedSourceByAnyFilter(file, myProject)) {
if (file.getParent() != null && file.getPath().contains("/generated/")) {
// Workaround for https://code.google.com/p/android/issues/detail?id=79843:
// The generated resource folders (e.g. for RenderScript) cannot be marked as generated, e.g.
/// build/generated/res/rs/debug, build/generated/res/rs/test/debug, etc.
continue;
}
dirs.add(dir);
}
}
selectedDirectories = dirs.toArray(new PsiDirectory[dirs.size()]);
}
return selectedDirectories;
}
@Override
protected Object exhumeElementFromNode(DefaultMutableTreeNode node) {
Object o = super.exhumeElementFromNode(node);
if (o instanceof ArrayList && node.getUserObject() instanceof DirectoryGroupNode) {
return ((ArrayList)o).toArray();
}
return o;
}
@Override
public Object getData(String dataId) {
if (CommonDataKeys.PROJECT.is(dataId)) {
return myProject;
}
if (LangDataKeys.MODULE.is(dataId)) {
Object o = getSelectedElement();
if (o instanceof PackageElement) {
PackageElement packageElement = (PackageElement)o;
return packageElement.getModule();
} else if (o instanceof AndroidFacet) {
return ((AndroidFacet)o).getModule();
}
}
if (CommonDataKeys.VIRTUAL_FILE.is(dataId)) {
Object o = getSelectedElement();
if (o instanceof PackageElement) {
PackageElement packageElement = (PackageElement)o;
Module m = packageElement.getModule();
if (m != null) {
PsiDirectory[] folders = packageElement.getPackage().getDirectories(GlobalSearchScope.moduleScope(m));
if (folders.length > 0) {
return folders[0].getVirtualFile();
} else {
return null;
}
}
}
}
if (CommonDataKeys.VIRTUAL_FILE_ARRAY.is(dataId)) {
NodeDescriptor selectedDescriptor = getSelectedDescriptor();
if (selectedDescriptor instanceof DirectoryGroupNode) {
PsiDirectory[] directories = ((DirectoryGroupNode)selectedDescriptor).getDirectories();
if (directories.length > 0) {
List<VirtualFile> virtualFiles = Lists.newArrayListWithExpectedSize(directories.length);
for (PsiDirectory directory : directories) {
if (directory.isValid()) {
virtualFiles.add(directory.getVirtualFile());
}
}
return virtualFiles.toArray(new VirtualFile[virtualFiles.size()]);
}
}
}
if (CommonDataKeys.PSI_ELEMENT.is(dataId)) {
Object o = getSelectedElement();
if (o instanceof PsiElement) {
return o;
} else if (o instanceof List<?>) {
List<?> l = (List<?>) o;
if (!l.isEmpty() && l.get(0) instanceof PsiElement) {
return l.get(0);
}
}
NodeDescriptor selectedDescriptor = getSelectedDescriptor();
if (selectedDescriptor instanceof DirectoryGroupNode) {
PsiDirectory[] directories = ((DirectoryGroupNode)selectedDescriptor).getDirectories();
if (directories.length > 0) {
return directories[0];
}
}
}
return super.getData(dataId);
}
private class MyProjectViewTree extends ProjectViewTree implements DataProvider {
public MyProjectViewTree(DefaultTreeModel treeModel) {
super(AndroidProjectViewPane.this.myProject, treeModel);
}
@Override
public DefaultMutableTreeNode getSelectedNode() {
return AndroidProjectViewPane.this.getSelectedNode();
}
@Nullable
@Override
public Object getData(@NonNls String dataId) {
return AndroidProjectViewPane.this.getData(dataId);
}
}
}