SVN_SILENT made messages (.desktop file) - always resolve ours
[kdepim.git] / kalarm / commandoptions.cpp
blobca490e86d4bc406cc9fcafb3b28222a5f50c158f
1 /*
2 * commandoptions.cpp - extract command line options
3 * Program: kalarm
4 * Copyright © 2001-2016 by David Jarvie <djarvie@kde.org>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21 #include "kalarm.h" //krazy:exclude=includes (kalarm.h must be first)
22 #include "commandoptions.h"
23 #include "alarmtime.h"
24 #include "kalarmapp.h"
25 #include "kamail.h"
26 #include "kalarm_debug.h"
28 #include <kalarmcal/identities.h>
29 #include <kpimtextedit/texttospeech.h>
30 #include <KLocalizedString>
32 #include <QCommandLineParser>
34 #include <iostream>
36 namespace
38 bool convInterval(const QString& timeParam, KARecurrence::Type&, int& timeInterval, bool allowMonthYear = false);
41 CommandOptions* CommandOptions::mInstance = Q_NULLPTR;
42 QCommandLineParser* CommandOptions::mParser = Q_NULLPTR;
43 QVector<QCommandLineOption*> CommandOptions::mOptions(Num_Options, Q_NULLPTR);
44 QStringList CommandOptions::mExecArguments;
46 void CommandOptions::setError(const QString& error)
48 if (mError.isEmpty())
49 mError = error;
52 /******************************************************************************
53 * Set the command line options for the parser, and remove any arguments
54 * following --exec or --exec-display (since the parser can't handle this).
55 * Reply = command line arguments for parser.
57 QStringList CommandOptions::setOptions(QCommandLineParser* parser, const QStringList& args)
59 mParser = parser;
61 mOptions[ACK_CONFIRM]
62 = new QCommandLineOption(QStringList() << QStringLiteral("a") << QStringLiteral("ack-confirm"),
63 i18n("Prompt for confirmation when alarm is acknowledged"));
64 mOptions[ATTACH]
65 = new QCommandLineOption(QStringList() << QStringLiteral("A") << QStringLiteral("attach"),
66 i18n("Attach file to email (repeat as needed)"),
67 QStringLiteral("url"));
68 mOptions[AUTO_CLOSE]
69 = new QCommandLineOption(QStringLiteral("auto-close"),
70 i18n("Auto-close alarm window after --late-cancel period"));
71 mOptions[BCC]
72 = new QCommandLineOption(QStringLiteral("bcc"),
73 i18n("Blind copy email to self"));
74 mOptions[BEEP]
75 = new QCommandLineOption(QStringList() << QStringLiteral("b") << QStringLiteral("beep"),
76 i18n("Beep when message is displayed"));
77 mOptions[COLOUR]
78 = new QCommandLineOption(QStringList() << QStringLiteral("c") << QStringLiteral("colour") << QStringLiteral("color"),
79 i18n("Message background color (name or hex 0xRRGGBB)"),
80 QStringLiteral("color"));
81 mOptions[COLOURFG]
82 = new QCommandLineOption(QStringList() << QStringLiteral("C") << QStringLiteral("colourfg") << QStringLiteral("colorfg"),
83 i18n("Message foreground color (name or hex 0xRRGGBB)"),
84 QStringLiteral("color"));
85 mOptions[OptCANCEL_EVENT]
86 = new QCommandLineOption(QStringLiteral("cancelEvent"),
87 i18n("Cancel alarm with the specified event ID"),
88 QStringLiteral("eventID"));
89 mOptions[DISABLE]
90 = new QCommandLineOption(QStringList() << QStringLiteral("d") << QStringLiteral("disable"),
91 i18n("Disable the alarm"));
92 mOptions[DISABLE_ALL]
93 = new QCommandLineOption(QStringLiteral("disable-all"),
94 i18n("Disable monitoring of all alarms"));
95 mOptions[EXEC]
96 = new QCommandLineOption(QStringList() << QStringLiteral("e") << QStringLiteral("exec"),
97 i18n("Execute a shell command line"),
98 QStringLiteral("commandLine"));
99 mOptions[EXEC_DISPLAY]
100 = new QCommandLineOption(QStringList() << QStringLiteral("E") << QStringLiteral("exec-display"),
101 i18n("Command line to generate alarm message text"),
102 QStringLiteral("commandLine"));
103 mOptions[OptEDIT]
104 = new QCommandLineOption(QStringLiteral("edit"),
105 i18n("Display the alarm edit dialog to edit the specified alarm"),
106 QStringLiteral("eventID"));
107 mOptions[EDIT_NEW_DISPLAY]
108 = new QCommandLineOption(QStringLiteral("edit-new-display"),
109 i18n("Display the alarm edit dialog to edit a new display alarm"));
110 mOptions[EDIT_NEW_COMMAND]
111 = new QCommandLineOption(QStringLiteral("edit-new-command"),
112 i18n("Display the alarm edit dialog to edit a new command alarm"));
113 mOptions[EDIT_NEW_EMAIL]
114 = new QCommandLineOption(QStringLiteral("edit-new-email"),
115 i18n("Display the alarm edit dialog to edit a new email alarm"));
116 mOptions[EDIT_NEW_AUDIO]
117 = new QCommandLineOption(QStringLiteral("edit-new-audio"),
118 i18n("Display the alarm edit dialog to edit a new audio alarm"));
119 mOptions[OptEDIT_NEW_PRESET]
120 = new QCommandLineOption(QStringLiteral("edit-new-preset"),
121 i18n("Display the alarm edit dialog, preset with a template"),
122 QStringLiteral("templateName"));
123 mOptions[FILE]
124 = new QCommandLineOption(QStringList() << QStringLiteral("f") << QStringLiteral("file"),
125 i18n("File to display"),
126 QStringLiteral("url"));
127 mOptions[FROM_ID]
128 = new QCommandLineOption(QStringList() << QStringLiteral("F") << QStringLiteral("from-id"),
129 i18n("KMail identity to use as sender of email"),
130 QStringLiteral("ID"));
131 mOptions[INTERVAL]
132 = new QCommandLineOption(QStringList() << QStringLiteral("i") << QStringLiteral("interval"),
133 i18n("Interval between alarm repetitions"),
134 QStringLiteral("period"));
135 mOptions[KORGANIZER]
136 = new QCommandLineOption(QStringList() << QStringLiteral("k") << QStringLiteral("korganizer"),
137 i18n("Show alarm as an event in KOrganizer"));
138 mOptions[LATE_CANCEL]
139 = new QCommandLineOption(QStringList() << QStringLiteral("l") << QStringLiteral("late-cancel"),
140 i18n("Cancel alarm if more than 'period' late when triggered"),
141 QStringLiteral("period"),
142 QStringLiteral("1"));
143 mOptions[OptLIST]
144 = new QCommandLineOption(QStringLiteral("list"),
145 i18n("Output list of scheduled alarms to stdout"));
146 mOptions[LOGIN]
147 = new QCommandLineOption(QStringList() << QStringLiteral("L") << QStringLiteral("login"),
148 i18n("Repeat alarm at every login"));
149 mOptions[MAIL]
150 = new QCommandLineOption(QStringList() << QStringLiteral("m") << QStringLiteral("mail"),
151 i18n("Send an email to the given address (repeat as needed)"),
152 QStringLiteral("address"));
153 mOptions[PLAY]
154 = new QCommandLineOption(QStringList() << QStringLiteral("p") << QStringLiteral("play"),
155 i18n("Audio file to play once"),
156 QStringLiteral("url"));
157 mOptions[PLAY_REPEAT]
158 = new QCommandLineOption(QStringList() << QStringLiteral("P") << QStringLiteral("play-repeat"),
159 i18n("Audio file to play repeatedly"),
160 QStringLiteral("url"));
161 mOptions[RECURRENCE]
162 = new QCommandLineOption(QStringLiteral("recurrence"),
163 i18n("Specify alarm recurrence using iCalendar syntax"),
164 QStringLiteral("spec"));
165 mOptions[REMINDER]
166 = new QCommandLineOption(QStringList() << QStringLiteral("R") << QStringLiteral("reminder"),
167 i18n("Display reminder before or after alarm"),
168 QStringLiteral("period"));
169 mOptions[REMINDER_ONCE]
170 = new QCommandLineOption(QStringLiteral("reminder-once"),
171 i18n("Display reminder once, before or after first alarm recurrence"),
172 QStringLiteral("period"));
173 mOptions[REPEAT]
174 = new QCommandLineOption(QStringList() << QStringLiteral("r") << QStringLiteral("repeat"),
175 i18n("Number of times to repeat alarm (including initial occasion)"),
176 QStringLiteral("count"));
177 mOptions[SPEAK]
178 = new QCommandLineOption(QStringList() << QStringLiteral("s") << QStringLiteral("speak"),
179 i18n("Speak the message when it is displayed"));
180 mOptions[SUBJECT]
181 = new QCommandLineOption(QStringList() << QStringLiteral("S") << QStringLiteral("subject"),
182 i18n("Email subject line"),
183 QStringLiteral("text"));
184 #ifndef NDEBUG
185 mOptions[TEST_SET_TIME]
186 = new QCommandLineOption(QStringLiteral("test-set-time"),
187 i18n("Simulate system time [[[yyyy-]mm-]dd-]hh:mm [TZ] (debug mode)"),
188 QStringLiteral("time"));
189 #endif
190 mOptions[TIME]
191 = new QCommandLineOption(QStringList() << QStringLiteral("t") << QStringLiteral("time"),
192 i18n("Trigger alarm at time [[[yyyy-]mm-]dd-]hh:mm [TZ], or date yyyy-mm-dd [TZ]"),
193 QStringLiteral("time"));
194 mOptions[OptTRAY]
195 = new QCommandLineOption(QStringLiteral("tray"),
196 i18n("Display system tray icon"));
197 mOptions[OptTRIGGER_EVENT]
198 = new QCommandLineOption(QStringLiteral("triggerEvent"),
199 i18n("Trigger alarm with the specified event ID"),
200 QStringLiteral("eventID"));
201 mOptions[UNTIL]
202 = new QCommandLineOption(QStringList() << QStringLiteral("u") << QStringLiteral("until"),
203 i18n("Repeat until time [[[yyyy-]mm-]dd-]hh:mm [TZ], or date yyyy-mm-dd [TZ]"),
204 QStringLiteral("time"));
205 mOptions[VOLUME]
206 = new QCommandLineOption(QStringList() << QStringLiteral("V") << QStringLiteral("volume"),
207 i18n("Volume to play audio file"),
208 QStringLiteral("percent"));
210 for (int i = 0; i < mOptions.size(); ++i)
212 if (!mOptions[i])
213 qCCritical(KALARM_LOG) << "Command option" << i << "not initialised";
214 else
215 mParser->addOption(*(mOptions[i]));
217 mParser->addVersionOption();
218 mParser->addHelpOption();
219 mParser->addPositionalArgument(QStringLiteral("message"),
220 i18n("Message text to display"),
221 QStringLiteral("[message]"));
223 // Check for any options which eat up all following arguments.
224 QStringList arguments;
225 for (int i = 0; i < args.size(); ++i)
227 const QString arg = args[i];
228 arguments << arg;
229 if (arg == optionName(EXEC) || arg == optionName(EXEC, true)
230 || arg == optionName(EXEC_DISPLAY) || arg == optionName(EXEC_DISPLAY, true))
232 // All following arguments (including ones beginning with '-')
233 // belong to this option. QCommandLineParser can't handle this, so
234 // remove them from the command line.
235 ++i; // leave the first argument, which is the command to be executed
236 while (++i < args.size())
237 mExecArguments << args[i];
240 qCDebug(KALARM_LOG) << arguments;
241 return arguments;
244 void CommandOptions::process()
246 if (!mInstance)
247 mInstance = new CommandOptions();
250 CommandOptions::CommandOptions()
251 : mCommand(NONE),
252 mEditActionSet(false),
253 mRecurrence(Q_NULLPTR),
254 mRepeatCount(0),
255 mRepeatInterval(0),
256 mLateCancel(0),
257 mBgColour(Preferences::defaultBgColour()),
258 mFgColour(Preferences::defaultFgColour()),
259 mReminderMinutes(0),
260 mAudioVolume(-1),
261 mFromID(0),
262 mFlags(KAEvent::DEFAULT_FONT),
263 mDisableAll(false)
265 #ifndef NDEBUG
266 if (mParser->isSet(*mOptions[TEST_SET_TIME]))
268 const QString time = mParser->value(*mOptions[TEST_SET_TIME]);
269 if (!AlarmTime::convertTimeString(time.toLatin1(), mSimulationTime, KDateTime::realCurrentLocalDateTime(), true))
270 setErrorParameter(TEST_SET_TIME);
272 #endif
273 if (checkCommand(OptTRAY, TRAY))
276 if (checkCommand(OptLIST, LIST))
278 if (!mParser->positionalArguments().empty())
279 setErrorParameter(OptLIST);
281 if (checkCommand(OptTRIGGER_EVENT, TRIGGER_EVENT)
282 || checkCommand(OptCANCEL_EVENT, CANCEL_EVENT)
283 || checkCommand(OptEDIT, EDIT))
285 // Fetch the event ID. This can optionally include a prefix of the
286 // resource ID followed by a colon delimiter.
287 mEventId = EventId(mParser->value(*mOptions[mCommandOpt]));
289 if (checkCommand(OptEDIT_NEW_PRESET, EDIT_NEW_PRESET))
291 mTemplateName = mParser->value(*mOptions[mCommandOpt]);
293 if (checkCommand(FILE, NEW))
295 mEditType = EditAlarmDlg::DISPLAY;
296 mEditAction = KAEvent::FILE;
297 mEditActionSet = true;
298 mText = mParser->value(*mOptions[mCommandOpt]);
300 if (checkCommand(EXEC_DISPLAY, NEW))
302 mEditType = EditAlarmDlg::DISPLAY;
303 mEditAction = KAEvent::COMMAND;
304 mEditActionSet = true;
305 mFlags |= KAEvent::DISPLAY_COMMAND;
306 mText = mParser->value(*mOptions[mCommandOpt]) + QStringLiteral(" ") + mExecArguments.join(QLatin1Char(' '));
308 if (checkCommand(EXEC, NEW))
310 mEditType = EditAlarmDlg::COMMAND;
311 mEditAction = KAEvent::COMMAND;
312 mEditActionSet = true;
313 mText = mParser->value(*mOptions[mCommandOpt]) + QStringLiteral(" ") + mExecArguments.join(QLatin1Char(' '));
315 if (checkCommand(MAIL, NEW))
317 mEditType = EditAlarmDlg::EMAIL;
318 mEditAction = KAEvent::EMAIL;
319 mEditActionSet = true;
321 if (checkCommand(EDIT_NEW_DISPLAY, EDIT_NEW, EditAlarmDlg::DISPLAY))
323 mEditType = EditAlarmDlg::DISPLAY;
324 if (!mEditActionSet || (mEditAction != KAEvent::COMMAND && mEditAction != KAEvent::FILE))
326 mEditAction = KAEvent::MESSAGE;
327 mEditActionSet = true;
329 const QStringList args = mParser->positionalArguments();
330 if (!args.empty())
331 mText = args[0];
333 if (checkCommand(EDIT_NEW_COMMAND, EDIT_NEW))
335 mEditType = EditAlarmDlg::COMMAND;
336 mEditAction = KAEvent::COMMAND;
337 mEditActionSet = true;
339 if (checkCommand(EDIT_NEW_EMAIL, EDIT_NEW, EditAlarmDlg::EMAIL))
341 mEditType = EditAlarmDlg::EMAIL;
342 mEditAction = KAEvent::EMAIL;
343 mEditActionSet = true;
345 if (checkCommand(EDIT_NEW_AUDIO, EDIT_NEW, EditAlarmDlg::AUDIO))
347 mEditType = EditAlarmDlg::AUDIO;
348 mEditAction = KAEvent::AUDIO;
349 mEditActionSet = true;
351 if (mError.isEmpty() && mCommand == NONE)
353 if (mParser->positionalArguments().empty())
355 if (checkCommand(PLAY, NEW) || checkCommand(PLAY_REPEAT, NEW))
357 mEditType = EditAlarmDlg::AUDIO;
358 mEditAction = KAEvent::AUDIO;
359 mEditActionSet = true;
362 else
364 qCDebug(KALARM_LOG) << "Message";
365 mCommand = NEW;
366 mCommandOpt = Opt_Message;
367 mEditType = EditAlarmDlg::DISPLAY;
368 mEditAction = KAEvent::MESSAGE;
369 mEditActionSet = true;
370 mText = arg(0);
373 if (mEditActionSet && mEditAction == KAEvent::EMAIL)
375 if (mParser->isSet(*mOptions[SUBJECT]))
376 mSubject = mParser->value(*mOptions[SUBJECT]);
377 if (mParser->isSet(*mOptions[FROM_ID]))
378 mFromID = Identities::identityUoid(mParser->value(*mOptions[FROM_ID]));
379 QStringList params = mParser->values(*mOptions[MAIL]);
380 for (int i = 0, count = params.count(); i < count; ++i)
382 QString addr = params[i];
383 if (!KAMail::checkAddress(addr))
384 setError(xi18nc("@info:shell", "<icode>%1</icode>: invalid email address", optionName(MAIL)));
385 KCalCore::Person::Ptr person(new KCalCore::Person(QString(), addr));
386 mAddressees += person;
388 params = mParser->values(*mOptions[ATTACH]);
389 for (int i = 0, count = params.count(); i < count; ++i)
390 mAttachments += params[i];
391 const QStringList args = mParser->positionalArguments();
392 if (!args.empty())
393 mText = args[0];
395 if (mParser->isSet(*mOptions[DISABLE_ALL]))
397 if (mCommand == TRIGGER_EVENT || mCommand == LIST)
398 setErrorIncompatible(DISABLE_ALL, mCommandOpt);
399 mDisableAll = true;
402 // Check that other options are only specified for the
403 // correct main command options.
404 checkEditType(EditAlarmDlg::DISPLAY, COLOUR);
405 checkEditType(EditAlarmDlg::DISPLAY, COLOURFG);
406 checkEditType(EditAlarmDlg::DISPLAY, EditAlarmDlg::AUDIO, PLAY);
407 checkEditType(EditAlarmDlg::DISPLAY, EditAlarmDlg::AUDIO, PLAY_REPEAT);
408 checkEditType(EditAlarmDlg::DISPLAY, EditAlarmDlg::AUDIO, VOLUME);
409 checkEditType(EditAlarmDlg::DISPLAY, SPEAK);
410 checkEditType(EditAlarmDlg::DISPLAY, BEEP);
411 checkEditType(EditAlarmDlg::DISPLAY, REMINDER);
412 checkEditType(EditAlarmDlg::DISPLAY, REMINDER_ONCE);
413 checkEditType(EditAlarmDlg::DISPLAY, ACK_CONFIRM);
414 checkEditType(EditAlarmDlg::DISPLAY, AUTO_CLOSE);
415 checkEditType(EditAlarmDlg::EMAIL, SUBJECT);
416 checkEditType(EditAlarmDlg::EMAIL, FROM_ID);
417 checkEditType(EditAlarmDlg::EMAIL, ATTACH);
418 checkEditType(EditAlarmDlg::EMAIL, BCC);
420 switch (mCommand)
422 case EDIT_NEW:
423 if (mParser->isSet(*mOptions[DISABLE]))
424 setErrorIncompatible(DISABLE, mCommandOpt);
425 // Fall through to NEW
426 case NEW:
428 // Display a message or file, execute a command, or send an email
429 if (mParser->isSet(*mOptions[COLOUR]))
431 // Background colour is specified
432 QString colourText = mParser->value(*mOptions[COLOUR]);
433 if (colourText[0] == QLatin1Char('0') && colourText[1].toLower() == QLatin1Char('x'))
434 colourText.replace(0, 2, QStringLiteral("#"));
435 mBgColour.setNamedColor(colourText);
436 if (!mBgColour.isValid())
437 setErrorParameter(COLOUR);
439 if (mParser->isSet(*mOptions[COLOURFG]))
441 // Foreground colour is specified
442 QString colourText = mParser->value(*mOptions[COLOURFG]);
443 if (colourText[0] == QLatin1Char('0') && colourText[1].toLower() == QLatin1Char('x'))
444 colourText.replace(0, 2, QStringLiteral("#"));
445 mFgColour.setNamedColor(colourText);
446 if (!mFgColour.isValid())
447 setErrorParameter(COLOURFG);
450 if (mParser->isSet(*mOptions[TIME]))
452 QByteArray dateTime = mParser->value(*mOptions[TIME]).toLocal8Bit();
453 if (!AlarmTime::convertTimeString(dateTime, mAlarmTime))
454 setErrorParameter(TIME);
456 else
457 mAlarmTime = KDateTime::currentLocalDateTime();
459 bool haveRecurrence = mParser->isSet(*mOptions[RECURRENCE]);
460 if (haveRecurrence)
462 if (mParser->isSet(*mOptions[LOGIN]))
463 setErrorIncompatible(LOGIN, RECURRENCE);
464 else if (mParser->isSet(*mOptions[UNTIL]))
465 setErrorIncompatible(UNTIL, RECURRENCE);
466 QString rule = mParser->value(*mOptions[RECURRENCE]);
467 mRecurrence = new KARecurrence;
468 mRecurrence->set(rule);
470 if (mParser->isSet(*mOptions[INTERVAL]))
472 // Repeat count is specified
473 int count = 0;
474 KDateTime endTime;
475 if (mParser->isSet(*mOptions[LOGIN]))
476 setErrorIncompatible(LOGIN, INTERVAL);
477 bool ok;
478 if (mParser->isSet(*mOptions[REPEAT]))
480 count = mParser->value(*mOptions[REPEAT]).toInt(&ok);
481 if (!ok || !count || count < -1 || (count < 0 && haveRecurrence))
482 setErrorParameter(REPEAT);
484 else if (haveRecurrence)
485 setErrorRequires(INTERVAL, REPEAT);
486 else if (mParser->isSet(*mOptions[UNTIL]))
488 count = 0;
489 QByteArray dateTime = mParser->value(*mOptions[UNTIL]).toLocal8Bit();
490 bool ok;
491 if (mParser->isSet(*mOptions[TIME]))
492 ok = AlarmTime::convertTimeString(dateTime, endTime, mAlarmTime);
493 else
494 ok = AlarmTime::convertTimeString(dateTime, endTime);
495 if (!ok)
496 setErrorParameter(UNTIL);
497 else if (mAlarmTime.isDateOnly() && !endTime.isDateOnly())
498 setError(xi18nc("@info:shell", "Invalid <icode>%1</icode> parameter for date-only alarm", optionName(UNTIL)));
499 if (!mAlarmTime.isDateOnly() && endTime.isDateOnly())
500 endTime.setTime(QTime(23,59,59));
501 if (endTime < mAlarmTime)
502 setError(xi18nc("@info:shell", "<icode>%1</icode> earlier than <icode>%2</icode>", optionName(UNTIL), optionName(TIME)));
504 else
505 count = -1;
507 // Get the recurrence interval
508 int intervalOfType;
509 KARecurrence::Type recurType;
510 if (!convInterval(mParser->value(*mOptions[INTERVAL]), recurType, intervalOfType, !haveRecurrence))
511 setErrorParameter(INTERVAL);
512 else if (mAlarmTime.isDateOnly() && recurType == KARecurrence::MINUTELY)
513 setError(xi18nc("@info:shell", "Invalid <icode>%1</icode> parameter for date-only alarm", optionName(INTERVAL)));
515 if (haveRecurrence)
517 if (mRecurrence)
519 // There is a also a recurrence specified, so set up a sub-repetition.
520 // In this case, 'intervalOfType' is in minutes.
521 mRepeatInterval = KCalCore::Duration(intervalOfType * 60);
522 KCalCore::Duration longestInterval = mRecurrence->longestInterval();
523 if (mRepeatInterval * count > longestInterval)
524 setError(xi18nc("@info:shell", "Invalid <icode>%1</icode> and <icode>%2</icode> parameters: repetition is longer than <icode>%3</icode> interval",
525 optionName(INTERVAL), optionName(REPEAT), optionName(RECURRENCE)));
526 mRepeatCount = count;
529 else
531 // There is no other recurrence specified, so convert the repetition
532 // parameters into a KCal::Recurrence
533 mRecurrence = new KARecurrence;
534 mRecurrence->set(recurType, intervalOfType, count, mAlarmTime, endTime);
537 else
539 if (mParser->isSet(*mOptions[REPEAT]))
540 setErrorRequires(REPEAT, INTERVAL);
541 else if (mParser->isSet(*mOptions[UNTIL]))
542 setErrorRequires(UNTIL, INTERVAL);
545 bool audioRepeat = mParser->isSet(*mOptions[PLAY_REPEAT]);
546 if (audioRepeat || mParser->isSet(*mOptions[PLAY]))
548 // Play a sound with the alarm
549 Option opt = audioRepeat ? PLAY_REPEAT : PLAY;
550 if (audioRepeat && mParser->isSet(*mOptions[PLAY]))
551 setErrorIncompatible(PLAY, PLAY_REPEAT);
552 if (mParser->isSet(*mOptions[BEEP]))
553 setErrorIncompatible(BEEP, opt);
554 else if (mParser->isSet(*mOptions[SPEAK]))
555 setErrorIncompatible(SPEAK, opt);
556 mAudioFile = mParser->value(*mOptions[audioRepeat ? PLAY_REPEAT : PLAY]);
557 if (mParser->isSet(*mOptions[VOLUME]))
559 bool ok;
560 int volumepc = mParser->value(*mOptions[VOLUME]).toInt(&ok);
561 if (!ok || volumepc < 0 || volumepc > 100)
562 setErrorParameter(VOLUME);
563 mAudioVolume = static_cast<float>(volumepc) / 100;
566 else if (mParser->isSet(*mOptions[VOLUME]))
567 setErrorRequires(VOLUME, PLAY, PLAY_REPEAT);
568 if (mParser->isSet(*mOptions[SPEAK]))
570 if (mParser->isSet(*mOptions[BEEP]))
571 setErrorIncompatible(BEEP, SPEAK);
572 else if (!KPIMTextEdit::TextToSpeech::self()->isReady())
573 setError(xi18nc("@info:shell", "<icode>%1</icode> requires KAlarm to be compiled with QTextToSpeech support", optionName(SPEAK)));
575 bool onceOnly = mParser->isSet(*mOptions[REMINDER_ONCE]);
576 if (mParser->isSet(*mOptions[REMINDER]) || onceOnly)
578 // Issue a reminder alarm in advance of or after the main alarm
579 if (onceOnly && mParser->isSet(*mOptions[REMINDER]))
580 setErrorIncompatible(REMINDER, REMINDER_ONCE);
581 Option opt = onceOnly ? REMINDER_ONCE : REMINDER;
582 KARecurrence::Type recurType;
583 QString optval = mParser->value(*mOptions[onceOnly ? REMINDER_ONCE : REMINDER]);
584 bool after = (optval[0] == QLatin1Char('+'));
585 if (after)
586 optval.remove(0, 1); // it's a reminder after the main alarm
587 if (!convInterval(optval, recurType, mReminderMinutes))
588 setErrorParameter(opt);
589 else if (recurType == KARecurrence::MINUTELY && mAlarmTime.isDateOnly())
590 setError(xi18nc("@info:shell", "Invalid <icode>%1</icode> parameter for date-only alarm", optionName(opt)));
591 if (after)
592 mReminderMinutes = -mReminderMinutes;
593 if (onceOnly)
594 mFlags |= KAEvent::REMINDER_ONCE;
597 if (mParser->isSet(*mOptions[LATE_CANCEL]))
599 KARecurrence::Type recurType;
600 bool ok = convInterval(mParser->value(*mOptions[LATE_CANCEL]), recurType, mLateCancel);
601 if (!ok)
602 setErrorParameter(LATE_CANCEL);
604 else if (mParser->isSet(*mOptions[AUTO_CLOSE]))
605 setErrorRequires(AUTO_CLOSE, LATE_CANCEL);
607 if (mParser->isSet(*mOptions[ACK_CONFIRM]))
608 mFlags |= KAEvent::CONFIRM_ACK;
609 if (mParser->isSet(*mOptions[AUTO_CLOSE]))
610 mFlags |= KAEvent::AUTO_CLOSE;
611 if (mParser->isSet(*mOptions[BEEP]))
612 mFlags |= KAEvent::BEEP;
613 if (mParser->isSet(*mOptions[SPEAK]))
614 mFlags |= KAEvent::SPEAK;
615 if (mParser->isSet(*mOptions[KORGANIZER]))
616 mFlags |= KAEvent::COPY_KORGANIZER;
617 if (mParser->isSet(*mOptions[DISABLE]))
618 mFlags |= KAEvent::DISABLED;
619 if (audioRepeat)
620 mFlags |= KAEvent::REPEAT_SOUND;
621 if (mParser->isSet(*mOptions[LOGIN]))
622 mFlags |= KAEvent::REPEAT_AT_LOGIN;
623 if (mParser->isSet(*mOptions[BCC]))
624 mFlags |= KAEvent::EMAIL_BCC;
625 if (mAlarmTime.isDateOnly())
626 mFlags |= KAEvent::ANY_TIME;
627 break;
629 case NONE:
631 // No arguments - run interactively & display the main window
632 if (!mError.isEmpty())
633 break;
634 qCDebug(KALARM_LOG) << "Interactive";
635 QStringList errors;
636 if (mParser->isSet(*mOptions[ACK_CONFIRM]))
637 errors << optionName(ACK_CONFIRM);
638 if (mParser->isSet(*mOptions[ATTACH]))
639 errors << optionName(ATTACH);
640 if (mParser->isSet(*mOptions[AUTO_CLOSE]))
641 errors << optionName(AUTO_CLOSE);
642 if (mParser->isSet(*mOptions[BCC]))
643 errors << optionName(BCC);
644 if (mParser->isSet(*mOptions[BEEP]))
645 errors << optionName(BEEP);
646 if (mParser->isSet(*mOptions[COLOUR]))
647 errors << optionName(COLOUR);
648 if (mParser->isSet(*mOptions[COLOURFG]))
649 errors << optionName(COLOURFG);
650 if (mParser->isSet(*mOptions[DISABLE]))
651 errors << optionName(DISABLE);
652 if (mParser->isSet(*mOptions[FROM_ID]))
653 errors << optionName(FROM_ID);
654 if (mParser->isSet(*mOptions[KORGANIZER]))
655 errors << optionName(KORGANIZER);
656 if (mParser->isSet(*mOptions[LATE_CANCEL]))
657 errors << optionName(LATE_CANCEL);
658 if (mParser->isSet(*mOptions[LOGIN]))
659 errors << optionName(LOGIN);
660 if (mParser->isSet(*mOptions[PLAY]))
661 errors << optionName(PLAY);
662 if (mParser->isSet(*mOptions[PLAY_REPEAT]))
663 errors << optionName(PLAY_REPEAT);
664 if (mParser->isSet(*mOptions[REMINDER]))
665 errors << optionName(REMINDER);
666 if (mParser->isSet(*mOptions[REMINDER_ONCE]))
667 errors << optionName(REMINDER_ONCE);
668 if (mParser->isSet(*mOptions[SPEAK]))
669 errors << optionName(SPEAK);
670 if (mParser->isSet(*mOptions[SUBJECT]))
671 errors << optionName(SUBJECT);
672 if (mParser->isSet(*mOptions[TIME]))
673 errors << optionName(TIME);
674 if (mParser->isSet(*mOptions[VOLUME]))
675 errors << optionName(VOLUME);
676 if (!errors.isEmpty())
677 mError = errors.join(QLatin1Char(' ')) + i18nc("@info:shell", ": option(s) only valid with an appropriate action option or message");
678 break;
680 default:
681 break;
684 if (!mError.isEmpty())
686 printError(mError);
687 mCommand = CMD_ERROR;
691 void CommandOptions::printError(const QString& errmsg)
693 qCDebug(KALARM_LOG)<<"ERROR=====================";
694 // Note: we can't use mArgs->usage() since that also quits any other
695 // running 'instances' of the program.
696 std::cerr << errmsg.toLocal8Bit().data()
697 << i18nc("@info:shell", "\nUse --help to get a list of available command line options.\n").toLocal8Bit().data();
700 /******************************************************************************
701 * Check if the given command option is specified, and if so set mCommand etc.
702 * If another command option has also been detected, issue an error.
703 * If 'allowedEditType' is set, supersede any previous specification of that
704 * edit type with the given command option - this allows, e.g., --mail to be
705 * used along with --edit-new-email so the user can specify addressees.
707 bool CommandOptions::checkCommand(Option command, Command code, EditAlarmDlg::Type allowedEditType)
709 if (!mError.isEmpty()
710 || !mParser->isSet(*mOptions[command]))
711 return false;
712 if (mCommand != NONE
713 && (allowedEditType == EditAlarmDlg::NO_TYPE
714 || (allowedEditType != EditAlarmDlg::NO_TYPE && (mCommand != NEW || mEditType != allowedEditType))))
715 setErrorIncompatible(mCommandOpt, command);
716 qCDebug(KALARM_LOG).nospace() << optionName(command);
717 mCommand = code;
718 mCommandOpt = command;
719 return true;
722 // Set the error message to "--opt requires --opt2" or "--opt requires --opt2 or --opt3".
723 void CommandOptions::setErrorRequires(Option opt, Option opt2, Option opt3)
725 if (opt3 == Num_Options)
726 setError(xi18nc("@info:shell", "<icode>%1</icode> requires <icode>%2</icode>", optionName(opt), optionName(opt2)));
727 else
728 setError(xi18nc("@info:shell", "<icode>%1</icode> requires <icode>%2</icode> or <icode>%3</icode>", optionName(opt), optionName(opt2), optionName(opt3)));
731 void CommandOptions::setErrorParameter(Option opt)
733 setError(xi18nc("@info:shell", "Invalid <icode>%1</icode> parameter", optionName(opt)));
736 void CommandOptions::setErrorIncompatible(Option opt1, Option opt2)
738 setError(xi18nc("@info:shell", "<icode>%1</icode> incompatible with <icode>%2</icode>", optionName(opt1), optionName(opt2)));
741 void CommandOptions::checkEditType(EditAlarmDlg::Type type1, EditAlarmDlg::Type type2, Option opt)
743 if (mParser->isSet(*mOptions[opt]) && mCommand != NONE
744 && ((mCommand != NEW && mCommand != EDIT_NEW) || (mEditType != type1 && (type2 == EditAlarmDlg::NO_TYPE || mEditType != type2))))
745 setErrorIncompatible(opt, mCommandOpt);
748 // Fetch one of the arguments (i.e. not belonging to any option).
749 QString CommandOptions::arg(int n)
751 const QStringList args = mParser->positionalArguments();
752 return (n < args.size()) ? args[n] : QString();
755 QString CommandOptions::optionName(Option opt, bool shortName)
757 if (opt == Opt_Message)
758 return QStringLiteral("message");
759 const QStringList names = mOptions[opt]->names();
760 if (names.empty())
761 return QString();
762 for (int i = 0; i < names.size(); ++i)
764 if (shortName && names[i].size() == 1)
765 return QStringLiteral("-") + names[i];
766 else if (!shortName && names[i].size() > 1)
767 return QStringLiteral("--") + names[i];
769 return QStringLiteral("-") + names[0];
772 namespace
775 /******************************************************************************
776 * Convert a non-zero positive time interval command line parameter.
777 * 'timeInterval' receives the count for the recurType. If 'allowMonthYear' is
778 * false, weeks and days are converted to minutes.
779 * Reply = true if successful.
781 bool convInterval(const QString& timeParam, KARecurrence::Type& recurType, int& timeInterval, bool allowMonthYear)
783 QByteArray timeString = timeParam.toLocal8Bit();
784 // Get the recurrence interval
785 bool ok = true;
786 uint interval = 0;
787 uint length = timeString.length();
788 switch (timeString[length - 1])
790 case 'Y':
791 if (!allowMonthYear)
792 ok = false;
793 recurType = KARecurrence::ANNUAL_DATE;
794 timeString = timeString.left(length - 1);
795 break;
796 case 'W':
797 recurType = KARecurrence::WEEKLY;
798 timeString = timeString.left(length - 1);
799 break;
800 case 'D':
801 recurType = KARecurrence::DAILY;
802 timeString = timeString.left(length - 1);
803 break;
804 case 'M':
806 int i = timeString.indexOf('H');
807 if (i < 0)
809 if (!allowMonthYear)
810 ok = false;
811 recurType = KARecurrence::MONTHLY_DAY;
812 timeString = timeString.left(length - 1);
814 else
816 recurType = KARecurrence::MINUTELY;
817 interval = timeString.left(i).toUInt(&ok) * 60;
818 timeString = timeString.mid(i + 1, length - i - 2);
820 break;
822 default: // should be a digit
823 recurType = KARecurrence::MINUTELY;
824 break;
826 if (ok)
827 interval += timeString.toUInt(&ok);
828 if (!allowMonthYear)
830 // Convert time interval to minutes
831 switch (recurType)
833 case KARecurrence::WEEKLY:
834 interval *= 7;
835 // fall through to DAILY
836 case KARecurrence::DAILY:
837 interval *= 24*60;
838 recurType = KARecurrence::MINUTELY;
839 break;
840 default:
841 break;
844 timeInterval = static_cast<int>(interval);
845 return ok && interval;
850 // vim: et sw=4: