Commit 8b7dad8f authored by David Jarvie's avatar David Jarvie

Wait for Akonadi collections to be populated before using at startup

Some commands (command line or D-Bus), e.g. --list, don't work
correctly at startup until all enabled Akonadi collections have been
populated. This patch uses new features in EntityTreeModel in 4.10 to
wait for collections to be populated when necessary.
parent bdf93154
......@@ -38,6 +38,7 @@
#include <QMouseEvent>
#include <QHelpEvent>
#include <QToolTip>
#include <QTimer>
#include <QObject>
using namespace Akonadi;
......@@ -279,6 +280,7 @@ CollectionCheckListModel::~CollectionCheckListModel()
}
}
/******************************************************************************
* Return the collection for a given row.
*/
......@@ -656,7 +658,8 @@ CollectionControlModel* CollectionControlModel::instance()
}
CollectionControlModel::CollectionControlModel(QObject* parent)
: FavoriteCollectionsModel(AkonadiModel::instance(), KConfigGroup(KGlobal::config(), "Collections"), parent)
: FavoriteCollectionsModel(AkonadiModel::instance(), KConfigGroup(KGlobal::config(), "Collections"), parent),
mPopulatedCheckLoop(0)
{
// Initialise the list of enabled collections
EntityMimeTypeFilterModel* filter = new EntityMimeTypeFilterModel(this);
......@@ -668,6 +671,12 @@ CollectionControlModel::CollectionControlModel(QObject* parent)
connect(AkonadiModel::instance(), SIGNAL(collectionStatusChanged(Akonadi::Collection,AkonadiModel::Change,QVariant,bool)),
SLOT(statusChanged(Akonadi::Collection,AkonadiModel::Change,QVariant,bool)));
#if KDE_IS_VERSION(4,9,80)
connect(AkonadiModel::instance(), SIGNAL(collectionTreeFetched(Akonadi::Collection::List)),
SLOT(collectionPopulated()));
connect(AkonadiModel::instance(), SIGNAL(collectionPopulated(Akonadi::Collection::Id)),
SLOT(collectionPopulated()));
#endif
}
/******************************************************************************
......@@ -1244,6 +1253,61 @@ Collection CollectionControlModel::collectionForResource(const QString& resource
return Collection();
}
#if KDE_IS_VERSION(4,9,80)
/******************************************************************************
* Return whether all enabled collections have been populated.
*/
bool CollectionControlModel::isPopulated(Collection::Id colId)
{
AkonadiModel* model = AkonadiModel::instance();
Collection::List cols = instance()->collections();
for (int i = 0, count = cols.count(); i < count; ++i)
{
if ((colId == -1 || colId == cols[i].id())
&& !model->data(model->collectionIndex(cols[i].id()), AkonadiModel::IsPopulatedRole).toBool())
{
model->refresh(cols[i]); // update with latest data
if (!cols[i].hasAttribute<CollectionAttribute>()
|| cols[i].attribute<CollectionAttribute>()->enabled() == CalEvent::EMPTY)
return false;
}
}
return true;
}
/******************************************************************************
* Wait for one or all enabled collections to be populated.
* Reply = true if successful.
*/
bool CollectionControlModel::waitUntilPopulated(Collection::Id colId, int timeout)
{
kDebug();
int result = 1;
AkonadiModel* model = AkonadiModel::instance();
while (!model->isCollectionTreeFetched()
|| !isPopulated(colId))
{
if (!mPopulatedCheckLoop)
mPopulatedCheckLoop = new QEventLoop(this);
if (timeout > 0)
QTimer::singleShot(timeout * 1000, mPopulatedCheckLoop, SLOT(quit()));
result = mPopulatedCheckLoop->exec();
}
delete mPopulatedCheckLoop;
mPopulatedCheckLoop = 0;
return result;
}
#endif
/******************************************************************************
* Exit from the populated event loop when a collection has been populated.
*/
void CollectionControlModel::collectionPopulated()
{
if (mPopulatedCheckLoop)
mPopulatedCheckLoop->exit(1);
}
/******************************************************************************
* Return the data for a given role, for a specified item.
*/
......
......@@ -28,12 +28,14 @@
#include <akonadi/favoritecollectionsmodel.h>
#include <kcheckableproxymodel.h>
#include <kdescendantsproxymodel.h>
#include <kdeversion.h>
#include <QSortFilterProxyModel>
#include <QListView>
using namespace KAlarmCal;
class QEventLoop;
namespace Akonadi
{
class EntityMimeTypeFilterModel;
......@@ -174,7 +176,7 @@ class CollectionControlModel : public Akonadi::FavoriteCollectionsModel
/** Enable or disable a collection (if it is valid) for specified alarm types.
* Note that this only changes the status for the specified alarm types.
* \return alarm types which can be enabled
* @return alarm types which can be enabled
*/
static CalEvent::Types setEnabled(const Akonadi::Collection&, CalEvent::Types, bool enabled);
......@@ -259,10 +261,25 @@ class CollectionControlModel : public Akonadi::FavoriteCollectionsModel
static Akonadi::Collection::List enabledCollections(CalEvent::Type, bool writable);
/** Return the collection ID for a given resource ID.
* \return collection ID, or -1 if the resource is not in KAlarm's list.
* @return collection ID, or -1 if the resource is not in KAlarm's list.
*/
static Akonadi::Collection collectionForResource(const QString& resourceId);
#if KDE_IS_VERSION(4,9,80)
/** Return whether one or all enabled collections have been populated,
* i.e. whether their items have been fetched.
*/
static bool isPopulated(Akonadi::Collection::Id);
/** Wait until one or all enabled collections have been populated,
* i.e. whether their items have been fetched.
* @param colId collection ID, or -1 for all collections
* @param timeout timeout in seconds, or 0 for no timeout
* @return true if successful.
*/
bool waitUntilPopulated(Akonadi::Collection::Id colId = -1, int timeout = 0);
#endif
virtual QVariant data(const QModelIndex&, int role = Qt::DisplayRole) const;
/** Return a bulleted list of alarm types for inclusion in an i18n message. */
......@@ -270,6 +287,7 @@ class CollectionControlModel : public Akonadi::FavoriteCollectionsModel
private slots:
void statusChanged(const Akonadi::Collection&, AkonadiModel::Change, const QVariant& value, bool inserted);
void collectionPopulated();
private:
explicit CollectionControlModel(QObject* parent = 0);
......@@ -279,6 +297,7 @@ class CollectionControlModel : public Akonadi::FavoriteCollectionsModel
static CollectionControlModel* mInstance;
static bool mAskDestination;
QEventLoop* mPopulatedCheckLoop;
};
#endif // COLLECTIONMODEL_H
......
......@@ -82,6 +82,10 @@
static const char* KTTSD_DBUS_SERVICE = "org.kde.kttsd";
static const char* KTTDS_DBUS_PATH = "/KSpeech";
#ifdef USE_AKONADI
static const int AKONADI_TIMEOUT = 30; // timeout (seconds) for Akonadi collections to be populated
#endif
static void setEventCommandError(const KAEvent&, KAEvent::CmdErrType);
static void clearEventCommandError(const KAEvent&, KAEvent::CmdErrType);
......@@ -328,10 +332,14 @@ int KAlarmApp::newInstance()
{
// Display or delete the event with the specified event ID
EventFunc function = (command == CommandOptions::TRIGGER_EVENT) ? EVENT_TRIGGER : EVENT_CANCEL;
if (!initCheck(true)) // open the calendar, don't start processing execution queue yet
// Open the calendar, don't start processing execution queue yet,
// and wait for the Akonadi collection to be populated.
#ifdef USE_AKONADI
if (!initCheck(true, true, options.eventId().collectionId()))
#else
if (!initCheck(true))
#endif
exitCode = 1;
else if (!checkResourcesPopulated()) // wait for Akonadi resources to be populated
exitCode = 1; // Akonadi not running
else
{
startProcessQueue(); // start processing the execution queue
......@@ -353,11 +361,15 @@ int KAlarmApp::newInstance()
break;
}
case CommandOptions::LIST:
// Output a list of scheduled alarms to stdout
if (!initCheck(true)) // open the calendar, don't start processing execution queue yet
// Output a list of scheduled alarms to stdout.
// Open the calendar, don't start processing execution queue yet,
// and wait for all Akonadi collections to be populated.
#ifdef USE_AKONADI
if (!initCheck(true, true))
#else
if (!initCheck(true))
#endif
exitCode = 1;
else if (!checkResourcesPopulated()) // wait for Akonadi resources to be populated
exitCode = 1; // Akonadi not running
else
{
dontRedisplay = true;
......@@ -367,8 +379,13 @@ int KAlarmApp::newInstance()
}
break;
case CommandOptions::EDIT:
// Edit a specified existing alarm
// Edit a specified existing alarm.
// Open the calendar and wait for the Akonadi collection to be populated.
#ifdef USE_AKONADI
if (!initCheck(false, true, options.eventId().collectionId()))
#else
if (!initCheck())
#endif
exitCode = 1;
else if (!KAlarm::editAlarmById(options.eventId()))
{
......@@ -2305,7 +2322,11 @@ void KAlarmApp::slotDBusServiceUnregistered(const QString& serviceName)
* If this is the first time through, open the calendar file, and start
* processing the execution queue.
*/
#ifdef USE_AKONADI
bool KAlarmApp::initCheck(bool calendarOnly, bool waitForCollection, Akonadi::Collection::Id collectionId)
#else
bool KAlarmApp::initCheck(bool calendarOnly)
#endif
{
static bool firstTime = true;
if (firstTime)
......@@ -2328,21 +2349,19 @@ bool KAlarmApp::initCheck(bool calendarOnly)
if (!calendarOnly)
startProcessQueue(); // start processing the execution queue
return true;
}
#ifdef USE_AKONADI
/******************************************************************************
* Wait until the Akonadi resources are fully populated with alarms.
*/
bool KAlarmApp::checkResourcesPopulated()
{
#ifdef __GNUC__
#warning Wait for Akonadi resources to be populated
if (waitForCollection)
{
#if KDE_IS_VERSION(4,9,80)
// Wait for one or all Akonadi collections to be populated
if (!CollectionControlModel::instance()->waitUntilPopulated(collectionId, AKONADI_TIMEOUT))
return false;
#endif
return true;
}
}
#endif
return true;
}
/******************************************************************************
* Called when an audio thread starts or stops.
......
......@@ -195,7 +195,11 @@ class KAlarmApp : public KUniqueApplication
KAEvent event;
};
#ifdef USE_AKONADI
bool initCheck(bool calendarOnly = false, bool waitForCollection = false, Akonadi::Collection::Id = -1);
#else
bool initCheck(bool calendarOnly = false);
#endif
bool quitIf(int exitCode, bool force = false);
bool checkSystemTray();
void startProcessQueue();
......@@ -218,9 +222,6 @@ class KAlarmApp : public KUniqueApplication
QString createTempScriptFile(const QString& command, bool insertShell, const KAEvent&, const KAAlarm&) const;
void commandErrorMsg(const ShellProcess*, const KAEvent&, const KAAlarm*, int flags = 0);
void purge(int daysToKeep);
#ifdef USE_AKONADI
bool checkResourcesPopulated();
#endif
QStringList scheduledAlarmList();
static KAlarmApp* theInstance; // the one and only KAlarmApp instance
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment