/**
* Copyright (c) 2009, 2010 Mark Feber, MulgaSoft
*
* 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
*
*/
package com.mulgasoft.emacsplus.commands;
import org.eclipse.core.commands.ExecutionEvent;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.ITextSelection;
import org.eclipse.jface.text.Position;
import org.eclipse.ui.texteditor.ITextEditor;
import com.mulgasoft.emacsplus.EmacsPlusUtils;
import com.mulgasoft.emacsplus.MarkUtils;
/**
* Implement: transpose-lines
* Exchange current line and previous line, leaving point after both.
* With argument ARG, takes previous line and moves it past ARG lines.
* With negative argument ARG, takes previous line and moves it up ARG lines.
* With argument 0, interchanges line point is in with line mark is in.
*
* Stop using ITextEditorActionDefinitionIds.MOVE_LINES_UP, as it has some unfortunate
* side-effects in formatting windows
*
* @author Mark Feber - initial API and implementation
*/
public class TransposeLineHandler extends EmacsPlusCmdHandler {
private final static String NOT_SET = "Mark_Not_Set"; //$NON-NLS-1$
/**
* @see com.mulgasoft.emacsplus.commands.EmacsPlusCmdHandler#transform(ITextEditor, IDocument, ITextSelection, ExecutionEvent)
*/
@Override
protected int transform(ITextEditor editor, IDocument document, ITextSelection currentSelection,
ExecutionEvent event) throws BadLocationException {
return ((getUniversalCount() == 0) ? transformAtPointAndMark(editor, document, currentSelection, event) :
transformAtPoint(editor, document, currentSelection, event));
}
protected int transformAtPoint(ITextEditor editor, IDocument document, ITextSelection currentSelection,
ExecutionEvent event) throws BadLocationException {
int result = NO_OFFSET;
int adj = (getUniversalCount() < 0 ? 1 : 2); // get line/cursor adjustmnent
try {
Lines lines = getLinesAtPoint(editor,document);
int line = document.getLineOfOffset(lines.getLine1().getOffset());
swapLines(document,lines);
// position cursor between swapped lines if -, else after
result = document.getLineOffset(line + adj);
} catch (Exception e) {
throw new BadLocationException();
}
return result;
}
protected int transformAtPointAndMark(ITextEditor editor, IDocument document, ITextSelection currentSelection,
ExecutionEvent event) throws BadLocationException {
int result = NO_OFFSET;
int mark = MarkUtils.getMark(editor);
if (mark == NO_OFFSET) {
EmacsPlusUtils.showMessage(editor, NOT_SET, true);
} else {
Position p1 = null, p2 = null, mp = null;
try {
Lines lines = getLinesAtPointAndMark(editor,document);
if (lines.isOk()) {
p1 = new Position(lines.getLine1().getOffset(), lines.getLine1().getLength());
p2 = new Position(lines.getLine2().getOffset(), lines.getLine2().getLength());
mp = new Position(mark);
document.addPosition(mp);
document.addPosition(p1);
document.addPosition(p2);
swapLines(document, lines);
}
} catch (Exception e) {
throw new BadLocationException();
} finally {
if (p1 != null) {
// position cursor, so that repeat invocation swaps lines back to original positions
mark = mp.getOffset();
if (MarkUtils.getMark(editor) == NO_OFFSET) { // Eclipse may have removed the mark
MarkUtils.setMark(editor, mark);
}
if (mark >= p1.getOffset() && mark <= p1.getOffset() + p1.getLength()) {
result = p2.getOffset();
} else {
result = p1.getOffset();
}
document.removePosition(mp);
document.removePosition(p1);
document.removePosition(p2);
}
}
}
return result;
}
private void swapLines(IDocument document, Lines lines) throws BadLocationException {
if (lines.isOk() && (lines.getLine2().getOffset() < document.getLength())) {
IRegion line1 = lines.getLine1();
IRegion line2 = lines.getLine2();
String line1Text = document.get(line1.getOffset(), line1.getLength());
String line2Text = document.get(line2.getOffset(), line2.getLength());
// swap the text from bottom up
updateText(document, line2.getOffset(), line2.getLength(), line1Text);
updateText(document, line1.getOffset(), line1.getLength(), line2Text);
}
}
private Lines getLinesAtPoint(ITextEditor editor, IDocument document) throws BadLocationException {
int line = document.getLineOfOffset(getCursorOffset(editor));
if (getUniversalCount() < 0) {
--line;
}
IRegion line2 = document.getLineInformation(line);
IRegion line1 = document.getLineInformation(--line);
return new Lines(line1,line2);
}
private Lines getLinesAtPointAndMark(ITextEditor editor, IDocument document) throws BadLocationException {
int point = getCursorOffset(editor);
int mark = MarkUtils.getMark(editor);
if (mark < point) {
int tmp = mark;
mark = point;
point = tmp;
}
return new Lines(document.getLineInformationOfOffset(point),document.getLineInformationOfOffset(mark));
}
/**
* Force undo protect
* @see com.mulgasoft.emacsplus.commands.EmacsPlusCmdHandler#undoProtect()
*/
protected boolean undoProtect() {
return true;
}
/**
* @see com.mulgasoft.emacsplus.commands.EmacsPlusCmdHandler#isZero()
*/
protected boolean isZero() {
return true;
}
private class Lines {
IRegion line1 = null; // the upper line
IRegion line2 = null; // the lower line
public Lines(IRegion line1, IRegion line2) {
this.line1 = line1;
this.line2 = line2;
}
public IRegion getLine1() {
return line1;
}
public IRegion getLine2() {
return line2;
}
public boolean isOk() {
return line1 != null && line2 != null && !line1.equals(line2);
}
}
}