kde-playground/kdepimlibs/akonadi/calendar/incidencechanger_p.h
Ivailo Monev a9b00154bc generic: replace forward class declarations with header inclusions
Signed-off-by: Ivailo Monev <xakepa10@laimg.moc>
2019-07-31 18:05:48 +00:00

359 lines
11 KiB
C++

/*
Copyright (C) 2004 Reinhold Kainhofer <reinhold@kainhofer.com>
Copyright (C) 2010 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.net
Author: Sergio Martins, <sergio.martins@kdab.com>
Copyright (C) 2010-2012 Sérgio Martins <iamsergio@gmail.com>
This library is free software; you can redistribute it and/or modify it
under the terms of the GNU Library General Public License as published by
the Free Software Foundation; either version 2 of the License, or (at your
option) any later version.
This library is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to the
Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301, USA.
*/
#ifndef AKONADI_INCIDENCECHANGER_P_H
#define AKONADI_INCIDENCECHANGER_P_H
#include "incidencechanger.h"
#include "itiphandlerhelper_p.h"
#include "history.h"
#include <akonadi/item.h>
#include <akonadi/collection.h>
#include <akonadi/transactionsequence.h>
#include <QSet>
#include <QObject>
#include <QPointer>
#include <QVector>
class KJob;
#include <QWidget>
namespace Akonadi {
class TransactionSequence;
class CollectionFetchJob;
class Change {
public:
typedef QSharedPointer<Change> Ptr;
typedef QList<Ptr> List;
Change(IncidenceChanger *incidenceChanger, int changeId,
IncidenceChanger::ChangeType changeType, uint operationId,
QWidget *parent) : id(changeId)
, type(changeType)
, recordToHistory(incidenceChanger->historyEnabled())
, parentWidget(parent)
, atomicOperationId(operationId)
, resultCode(Akonadi::IncidenceChanger::ResultCodeSuccess)
, completed(false)
, queuedModification(false)
, useGroupwareCommunication(incidenceChanger->groupwareCommunication())
, changer(incidenceChanger)
{
}
virtual ~Change()
{
if (parentChange) {
parentChange->childAboutToDie(this);
}
}
virtual void childAboutToDie(Change *child)
{
Q_UNUSED(child);
}
virtual void emitCompletionSignal() = 0;
const int id;
const IncidenceChanger::ChangeType type;
const bool recordToHistory;
const QPointer<QWidget> parentWidget;
uint atomicOperationId;
// If this change is internal, i.e. not initiated by the user, mParentChange will
// contain the non-internal change.
QSharedPointer<Change> parentChange;
Akonadi::Item::List originalItems;
Akonadi::Item newItem;
QString errorString;
IncidenceChanger::ResultCode resultCode;
bool completed;
bool queuedModification;
bool useGroupwareCommunication;
protected:
IncidenceChanger *const changer;
};
class ModificationChange : public Change
{
public:
typedef QSharedPointer<ModificationChange> Ptr;
ModificationChange(IncidenceChanger *changer, int id, uint atomicOperationId,
QWidget *parent) : Change(changer, id,
IncidenceChanger::ChangeTypeModify,
atomicOperationId, parent)
{
}
~ModificationChange()
{
if (!parentChange)
emitCompletionSignal();
}
/**reimp*/
void emitCompletionSignal();
};
class CreationChange : public Change
{
public:
typedef QSharedPointer<CreationChange> Ptr;
CreationChange(IncidenceChanger *changer, int id, uint atomicOperationId,
QWidget *parent) : Change(changer, id, IncidenceChanger::ChangeTypeCreate,
atomicOperationId, parent)
{
}
~CreationChange()
{
//kDebug() << "CreationChange::~ will emit signal with " << resultCode;
if (!parentChange)
emitCompletionSignal();
}
/**reimp*/
void emitCompletionSignal();
Akonadi::Collection mUsedCol1lection;
};
class DeletionChange : public Change
{
public:
typedef QSharedPointer<DeletionChange> Ptr;
DeletionChange(IncidenceChanger *changer, int id, uint atomicOperationId,
QWidget *parent) : Change(changer, id, IncidenceChanger::ChangeTypeDelete,
atomicOperationId, parent)
{
}
~DeletionChange()
{
//kDebug() << "DeletionChange::~ will emit signal with " << resultCode;
if (!parentChange)
emitCompletionSignal();
}
/**reimp*/
void emitCompletionSignal();
QVector<Akonadi::Item::Id> mItemIds;
};
class AtomicOperation {
public:
uint m_id;
// To make sure they are not repeated
QSet<Akonadi::Item::Id> m_itemIdsInOperation;
// After endAtomicOperation() is called we don't accept more changes
bool m_endCalled;
// Number of completed changes(jobs)
int m_numCompletedChanges;
QString m_description;
bool m_transactionCompleted;
AtomicOperation(IncidenceChanger::Private *icp, uint ident);
~AtomicOperation()
{
//kDebug() << "AtomicOperation::~ " << wasRolledback << changes.count();
if (m_wasRolledback) {
for (int i=0; i<m_changes.count(); ++i) {
// When a job that can finish successfully is aborted because the transaction failed
// because of some other job, akonadi is returning an Unknown error
// which isnt very specific
if (m_changes[i]->completed &&
(m_changes[i]->resultCode == IncidenceChanger::ResultCodeSuccess ||
(m_changes[i]->resultCode == IncidenceChanger::ResultCodeJobError &&
m_changes[i]->errorString == QLatin1String("Unknown error.")))) {
m_changes[i]->resultCode = IncidenceChanger::ResultCodeRolledback;
}
}
}
}
// Did all jobs return ?
bool pendingJobs() const
{
return m_changes.count() > m_numCompletedChanges;
}
void setRolledback()
{
//kDebug() << "AtomicOperation::setRolledBack()";
m_wasRolledback = true;
transaction()->rollback();
}
bool rolledback() const
{
return m_wasRolledback;
}
void addChange(const Change::Ptr &change)
{
if (change->type == IncidenceChanger::ChangeTypeDelete) {
DeletionChange::Ptr deletion = change.staticCast<DeletionChange>();
foreach(Akonadi::Item::Id id, deletion->mItemIds) {
Q_ASSERT(!m_itemIdsInOperation.contains(id));
m_itemIdsInOperation.insert(id);
}
} else if (change->type == IncidenceChanger::ChangeTypeModify) {
Q_ASSERT(!m_itemIdsInOperation.contains(change->newItem.id()));
m_itemIdsInOperation.insert(change->newItem.id());
}
m_changes << change;
}
Akonadi::TransactionSequence *transaction();
private:
QVector<Change::Ptr> m_changes;
bool m_wasRolledback;
Akonadi::TransactionSequence *m_transaction; // constructed in first use
IncidenceChanger::Private *m_incidenceChangerPrivate;
};
class IncidenceChanger::Private : public QObject
{
Q_OBJECT
public:
explicit Private(bool enableHistory, IncidenceChanger *mIncidenceChanger);
~Private();
void loadCollections(); // async-loading of list of writable collections
bool isLoadingCollections() const;
Collection::List collectionsForMimeType(const QString &mimeType, const Collection::List &collections);
// steps for the async operation:
void step1DetermineDestinationCollection(const Change::Ptr &change, const Collection &collection);
void step2CreateIncidence(const Change::Ptr &change, const Collection &collection);
/**
Returns true if, for a specific item, an ItemDeleteJob is already running,
or if one already run successfully.
*/
bool deleteAlreadyCalled(Akonadi::Item::Id id) const;
QString showErrorDialog(Akonadi::IncidenceChanger::ResultCode, QWidget *parent);
void setChangeInternal(int changeId);
void adjustRecurrence(const KCalCore::Incidence::Ptr &originalIncidence,
const KCalCore::Incidence::Ptr &incidence);
bool hasRights(const Akonadi::Collection &collection, IncidenceChanger::ChangeType) const;
void queueModification(Change::Ptr);
void performModification(Change::Ptr);
bool atomicOperationIsValid(uint atomicOperationId) const;
Akonadi::Job* parentJob(const Change::Ptr &) const;
void cancelTransaction();
void cleanupTransaction();
bool allowAtomicOperation(int atomicOperationId, const Change::Ptr &change) const;
bool handleInvitationsBeforeChange(const Change::Ptr &change);
bool handleInvitationsAfterChange(const Change::Ptr &change);
static bool myAttendeeStatusChanged(const KCalCore::Incidence::Ptr &newIncidence,
const KCalCore::Incidence::Ptr &oldIncidence,
const QStringList &myEmails);
public Q_SLOTS:
void handleCreateJobResult(KJob*);
void handleModifyJobResult(KJob*);
void handleDeleteJobResult(KJob*);
void handleTransactionJobResult(KJob*);
void performNextModification(Akonadi::Item::Id id);
void onCollectionsLoaded(KJob*);
public:
int mLatestChangeId;
QHash<const KJob*,Change::Ptr> mChangeForJob;
bool mShowDialogsOnError;
Akonadi::Collection mDefaultCollection;
DestinationPolicy mDestinationPolicy;
QVector<Akonadi::Item::Id> mDeletedItemIds;
Change::List mPendingCreations; // Creations waiting for collections to be loaded
History *mHistory;
bool mUseHistory;
/**
Queue modifications by ID. We can only send a modification to akonadi when the previous
one ended.
The container doesn't look like a queue because of an optimization: if there's a modification
A in progress, a modification B waiting (queued), and then a new one C comes in,
we just discard B, and queue C. The queue always has 1 element max.
*/
QHash<Akonadi::Item::Id,Change::Ptr> mQueuedModifications;
/**
So we know if there's already a modification in progress
*/
QHash<Akonadi::Item::Id,Change::Ptr> mModificationsInProgress;
QHash<int,Change::Ptr> mChangeById;
/**
Indexed by atomic operation id.
*/
QHash<uint,AtomicOperation*> mAtomicOperations;
bool mRespectsCollectionRights;
bool mGroupwareCommunication;
QHash<Akonadi::TransactionSequence*, uint> mAtomicOperationByTransaction;
QHash<uint,ITIPHandlerHelper::SendResult> mInvitationStatusByAtomicOperation;
uint mLatestAtomicOperationId;
bool mBatchOperationInProgress;
Akonadi::Collection mLastCollectionUsed;
bool mAutoAdjustRecurrence;
Akonadi::CollectionFetchJob *m_collectionFetchJob;
QMap<KJob *, QSet<KCalCore::IncidenceBase::Field> > mDirtyFieldsByJob;
IncidenceChanger::InvitationPolicy m_invitationPolicy;
private:
IncidenceChanger *q;
};
}
#endif