package com.hipipal.texteditor.undo;
import java.util.Stack;
import com.hipipal.texteditor.common.Constants;
import com.hipipal.texteditor.common.Settings;
import android.text.Editable;
import android.util.Log;
import com.hipipal.texteditor.BuildConfig;
public class TextChangeWatcher implements Constants {
/**
*
*/
public TextChangeWatcher() {
mChanges = new Stack<TextChange>();
}
/**
* Undo the last operation
*
* @param text
* the text to undo on
* @return the caret position
*/
public int undo(Editable text) {
pushCurrentChange();
if (mChanges.size() == 0) {
if (BuildConfig.DEBUG)
Log.i(TAG, "Nothing to undo");
return -1;
}
TextChange change = mChanges.pop();
if (change != null)
return change.undo(text);
else if (BuildConfig.DEBUG)
Log.w(TAG, "Null change ?!");
return -1;
}
/**
* A change to the text {@linkplain s} will be made, where the
* {@linkplain count} characters starting at {@linkplain start} will be
* replaced by {@linkplain after} characters
*
* @param s
* the sequence being changed
* @param start
* the start index
* @param count
* the number of characters that will change
* @param after
* the number of characters that will replace the old ones
*
*/
public void beforeChange(CharSequence s, int start, int count, int after) {
if ((mCurrentChange != null)
&& (mCurrentChange.canMergeChangeBefore(s, start, count, after))) {
} else {
if (count == 0) {
// no existing character changed
// ignore, will be processed after
} else if (after == 0) {
// existing character replaced by none => delete
processDelete(s, start, count);
} else {
// n chars replaced by m other chars => replace
// replace is a delete AND an insert...
processDelete(s, start, count);
}
}
}
/**
* A change to the text {@linkplain s} has been made, where the
* {@linkplain count} characters starting at {@linkplain start} have
* replaced the substring of length {@linkplain before}
*
* @param s
* the sequence being changed
* @param start
* the start index
* @param before
* the number of character that were replaced
* @param count
* the number of characters that will change
*/
public void afterChange(CharSequence s, int start, int before, int count) {
if ((mCurrentChange != null)
&& (mCurrentChange.canMergeChangeAfter(s, start, before, count))) {
} else {
if (before == 0) {
// 0 charactes replaced by count => insert
processInsert(s, start, count);
} else if (count == 0) {
// existing character replaced by none => delete, already done
// before
} else {
// n chars replaced by m other chars => replace
// replace is a delete AND an insert...
processInsert(s, start, count);
}
}
// printStack();
}
/**
* @param s
* the sequence being modified
* @param start
* the first character index
* @param count
* the number of inserted text
*/
public void processInsert(CharSequence s, int start, int count) {
CharSequence sub = s.subSequence(start, start + count);
if (mCurrentChange != null)
pushCurrentChange();
mCurrentChange = new TextChangeInsert(sub, start);
}
/**
* @param s
* the sequence being modified
* @param start
* the first character index
* @param count
* the number of inserted text
*/
public void processDelete(CharSequence s, int start, int count) {
CharSequence sub = s.subSequence(start, start + count);
if (mCurrentChange != null)
pushCurrentChange();
mCurrentChange = new TextChangeDelete(sub, start);
}
/**
* Pushes the current change on top of the stack
*/
protected void pushCurrentChange() {
if (mCurrentChange == null)
return;
mChanges.push(mCurrentChange);
while (mChanges.size() > Settings.UNDO_MAX_STACK) {
mChanges.remove(0);
}
mCurrentChange = null;
}
/**
* Prints the current stack
*/
public void printStack() {
if (!BuildConfig.DEBUG)
return;
Log.i(TAG, "STACK");
for (TextChange change : mChanges) {
Log.d(TAG, change.toString());
}
Log.d(TAG, "Current change : " + mCurrentChange.toString());
}
protected TextChange mCurrentChange;
protected final Stack<TextChange> mChanges;
}