aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/CellBuffer.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'src/CellBuffer.cxx')
-rw-r--r--src/CellBuffer.cxx316
1 files changed, 195 insertions, 121 deletions
diff --git a/src/CellBuffer.cxx b/src/CellBuffer.cxx
index 777688511..4d667dcf4 100644
--- a/src/CellBuffer.cxx
+++ b/src/CellBuffer.cxx
@@ -344,31 +344,199 @@ void Action::Grab(Action *source) {
source->lenData = 0;
}
-CellBuffer::CellBuffer(int initialLength) {
- body = new char[initialLength];
- size = initialLength;
- length = 0;
- part1len = 0;
- gaplen = initialLength;
- part2body = body + gaplen;
- readOnly = false;
+// The undo history stores a sequence of user operations that represent the user's view of the
+// commands executed on the text.
+// Each user operation contains a sequence of text insertion and text deletion actions.
+// All the user operations are stored in a list of individual actions with 'start' actions used
+// as delimiters between user operations.
+// Initially there is one start action in the history.
+// As each action is performed, it is recorded in the history. The action may either become
+// part of the current user operation or may start a new user operation. If it is to be part of the
+// current operation, then it overwrites the current last action. If it is to be part of a new
+// operation, it is appended after the current last action.
+// After writing the new action, a new start action is appended at the end of the history.
+// The decision of whether to start a new user operation is based upon two factors. If a
+// compound operation has been explicitly started by calling BeginUndoAction and no matching
+// EndUndoAction (these calls nest) has been called, then the action is coalesced into the current
+// operation. If there is no outstanding BeginUndoAction call then a new operation is started
+// unless it looks as if the new action is caused by the user typing or deleting a stream of text.
+// Sequences that look like typing or deletion are coalesced into a single user operation.
+
+UndoHistory::UndoHistory() {
lenActions = 100;
actions = new Action[lenActions];
maxAction = 0;
currentAction = 0;
- collectingUndo = undoCollectAutoStart;
undoSequenceDepth = 0;
savePoint = 0;
actions[currentAction].Create(startAction);
}
+UndoHistory::~UndoHistory() {
+ delete []actions;
+ actions = 0;
+}
+
+void UndoHistory::EnsureUndoRoom() {
+ //Platform::DebugPrintf("%% %d action %d %d %d\n", at, position, length, currentAction);
+ if (currentAction >= 2) {
+ // Have to test that there is room for 2 more actions in the array
+ // as two actions may be created by this function
+ if (currentAction >= (lenActions - 2)) {
+ // Run out of undo nodes so extend the array
+ int lenActionsNew = lenActions * 2;
+ Action *actionsNew = new Action[lenActionsNew];
+ if (!actionsNew)
+ return;
+ for (int act = 0; act <= currentAction; act++)
+ actionsNew[act].Grab(&actions[act]);
+ delete []actions;
+ lenActions = lenActionsNew;
+ actions = actionsNew;
+ }
+ }
+}
+
+void UndoHistory::AppendAction(actionType at, int position, char *data, int lengthData) {
+ EnsureUndoRoom();
+ Platform::DebugPrintf("%% %d action %d %d %d\n", at, position, lengthData, currentAction);
+ Platform::DebugPrintf("^ %d action %d %d\n", actions[currentAction - 1].at,
+ actions[currentAction - 1].position, actions[currentAction - 1].lenData);
+ if (0 == undoSequenceDepth) {
+ // Top level actions may not always be coalesced
+ if (currentAction >= 2) {
+ Action &actPrevious = actions[currentAction - 1];
+ // See if current action can be coalesced into previous action
+ // Will work if both are inserts or deletes and position is same
+ if (at != actPrevious.at) {
+ currentAction++;
+ } else if (currentAction == savePoint) {
+ currentAction++;
+ } else if ((at == removeAction) &&
+ ((position + lengthData * 2) != actPrevious.position)) {
+ // Removals must be at same position to coalesce
+ currentAction++;
+ } else if ((at == insertAction) &&
+ (position != (actPrevious.position + actPrevious.lenData*2))) {
+ // Insertions must be immediately after to coalesce
+ currentAction++;
+ } else {
+ Platform::DebugPrintf("action coalesced\n");
+ }
+ } else {
+ currentAction++;
+ }
+ }
+ actions[currentAction].Create(at, position, data, lengthData);
+ //if ((collectingUndo == undoCollectAutoStart) && (0 == undoSequenceDepth)) {
+ currentAction++;
+ actions[currentAction].Create(startAction);
+ //}
+ maxAction = currentAction;
+}
+
+void UndoHistory::BeginUndoAction() {
+ EnsureUndoRoom();
+ if (undoSequenceDepth == 0) {
+ if (actions[currentAction].at != startAction) {
+ currentAction++;
+ actions[currentAction].Create(startAction);
+ maxAction = currentAction;
+ }
+ }
+ undoSequenceDepth++;
+}
+
+void UndoHistory::EndUndoAction() {
+ EnsureUndoRoom();
+ undoSequenceDepth--;
+ if (0 == undoSequenceDepth) {
+ if (actions[currentAction].at != startAction) {
+ currentAction++;
+ actions[currentAction].Create(startAction);
+ maxAction = currentAction;
+ }
+ }
+}
+
+void UndoHistory::DropUndoSequence() {
+ undoSequenceDepth = 0;
+}
+
+void UndoHistory::DeleteUndoHistory() {
+ for (int i = 1; i < maxAction; i++)
+ actions[i].Destroy();
+ maxAction = 0;
+ currentAction = 0;
+ savePoint = 0;
+}
+
+void UndoHistory::SetSavePoint() {
+ savePoint = currentAction;
+}
+
+bool UndoHistory::IsSavePoint() const {
+ return savePoint == currentAction;
+}
+
+bool UndoHistory::CanUndo() const {
+ return (currentAction > 0) && (maxAction > 0);
+}
+
+int UndoHistory::StartUndo() {
+ // Drop any trailing startAction
+ if (actions[currentAction].at == startAction && currentAction > 0)
+ currentAction--;
+
+ // Count the steps in this action
+ int act = currentAction;
+ while (actions[act].at != startAction && act > 0) {
+ act--;
+ }
+ return currentAction - act;
+}
+
+const Action &UndoHistory::UndoStep() {
+ return actions[currentAction--];
+}
+
+bool UndoHistory::CanRedo() const {
+ return maxAction > currentAction;
+}
+
+int UndoHistory::StartRedo() {
+ // Drop any leading startAction
+ if (actions[currentAction].at == startAction && currentAction < maxAction)
+ currentAction++;
+
+ // Count the steps in this action
+ int act = currentAction;
+ while (actions[act].at != startAction && act < maxAction) {
+ act++;
+ }
+ return act - currentAction;
+}
+
+const Action &UndoHistory::RedoStep() {
+ return actions[currentAction++];
+}
+
+CellBuffer::CellBuffer(int initialLength) {
+ body = new char[initialLength];
+ size = initialLength;
+ length = 0;
+ part1len = 0;
+ gaplen = initialLength;
+ part2body = body + gaplen;
+ readOnly = false;
+ collectingUndo = undoCollectAutoStart;
+}
+
CellBuffer::~CellBuffer() {
delete []body;
body = 0;
- delete []actions;
- actions = 0;
}
void CellBuffer::GapTo(int position) {
@@ -486,7 +654,7 @@ const char *CellBuffer::InsertString(int position, char *s, int insertLength) {
for (int i = 0; i < insertLength / 2; i++) {
data[i] = s[i * 2];
}
- AppendAction(insertAction, position, data, insertLength / 2);
+ uh.AppendAction(insertAction, position, data, insertLength / 2);
}
BasicInsertString(position, s, insertLength);
@@ -525,48 +693,6 @@ bool CellBuffer::SetStyleFor(int position, int lengthStyle, char style, char mas
return changed;
}
-void CellBuffer::EnsureUndoRoom() {
- //Platform::DebugPrintf("%% %d action %d %d %d\n", at, position, length, currentAction);
- if (currentAction >= 2) {
- // Have to test that there is room for 2 more actions in the array
- // as two actions may be created by this function
- if (currentAction >= (lenActions - 2)) {
- // Run out of undo nodes so extend the array
- int lenActionsNew = lenActions * 2;
- Action *actionsNew = new Action[lenActionsNew];
- if (!actionsNew)
- return;
- for (int act = 0; act <= currentAction; act++)
- actionsNew[act].Grab(&actions[act]);
- delete []actions;
- lenActions = lenActionsNew;
- actions = actionsNew;
- }
- }
-}
-
-void CellBuffer::AppendAction(actionType at, int position, char *data, int lengthData) {
- EnsureUndoRoom();
- //Platform::DebugPrintf("%% %d action %d %d %d\n", at, position, lengthData, currentAction);
- if (currentAction >= 2) {
- // See if current action can be coalesced into previous action
- // Will work if both are inserts or deletes and position is same or two different
- if ((at != actions[currentAction - 1].at) || (abs(position - actions[currentAction - 1].position) > 2)) {
- currentAction++;
- } else if (currentAction == savePoint) {
- currentAction++;
- }
- } else {
- currentAction++;
- }
- actions[currentAction].Create(at, position, data, lengthData);
- if ((collectingUndo == undoCollectAutoStart) && (0 == undoSequenceDepth)) {
- currentAction++;
- actions[currentAction].Create(startAction);
- }
- maxAction = currentAction;
-}
-
const char *CellBuffer::DeleteChars(int position, int deleteLength) {
// InsertString and DeleteChars are the bottleneck though which all changes occur
char *data = 0;
@@ -577,7 +703,7 @@ const char *CellBuffer::DeleteChars(int position, int deleteLength) {
for (int i = 0; i < deleteLength / 2; i++) {
data[i] = ByteAt(position + i * 2);
}
- AppendAction(removeAction, position, data, deleteLength / 2);
+ uh.AppendAction(removeAction, position, data, deleteLength / 2);
}
BasicDeleteChars(position, deleteLength);
@@ -616,11 +742,11 @@ void CellBuffer::SetReadOnly(bool set) {
}
void CellBuffer::SetSavePoint() {
- savePoint = currentAction;
+ uh.SetSavePoint();
}
bool CellBuffer::IsSavePoint() {
- return savePoint == currentAction;
+ return uh.IsSavePoint();
}
int CellBuffer::AddMark(int line, int markerNum) {
@@ -659,7 +785,7 @@ int CellBuffer::LineFromHandle(int markerHandle) {
// Without undo
void CellBuffer::BasicInsertString(int position, char *s, int insertLength) {
- //Platform::DebugPrintf("Inserting at %d for %d\n", position, insertLength);
+ Platform::DebugPrintf("Inserting at %d for %d\n", position, insertLength);
if (insertLength == 0)
return;
RoomFor(insertLength);
@@ -719,7 +845,7 @@ void CellBuffer::BasicInsertString(int position, char *s, int insertLength) {
}
void CellBuffer::BasicDeleteChars(int position, int deleteLength) {
- //Platform::DebugPrintf("Deleting at %d for %d\n", position, deleteLength);
+ Platform::DebugPrintf("Deleting at %d for %d\n", position, deleteLength);
if (deleteLength == 0)
return;
@@ -792,7 +918,7 @@ void CellBuffer::BasicDeleteChars(int position, int deleteLength) {
undoCollectionType CellBuffer::SetUndoCollection(undoCollectionType collectUndo) {
collectingUndo = collectUndo;
- undoSequenceDepth = 0;
+ uh.DropUndoSequence();
return collectingUndo;
}
@@ -800,69 +926,28 @@ bool CellBuffer::IsCollectingUndo() {
return collectingUndo;
}
-void CellBuffer::AppendUndoStartAction() {
- EnsureUndoRoom();
- // Finish any currently active undo sequence
- undoSequenceDepth = 0;
- if (actions[currentAction].at != startAction) {
- undoSequenceDepth++;
- currentAction++;
- actions[currentAction].Create(startAction);
- maxAction = currentAction;
- }
-}
-
void CellBuffer::BeginUndoAction() {
- EnsureUndoRoom();
- if (undoSequenceDepth == 0) {
- if (actions[currentAction].at != startAction) {
- currentAction++;
- actions[currentAction].Create(startAction);
- maxAction = currentAction;
- }
- }
- undoSequenceDepth++;
+ uh.BeginUndoAction();
}
void CellBuffer::EndUndoAction() {
- EnsureUndoRoom();
- undoSequenceDepth--;
- if (0 == undoSequenceDepth) {
- if (actions[currentAction].at != startAction) {
- currentAction++;
- actions[currentAction].Create(startAction);
- maxAction = currentAction;
- }
- }
+ uh.EndUndoAction();
}
void CellBuffer::DeleteUndoHistory() {
- for (int i = 1; i < maxAction; i++)
- actions[i].Destroy();
- maxAction = 0;
- currentAction = 0;
- savePoint = 0;
+ uh.DeleteUndoHistory();
}
bool CellBuffer::CanUndo() {
- return (!readOnly) && ((currentAction > 0) && (maxAction > 0));
+ return (!readOnly) && (uh.CanUndo());
}
int CellBuffer::StartUndo() {
- // Drop any trailing startAction
- if (actions[currentAction].at == startAction && currentAction > 0)
- currentAction--;
-
- // Count the steps in this action
- int act = currentAction;
- while (actions[act].at != startAction && act > 0) {
- act--;
- }
- return currentAction - act;
+ return uh.StartUndo();
}
const Action &CellBuffer::UndoStep() {
- const Action &actionStep = actions[currentAction];
+ const Action &actionStep = uh.UndoStep();
if (actionStep.at == insertAction) {
BasicDeleteChars(actionStep.position, actionStep.lenData*2);
} else if (actionStep.at == removeAction) {
@@ -874,29 +959,19 @@ const Action &CellBuffer::UndoStep() {
BasicInsertString(actionStep.position, styledData, actionStep.lenData*2);
delete []styledData;
}
- currentAction--;
return actionStep;
}
bool CellBuffer::CanRedo() {
- return (!readOnly) && (maxAction > currentAction);
+ return (!readOnly) && (uh.CanRedo());
}
int CellBuffer::StartRedo() {
- // Drop any leading startAction
- if (actions[currentAction].at == startAction && currentAction < maxAction)
- currentAction++;
-
- // Count the steps in this action
- int act = currentAction;
- while (actions[act].at != startAction && act < maxAction) {
- act++;
- }
- return act - currentAction;
+ return uh.StartRedo();
}
const Action &CellBuffer::RedoStep() {
- const Action &actionStep = actions[currentAction];
+ const Action &actionStep = uh.RedoStep();
if (actionStep.at == insertAction) {
char *styledData = new char[actionStep.lenData * 2];
for (int i = 0; i < actionStep.lenData; i++) {
@@ -908,7 +983,6 @@ const Action &CellBuffer::RedoStep() {
} else if (actionStep.at == removeAction) {
BasicDeleteChars(actionStep.position, actionStep.lenData*2);
}
- currentAction++;
return actionStep;
}