// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. package com.intellij.ide.projectView.impl; import com.intellij.history.LocalHistory; import com.intellij.history.LocalHistoryAction; import com.intellij.icons.AllIcons; import com.intellij.ide.DeleteProvider; import com.intellij.ide.IdeBundle; import com.intellij.ide.SelectInTarget; import com.intellij.ide.impl.PackagesPaneSelectInTarget; import com.intellij.ide.projectView.ProjectView; import com.intellij.ide.projectView.ViewSettings; import com.intellij.ide.projectView.impl.nodes.PackageElement; import com.intellij.ide.projectView.impl.nodes.PackageElementNode; import com.intellij.ide.projectView.impl.nodes.PackageViewProjectNode; import com.intellij.ide.util.DeleteHandler; import com.intellij.ide.util.treeView.AbstractTreeNode; import com.intellij.java.JavaBundle; import com.intellij.openapi.actionSystem.*; import com.intellij.openapi.module.Module; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.registry.Registry; import com.intellij.psi.PsiDirectory; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiPackage; import com.intellij.psi.search.GlobalSearchScope; import com.intellij.psi.util.PsiUtilCore; import com.intellij.util.ObjectUtils; import com.intellij.util.PlatformUtils; import com.intellij.util.concurrency.annotations.RequiresBackgroundThread; import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; import javax.swing.tree.DefaultTreeModel; import java.util.ArrayList; import java.util.Collections; import java.util.List; import static com.intellij.openapi.module.ModuleGrouperKt.isQualifiedModuleNamesEnabled; public class PackageViewPane extends AbstractProjectViewPaneWithAsyncSupport { public static final @NonNls String ID = "PackagesPane"; private final MyDeletePSIElementProvider myDeletePSIElementProvider = new MyDeletePSIElementProvider(); public PackageViewPane(Project project) { super(project); } @ApiStatus.Internal @Override protected void configureAsyncSupport(@NotNull ProjectViewPaneSupport support) { support.setMultiSelectionEnabled(false); } @Override public @NotNull String getTitle() { return JavaBundle.message("title.packages"); } @Override public @NotNull Icon getIcon() { return AllIcons.Nodes.CopyOfFolder; } @Override public @NotNull String getId() { return ID; } @Override public @NotNull List getElementsFromNode(@Nullable Object node) { Object o = getValueFromNode(node); if (o instanceof PackageElement) { PsiPackage aPackage = ((PackageElement)o).getPackage(); return ContainerUtil.createMaybeSingletonList(aPackage.isValid() ? aPackage : null); } return super.getElementsFromNode(node); } @Override protected Module getNodeModule(@Nullable Object element) { if (element instanceof PackageElement) { return ((PackageElement)element).getModule(); } return super.getNodeModule(element); } @Override protected void uiDataSnapshotForSelection(@NotNull DataSink sink, @Nullable Object @NotNull [] selectedUserObjects, @Nullable Object @Nullable [] singleSelectedPathUserObjects) { super.uiDataSnapshotForSelection(sink, selectedUserObjects, singleSelectedPathUserObjects); sink.lazy(PlatformDataKeys.DELETE_ELEMENT_PROVIDER, () -> { Object o = selectedUserObjects.length != 1 ? null : getValueFromNode(selectedUserObjects[0]); if (o instanceof PackageElement) { return myDeletePSIElementProvider; } return null; }); sink.lazy(PackageElement.DATA_KEY, () -> { Object value = selectedUserObjects.length != 1 ? null : getValueFromNode(selectedUserObjects[0]); return value instanceof PackageElement o ? o : null; }); sink.lazy(PlatformCoreDataKeys.MODULE, () -> { Object value = selectedUserObjects.length != 1 ? null : getValueFromNode(selectedUserObjects[0]); return value instanceof PackageElement o ? o.getModule() : null; }); } @RequiresBackgroundThread(generateAssertion = false) @Override protected PsiDirectory @NotNull [] getSelectedDirectories(Object @NotNull[] objects) { List directories = new ArrayList<>(); for (Object obj : objects) { PackageElementNode node = ObjectUtils.tryCast(obj, PackageElementNode.class); if (node != null) { PackageElement packageElement = node.getValue(); PsiPackage aPackage = packageElement != null ? packageElement.getPackage() : null; final Module module = packageElement != null ? packageElement.getModule() : null; if (aPackage != null && module != null) { GlobalSearchScope scope = GlobalSearchScope.moduleScope(module); Collections.addAll(directories, aPackage.getDirectories(scope)); if (Registry.is("projectView.choose.directory.on.compacted.middle.packages")) { Object parentValue = node.getParent().getValue(); PsiPackage parentNodePackage = parentValue instanceof PackageElement ? ((PackageElement)parentValue).getPackage() : null; while (true) { aPackage = aPackage.getParentPackage(); if (aPackage == null || aPackage.getQualifiedName().isEmpty() || aPackage.equals(parentNodePackage)) { break; } Collections.addAll(directories, aPackage.getDirectories(scope)); } } } } } if (!directories.isEmpty()) { return directories.toArray(PsiDirectory.EMPTY_ARRAY); } return super.getSelectedDirectories(objects); } @Override public @NotNull SelectInTarget createSelectInTarget() { return new PackagesPaneSelectInTarget(myProject); } @Override protected @NotNull ProjectAbstractTreeStructureBase createStructure() { return new ProjectTreeStructure(myProject, ID){ @Override protected AbstractTreeNode createRoot(final @NotNull Project project, @NotNull ViewSettings settings) { return new PackageViewProjectNode(project, settings); } @Override public boolean isToBuildChildrenInBackground(@NotNull Object element) { return Registry.is("ide.projectView.PackageViewTreeStructure.BuildChildrenInBackground"); } }; } @Override protected @NotNull ProjectViewTree createTree(@NotNull DefaultTreeModel treeModel) { return new ProjectViewTree(treeModel) { public String toString() { return getTitle() + " " + super.toString(); } }; } public @NotNull String getComponentName() { return "PackagesPane"; } @Override public int getWeight() { return 1; } private Project getProject() { return myProject; } private final class MyDeletePSIElementProvider implements DeleteProvider { @Override public @NotNull ActionUpdateThread getActionUpdateThread() { return ActionUpdateThread.BGT; } @Override public boolean canDeleteElement(@NotNull DataContext dataContext) { Object[] objs = PlatformCoreDataKeys.SELECTED_ITEMS.getData(dataContext); if (objs != null && objs.length > 0) { for (PsiDirectory directory : getSelectedDirectories(objs)) { if (!directory.getManager().isInProject(directory)) return false; } } return true; } @Override public void deleteElement(@NotNull DataContext dataContext) { Object[] objs = PlatformCoreDataKeys.SELECTED_ITEMS.getData(dataContext); PsiDirectory[] allElements = objs != null ? getSelectedDirectories(objs) : PsiDirectory.EMPTY_ARRAY; List validElements = new ArrayList<>(); for (PsiElement psiElement : allElements) { if (psiElement != null && psiElement.isValid()) validElements.add(psiElement); } final PsiElement[] elements = PsiUtilCore.toPsiElementArray(validElements); LocalHistoryAction a = LocalHistory.getInstance().startAction(IdeBundle.message("progress.deleting")); try { DeleteHandler.deletePsiElement(elements, getProject()); } finally { a.finish(); } } } @Override public boolean supportsFlattenModules() { return PlatformUtils.isIntelliJ() && isQualifiedModuleNamesEnabled(myProject) && ProjectView.getInstance(myProject).isShowModules(ID); } @Override public boolean supportsShowLibraryContents() { return true; } @Override public boolean supportsShowModules() { return PlatformUtils.isIntelliJ(); } }