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 )
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>(),
75 description,
this ) );
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 ) {
99 Q_ASSERT_X( item.isValid(),
100 "History::recordDeletion()",
"Item must be valid." );
101 Q_ASSERT_X( item.hasPayload<Incidence::Ptr>(),
102 "History::recordDeletion()",
"Item must have an Incidence::Ptr payload." );
105 d->stackEntry( entry, atomicOperationId );
110 if ( !d->mUndoStack.isEmpty() )
111 return d->mUndoStack.top()->mDescription;
118 if ( !d->mRedoStack.isEmpty() )
119 return d->mRedoStack.top()->mDescription;
126 d->undoOrRedo( TypeUndo, parent );
131 d->undoOrRedo( TypeRedo, parent );
136 if ( d->mOperationTypeInProgress != TypeNone ) {
137 kWarning() <<
"Dont call History::undoAll() while an undo/redo/undoAll is in progress";
138 }
else if ( d->mEnabled ) {
139 d->mUndoAllInProgress =
true;
140 d->mCurrentParent = parent;
143 kWarning() <<
"Don't call undo/redo when History is disabled";
150 if ( d->mOperationTypeInProgress == TypeNone ) {
151 d->mRedoStack.clear();
152 d->mUndoStack.clear();
153 d->mLastErrorString.clear();
154 d->mQueuedEntries.clear();
164 return d->mLastErrorString;
167 void History::setEnabled(
bool enabled )
169 if ( enabled != d->mEnabled ) {
170 d->mEnabled = enabled;
176 return !d->mUndoStack.isEmpty() && d->mOperationTypeInProgress == TypeNone;
181 return !d->mRedoStack.isEmpty() && d->mOperationTypeInProgress == TypeNone;
184 void History::Private::updateIds( Item::Id oldId, Item::Id newId )
186 mEntryInProgress->updateIds( oldId, newId );
188 foreach(
const Entry::Ptr &entry, mUndoStack )
189 entry->updateIds( oldId, newId );
191 foreach(
const Entry::Ptr &entry, mRedoStack )
192 entry->updateIds( oldId, newId );
195 void History::Private::doIt( OperationType type )
197 mOperationTypeInProgress = type;
199 Q_ASSERT( !stack().isEmpty() );
200 mEntryInProgress = stack().pop();
202 connect( mEntryInProgress.data(), SIGNAL(finished(Akonadi::IncidenceChanger::ResultCode,QString)),
203 SLOT(handleFinished(Akonadi::IncidenceChanger::ResultCode,QString)),
204 Qt::UniqueConnection);
205 mEntryInProgress->doIt( type );
208 void History::Private::handleFinished( IncidenceChanger::ResultCode changerResult,
209 const QString &errorString )
211 Q_ASSERT( mOperationTypeInProgress != TypeNone );
212 Q_ASSERT( !( mUndoAllInProgress && mOperationTypeInProgress == TypeRedo ) );
214 const bool success = ( changerResult == IncidenceChanger::ResultCodeSuccess );
219 mLastErrorString.clear();
220 destinationStack().push( mEntryInProgress );
222 mLastErrorString = errorString;
223 stack().push( mEntryInProgress );
230 if ( !mQueuedEntries.isEmpty() ) {
232 foreach(
const Entry::Ptr &entry, mQueuedEntries ) {
233 mUndoStack.push( entry );
235 mQueuedEntries.clear();
238 emitDone( mOperationTypeInProgress, resultCode );
239 mOperationTypeInProgress = TypeNone;
243 void History::Private::stackEntry(
const Entry::Ptr &entry, uint atomicOperationId )
245 const bool useMultiEntry = ( atomicOperationId > 0 );
247 Entry::Ptr entryToPush;
249 if ( useMultiEntry ) {
250 Entry::Ptr topEntry = ( mOperationTypeInProgress == TypeNone ) ?
251 ( mUndoStack.isEmpty() ? Entry::Ptr() : mUndoStack.top() ) :
252 ( mQueuedEntries.isEmpty() ? Entry::Ptr() : mQueuedEntries.last() );
254 const bool topIsMultiEntry = qobject_cast<MultiEntry*>( topEntry.data() );
256 if ( topIsMultiEntry ) {
257 MultiEntry::Ptr multiEntry = topEntry.staticCast<MultiEntry>();
258 if ( multiEntry->mAtomicOperationId != atomicOperationId ) {
259 multiEntry = MultiEntry::Ptr(
new MultiEntry( atomicOperationId, entry->mDescription, q ) );
260 entryToPush = multiEntry;
262 multiEntry->addEntry( entry );
264 MultiEntry::Ptr multiEntry = MultiEntry::Ptr(
new MultiEntry( atomicOperationId,
265 entry->mDescription, q ) );
266 multiEntry->addEntry( entry );
267 entryToPush = multiEntry;
273 if ( mOperationTypeInProgress == TypeNone ) {
275 mUndoStack.push( entryToPush );
281 mQueuedEntries.append( entryToPush );
286 void History::Private::undoOrRedo( OperationType type, QWidget *parent )
289 Q_ASSERT( mOperationTypeInProgress == TypeNone );
291 if ( !stack( type ).isEmpty() ) {
293 mCurrentParent = parent;
296 kWarning() <<
"Don't call undo/redo when History is disabled";
299 kWarning() <<
"Don't call undo/redo when the stack is empty.";
303 QStack<Entry::Ptr>& History::Private::stack( OperationType type )
306 return type == TypeUndo ? mUndoStack : mRedoStack;
309 QStack<Entry::Ptr>& History::Private::stack()
311 return stack( mOperationTypeInProgress );
314 QStack<Entry::Ptr>& History::Private::destinationStack()
317 return mOperationTypeInProgress == TypeRedo ? mUndoStack : mRedoStack;
322 if ( type == TypeUndo ) {
323 emit q->undone( resultCode );
324 }
else if ( type == TypeRedo ){
325 emit q->redone( resultCode );
331 #include "history.moc"
332 #include "history_p.moc"
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.
QString lastErrorString() const
Returns the last error message.