Commit c8304e8c 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 e2779591
......@@ -39,7 +39,8 @@ using namespace KAlarmCal;
*/
struct EventId : public QPair<ResourceId, QString>
{
EventId() {}
EventId()
: QPair<ResourceId, QString>(-1, QString()) {}
EventId(ResourceId c, const QString& e)
: QPair<ResourceId, QString>(c, e) {}
explicit EventId(const KAEvent& event)
......
......@@ -453,18 +453,16 @@ int KAlarmApp::activateInstance(const QStringList& args, const QString& workingD
case CommandOptions::EDIT:
// Edit a specified existing alarm.
// Open the calendar and wait for the calendar resources to be populated.
if (!initCheck(false)
|| !waitUntilPopulated(options->eventId().resourceId(), AKONADI_TIMEOUT))
if (!initCheck(false))
exitCode = 1;
else
{
if (!KAlarm::editAlarmById(options->eventId()))
{
CommandOptions::printError(xi18nc("@info:shell", "%1: Event <resource>%2</resource> not found, or not editable", options->commandName(), options->eventId().eventId()));
exitCode = 1;
}
else
createOnlyMainWindow(); // prevent the application from quitting
mCommandOption = options->commandName();
if (firstInstance)
mEditingCmdLineAlarm = 0x10; // want to redisplay alarms if successful
mActionQueue.enqueue(ActionQEntry(QueuedAction::Edit, options->eventId()));
startProcessQueue(); // start processing the execution queue
dontRedisplay = true;
}
break;
......@@ -541,13 +539,13 @@ int KAlarmApp::activateInstance(const QStringList& args, const QString& workingD
break;
}
createOnlyMainWindow(); // prevent the application from quitting
// Execute the edit dialogue. Note that if no other instance of KAlarm is
// running, this new instance will not exit after the dialogue is closed.
// This is deliberate, since exiting would mean that KAlarm wouldn't
// trigger the new alarm.
KAlarm::execNewAlarmDlg(editDlg);
createOnlyMainWindow(); // prevent the application from quitting
}
break;
}
......@@ -557,13 +555,13 @@ int KAlarmApp::activateInstance(const QStringList& args, const QString& workingD
exitCode = 1;
else
{
createOnlyMainWindow(); // prevent the application from quitting
// Execute the edit dialogue. Note that if no other instance of KAlarm is
// running, this new instance will not exit after the dialogue is closed.
// This is deliberate, since exiting would mean that KAlarm wouldn't
// trigger the new alarm.
KAlarm::editNewAlarm(options->templateName());
createOnlyMainWindow(); // prevent the application from quitting
}
break;
......@@ -941,8 +939,12 @@ void KAlarmApp::processQueue()
// Process queued events
while (!mActionQueue.isEmpty())
{
// Can't add a new event until resources have been populated.
if (!Resources::allPopulated())
ActionQEntry& entry = mActionQueue.head();
// Can't process the first action until its resource has been populated.
const ResourceId id = entry.eventId.resourceId();
if ((id < 0 && !Resources::allPopulated())
|| (id >= 0 && !Resources::resource(id).isPopulated()))
{
// If resource population has timed out, discard all queued events.
if (mResourcesTimedOut)
......@@ -953,7 +955,7 @@ void KAlarmApp::processQueue()
break;
}
ActionQEntry& entry = mActionQueue.head();
// Process the first action in the queue.
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));
......@@ -983,9 +985,36 @@ void KAlarmApp::processQueue()
}
else
{
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 (action == QueuedAction::Edit)
{
int editingCmdLineAlarm = mEditingCmdLineAlarm & 3;
bool keepQueued = editingCmdLineAlarm <= 1;
switch (editingCmdLineAlarm)
{
case 0:
// Initiate editing an alarm specified on the command line.
mEditingCmdLineAlarm |= 1;
QTimer::singleShot(0, this, &KAlarmApp::slotEditAlarmById);
break;
case 1:
// Currently editing the alarm.
break;
case 2:
// The edit has completed.
mEditingCmdLineAlarm = 0;
break;
default:
break;
}
if (keepQueued)
break;
}
else
{
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)
......@@ -1014,8 +1043,11 @@ void KAlarmApp::processQueue()
mProcessingQueue = false;
// Schedule the application to be woken when the next alarm is due
checkNextDueAlarm();
if (!mEditingCmdLineAlarm)
{
// Schedule the application to be woken when the next alarm is due
checkNextDueAlarm();
}
}
}
......@@ -1318,6 +1350,32 @@ void KAlarmApp::checkArchivedCalendar()
}
}
/******************************************************************************
* Edit an alarm specified on the command line.
*/
void KAlarmApp::slotEditAlarmById()
{
qCDebug(KALARM_LOG) << "KAlarmApp::slotEditAlarmById";
ActionQEntry& entry = mActionQueue.head();
if (!KAlarm::editAlarmById(entry.eventId))
{
CommandOptions::printError(xi18nc("@info:shell", "%1: Event <resource>%2</resource> not found, or not editable", mCommandOption, entry.eventId.eventId()));
mActionQueue.clear();
quitIf(1);
}
else
{
createOnlyMainWindow(); // prevent the application from quitting
if (mEditingCmdLineAlarm & 0x10)
{
mRedisplayAlarms = false;
MessageWin::redisplayAlarms();
}
mEditingCmdLineAlarm = 2; // indicate edit completion
QTimer::singleShot(0, this, &KAlarmApp::processQueue);
}
}
/******************************************************************************
* If alarms are being archived, check whether there is a default archived
* calendar, and if not, warn the user.
......@@ -2554,39 +2612,6 @@ bool KAlarmApp::initCheck(bool calendarOnly)
return true;
}
/******************************************************************************
* Wait for one or all enabled resources to be populated.
* Reply = true if successful.
*/
bool KAlarmApp::waitUntilPopulated(ResourceId id, int timeout)
{
qCDebug(KALARM_LOG) << "KAlarmApp::waitUntilPopulated" << id;
const Resource res = Resources::resource(id);
if ((id < 0 && !Resources::allPopulated())
|| (id >= 0 && !res.isPopulated()))
{
// Use AutoQPointer to guard against crash on application exit while
// the event loop is still running. It prevents double deletion (both
// on deletion of parent, and on return from this function).
AutoQPointer<QEventLoop> loop = new QEventLoop(DataModel::allAlarmListModel());
//TODO: The choice of parent object for QEventLoop can prevent EntityTreeModel signals
// from activating connected slots in AkonadiDataModel, which prevents resources
// from being informed that collections have loaded. Need to find a better parent
// object - Qt item models seem to work, but what else?
// These don't work: Resources::instance(), qApp(), theApp(), MainWindow::mainMainWindow(), AlarmCalendar::resources(), QStandardItemModel.
// These do work: CollectionControlModel::instance(), AlarmListModel::all().
if (id < 0)
connect(Resources::instance(), &Resources::resourcesPopulated, loop, &QEventLoop::quit);
else
connect(Resources::instance(), &Resources::resourcePopulated, [&loop, &id](Resource& r) {
if (r.id() == id) loop->quit(); });
if (timeout > 0)
QTimer::singleShot(timeout * 1000, loop, &QEventLoop::quit);
loop->exec();
}
return (id < 0) ? Resources::allPopulated() : res.isPopulated();
}
/******************************************************************************
* Called when an audio thread starts or stops.
*/
......
......@@ -133,6 +133,7 @@ class KAlarmApp : public QApplication
void slotFeb29TypeChanged(Feb29Type);
void slotResourcesTimeout();
void slotResourcesCreated();
void slotEditAlarmById();
void checkWritableCalendar();
void promptArchivedCalendar();
void slotMessageFontChanged(const QFont&);
......@@ -151,7 +152,8 @@ class KAlarmApp : public QApplication
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
Edit = 0x04, // edit an alarm (command line option)
List = 0x05, // list all alarms (command line option)
// Modifier flags
FindId = 0x10, // search all resources for unique event ID
Exit = 0x20 // exit application after executing action
......@@ -191,7 +193,6 @@ class KAlarmApp : public QApplication
bool initialiseTimerResources();
int activateInstance(const QStringList& args, const QString& workingDirectory, QString* outputText);
bool initCheck(bool calendarOnly = false);
bool waitUntilPopulated(ResourceId, int timeout);
bool quitIf(int exitCode, bool force = false);
void createOnlyMainWindow();
bool checkSystemTray();
......@@ -217,35 +218,36 @@ class KAlarmApp : public QApplication
void clearEventCommandError(const KAEvent&, KAEvent::CmdErrType) const;
ProcData* findCommandProcess(const QString& eventId) const;
static KAlarmApp* mInstance; // the one and only KAlarmApp instance
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
bool mReadOnly{false}; // only read-only access to calendars is needed
QString mActivateArg0; // activate()'s first arg the first time it was called
DBusHandler* mDBusHandler; // the parent of the main DCOP receiver object
TrayWindow* mTrayWindow{nullptr}; // active system tray icon
QTimer* mAlarmTimer{nullptr}; // activates KAlarm when next alarm is due
QColor mPrefsArchivedColour; // archived alarms text colour
int mArchivedPurgeDays{-1}; // how long to keep archived alarms, 0 = don't keep, -1 = keep indefinitely
int mPurgeDaysQueued{-1}; // >= 0 to purge the archive calendar from KAlarmApp::processLoop()
QVector<ResourceId> mPendingPurges; // new resources which may need to be purged when populated
QList<ProcData*> mCommandProcesses; // currently active command alarm processes
QQueue<ActionQEntry> mActionQueue; // queued commands and actions
int mPendingQuitCode; // exit code for a pending quit
bool mPendingQuit{false}; // quit once the DCOP command and shell command queues have been processed
bool mCancelRtcWake{false}; // cancel RTC wake on quitting
bool mProcessingQueue{false}; // a mActionQueue entry is currently being processed
bool mNoSystemTray; // no system tray exists
bool mOldShowInSystemTray; // showing in system tray was selected
bool mAlarmsEnabled{true}; // alarms are enabled
bool mKOrganizerEnabled; // KOrganizer options are enabled (korganizer exists)
bool mWindowFocusBroken; // keyboard focus transfer between windows doesn't work
bool mResourcesTimedOut{false}; // timeout has expired for populating resources
static KAlarmApp* mInstance; // the one and only KAlarmApp instance
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
bool mReadOnly {false}; // only read-only access to calendars is needed
QString mActivateArg0; // activate()'s first arg the first time it was called
DBusHandler* mDBusHandler; // the parent of the main DCOP receiver object
TrayWindow* mTrayWindow {nullptr}; // active system tray icon
QTimer* mAlarmTimer {nullptr}; // activates KAlarm when next alarm is due
QColor mPrefsArchivedColour; // archived alarms text colour
int mArchivedPurgeDays {-1}; // how long to keep archived alarms, 0 = don't keep, -1 = keep indefinitely
int mPurgeDaysQueued {-1}; // >= 0 to purge the archive calendar from KAlarmApp::processLoop()
QVector<ResourceId> mPendingPurges; // new resources which may need to be purged when populated
QList<ProcData*> mCommandProcesses; // currently active command alarm processes
QQueue<ActionQEntry> mActionQueue; // queued commands and actions
int mEditingCmdLineAlarm {0}; // whether currently editing alarm specified on command line
int mPendingQuitCode; // exit code for a pending quit
bool mPendingQuit {false}; // quit once the DCOP command and shell command queues have been processed
bool mCancelRtcWake {false}; // cancel RTC wake on quitting
bool mProcessingQueue {false}; // a mActionQueue entry is currently being processed
bool mNoSystemTray; // no system tray exists
bool mOldShowInSystemTray; // showing in system tray was selected
bool mAlarmsEnabled {true}; // alarms are enabled
bool mKOrganizerEnabled; // KOrganizer options are enabled (korganizer exists)
bool mWindowFocusBroken; // keyboard focus transfer between windows doesn't work
bool mResourcesTimedOut {false}; // timeout has expired for populating resources
};
inline KAlarmApp* theApp() { return 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