21 #include "history_p.h"
23 #include <kcalutils/stringify.h>
25 using namespace KCalCore;
26 using namespace Akonadi;
28 History::History(
QObject *parent) :
QObject(parent), d(new Private(this))
30 d->mChanger =
new IncidenceChanger(
false,
this);
31 d->mChanger->setObjectName(
"changer");
32 d->mOperationTypeInProgress = TypeNone;
34 d->mUndoAllInProgress =
false;
42 History::Private::Private(
History *qq) : q(qq)
49 const uint atomicOperationId)
51 Q_ASSERT_X(item.isValid(),
"History::recordCreation()",
52 "Item must be valid.");
54 Q_ASSERT_X(item.hasPayload<KCalCore::Incidence::Ptr>(),
"History::recordCreation()",
55 "Item must have Incidence::Ptr payload.");
57 Entry::Ptr entry(
new CreationEntry(item, description,
this));
59 d->stackEntry(entry, atomicOperationId);
63 const Akonadi::Item &newItem,
65 const uint atomicOperationId)
67 Q_ASSERT_X(oldItem.isValid(),
"History::recordModification",
"old item must be valid");
68 Q_ASSERT_X(newItem.isValid(),
"History::recordModification",
"newItem item must be valid");
69 Q_ASSERT_X(oldItem.hasPayload<KCalCore::Incidence::Ptr>(),
"History::recordModification",
70 "old item must have Incidence::Ptr payload");
71 Q_ASSERT_X(newItem.hasPayload<KCalCore::Incidence::Ptr>(),
"History::recordModification",
72 "newItem item must have Incidence::Ptr payload");
74 Entry::Ptr entry(
new ModificationEntry(newItem, oldItem.payload<KCalCore::Incidence::Ptr>(),
77 Q_ASSERT(newItem.revision() >= oldItem.revision());
79 d->stackEntry(entry, atomicOperationId);
84 const uint atomicOperationId)
86 Q_ASSERT_X(item.isValid(),
"History::recordDeletion",
"Item must be valid");
94 const uint atomicOperationId)
96 Entry::Ptr entry(
new DeletionEntry(items, description,
this));
98 foreach(
const Akonadi::Item &item, items) {
100 Q_ASSERT_X(item.isValid(),
101 "History::recordDeletion()",
"Item must be valid.");
102 Q_ASSERT_X(item.hasPayload<Incidence::Ptr>(),
103 "History::recordDeletion()",
"Item must have an Incidence::Ptr payload.");
106 d->stackEntry(entry, atomicOperationId);
111 if (!d->mUndoStack.isEmpty())
112 return d->mUndoStack.top()->mDescription;
119 if (!d->mRedoStack.isEmpty())
120 return d->mRedoStack.top()->mDescription;
127 d->undoOrRedo(TypeUndo, parent);
132 d->undoOrRedo(TypeRedo, parent);
137 if (d->mOperationTypeInProgress != TypeNone) {
138 kWarning() <<
"Don't call History::undoAll() while an undo/redo/undoAll is in progress";
139 }
else if (d->mEnabled) {
140 d->mUndoAllInProgress =
true;
141 d->mCurrentParent =
parent;
144 kWarning() <<
"Don't call undo/redo when History is disabled";
151 if (d->mOperationTypeInProgress == TypeNone) {
152 d->mRedoStack.clear();
153 d->mUndoStack.clear();
154 d->mLastErrorString.clear();
155 d->mQueuedEntries.clear();
165 return d->mLastErrorString;
170 return !d->mUndoStack.isEmpty() && d->mOperationTypeInProgress == TypeNone;
175 return !d->mRedoStack.isEmpty() && d->mOperationTypeInProgress == TypeNone;
178 void History::Private::updateIds(Item::Id oldId, Item::Id newId)
180 mEntryInProgress->updateIds(oldId, newId);
183 entry->updateIds(oldId, newId);
186 entry->updateIds(oldId, newId);
189 void History::Private::doIt(OperationType type)
191 mOperationTypeInProgress = type;
193 Q_ASSERT(!stack().isEmpty());
194 mEntryInProgress = stack().pop();
196 connect(mEntryInProgress.data(), SIGNAL(finished(Akonadi::IncidenceChanger::ResultCode,
QString)),
197 SLOT(handleFinished(Akonadi::IncidenceChanger::ResultCode,
QString)),
198 Qt::UniqueConnection);
199 mEntryInProgress->doIt(type);
202 void History::Private::handleFinished(IncidenceChanger::ResultCode changerResult,
205 Q_ASSERT(mOperationTypeInProgress != TypeNone);
206 Q_ASSERT(!(mUndoAllInProgress && mOperationTypeInProgress == TypeRedo));
208 const bool success = (changerResult == IncidenceChanger::ResultCodeSuccess);
213 mLastErrorString.clear();
214 destinationStack().push(mEntryInProgress);
216 mLastErrorString = errorString;
217 stack().push(mEntryInProgress);
224 if (!mQueuedEntries.isEmpty()) {
226 foreach(
const Entry::Ptr &entry, mQueuedEntries) {
227 mUndoStack.push(entry);
229 mQueuedEntries.clear();
232 emitDone(mOperationTypeInProgress, resultCode);
233 mOperationTypeInProgress = TypeNone;
237 void History::Private::stackEntry(
const Entry::Ptr &entry, uint atomicOperationId)
239 const bool useMultiEntry = (atomicOperationId > 0);
244 Entry::Ptr topEntry = (mOperationTypeInProgress == TypeNone) ?
245 (mUndoStack.isEmpty() ?
Entry::Ptr() : mUndoStack.top()) :
246 (mQueuedEntries.isEmpty() ? Entry::Ptr() : mQueuedEntries.last());
248 const bool topIsMultiEntry = qobject_cast<MultiEntry*>(topEntry.
data());
250 if (topIsMultiEntry) {
252 if (multiEntry->mAtomicOperationId != atomicOperationId) {
253 multiEntry =
MultiEntry::Ptr(
new MultiEntry(atomicOperationId, entry->mDescription, q));
254 entryToPush = multiEntry;
256 multiEntry->addEntry(entry);
259 entry->mDescription, q));
260 multiEntry->addEntry(entry);
261 entryToPush = multiEntry;
267 if (mOperationTypeInProgress == TypeNone) {
269 mUndoStack.push(entryToPush);
275 mQueuedEntries.append(entryToPush);
280 void History::Private::undoOrRedo(OperationType type,
QWidget *parent)
283 Q_ASSERT(mOperationTypeInProgress == TypeNone);
285 if (!stack(type).isEmpty()) {
287 mCurrentParent = parent;
290 kWarning() <<
"Don't call undo/redo when History is disabled";
293 kWarning() <<
"Don't call undo/redo when the stack is empty.";
300 return type == TypeUndo ? mUndoStack : mRedoStack;
303 void History::Private::setEnabled(
bool enabled)
305 if (enabled != mEnabled) {
310 int History::Private::redoCount()
const
312 return mRedoStack.count();
315 int History::Private::undoCount()
const
317 return mUndoStack.count();
322 return stack(mOperationTypeInProgress);
328 return mOperationTypeInProgress == TypeRedo ? mUndoStack : mRedoStack;
333 if (type == TypeUndo) {
334 emit q->undone(resultCode);
335 }
else if (type == TypeRedo) {
336 emit q->redone(resultCode);
342 #include "moc_history.cpp"
343 #include "moc_history_p.cpp"
QString nextRedoDescription() const
Returns the description of the next redo.
void undo(QWidget *parent=0)
Reverts the change that's on top of the undo stack.
void recordCreation(const Akonadi::Item &item, const QString &description, const uint atomicOperationId=0)
Pushes an incidence creation onto the undo stack.
bool clear()
Clears the undo and redo stacks.
~History()
Destroys the History instance.
History class for implementing undo/redo of calendar operations.
bool redoAvailable() const
Returns true if there are changes that can be redone.
ResultCode
This enum describes the possible result codes (success/error values) for an undo or redo operation...
QString nextUndoDescription() const
Returns the description of the next undo.
void recordDeletion(const Akonadi::Item &item, const QString &description, const uint atomicOperationId=0)
Pushes an incidence deletion onto the undo stack.
An error occurred. Call lastErrorString() for the error message. This isn't very verbose because Inci...
bool undoAvailable() const
Returns true if there are changes that can be undone.
void changed()
The redo/undo stacks have changed.
void recordDeletions(const Akonadi::Item::List &items, const QString &description, const uint atomicOperationId=0)
Pushes a list of incidence deletions onto the undo stack.
void redo(QWidget *parent=0)
Reverts the change that's on top of the redo stack.
void recordModification(const Akonadi::Item &oldItem, const Akonadi::Item &newItem, const QString &description, const uint atomicOperationId=0)
Pushes an incidence modification onto the undo stack.
void undoAll(QWidget *parent=0)
Reverts every change in the undo stack.
QSharedPointer< X > staticCast() const
QString lastErrorString() const
Returns the last error message.