| /* |
| * 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"; |
| } |
| } |