update copyright
[fedora-idea.git] / java / idea-ui / src / com / intellij / ide / util / newProjectWizard / SourcePathsStep.java
blobef53ee930f843f4b4566c377c88972a57d21e621
1 /*
2 * Copyright 2000-2009 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 package com.intellij.ide.util.newProjectWizard;
18 import com.intellij.CommonBundle;
19 import com.intellij.ide.IdeBundle;
20 import com.intellij.ide.util.BrowseFilesListener;
21 import com.intellij.ide.util.ElementsChooser;
22 import com.intellij.ide.util.JavaUtil;
23 import com.intellij.ide.util.projectWizard.AbstractStepWithProgress;
24 import com.intellij.ide.util.projectWizard.SourcePathsBuilder;
25 import com.intellij.openapi.application.ApplicationManager;
26 import com.intellij.openapi.application.ApplicationNamesInfo;
27 import com.intellij.openapi.diagnostic.Logger;
28 import com.intellij.openapi.fileChooser.FileChooserDescriptor;
29 import com.intellij.openapi.ui.Messages;
30 import com.intellij.openapi.ui.MultiLineLabelUI;
31 import com.intellij.openapi.util.Computable;
32 import com.intellij.openapi.util.Pair;
33 import com.intellij.openapi.util.io.FileUtil;
34 import com.intellij.openapi.vfs.LocalFileSystem;
35 import com.intellij.openapi.vfs.VfsUtil;
36 import com.intellij.openapi.vfs.VirtualFile;
37 import com.intellij.ui.DocumentAdapter;
38 import com.intellij.ui.FieldPanel;
39 import org.jetbrains.annotations.NonNls;
40 import org.jetbrains.annotations.Nullable;
41 import org.jetbrains.annotations.NotNull;
43 import javax.swing.*;
44 import javax.swing.event.DocumentEvent;
45 import java.awt.*;
46 import java.awt.event.ActionEvent;
47 import java.awt.event.ActionListener;
48 import java.awt.event.ItemEvent;
49 import java.awt.event.ItemListener;
50 import java.io.File;
51 import java.io.IOException;
52 import java.util.ArrayList;
53 import java.util.Collections;
54 import java.util.List;
56 /**
57 * @author Eugene Zhuravlev
58 * Date: Jan 6, 2004
60 public class SourcePathsStep extends AbstractStepWithProgress<List<Pair<String, String>>> {
61 private static final Logger LOG = Logger.getInstance("#com.intellij.ide.util.newProjectWizard.SourcePathsStep");
63 private String myCurrentMode;
64 @NonNls private static final String CREATE_SOURCE_PANEL = "create_source";
65 @NonNls private static final String CHOOSE_SOURCE_PANEL = "choose_source";
67 private static final List<Pair<String,String>> EMPTY_STRING_STRING_ARRAY = Collections.emptyList();
68 private final SourcePathsBuilder myBuilder;
69 private final Icon myIcon;
70 private final String myHelpId;
71 private ElementsChooser<Pair<String,String>> mySourcePathsChooser;
72 private String myCurrentContentEntryPath = null;
73 private JRadioButton myRbCreateSource;
74 private JRadioButton myRbNoSource;
75 private JTextField myTfSourceDirectoryName;
76 private JTextField myTfFullPath;
77 private JPanel myResultPanel;
79 public SourcePathsStep(SourcePathsBuilder builder, Icon icon, @NonNls String helpId) {
80 super(IdeBundle.message("prompt.stop.searching.for.sources", ApplicationNamesInfo.getInstance().getProductName()));
81 myBuilder = builder;
82 myIcon = icon;
83 myHelpId = helpId;
86 protected JComponent createResultsPanel() {
87 myResultPanel = new JPanel(new CardLayout());
88 myResultPanel.add(createComponentForEmptyRootCase(), CREATE_SOURCE_PANEL);
89 myResultPanel.add(createComponentForChooseSources(), CHOOSE_SOURCE_PANEL);
90 return myResultPanel;
93 private JComponent createComponentForEmptyRootCase() {
94 final JPanel panel = new JPanel(new GridBagLayout());
95 final String text = IdeBundle.message("prompt.please.specify.java.sources.directory");
97 final JLabel label = new JLabel(text);
98 label.setUI(new MultiLineLabelUI());
99 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));
101 myRbCreateSource = new JRadioButton(IdeBundle.message("radio.create.source.directory"), true);
102 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));
104 myTfSourceDirectoryName = new JTextField(suggestSourceDirectoryName());
105 final JLabel srcPathLabel = new JLabel(IdeBundle.message("prompt.enter.relative.path.to.module.content.root", File.separator));
106 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));
107 final FileChooserDescriptor chooserDescriptor = new FileChooserDescriptor(false, true, false, false, false, false);
108 chooserDescriptor.setIsTreeRootVisible(true);
109 final FieldPanel fieldPanel = createFieldPanel(myTfSourceDirectoryName, null, new BrowsePathListener(myTfSourceDirectoryName, chooserDescriptor));
110 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));
112 myRbNoSource = new JRadioButton(IdeBundle.message("radio.do.not.create.source.directory"), true);
113 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));
115 final JLabel fullPathLabel = new JLabel(IdeBundle.message("label.source.directory"));
116 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));
118 myTfFullPath = new JTextField();
119 myTfFullPath.setEditable(false);
120 final Insets borderInsets = myTfFullPath.getBorder().getBorderInsets(myTfFullPath);
121 myTfFullPath.setBorder(BorderFactory.createEmptyBorder(borderInsets.top, borderInsets.left, borderInsets.bottom, borderInsets.right));
122 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));
124 ButtonGroup group = new ButtonGroup();
125 group.add(myRbCreateSource);
126 group.add(myRbNoSource);
127 myTfSourceDirectoryName.getDocument().addDocumentListener(new DocumentAdapter() {
128 public void textChanged(DocumentEvent event) {
129 updateFullPathField();
133 myRbCreateSource.addItemListener(new ItemListener() {
134 public void itemStateChanged(ItemEvent e) {
135 final boolean enabled = e.getStateChange() == ItemEvent.SELECTED;
136 srcPathLabel.setEnabled(enabled);
137 fieldPanel.setEnabled(enabled);
138 fullPathLabel.setVisible(enabled);
139 myTfFullPath.setVisible(enabled);
140 if (enabled) {
141 myTfSourceDirectoryName.requestFocus();
145 return panel;
148 @NonNls protected String suggestSourceDirectoryName() {
149 return "src";
152 private void updateFullPathField() {
153 final String sourceDirectoryPath = getSourceDirectoryPath();
154 if (sourceDirectoryPath != null) {
155 myTfFullPath.setText(sourceDirectoryPath.replace('/', File.separatorChar));
157 else {
158 myTfFullPath.setText("");
162 private JComponent createComponentForChooseSources() {
163 final JPanel panel = new JPanel(new GridBagLayout());
164 mySourcePathsChooser = new ElementsChooser<Pair<String, String>>(true) {
165 public String getItemText(@NotNull Pair<String, String> pair) {
166 if ("".equals(pair.second)) return pair.first;
167 return pair.first + " (" + pair.second + ")";
170 final String text = IdeBundle.message("label.java.source.files.have.been.found");
171 final JLabel label = new JLabel(text);
172 label.setUI(new MultiLineLabelUI());
173 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));
174 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));
176 final JButton markAllButton = new JButton(IdeBundle.message("button.mark.all"));
177 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));
179 final JButton unmarkAllButton = new JButton(IdeBundle.message("button.unmark.all"));
180 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));
182 markAllButton.addActionListener(new ActionListener() {
183 public void actionPerformed(ActionEvent e) {
184 mySourcePathsChooser.setAllElementsMarked(true);
187 unmarkAllButton.addActionListener(new ActionListener() {
188 public void actionPerformed(ActionEvent e) {
189 mySourcePathsChooser.setAllElementsMarked(false);
193 return panel;
196 public JComponent getPreferredFocusedComponent() {
197 return myRbCreateSource.isSelected()? myTfSourceDirectoryName : mySourcePathsChooser.getComponent();
200 public void updateDataModel() {
201 List<Pair<String,String>> paths = null;
202 if (CHOOSE_SOURCE_PANEL.equals(myCurrentMode)) {
203 final List<Pair<String, String>> selectedElements = mySourcePathsChooser.getMarkedElements();
204 if (selectedElements.size() > 0) {
205 paths = new ArrayList<Pair<String, String>>(selectedElements.size());
207 for (final Pair<String, String> path : selectedElements) {
208 paths.add(Pair.create(path.first.replace(File.separatorChar, '/'), path.second));
212 else {
213 if (myRbCreateSource.isSelected()) {
214 final String sourceDirectoryPath = getSourceDirectoryPath();
215 if (sourceDirectoryPath != null) {
216 paths = Collections.singletonList(Pair.create(sourceDirectoryPath, ""));
220 myBuilder.setContentEntryPath(getContentRootPath());
221 if (paths != null) {
222 myBuilder.setSourcePaths(paths);
224 else {
225 myBuilder.setSourcePaths(new ArrayList<Pair<String, String>>());
229 public boolean validate() {
230 if (!super.validate()) {
231 return false;
234 if (CREATE_SOURCE_PANEL.equals(myCurrentMode) && myRbCreateSource.isSelected()) {
235 final String sourceDirectoryPath = getSourceDirectoryPath();
236 final String relativePath = myTfSourceDirectoryName.getText().trim();
237 if (relativePath.length() == 0) {
238 String text = IdeBundle.message("prompt.relative.path.to.sources.empty", sourceDirectoryPath);
239 final int answer = Messages.showDialog(myTfSourceDirectoryName, text, IdeBundle.message("title.mark.source.directory"),
240 new String[]{IdeBundle.message("action.mark"), IdeBundle.message("action.do.not.mark"),
241 CommonBundle.getCancelButtonText()}, 0, Messages.getQuestionIcon());
242 if (answer == 2) {
243 return false; // cancel
245 if (answer == 1) { // don't mark
246 myRbNoSource.doClick();
249 if (sourceDirectoryPath != null) {
250 final File rootDir = new File(getContentRootPath());
251 final File srcDir = new File(sourceDirectoryPath);
252 try {
253 if (!FileUtil.isAncestor(rootDir, srcDir, false)) {
254 Messages.showErrorDialog(myTfSourceDirectoryName,
255 IdeBundle.message("error.source.directory.should.be.under.module.content.root.directory"),
256 CommonBundle.getErrorTitle());
257 return false;
260 catch (IOException e) {
261 Messages.showErrorDialog(myTfSourceDirectoryName, e.getMessage(), CommonBundle.getErrorTitle());
262 return false;
264 srcDir.mkdirs();
267 return true;
270 @Nullable
271 private String getSourceDirectoryPath() {
272 final String contentEntryPath = getContentRootPath();
273 final String dirName = myTfSourceDirectoryName.getText().trim().replace(File.separatorChar, '/');
274 if (contentEntryPath != null) {
275 return dirName.length() > 0? contentEntryPath + "/" + dirName : contentEntryPath;
277 return null;
280 protected boolean shouldRunProgress() {
281 return isContentEntryChanged();
284 protected void onFinished(final List<Pair<String, String>> foundPaths, final boolean canceled) {
285 if (foundPaths.size() > 0) {
286 myCurrentMode = CHOOSE_SOURCE_PANEL;
287 mySourcePathsChooser.setElements(foundPaths, true);
289 else {
290 myCurrentMode = CREATE_SOURCE_PANEL;
291 updateFullPathField();
293 updateStepUI(canceled ? null : getContentRootPath());
294 if (CHOOSE_SOURCE_PANEL.equals(myCurrentMode)) {
295 mySourcePathsChooser.selectElements(foundPaths.subList(0, 1));
297 else if (CREATE_SOURCE_PANEL.equals(myCurrentMode)) {
298 myTfSourceDirectoryName.selectAll();
302 private void updateStepUI(final String contentEntryPath) {
303 myCurrentContentEntryPath = contentEntryPath;
304 ((CardLayout)myResultPanel.getLayout()).show(myResultPanel, myCurrentMode);
305 myResultPanel.revalidate();
308 protected boolean isContentEntryChanged() {
309 final String contentEntryPath = getContentRootPath();
310 return myCurrentContentEntryPath == null? contentEntryPath != null : !myCurrentContentEntryPath.equals(contentEntryPath);
313 protected List<Pair<String,String>> calculate() {
314 String contentEntryPath = getContentRootPath();
315 if (contentEntryPath == null) {
316 return EMPTY_STRING_STRING_ARRAY;
318 final File entryFile = new File(contentEntryPath);
319 if (!entryFile.exists()) {
320 return EMPTY_STRING_STRING_ARRAY;
322 final File[] children = entryFile.listFiles();
323 if (children == null || children.length == 0) {
324 return EMPTY_STRING_STRING_ARRAY;
326 final List<Pair<File,String>> suggestedRoots = JavaUtil.suggestRoots(entryFile);
327 final List<Pair<String,String>> paths = new ArrayList<Pair<String, String>>();
328 for (final Pair<File, String> suggestedRoot : suggestedRoots) {
329 try {
330 if (FileUtil.isAncestor(entryFile, suggestedRoot.first, false)) {
331 final String path = FileUtil.resolveShortWindowsName(suggestedRoot.first.getPath());
332 paths.add(Pair.create(path, suggestedRoot.second));
335 catch (IOException e) {
336 LOG.info(e);
339 return paths;
342 @Nullable
343 private String getContentRootPath() {
344 return myBuilder.getContentEntryPath();
347 protected void setSourceDirectoryName(String name) {
348 name = name == null? "" : name.trim();
349 myTfSourceDirectoryName.setText(name);
352 protected String getProgressText() {
353 final String root = getContentRootPath();
354 return IdeBundle.message("progress.searching.for.sources", root != null? root.replace('/', File.separatorChar) : "") ;
357 private class BrowsePathListener extends BrowseFilesListener {
358 private final FileChooserDescriptor myChooserDescriptor;
359 private final JTextField myField;
361 public BrowsePathListener(JTextField textField, final FileChooserDescriptor chooserDescriptor) {
362 super(textField, IdeBundle.message("prompt.select.source.directory"), "", chooserDescriptor);
363 myChooserDescriptor = chooserDescriptor;
364 myField = textField;
367 @Nullable
368 private VirtualFile getContentEntryDir() {
369 final String contentEntryPath = getContentRootPath();
370 if (contentEntryPath != null) {
371 return ApplicationManager.getApplication().runWriteAction(new Computable<VirtualFile>() {
372 public VirtualFile compute() {
373 return LocalFileSystem.getInstance().refreshAndFindFileByPath(contentEntryPath);
377 return null;
380 public void actionPerformed(ActionEvent e) {
381 final VirtualFile contentEntryDir = getContentEntryDir();
382 if (contentEntryDir != null) {
383 myChooserDescriptor.setRoot(contentEntryDir);
384 final String textBefore = myField.getText().trim();
385 super.actionPerformed(e);
386 if (!textBefore.equals(myField.getText().trim())) {
387 final String fullPath = myField.getText().trim().replace(File.separatorChar, '/');
388 final VirtualFile fileByPath = LocalFileSystem.getInstance().findFileByPath(fullPath);
389 LOG.assertTrue(fileByPath != null);
390 myField.setText(VfsUtil.getRelativePath(fileByPath, contentEntryDir, File.separatorChar));
396 public Icon getIcon() {
397 return myIcon;
400 public String getHelpId() {
401 return myHelpId;