19 #include "UndoRedoStack.hpp"
20 #include "TextEditChecker_p.hpp"
21 #include <QTextDocument>
25 struct UndoRedoStack::Action {
29 struct UndoRedoStack::UndoableInsert :
public UndoRedoStack::Action {
35 UndoableInsert(
int _pos,
const QString& _text){
38 isWhitespace = text.length() == 1 && text[0].isSpace();
39 isMergeable = (text.length() == 1);
43 struct UndoRedoStack::UndoableDelete :
public UndoRedoStack::Action {
50 UndoableDelete(
int _start,
int _end,
const QString& _text,
bool _deleteKeyUsed){
54 deleteKeyUsed = _deleteKeyUsed;
55 isWhitespace = text.length() == 1 && text[0].isSpace();
56 isMergeable = (text.length() == 1);
60 UndoRedoStack::UndoRedoStack(TextEditProxy* textEdit)
61 : m_textEdit(textEdit)
65 m_textEdit->document()->setUndoRedoEnabled(
true);
69 void UndoRedoStack::clear()
71 qDeleteAll(m_undoStack);
72 qDeleteAll(m_redoStack);
75 emit undoAvailable(
false);
76 emit redoAvailable(
false);
79 void UndoRedoStack::handleContentsChange(
int pos,
int removed,
int added)
81 if(m_actionInProgress || (added == 0 && removed == 0)){
85 QTextCursor c(m_textEdit->textCursor());
86 c.movePosition(QTextCursor::End);
87 int len = c.position();
88 if(pos == 0 && added > len){
92 qDeleteAll(m_redoStack);
95 m_textEdit->document()->undo();
96 bool deleteWasUsed = (c.anchor() == c.position() && c.position() == pos);
98 c.setPosition(pos + removed, QTextCursor::KeepAnchor);
99 UndoableDelete* undoAction =
new UndoableDelete(pos, pos + removed, c.selectedText(), deleteWasUsed);
100 m_textEdit->document()->redo();
101 if(m_undoStack.empty() || !dynamic_cast<UndoableDelete*>(m_undoStack.top())){
102 m_undoStack.push(undoAction);
104 UndoableDelete* prevDelete = static_cast<UndoableDelete*>(m_undoStack.top());
105 if(deleteMergeable(prevDelete, undoAction)){
106 if(prevDelete->start == undoAction->start){
107 prevDelete->text += undoAction->text;
108 prevDelete->end += (undoAction->end - undoAction->start);
110 prevDelete->text = undoAction->text + prevDelete->text;
111 prevDelete->start = undoAction->start;
114 m_undoStack.push(undoAction);
119 QTextCursor c(m_textEdit->textCursor());
121 c.setPosition(pos + added, QTextCursor::KeepAnchor);
122 UndoableInsert* undoAction =
new UndoableInsert(pos, c.selectedText());
123 if(m_undoStack.empty() || !dynamic_cast<UndoableInsert*>(m_undoStack.top())){
124 m_undoStack.push(undoAction);
126 UndoableInsert* prevInsert = static_cast<UndoableInsert*>(m_undoStack.top());
127 if(insertMergeable(prevInsert, undoAction)){
128 prevInsert->text += undoAction->text;
130 m_undoStack.push(undoAction);
135 if(added > 0 || removed > 0){
136 m_textEdit->document()->clearUndoRedoStacks();
138 emit redoAvailable(
false);
139 emit undoAvailable(
true);
142 void UndoRedoStack::undo()
144 if(m_undoStack.empty()){
147 m_actionInProgress =
true;
148 Action* undoAction = m_undoStack.pop();
149 m_redoStack.push(undoAction);
150 QTextCursor c(m_textEdit->textCursor());
151 if(dynamic_cast<UndoableInsert*>(undoAction)){
152 UndoableInsert* insertAction = static_cast<UndoableInsert*>(undoAction);
153 c.setPosition(insertAction->pos);
154 c.setPosition(insertAction->pos + insertAction->text.length(), QTextCursor::KeepAnchor);
155 c.removeSelectedText();
157 UndoableDelete* deleteAction = static_cast<UndoableDelete*>(undoAction);
158 c.setPosition(deleteAction->start);
159 c.insertText(deleteAction->text);
160 if(deleteAction->deleteKeyUsed){
161 c.setPosition(deleteAction->start);
164 m_textEdit->setTextCursor(c);
165 emit undoAvailable(!m_undoStack.empty());
166 emit redoAvailable(!m_redoStack.empty());
167 m_actionInProgress =
false;
170 void UndoRedoStack::redo()
172 if(m_redoStack.empty()){
175 m_actionInProgress =
true;
176 Action* redoAction = m_redoStack.top();
178 m_undoStack.push(redoAction);
179 QTextCursor c(m_textEdit->textCursor());
180 if(dynamic_cast<UndoableInsert*>(redoAction)){
181 UndoableInsert* insertAction = static_cast<UndoableInsert*>(redoAction);
182 c.setPosition(insertAction->pos);
183 c.insertText(insertAction->text);
185 UndoableDelete* deleteAction = static_cast<UndoableDelete*>(redoAction);
186 c.setPosition(deleteAction->start);
187 c.setPosition(deleteAction->end, QTextCursor::KeepAnchor);
188 c.removeSelectedText();
190 m_textEdit->setTextCursor(c);
191 emit undoAvailable(!m_undoStack.empty());
192 emit redoAvailable(!m_redoStack.empty());
193 m_actionInProgress =
false;
196 bool UndoRedoStack::insertMergeable(
const UndoableInsert* prev,
const UndoableInsert* cur)
const
198 return (cur->pos == prev->pos + prev->text.length()) &&
199 (cur->isWhitespace == prev->isWhitespace) &&
200 (cur->isMergeable && prev->isMergeable);
203 bool UndoRedoStack::deleteMergeable(
const UndoableDelete* prev,
const UndoableDelete* cur)
const
205 return (prev->deleteKeyUsed == cur->deleteKeyUsed) &&
206 (cur->isWhitespace == prev->isWhitespace) &&
207 (cur->isMergeable && prev->isMergeable) &&
208 (prev->start == cur->start || prev->start == cur->end);