| /* |
| * 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.uiDesigner.propertyInspector.editors.string; |
| |
| import com.intellij.CommonBundle; |
| import com.intellij.ide.util.TreeClassChooserFactory; |
| import com.intellij.ide.util.TreeFileChooser; |
| import com.intellij.lang.properties.IProperty; |
| import com.intellij.lang.properties.PropertiesReferenceManager; |
| import com.intellij.lang.properties.PropertiesUtilBase; |
| import com.intellij.lang.properties.psi.PropertiesFile; |
| import com.intellij.lang.properties.psi.Property; |
| import com.intellij.openapi.application.ApplicationManager; |
| import com.intellij.openapi.command.CommandProcessor; |
| import com.intellij.openapi.command.undo.UndoUtil; |
| import com.intellij.openapi.diagnostic.Logger; |
| import com.intellij.openapi.fileTypes.StdFileTypes; |
| import com.intellij.openapi.module.Module; |
| import com.intellij.openapi.progress.ProgressManager; |
| import com.intellij.openapi.project.Project; |
| import com.intellij.openapi.ui.DialogWrapper; |
| import com.intellij.openapi.ui.InputValidator; |
| import com.intellij.openapi.ui.Messages; |
| import com.intellij.openapi.ui.TextFieldWithBrowseButton; |
| import com.intellij.openapi.vfs.ReadonlyStatusHandler; |
| import com.intellij.openapi.vfs.VirtualFile; |
| import com.intellij.psi.PsiDocumentManager; |
| import com.intellij.psi.PsiFile; |
| import com.intellij.psi.PsiMethod; |
| import com.intellij.psi.PsiReference; |
| import com.intellij.psi.search.GlobalSearchScope; |
| import com.intellij.psi.search.searches.ReferencesSearch; |
| import com.intellij.psi.util.PsiTreeUtil; |
| import com.intellij.uiDesigner.FormEditingUtil; |
| import com.intellij.uiDesigner.StringDescriptorManager; |
| import com.intellij.uiDesigner.UIDesignerBundle; |
| import com.intellij.uiDesigner.binding.FormReferenceProvider; |
| import com.intellij.uiDesigner.compiler.AsmCodeGenerator; |
| import com.intellij.uiDesigner.designSurface.GuiEditor; |
| import com.intellij.uiDesigner.lw.StringDescriptor; |
| import com.intellij.util.ArrayUtil; |
| import com.intellij.util.IncorrectOperationException; |
| import com.intellij.util.Processor; |
| import org.jetbrains.annotations.NonNls; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.annotations.Nullable; |
| |
| import javax.swing.*; |
| import java.awt.*; |
| import java.awt.event.ActionEvent; |
| import java.awt.event.ActionListener; |
| import java.awt.event.KeyEvent; |
| import java.util.*; |
| |
| /** |
| * @author Anton Katilin |
| * @author Vladimir Kondratyev |
| */ |
| public final class StringEditorDialog extends DialogWrapper{ |
| private static final Logger LOG = Logger.getInstance("#com.intellij.uiDesigner.propertyInspector.editors.string.StringEditorDialog"); |
| |
| @NonNls private static final String CARD_STRING = "string"; |
| @NonNls private static final String CARD_BUNDLE = "bundle"; |
| |
| private final GuiEditor myEditor; |
| /** Descriptor to be edited */ |
| private StringDescriptor myValue; |
| private final MyForm myForm; |
| private final Locale myLocale; |
| private boolean myDefaultBundleInitialized = false; |
| |
| StringEditorDialog(final Component parent, |
| final StringDescriptor descriptor, |
| @Nullable Locale locale, |
| final GuiEditor editor) { |
| super(parent, true); |
| myLocale = locale; |
| |
| myEditor = editor; |
| |
| myForm = new MyForm(); |
| setTitle(UIDesignerBundle.message("title.edit.text")); |
| setValue(descriptor); |
| |
| init(); /* run initialization proc */ |
| } |
| |
| protected String getDimensionServiceKey() { |
| return getClass().getName(); |
| } |
| |
| public JComponent getPreferredFocusedComponent() { |
| if(myForm.myRbString.isSelected()){ |
| return myForm.myTfValue; |
| } |
| else{ |
| return super.getPreferredFocusedComponent(); |
| } |
| } |
| |
| @Override protected void doOKAction() { |
| if (myForm.myRbResourceBundle.isSelected()) { |
| final StringDescriptor descriptor = getDescriptor(); |
| if (descriptor != null && descriptor.getKey().length() > 0) { |
| final String value = myForm.myTfRbValue.getText(); |
| final PropertiesFile propFile = getPropertiesFile(descriptor); |
| if (propFile != null && propFile.findPropertyByKey(descriptor.getKey()) == null) { |
| saveCreatedProperty(propFile, descriptor.getKey(), value, myEditor.getPsiFile()); |
| } |
| else { |
| final String newKeyName = saveModifiedPropertyValue(myEditor.getModule(), descriptor, myLocale, value, myEditor.getPsiFile()); |
| if (newKeyName != null) { |
| myForm.myTfKey.setText(newKeyName); |
| } |
| } |
| } |
| } |
| super.doOKAction(); |
| } |
| |
| private PropertiesFile getPropertiesFile(final StringDescriptor descriptor) { |
| final PropertiesReferenceManager manager = PropertiesReferenceManager.getInstance(myEditor.getProject()); |
| return manager.findPropertiesFile(myEditor.getModule(), descriptor.getDottedBundleName(), myLocale); |
| } |
| |
| @Nullable |
| public static String saveModifiedPropertyValue(final Module module, final StringDescriptor descriptor, |
| final Locale locale, final String editedValue, final PsiFile formFile) { |
| final PropertiesReferenceManager manager = PropertiesReferenceManager.getInstance(module.getProject()); |
| final PropertiesFile propFile = manager.findPropertiesFile(module, descriptor.getDottedBundleName(), locale); |
| if (propFile != null) { |
| final IProperty propertyByKey = propFile.findPropertyByKey(descriptor.getKey()); |
| if (propertyByKey instanceof Property && !editedValue.equals(propertyByKey.getValue())) { |
| final Collection<PsiReference> references = findPropertyReferences((Property)propertyByKey, module); |
| |
| String newKeyName = null; |
| if (references.size() > 1) { |
| final int rc = Messages.showYesNoCancelDialog(module.getProject(), UIDesignerBundle.message("edit.text.multiple.usages", |
| propertyByKey.getUnescapedKey(), references.size()), |
| UIDesignerBundle.message("edit.text.multiple.usages.title"), |
| UIDesignerBundle.message("edit.text.change.all"), |
| UIDesignerBundle.message("edit.text.make.unique"), |
| CommonBundle.getCancelButtonText(), |
| Messages.getWarningIcon()); |
| if (rc == Messages.CANCEL) { |
| return null; |
| } |
| if (rc == Messages.NO) { |
| newKeyName = promptNewKeyName(module.getProject(), propFile, descriptor.getKey()); |
| if (newKeyName == null) return null; |
| } |
| } |
| final ReadonlyStatusHandler.OperationStatus operationStatus = |
| ReadonlyStatusHandler.getInstance(module.getProject()).ensureFilesWritable(propFile.getVirtualFile()); |
| if (operationStatus.hasReadonlyFiles()) { |
| return null; |
| } |
| final String newKeyName1 = newKeyName; |
| CommandProcessor.getInstance().executeCommand( |
| module.getProject(), |
| new Runnable() { |
| public void run() { |
| UndoUtil.markPsiFileForUndo(formFile); |
| ApplicationManager.getApplication().runWriteAction(new Runnable() { |
| public void run() { |
| PsiDocumentManager.getInstance(module.getProject()).commitAllDocuments(); |
| try { |
| if (newKeyName1 != null) { |
| propFile.addProperty(newKeyName1, editedValue); |
| } |
| else { |
| final IProperty propertyByKey = propFile.findPropertyByKey(descriptor.getKey()); |
| if (propertyByKey != null) { |
| propertyByKey.setValue(editedValue); |
| } |
| } |
| } |
| catch (IncorrectOperationException e) { |
| LOG.error(e); |
| } |
| } |
| }); |
| } |
| }, UIDesignerBundle.message("command.update.property"), null); |
| return newKeyName; |
| } |
| } |
| return null; |
| } |
| |
| private static Collection<PsiReference> findPropertyReferences(final Property pproperty, final Module module) { |
| final Collection<PsiReference> references = Collections.synchronizedList(new ArrayList<PsiReference>()); |
| ProgressManager.getInstance().runProcessWithProgressSynchronously( |
| new Runnable() { |
| public void run() { |
| ReferencesSearch.search(pproperty).forEach(new Processor<PsiReference>() { |
| public boolean process(final PsiReference psiReference) { |
| PsiMethod method = PsiTreeUtil.getParentOfType(psiReference.getElement(), PsiMethod.class); |
| if (method == null || !AsmCodeGenerator.SETUP_METHOD_NAME.equals(method.getName())) { |
| references.add(psiReference); |
| } |
| return true; |
| } |
| }); |
| } |
| }, UIDesignerBundle.message("edit.text.searching.references"), false, module.getProject() |
| ); |
| return references; |
| } |
| |
| private static String promptNewKeyName(final Project project, final PropertiesFile propFile, final String key) { |
| String newName; |
| int index = 0; |
| do { |
| index++; |
| newName = key + index; |
| } while(propFile.findPropertyByKey(newName) != null); |
| |
| InputValidator validator = new InputValidator() { |
| public boolean checkInput(String inputString) { |
| return inputString.length() > 0 && propFile.findPropertyByKey(inputString) == null; |
| } |
| |
| public boolean canClose(String inputString) { |
| return checkInput(inputString); |
| } |
| }; |
| return Messages.showInputDialog(project, UIDesignerBundle.message("edit.text.unique.key.prompt"), |
| UIDesignerBundle.message("edit.text.multiple.usages.title"), |
| Messages.getQuestionIcon(), newName, validator); |
| } |
| |
| public static boolean saveCreatedProperty(final PropertiesFile bundle, final String name, final String value, |
| final PsiFile formFile) { |
| final ReadonlyStatusHandler.OperationStatus operationStatus = |
| ReadonlyStatusHandler.getInstance(bundle.getProject()).ensureFilesWritable(bundle.getVirtualFile()); |
| if (operationStatus.hasReadonlyFiles()) { |
| return false; |
| } |
| CommandProcessor.getInstance().executeCommand( |
| bundle.getProject(), |
| new Runnable() { |
| public void run() { |
| UndoUtil.markPsiFileForUndo(formFile); |
| ApplicationManager.getApplication().runWriteAction(new Runnable() { |
| public void run() { |
| try { |
| bundle.addProperty(name, value); |
| } |
| catch (IncorrectOperationException e1) { |
| LOG.error(e1); |
| } |
| } |
| }); |
| } |
| }, UIDesignerBundle.message("command.create.property"), null); |
| return true; |
| } |
| |
| /** |
| * @return edited descriptor. If initial descriptor was <code>null</code> |
| * and user didn't change anything then this method returns <code>null</code>. |
| */ |
| @Nullable |
| StringDescriptor getDescriptor(){ |
| if(myForm.myRbString.isSelected()){ // plain value |
| final String value = myForm.myTfValue.getText(); |
| if(myValue == null && value.length() == 0){ |
| return null; |
| } |
| else{ |
| final StringDescriptor stringDescriptor = StringDescriptor.create(value); |
| stringDescriptor.setNoI18n(myForm.myNoI18nCheckbox.isSelected()); |
| return stringDescriptor; |
| } |
| } |
| else{ // bundled value |
| final String bundleName = myForm.myTfBundleName.getText(); |
| final String key = myForm.myTfKey.getText(); |
| return new StringDescriptor(bundleName, key); |
| } |
| } |
| |
| /** |
| * Applies specified descriptor to the proper card |
| */ |
| private void setValue(final StringDescriptor descriptor){ |
| myValue = descriptor; |
| final CardLayout cardLayout = (CardLayout)myForm.myCardHolder.getLayout(); |
| if(descriptor == null || descriptor.getValue() != null){ // trivial descriptor |
| myForm.myRbString.setSelected(true); |
| myForm.showStringDescriptor(descriptor); |
| cardLayout.show(myForm.myCardHolder, CARD_STRING); |
| } |
| else{ // bundled property |
| myForm.myRbResourceBundle.setSelected(true); |
| myForm.showResourceBundleDescriptor(descriptor); |
| cardLayout.show(myForm.myCardHolder, CARD_BUNDLE); |
| } |
| } |
| |
| protected JComponent createCenterPanel() { |
| return myForm.myPanel; |
| } |
| |
| private final class MyForm{ |
| private JRadioButton myRbString; |
| private JRadioButton myRbResourceBundle; |
| private JPanel myCardHolder; |
| private JPanel myPanel; |
| private JTextArea myTfValue; |
| private JCheckBox myNoI18nCheckbox; |
| private TextFieldWithBrowseButton myTfBundleName; |
| private TextFieldWithBrowseButton myTfKey; |
| private JTextField myTfRbValue; |
| private JLabel myLblKey; |
| private JLabel myLblBundleName; |
| |
| public MyForm() { |
| myRbString.addActionListener( |
| new ActionListener() { |
| public void actionPerformed(final ActionEvent e) { |
| CardLayout cardLayout = (CardLayout) myCardHolder.getLayout(); |
| cardLayout.show(myCardHolder, CARD_STRING); |
| } |
| } |
| ); |
| |
| myRbResourceBundle.addActionListener( |
| new ActionListener() { |
| public void actionPerformed(final ActionEvent e) { |
| if (!myDefaultBundleInitialized) { |
| myDefaultBundleInitialized = true; |
| Set<String> bundleNames = FormEditingUtil.collectUsedBundleNames(myEditor.getRootContainer()); |
| if (bundleNames.size() > 0) { |
| myTfBundleName.setText(ArrayUtil.toStringArray(bundleNames)[0]); |
| } |
| } |
| CardLayout cardLayout = (CardLayout) myCardHolder.getLayout(); |
| cardLayout.show(myCardHolder, CARD_BUNDLE); |
| } |
| } |
| ); |
| |
| setupResourceBundleCard(); |
| } |
| |
| private void setupResourceBundleCard() { |
| // Enable keyboard pressing |
| myTfBundleName.registerKeyboardAction( |
| new AbstractAction() { |
| public void actionPerformed(final ActionEvent e) { |
| myTfBundleName.getButton().doClick(); |
| } |
| }, |
| KeyStroke.getKeyStroke(myLblBundleName.getDisplayedMnemonic(), KeyEvent.ALT_DOWN_MASK), |
| JComponent.WHEN_IN_FOCUSED_WINDOW |
| ); |
| |
| myTfBundleName.addActionListener( |
| new ActionListener() { |
| public void actionPerformed(final ActionEvent e) { |
| Project project = myEditor.getProject(); |
| final String bundleNameText = myTfBundleName.getText().replace('/', '.'); |
| PropertiesFile file = PropertiesUtilBase.getPropertiesFile(bundleNameText, myEditor.getModule(), myLocale); |
| PsiFile initialPropertiesFile = file == null ? null : file.getContainingFile(); |
| final GlobalSearchScope moduleScope = GlobalSearchScope.moduleWithDependenciesScope(myEditor.getModule()); |
| TreeFileChooser fileChooser = TreeClassChooserFactory.getInstance(project).createFileChooser(UIDesignerBundle.message("title.choose.properties.file"), initialPropertiesFile, |
| StdFileTypes.PROPERTIES, new TreeFileChooser.PsiFileFilter() { |
| public boolean accept(PsiFile file) { |
| final VirtualFile virtualFile = file.getVirtualFile(); |
| return virtualFile != null && moduleScope.contains(virtualFile); |
| } |
| }); |
| fileChooser.showDialog(); |
| PropertiesFile propertiesFile = (PropertiesFile)fileChooser.getSelectedFile(); |
| if (propertiesFile == null) { |
| return; |
| } |
| final String bundleName = FormReferenceProvider.getBundleName(propertiesFile); |
| if (bundleName == null) { |
| return; |
| } |
| myTfBundleName.setText(bundleName); |
| } |
| } |
| ); |
| |
| // Enable keyboard pressing |
| myTfKey.registerKeyboardAction( |
| new AbstractAction() { |
| public void actionPerformed(final ActionEvent e) { |
| myTfKey.getButton().doClick(); |
| } |
| }, |
| KeyStroke.getKeyStroke(myLblKey.getDisplayedMnemonic(), KeyEvent.ALT_DOWN_MASK), |
| JComponent.WHEN_IN_FOCUSED_WINDOW |
| ); |
| |
| myTfKey.addActionListener( |
| new ActionListener() { |
| public void actionPerformed(final ActionEvent e) { |
| // 1. Check that bundle exist. Otherwise we cannot show key chooser |
| final String bundleName = myTfBundleName.getText(); |
| if(bundleName.length() == 0){ |
| Messages.showErrorDialog( |
| UIDesignerBundle.message("error.specify.bundle.name"), |
| CommonBundle.getErrorTitle() |
| ); |
| return; |
| } |
| final PropertiesReferenceManager manager = PropertiesReferenceManager.getInstance(myEditor.getProject()); |
| final PropertiesFile bundle = manager.findPropertiesFile(myEditor.getModule(), bundleName.replace('/', '.'), myLocale); |
| if(bundle == null){ |
| Messages.showErrorDialog( |
| UIDesignerBundle.message("error.bundle.does.not.exist", bundleName), |
| CommonBundle.getErrorTitle() |
| ); |
| return; |
| } |
| |
| // 2. Show key chooser |
| final KeyChooserDialog dialog = new KeyChooserDialog( |
| myTfKey, |
| bundle, |
| bundleName, |
| myTfKey.getText(), // key to preselect |
| myEditor |
| ); |
| dialog.show(); |
| if(!dialog.isOK()){ |
| return; |
| } |
| |
| // 3. Apply new key/value |
| final StringDescriptor descriptor = dialog.getDescriptor(); |
| if(descriptor == null){ |
| return; |
| } |
| myTfKey.setText(descriptor.getKey()); |
| myTfRbValue.setText(descriptor.getResolvedValue()); |
| } |
| } |
| ); |
| } |
| |
| public void showStringDescriptor(@Nullable final StringDescriptor descriptor) { |
| myTfValue.setText(StringDescriptorManager.getInstance(myEditor.getModule()).resolve(descriptor, myLocale)); |
| myNoI18nCheckbox.setSelected(descriptor != null && descriptor.isNoI18n()); |
| } |
| |
| public void showResourceBundleDescriptor(@NotNull final StringDescriptor descriptor) { |
| final String key = descriptor.getKey(); |
| LOG.assertTrue(key != null); |
| myTfBundleName.setText(descriptor.getBundleName()); |
| myTfKey.setText(key); |
| myTfRbValue.setText(StringDescriptorManager.getInstance(myEditor.getModule()).resolve(descriptor, myLocale)); |
| } |
| } |
| } |