package com.himamis.retex.editor.share.controller;
import com.himamis.retex.editor.share.meta.MetaModel;
import com.himamis.retex.editor.share.model.MathArray;
import com.himamis.retex.editor.share.model.MathComponent;
import com.himamis.retex.editor.share.model.MathContainer;
import com.himamis.retex.editor.share.model.MathFunction;
import com.himamis.retex.editor.share.model.MathSequence;
public class EditorState {
private MetaModel metaModel;
private MathSequence rootComponent;
private MathSequence currentField;
private int currentOffset;
private MathComponent currentSelStart, currentSelEnd;
private MathComponent selectionAnchor;
public EditorState(MetaModel metaModel) {
this.metaModel = metaModel;
}
public MathSequence getRootComponent() {
return rootComponent;
}
public void setRootComponent(MathSequence rootComponent) {
this.rootComponent = rootComponent;
}
public MathSequence getCurrentField() {
return currentField;
}
public void setCurrentField(MathSequence currentField) {
this.currentField = currentField;
}
public int getCurrentOffset() {
return currentOffset;
}
public void setCurrentOffset(int currentOffset) {
this.currentOffset = currentOffset;
}
public void incCurrentOffset() {
currentOffset++;
}
public void addCurrentOffset(int size) {
currentOffset += size;
}
public void decCurrentOffset() {
currentOffset--;
}
public void addArgument(MathComponent mathComponent) {
currentField.addArgument(currentOffset, mathComponent);
incCurrentOffset();
}
public MetaModel getMetaModel() {
return metaModel;
}
public MathComponent getSelectionStart() {
return currentSelStart;
}
public MathComponent getSelectionEnd() {
return currentSelEnd;
}
public void setSelectionStart(MathComponent selStart) {
currentSelStart = selStart;
}
public void setSelectionEnd(MathComponent selEnd) {
currentSelEnd = selEnd;
}
/**
* Extends selection from current cursor position
*
* @param left
* true to go to the left from cursor
*/
public void extendSelection(boolean left) {
MathComponent cursorField = getCursorField(left);
extendSelection(cursorField);
}
/**
* Extends selection to include a field
*
* @param cursorField
* newly selected field
*/
public void extendSelection(MathComponent cursorField) {
if (selectionAnchor == null) {
currentSelStart = cursorField;
currentSelEnd = cursorField;
anchor(true);
return;
}
currentSelStart = selectionAnchor;
// go from selection start to the root until we find common root
MathContainer commonParent = currentSelStart.getParent();
while (commonParent != null && !contains(commonParent, cursorField)) {
currentSelStart = currentSelStart.getParent();
commonParent = currentSelStart.getParent();
}
if (commonParent == null) {
commonParent = rootComponent;
}
currentSelEnd = cursorField;
// special case: start is inside end -> select single component
if (currentSelEnd == commonParent
|| commonParent instanceof MathFunction
&& "\\frac".equals(((MathFunction) commonParent).getTexName())) {
currentSelStart = commonParent;
currentSelEnd = commonParent;
return;
}
// go from selection end to the root
while (currentSelEnd != null
&& commonParent.indexOf(currentSelEnd) < 0) {
currentSelEnd = currentSelEnd.getParent();
}
// swap start and end when necessary
int to = commonParent.indexOf(currentSelEnd);
int from = commonParent.indexOf(currentSelStart);
if (from > to) {
MathComponent swap = currentSelStart;
currentSelStart = currentSelEnd;
currentSelEnd = swap;
}
}
/**
* Select the whole formula
*/
public void selectAll() {
currentSelStart = getRootComponent();
currentSelEnd = currentSelStart;
anchor(true);
}
/**
* @param left
* whether to search left
* @return field directly left or right to the caret
*/
public MathComponent getCursorField(boolean left) {
return getCurrentField().getArgument(
Math.max(0, Math.min(getCurrentOffset() + (left ? 0 : -1),
getCurrentField().size() - 1)));
}
public String getSelectedText() {
StringBuilder sb = new StringBuilder();
if (currentSelStart != null && currentSelEnd != null
&& currentSelStart.getParent() != null) {
for (int i = currentSelStart.getParentIndex(); i <= currentSelEnd
.getParentIndex(); i++) {
sb.append(currentSelStart.getParent().getArgument(i));
}
}
return sb.toString();
}
private static boolean contains(MathContainer commonParent,
MathComponent cursorField0) {
MathComponent cursorField = cursorField0;
while (cursorField != null) {
if (cursorField == commonParent) {
return true;
}
cursorField = cursorField.getParent();
}
return false;
}
/**
* Reset selection start/end/anchor pointers (NOT the caret)
*/
public void resetSelection() {
selectionAnchor = null;
currentSelEnd = null;
currentSelStart = null;
}
/**
* @return true if has selection
*/
public boolean hasSelection() {
return currentSelStart != null;
}
public void anchor(boolean start) {
this.selectionAnchor = start ? this.currentSelStart
: this.currentSelEnd;
}
public void cursorToSelectionStart() {
if (this.currentSelStart != null) {
if (this.currentSelStart.getParent() != null) {
currentField = (MathSequence) this.currentSelStart.getParent();
} else {
this.currentField = (MathSequence) this.currentSelStart;
}
this.currentOffset = currentField.indexOf(currentSelStart) + 1;
}
}
public void cursorToSelectionEnd() {
if (currentSelEnd != null) {
if (this.currentSelEnd.getParent() != null) {
this.currentField = (MathSequence) this.currentSelEnd
.getParent();
} else {
this.currentField = (MathSequence) this.currentSelEnd;
}
this.currentOffset = currentField.indexOf(currentSelEnd) + 1;
}
}
public MathComponent getSelectionAnchor() {
return selectionAnchor;
}
public boolean isInsideQuotes() {
MathContainer fieldParent = currentField;
while (fieldParent != null) {
if (fieldParent instanceof MathArray
&& ((MathArray) fieldParent).getOpenKey() == '"') {
return true;
}
fieldParent = fieldParent.getParent();
}
return false;
}
}