| /* |
| * 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.fileTemplates.impl; |
| |
| import com.intellij.codeInsight.template.impl.TemplateColors; |
| import com.intellij.ide.DataManager; |
| import com.intellij.ide.IdeBundle; |
| import com.intellij.ide.fileTemplates.FileTemplate; |
| import com.intellij.ide.fileTemplates.FileTemplateManager; |
| import com.intellij.lexer.FlexAdapter; |
| import com.intellij.lexer.Lexer; |
| import com.intellij.lexer.MergingLexerAdapter; |
| import com.intellij.openapi.actionSystem.CommonDataKeys; |
| import com.intellij.openapi.diagnostic.Logger; |
| import com.intellij.openapi.editor.Document; |
| import com.intellij.openapi.editor.Editor; |
| import com.intellij.openapi.editor.EditorFactory; |
| import com.intellij.openapi.editor.EditorSettings; |
| import com.intellij.openapi.editor.colors.EditorColors; |
| import com.intellij.openapi.editor.colors.EditorColorsManager; |
| import com.intellij.openapi.editor.colors.EditorColorsScheme; |
| import com.intellij.openapi.editor.colors.TextAttributesKey; |
| import com.intellij.openapi.editor.event.DocumentAdapter; |
| import com.intellij.openapi.editor.event.DocumentEvent; |
| import com.intellij.openapi.editor.ex.EditorEx; |
| import com.intellij.openapi.editor.ex.util.LayerDescriptor; |
| import com.intellij.openapi.editor.ex.util.LayeredLexerEditorHighlighter; |
| import com.intellij.openapi.editor.highlighter.EditorHighlighter; |
| import com.intellij.openapi.editor.highlighter.EditorHighlighterFactory; |
| import com.intellij.openapi.fileTypes.*; |
| import com.intellij.openapi.options.Configurable; |
| import com.intellij.openapi.options.ConfigurationException; |
| import com.intellij.openapi.project.Project; |
| import com.intellij.openapi.project.ProjectManager; |
| import com.intellij.openapi.ui.Splitter; |
| import com.intellij.openapi.util.Comparing; |
| import com.intellij.openapi.util.io.FileUtil; |
| import com.intellij.openapi.util.text.StringUtil; |
| import com.intellij.psi.PsiDocumentManager; |
| import com.intellij.psi.PsiFile; |
| import com.intellij.psi.PsiFileFactory; |
| import com.intellij.psi.tree.IElementType; |
| import com.intellij.psi.tree.TokenSet; |
| import com.intellij.testFramework.LightVirtualFile; |
| import com.intellij.ui.ScrollPaneFactory; |
| import com.intellij.ui.SeparatorFactory; |
| import com.intellij.util.containers.ContainerUtil; |
| import com.intellij.util.ui.UIUtil; |
| import org.jetbrains.annotations.NonNls; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.annotations.Nullable; |
| |
| import javax.swing.*; |
| import javax.swing.event.ChangeEvent; |
| import javax.swing.event.ChangeListener; |
| import java.awt.*; |
| import java.awt.event.FocusAdapter; |
| import java.awt.event.FocusEvent; |
| import java.io.File; |
| import java.io.IOException; |
| import java.net.URL; |
| import java.util.List; |
| |
| /* |
| * @author: MYakovlev |
| * Date: Jul 26, 2002 |
| * Time: 12:46:00 PM |
| */ |
| |
| public class FileTemplateConfigurable implements Configurable, Configurable.NoScroll { |
| private static final Logger LOG = Logger.getInstance("#com.intellij.ide.fileTemplates.impl.FileTemplateConfigurable"); |
| @NonNls private static final String EMPTY_HTML = "<html></html>"; |
| @NonNls private static final String CONTENT_TYPE_PLAIN = "text/plain"; |
| |
| private JPanel myMainPanel; |
| private FileTemplate myTemplate; |
| private PsiFile myFile; |
| private Editor myTemplateEditor; |
| private JTextField myNameField; |
| private JTextField myExtensionField; |
| private JCheckBox myAdjustBox; |
| private JPanel myTopPanel; |
| private JEditorPane myDescriptionComponent; |
| private boolean myModified = false; |
| private URL myDefaultDescriptionUrl; |
| private final Project myProject; |
| |
| private final List<ChangeListener> myChangeListeners = ContainerUtil.createLockFreeCopyOnWriteList(); |
| private Splitter mySplitter; |
| private final FileType myVelocityFileType = FileTypeManager.getInstance().getFileTypeByExtension("ft"); |
| private JPanel myDescriptionPanel; |
| |
| public FileTemplateConfigurable() { |
| Project project = CommonDataKeys.PROJECT.getData(DataManager.getInstance().getDataContext()); |
| myProject = project != null ? project : ProjectManager.getInstance().getDefaultProject(); |
| } |
| |
| public FileTemplate getTemplate() { |
| return myTemplate; |
| } |
| |
| public void setTemplate(FileTemplate template, URL defaultDescription) { |
| myDefaultDescriptionUrl = defaultDescription; |
| myTemplate = template; |
| if (myMainPanel != null) { |
| reset(); |
| myNameField.selectAll(); |
| myExtensionField.selectAll(); |
| } |
| } |
| |
| public void setShowInternalMessage(String message) { |
| if (message == null) { |
| myTopPanel.removeAll(); |
| myTopPanel.add(new JLabel(IdeBundle.message("label.name")), |
| new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.NONE, |
| new Insets(0, 0, 0, 2), 0, 0)); |
| myTopPanel.add(myNameField, |
| new GridBagConstraints(1, 0, 1, 1, 1.0, 0.0, GridBagConstraints.CENTER, |
| GridBagConstraints.HORIZONTAL, new Insets(0, 2, 0, 2), 0, 0)); |
| myTopPanel.add(new JLabel(IdeBundle.message("label.extension")), |
| new GridBagConstraints(2, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.NONE, |
| new Insets(0, 2, 0, 2), 0, 0)); |
| myTopPanel.add(myExtensionField, |
| new GridBagConstraints(3, 0, 1, 1, .3, 0.0, GridBagConstraints.CENTER, |
| GridBagConstraints.HORIZONTAL, new Insets(0, 2, 0, 0), 0, 0)); |
| myExtensionField.setColumns(7); |
| } |
| else { |
| myTopPanel.removeAll(); |
| myTopPanel.add(new JLabel(message), |
| new GridBagConstraints(0, 0, 4, 1, 1.0, 0.0, GridBagConstraints.WEST, |
| GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0)); |
| myTopPanel.add(Box.createVerticalStrut(myNameField.getPreferredSize().height), |
| new GridBagConstraints(4, 0, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, |
| GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0)); |
| } |
| myMainPanel.revalidate(); |
| myTopPanel.repaint(); |
| } |
| |
| public void setShowAdjustCheckBox(boolean show) { |
| myAdjustBox.setEnabled(show); |
| } |
| |
| @Override |
| public String getDisplayName() { |
| return IdeBundle.message("title.file.templates"); |
| } |
| |
| @Override |
| public String getHelpTopic() { |
| return null; |
| } |
| |
| @Override |
| public JComponent createComponent() { |
| myMainPanel = new JPanel(new GridBagLayout()); |
| myNameField = new JTextField(); |
| myExtensionField = new JTextField(); |
| mySplitter = new Splitter(true, 0.4f); |
| |
| myTemplateEditor = createEditor(); |
| |
| myDescriptionComponent = new JEditorPane(UIUtil.HTML_MIME, EMPTY_HTML); |
| myDescriptionComponent.setEditable(false); |
| |
| myAdjustBox = new JCheckBox(IdeBundle.message("checkbox.reformat.according.to.style")); |
| myTopPanel = new JPanel(new GridBagLayout()); |
| |
| myDescriptionPanel = new JPanel(new GridBagLayout()); |
| myDescriptionPanel.add(SeparatorFactory.createSeparator(IdeBundle.message("label.description"), null), |
| new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, |
| new Insets(0, 0, 2, 0), 0, 0)); |
| myDescriptionPanel.add(ScrollPaneFactory.createScrollPane(myDescriptionComponent), |
| new GridBagConstraints(0, 1, 1, 1, 1.0, 1.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH, |
| new Insets(2, 0, 0, 0), 0, 0)); |
| |
| myMainPanel.add(myTopPanel, |
| new GridBagConstraints(0, 0, 4, 1, 1.0, 0.0, GridBagConstraints.CENTER, |
| GridBagConstraints.HORIZONTAL, new Insets(0, 0, 2, 0), 0, 0)); |
| myMainPanel.add(myAdjustBox, |
| new GridBagConstraints(0, 1, 4, 1, 0.0, 0.0, GridBagConstraints.WEST, |
| GridBagConstraints.HORIZONTAL, new Insets(2, 0, 2, 0), 0, 0)); |
| myMainPanel.add(mySplitter, |
| new GridBagConstraints(0, 2, 4, 1, 1.0, 1.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH, |
| new Insets(2, 0, 0, 0), 0, 0)); |
| |
| mySplitter.setSecondComponent(myDescriptionPanel); |
| setShowInternalMessage(null); |
| |
| myNameField.addFocusListener(new FocusAdapter() { |
| @Override |
| public void focusLost(@NotNull FocusEvent e) { |
| onNameChanged(); |
| } |
| }); |
| myExtensionField.addFocusListener(new FocusAdapter() { |
| @Override |
| public void focusLost(@NotNull FocusEvent e) { |
| onNameChanged(); |
| } |
| }); |
| myMainPanel.setPreferredSize(new Dimension(400, 300)); |
| return myMainPanel; |
| } |
| |
| private Editor createEditor() { |
| EditorFactory editorFactory = EditorFactory.getInstance(); |
| Document doc = myFile == null |
| ? editorFactory.createDocument(myTemplate == null ? "" : myTemplate.getText()) |
| : PsiDocumentManager.getInstance(myFile.getProject()).getDocument(myFile); |
| assert doc != null; |
| Editor editor = editorFactory.createEditor(doc, myProject); |
| |
| EditorSettings editorSettings = editor.getSettings(); |
| editorSettings.setVirtualSpace(false); |
| editorSettings.setLineMarkerAreaShown(false); |
| editorSettings.setIndentGuidesShown(false); |
| editorSettings.setLineNumbersShown(false); |
| editorSettings.setFoldingOutlineShown(false); |
| editorSettings.setAdditionalColumnsCount(3); |
| editorSettings.setAdditionalLinesCount(3); |
| |
| EditorColorsScheme scheme = editor.getColorsScheme(); |
| scheme.setColor(EditorColors.CARET_ROW_COLOR, null); |
| |
| editor.getDocument().addDocumentListener(new DocumentAdapter() { |
| @Override |
| public void documentChanged(DocumentEvent e) { |
| onTextChanged(); |
| } |
| }); |
| |
| ((EditorEx)editor).setHighlighter(createHighlighter()); |
| mySplitter.setFirstComponent(editor.getComponent()); |
| return editor; |
| } |
| |
| private void onTextChanged() { |
| myModified = true; |
| } |
| |
| public String getNameValue() { |
| return myNameField.getText(); |
| } |
| |
| public String getExtensionValue() { |
| return myExtensionField.getText(); |
| } |
| |
| private void onNameChanged() { |
| ChangeEvent event = new ChangeEvent(this); |
| for (ChangeListener changeListener : myChangeListeners) { |
| changeListener.stateChanged(event); |
| } |
| } |
| |
| public void addChangeListener(ChangeListener listener) { |
| if (!myChangeListeners.contains(listener)) { |
| myChangeListeners.add(listener); |
| } |
| } |
| |
| public void removeChangeListener(ChangeListener listener) { |
| myChangeListeners.remove(listener); |
| } |
| |
| @Override |
| public boolean isModified() { |
| if (myModified) { |
| return true; |
| } |
| String name = (myTemplate == null) ? "" : myTemplate.getName(); |
| String extension = (myTemplate == null) ? "" : myTemplate.getExtension(); |
| if (!Comparing.equal(name, myNameField.getText())) { |
| return true; |
| } |
| if (!Comparing.equal(extension, myExtensionField.getText())) { |
| return true; |
| } |
| if (myTemplate != null) { |
| if (myTemplate.isReformatCode() != myAdjustBox.isSelected()) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| @Override |
| public void apply() throws ConfigurationException { |
| if (myTemplate != null) { |
| myTemplate.setText(myTemplateEditor.getDocument().getText()); |
| String name = myNameField.getText(); |
| String extension = myExtensionField.getText(); |
| if (name.length() == 0 || !isValidFilename(name + "." + extension)) { |
| throw new ConfigurationException(IdeBundle.message("error.invalid.template.file.name.or.extension")); |
| } |
| myTemplate.setName(name); |
| myTemplate.setExtension(extension); |
| myTemplate.setReformatCode(myAdjustBox.isSelected()); |
| } |
| myModified = false; |
| } |
| |
| // TODO: needs to be generalized someday for other profiles |
| private static boolean isValidFilename(final String filename) { |
| if ( filename.contains("/") || filename.contains("\\") || filename.contains(":") ) { |
| return false; |
| } |
| final File tempFile = new File (FileUtil.getTempDirectory() + File.separator + filename); |
| return FileUtil.ensureCanCreateFile(tempFile); |
| } |
| |
| @Override |
| public void reset() { |
| final String text = (myTemplate == null) ? "" : myTemplate.getText(); |
| String name = (myTemplate == null) ? "" : myTemplate.getName(); |
| String extension = (myTemplate == null) ? "" : myTemplate.getExtension(); |
| String description = (myTemplate == null) ? "" : myTemplate.getDescription(); |
| |
| if ((description.length() == 0) && (myDefaultDescriptionUrl != null)) { |
| try { |
| description = UrlUtil.loadText(myDefaultDescriptionUrl); |
| } |
| catch (IOException e) { |
| LOG.error(e); |
| } |
| } |
| |
| EditorFactory.getInstance().releaseEditor(myTemplateEditor); |
| myFile = createFile(text, name); |
| myTemplateEditor = createEditor(); |
| |
| boolean adjust = (myTemplate != null) && myTemplate.isReformatCode(); |
| myNameField.setText(name); |
| myExtensionField.setText(extension); |
| myAdjustBox.setSelected(adjust); |
| String desc = description.length() > 0 ? description : EMPTY_HTML; |
| |
| // [myakovlev] do not delete these stupid lines! Or you get Exception! |
| myDescriptionComponent.setContentType(CONTENT_TYPE_PLAIN); |
| myDescriptionComponent.setEditable(true); |
| myDescriptionComponent.setText(desc); |
| myDescriptionComponent.setContentType(UIUtil.HTML_MIME); |
| myDescriptionComponent.setText(desc); |
| myDescriptionComponent.setCaretPosition(0); |
| myDescriptionComponent.setEditable(false); |
| |
| myDescriptionPanel.setVisible(StringUtil.isNotEmpty(description)); |
| |
| myNameField.setEditable((myTemplate != null) && (!myTemplate.isDefault())); |
| myExtensionField.setEditable((myTemplate != null) && (!myTemplate.isDefault())); |
| myModified = false; |
| } |
| |
| @Nullable |
| private PsiFile createFile(final String text, final String name) { |
| if (myTemplate == null) return null; |
| |
| final FileType fileType = myVelocityFileType; |
| if (fileType == FileTypes.UNKNOWN) return null; |
| |
| final PsiFile file = PsiFileFactory.getInstance(myProject).createFileFromText(name + ".txt.ft", fileType, text, 0, true); |
| file.getViewProvider().putUserData(FileTemplateManager.DEFAULT_TEMPLATE_PROPERTIES, FileTemplateManager.getInstance().getDefaultProperties(myProject)); |
| return file; |
| } |
| |
| @Override |
| public void disposeUIResources() { |
| myMainPanel = null; |
| if (myTemplateEditor != null) { |
| EditorFactory.getInstance().releaseEditor(myTemplateEditor); |
| myTemplateEditor = null; |
| } |
| myFile = null; |
| } |
| |
| private EditorHighlighter createHighlighter() { |
| if (myTemplate != null && myVelocityFileType != FileTypes.UNKNOWN) { |
| return EditorHighlighterFactory.getInstance().createEditorHighlighter(myProject, new LightVirtualFile("aaa." + myTemplate.getExtension() + ".ft")); |
| } |
| |
| FileType fileType = null; |
| if (myTemplate != null) { |
| fileType = FileTypeManager.getInstance().getFileTypeByExtension(myTemplate.getExtension()); |
| } |
| if (fileType == null) { |
| fileType = FileTypes.PLAIN_TEXT; |
| } |
| |
| SyntaxHighlighter originalHighlighter = SyntaxHighlighterFactory.getSyntaxHighlighter(fileType, null, null); |
| if (originalHighlighter == null) { |
| originalHighlighter = new PlainSyntaxHighlighter(); |
| } |
| |
| final EditorColorsScheme scheme = EditorColorsManager.getInstance().getGlobalScheme(); |
| LayeredLexerEditorHighlighter highlighter = new LayeredLexerEditorHighlighter(new TemplateHighlighter(), scheme); |
| highlighter.registerLayer(FileTemplateTokenType.TEXT, new LayerDescriptor(originalHighlighter, "")); |
| return highlighter; |
| } |
| |
| private static class TemplateHighlighter extends SyntaxHighlighterBase { |
| private final Lexer myLexer; |
| |
| public TemplateHighlighter() { |
| myLexer = new MergingLexerAdapter(new FlexAdapter(new FileTemplateTextLexer()), TokenSet.create(FileTemplateTokenType.TEXT)); |
| } |
| |
| @NotNull |
| @Override |
| public Lexer getHighlightingLexer() { |
| return myLexer; |
| } |
| |
| @Override |
| @NotNull |
| public TextAttributesKey[] getTokenHighlights(IElementType tokenType) { |
| if (tokenType == FileTemplateTokenType.MACRO || tokenType == FileTemplateTokenType.DIRECTIVE) { |
| return pack(TemplateColors.TEMPLATE_VARIABLE_ATTRIBUTES); |
| } |
| |
| return EMPTY; |
| } |
| } |
| |
| |
| public void focusToNameField() { |
| myNameField.selectAll(); |
| myNameField.requestFocus(); |
| } |
| |
| public void focusToExtensionField() { |
| myExtensionField.selectAll(); |
| myExtensionField.requestFocus(); |
| } |
| } |