/*******************************************************************************
* Copyright (c) 2014-2015 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.ide.editor.orion.client;
import com.google.web.bindery.event.shared.HandlerRegistration;
import org.eclipse.che.ide.api.text.Region;
import org.eclipse.che.ide.editor.orion.client.jso.ModelChangedEventOverlay;
import org.eclipse.che.ide.editor.orion.client.jso.OrionEditorOverlay;
import org.eclipse.che.ide.editor.orion.client.jso.OrionPixelPositionOverlay;
import org.eclipse.che.ide.editor.orion.client.jso.OrionSelectionOverlay;
import org.eclipse.che.ide.editor.orion.client.jso.OrionTextModelOverlay.EventHandler;
import org.eclipse.che.ide.editor.orion.client.jso.OrionTextViewOverlay;
import org.eclipse.che.ide.jseditor.client.document.AbstractEmbeddedDocument;
import org.eclipse.che.ide.jseditor.client.document.Document;
import org.eclipse.che.ide.jseditor.client.events.CursorActivityHandler;
import org.eclipse.che.ide.jseditor.client.events.DocumentChangeEvent;
import org.eclipse.che.ide.jseditor.client.events.HasCursorActivityHandlers;
import org.eclipse.che.ide.jseditor.client.position.PositionConverter;
import org.eclipse.che.ide.jseditor.client.text.LinearRange;
import org.eclipse.che.ide.jseditor.client.text.TextPosition;
import org.eclipse.che.ide.jseditor.client.text.TextRange;
/**
* The implementation of {@link Document} for Orion.
*
* @author "Mickaƫl Leduque"
*/
public class OrionDocument extends AbstractEmbeddedDocument {
private final OrionTextViewOverlay textViewOverlay;
private final OrionPositionConverter positionConverter;
private final HasCursorActivityHandlers hasCursorActivityHandlers;
private final OrionEditorOverlay editorOverlay;
public OrionDocument(OrionTextViewOverlay textViewOverlay,
HasCursorActivityHandlers hasCursorActivityHandlers,
OrionEditorOverlay editorOverlay) {
this.textViewOverlay = textViewOverlay;
this.hasCursorActivityHandlers = hasCursorActivityHandlers;
this.editorOverlay = editorOverlay;
this.positionConverter = new OrionPositionConverter();
this.editorOverlay.getModel().addEventListener("Changed", new EventHandler<ModelChangedEventOverlay>() {
@Override
public void onEvent(ModelChangedEventOverlay parameter) {
fireDocumentChangeEvent(parameter);
}
}, true);
}
private void fireDocumentChangeEvent(final ModelChangedEventOverlay param) {
int startOffset = param.start();
int addedCharCount = param.addedCharCount();
int removedCharCount = param.removedCharCount();
int length = 0;
if (addedCharCount != 0) {
//adding
length = addedCharCount;
} else if (removedCharCount != 0) {
//deleting
//TODO there may be bug
length = removedCharCount;
startOffset = startOffset - length;
}
String text = editorOverlay.getModel().getText(startOffset, startOffset + length);
final DocumentChangeEvent event = new DocumentChangeEvent(this,
startOffset,
length,
text,
removedCharCount);//TODO: need check removedCharCount add it for fix
// according to https://github.com/codenvy/che-core/pull/122
getDocEventBus().fireEvent(event);
}
@Override
public TextPosition getPositionFromIndex(final int index) {
final int line = this.editorOverlay.getModel().getLineAtOffset(index);
if (line == -1) {
return null;
}
final int lineStart = this.editorOverlay.getModel().getLineStart(line);
if (lineStart == -1) {
return null;
}
final int character = index - lineStart;
if (character < 0) {
return null;
}
return new TextPosition(line, character);
}
@Override
public int getIndexFromPosition(final TextPosition position) {
final int lineStart = this.editorOverlay.getModel().getLineStart(position.getLine());
if (lineStart == -1) {
return -1;
}
final int result = lineStart + position.getCharacter();
final int lineEnd = this.editorOverlay.getModel().getLineEnd(position.getLine());
if (lineEnd < result) {
return -1;
}
return result;
}
@Override
public void setCursorPosition(final TextPosition position) {
this.editorOverlay.setCaretOffset(getIndexFromPosition(position));
}
@Override
public TextPosition getCursorPosition() {
final int offset = this.editorOverlay.getCaretOffset();
return getPositionFromIndex(offset);
}
public int getCursorOffset() {
return this.editorOverlay.getTextView().getCaretOffset();
}
@Override
public int getLineCount() {
return this.editorOverlay.getModel().getLineCount();
}
@Override
public HandlerRegistration addCursorHandler(final CursorActivityHandler handler) {
return this.hasCursorActivityHandlers.addCursorActivityHandler(handler);
}
@Override
public String getContents() {
return editorOverlay.getText();
}
@Override
public String getContentRange(final int offset, final int length) {
return this.editorOverlay.getModel().getText(offset, offset + length);
}
@Override
public String getContentRange(final TextRange range) {
final int startOffset = getIndexFromPosition(range.getFrom());
final int endOffset = getIndexFromPosition(range.getTo());
return this.editorOverlay.getModel().getText(startOffset, endOffset);
}
public PositionConverter getPositionConverter() {
return this.positionConverter;
}
private class OrionPositionConverter implements PositionConverter {
@Override
public PixelCoordinates textToPixel(TextPosition textPosition) {
final int textOffset = getIndexFromPosition(textPosition);
return offsetToPixel(textOffset);
}
@Override
public PixelCoordinates offsetToPixel(int textOffset) {
OrionPixelPositionOverlay location = textViewOverlay.getLocationAtOffset(textOffset);
location.setY(location.getY() + textViewOverlay.getLineHeight());
location = textViewOverlay.convert(location, "document", "page");
return new PixelCoordinates(location.getX(), location.getY());
}
@Override
public TextPosition pixelToText(PixelCoordinates coordinates) {
final int offset = pixelToOffset(coordinates);
return getPositionFromIndex(offset);
}
@Override
public int pixelToOffset(PixelCoordinates coordinates) {
return textViewOverlay.getOffsetAtLocation(coordinates.getX(), coordinates.getY());
}
}
public void replace(int offset, int length, String text) {
this.editorOverlay.setText(text, offset, offset + length);
}
public int getContentsCharCount() {
return this.editorOverlay.getModel().getCharCount();
}
@Override
public String getLineContent(final int line) {
return this.editorOverlay.getModel().getLine(line);
}
@Override
public TextRange getTextRangeForLine(final int line) {
final int startOffset = this.textViewOverlay.getModel().getLineStart(line);
final int endOffset = this.textViewOverlay.getModel().getLineEnd(line);
final int length = endOffset - startOffset;
return new TextRange(new TextPosition(line, 0), new TextPosition(line, length - 1));
}
@Override
public LinearRange getLinearRangeForLine(final int line) {
return LinearRange.createWithStart(this.textViewOverlay.getModel().getLineStart(line))
.andEnd(textViewOverlay.getModel().getLineEnd(line));
}
@Override
public TextRange getSelectedTextRange() {
final OrionSelectionOverlay selection = this.textViewOverlay.getSelection();
final int start = selection.getStart();
final TextPosition startPosition = getPositionFromIndex(start);
final int end = selection.getEnd();
final TextPosition endPosition = getPositionFromIndex(end);
return new TextRange(startPosition, endPosition);
}
@Override
public LinearRange getSelectedLinearRange() {
final OrionSelectionOverlay selection = this.textViewOverlay.getSelection();
final int start = selection.getStart();
final int end = selection.getEnd();
return LinearRange.createWithStart(start).andEnd(end);
}
/** {@inheritDoc} */
@Override
public void setSelectedRange(LinearRange range, boolean show) {
int startOffset = range.getStartOffset();
editorOverlay.setSelection(startOffset, startOffset + range.getLength(), show);
}
}