blob: da44ae6c123c787dc702da3e84fa15bb21e2bf22 [file] [log] [blame]
/*
* 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.injected.editor;
import com.intellij.openapi.editor.*;
import com.intellij.openapi.editor.event.CaretAdapter;
import com.intellij.openapi.editor.event.CaretEvent;
import com.intellij.openapi.editor.event.CaretListener;
import com.intellij.openapi.editor.ex.EditorEx;
import com.intellij.openapi.editor.markup.TextAttributes;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.WeakHashMap;
/**
* @author Alexey
*/
public class CaretModelWindow implements CaretModel {
private final CaretModel myDelegate;
private final EditorEx myHostEditor;
private final EditorWindow myEditorWindow;
private final Map<Caret, InjectedCaret> myInjectedCaretMap = new WeakHashMap<Caret, InjectedCaret>();
public CaretModelWindow(CaretModel delegate, EditorWindow editorWindow) {
myDelegate = delegate;
myHostEditor = (EditorEx)editorWindow.getDelegate();
myEditorWindow = editorWindow;
}
@Override
public void moveCaretRelatively(final int columnShift,
final int lineShift,
final boolean withSelection,
final boolean blockSelection,
final boolean scrollToCaret) {
myDelegate.moveCaretRelatively(columnShift, lineShift, withSelection, blockSelection, scrollToCaret);
}
@Override
public void moveToLogicalPosition(@NotNull final LogicalPosition pos) {
LogicalPosition hostPos = myEditorWindow.injectedToHost(pos);
myDelegate.moveToLogicalPosition(hostPos);
}
@Override
public void moveToVisualPosition(@NotNull final VisualPosition pos) {
LogicalPosition hostPos = myEditorWindow.injectedToHost(myEditorWindow.visualToLogicalPosition(pos));
myDelegate.moveToLogicalPosition(hostPos);
}
@Override
public void moveToOffset(int offset) {
moveToOffset(offset, false);
}
@Override
public void moveToOffset(final int offset, boolean locateBeforeSoftWrap) {
int hostOffset = myEditorWindow.getDocument().injectedToHost(offset);
myDelegate.moveToOffset(hostOffset, locateBeforeSoftWrap);
}
@Override
@NotNull
public LogicalPosition getLogicalPosition() {
LogicalPosition hostPos = myDelegate.getLogicalPosition();
return myEditorWindow.hostToInjected(hostPos);
}
@Override
@NotNull
public VisualPosition getVisualPosition() {
LogicalPosition logicalPosition = getLogicalPosition();
return myEditorWindow.logicalToVisualPosition(logicalPosition);
}
@Override
public int getOffset() {
return myEditorWindow.getDocument().hostToInjected(myDelegate.getOffset());
}
@Override
public boolean isUpToDate() {
return myDelegate.isUpToDate();
}
private final ListenerWrapperMap<CaretListener> myCaretListeners = new ListenerWrapperMap<CaretListener>();
@Override
public void addCaretListener(@NotNull final CaretListener listener) {
CaretListener wrapper = new CaretAdapter() {
@Override
public void caretPositionChanged(CaretEvent e) {
if (!myEditorWindow.getDocument().isValid()) return; // injected document can be destroyed by now
CaretEvent event = new CaretEvent(myEditorWindow, createInjectedCaret(e.getCaret()),
myEditorWindow.hostToInjected(e.getOldPosition()),
myEditorWindow.hostToInjected(e.getNewPosition()));
listener.caretPositionChanged(event);
}
};
myCaretListeners.registerWrapper(listener, wrapper);
myDelegate.addCaretListener(wrapper);
}
@Override
public void removeCaretListener(@NotNull final CaretListener listener) {
CaretListener wrapper = myCaretListeners.removeWrapper(listener);
if (wrapper != null) {
myDelegate.removeCaretListener(wrapper);
}
}
public void disposeModel() {
for (CaretListener wrapper : myCaretListeners.wrappers()) {
myDelegate.removeCaretListener(wrapper);
}
myCaretListeners.clear();
}
@Override
public int getVisualLineStart() {
return myEditorWindow.getDocument().hostToInjected(myDelegate.getVisualLineStart());
}
@Override
public int getVisualLineEnd() {
return myEditorWindow.getDocument().hostToInjected(myDelegate.getVisualLineEnd());
}
@Override
public TextAttributes getTextAttributes() {
return myDelegate.getTextAttributes();
}
@Override
public boolean supportsMultipleCarets() {
return myDelegate.supportsMultipleCarets();
}
@NotNull
@Override
public Caret getCurrentCaret() {
return createInjectedCaret(myDelegate.getCurrentCaret());
}
@NotNull
@Override
public Caret getPrimaryCaret() {
return createInjectedCaret(myDelegate.getPrimaryCaret());
}
@Override
public int getCaretCount() {
return myDelegate.getCaretCount();
}
@NotNull
@Override
public List<Caret> getAllCarets() {
List<Caret> hostCarets = myDelegate.getAllCarets();
List<Caret> carets = new ArrayList<Caret>(hostCarets.size());
for (Caret hostCaret : hostCarets) {
carets.add(createInjectedCaret(hostCaret));
}
return carets;
}
@Nullable
@Override
public Caret getCaretAt(@NotNull VisualPosition pos) {
LogicalPosition hostPos = myEditorWindow.injectedToHost(myEditorWindow.visualToLogicalPosition(pos));
Caret caret = myDelegate.getCaretAt(myHostEditor.logicalToVisualPosition(hostPos));
return createInjectedCaret(caret);
}
@Nullable
@Override
public Caret addCaret(@NotNull VisualPosition pos) {
LogicalPosition hostPos = myEditorWindow.injectedToHost(myEditorWindow.visualToLogicalPosition(pos));
Caret caret = myDelegate.addCaret(myHostEditor.logicalToVisualPosition(hostPos));
return createInjectedCaret(caret);
}
@Override
public boolean removeCaret(@NotNull Caret caret) {
if (caret instanceof InjectedCaret) {
caret = ((InjectedCaret)caret).myDelegate;
}
return myDelegate.removeCaret(caret);
}
@Override
public void removeSecondaryCarets() {
myDelegate.removeSecondaryCarets();
}
@Override
public void setCaretsAndSelections(@NotNull List<CaretState> caretStates) {
List<CaretState> convertedStates = convertCaretStates(caretStates);
myDelegate.setCaretsAndSelections(convertedStates);
}
@Override
public void setCaretsAndSelections(@NotNull List<CaretState> caretStates, boolean updateSystemSelection) {
List<CaretState> convertedStates = convertCaretStates(caretStates);
myDelegate.setCaretsAndSelections(convertedStates, updateSystemSelection);
}
private List<CaretState> convertCaretStates(List<CaretState> caretStates) {
List<CaretState> convertedStates = new ArrayList<CaretState>(caretStates.size());
for (CaretState state : caretStates) {
convertedStates.add(new CaretState(injectedToHost(state.getCaretPosition()),
injectedToHost(state.getSelectionStart()),
injectedToHost(state.getSelectionEnd())));
}
return convertedStates;
}
private LogicalPosition injectedToHost(@Nullable LogicalPosition position) {
return position == null ? null : myEditorWindow.injectedToHost(position);
}
@NotNull
@Override
public List<CaretState> getCaretsAndSelections() {
List<CaretState> caretsAndSelections = myDelegate.getCaretsAndSelections();
List<CaretState> convertedStates = new ArrayList<CaretState>(caretsAndSelections.size());
for (CaretState state : caretsAndSelections) {
convertedStates.add(new CaretState(hostToInjected(state.getCaretPosition()),
hostToInjected(state.getSelectionStart()),
hostToInjected(state.getSelectionEnd())));
}
return convertedStates;
}
private LogicalPosition hostToInjected(@Nullable LogicalPosition position) {
return position == null ? null : myEditorWindow.hostToInjected(position);
}
private InjectedCaret createInjectedCaret(Caret caret) {
if (caret == null) {
return null;
}
synchronized (myInjectedCaretMap) {
InjectedCaret injectedCaret = myInjectedCaretMap.get(caret);
if (injectedCaret == null) {
injectedCaret = new InjectedCaret(myEditorWindow, caret);
myInjectedCaretMap.put(caret, injectedCaret);
}
return injectedCaret;
}
}
@Override
public void runForEachCaret(final @NotNull CaretAction action) {
myDelegate.runForEachCaret(new CaretAction() {
@Override
public void perform(Caret caret) {
action.perform(createInjectedCaret(caret));
}
});
}
@Override
public void runForEachCaret(@NotNull final CaretAction action, boolean reverseOrder) {
myDelegate.runForEachCaret(new CaretAction() {
@Override
public void perform(Caret caret) {
action.perform(createInjectedCaret(caret));
}
}, reverseOrder);
}
@Override
public void runBatchCaretOperation(@NotNull Runnable runnable) {
myDelegate.runBatchCaretOperation(runnable);
}
}