blob: ac9ddb4f95f64ced8d2bf1a8ffc20c06a305b634 [file] [log] [blame]
/*
* Copyright 2000-2009 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.ide.util;
import com.intellij.CommonBundle;
import com.intellij.icons.AllIcons;
import com.intellij.ide.IdeBundle;
import com.intellij.openapi.actionSystem.*;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.command.CommandProcessor;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.event.*;
import com.intellij.openapi.editor.event.DocumentAdapter;
import com.intellij.openapi.fileChooser.ex.FileChooserDialogImpl;
import com.intellij.openapi.fileChooser.ex.TextFieldAction;
import com.intellij.openapi.fileTypes.StdFileTypes;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.ContentIterator;
import com.intellij.openapi.roots.FileIndex;
import com.intellij.openapi.roots.ModuleRootManager;
import com.intellij.openapi.roots.ProjectRootManager;
import com.intellij.openapi.ui.InputValidator;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.ui.PackageChooser;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.*;
import com.intellij.ui.*;
import com.intellij.ui.components.labels.LinkLabel;
import com.intellij.ui.treeStructure.Tree;
import com.intellij.util.Alarm;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.PlatformIcons;
import com.intellij.util.containers.Convertor;
import com.intellij.util.ui.UIUtil;
import com.intellij.util.ui.tree.TreeUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jps.model.java.JavaModuleSourceRootTypes;
import javax.swing.*;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeCellRenderer;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreePath;
import java.awt.*;
import java.util.Comparator;
import java.util.Enumeration;
import java.util.List;
public class PackageChooserDialog extends PackageChooser {
private static final Logger LOG = Logger.getInstance("#com.intellij.ide.util.PackageChooserDialog");
private Tree myTree;
private DefaultTreeModel myModel;
private final Project myProject;
private final String myTitle;
private Module myModule;
private EditorTextField myPathEditor;
private Alarm myAlarm = new Alarm(getDisposable());
public PackageChooserDialog(String title, @NotNull Module module) {
super(module.getProject(), true);
setTitle(title);
myTitle = title;
myProject = module.getProject();
myModule = module;
init();
}
public PackageChooserDialog(String title, Project project) {
super(project, true);
setTitle(title);
myTitle = title;
myProject = project;
init();
}
protected JComponent createCenterPanel() {
JPanel panel = new JPanel();
panel.setLayout(new BorderLayout());
myModel = new DefaultTreeModel(new DefaultMutableTreeNode());
createTreeModel();
myTree = new Tree(myModel);
UIUtil.setLineStyleAngled(myTree);
myTree.setCellRenderer(
new DefaultTreeCellRenderer() {
public Component getTreeCellRendererComponent(
JTree tree, Object value,
boolean sel,
boolean expanded,
boolean leaf, int row,
boolean hasFocus
) {
super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus);
setIcon(PlatformIcons.PACKAGE_ICON);
if (value instanceof DefaultMutableTreeNode) {
DefaultMutableTreeNode node = (DefaultMutableTreeNode)value;
Object object = node.getUserObject();
if (object instanceof PsiPackage) {
String name = ((PsiPackage)object).getName();
if (name != null && name.length() > 0) {
setText(name);
}
else {
setText(IdeBundle.message("node.default"));
}
}
}
return this;
}
}
);
JScrollPane scrollPane = ScrollPaneFactory.createScrollPane(myTree);
scrollPane.setPreferredSize(new Dimension(500, 300));
new TreeSpeedSearch(myTree, new Convertor<TreePath, String>() {
public String convert(TreePath path) {
DefaultMutableTreeNode node = (DefaultMutableTreeNode)path.getLastPathComponent();
Object object = node.getUserObject();
if (object instanceof PsiPackage) return ((PsiPackage)object).getName();
else
return "";
}
});
myTree.getSelectionModel().addTreeSelectionListener(new TreeSelectionListener() {
public void valueChanged(TreeSelectionEvent e) {
PsiPackage selection = getTreeSelection();
if (selection != null) {
String name = selection.getQualifiedName();
setTitle(myTitle + " - " + ("".equals(name) ? IdeBundle.message("node.default.package") : name));
}
else {
setTitle(myTitle);
}
updatePathFromTree();
}
});
panel.add(scrollPane, BorderLayout.CENTER);
DefaultActionGroup group = createActionGroup(myTree);
final JPanel northPanel = new JPanel(new BorderLayout());
panel.add(northPanel, BorderLayout.NORTH);
ActionToolbar toolBar = ActionManager.getInstance().createActionToolbar(ActionPlaces.UNKNOWN, group, true);
northPanel.add(toolBar.getComponent(), BorderLayout.WEST);
setupPathComponent(northPanel);
return panel;
}
private void setupPathComponent(final JPanel northPanel) {
northPanel.add(new TextFieldAction() {
@Override
public void linkSelected(LinkLabel aSource, Object aLinkData) {
toggleShowPathComponent(northPanel, this);
}
}, BorderLayout.EAST);
myPathEditor = new EditorTextField(JavaReferenceEditorUtil.createDocument("", myProject, false), myProject, StdFileTypes.JAVA);
myPathEditor.addDocumentListener(new DocumentAdapter() {
@Override
public void documentChanged(DocumentEvent e) {
myAlarm.cancelAllRequests();
myAlarm.addRequest(new Runnable() {
@Override
public void run() {
updateTreeFromPath();
}
}, 300);
}
});
myPathEditor.setBorder(BorderFactory.createEmptyBorder(0, 0, 2, 0));
northPanel.add(myPathEditor, BorderLayout.SOUTH);
}
private void toggleShowPathComponent(JPanel northPanel, TextFieldAction fieldAction) {
boolean toShowTextField = !isPathShowing();
PropertiesComponent.getInstance().setValue(FileChooserDialogImpl.FILE_CHOOSER_SHOW_PATH_PROPERTY, Boolean.toString(toShowTextField));
myPathEditor.setVisible(toShowTextField);
fieldAction.update();
northPanel.revalidate();
northPanel.repaint();
updatePathFromTree();
}
private static boolean isPathShowing() {
return PropertiesComponent.getInstance().getBoolean(FileChooserDialogImpl.FILE_CHOOSER_SHOW_PATH_PROPERTY, true);
}
private void updatePathFromTree() {
if (!isPathShowing()) return;
final PsiPackage selection = getTreeSelection();
myPathEditor.setText(selection != null ? selection.getQualifiedName() : "");
}
private void updateTreeFromPath() {
selectPackage(myPathEditor.getText().trim());
}
private DefaultActionGroup createActionGroup(JComponent component) {
final DefaultActionGroup group = new DefaultActionGroup();
final DefaultActionGroup temp = new DefaultActionGroup();
NewPackageAction newPackageAction = new NewPackageAction();
newPackageAction.enableInModalConext();
newPackageAction.registerCustomShortcutSet(ActionManager.getInstance().getAction(IdeActions.ACTION_NEW_ELEMENT).getShortcutSet(), component);
temp.add(newPackageAction);
group.add(temp);
return group;
}
protected void doOKAction(){
super.doOKAction();
}
public String getDimensionServiceKey(){
return "#com.intellij.ide.util.PackageChooserDialog";
}
public JComponent getPreferredFocusedComponent(){
return myTree;
}
public PsiPackage getSelectedPackage(){
return getTreeSelection();
}
public List<PsiPackage> getSelectedPackages() {
return TreeUtil.collectSelectedObjectsOfType(myTree, PsiPackage.class);
}
public void selectPackage(final String qualifiedName) {
/*ApplicationManager.getApplication().invokeLater(new Runnable() {
public void run() {*/
DefaultMutableTreeNode node = findNodeForPackage(qualifiedName);
if (node != null) {
TreePath path = new TreePath(node.getPath());
TreeUtil.selectPath(myTree, path);
}
/* }
}, ModalityState.stateForComponent(getRootPane()));*/
}
@Nullable
private PsiPackage getTreeSelection() {
if (myTree == null) return null;
TreePath path = myTree.getSelectionPath();
if (path == null) return null;
DefaultMutableTreeNode node = (DefaultMutableTreeNode)path.getLastPathComponent();
return (PsiPackage)node.getUserObject();
}
private void createTreeModel() {
final PsiManager psiManager = PsiManager.getInstance(myProject);
final FileIndex fileIndex = myModule != null ? ModuleRootManager.getInstance(myModule).getFileIndex() : ProjectRootManager.getInstance(myProject).getFileIndex();
fileIndex.iterateContent(
new ContentIterator() {
public boolean processFile(VirtualFile fileOrDir) {
if (fileOrDir.isDirectory() && fileIndex.isUnderSourceRootOfType(fileOrDir, JavaModuleSourceRootTypes.SOURCES)) {
final PsiDirectory psiDirectory = psiManager.findDirectory(fileOrDir);
LOG.assertTrue(psiDirectory != null);
PsiPackage aPackage = JavaDirectoryService.getInstance().getPackage(psiDirectory);
if (aPackage != null){
addPackage(aPackage);
}
}
return true;
}
}
);
TreeUtil.sort(myModel, new Comparator() {
public int compare(Object o1, Object o2) {
DefaultMutableTreeNode n1 = (DefaultMutableTreeNode) o1;
DefaultMutableTreeNode n2 = (DefaultMutableTreeNode) o2;
PsiNamedElement element1 = (PsiNamedElement) n1.getUserObject();
PsiNamedElement element2 = (PsiNamedElement) n2.getUserObject();
return element1.getName().compareToIgnoreCase(element2.getName());
}
});
}
@NotNull
private DefaultMutableTreeNode addPackage(PsiPackage aPackage) {
final String qualifiedPackageName = aPackage.getQualifiedName();
final PsiPackage parentPackage = aPackage.getParentPackage();
if (parentPackage == null) {
final DefaultMutableTreeNode rootNode = (DefaultMutableTreeNode)myModel.getRoot();
if (qualifiedPackageName.length() == 0) {
rootNode.setUserObject(aPackage);
return rootNode;
}
else {
DefaultMutableTreeNode packageNode = findPackageNode(rootNode, qualifiedPackageName);
if (packageNode != null) return packageNode;
packageNode = new DefaultMutableTreeNode(aPackage);
rootNode.add(packageNode);
return packageNode;
}
}
else {
final DefaultMutableTreeNode parentNode = addPackage(parentPackage);
DefaultMutableTreeNode packageNode = findPackageNode(parentNode, qualifiedPackageName);
if (packageNode != null) {
return packageNode;
}
packageNode = new DefaultMutableTreeNode(aPackage);
parentNode.add(packageNode);
return packageNode;
}
}
@Nullable
private static DefaultMutableTreeNode findPackageNode(DefaultMutableTreeNode rootNode, String qualifiedName) {
for (int i = 0; i < rootNode.getChildCount(); i++) {
final DefaultMutableTreeNode child = (DefaultMutableTreeNode)rootNode.getChildAt(i);
final PsiPackage nodePackage = (PsiPackage)child.getUserObject();
if (nodePackage != null) {
if (Comparing.equal(nodePackage.getQualifiedName(), qualifiedName)) return child;
}
}
return null;
}
private DefaultMutableTreeNode findNodeForPackage(String qualifiedPackageName) {
DefaultMutableTreeNode root = (DefaultMutableTreeNode)myModel.getRoot();
Enumeration enumeration = root.depthFirstEnumeration();
while (enumeration.hasMoreElements()) {
Object o = enumeration.nextElement();
if (o instanceof DefaultMutableTreeNode) {
DefaultMutableTreeNode node = (DefaultMutableTreeNode)o;
PsiPackage nodePackage = (PsiPackage)node.getUserObject();
if (nodePackage != null) {
if (Comparing.equal(nodePackage.getQualifiedName(), qualifiedPackageName)) return node;
}
}
}
return null;
}
private void createNewPackage() {
final PsiPackage selectedPackage = getTreeSelection();
if (selectedPackage == null) return;
final String newPackageName = Messages.showInputDialog(myProject, IdeBundle.message("prompt.enter.a.new.package.name"), IdeBundle.message("title.new.package"), Messages.getQuestionIcon(), "",
new InputValidator() {
public boolean checkInput(final String inputString) {
return inputString != null && inputString.length() > 0;
}
public boolean canClose(final String inputString) {
return checkInput(inputString);
}
});
if (newPackageName == null) return;
CommandProcessor.getInstance().executeCommand(myProject, new Runnable() {
public void run() {
final Runnable action = new Runnable() {
public void run() {
try {
String newQualifiedName = selectedPackage.getQualifiedName();
if (!Comparing.strEqual(newQualifiedName,"")) newQualifiedName += ".";
newQualifiedName += newPackageName;
final PsiDirectory dir = PackageUtil.findOrCreateDirectoryForPackage(myProject, newQualifiedName, null, false);
if (dir == null) return;
final PsiPackage newPackage = JavaDirectoryService.getInstance().getPackage(dir);
DefaultMutableTreeNode node = (DefaultMutableTreeNode)myTree.getSelectionPath().getLastPathComponent();
final DefaultMutableTreeNode newChild = new DefaultMutableTreeNode();
newChild.setUserObject(newPackage);
node.add(newChild);
final DefaultTreeModel model = (DefaultTreeModel)myTree.getModel();
model.nodeStructureChanged(node);
final TreePath selectionPath = myTree.getSelectionPath();
TreePath path;
if (selectionPath == null) {
path = new TreePath(newChild.getPath());
} else {
path = selectionPath.pathByAddingChild(newChild);
}
myTree.setSelectionPath(path);
myTree.scrollPathToVisible(path);
myTree.expandPath(path);
}
catch (IncorrectOperationException e) {
Messages.showMessageDialog(
getContentPane(),
StringUtil.getMessage(e),
CommonBundle.getErrorTitle(),
Messages.getErrorIcon()
);
if (LOG.isDebugEnabled()) {
LOG.debug(e);
}
}
}
};
ApplicationManager.getApplication().runReadAction(action);
}
},
IdeBundle.message("command.create.new.package"),
null);
}
private class NewPackageAction extends AnAction {
public NewPackageAction() {
super(IdeBundle.message("action.new.package"),
IdeBundle.message("action.description.create.new.package"), AllIcons.Actions.NewFolder);
}
public void actionPerformed(AnActionEvent e) {
createNewPackage();
}
public void update(AnActionEvent event) {
Presentation presentation = event.getPresentation();
presentation.setEnabled(getTreeSelection() != null);
}
public void enableInModalConext() {
setEnabledInModalContext(true);
}
}
}