| /* |
| * Copyright 2000-2013 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.refactoring.copy; |
| |
| import com.intellij.ide.util.DirectoryUtil; |
| import com.intellij.ide.util.PropertiesComponent; |
| import com.intellij.openapi.actionSystem.ActionManager; |
| import com.intellij.openapi.actionSystem.IdeActions; |
| import com.intellij.openapi.application.ApplicationManager; |
| import com.intellij.openapi.command.CommandProcessor; |
| import com.intellij.openapi.fileChooser.FileChooserDescriptor; |
| import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory; |
| import com.intellij.openapi.help.HelpManager; |
| import com.intellij.openapi.keymap.KeymapUtil; |
| import com.intellij.openapi.project.Project; |
| import com.intellij.openapi.ui.DialogWrapper; |
| import com.intellij.openapi.ui.DialogWrapperPeer; |
| import com.intellij.openapi.ui.Messages; |
| import com.intellij.openapi.ui.TextComponentAccessor; |
| import com.intellij.openapi.util.text.StringUtil; |
| import com.intellij.openapi.vfs.VirtualFile; |
| import com.intellij.psi.*; |
| import com.intellij.refactoring.RefactoringBundle; |
| import com.intellij.ui.DocumentAdapter; |
| import com.intellij.ui.RecentsManager; |
| import com.intellij.ui.TextFieldWithHistoryWithBrowseButton; |
| import com.intellij.ui.components.JBLabelDecorator; |
| import com.intellij.util.IncorrectOperationException; |
| import com.intellij.util.PathUtil; |
| import com.intellij.util.ui.FormBuilder; |
| import com.intellij.util.ui.UIUtil; |
| import org.jetbrains.annotations.NotNull; |
| |
| import javax.swing.*; |
| import javax.swing.event.DocumentEvent; |
| import javax.swing.event.DocumentListener; |
| import java.awt.*; |
| import java.io.File; |
| import java.util.List; |
| |
| public class CopyFilesOrDirectoriesDialog extends DialogWrapper { |
| public static final int MAX_PATH_LENGTH = 70; |
| |
| private static final String COPY_OPEN_IN_EDITOR = "Copy.OpenInEditor"; |
| private static final String RECENT_KEYS = "CopyFile.RECENT_KEYS"; |
| |
| public static String shortenPath(VirtualFile file) { |
| return StringUtil.shortenPathWithEllipsis(file.getPresentableUrl(), MAX_PATH_LENGTH); |
| } |
| |
| public static JCheckBox createOpenInEditorCB() { |
| JCheckBox checkBox = new JCheckBox("Open copy in editor", PropertiesComponent.getInstance().getBoolean(COPY_OPEN_IN_EDITOR, true)); |
| checkBox.setMnemonic('o'); |
| return checkBox; |
| } |
| |
| public static void saveOpenInEditorState(boolean selected) { |
| PropertiesComponent.getInstance().setValue(COPY_OPEN_IN_EDITOR, String.valueOf(selected)); |
| } |
| |
| private JLabel myInformationLabel; |
| private TextFieldWithHistoryWithBrowseButton myTargetDirectoryField; |
| private JCheckBox myOpenFilesInEditor = createOpenInEditorCB(); |
| private JTextField myNewNameField; |
| private final Project myProject; |
| private final boolean myShowDirectoryField; |
| private final boolean myShowNewNameField; |
| |
| private PsiDirectory myTargetDirectory; |
| private boolean myFileCopy = false; |
| |
| public CopyFilesOrDirectoriesDialog(PsiElement[] elements, PsiDirectory defaultTargetDirectory, Project project, boolean doClone) { |
| super(project, true); |
| myProject = project; |
| myShowDirectoryField = !doClone; |
| myShowNewNameField = elements.length == 1; |
| |
| if (doClone && elements.length != 1) { |
| throw new IllegalArgumentException("wrong number of elements to clone: " + elements.length); |
| } |
| |
| setTitle(RefactoringBundle.message(doClone ? "copy.files.clone.title" : "copy.files.copy.title")); |
| init(); |
| |
| if (elements.length == 1) { |
| String text; |
| if (elements[0] instanceof PsiFile) { |
| PsiFile file = (PsiFile)elements[0]; |
| String url = shortenPath(file.getVirtualFile()); |
| text = RefactoringBundle.message(doClone ? "copy.files.clone.file.0" : "copy.files.copy.file.0", url); |
| // keep extensions (dots) and spaces, e.g. fragment file name will be "HTML Fragment (my.sql_61).html" |
| // and leave ordinary file name AS IS |
| String fileName = PathUtil.suggestFileName(file.getName(), true, true); |
| myNewNameField.setText(fileName); |
| final int dotIdx = fileName.lastIndexOf("."); |
| if (dotIdx > -1) { |
| myNewNameField.select(0, dotIdx); |
| myNewNameField.putClientProperty(DialogWrapperPeer.HAVE_INITIAL_SELECTION, true); |
| } |
| myFileCopy = true; |
| } |
| else { |
| PsiDirectory directory = (PsiDirectory)elements[0]; |
| String url = shortenPath(directory.getVirtualFile()); |
| text = RefactoringBundle.message(doClone ? "copy.files.clone.directory.0" : "copy.files.copy.directory.0", url); |
| myNewNameField.setText(directory.getName()); |
| } |
| myInformationLabel.setText(text); |
| } |
| else { |
| setMultipleElementCopyLabel(elements); |
| } |
| |
| boolean allBinary = true; |
| for (PsiElement element : elements) { |
| if (!(element.getContainingFile() instanceof PsiBinaryFile)) { |
| allBinary = false; |
| break; |
| } |
| } |
| if (allBinary) { |
| myOpenFilesInEditor.setVisible(false); |
| } |
| if (myShowDirectoryField) { |
| String targetPath = defaultTargetDirectory == null ? "" : defaultTargetDirectory.getVirtualFile().getPresentableUrl(); |
| myTargetDirectoryField.getChildComponent().setText(targetPath); |
| } |
| validateOKButton(); |
| } |
| |
| private void setMultipleElementCopyLabel(PsiElement[] elements) { |
| boolean allFiles = true; |
| boolean allDirectories = true; |
| for (PsiElement element : elements) { |
| if (element instanceof PsiDirectory) { |
| allFiles = false; |
| } |
| else { |
| allDirectories = false; |
| } |
| } |
| if (allFiles) { |
| myInformationLabel.setText(RefactoringBundle.message("copy.files.copy.specified.files.label")); |
| } |
| else if (allDirectories) { |
| myInformationLabel.setText(RefactoringBundle.message("copy.files.copy.specified.directories.label")); |
| } |
| else { |
| myInformationLabel.setText(RefactoringBundle.message("copy.files.copy.specified.mixed.label")); |
| } |
| } |
| |
| @Override |
| @NotNull |
| protected Action[] createActions() { |
| return new Action[]{getOKAction(), getCancelAction(), getHelpAction()}; |
| } |
| |
| @Override |
| public JComponent getPreferredFocusedComponent() { |
| return myShowNewNameField ? myNewNameField : myTargetDirectoryField.getChildComponent(); |
| } |
| |
| @Override |
| protected JComponent createCenterPanel() { |
| return new JPanel(new BorderLayout()); |
| } |
| |
| @Override |
| protected JComponent createNorthPanel() { |
| myInformationLabel = JBLabelDecorator.createJBLabelDecorator().setBold(true); |
| final FormBuilder formBuilder = FormBuilder.createFormBuilder().addComponent(myInformationLabel).addVerticalGap( |
| UIUtil.LARGE_VGAP - UIUtil.DEFAULT_VGAP); |
| DocumentListener documentListener = new DocumentAdapter() { |
| @Override |
| public void textChanged(DocumentEvent event) { |
| validateOKButton(); |
| } |
| }; |
| |
| if (myShowNewNameField) { |
| myNewNameField = new JTextField(); |
| myNewNameField.getDocument().addDocumentListener(documentListener); |
| formBuilder.addLabeledComponent(RefactoringBundle.message("copy.files.new.name.label"), myNewNameField); |
| } |
| |
| if (myShowDirectoryField) { |
| myTargetDirectoryField = new TextFieldWithHistoryWithBrowseButton(); |
| myTargetDirectoryField.setTextFieldPreferredWidth(MAX_PATH_LENGTH); |
| final List<String> recentEntries = RecentsManager.getInstance(myProject).getRecentEntries(RECENT_KEYS); |
| if (recentEntries != null) { |
| myTargetDirectoryField.getChildComponent().setHistory(recentEntries); |
| } |
| final FileChooserDescriptor descriptor = FileChooserDescriptorFactory.createSingleFolderDescriptor(); |
| myTargetDirectoryField.addBrowseFolderListener(RefactoringBundle.message("select.target.directory"), |
| RefactoringBundle.message("the.file.will.be.copied.to.this.directory"), |
| myProject, descriptor, |
| TextComponentAccessor.TEXT_FIELD_WITH_HISTORY_WHOLE_TEXT); |
| myTargetDirectoryField.getChildComponent().addDocumentListener(new DocumentAdapter() { |
| @Override |
| protected void textChanged(DocumentEvent e) { |
| validateOKButton(); |
| } |
| }); |
| formBuilder.addLabeledComponent(RefactoringBundle.message("copy.files.to.directory.label"), myTargetDirectoryField); |
| |
| String shortcutText = |
| KeymapUtil.getFirstKeyboardShortcutText(ActionManager.getInstance().getAction(IdeActions.ACTION_CODE_COMPLETION)); |
| formBuilder.addTooltip(RefactoringBundle.message("path.completion.shortcut", shortcutText)); |
| } |
| |
| final JPanel wrapper = new JPanel(new BorderLayout()); |
| wrapper.add(myOpenFilesInEditor, BorderLayout.EAST); |
| formBuilder.addComponent(wrapper); |
| return formBuilder.getPanel(); |
| } |
| |
| public PsiDirectory getTargetDirectory() { |
| return myTargetDirectory; |
| } |
| |
| public String getNewName() { |
| return myNewNameField != null ? myNewNameField.getText().trim() : null; |
| } |
| |
| public boolean openInEditor() { |
| return myOpenFilesInEditor.isSelected(); |
| } |
| |
| @Override |
| protected void doOKAction() { |
| if (myShowNewNameField) { |
| String newName = getNewName(); |
| |
| if (newName.length() == 0) { |
| Messages.showErrorDialog(myProject, RefactoringBundle.message("no.new.name.specified"), RefactoringBundle.message("error.title")); |
| return; |
| } |
| |
| if (myFileCopy && !PathUtil.isValidFileName(newName)) { |
| Messages.showErrorDialog(myNewNameField, "Name is not a valid file name"); |
| return; |
| } |
| } |
| |
| saveOpenInEditorState(myOpenFilesInEditor.isSelected()); |
| if (myShowDirectoryField) { |
| final String targetDirectoryName = myTargetDirectoryField.getChildComponent().getText(); |
| |
| if (targetDirectoryName.length() == 0) { |
| Messages.showErrorDialog(myProject, RefactoringBundle.message("no.target.directory.specified"), |
| RefactoringBundle.message("error.title")); |
| return; |
| } |
| |
| RecentsManager.getInstance(myProject).registerRecentEntry(RECENT_KEYS, targetDirectoryName); |
| |
| CommandProcessor.getInstance().executeCommand(myProject, new Runnable() { |
| @Override |
| public void run() { |
| ApplicationManager.getApplication().runWriteAction(new Runnable() { |
| @Override |
| public void run() { |
| try { |
| myTargetDirectory = |
| DirectoryUtil.mkdirs(PsiManager.getInstance(myProject), targetDirectoryName.replace(File.separatorChar, '/')); |
| } |
| catch (IncorrectOperationException ignored) { } |
| } |
| }); |
| } |
| }, RefactoringBundle.message("create.directory"), null); |
| |
| if (myTargetDirectory == null) { |
| Messages.showErrorDialog(myProject, RefactoringBundle.message("cannot.create.directory"), RefactoringBundle.message("error.title")); |
| return; |
| } |
| } |
| |
| super.doOKAction(); |
| } |
| |
| private void validateOKButton() { |
| if (myShowDirectoryField) { |
| if (myTargetDirectoryField.getChildComponent().getText().length() == 0) { |
| setOKActionEnabled(false); |
| return; |
| } |
| } |
| if (myShowNewNameField) { |
| final String newName = getNewName(); |
| if (newName.length() == 0 || myFileCopy && !PathUtil.isValidFileName(newName)) { |
| setOKActionEnabled(false); |
| return; |
| } |
| } |
| setOKActionEnabled(true); |
| } |
| |
| @Override |
| protected void doHelpAction() { |
| HelpManager.getInstance().invokeHelp("refactoring.copyClass"); |
| } |
| } |