23#include <kcalutils/stringify.h>
25using namespace KCalCore;
28History::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;
42History::Private::Private(
History *qq) : q(qq)
48 const QString &description,
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,
64 const QString &description,
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);
83 const QString &description,
84 const uint atomicOperationId)
86 Q_ASSERT_X(item.isValid(),
"History::recordDeletion",
"Item must be valid");
93 const QString &description,
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;
178void History::Private::updateIds(Item::Id oldId, Item::Id newId)
180 mEntryInProgress->updateIds(oldId, newId);
182 foreach(
const Entry::Ptr &entry, mUndoStack)
183 entry->updateIds(oldId, newId);
185 foreach(
const Entry::Ptr &entry, mRedoStack)
186 entry->updateIds(oldId, newId);
189void 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);
202void History::Private::handleFinished(IncidenceChanger::ResultCode changerResult,
203 const QString &errorString)
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;
237void History::Private::stackEntry(
const Entry::Ptr &entry, uint atomicOperationId)
239 const bool useMultiEntry = (atomicOperationId > 0);
241 Entry::Ptr entryToPush;
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) {
251 MultiEntry::Ptr multiEntry = topEntry.staticCast<MultiEntry>();
252 if (multiEntry->mAtomicOperationId != atomicOperationId) {
253 multiEntry = MultiEntry::Ptr(
new MultiEntry(atomicOperationId, entry->mDescription, q));
254 entryToPush = multiEntry;
256 multiEntry->addEntry(entry);
258 MultiEntry::Ptr multiEntry = MultiEntry::Ptr(
new MultiEntry(atomicOperationId,
259 entry->mDescription, q));
260 multiEntry->addEntry(entry);
261 entryToPush = multiEntry;
267 if (mOperationTypeInProgress == TypeNone) {
269 mUndoStack.push(entryToPush);
275 mQueuedEntries.append(entryToPush);
280void 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.";
297QStack<Entry::Ptr>& History::Private::stack(OperationType type)
300 return type == TypeUndo ? mUndoStack : mRedoStack;
303void History::Private::setEnabled(
bool enabled)
305 if (enabled != mEnabled) {
310int History::Private::redoCount()
const
312 return mRedoStack.count();
315int History::Private::undoCount()
const
317 return mUndoStack.count();
320QStack<Entry::Ptr>& History::Private::stack()
322 return stack(mOperationTypeInProgress);
325QStack<Entry::Ptr>& History::Private::destinationStack()
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"
History class for implementing undo/redo of calendar operations.
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 recordDeletion(const Akonadi::Item &item, const QString &description, const uint atomicOperationId=0)
Pushes an incidence deletion onto the undo stack.
void changed()
The redo/undo stacks have changed.
QString nextUndoDescription() const
Returns the description of the next undo.
~History()
Destroys the History instance.
bool redoAvailable() const
Returns true if there are changes that can be redone.
void undoAll(QWidget *parent=0)
Reverts every change in the undo stack.
bool clear()
Clears the undo and redo stacks.
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.
QString nextRedoDescription() const
Returns the description of the next redo.
bool undoAvailable() const
Returns true if there are changes that can be undone.
QString lastErrorString() const
Returns the last error message.
ResultCode
This enum describes the possible result codes (success/error values) for an undo or redo operation.
@ ResultCodeError
An error occurred. Call lastErrorString() for the error message. This isn't very verbose because Inci...
@ ResultCodeSuccess
Success.
void redo(QWidget *parent=0)
Reverts the change that's on top of the redo stack.
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.
FreeBusyManager::Singleton.