/*
* Copyright 2006 Sascha Weinreuter
* Copyright (c) 2015-2015 Vladimir Schneider <vladimir.schneider@gmail.com>
*
* 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 fileType governing permissions and
* limitations under the License.
*
* This class is based on LanguageTextField
*
*/
package com.vladsch.idea.multimarkdown.settings;
import com.intellij.ide.highlighter.HighlighterFactory;
import com.intellij.openapi.application.Application;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.ex.EditorEx;
import com.intellij.openapi.fileTypes.FileType;
import com.intellij.openapi.fileTypes.StdFileTypes;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.project.ProjectManager;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiFileFactory;
import com.intellij.ui.EditorTextField;
import com.intellij.util.LocalTimeCounter;
import com.intellij.util.xml.ui.BooleanColumnInfo;
import org.jdom.Element;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.awt.event.FocusEvent;
import java.util.EventListener;
public class CustomizableEditorTextField extends EditorTextField implements ComponentState {
private final static String VERTICALSCROLLPOSITION = "verticalScrollPosition";
private final static String SELECTIONSTARTOFFSET = "selectionStartOffset";
private final static String SELECTIONENDOFFSET = "selectionEndOffset";
private final static String CARETOFFSET = "caretOffset";
private final FileType myFileType;
//private final List<FocusListener> myFocusListeners = ContainerUtil.createLockFreeCopyOnWriteList();
// Could be null to allow usage in UI designer, as EditorTextField permits
private @NotNull Project myProject;
protected final SettingHandlers<EditorEx> handlers = new SettingHandlers<EditorEx>(null);
SettingHandlers.GroupHandler verticalScrollPosition = handlers.newHandler(new SettingHandler<EditorEx, Integer>() {
@Override public void setModelValue(@NotNull EditorEx model, Integer value) {
assert isSettingValid(model, value);
model.getScrollingModel().scrollVertically(value - model.getScrollingModel().getVerticalScrollOffset());
}
@Override public boolean isSettingValid(@NotNull EditorEx model, Integer value) {
return value >= 0;
}
@Override public Integer getModelValue(@NotNull EditorEx model) {
return model.getScrollingModel().getVerticalScrollOffset();
}
}, handlers.IntegerSetting(-1, VERTICALSCROLLPOSITION));
SettingHandlers.GroupHandler caretOffset = handlers.newHandler(new SettingHandler<EditorEx, Integer>() {
@Override public void setModelValue(@NotNull EditorEx model, Integer value) {
assert isSettingValid(model, value);
model.getCaretModel().getPrimaryCaret().moveToOffset(value);
}
@Override public boolean isSettingValid(@NotNull EditorEx model, Integer value) {
return value <= model.getDocument().getTextLength();
}
@Override public Integer getModelValue(@NotNull EditorEx model) {
return model.getCaretModel().getPrimaryCaret().getOffset();
}
}, handlers.IntegerSetting(0, CARETOFFSET));
SettingHandlers.GroupHandler selectionOffset = handlers.newGroupHandler(new SettingGroupHandler<EditorEx>() {
@Override public void loadModelValue(@NotNull EditorEx model, Settings.Setting[] settings) {
assert isSettingValid(model, settings);
model.getCaretModel().getPrimaryCaret().setSelection(((Settings.IntegerSetting) settings[0]).getValue(), ((Settings.IntegerSetting) settings[1]).getValue());
}
@Override public void saveModelValue(@NotNull EditorEx model, Settings.Setting[] settings) {
((Settings.IntegerSetting) settings[0]).setValue(model.getCaretModel().getPrimaryCaret().getSelectionStart());
((Settings.IntegerSetting) settings[1]).setValue(model.getCaretModel().getPrimaryCaret().getSelectionEnd());
}
@Override public boolean isSettingValid(@NotNull EditorEx model, Settings.Setting[] settings) {
int textLength = model.getDocument().getTextLength();
int startOffset = ((Settings.IntegerSetting) settings[0]).getValue();
int endOffset = ((Settings.IntegerSetting) settings[1]).getValue();
return startOffset >= 0 && endOffset >= 0 && startOffset < textLength && endOffset <= textLength;
}
// this returns individual values of the settings in string form
@Override public @Nullable String getModelValue(@NotNull EditorEx model, int index) {
switch (index) {
case 0:
return String.valueOf(model.getCaretModel().getPrimaryCaret().getSelectionStart());
case 1:
return String.valueOf(model.getCaretModel().getPrimaryCaret().getSelectionEnd());
default:
return null;
}
}
}, handlers.IntegerSetting(-1, SELECTIONSTARTOFFSET), handlers.IntegerSetting(-1, SELECTIONENDOFFSET));
public interface EditorCustomizationListener extends EventListener {
// return false if highlighter initialization handled for editor
boolean editorCreated(@NotNull final EditorEx editor, @NotNull final Project project);
}
EditorCustomizationListener listener = null;
public CustomizableEditorTextField() {
this(null, null, "");
}
public CustomizableEditorTextField(FileType fileType, @Nullable Project project, @NotNull String value) {
this(fileType, project, value, true);
}
public CustomizableEditorTextField(FileType fileType, Project project, @NotNull String value, boolean oneLineMode) {
this(fileType, project, value, new SimpleDocumentCreator(), oneLineMode);
}
public CustomizableEditorTextField(@Nullable FileType fileType,
@Nullable Project project,
@NotNull String value,
@NotNull DocumentCreator documentCreator) {
this(fileType, project, value, documentCreator, true);
}
public CustomizableEditorTextField(@Nullable FileType fileType,
@Nullable Project project,
@NotNull String value,
@NotNull DocumentCreator documentCreator,
boolean oneLineMode) {
super(documentCreator.createDocument(value, (fileType == null ? StdFileTypes.PLAIN_TEXT : fileType), project), project, (fileType == null ? StdFileTypes.PLAIN_TEXT : fileType), fileType == null, oneLineMode);
myFileType = (fileType == null ? StdFileTypes.PLAIN_TEXT : fileType);
myProject = getAnyProject(project, false);
setEnabled(fileType != null);
}
public static Project getAnyProject(Project project, boolean neverNull) {
if (project == null) {
ProjectManager projectManager = ProjectManager.getInstance();
Project[] projects = projectManager.getOpenProjects();
project = projects.length > 0 ? projects[0] : (neverNull ? projectManager.getDefaultProject() : null);
}
return project;
}
public interface DocumentCreator {
Document createDocument(String value, @NotNull FileType fileType, Project project);
}
public static class SimpleDocumentCreator implements DocumentCreator {
@Override
public Document createDocument(String value, @NotNull FileType fileType, Project project) {
return CustomizableEditorTextField.createDocument(value, fileType, project, this);
}
public void customizePsiFile(PsiFile file) {
}
}
public static Document createDocument(String value, @NotNull FileType fileType, Project project,
@NotNull SimpleDocumentCreator documentCreator) {
project = getAnyProject(project, true);
final PsiFileFactory factory = PsiFileFactory.getInstance(project);
final long stamp = LocalTimeCounter.currentTime();
final PsiFile psiFile = factory.createFileFromText("Dummy." + fileType.getDefaultExtension(), fileType, value, stamp, true, true);
documentCreator.customizePsiFile(psiFile);
final Document document = PsiDocumentManager.getInstance(project).getDocument(psiFile);
assert document != null;
return document;
}
public void registerListener(EditorCustomizationListener listener) {
this.listener = listener;
}
@Override
public void removeNotify() {
listener = null;
myProject = null;
super.removeNotify();
}
@Override
public void addNotify() {
super.addNotify();
}
@Override
protected EditorEx createEditor() {
final EditorEx ex = super.createEditor();
Project project = getAnyProject(myProject, true);
if (listener == null || listener.editorCreated(ex, project)) {
ex.setHighlighter(HighlighterFactory.createHighlighter(project, myFileType));
}
ex.setEmbeddedIntoDialogWrapper(true);
handlers.loadState(ex);
return ex;
}
public boolean isPendingTextUpdate() {
return pendingTextUpdate != 0;
}
protected int pendingTextUpdate = 0;
@Override
public void setText(@Nullable final String text) {
final Application application = ApplicationManager.getApplication();
if (application.isDispatchThread()) {
setRawText(text);
} else {
pendingTextUpdate++;
application.invokeLater(new Runnable() {
@Override
public void run() {
setRawText(text);
pendingTextUpdate--;
}
}, application.getCurrentModalityState());
}
}
private void setRawText(@Nullable final String text) {
selectionOffset.getSetting(0).setValue(0);
selectionOffset.getSetting(1).setValue(0);
caretOffset.getSetting(0).setValue(0);
handlers.loadState((EditorEx) getEditor());
super.setText(text);
}
// all the settings are either roaming or non-roaming, there is no splitting
@Override public Element getState(String elementName) {
return handlers.getState((EditorEx) getEditor(), elementName, this, null);
}
public void loadState(@NotNull final Element element) {
final Application application = ApplicationManager.getApplication();
if (application.isDispatchThread()) {
handlers.loadState((EditorEx) getEditor(), element);
} else {
application.invokeLater(new Runnable() {
@Override
public void run() {
handlers.loadState((EditorEx) getEditor(), element);
}
}, application.getCurrentModalityState());
}
}
public boolean isChanged(@NotNull Element element) {
return handlers.isChanged(element, this);
}
@Override public boolean haveSavedState() {
//noinspection unchecked
return getEditor() != null && selectionOffset.isSettingValid((EditorEx) getEditor());
}
public boolean haveSavedState(@NotNull EditorEx ex) {
//noinspection unchecked
return selectionOffset.isSettingValid(ex);
}
@Override
public @Nullable Object getComponent(@NotNull String persistName) {
return handlers.getComponentValue((EditorEx) getEditor(), persistName);
}
@Override
public void focusLost(FocusEvent e) {
super.focusLost(e);
}
}