Commit 6036aaa4 authored by David Jarvie's avatar David Jarvie
Browse files

Rationalise processing of command line options needing calendar access

Use the general action queue to carry out command line options, which
eliminates the need for a special event loop to wait for calendars to
become available.
parent e03135a9
......@@ -418,23 +418,18 @@ int KAlarmApp::activateInstance(const QStringList& args, const QString& workingD
case CommandOptions::CANCEL_EVENT:
{
// Display or delete the event with the specified event ID
const EventFunc function = (command == CommandOptions::TRIGGER_EVENT) ? EVENT_TRIGGER : EVENT_CANCEL;
const QueuedAction action = static_cast<QueuedAction>(int((command == CommandOptions::TRIGGER_EVENT) ? QueuedAction::Trigger : QueuedAction::Cancel)
| int(QueuedAction::FindId) | int(QueuedAction::Exit));
// Open the calendar, don't start processing execution queue yet,
// and wait for the calendar resources to be populated.
if (!initCheck(true)
|| !waitUntilPopulated(options->eventId().resourceId(), AKONADI_TIMEOUT))
if (!initCheck(true))
exitCode = 1;
else
{
mCommandOption = options->commandName();
mActionQueue.enqueue(ActionQEntry(action, options->eventId()));
startProcessQueue(); // start processing the execution queue
dontRedisplay = true;
if (!handleEvent(options->eventId(), function, true))
{
CommandOptions::printError(xi18nc("@info:shell", "%1: Event <resource>%2</resource> not found, or not unique", QStringLiteral("--") + options->commandName(), options->eventId().eventId()));
exitCode = 1;
}
else
createOnlyMainWindow(); // prevent the application from quitting
}
break;
}
......@@ -444,15 +439,14 @@ int KAlarmApp::activateInstance(const QStringList& args, const QString& workingD
// and wait for all calendar resources to be populated.
mReadOnly = true; // don't need write access to calendars
mAlarmsEnabled = false; // prevent alarms being processed
if (!initCheck(true)
|| !waitUntilPopulated(-1, AKONADI_TIMEOUT))
if (!initCheck(true))
exitCode = 1;
else
{
const QueuedAction action = static_cast<QueuedAction>(int(QueuedAction::List) | int(QueuedAction::Exit));
mActionQueue.enqueue(ActionQEntry(action, EventId()));
startProcessQueue(); // start processing the execution queue
dontRedisplay = true;
const QStringList alarms = scheduledAlarmList();
for (const QString& alarm : alarms)
std::cout << alarm.toUtf8().constData() << std::endl;
}
break;
......@@ -466,7 +460,7 @@ int KAlarmApp::activateInstance(const QStringList& args, const QString& workingD
{
if (!KAlarm::editAlarmById(options->eventId()))
{
CommandOptions::printError(xi18nc("@info:shell", "%1: Event <resource>%2</resource> not found, or not editable", QStringLiteral("--") + options->commandName(), options->eventId().eventId()));
CommandOptions::printError(xi18nc("@info:shell", "%1: Event <resource>%2</resource> not found, or not editable", options->commandName(), options->eventId().eventId()));
exitCode = 1;
}
else
......@@ -748,6 +742,7 @@ bool KAlarmApp::quitIf(int exitCode, bool force)
mAlarmTimer = nullptr;
mInitialised = false; // prevent processQueue() from running
AlarmCalendar::terminateCalendars();
DataModel::terminate();
exit(exitCode);
return true; // sometimes we actually get to here, despite calling exit()
}
......@@ -902,10 +897,10 @@ void KAlarmApp::queueAlarmId(const KAEvent& event)
const EventId id(event);
for (const ActionQEntry& entry : qAsConst(mActionQueue))
{
if (entry.function == EVENT_HANDLE && entry.eventId == id)
if (entry.action == QueuedAction::Handle && entry.eventId == id)
return; // the alarm is already queued
}
mActionQueue.enqueue(ActionQEntry(EVENT_HANDLE, id));
mActionQueue.enqueue(ActionQEntry(QueuedAction::Handle, id));
}
/******************************************************************************
......@@ -945,35 +940,60 @@ void KAlarmApp::processQueue()
// Process queued events
while (!mActionQueue.isEmpty())
{
bool removeFromQueue = true;
// Can't add a new event until resources have been populated.
if (!Resources::allPopulated())
{
// If resource population has timed out, discard all queued events.
if (mResourcesTimedOut)
{
qCCritical(KALARM_LOG) << "Error! Timeout reading calendars";
mActionQueue.clear();
}
break;
}
ActionQEntry& entry = mActionQueue.head();
const bool findUniqueId = int(entry.action) & int(QueuedAction::FindId);
const bool exitAfter = int(entry.action) & int(QueuedAction::Exit);
const QueuedAction action = static_cast<QueuedAction>(int(entry.action) & int(QueuedAction::ActionMask));
bool ok = true;
if (entry.eventId.isEmpty())
{
// It's a new alarm
switch (entry.function)
switch (action)
{
case EVENT_TRIGGER:
case QueuedAction::Trigger:
execAlarm(entry.event, entry.event.firstAlarm(), false);
break;
case EVENT_HANDLE:
// Can't add a new event until resources have been populated.
if (!Resources::allPopulated())
{
// Keep the queued item unless resource population has timed out.
if (!mResourcesTimedOut)
removeFromQueue = false;
}
else
KAlarm::addEvent(entry.event, nullptr, nullptr, KAlarm::ALLOW_KORG_UPDATE | KAlarm::NO_RESOURCE_PROMPT);
case QueuedAction::Handle:
KAlarm::addEvent(entry.event, nullptr, nullptr, KAlarm::ALLOW_KORG_UPDATE | KAlarm::NO_RESOURCE_PROMPT);
break;
case QueuedAction::List:
{
const QStringList alarms = scheduledAlarmList();
for (const QString& alarm : alarms)
std::cout << alarm.toUtf8().constData() << std::endl;
break;
case EVENT_CANCEL:
}
default:
break;
}
}
else
handleEvent(entry.eventId, entry.function);
if (!removeFromQueue)
break;
{
ok = handleEvent(entry.eventId, action, findUniqueId);
if (!ok && exitAfter)
CommandOptions::printError(xi18nc("@info:shell", "%1: Event <resource>%2</resource> not found, or not unique", mCommandOption, entry.eventId.eventId()));
}
if (exitAfter)
{
mActionQueue.clear(); // ensure that quitIf() actually exits the program
quitIf(ok ? 0 : 1);
return; // quitIf() can sometimes return, despite calling exit()
}
mActionQueue.dequeue();
}
......@@ -1011,7 +1031,7 @@ void KAlarmApp::atLoginEventAdded(const KAEvent& event)
{
if (mAlarmsEnabled)
{
mActionQueue.enqueue(ActionQEntry(EVENT_HANDLE, EventId(ev)));
mActionQueue.enqueue(ActionQEntry(QueuedAction::Handle, EventId(ev)));
if (mInitialised)
QTimer::singleShot(0, this, &KAlarmApp::processQueue);
}
......@@ -1520,7 +1540,7 @@ bool KAlarmApp::scheduleEvent(KAEvent::SubAction action, const QString& text, co
// Alarm is due for display already.
// First execute it once without adding it to the calendar file.
if (!mInitialised)
mActionQueue.enqueue(ActionQEntry(event, EVENT_TRIGGER));
mActionQueue.enqueue(ActionQEntry(event, QueuedAction::Trigger));
else
execAlarm(event, event.firstAlarm(), false);
// If it's a recurring alarm, reschedule it for its next occurrence
......@@ -1542,10 +1562,10 @@ bool KAlarmApp::scheduleEvent(KAEvent::SubAction action, const QString& text, co
* Optionally display the event. Delete the event from the calendar file and
* from every main window instance.
*/
bool KAlarmApp::dbusHandleEvent(const EventId& eventID, EventFunc function)
bool KAlarmApp::dbusHandleEvent(const EventId& eventID, QueuedAction action)
{
qCDebug(KALARM_LOG) << "KAlarmApp::dbusHandleEvent:" << eventID;
mActionQueue.append(ActionQEntry(function, eventID));
mActionQueue.append(ActionQEntry(action, eventID));
if (mInitialised)
QTimer::singleShot(0, this, &KAlarmApp::processQueue);
return true;
......@@ -1572,8 +1592,10 @@ QString KAlarmApp::dbusList()
* Reply = false if event ID not found, or if more than one event with the same
* ID is found.
*/
bool KAlarmApp::handleEvent(const EventId& id, EventFunc function, bool findUniqueId)
bool KAlarmApp::handleEvent(const EventId& id, QueuedAction action, bool findUniqueId)
{
Q_ASSERT(!(int(action) & ~int(QueuedAction::ActionMask)));
// Delete any expired wake-on-suspend config data
KAlarm::checkRtcWakeConfig();
......@@ -1587,18 +1609,18 @@ bool KAlarmApp::handleEvent(const EventId& id, EventFunc function, bool findUniq
qCWarning(KALARM_LOG) << "KAlarmApp::handleEvent: Event ID not found:" << eventID;
return false;
}
switch (function)
switch (action)
{
case EVENT_CANCEL:
case QueuedAction::Cancel:
qCDebug(KALARM_LOG) << "KAlarmApp::handleEvent:" << eventID << ", CANCEL";
KAlarm::deleteEvent(*event, true);
break;
case EVENT_TRIGGER: // handle it if it's due, else execute it regardless
case EVENT_HANDLE: // handle it if it's due
case QueuedAction::Trigger: // handle it if it's due, else execute it regardless
case QueuedAction::Handle: // handle it if it's due
{
const KADateTime now = KADateTime::currentUtcDateTime();
qCDebug(KALARM_LOG) << "KAlarmApp::handleEvent:" << eventID << "," << (function==EVENT_TRIGGER?"TRIGGER:":"HANDLE:") << qPrintable(now.qDateTime().toString(QStringLiteral("yyyy-MM-dd hh:mm"))) << "UTC";
qCDebug(KALARM_LOG) << "KAlarmApp::handleEvent:" << eventID << "," << (action==QueuedAction::Trigger?"TRIGGER:":"HANDLE:") << qPrintable(now.qDateTime().toString(QStringLiteral("yyyy-MM-dd hh:mm"))) << "UTC";
bool updateCalAndDisplay = false;
bool alarmToExecuteValid = false;
KAAlarm alarmToExecute;
......@@ -1774,7 +1796,7 @@ bool KAlarmApp::handleEvent(const EventId& id, EventFunc function, bool findUniq
execAlarm(*event, alarmToExecute, true, !alarmToExecute.repeatAtLogin());
else
{
if (function == EVENT_TRIGGER)
if (action == QueuedAction::Trigger)
{
// The alarm is to be executed regardless of whether it's due.
// Only trigger one alarm from the event - we don't want multiple
......@@ -1785,10 +1807,12 @@ bool KAlarmApp::handleEvent(const EventId& id, EventFunc function, bool findUniq
}
if (updateCalAndDisplay)
KAlarm::updateEvent(*event); // update the window lists and calendar file
else if (function != EVENT_TRIGGER) { qCDebug(KALARM_LOG) << "KAlarmApp::handleEvent: No action"; }
else if (action != QueuedAction::Trigger) { qCDebug(KALARM_LOG) << "KAlarmApp::handleEvent: No action"; }
}
break;
}
default:
break;
}
return true;
}
......
......@@ -98,8 +98,8 @@ class KAlarmApp : public QApplication
uint mailFromID = 0, const KCalendarCore::Person::List& mailAddresses = KCalendarCore::Person::List(),
const QString& mailSubject = QString(),
const QStringList& mailAttachments = QStringList());
bool dbusTriggerEvent(const EventId& eventID) { return dbusHandleEvent(eventID, EVENT_TRIGGER); }
bool dbusDeleteEvent(const EventId& eventID) { return dbusHandleEvent(eventID, EVENT_CANCEL); }
bool dbusTriggerEvent(const EventId& eventID) { return dbusHandleEvent(eventID, QueuedAction::Trigger); }
bool dbusDeleteEvent(const EventId& eventID) { return dbusHandleEvent(eventID, QueuedAction::Cancel); }
QString dbusList();
public Q_SLOTS:
......@@ -143,11 +143,18 @@ class KAlarmApp : public QApplication
void slotCommandExited(ShellProcess*);
private:
enum EventFunc
// Actions to execute in processQueue(). May be OR'ed together.
enum class QueuedAction
{
EVENT_HANDLE, // if the alarm is due, execute it and then reschedule it
EVENT_TRIGGER, // execute the alarm regardless, and then reschedule it if it's already due
EVENT_CANCEL // delete the alarm
// Action to execute
ActionMask = 0x07, // bit mask to extract action to execute
Handle = 0x01, // if the alarm is due, execute it and then reschedule it
Trigger = 0x02, // execute the alarm regardless, and then reschedule it if it's already due
Cancel = 0x03, // delete the alarm
List = 0x04, // list all alarms
// Modifier flags
FindId = 0x10, // search all resources for unique event ID
Exit = 0x20 // exit application after executing action
};
struct ProcData
{
......@@ -172,12 +179,12 @@ class KAlarmApp : public QApplication
};
struct ActionQEntry
{
ActionQEntry(EventFunc f, const EventId& id) : function(f), eventId(id) { }
ActionQEntry(const KAEvent& e, EventFunc f = EVENT_HANDLE) : function(f), event(e) { }
ActionQEntry(QueuedAction a, const EventId& id) : action(a), eventId(id) { }
ActionQEntry(const KAEvent& e, QueuedAction a = QueuedAction::Handle) : action(a), event(e) { }
ActionQEntry() { }
EventFunc function;
EventId eventId;
KAEvent event;
QueuedAction action;
EventId eventId;
KAEvent event;
};
KAlarmApp(int& argc, char** argv);
......@@ -192,8 +199,8 @@ class KAlarmApp : public QApplication
void setResourcesTimeout();
void checkArchivedCalendar();
void queueAlarmId(const KAEvent&);
bool dbusHandleEvent(const EventId&, EventFunc);
bool handleEvent(const EventId&, EventFunc, bool findUniqueId = false);
bool dbusHandleEvent(const EventId&, QueuedAction);
bool handleEvent(const EventId&, QueuedAction, bool findUniqueId = false);
int rescheduleAlarm(KAEvent&, const KAAlarm&, bool updateCalAndDisplay,
const KADateTime& nextDt = KADateTime());
bool cancelAlarm(KAEvent&, KAAlarm::Type, bool updateCalAndDisplay);
......@@ -214,6 +221,7 @@ class KAlarmApp : public QApplication
static int mActiveCount; // number of active instances without main windows
static int mFatalError; // a fatal error has occurred - just wait to exit
static QString mFatalMessage; // fatal error message to output
QString mCommandOption; // command option used on command line
bool mInitialised{false}; // initialisation complete: ready to process execution queue
bool mRedisplayAlarms{false}; // need to redisplay alarms when collection tree fetched
bool mQuitting{false}; // a forced quit is in progress
......
......@@ -35,6 +35,10 @@ void initialise()
AkonadiDataModel::instance();
}
void terminate()
{
}
void reload()
{
AkonadiDataModel::instance()->reload();
......
......@@ -38,6 +38,8 @@ namespace DataModel
void initialise();
void terminate();
/** Reload all resources' data from storage.
* @note In the case of Akonadi, this does not reload from the backend storage.
*/
......
......@@ -265,6 +265,12 @@ bool Resource::isSaving() const
return mResource.isNull() ? false : mResource->isSaving();
}
void Resource::close()
{
if (!mResource.isNull())
mResource->close();
}
QList<KAEvent> Resource::events() const
{
return mResource.isNull() ? QList<KAEvent>() : mResource->events();
......
......@@ -310,6 +310,11 @@ public:
/** Return whether the resource is waiting for a save() to complete. */
bool isSaving() const;
/** Close the resource. This saves any unsaved data.
* Saving is not performed if the resource is disabled.
*/
void close();
/** Return all events belonging to this resource. */
QList<KAEvent> events() const;
......
......@@ -51,6 +51,13 @@ Resources::Resources()
{
}
Resources::~Resources()
{
qCDebug(KALARM_LOG) << "Resources::~Resources";
for (auto it = mResources.begin(); it != mResources.end(); ++it)
it.value().close();
}
Resource Resources::resource(ResourceId id)
{
return mResources.value(id, Resource::null());
......
/*
* resources.h - container for all ResourceType instances
* Program: kalarm
* Copyright © 2019 David Jarvie <djarvie@kde.org>
* Copyright © 2019-2020 David Jarvie <djarvie@kde.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
......@@ -40,7 +40,7 @@ public:
/** Creates the unique Resources instance. */
static Resources* instance();
~Resources() {}
~Resources();
Resources(const Resources&) = delete;
Resources& operator=(const Resources&) const = delete;
......
......@@ -341,6 +341,11 @@ public:
/** Return whether the resource is waiting for a save() to complete. */
virtual bool isSaving() const { return false; }
/** Close the resource. This saves any unsaved data.
* Saving is not performed if the resource is disabled.
*/
virtual void close() {}
/** Return all events belonging to this resource, for enabled alarm types. */
QList<KAEvent> events() const;
......
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