/* * 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. */ /** * @author Alexey */ package com.intellij.lang.properties.editor; import com.intellij.codeHighlighting.BackgroundEditorHighlighter; import com.intellij.ide.FileEditorProvider; import com.intellij.ide.SelectInContext; import com.intellij.ide.structureView.StructureViewBuilder; import com.intellij.ide.structureView.newStructureView.StructureViewComponent; import com.intellij.ide.util.treeView.AbstractTreeNode; import com.intellij.ide.util.treeView.AbstractTreeUi; import com.intellij.ide.util.treeView.smartTree.TreeElement; import com.intellij.lang.properties.IProperty; import com.intellij.lang.properties.PropertiesImplUtil; import com.intellij.lang.properties.ResourceBundle; import com.intellij.lang.properties.psi.PropertiesFile; import com.intellij.lang.properties.psi.PropertiesResourceBundleUtil; import com.intellij.openapi.actionSystem.DataProvider; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.command.CommandProcessor; import com.intellij.openapi.command.WriteCommandAction; import com.intellij.openapi.command.undo.UndoConstants; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.*; import com.intellij.openapi.editor.colors.EditorColorsManager; import com.intellij.openapi.editor.colors.EditorColorsScheme; import com.intellij.openapi.editor.ex.EditorEx; import com.intellij.openapi.editor.ex.FocusChangeListener; import com.intellij.openapi.editor.ex.util.LexerEditorHighlighter; import com.intellij.openapi.fileEditor.*; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.UserDataHolderBase; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.*; import com.intellij.psi.*; import com.intellij.ui.IdeBorderFactory; import com.intellij.ui.JBColor; import com.intellij.ui.JBSplitter; import com.intellij.ui.components.JBScrollPane; import com.intellij.util.Alarm; import com.intellij.util.Function; import com.intellij.util.IncorrectOperationException; import com.intellij.util.NullableFunction; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.ContainerUtilRt; import com.intellij.util.containers.Stack; import com.intellij.util.ui.UIUtil; import gnu.trove.THashMap; import gnu.trove.THashSet; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; import javax.swing.border.TitledBorder; import javax.swing.event.TreeSelectionEvent; import javax.swing.event.TreeSelectionListener; import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.TreePath; import java.awt.*; import java.beans.PropertyChangeListener; import java.util.*; import java.util.List; public class ResourceBundleEditor extends UserDataHolderBase implements FileEditor { private static final Logger LOG = Logger.getInstance("#com.intellij.lang.properties.editor.ResourceBundleEditor"); @NonNls private static final String VALUES = "values"; @NonNls private static final String NO_PROPERTY_SELECTED = "noPropertySelected"; private final StructureViewComponent myStructureViewComponent; private final Map myEditors; private final ResourceBundle myResourceBundle; private final Map myTitledPanels; private final JComponent myNoPropertySelectedPanel = new NoPropertySelectedPanel().getComponent(); private final Project myProject; private final DataProviderPanel myDataProviderPanel; // user pressed backslash in the corresponding editor. // we cannot store it back to properties file right now, so just append the backslash to the editor and wait for the subsequent chars private final Set myBackSlashPressed = new THashSet(); private final Alarm mySelectionChangeAlarm = new Alarm(Alarm.ThreadToUse.SWING_THREAD); private JPanel myValuesPanel; private JPanel myStructureViewPanel; private boolean myDisposed; private VirtualFileListener myVfsListener; private Editor mySelectedEditor; public ResourceBundleEditor(@NotNull ResourceBundle resourceBundle) { myProject = resourceBundle.getProject(); final JPanel splitPanel = new JPanel(); myValuesPanel = new JPanel(); myStructureViewPanel = new JPanel(); JBSplitter splitter = new JBSplitter(false); splitter.setFirstComponent(myStructureViewPanel); splitter.setSecondComponent(myValuesPanel); splitter.setShowDividerControls(true); splitter.setHonorComponentsMinimumSize(true); splitter.setAndLoadSplitterProportionKey(getClass() + ".splitter"); splitPanel.setLayout(new BorderLayout()); splitPanel.add(splitter, BorderLayout.CENTER); myResourceBundle = resourceBundle; myStructureViewComponent = new ResourceBundleStructureViewComponent(myResourceBundle, this); myStructureViewPanel.setLayout(new BorderLayout()); myStructureViewPanel.add(myStructureViewComponent, BorderLayout.CENTER); myStructureViewComponent.getTree().getSelectionModel().addTreeSelectionListener(new TreeSelectionListener() { private IProperty selectedProperty; private PropertiesFile selectedPropertiesFile; @Override public void valueChanged(TreeSelectionEvent e) { // filter out temp unselect/select events if (Comparing.equal(e.getNewLeadSelectionPath(), e.getOldLeadSelectionPath()) || getSelectedProperty() == null) return; if (!arePropertiesEquivalent(selectedProperty, getSelectedProperty()) || !Comparing.equal(selectedPropertiesFile, getSelectedPropertiesFile())) { if (e.getOldLeadSelectionPath() != null) { for (Map.Entry entry : myEditors.entrySet()) { if (entry.getValue() == mySelectedEditor) { writeEditorPropertyValue(mySelectedEditor, entry.getKey(), selectedProperty.getName()); break; } } } selectedProperty = getSelectedProperty(); selectedPropertiesFile = getSelectedPropertiesFile(); selectionChanged(); } } private boolean arePropertiesEquivalent(@Nullable IProperty p1, @Nullable IProperty p2) { if (p1 == p2) { return true; } if (p1 == null || p2 == null) { return false; } return p1.getPsiElement().isEquivalentTo(p2.getPsiElement()); } }); installPropertiesChangeListeners(); myEditors = new THashMap(); myTitledPanels = new THashMap(); recreateEditorsPanel(); TreeElement[] children = myStructureViewComponent.getTreeModel().getRoot().getChildren(); if (children.length != 0) { TreeElement child = children[0]; String propName = ((ResourceBundlePropertyStructureViewElement)child).getValue().getUnescapedKey(); setState(new ResourceBundleEditorState(propName)); } myDataProviderPanel = new DataProviderPanel(splitPanel); myProject.getMessageBus().connect(myProject).subscribe(FileEditorManagerListener.FILE_EDITOR_MANAGER, new FileEditorManagerAdapter() { @Override public void selectionChanged(@NotNull FileEditorManagerEvent event) { onSelectionChanged(event); } }); } public ResourceBundle getResourceBundle() { return myResourceBundle; } private void onSelectionChanged(@NotNull FileEditorManagerEvent event) { // Ignore events which don't target current editor. FileEditor oldEditor = event.getOldEditor(); FileEditor newEditor = event.getNewEditor(); if (oldEditor != this && newEditor != this) { return; } // We want to sync selected property key on selection change. if (newEditor == this) { if (oldEditor instanceof TextEditor) { setStructureViewSelectionFromPropertiesFile(((TextEditor)oldEditor).getEditor()); } } else if (newEditor instanceof TextEditor) { setPropertiesFileSelectionFromStructureView(((TextEditor)newEditor).getEditor()); } } private void setStructureViewSelectionFromPropertiesFile(@NotNull Editor propertiesFileEditor) { int line = propertiesFileEditor.getCaretModel().getLogicalPosition().line; Document document = propertiesFileEditor.getDocument(); if (line >= document.getLineCount()) { return; } final String propertyName = getPropertyName(document, line); if (propertyName == null) { return; } setStructureViewSelection(propertyName); } private void setStructureViewSelection(@NotNull final String propertyName) { if (myStructureViewComponent.isDisposed()) { return; } JTree tree = myStructureViewComponent.getTree(); if (tree == null) { return; } Object root = tree.getModel().getRoot(); if (AbstractTreeUi.isLoadingChildrenFor(root)) { mySelectionChangeAlarm.cancelAllRequests(); mySelectionChangeAlarm.addRequest(new Runnable() { @Override public void run() { mySelectionChangeAlarm.cancelAllRequests(); setStructureViewSelection(propertyName); } }, 500); return; } Stack toCheck = ContainerUtilRt.newStack(); toCheck.push((DefaultMutableTreeNode)root); DefaultMutableTreeNode nodeToSelect = null; while (!toCheck.isEmpty()) { DefaultMutableTreeNode node = toCheck.pop(); final ResourceBundleEditorViewElement element = getSelectedElement(node); String value = element instanceof ResourceBundlePropertyStructureViewElement ? ((ResourceBundlePropertyStructureViewElement)element).getValue().getUnescapedKey() : null; if (propertyName.equals(value)) { nodeToSelect = node; break; } else { for (int i = 0; i < node.getChildCount(); i++) { toCheck.push((DefaultMutableTreeNode)node.getChildAt(i)); } } } if (nodeToSelect != null) { TreePath path = new TreePath(nodeToSelect.getPath()); tree.setSelectionPath(path); tree.scrollPathToVisible(path); } } @Nullable private static String getPropertyName(@NotNull Document document, int line) { int startOffset = document.getLineStartOffset(line); int endOffset = StringUtil.indexOf(document.getCharsSequence(), '=', startOffset, document.getLineEndOffset(line)); if (endOffset <= startOffset) { return null; } String propertyName = document.getCharsSequence().subSequence(startOffset, endOffset).toString().trim(); return propertyName.isEmpty() ? null : propertyName; } private void setPropertiesFileSelectionFromStructureView(@NotNull Editor propertiesFileEditor) { String selectedPropertyName = getSelectedPropertyName(); if (selectedPropertyName == null) { return; } Document document = propertiesFileEditor.getDocument(); for (int i = 0; i < document.getLineCount(); i++) { String propertyName = getPropertyName(document, i); if (selectedPropertyName.equals(propertyName)) { propertiesFileEditor.getCaretModel().moveToLogicalPosition(new LogicalPosition(i, 0)); return; } } } @Nullable private static ResourceBundleEditorViewElement getSelectedElement(@NotNull DefaultMutableTreeNode node) { Object userObject = node.getUserObject(); if (!(userObject instanceof AbstractTreeNode)) return null; Object value = ((AbstractTreeNode)userObject).getValue(); return value instanceof ResourceBundleEditorViewElement ? (ResourceBundleEditorViewElement) value : null; } private void writeEditorPropertyValue(final Editor editor, final PropertiesFile propertiesFile, final @Nullable String propertyName) { final String currentValue = editor.getDocument().getText(); final String currentSelectedProperty = propertyName == null ? getSelectedPropertyName() : propertyName; if (currentSelectedProperty == null) { return; } ApplicationManager.getApplication().runWriteAction(new Runnable() { @Override public void run() { WriteCommandAction.runWriteCommandAction(myProject, new Runnable() { @Override public void run() { final IProperty property = propertiesFile.findPropertyByKey(currentSelectedProperty); try { if (property == null) { propertiesFile.addProperty(currentSelectedProperty, currentValue); } else { property.setValue(currentValue); } } catch (final IncorrectOperationException e) { LOG.error(e); } } }); } }); } private void recreateEditorsPanel() { myValuesPanel.removeAll(); myValuesPanel.setLayout(new CardLayout()); if (!myProject.isOpen()) return; JPanel valuesPanelComponent = new MyJPanel(new GridBagLayout()); myValuesPanel.add(new JBScrollPane(valuesPanelComponent){ @Override public void updateUI() { super.updateUI(); getViewport().setBackground(UIUtil.getPanelBackground()); } }, VALUES); myValuesPanel.add(myNoPropertySelectedPanel, NO_PROPERTY_SELECTED); List propertiesFiles = myResourceBundle.getPropertiesFiles(); GridBagConstraints gc = new GridBagConstraints(0, 0, 0, 0, 0, 0, GridBagConstraints.NORTHWEST, GridBagConstraints.BOTH, new Insets(5, 5, 5, 5), 0, 0); releaseAllEditors(); myTitledPanels.clear(); int y = 0; Editor previousEditor = null; Editor firstEditor = null; for (final PropertiesFile propertiesFile : propertiesFiles) { final Editor editor = createEditor(); final Editor oldEditor = myEditors.put(propertiesFile, editor); if (firstEditor == null) { firstEditor = editor; } if (previousEditor != null) { editor.putUserData(ChooseSubsequentPropertyValueEditorAction.PREV_EDITOR_KEY, previousEditor); previousEditor.putUserData(ChooseSubsequentPropertyValueEditorAction.NEXT_EDITOR_KEY, editor); } previousEditor = editor; if (oldEditor != null) { EditorFactory.getInstance().releaseEditor(oldEditor); } ((EditorEx) editor).addFocusListener(new FocusChangeListener() { @Override public void focusGained(final Editor editor) { mySelectedEditor = editor; } @Override public void focusLost(final Editor eventEditor) { writeEditorPropertyValue(editor, propertiesFile, null); } }); editor.getDocument().putUserData(UndoConstants.DONT_RECORD_UNDO, Boolean.TRUE); gc.gridx = 0; gc.gridy = y++; gc.gridheight = 1; gc.gridwidth = GridBagConstraints.REMAINDER; gc.weightx = 1; gc.weighty = 1; gc.anchor = GridBagConstraints.CENTER; Locale locale = propertiesFile.getLocale(); List names = new ArrayList(); if (!Comparing.strEqual(locale.getDisplayLanguage(), null)) { names.add(locale.getDisplayLanguage()); } if (!Comparing.strEqual(locale.getDisplayCountry(), null)) { names.add(locale.getDisplayCountry()); } if (!Comparing.strEqual(locale.getDisplayVariant(), null)) { names.add(locale.getDisplayVariant()); } String title = propertiesFile.getName(); if (!names.isEmpty()) { title += " ("+StringUtil.join(names, "/")+")"; } JComponent comp = new JPanel(new BorderLayout()) { @Override public Dimension getPreferredSize() { Insets insets = getBorder().getBorderInsets(this); return new Dimension(100,editor.getLineHeight()*4+ insets.top + insets.bottom); } }; comp.add(editor.getComponent(), BorderLayout.CENTER); comp.setBorder(IdeBorderFactory.createTitledBorder(title, true)); myTitledPanels.put(propertiesFile, (JPanel)comp); valuesPanelComponent.add(comp, gc); } if (previousEditor != null) { previousEditor.putUserData(ChooseSubsequentPropertyValueEditorAction.NEXT_EDITOR_KEY, firstEditor); firstEditor.putUserData(ChooseSubsequentPropertyValueEditorAction.PREV_EDITOR_KEY, previousEditor); } gc.gridx = 0; gc.gridy = y; gc.gridheight = GridBagConstraints.REMAINDER; gc.gridwidth = GridBagConstraints.REMAINDER; gc.weightx = 10; gc.weighty = 1; valuesPanelComponent.add(new JPanel(), gc); selectionChanged(); myValuesPanel.repaint(); UIUtil.invokeAndWaitIfNeeded(new Runnable() { @Override public void run() { updateEditorsFromProperties(); } }); } @NotNull public static String getPropertyEditorValue(@Nullable final IProperty property) { if (property == null) { return ""; } else { String rawValue = property.getValue(); return rawValue == null ? "" : PropertiesResourceBundleUtil.fromPropertyValueToValueEditor(rawValue); } } private void updateEditorsFromProperties() { String propertyName = getSelectedPropertyName(); ((CardLayout)myValuesPanel.getLayout()).show(myValuesPanel, propertyName == null ? NO_PROPERTY_SELECTED : VALUES); if (propertyName == null) return; for (final PropertiesFile propertiesFile : myResourceBundle.getPropertiesFiles(myProject)) { final EditorEx editor = (EditorEx)myEditors.get(propertiesFile); if (editor == null) continue; final IProperty property = propertiesFile.findPropertyByKey(propertyName); final Document document = editor.getDocument(); CommandProcessor.getInstance().executeCommand(null, new Runnable() { @Override public void run() { ApplicationManager.getApplication().runWriteAction(new Runnable() { @Override public void run() { updateDocumentFromPropertyValue(getPropertyEditorValue(property), document, propertiesFile); } }); } }, "", this); JPanel titledPanel = myTitledPanels.get(propertiesFile); ((TitledBorder)titledPanel.getBorder()).setTitleColor(property == null ? JBColor.RED : UIUtil.getLabelTextForeground()); titledPanel.repaint(); } } private void installPropertiesChangeListeners() { final VirtualFileManager virtualFileManager = VirtualFileManager.getInstance(); if (myVfsListener != null) { assert false; virtualFileManager.removeVirtualFileListener(myVfsListener); } myVfsListener = new VirtualFileAdapter() { @Override public void fileCreated(@NotNull VirtualFileEvent event) { if (PropertiesImplUtil.isPropertiesFile(event.getFile(), myProject)) { recreateEditorsPanel(); } } @Override public void fileDeleted(@NotNull VirtualFileEvent event) { for (PropertiesFile file : myEditors.keySet()) { if (Comparing.equal(file.getVirtualFile(), event.getFile())) { recreateEditorsPanel(); return; } } } @Override public void propertyChanged(@NotNull VirtualFilePropertyEvent event) { if (PropertiesImplUtil.isPropertiesFile(event.getFile(), myProject)) { if (VirtualFile.PROP_NAME.equals(event.getPropertyName())) { recreateEditorsPanel(); } else { updateEditorsFromProperties(); } } } }; virtualFileManager.addVirtualFileListener(myVfsListener, this); PsiTreeChangeAdapter psiTreeChangeAdapter = new PsiTreeChangeAdapter() { @Override public void childAdded(@NotNull PsiTreeChangeEvent event) { childrenChanged(event); } @Override public void childRemoved(@NotNull PsiTreeChangeEvent event) { childrenChanged(event); } @Override public void childReplaced(@NotNull PsiTreeChangeEvent event) { childrenChanged(event); } @Override public void childMoved(@NotNull PsiTreeChangeEvent event) { childrenChanged(event); } @Override public void childrenChanged(@NotNull PsiTreeChangeEvent event) { final PsiFile file = event.getFile(); PropertiesFile propertiesFile = PropertiesImplUtil.getPropertiesFile(file); if (propertiesFile == null) return; if (!propertiesFile.getResourceBundle().equals(myResourceBundle)) return; updateEditorsFromProperties(); } }; PsiManager.getInstance(myProject).addPsiTreeChangeListener(psiTreeChangeAdapter, this); } private void selectionChanged() { myBackSlashPressed.clear(); UIUtil.invokeLaterIfNeeded(new Runnable() { @Override public void run() { updateEditorsFromProperties(); } }); } private void updateDocumentFromPropertyValue(final String value, final Document document, final PropertiesFile propertiesFile) { @NonNls String text = value; if (myBackSlashPressed.contains(propertiesFile)) { text += "\\"; } document.replaceString(0, document.getTextLength(), text); } @NotNull private Collection getSelectedNodes() { if (!isValid()) { return Collections.emptyList(); } JTree tree = myStructureViewComponent.getTree(); if (tree == null) return Collections.emptyList(); TreePath[] selected = tree.getSelectionModel().getSelectionPaths(); if (selected == null || selected.length == 0) return Collections.emptyList(); return ContainerUtil.map(selected, new Function() { @Override public DefaultMutableTreeNode fun(TreePath treePath) { return (DefaultMutableTreeNode)treePath.getLastPathComponent(); } }); } @Nullable private String getSelectedPropertyName() { final IProperty selectedProperty = getSelectedProperty(); return selectedProperty == null ? null : selectedProperty.getName(); } @Nullable private IProperty getSelectedProperty() { final Collection selectedNode = getSelectedNodes(); if (selectedNode.isEmpty()) { return null; } final ResourceBundleEditorViewElement element = getSelectedElement(ContainerUtil.getFirstItem(selectedNode)); return element instanceof ResourceBundlePropertyStructureViewElement ? ((ResourceBundlePropertyStructureViewElement)element).getProperty() : null; } @NotNull public Collection getSelectedElements() { final Collection selectedNodes = getSelectedNodes(); return ContainerUtil.mapNotNull(selectedNodes, new NullableFunction() { @Nullable @Override public ResourceBundleEditorViewElement fun(DefaultMutableTreeNode selectedNode) { Object userObject = selectedNode.getUserObject(); if (!(userObject instanceof AbstractTreeNode)) return null; Object value = ((AbstractTreeNode)userObject).getValue(); return value instanceof ResourceBundleEditorViewElement ? (ResourceBundleEditorViewElement) value : null; } }); } @Nullable public ResourceBundleEditorViewElement getSelectedElementIfOnlyOne() { final Collection selectedElements = getSelectedElements(); return selectedElements.size() == 1 ? ContainerUtil.getFirstItem(selectedElements) : null; } @Override @NotNull public JComponent getComponent() { return myDataProviderPanel; } private Object getData(final String dataId) { if (SelectInContext.DATA_KEY.is(dataId)) { return new SelectInContext(){ @Override @NotNull public Project getProject() { return myProject; } @Override @NotNull public VirtualFile getVirtualFile() { PropertiesFile selectedFile = getSelectedPropertiesFile(); VirtualFile virtualFile = selectedFile == null ? null : selectedFile.getVirtualFile(); assert virtualFile != null; return virtualFile; } @Override public Object getSelectorInFile() { return getSelectedPropertiesFile(); } @Override public FileEditorProvider getFileEditorProvider() { final PropertiesFile selectedPropertiesFile = getSelectedPropertiesFile(); if (selectedPropertiesFile == null) return null; return new FileEditorProvider() { @Override public FileEditor openFileEditor() { final VirtualFile file = selectedPropertiesFile.getVirtualFile(); if (file == null) { return null; } return FileEditorManager.getInstance(getProject()).openFile(file, false)[0]; } }; } }; } return null; } private PropertiesFile getSelectedPropertiesFile() { if (mySelectedEditor == null) return null; PropertiesFile selectedFile = null; for (Map.Entry entry : myEditors.entrySet()) { Editor editor = entry.getValue(); if (editor == mySelectedEditor) { selectedFile = entry.getKey(); break; } } return selectedFile; } @Override public JComponent getPreferredFocusedComponent() { return myStructureViewPanel; } @Override @NotNull public String getName() { return "Resource Bundle"; } @Override @NotNull public ResourceBundleEditorState getState(@NotNull FileEditorStateLevel level) { return new ResourceBundleEditorState(getSelectedPropertyName()); } @Override public void setState(@NotNull FileEditorState state) { ResourceBundleEditorState myState = (ResourceBundleEditorState)state; String propertyName = myState.myPropertyName; if (propertyName != null) { myStructureViewComponent.select(propertyName, true); selectionChanged(); } } @Override public boolean isModified() { return false; } @Override public boolean isValid() { return !myDisposed && !myProject.isDisposed(); } @Override public void selectNotify() { } @Override public void deselectNotify() { } @Override public void addPropertyChangeListener(@NotNull PropertyChangeListener listener) { } @Override public void removePropertyChangeListener(@NotNull PropertyChangeListener listener) { } @Override public BackgroundEditorHighlighter getBackgroundHighlighter() { return null; } @Override public FileEditorLocation getCurrentLocation() { return null; } @Override public StructureViewBuilder getStructureViewBuilder() { return null; } @Override public void dispose() { if (mySelectedEditor != null) { for (final Map.Entry entry : myEditors.entrySet()) { if (mySelectedEditor.equals(entry.getValue())) { writeEditorPropertyValue(mySelectedEditor, entry.getKey(), null); } } } VirtualFileManager.getInstance().removeVirtualFileListener(myVfsListener); myDisposed = true; Disposer.dispose(myStructureViewComponent); releaseAllEditors(); } private void releaseAllEditors() { for (final Editor editor : myEditors.values()) { if (!editor.isDisposed()) { EditorFactory.getInstance().releaseEditor(editor); } } myEditors.clear(); } public static class ResourceBundleEditorState implements FileEditorState { private final String myPropertyName; public ResourceBundleEditorState(String propertyName) { myPropertyName = propertyName; } @Override public boolean canBeMergedWith(FileEditorState otherState, FileEditorStateLevel level) { return false; } public String getPropertyName() { return myPropertyName; } } private static Editor createEditor() { EditorFactory editorFactory = EditorFactory.getInstance(); Document document = editorFactory.createDocument(""); EditorEx editor = (EditorEx)editorFactory.createEditor(document); reinitSettings(editor); return editor; } private static void reinitSettings(final EditorEx editor) { EditorColorsScheme scheme = EditorColorsManager.getInstance().getGlobalScheme(); editor.setColorsScheme(scheme); EditorSettings settings = editor.getSettings(); settings.setLineNumbersShown(false); settings.setWhitespacesShown(false); settings.setLineMarkerAreaShown(false); settings.setIndentGuidesShown(false); settings.setFoldingOutlineShown(false); settings.setAdditionalColumnsCount(0); settings.setAdditionalLinesCount(0); settings.setRightMarginShown(true); settings.setRightMargin(60); settings.setVirtualSpace(false); editor.setHighlighter(new LexerEditorHighlighter(new PropertiesValueHighlighter(), scheme)); editor.setVerticalScrollbarVisible(true); } private class DataProviderPanel extends JPanel implements DataProvider { private DataProviderPanel(final JPanel panel) { super(new BorderLayout()); add(panel, BorderLayout.CENTER); } @Override @Nullable public Object getData(String dataId) { return ResourceBundleEditor.this.getData(dataId); } } private class MyJPanel extends JPanel implements Scrollable{ private MyJPanel(LayoutManager layout) { super(layout); } @Override public Dimension getPreferredScrollableViewportSize() { return getPreferredSize(); } @Override public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction) { Editor editor = myEditors.values().iterator().next(); return editor.getLineHeight()*4; } @Override public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction) { return visibleRect.height; } @Override public boolean getScrollableTracksViewportWidth() { return true; } @Override public boolean getScrollableTracksViewportHeight() { return false; } } }