diff options
Diffstat (limited to 'src/CellBuffer.cxx')
-rw-r--r-- | src/CellBuffer.cxx | 316 |
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; } |