blob: 86b5a042f1bc867cdd15a6ba8fd5c4047fa40966 [file] [log] [blame]
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.ide.util.newProjectWizard;
import com.intellij.CommonBundle;
import com.intellij.ide.IdeBundle;
import com.intellij.ide.util.BrowseFilesListener;
import com.intellij.ide.util.ElementsChooser;
import com.intellij.ide.util.projectWizard.AbstractStepWithProgress;
import com.intellij.ide.util.projectWizard.SourcePathsBuilder;
import com.intellij.ide.util.projectWizard.importSources.JavaModuleSourceRoot;
import com.intellij.ide.util.projectWizard.importSources.JavaSourceRootDetectionUtil;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ApplicationNamesInfo;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.fileChooser.FileChooserDescriptor;
import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory;
import com.intellij.openapi.options.ConfigurationException;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.ui.MultiLineLabelUI;
import com.intellij.openapi.util.Computable;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VfsUtil;
import com.intellij.openapi.vfs.VfsUtilCore;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.ui.DocumentAdapter;
import com.intellij.ui.FieldPanel;
import com.intellij.util.StringBuilderSpinAllocator;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import javax.swing.event.DocumentEvent;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
/**
* @author Eugene Zhuravlev
* Date: Jan 6, 2004
*/
public class SourcePathsStep extends AbstractStepWithProgress<List<JavaModuleSourceRoot>> {
private static final Logger LOG = Logger.getInstance("#com.intellij.ide.util.newProjectWizard.SourcePathsStep");
private String myCurrentMode;
@NonNls private static final String CREATE_SOURCE_PANEL = "create_source";
@NonNls private static final String CHOOSE_SOURCE_PANEL = "choose_source";
private final SourcePathsBuilder myBuilder;
private final Icon myIcon;
private final String myHelpId;
private ElementsChooser<JavaModuleSourceRoot> mySourcePathsChooser;
private String myCurrentContentEntryPath = null;
private JRadioButton myRbCreateSource;
private JRadioButton myRbNoSource;
private JTextField myTfSourceDirectoryName;
private JTextField myTfFullPath;
private JPanel myResultPanel;
public SourcePathsStep(SourcePathsBuilder builder, Icon icon, @NonNls String helpId) {
super(IdeBundle.message("prompt.stop.searching.for.sources", ApplicationNamesInfo.getInstance().getProductName()));
myBuilder = builder;
myIcon = icon;
myHelpId = helpId;
}
protected JComponent createResultsPanel() {
myResultPanel = new JPanel(new CardLayout());
myResultPanel.add(createComponentForEmptyRootCase(), CREATE_SOURCE_PANEL);
myResultPanel.add(createComponentForChooseSources(), CHOOSE_SOURCE_PANEL);
return myResultPanel;
}
private JComponent createComponentForEmptyRootCase() {
final JPanel panel = new JPanel(new GridBagLayout());
final String text = IdeBundle.message("prompt.please.specify.java.sources.directory");
final JLabel label = new JLabel(text);
label.setUI(new MultiLineLabelUI());
panel.add(label, new GridBagConstraints(0, GridBagConstraints.RELATIVE, 1, 1, 1.0, 0.0, GridBagConstraints.NORTHWEST, GridBagConstraints.NONE, new Insets(8, 10, 0, 10), 0, 0));
myRbCreateSource = new JRadioButton(IdeBundle.message("radio.create.source.directory"), true);
panel.add(myRbCreateSource, new GridBagConstraints(0, GridBagConstraints.RELATIVE, 1, 1, 1.0, 0.0, GridBagConstraints.NORTHWEST, GridBagConstraints.NONE, new Insets(8, 10, 0, 10), 0, 0));
myTfSourceDirectoryName = new JTextField(suggestSourceDirectoryName());
final JLabel srcPathLabel = new JLabel(IdeBundle.message("prompt.enter.relative.path.to.module.content.root", File.separator));
panel.add(srcPathLabel, new GridBagConstraints(0, GridBagConstraints.RELATIVE, 1, 1, 1.0, 0.0, GridBagConstraints.NORTHWEST, GridBagConstraints.HORIZONTAL, new Insets(8, 30, 0, 0), 0, 0));
final FileChooserDescriptor chooserDescriptor = FileChooserDescriptorFactory.createSingleFolderDescriptor();
chooserDescriptor.withTreeRootVisible(true);
final FieldPanel fieldPanel = createFieldPanel(myTfSourceDirectoryName, null, new BrowsePathListener(myTfSourceDirectoryName, chooserDescriptor));
panel.add(fieldPanel, new GridBagConstraints(0, GridBagConstraints.RELATIVE, 1, 1, 1.0, 0.0, GridBagConstraints.NORTHWEST, GridBagConstraints.HORIZONTAL, new Insets(8, 30, 0, 10), 0, 0));
myRbNoSource = new JRadioButton(IdeBundle.message("radio.do.not.create.source.directory"), true);
panel.add(myRbNoSource, new GridBagConstraints(0, GridBagConstraints.RELATIVE, 1, 1, 1.0, 1.0, GridBagConstraints.NORTHWEST, GridBagConstraints.NONE, new Insets(8, 10, 0, 10), 0, 0));
final JLabel fullPathLabel = new JLabel(IdeBundle.message("label.source.directory"));
panel.add(fullPathLabel, new GridBagConstraints(0, GridBagConstraints.RELATIVE, 1, 1, 1.0, 0.0, GridBagConstraints.SOUTHWEST, GridBagConstraints.NONE, new Insets(8, 10, 0, 10), 0, 0));
myTfFullPath = new JTextField();
myTfFullPath.setEditable(false);
myTfFullPath.setOpaque(false);
final Insets borderInsets = myTfFullPath.getBorder().getBorderInsets(myTfFullPath);
myTfFullPath.setBorder(BorderFactory.createEmptyBorder(borderInsets.top, borderInsets.left, borderInsets.bottom, borderInsets.right));
panel.add(myTfFullPath, new GridBagConstraints(0, GridBagConstraints.RELATIVE, 1, 1, 1.0, 0.0, GridBagConstraints.SOUTHWEST, GridBagConstraints.HORIZONTAL, new Insets(8, 10, 8, 10), 0, 0));
ButtonGroup group = new ButtonGroup();
group.add(myRbCreateSource);
group.add(myRbNoSource);
myTfSourceDirectoryName.getDocument().addDocumentListener(new DocumentAdapter() {
public void textChanged(DocumentEvent event) {
updateFullPathField();
}
});
myRbCreateSource.addItemListener(new ItemListener() {
public void itemStateChanged(ItemEvent e) {
final boolean enabled = e.getStateChange() == ItemEvent.SELECTED;
srcPathLabel.setEnabled(enabled);
fieldPanel.setEnabled(enabled);
fullPathLabel.setVisible(enabled);
myTfFullPath.setVisible(enabled);
if (enabled) {
myTfSourceDirectoryName.requestFocus();
}
}
});
return panel;
}
@NonNls protected String suggestSourceDirectoryName() {
return "src";
}
private void updateFullPathField() {
final String sourceDirectoryPath = getSourceDirectoryPath();
if (sourceDirectoryPath != null) {
myTfFullPath.setText(sourceDirectoryPath.replace('/', File.separatorChar));
}
else {
myTfFullPath.setText("");
}
}
private JComponent createComponentForChooseSources() {
final JPanel panel = new JPanel(new GridBagLayout());
mySourcePathsChooser = new ElementsChooser<JavaModuleSourceRoot>(true) {
public String getItemText(@NotNull JavaModuleSourceRoot sourceRoot) {
StringBuilder builder = StringBuilderSpinAllocator.alloc();
try {
builder.append(sourceRoot.getDirectory().getAbsolutePath());
final String packagePrefix = sourceRoot.getPackagePrefix();
if (!packagePrefix.isEmpty()) {
builder.append(" (").append(packagePrefix).append(")");
}
builder.append(" [").append(sourceRoot.getRootTypeName()).append("]");
return builder.toString();
}
finally {
StringBuilderSpinAllocator.dispose(builder);
}
}
};
final String text = IdeBundle.message("label.java.source.files.have.been.found");
final JLabel label = new JLabel(text);
label.setUI(new MultiLineLabelUI());
panel.add(label, new GridBagConstraints(0, GridBagConstraints.RELATIVE, 2, 1, 1.0, 0.0, GridBagConstraints.NORTHWEST, GridBagConstraints.HORIZONTAL, new Insets(8, 10, 0, 10), 0, 0));
panel.add(mySourcePathsChooser, new GridBagConstraints(0, GridBagConstraints.RELATIVE, 2, 1, 1.0, 1.0, GridBagConstraints.NORTHWEST, GridBagConstraints.BOTH, new Insets(8, 10, 8, 10), 0, 0));
final JButton markAllButton = new JButton(IdeBundle.message("button.mark.all"));
panel.add(markAllButton, new GridBagConstraints(0, GridBagConstraints.RELATIVE, 1, 1, 0.0, 0.0, GridBagConstraints.NORTHWEST, GridBagConstraints.NONE, new Insets(0, 10, 8, 2), 0, 0));
final JButton unmarkAllButton = new JButton(IdeBundle.message("button.unmark.all"));
panel.add(unmarkAllButton, new GridBagConstraints(1, GridBagConstraints.RELATIVE, 1, 1, 1.0, 0.0, GridBagConstraints.NORTHWEST, GridBagConstraints.NONE, new Insets(0, 0, 8, 10), 0, 0));
markAllButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
mySourcePathsChooser.setAllElementsMarked(true);
}
});
unmarkAllButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
mySourcePathsChooser.setAllElementsMarked(false);
}
});
return panel;
}
public JComponent getPreferredFocusedComponent() {
return myRbCreateSource.isSelected()? myTfSourceDirectoryName : mySourcePathsChooser.getComponent();
}
public void updateDataModel() {
List<Pair<String,String>> paths = null;
if (CHOOSE_SOURCE_PANEL.equals(myCurrentMode)) {
final List<JavaModuleSourceRoot> selectedElements = mySourcePathsChooser.getMarkedElements();
if (selectedElements.size() > 0) {
paths = new ArrayList<Pair<String, String>>(selectedElements.size());
for (final JavaModuleSourceRoot root : selectedElements) {
paths.add(Pair.create(FileUtil.toSystemIndependentName(root.getDirectory().getAbsolutePath()), root.getPackagePrefix()));
}
}
}
else {
if (myRbCreateSource.isSelected()) {
final String sourceDirectoryPath = getSourceDirectoryPath();
if (sourceDirectoryPath != null) {
paths = Collections.singletonList(Pair.create(sourceDirectoryPath, ""));
}
}
}
myBuilder.setContentEntryPath(getContentRootPath());
if (paths != null) {
myBuilder.setSourcePaths(paths);
}
else {
myBuilder.setSourcePaths(new ArrayList<Pair<String, String>>());
}
}
public boolean validate() throws ConfigurationException {
if (!super.validate()) {
return false;
}
if (CREATE_SOURCE_PANEL.equals(myCurrentMode) && myRbCreateSource.isSelected()) {
final String sourceDirectoryPath = getSourceDirectoryPath();
final String relativePath = myTfSourceDirectoryName.getText().trim();
if (relativePath.length() == 0) {
String text = IdeBundle.message("prompt.relative.path.to.sources.empty", FileUtil.toSystemDependentName(sourceDirectoryPath));
final int answer = Messages.showYesNoCancelDialog(myTfSourceDirectoryName, text, IdeBundle.message("title.mark.source.directory"),
IdeBundle.message("action.mark"), IdeBundle.message("action.do.not.mark"),
CommonBundle.getCancelButtonText(), Messages.getQuestionIcon());
if (answer == Messages.CANCEL) {
return false; // cancel
}
if (answer == Messages.NO) { // don't mark
myRbNoSource.doClick();
}
}
if (sourceDirectoryPath != null) {
final File rootDir = new File(getContentRootPath());
final File srcDir = new File(sourceDirectoryPath);
if (!FileUtil.isAncestor(rootDir, srcDir, false)) {
Messages.showErrorDialog(myTfSourceDirectoryName,
IdeBundle.message("error.source.directory.should.be.under.module.content.root.directory"),
CommonBundle.getErrorTitle());
return false;
}
try {
VfsUtil.createDirectories(srcDir.getPath());
}
catch (IOException e) {
throw new ConfigurationException(e.getMessage());
}
}
}
return true;
}
@Nullable
private String getSourceDirectoryPath() {
final String contentEntryPath = getContentRootPath();
if (contentEntryPath != null) {
final String dirName = myTfSourceDirectoryName.getText().trim().replace(File.separatorChar, '/');
return dirName.length() > 0? contentEntryPath + "/" + dirName : contentEntryPath;
}
return null;
}
protected boolean shouldRunProgress() {
return isContentEntryChanged();
}
protected void onFinished(final List<JavaModuleSourceRoot> foundPaths, final boolean canceled) {
if (foundPaths.size() > 0) {
myCurrentMode = CHOOSE_SOURCE_PANEL;
mySourcePathsChooser.setElements(foundPaths, true);
}
else {
myCurrentMode = CREATE_SOURCE_PANEL;
updateFullPathField();
}
updateStepUI(canceled ? null : getContentRootPath());
if (CHOOSE_SOURCE_PANEL.equals(myCurrentMode)) {
mySourcePathsChooser.selectElements(foundPaths.subList(0, 1));
}
else if (CREATE_SOURCE_PANEL.equals(myCurrentMode)) {
myTfSourceDirectoryName.selectAll();
}
}
private void updateStepUI(final String contentEntryPath) {
myCurrentContentEntryPath = contentEntryPath;
((CardLayout)myResultPanel.getLayout()).show(myResultPanel, myCurrentMode);
myResultPanel.revalidate();
}
protected boolean isContentEntryChanged() {
final String contentEntryPath = getContentRootPath();
return myCurrentContentEntryPath == null? contentEntryPath != null : !myCurrentContentEntryPath.equals(contentEntryPath);
}
protected List<JavaModuleSourceRoot> calculate() {
return new ArrayList<JavaModuleSourceRoot>(calculateSourceRoots(getContentRootPath()));
}
@NotNull
public static Collection<JavaModuleSourceRoot> calculateSourceRoots(final String contentRootPath) {
if (contentRootPath == null) {
return Collections.emptyList();
}
return JavaSourceRootDetectionUtil.suggestRoots(new File(contentRootPath));
}
@Nullable
private String getContentRootPath() {
return myBuilder.getContentEntryPath();
}
protected void setSourceDirectoryName(String name) {
name = name == null? "" : name.trim();
myTfSourceDirectoryName.setText(name);
}
protected String getProgressText() {
final String root = getContentRootPath();
return IdeBundle.message("progress.searching.for.sources", root != null? root.replace('/', File.separatorChar) : "") ;
}
private class BrowsePathListener extends BrowseFilesListener {
private final FileChooserDescriptor myChooserDescriptor;
private final JTextField myField;
public BrowsePathListener(JTextField textField, final FileChooserDescriptor chooserDescriptor) {
super(textField, IdeBundle.message("prompt.select.source.directory"), "", chooserDescriptor);
myChooserDescriptor = chooserDescriptor;
myField = textField;
}
@Nullable
private VirtualFile getContentEntryDir() {
final String contentEntryPath = getContentRootPath();
if (contentEntryPath != null) {
return ApplicationManager.getApplication().runWriteAction(new Computable<VirtualFile>() {
public VirtualFile compute() {
return LocalFileSystem.getInstance().refreshAndFindFileByPath(contentEntryPath);
}
});
}
return null;
}
public void actionPerformed(ActionEvent e) {
final VirtualFile contentEntryDir = getContentEntryDir();
if (contentEntryDir != null) {
myChooserDescriptor.setRoots(contentEntryDir);
final String textBefore = myField.getText().trim();
super.actionPerformed(e);
if (!textBefore.equals(myField.getText().trim())) {
final String fullPath = myField.getText().trim().replace(File.separatorChar, '/');
final VirtualFile fileByPath = LocalFileSystem.getInstance().findFileByPath(fullPath);
LOG.assertTrue(fileByPath != null);
myField.setText(VfsUtilCore.getRelativePath(fileByPath, contentEntryDir, File.separatorChar));
}
}
}
}
public Icon getIcon() {
return myIcon;
}
public String getHelpId() {
return myHelpId;
}
@Override
public String getName() {
return "Path to Sources";
}
}