• Skip to content
  • Skip to link menu
  • KDE API Reference
  • kdepimlibs-4.14.10 API Reference
  • KDE Home
  • Contact Us
 

akonadi

  • akonadi
  • calendar
history.cpp
1/*
2 Copyright (C) 2010-2012 Sérgio Martins <iamsergio@gmail.com>
3
4 This library is free software; you can redistribute it and/or modify it
5 under the terms of the GNU Library General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or (at your
7 option) any later version.
8
9 This library is distributed in the hope that it will be useful, but WITHOUT
10 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
12 License for more details.
13
14 You should have received a copy of the GNU Library General Public License
15 along with this library; see the file COPYING.LIB. If not, write to the
16 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17 02110-1301, USA.
18*/
19
20#include "history.h"
21#include "history_p.h"
22
23#include <kcalutils/stringify.h>
24
25using namespace KCalCore;
26using namespace Akonadi;
27
28History::History(QObject *parent) : QObject(parent), d(new Private(this))
29{
30 d->mChanger = new IncidenceChanger(/*history=*/false, this);
31 d->mChanger->setObjectName("changer"); // for auto-connects
32 d->mOperationTypeInProgress = TypeNone;
33 d->mEnabled = true;
34 d->mUndoAllInProgress = false;
35}
36
37History::~History()
38{
39 delete d;
40}
41
42History::Private::Private(History *qq) : q(qq)
43{
44
45}
46
47void History::recordCreation(const Akonadi::Item &item,
48 const QString &description,
49 const uint atomicOperationId)
50{
51 Q_ASSERT_X(item.isValid(), "History::recordCreation()",
52 "Item must be valid.");
53
54 Q_ASSERT_X(item.hasPayload<KCalCore::Incidence::Ptr>(), "History::recordCreation()",
55 "Item must have Incidence::Ptr payload.");
56
57 Entry::Ptr entry(new CreationEntry(item, description, this));
58
59 d->stackEntry(entry, atomicOperationId);
60}
61
62void History::recordModification(const Akonadi::Item &oldItem,
63 const Akonadi::Item &newItem,
64 const QString &description,
65 const uint atomicOperationId)
66{
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");
73
74 Entry::Ptr entry(new ModificationEntry(newItem, oldItem.payload<KCalCore::Incidence::Ptr>(),
75 description, this));
76
77 Q_ASSERT(newItem.revision() >= oldItem.revision());
78
79 d->stackEntry(entry, atomicOperationId);
80}
81
82void History::recordDeletion(const Akonadi::Item &item,
83 const QString &description,
84 const uint atomicOperationId)
85{
86 Q_ASSERT_X(item.isValid(), "History::recordDeletion", "Item must be valid");
87 Item::List list;
88 list.append(item);
89 recordDeletions(list, description, atomicOperationId);
90}
91
92void History::recordDeletions(const Akonadi::Item::List &items,
93 const QString &description,
94 const uint atomicOperationId)
95{
96 Entry::Ptr entry(new DeletionEntry(items, description, this));
97
98 foreach(const Akonadi::Item &item, items) {
99 Q_UNUSED(item);
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.");
104 }
105
106 d->stackEntry(entry, atomicOperationId);
107}
108
109QString History::nextUndoDescription() const
110{
111 if (!d->mUndoStack.isEmpty())
112 return d->mUndoStack.top()->mDescription;
113 else
114 return QString();
115}
116
117QString History::nextRedoDescription() const
118{
119 if (!d->mRedoStack.isEmpty())
120 return d->mRedoStack.top()->mDescription;
121 else
122 return QString();
123}
124
125void History::undo(QWidget *parent)
126{
127 d->undoOrRedo(TypeUndo, parent);
128}
129
130void History::redo(QWidget *parent)
131{
132 d->undoOrRedo(TypeRedo, parent);
133}
134
135void History::undoAll(QWidget *parent)
136{
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;
142 d->doIt(TypeUndo);
143 } else {
144 kWarning() << "Don't call undo/redo when History is disabled";
145 }
146}
147
148bool History::clear()
149{
150 bool result = true;
151 if (d->mOperationTypeInProgress == TypeNone) {
152 d->mRedoStack.clear();
153 d->mUndoStack.clear();
154 d->mLastErrorString.clear();
155 d->mQueuedEntries.clear();
156 } else {
157 result = false;
158 }
159 emit changed();
160 return result;
161}
162
163QString History::lastErrorString() const
164{
165 return d->mLastErrorString;
166}
167
168bool History::undoAvailable() const
169{
170 return !d->mUndoStack.isEmpty() && d->mOperationTypeInProgress == TypeNone;
171}
172
173bool History::redoAvailable() const
174{
175 return !d->mRedoStack.isEmpty() && d->mOperationTypeInProgress == TypeNone;
176}
177
178void History::Private::updateIds(Item::Id oldId, Item::Id newId)
179{
180 mEntryInProgress->updateIds(oldId, newId);
181
182 foreach(const Entry::Ptr &entry, mUndoStack)
183 entry->updateIds(oldId, newId);
184
185 foreach(const Entry::Ptr &entry, mRedoStack)
186 entry->updateIds(oldId, newId);
187}
188
189void History::Private::doIt(OperationType type)
190{
191 mOperationTypeInProgress = type;
192 emit q->changed(); // Application will disable undo/redo buttons because operation is in progress
193 Q_ASSERT(!stack().isEmpty());
194 mEntryInProgress = stack().pop();
195
196 connect(mEntryInProgress.data(), SIGNAL(finished(Akonadi::IncidenceChanger::ResultCode,QString)),
197 SLOT(handleFinished(Akonadi::IncidenceChanger::ResultCode,QString)),
198 Qt::UniqueConnection);
199 mEntryInProgress->doIt(type);
200}
201
202void History::Private::handleFinished(IncidenceChanger::ResultCode changerResult,
203 const QString &errorString)
204{
205 Q_ASSERT(mOperationTypeInProgress != TypeNone);
206 Q_ASSERT(!(mUndoAllInProgress && mOperationTypeInProgress == TypeRedo));
207
208 const bool success = (changerResult == IncidenceChanger::ResultCodeSuccess);
209 const History::ResultCode resultCode = success ? History::ResultCodeSuccess :
210 History::ResultCodeError;
211
212 if (success) {
213 mLastErrorString.clear();
214 destinationStack().push(mEntryInProgress);
215 } else {
216 mLastErrorString = errorString;
217 stack().push(mEntryInProgress);
218 }
219
220 mCurrentParent = 0;
221
222 // Process recordCreation/Modification/Deletions that came in while an operation
223 // was in progress
224 if (!mQueuedEntries.isEmpty()) {
225 mRedoStack.clear();
226 foreach(const Entry::Ptr &entry, mQueuedEntries) {
227 mUndoStack.push(entry);
228 }
229 mQueuedEntries.clear();
230 }
231
232 emitDone(mOperationTypeInProgress, resultCode);
233 mOperationTypeInProgress = TypeNone;
234 emit q->changed();
235}
236
237void History::Private::stackEntry(const Entry::Ptr &entry, uint atomicOperationId)
238{
239 const bool useMultiEntry = (atomicOperationId > 0);
240
241 Entry::Ptr entryToPush;
242
243 if (useMultiEntry) {
244 Entry::Ptr topEntry = (mOperationTypeInProgress == TypeNone) ?
245 (mUndoStack.isEmpty() ? Entry::Ptr() : mUndoStack.top()) :
246 (mQueuedEntries.isEmpty() ? Entry::Ptr() : mQueuedEntries.last());
247
248 const bool topIsMultiEntry = qobject_cast<MultiEntry*>(topEntry.data());
249
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;
255 }
256 multiEntry->addEntry(entry);
257 } else {
258 MultiEntry::Ptr multiEntry = MultiEntry::Ptr(new MultiEntry(atomicOperationId,
259 entry->mDescription, q));
260 multiEntry->addEntry(entry);
261 entryToPush = multiEntry;
262 }
263 } else {
264 entryToPush = entry;
265 }
266
267 if (mOperationTypeInProgress == TypeNone) {
268 if (entryToPush) {
269 mUndoStack.push(entryToPush);
270 }
271 mRedoStack.clear();
272 emit q->changed();
273 } else {
274 if (entryToPush) {
275 mQueuedEntries.append(entryToPush);
276 }
277 }
278}
279
280void History::Private::undoOrRedo(OperationType type, QWidget *parent)
281{
282 // Don't call undo() without the previous one finishing
283 Q_ASSERT(mOperationTypeInProgress == TypeNone);
284
285 if (!stack(type).isEmpty()) {
286 if (mEnabled) {
287 mCurrentParent = parent;
288 doIt(type);
289 } else {
290 kWarning() << "Don't call undo/redo when History is disabled";
291 }
292 } else {
293 kWarning() << "Don't call undo/redo when the stack is empty.";
294 }
295}
296
297QStack<Entry::Ptr>& History::Private::stack(OperationType type)
298{
299 // Entries from the undo stack go to the redo stack, and vice-versa
300 return type == TypeUndo ? mUndoStack : mRedoStack;
301}
302
303void History::Private::setEnabled(bool enabled)
304{
305 if (enabled != mEnabled) {
306 mEnabled = enabled;
307 }
308}
309
310int History::Private::redoCount() const
311{
312 return mRedoStack.count();
313}
314
315int History::Private::undoCount() const
316{
317 return mUndoStack.count();
318}
319
320QStack<Entry::Ptr>& History::Private::stack()
321{
322 return stack(mOperationTypeInProgress);
323}
324
325QStack<Entry::Ptr>& History::Private::destinationStack()
326{
327 // Entries from the undo stack go to the redo stack, and vice-versa
328 return mOperationTypeInProgress == TypeRedo ? mUndoStack : mRedoStack;
329}
330
331void History::Private::emitDone(OperationType type, History::ResultCode resultCode)
332{
333 if (type == TypeUndo) {
334 emit q->undone(resultCode);
335 } else if (type == TypeRedo) {
336 emit q->redone(resultCode);
337 } else {
338 Q_ASSERT(false);
339 }
340}
341
342#include "moc_history.cpp"
343#include "moc_history_p.cpp"
Akonadi::History
History class for implementing undo/redo of calendar operations.
Definition: history.h:58
Akonadi::History::recordDeletions
void recordDeletions(const Akonadi::Item::List &items, const QString &description, const uint atomicOperationId=0)
Pushes a list of incidence deletions onto the undo stack.
Definition: history.cpp:92
Akonadi::History::recordDeletion
void recordDeletion(const Akonadi::Item &item, const QString &description, const uint atomicOperationId=0)
Pushes an incidence deletion onto the undo stack.
Definition: history.cpp:82
Akonadi::History::changed
void changed()
The redo/undo stacks have changed.
Akonadi::History::nextUndoDescription
QString nextUndoDescription() const
Returns the description of the next undo.
Definition: history.cpp:109
Akonadi::History::~History
~History()
Destroys the History instance.
Definition: history.cpp:37
Akonadi::History::redoAvailable
bool redoAvailable() const
Returns true if there are changes that can be redone.
Definition: history.cpp:173
Akonadi::History::undoAll
void undoAll(QWidget *parent=0)
Reverts every change in the undo stack.
Definition: history.cpp:135
Akonadi::History::clear
bool clear()
Clears the undo and redo stacks.
Definition: history.cpp:148
Akonadi::History::recordModification
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.
Definition: history.cpp:62
Akonadi::History::nextRedoDescription
QString nextRedoDescription() const
Returns the description of the next redo.
Definition: history.cpp:117
Akonadi::History::undoAvailable
bool undoAvailable() const
Returns true if there are changes that can be undone.
Definition: history.cpp:168
Akonadi::History::lastErrorString
QString lastErrorString() const
Returns the last error message.
Definition: history.cpp:163
Akonadi::History::ResultCode
ResultCode
This enum describes the possible result codes (success/error values) for an undo or redo operation.
Definition: history.h:67
Akonadi::History::ResultCodeError
@ ResultCodeError
An error occurred. Call lastErrorString() for the error message. This isn't very verbose because Inci...
Definition: history.h:69
Akonadi::History::ResultCodeSuccess
@ ResultCodeSuccess
Success.
Definition: history.h:68
Akonadi::History::redo
void redo(QWidget *parent=0)
Reverts the change that's on top of the redo stack.
Definition: history.cpp:130
Akonadi::History::undo
void undo(QWidget *parent=0)
Reverts the change that's on top of the undo stack.
Definition: history.cpp:125
Akonadi::History::recordCreation
void recordCreation(const Akonadi::Item &item, const QString &description, const uint atomicOperationId=0)
Pushes an incidence creation onto the undo stack.
Definition: history.cpp:47
Akonadi
FreeBusyManager::Singleton.
Definition: actionstatemanager_p.h:28
This file is part of the KDE documentation.
Documentation copyright © 1996-2022 The KDE developers.
Generated on Thu Jul 21 2022 00:00:00 by doxygen 1.9.5 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

akonadi

Skip menu "akonadi"
  • Main Page
  • Namespace List
  • Namespace Members
  • Alphabetical List
  • Class List
  • Class Hierarchy
  • Class Members
  • File List
  • Modules
  • Related Pages

kdepimlibs-4.14.10 API Reference

Skip menu "kdepimlibs-4.14.10 API Reference"
  • akonadi
  •   contact
  •   kmime
  •   socialutils
  • kabc
  • kalarmcal
  • kblog
  • kcal
  • kcalcore
  • kcalutils
  • kholidays
  • kimap
  • kioslave
  •   imap4
  •   mbox
  •   nntp
  • kldap
  • kmbox
  • kmime
  • kontactinterface
  • kpimidentities
  • kpimtextedit
  • kpimutils
  • kresources
  • ktnef
  • kxmlrpcclient
  • mailtransport
  • microblog
  • qgpgme
  • syndication
  •   atom
  •   rdf
  •   rss2
Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal