| /* |
| * Copyright 2016 The Bazel Authors. All rights reserved. |
| * |
| * 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.google.idea.blaze.android.resources.actions; |
| |
| import com.android.resources.ResourceFolderType; |
| import com.android.resources.ResourceType; |
| import com.android.tools.idea.res.ResourceNameValidator; |
| import com.intellij.openapi.application.ApplicationManager; |
| import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory; |
| import com.intellij.openapi.module.Module; |
| import com.intellij.openapi.project.Project; |
| import com.intellij.openapi.ui.ValidationInfo; |
| import com.intellij.openapi.util.text.StringUtil; |
| import com.intellij.openapi.vfs.VirtualFile; |
| import com.intellij.psi.PsiDirectory; |
| import com.intellij.psi.PsiManager; |
| import com.intellij.ui.ComboboxWithBrowseButton; |
| import com.intellij.ui.components.JBLabel; |
| import com.intellij.uiDesigner.core.GridConstraints; |
| import com.intellij.uiDesigner.core.GridLayoutManager; |
| import java.awt.BorderLayout; |
| import java.awt.Dimension; |
| import java.awt.Insets; |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Set; |
| import java.util.function.Function; |
| import javax.swing.DefaultComboBoxModel; |
| import javax.swing.JComboBox; |
| import javax.swing.JComponent; |
| import javax.swing.JPanel; |
| import javax.swing.JTextField; |
| import org.jetbrains.android.actions.CreateXmlResourceDialog; |
| import org.jetbrains.android.actions.CreateXmlResourcePanel; |
| import org.jetbrains.android.actions.CreateXmlResourceSubdirPanel; |
| import org.jetbrains.android.actions.CreateXmlResourceSubdirPanel.Parent; |
| import org.jetbrains.android.util.AndroidResourceUtil; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.annotations.Nullable; |
| |
| /** |
| * Embeddable UI for selecting how to create a new resource value (which XML file and directories to |
| * place it). |
| */ |
| public class BlazeCreateXmlResourcePanel implements CreateXmlResourcePanel, Parent { |
| |
| private JPanel myPanel; |
| private JTextField myNameField; |
| private JTextField myValueField; |
| private JBLabel myValueLabel; |
| private JBLabel myNameLabel; |
| private JComboBox myFileNameCombo; |
| private JBLabel myResDirLabel; |
| private ComboboxWithBrowseButton myResDirCombo; |
| private JBLabel myFileNameLabel; |
| |
| private final Module myModule; |
| private final ResourceType myResourceType; |
| |
| private JPanel myDirectoriesPanel; |
| private JBLabel myDirectoriesLabel; |
| private CreateXmlResourceSubdirPanel mySubdirPanel; |
| |
| private ResourceNameValidator myResourceNameValidator; |
| @Nullable private VirtualFile myContextFile; |
| @Nullable private VirtualFile myResDirectory; |
| |
| public BlazeCreateXmlResourcePanel( |
| @NotNull Module module, |
| @NotNull ResourceType resourceType, |
| @NotNull ResourceFolderType folderType, |
| @Nullable String resourceName, |
| @Nullable String resourceValue, |
| boolean chooseName, |
| boolean chooseValue, |
| boolean chooseFilename, |
| @Nullable VirtualFile defaultFile, |
| @Nullable VirtualFile contextFile, |
| @NotNull Function<Module, ResourceNameValidator> nameValidatorFactory) { |
| setupUi(); |
| setChangeNameVisible(false); |
| setChangeValueVisible(false); |
| setChangeFileNameVisible(chooseFilename); |
| myModule = module; |
| myContextFile = contextFile; |
| if (chooseName) { |
| setChangeNameVisible(true); |
| } |
| if (!StringUtil.isEmpty(resourceName)) { |
| myNameField.setText(resourceName); |
| } |
| |
| if (chooseValue) { |
| setChangeValueVisible(true); |
| if (!StringUtil.isEmpty(resourceValue)) { |
| myValueField.setText(resourceValue); |
| } |
| } |
| |
| myResourceType = resourceType; |
| |
| ApplicationManager.getApplication().assertReadAccessAllowed(); |
| // Set up UI to choose the base directory if needed (use context to prune selection). |
| myResDirLabel.setVisible(false); |
| myResDirCombo.setVisible(false); |
| myResDirCombo.addBrowseFolderListener( |
| module.getProject(), FileChooserDescriptorFactory.createSingleFolderDescriptor()); |
| setupResourceDirectoryCombo(); |
| |
| if (defaultFile == null) { |
| final String defaultFileName = AndroidResourceUtil.getDefaultResourceFileName(myResourceType); |
| |
| if (defaultFileName != null) { |
| myFileNameCombo.getEditor().setItem(defaultFileName); |
| } |
| } |
| |
| myDirectoriesLabel.setLabelFor(myDirectoriesPanel); |
| mySubdirPanel = |
| new CreateXmlResourceSubdirPanel(module.getProject(), folderType, myDirectoriesPanel, this); |
| myResourceNameValidator = nameValidatorFactory.apply(getModule()); |
| |
| if (defaultFile != null) { |
| resetFromFile(defaultFile, module.getProject()); |
| } |
| } |
| |
| private void setupResourceDirectoryCombo() { |
| Project project = myModule.getProject(); |
| if (myContextFile != null) { |
| // Try to figure out res/ dir from context |
| // (e.g., while refactoring an xml file that's a child of a res/ directory). |
| // We currently take the parent and hide the combo box. |
| PsiManager manager = PsiManager.getInstance(project); |
| VirtualFile virtualDirectory = |
| BlazeCreateResourceUtils.getResDirFromDataContext(myContextFile); |
| PsiDirectory directory = |
| virtualDirectory != null ? manager.findDirectory(virtualDirectory) : null; |
| if (directory != null) { |
| myResDirectory = directory.getVirtualFile(); |
| } else { |
| // As a last resort, if we have poor context, |
| // e.g., quick fix from within a .java file, set up the UI |
| // based on the deps of the .java file. |
| BlazeCreateResourceUtils.setupResDirectoryChoices( |
| project, myContextFile, myResDirLabel, myResDirCombo); |
| } |
| } else { |
| // As a last resort, if we have no context at all, set up some UI. |
| BlazeCreateResourceUtils.setupResDirectoryChoices( |
| project, null, myResDirLabel, myResDirCombo); |
| } |
| } |
| |
| @Override |
| public void resetToDefault() { |
| String defaultFileName = AndroidResourceUtil.getDefaultResourceFileName(myResourceType); |
| if (defaultFileName != null) { |
| myFileNameCombo.getEditor().setItem(defaultFileName); |
| } |
| setupResourceDirectoryCombo(); |
| mySubdirPanel.resetToDefault(); |
| } |
| |
| @Override |
| public void resetFromFile(@NotNull VirtualFile file, @NotNull Project project) { |
| final VirtualFile parent = file.getParent(); |
| if (parent == null) { |
| return; |
| } |
| mySubdirPanel.resetFromFile(parent); |
| myFileNameCombo.getEditor().setItem(file.getName()); |
| setupResourceDirectoryCombo(); |
| myPanel.repaint(); |
| } |
| |
| @Override |
| public String getResourceName() { |
| return myNameField.getText().trim(); |
| } |
| |
| @Override |
| public ResourceType getType() { |
| return myResourceType; |
| } |
| |
| @Override |
| public String getValue() { |
| return myValueField.getText().trim(); |
| } |
| |
| @Override |
| public VirtualFile getResourceDirectory() { |
| if (myResDirectory != null) { |
| return myResDirectory; |
| } |
| if (myResDirCombo.isVisible()) { |
| PsiDirectory directory = |
| BlazeCreateResourceUtils.getResDirFromUI(myModule.getProject(), myResDirCombo); |
| return directory != null ? directory.getVirtualFile() : null; |
| } |
| return null; |
| } |
| |
| @Override |
| public List<String> getDirNames() { |
| return mySubdirPanel.getDirNames(); |
| } |
| |
| @Override |
| public String getFileName() { |
| return ((String) myFileNameCombo.getEditor().getItem()).trim(); |
| } |
| |
| @Override |
| public ValidationInfo doValidate() { |
| String resourceName = getResourceName(); |
| VirtualFile resourceDir = getResourceDirectory(); |
| List<String> directoryNames = getDirNames(); |
| String fileName = getFileName(); |
| |
| if (myNameField.isVisible() && resourceName.isEmpty()) { |
| return new ValidationInfo("specify resource name", myNameField); |
| } else if (myNameField.isVisible() |
| && !AndroidResourceUtil.isCorrectAndroidResourceName(resourceName)) { |
| return new ValidationInfo(resourceName + " is not correct resource name", myNameField); |
| } else if (fileName.isEmpty()) { |
| return new ValidationInfo("specify file name", myFileNameCombo); |
| } else if (resourceDir == null) { |
| return new ValidationInfo("specify a resource directory", myResDirCombo); |
| } else if (directoryNames.isEmpty()) { |
| return new ValidationInfo("choose directories", myDirectoriesPanel); |
| } |
| |
| return CreateXmlResourceDialog.checkIfResourceAlreadyExists( |
| myModule.getProject(), resourceDir, resourceName, myResourceType, directoryNames, fileName); |
| } |
| |
| @Override |
| public ResourceNameValidator getResourceNameValidator() { |
| return myResourceNameValidator; |
| } |
| |
| @NotNull |
| @Override |
| public Module getModule() { |
| return myModule; |
| } |
| |
| /** @see CreateXmlResourceDialog#getPreferredFocusedComponent() */ |
| @Override |
| public JComponent getPreferredFocusedComponent() { |
| String name = myNameField.getText(); |
| if (name.isEmpty()) { |
| return myNameField; |
| } else if (myValueField.isVisible()) { |
| return myValueField; |
| } else { |
| return myFileNameCombo; |
| } |
| } |
| |
| @Override |
| public JComponent getPanel() { |
| return myPanel; |
| } |
| |
| private void setChangeFileNameVisible(boolean isVisible) { |
| myFileNameLabel.setVisible(isVisible); |
| myFileNameCombo.setVisible(isVisible); |
| } |
| |
| private void setChangeValueVisible(boolean isVisible) { |
| myValueField.setVisible(isVisible); |
| myValueLabel.setVisible(isVisible); |
| } |
| |
| private void setChangeNameVisible(boolean isVisible) { |
| myNameField.setVisible(isVisible); |
| myNameLabel.setVisible(isVisible); |
| } |
| |
| // Only public for CreateXmlResourceSubdirPanel.Parent |
| @Override |
| public void updateFilesCombo(List<VirtualFile> directories) { |
| final Object oldItem = myFileNameCombo.getEditor().getItem(); |
| final Set<String> fileNameSet = new HashSet<>(); |
| |
| for (VirtualFile dir : directories) { |
| for (VirtualFile file : dir.getChildren()) { |
| fileNameSet.add(file.getName()); |
| } |
| } |
| final List<String> fileNames = new ArrayList<>(fileNameSet); |
| Collections.sort(fileNames); |
| myFileNameCombo.setModel(new DefaultComboBoxModel(fileNames.toArray())); |
| myFileNameCombo.getEditor().setItem(oldItem); |
| } |
| |
| /** Initially generated by IntelliJ from a .form file. */ |
| private void setupUi() { |
| myPanel = new JPanel(); |
| myPanel.setLayout(new GridLayoutManager(6, 2, new Insets(0, 0, 5, 0), -1, -1)); |
| myNameLabel = new JBLabel(); |
| myNameLabel.setText("Resource name:"); |
| myNameLabel.setDisplayedMnemonic('N'); |
| myNameLabel.setDisplayedMnemonicIndex(9); |
| myPanel.add( |
| myNameLabel, |
| new GridConstraints( |
| 0, |
| 0, |
| 1, |
| 1, |
| GridConstraints.ANCHOR_WEST, |
| GridConstraints.FILL_NONE, |
| GridConstraints.SIZEPOLICY_FIXED, |
| GridConstraints.SIZEPOLICY_FIXED, |
| null, |
| null, |
| null, |
| 0, |
| false)); |
| myNameField = new JTextField(); |
| myPanel.add( |
| myNameField, |
| new GridConstraints( |
| 0, |
| 1, |
| 1, |
| 1, |
| GridConstraints.ANCHOR_WEST, |
| GridConstraints.FILL_HORIZONTAL, |
| GridConstraints.SIZEPOLICY_WANT_GROW, |
| GridConstraints.SIZEPOLICY_FIXED, |
| null, |
| new Dimension(150, -1), |
| null, |
| 0, |
| false)); |
| myFileNameLabel = new JBLabel(); |
| myFileNameLabel.setText("File name:"); |
| myFileNameLabel.setDisplayedMnemonic('F'); |
| myFileNameLabel.setDisplayedMnemonicIndex(0); |
| myPanel.add( |
| myFileNameLabel, |
| new GridConstraints( |
| 3, |
| 0, |
| 1, |
| 1, |
| GridConstraints.ANCHOR_WEST, |
| GridConstraints.FILL_NONE, |
| GridConstraints.SIZEPOLICY_FIXED, |
| GridConstraints.SIZEPOLICY_FIXED, |
| null, |
| null, |
| null, |
| 0, |
| false)); |
| myDirectoriesPanel = new JPanel(); |
| myDirectoriesPanel.setLayout(new BorderLayout(0, 0)); |
| myPanel.add( |
| myDirectoriesPanel, |
| new GridConstraints( |
| 5, |
| 0, |
| 1, |
| 2, |
| GridConstraints.ANCHOR_CENTER, |
| GridConstraints.FILL_BOTH, |
| GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, |
| GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, |
| null, |
| null, |
| null, |
| 0, |
| false)); |
| myDirectoriesLabel = new JBLabel(); |
| myDirectoriesLabel.setText("Create the resource in directories:"); |
| myDirectoriesLabel.setDisplayedMnemonic('C'); |
| myDirectoriesLabel.setDisplayedMnemonicIndex(0); |
| myPanel.add( |
| myDirectoriesLabel, |
| new GridConstraints( |
| 4, |
| 0, |
| 1, |
| 2, |
| GridConstraints.ANCHOR_WEST, |
| GridConstraints.FILL_NONE, |
| GridConstraints.SIZEPOLICY_FIXED, |
| GridConstraints.SIZEPOLICY_FIXED, |
| null, |
| null, |
| null, |
| 0, |
| false)); |
| myValueLabel = new JBLabel(); |
| myValueLabel.setText("Resource value:"); |
| myValueLabel.setDisplayedMnemonic('V'); |
| myValueLabel.setDisplayedMnemonicIndex(9); |
| myPanel.add( |
| myValueLabel, |
| new GridConstraints( |
| 1, |
| 0, |
| 1, |
| 1, |
| GridConstraints.ANCHOR_WEST, |
| GridConstraints.FILL_NONE, |
| GridConstraints.SIZEPOLICY_FIXED, |
| GridConstraints.SIZEPOLICY_FIXED, |
| null, |
| null, |
| null, |
| 0, |
| false)); |
| myValueField = new JTextField(); |
| myPanel.add( |
| myValueField, |
| new GridConstraints( |
| 1, |
| 1, |
| 1, |
| 1, |
| GridConstraints.ANCHOR_WEST, |
| GridConstraints.FILL_HORIZONTAL, |
| GridConstraints.SIZEPOLICY_WANT_GROW, |
| GridConstraints.SIZEPOLICY_FIXED, |
| null, |
| new Dimension(150, -1), |
| null, |
| 0, |
| false)); |
| myFileNameCombo = new JComboBox(); |
| myFileNameCombo.setEditable(true); |
| myPanel.add( |
| myFileNameCombo, |
| new GridConstraints( |
| 3, |
| 1, |
| 1, |
| 1, |
| GridConstraints.ANCHOR_WEST, |
| GridConstraints.FILL_HORIZONTAL, |
| GridConstraints.SIZEPOLICY_CAN_GROW, |
| GridConstraints.SIZEPOLICY_FIXED, |
| null, |
| null, |
| null, |
| 0, |
| false)); |
| myResDirLabel = new JBLabel(); |
| myResDirLabel.setText("Base directory:"); |
| myResDirLabel.setDisplayedMnemonic('B'); |
| myResDirLabel.setDisplayedMnemonicIndex(0); |
| myPanel.add( |
| myResDirLabel, |
| new GridConstraints( |
| 2, |
| 0, |
| 1, |
| 1, |
| GridConstraints.ANCHOR_WEST, |
| GridConstraints.FILL_NONE, |
| GridConstraints.SIZEPOLICY_FIXED, |
| GridConstraints.SIZEPOLICY_FIXED, |
| null, |
| null, |
| null, |
| 0, |
| false)); |
| myResDirCombo = new ComboboxWithBrowseButton(); |
| myPanel.add( |
| myResDirCombo, |
| new GridConstraints( |
| 2, |
| 1, |
| 1, |
| 1, |
| GridConstraints.ANCHOR_WEST, |
| GridConstraints.FILL_HORIZONTAL, |
| GridConstraints.SIZEPOLICY_CAN_GROW, |
| GridConstraints.SIZEPOLICY_FIXED, |
| null, |
| null, |
| null, |
| 0, |
| false)); |
| myNameLabel.setLabelFor(myNameField); |
| myFileNameLabel.setLabelFor(myFileNameCombo); |
| myValueLabel.setLabelFor(myValueField); |
| } |
| } |