package org.eclipse.iee.editor.core.pad.common.ui;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.iee.core.document.text.ITextLocation;
import org.eclipse.iee.core.document.text.OffsetTextLocation;
import org.eclipse.iee.core.document.text.Span;
import org.eclipse.iee.core.document.text.Text;
import org.eclipse.iee.core.document.text.TextStyle;
import org.eclipse.iee.editor.core.container.EditorManager;
import org.eclipse.iee.editor.core.container.ICursorManager;
import org.eclipse.iee.editor.core.container.ITextEditor;
import org.eclipse.iee.editor.core.pad.common.text.IEditorLocation;
import org.eclipse.iee.editor.core.pad.common.text.ITextContainer;
import org.eclipse.iee.editor.core.pad.common.text.OffsetEditorLocation;
import org.eclipse.iee.editor.core.pad.common.text.TextPartEditor;
import org.eclipse.iee.editor.text.edit.ChangeStyleCtx;
import org.eclipse.iee.editor.text.edit.ChangeStyleVisitor;
import org.eclipse.iee.editor.text.edit.CompositeCommand;
import org.eclipse.iee.editor.text.edit.CopyCtx;
import org.eclipse.iee.editor.text.edit.CopyVisitor;
import org.eclipse.iee.editor.text.edit.IEditCommand;
import org.eclipse.iee.editor.text.edit.ReplaceCtx;
import org.eclipse.iee.editor.text.edit.ReplaceVisitor;
import com.google.common.base.Optional;
import com.google.common.base.Supplier;
import com.google.common.base.Verify;
public class SelectionModel {
private IEditorLocation fStart;
private IEditorLocation fEnd;
private EditorManager fEditorManager;
private ICursorManager fCursorManager;
public SelectionModel(EditorManager editorManager, ICursorManager cursorManager) {
fEditorManager = editorManager;
fCursorManager = cursorManager;
}
public SelectionModel(IEditorLocation start, IEditorLocation end) {
this.fStart = start;
this.fEnd = end;
}
public void select() {
if (fStart == null || fEnd == null) {
return;
}
ITextEditor<?> commonAncestor = findCommonAncestor(fStart.getEditor(), fEnd.getEditor());
SelectionModel normalized = normalize();
commonAncestor.selectBetween(normalized.getStart(), normalized.getEnd());
}
public SelectionModel normalize() {
ITextEditor<?> commonAncestor = findCommonAncestor(fStart.getEditor(), fEnd.getEditor());
ITextEditor<?> l = fStart.getEditor();
while(l != commonAncestor && l.getParent().get() != commonAncestor) {
l = l.getParent().get();
}
ITextEditor<?> r = fEnd.getEditor();
while(r != commonAncestor && r.getParent().get() != commonAncestor) {
r = r.getParent().get();
}
List<ITextEditor<?>> children = commonAncestor.getChildren();
IEditorLocation left;
IEditorLocation right;
if (children.indexOf(l) < children.indexOf(r)) {
left = fStart;
right = fEnd;
} else if (children.indexOf(l) == children.indexOf(r) && fEnd.getOffset() > fStart.getOffset()) {
left = fStart;
right = fEnd;
} else {
left = fEnd;
right = fStart;
}
return new SelectionModel(left, right);
}
public void unselect() {
if (fStart == null || fEnd == null) {
return;
}
ITextEditor<?> commonAncestor = findCommonAncestor(fStart.getEditor(), fEnd.getEditor());
SelectionModel normalized = normalize();
commonAncestor.unselectBetween(normalized.getStart(), normalized.getEnd());
}
public static ITextEditor<?> findCommonAncestor(ITextEditor<?> ll, ITextEditor<?> rr) {
if (ll == rr) {
return ll;
}
List<ITextEditor<?>> leftAncestors = new ArrayList<>();
List<ITextEditor<?>> rightAncestors = new ArrayList<>();
Optional<ITextEditor<?>> l = Optional.<ITextEditor<?>> of(ll);
Optional<ITextEditor<?>> r = Optional.<ITextEditor<?>> of(rr);
while (l.isPresent()) {
leftAncestors.add(l.get());
l = l.get().getParent();
}
while (r.isPresent()) {
rightAncestors.add(r.get());
r = r.get().getParent();
}
int il = leftAncestors.size() - 1;
int ir = rightAncestors.size() - 1;
do {
if (leftAncestors.get(il) != rightAncestors.get(ir)) {
break;
}
il--;
ir--;
} while (il >= 0 && ir >= 0);
return leftAncestors.get(il + 1);
}
public void append(IEditorLocation end) {
unselect();
this.fEnd = end;
select();
}
public void set(IEditorLocation textLocation) {
unselect();
this.fStart = this.fEnd = textLocation;
}
public void setEnd(IEditorLocation textLocation) {
unselect();
this.fEnd = textLocation;
select();
}
public IEditorLocation getStart() {
return fStart;
}
public IEditorLocation getEnd() {
return fEnd;
}
public IEditorLocation replace(String text) {
Verify.verifyNotNull(fStart);
Verify.verifyNotNull(fEnd);
SelectionModel normalized = normalize();
IEditorLocation start = normalized.getStart();
ITextEditor<?> startEditor = start.getEditor();
IEditorLocation end = normalized.getEnd();
ITextEditor<?> endEditor = end.getEditor();
if (startEditor == endEditor && startEditor instanceof TextPartEditor) {
TextPartEditor textPartEditor = (TextPartEditor) startEditor;
textPartEditor.replace(start.getOffset(), end.getOffset(), text);
return new OffsetEditorLocation((ITextContainer<?>) end.getEditor(), start.getOffset() + text.length());
}
ITextLocation modelStart = new OffsetTextLocation((Text) startEditor.getModel(), start.getOffset());
ITextLocation modelEnd = new OffsetTextLocation((Text) endEditor.getModel(), end.getOffset());
CompositeCommand accept = modelStart.findCommonAncestor(modelEnd).accept(new ReplaceVisitor(), new ReplaceCtx(modelStart, modelEnd, text));
accept.perform();
modelEnd = accept.adjust(modelEnd);
return new OffsetEditorLocation((ITextContainer<?>) end.getEditor(), modelEnd.getOffset());
}
public IEditorLocation applyStyle(final StyleProcessor styleProcessor) {
Verify.verifyNotNull(fStart);
Verify.verifyNotNull(fEnd);
SelectionModel normalized = normalize();
ITextLocation modelStart = new OffsetTextLocation((Text) normalized.getStart().getEditor().getModel(), normalized.getStart().getOffset());
ITextLocation modelEnd = new OffsetTextLocation((Text) normalized.getEnd().getEditor().getModel(), normalized.getEnd().getOffset());
CompositeCommand accept = modelStart.findCommonAncestor(modelEnd).accept(new ChangeStyleVisitor(), new ChangeStyleCtx(modelStart, modelEnd) {
@Override
protected IEditCommand do_(final Supplier<Span> span) {
return new IEditCommand() {
@Override
public void perform() {
styleProcessor.apply(span.get().getStyle());
}
@Override
public ITextLocation adjust(ITextLocation location) {
return location;
}
};
}
});
accept.perform();
modelStart = accept.adjust(modelStart);
modelEnd = accept.adjust(modelEnd);
ITextContainer<?> editorStart = (ITextContainer<?>) Verify.verifyNotNull(fEditorManager.getEditorByModel(modelStart.getModel()));
fStart = new OffsetEditorLocation(editorStart, modelStart.getOffset());
ITextContainer<?> editorEnd = (ITextContainer<?>) Verify.verifyNotNull(fEditorManager.getEditorByModel(modelEnd.getModel()));
fEnd = new OffsetEditorLocation(editorEnd, modelEnd.getOffset());
select();
fCursorManager.putCursor(fEnd);
return fEnd;
}
public boolean isEmpty() {
return fStart.getEditor() == fEnd.getEditor() && fStart.getOffset() == fEnd.getOffset();
}
public static interface StyleProcessor {
void apply(TextStyle style);
}
public TextStyle getStyle() {
SelectionModel normalized = normalize();
Text model = (Text) normalized.getStart().getEditor().getModel();
return model.getStyle();
}
public String copy() {
Verify.verifyNotNull(fStart);
Verify.verifyNotNull(fEnd);
SelectionModel normalized = normalize();
IEditorLocation start = normalized.getStart();
ITextEditor<?> startEditor = start.getEditor();
IEditorLocation end = normalized.getEnd();
ITextEditor<?> endEditor = end.getEditor();
if (startEditor == endEditor && startEditor instanceof TextPartEditor) {
TextPartEditor textPartEditor = (TextPartEditor) startEditor;
return textPartEditor.getModel().substring(start.getOffset(), end.getOffset());
}
ITextLocation modelStart = new OffsetTextLocation((Text) startEditor.getModel(), start.getOffset());
ITextLocation modelEnd = new OffsetTextLocation((Text) endEditor.getModel(), end.getOffset());
String txt = modelStart.findCommonAncestor(modelEnd).accept(new CopyVisitor(), new CopyCtx(modelStart, modelEnd));
return txt;
}
}