| /* |
| * 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.projectWizard; |
| |
| import com.intellij.ide.IdeBundle; |
| import com.intellij.ide.highlighter.ProjectFileType; |
| import com.intellij.ide.util.BrowseFilesListener; |
| import com.intellij.openapi.application.ApplicationInfo; |
| import com.intellij.openapi.diagnostic.Logger; |
| import com.intellij.openapi.fileChooser.FileChooserDescriptor; |
| import com.intellij.openapi.options.ConfigurationException; |
| import com.intellij.openapi.project.Project; |
| import com.intellij.openapi.ui.Messages; |
| import com.intellij.openapi.util.io.FileUtil; |
| import com.intellij.openapi.util.text.StringUtil; |
| import com.intellij.ui.DocumentAdapter; |
| import com.intellij.ui.FieldPanel; |
| import com.intellij.util.ui.UIUtil; |
| import org.jetbrains.annotations.NotNull; |
| |
| import javax.swing.*; |
| import javax.swing.event.DocumentEvent; |
| import javax.swing.text.AttributeSet; |
| import javax.swing.text.BadLocationException; |
| import javax.swing.text.PlainDocument; |
| import java.awt.*; |
| import java.awt.event.ActionEvent; |
| import java.io.File; |
| |
| /** |
| * @author Eugene Zhuravlev |
| * Date: Dec 30, 2003 |
| */ |
| public class NamePathComponent extends JPanel{ |
| private static final Logger LOG = Logger.getInstance("#com.intellij.ide.util.projectWizard.NamePathComponent"); |
| |
| private JTextField myTfName; |
| private JTextField myTfPath; |
| private boolean myIsNameChangedByUser = false; |
| private boolean myIsPathChangedByUser = false; |
| private boolean myIsPathNameSyncEnabled = true; |
| private boolean myIsNamePathSyncEnabled = true; |
| private boolean myIsSyncEnabled = true; |
| |
| private FieldPanel myPathPanel; |
| private JLabel myNameLabel; |
| private JLabel myPathLabel; |
| private boolean myForceSync; |
| private boolean myShouldBeAbsolute; |
| |
| public NamePathComponent(String nameLabelText, String pathLabelText, char nameMnemonic, char locationMnemonic, final String pathChooserTitle, final String pathChooserDescription) { |
| this(nameLabelText, pathLabelText, pathChooserTitle, pathChooserDescription, true); |
| } |
| |
| public NamePathComponent(String nameLabelText, |
| String pathLabelText, |
| final String pathChooserTitle, |
| final String pathChooserDescription, |
| boolean hideIgnored) { |
| this(nameLabelText, pathLabelText, pathChooserTitle, pathChooserDescription, hideIgnored, true); |
| } |
| |
| public NamePathComponent(String nameLabelText, |
| String pathLabelText, |
| final String pathChooserTitle, |
| final String pathChooserDescription, |
| boolean hideIgnored, |
| boolean bold) { |
| super(new GridBagLayout()); |
| |
| myTfName = new JTextField(); |
| myTfName.setDocument(new NameFieldDocument()); |
| myTfName.setPreferredSize(new Dimension(200, myTfName.getPreferredSize().height)); |
| |
| myTfPath = new JTextField(); |
| myTfPath.setDocument(new PathFieldDocument()); |
| myTfPath.setPreferredSize(new Dimension(200, myTfPath.getPreferredSize().height)); |
| |
| myNameLabel = new JLabel(nameLabelText); |
| if (bold) myNameLabel.setFont(UIUtil.getLabelFont().deriveFont(Font.BOLD)); |
| myNameLabel.setLabelFor(myTfName); |
| Insets insets = new Insets(0, 0, 5, 4); |
| this.add(myNameLabel, new GridBagConstraints(0, GridBagConstraints.RELATIVE, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.NONE, |
| insets, 0, 0)); |
| |
| insets = new Insets(0, 0, 5, 0); |
| this.add(myTfName, new GridBagConstraints(1, GridBagConstraints.RELATIVE, 1, 1, 1.0, 0.0, GridBagConstraints.NORTHWEST, GridBagConstraints.HORIZONTAL, |
| insets, 0, 0)); |
| // todo: review texts |
| final FileChooserDescriptor chooserDescriptor = (FileChooserDescriptor)BrowseFilesListener.SINGLE_DIRECTORY_DESCRIPTOR.clone(); |
| chooserDescriptor.setHideIgnored(hideIgnored); |
| final BrowseFilesListener browseButtonActionListener = new BrowseFilesListener(myTfPath, pathChooserTitle, pathChooserDescription, chooserDescriptor) { |
| public void actionPerformed(ActionEvent e) { |
| super.actionPerformed(e); |
| myIsPathChangedByUser = true; |
| } |
| }; |
| myPathPanel = new FieldPanel(myTfPath, null, null, browseButtonActionListener, null); |
| myPathLabel = new JLabel(pathLabelText); |
| myPathLabel.setLabelFor(myTfPath); |
| if (bold) myPathLabel.setFont(UIUtil.getLabelFont().deriveFont(Font.BOLD)); |
| insets = new Insets(0, 0, 5, 4); |
| this.add(myPathLabel, new GridBagConstraints(0, GridBagConstraints.RELATIVE, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.NONE, |
| insets, 0, 0)); |
| insets = new Insets(0, 0, 5, 0); |
| this.add(myPathPanel, new GridBagConstraints(1, GridBagConstraints.RELATIVE, 1, 1, 1.0, 0.0, GridBagConstraints.NORTHWEST, GridBagConstraints.HORIZONTAL, |
| insets, 0, 0)); |
| } |
| |
| public static NamePathComponent initNamePathComponent(WizardContext context) { |
| NamePathComponent component = new NamePathComponent( |
| IdeBundle.message("label.project.name"), |
| IdeBundle.message("label.project.files.location"), |
| IdeBundle.message("title.select.project.file.directory", IdeBundle.message("project.new.wizard.project.identification")), |
| IdeBundle.message("description.select.project.file.directory", StringUtil |
| .capitalize(IdeBundle.message("project.new.wizard.project.identification"))), |
| true, false |
| ); |
| final String baseDir = context.getProjectFileDirectory(); |
| final String projectName = context.getProjectName(); |
| final String initialProjectName = projectName != null ? projectName : ProjectWizardUtil.findNonExistingFileName(baseDir, "untitled", ""); |
| component.setPath(projectName == null ? (baseDir + File.separator + initialProjectName) : baseDir); |
| component.setNameValue(initialProjectName); |
| component.getNameComponent().select(0, initialProjectName.length()); |
| return component; |
| } |
| |
| public boolean validateNameAndPath(WizardContext context, boolean defaultFormat) throws ConfigurationException { |
| String name = getNameValue(); |
| if (StringUtil.isEmptyOrSpaces(name)) { |
| ApplicationInfo info = ApplicationInfo.getInstance(); |
| throw new ConfigurationException(IdeBundle.message("prompt.new.project.file.name", info.getVersionName(), context.getPresentationName())); |
| } |
| |
| String projectDirectory = getPath(); |
| if (StringUtil.isEmptyOrSpaces(projectDirectory)) { |
| throw new ConfigurationException(IdeBundle.message("prompt.enter.project.file.location", context.getPresentationName())); |
| } |
| if (myShouldBeAbsolute && !new File(projectDirectory).isAbsolute()) { |
| throw new ConfigurationException(StringUtil.capitalize(IdeBundle.message("file.location.should.be.absolute", context.getPresentationName()))); |
| } |
| |
| boolean shouldPromptCreation = isPathChangedByUser(); |
| String message = IdeBundle.message("directory.project.file.directory", context.getPresentationName()); |
| if (!ProjectWizardUtil.createDirectoryIfNotExists(message, projectDirectory, shouldPromptCreation)) { |
| return false; |
| } |
| |
| File file = new File(projectDirectory); |
| if (file.exists() && !file.canWrite()) { |
| throw new ConfigurationException(String.format("Directory '%s' is not writable!\nPlease choose another project location.", projectDirectory)); |
| } |
| |
| boolean shouldContinue = true; |
| String fileName = defaultFormat ? name + ProjectFileType.DOT_DEFAULT_EXTENSION : Project.DIRECTORY_STORE_FOLDER; |
| File projectFile = new File(file, fileName); |
| if (projectFile.exists()) { |
| message = IdeBundle.message("prompt.overwrite.project.file", projectFile.getAbsolutePath(), context.getPresentationName()); |
| int answer = Messages.showYesNoDialog(message, IdeBundle.message("title.file.already.exists"), Messages.getQuestionIcon()); |
| shouldContinue = (answer == Messages.YES); |
| } |
| return shouldContinue; |
| } |
| |
| public String getNameValue() { |
| return myTfName.getText().trim(); |
| } |
| |
| public void setNameValue(String name) { |
| final boolean isNameChangedByUser = myIsNameChangedByUser; |
| setNamePathSyncEnabled(false); |
| try { |
| myTfName.setText(name); |
| } |
| finally { |
| myIsNameChangedByUser = isNameChangedByUser; |
| setNamePathSyncEnabled(true); |
| } |
| } |
| |
| public String getPath() { |
| String text = myTfPath.getText().trim(); |
| return FileUtil.expandUserHome(FileUtil.toSystemIndependentName(text)); |
| } |
| |
| public void setPath(String path) { |
| final boolean isPathChangedByUser = myIsPathChangedByUser; |
| setPathNameSyncEnabled(false); |
| try { |
| myTfPath.setText(FileUtil.getLocationRelativeToUserHome(FileUtil.toSystemDependentName(path))); |
| } |
| finally { |
| myIsPathChangedByUser = isPathChangedByUser; |
| setPathNameSyncEnabled(true); |
| } |
| } |
| |
| public JTextField getNameComponent() { |
| return myTfName; |
| } |
| |
| @NotNull |
| public JLabel getPathLabel() { |
| return myPathLabel; |
| } |
| |
| public JTextField getPathComponent() { |
| return myTfPath; |
| } |
| |
| @NotNull |
| public FieldPanel getPathPanel() { |
| return myPathPanel; |
| } |
| |
| public void setPathComponentVisible(boolean visible) { |
| myPathPanel.setVisible(visible); |
| } |
| |
| public void setNameComponentVisible(boolean visible) { |
| myTfName.setVisible(visible); |
| myNameLabel.setVisible(visible); |
| } |
| |
| public boolean isNameChangedByUser() { |
| return myIsNameChangedByUser; |
| } |
| |
| public boolean isPathChangedByUser() { |
| return myIsPathChangedByUser; |
| } |
| |
| public boolean isSyncEnabled() { |
| return myIsSyncEnabled; |
| } |
| |
| public void setSyncEnabled(boolean isSyncEnabled) { |
| myIsSyncEnabled = isSyncEnabled; |
| } |
| |
| private boolean isPathNameSyncEnabled() { |
| if (!isSyncEnabled()) { |
| return false; |
| } |
| return myIsPathNameSyncEnabled; |
| } |
| |
| private void setPathNameSyncEnabled(boolean isPathNameSyncEnabled) { |
| myIsPathNameSyncEnabled = isPathNameSyncEnabled; |
| } |
| |
| private boolean isNamePathSyncEnabled() { |
| if (!isSyncEnabled()) { |
| return false; |
| } |
| return myIsNamePathSyncEnabled; |
| } |
| |
| private void setNamePathSyncEnabled(boolean isNamePathSyncEnabled) { |
| myIsNamePathSyncEnabled = isNamePathSyncEnabled; |
| } |
| |
| public void syncNameToPath(boolean b) { |
| myForceSync = b; |
| if (b) ((PathFieldDocument)myTfPath.getDocument()).syncPathAndName(); |
| } |
| |
| public void addChangeListener(final Runnable callback) { |
| DocumentAdapter adapter = new DocumentAdapter() { |
| @Override |
| protected void textChanged(DocumentEvent e) { |
| callback.run(); |
| } |
| }; |
| myTfName.getDocument().addDocumentListener(adapter); |
| myTfPath.getDocument().addDocumentListener(adapter); |
| } |
| |
| public void setShouldBeAbsolute(boolean shouldBeAbsolute) { |
| myShouldBeAbsolute = shouldBeAbsolute; |
| } |
| |
| private class NameFieldDocument extends PlainDocument { |
| public NameFieldDocument() { |
| addDocumentListener(new DocumentAdapter() { |
| public void textChanged(DocumentEvent event) { |
| myIsNameChangedByUser = true; |
| syncNameAndPath(); |
| } |
| }); |
| } |
| |
| public void insertString(int offs, String str, AttributeSet a) throws BadLocationException { |
| boolean ok = true; |
| for (int idx = 0; idx < str.length() && ok; idx++) { |
| char ch = str.charAt(idx); |
| ok = ch != File.separatorChar && ch != '\\' && ch != '/' && ch != '|' && ch != ':'; |
| } |
| if (ok) { |
| super.insertString(offs, str, a); |
| } |
| } |
| |
| private void syncNameAndPath() { |
| if (isNamePathSyncEnabled() && (myForceSync || !myIsPathChangedByUser)) { |
| try { |
| setPathNameSyncEnabled(false); |
| final String name = getText(0, getLength()); |
| final String path = myTfPath.getText().trim(); |
| final int lastSeparatorIndex = path.lastIndexOf(File.separator); |
| if (lastSeparatorIndex >= 0) { |
| setPath(path.substring(0, lastSeparatorIndex + 1) + name); |
| } |
| } |
| catch (BadLocationException e) { |
| LOG.error(e); |
| } |
| finally { |
| setPathNameSyncEnabled(true); |
| } |
| } |
| } |
| } |
| |
| private class PathFieldDocument extends PlainDocument { |
| public PathFieldDocument() { |
| addDocumentListener(new DocumentAdapter() { |
| public void textChanged(DocumentEvent event) { |
| myIsPathChangedByUser = true; |
| syncPathAndName(); |
| } |
| }); |
| } |
| |
| private void syncPathAndName() { |
| if (isPathNameSyncEnabled() && (myForceSync || !myIsNameChangedByUser)) { |
| try { |
| setNamePathSyncEnabled(false); |
| final String path = getText(0, getLength()); |
| final int lastSeparatorIndex = path.lastIndexOf(File.separator); |
| if (lastSeparatorIndex >= 0 && (lastSeparatorIndex + 1) < path.length()) { |
| setNameValue(path.substring(lastSeparatorIndex + 1)); |
| } |
| } |
| catch (BadLocationException e) { |
| LOG.error(e); |
| } |
| finally { |
| setNamePathSyncEnabled(true); |
| } |
| } |
| } |
| } |
| |
| } |