Git commit 41f82745d18b3f93953332af0b30593d4463aa0f by David Jarvie. Committed on 10/03/2022 at 16:19. Pushed by djarvie into branch 'master'.
Remove all 'speak' functions if text-to-speech not available at build time M +2 -1 Changelog M +10 -9 doc/index.docbook M +224 -139 src/commandoptions.cpp M +4 -63 src/commandoptions.h M +2 -2 src/data/kalarmconfig.kcfg M +5 -1 src/prefdlg.cpp M +19 -0 src/preferences.cpp M +2 -0 src/preferences.h M +5 -1 src/soundpicker.cpp https://invent.kde.org/pim/kalarm/commit/41f82745d18b3f93953332af0b30593d4463aa0f diff --git a/Changelog b/Changelog index ec5a734d..cdbb928b 100644 --- a/Changelog +++ b/Changelog @@ -1,9 +1,10 @@ KAlarm Change Log -=== Version 3.4.0 (KDE Applications 22.04) --- 4 March 2022 === +=== Version 3.4.0 (KDE Applications 22.04) --- 10 March 2022 === * Allow calendars and date picker to be shown together in side panel [KDE Bug 440250] * Shrink calendar list to remove empty space when too large. * Cancel sound file playback if audio alarm edit dialogue is closed after clicking Try. +* Remove all 'speak' functions if KAlarm is built with text-to-speech disabled. * Use threading correctly for playing sound files. * Incorporate kalarmcal library into KAlarm as a private library. diff --git a/doc/index.docbook b/doc/index.docbook index 60c76a59..44ff65ce 100644 --- a/doc/index.docbook +++ b/doc/index.docbook @@ -31,7 +31,7 @@ </authorgroup> <copyright> -<year>2001</year><year>2002</year><year>2003</year><year>2004</year><year>2005</year><year>2006</year><year>2007</year><year>2008</year><year>2009</year><year>2010</year><year>2011</year><year>2012</year><year>2013</year><year>2016</year><year>2018</year><year>2019</year><year>2020</year><year>2021</year> +<year>2001</year><year>2002</year><year>2003</year><year>2004</year><year>2005</year><year>2006</year><year>2007</year><year>2008</year><year>2009</year><year>2010</year><year>2011</year><year>2012</year><year>2013</year><year>2016</year><year>2018</year><year>2019</year><year>2020</year><year>2021</year><year>2022</year> <holder>&David.Jarvie;</holder> </copyright> @@ -39,8 +39,8 @@ <!-- Don't change format of date and version of the documentation --> -<date>2021-7-9</date> -<releaseinfo>3.3.0 (Applications 21.08)</releaseinfo> +<date>2022-3-10</date> +<releaseinfo>3.4.0 (Applications 22.04)</releaseinfo> <abstract> <para>&kalarm; is a personal alarm message, command and email scheduler by &kde;.</para> @@ -1274,8 +1274,9 @@ displayed. Choose:</para> <listitem> <para><guilabel>Speak</guilabel> to have the alarm message spoken as -well as being displayed. This option is only available if you have the -&Qt; Speech module installed and working.</para> +well as being displayed. This option is only available if &kalarm; was +built with speech enabled and you have the &Qt; Speech module installed +and working.</para> </listitem> <listitem> @@ -3348,10 +3349,10 @@ time.</para> <row> <entry><option>-s</option>, <option>--speak</option></entry> <entry>Speak the message when it is displayed. This option requires - &jovie; to be installed and configured, together with a compatible - speech synthesizer. <option>--beep</option>, <option>--play</option> - and <option>--play-repeat</option> cannot be specified with this - option.</entry> + &kalarm; to have been built with speech enabled and the &Qt; Speech + module to be installed and working. <option>--beep</option>, + <option>--play</option> and <option>--play-repeat</option> cannot be + specified with this option.</entry> </row> <row> <entry><option>-S</option>, <option>--subject diff --git a/src/commandoptions.cpp b/src/commandoptions.cpp index 8bc201ad..00387ba1 100644 --- a/src/commandoptions.cpp +++ b/src/commandoptions.cpp @@ -15,9 +15,6 @@ #include "kalarm_debug.h" #include <kpimtextedit/kpimtextedit-texttospeech.h> -#if KPIMTEXTEDIT_TEXT_TO_SPEECH -#include <KPIMTextEdit/TextToSpeech> -#endif #include <KLocalizedString> #include <QCommandLineParser> @@ -26,13 +23,88 @@ namespace { +enum Option +{ + ACK_CONFIRM, + ATTACH, + AUTO_CLOSE, + BCC, + BEEP, + COLOUR, + COLOURFG, + OptCANCEL_EVENT, + DISABLE, + DISABLE_ALL, + EXEC, + EXEC_DISPLAY, + OptEDIT, + EDIT_NEW_DISPLAY, + EDIT_NEW_COMMAND, + EDIT_NEW_EMAIL, + EDIT_NEW_AUDIO, + OptEDIT_NEW_PRESET, + OptFILE, + FROM_ID, + INTERVAL, + KORGANIZER, + LATE_CANCEL, + OptLIST, + LOGIN, + MAIL, + NAME, + NOTIFY, + PLAY, + PLAY_REPEAT, + RECURRENCE, + REMINDER, + REMINDER_ONCE, + REPEAT, +#if KPIMTEXTEDIT_TEXT_TO_SPEECH + SPEAK, +#endif + SUBJECT, +#ifndef NDEBUG + TEST_SET_TIME, +#endif + TIME, + OptTRAY, + OptTRIGGER_EVENT, + UNTIL, + VOLUME, + Num_Options, // number of Option values + Opt_Message // special value representing "message" +}; + bool convInterval(const QString& timeParam, KARecurrence::Type&, int& timeInterval, bool allowMonthYear = false); } +class CommandOptions::Private +{ +public: + Private(CommandOptions* parent) + : p(parent) + {} + bool checkCommand(Option, Command, EditAlarmDlg::Type = EditAlarmDlg::NO_TYPE); + void setError(const QString& error); + void setErrorRequires(Option opt, Option opt2, Option opt3 = Num_Options); + void setErrorParameter(Option); + void setErrorIncompatible(Option opt1, Option opt2); + void checkEditType(EditAlarmDlg::Type type, Option opt) + { checkEditType(type, EditAlarmDlg::NO_TYPE, opt); } + void checkEditType(EditAlarmDlg::Type, EditAlarmDlg::Type, Option); + QString optionName(Option, bool shortName = false) const; + + CommandOptions* const p; + Option mCommandOpt; // option for the selected command +}; + +/*===========================================================================*/ + CommandOptions* CommandOptions::mFirstInstance = nullptr; CommandOptions::CommandOptions() - : mOptions(Num_Options, nullptr) + : d(new Private(this)) + , mOptions(Num_Options, nullptr) , mBgColour(Preferences::defaultBgColour()) , mFgColour(Preferences::defaultFgColour()) , mFlags(KAEvent::DEFAULT_FONT) @@ -112,7 +184,7 @@ QStringList CommandOptions::setOptions(QCommandLineParser* parser, const QString = new QCommandLineOption(QStringLiteral("edit-new-preset"), i18n("Display the alarm edit dialog, preset with a template"), QStringLiteral("templateName")); - mOptions[FILE] + mOptions[OptFILE] = new QCommandLineOption(QStringList{QStringLiteral("f"), QStringLiteral("file")}, i18n("File to display"), QStringLiteral("url")); @@ -172,9 +244,11 @@ QStringList CommandOptions::setOptions(QCommandLineParser* parser, const QString = new QCommandLineOption(QStringList{QStringLiteral("r"), QStringLiteral("repeat")}, i18n("Number of times to repeat alarm (including initial occasion)"), QStringLiteral("count")); +#if KPIMTEXTEDIT_TEXT_TO_SPEECH mOptions[SPEAK] = new QCommandLineOption(QStringList{QStringLiteral("s"), QStringLiteral("speak")}, i18n("Speak the message when it is displayed")); +#endif mOptions[SUBJECT] = new QCommandLineOption(QStringList{QStringLiteral("S"), QStringLiteral("subject")}, i18n("Email subject line"), @@ -224,8 +298,8 @@ QStringList CommandOptions::setOptions(QCommandLineParser* parser, const QString if (arg == QLatin1String("--nofork")) continue; // Ignore debugging option mNonExecArguments << arg; - if (arg == optionName(EXEC) || arg == optionName(EXEC, true) - || arg == optionName(EXEC_DISPLAY) || arg == optionName(EXEC_DISPLAY, true)) + if (arg == d->optionName(EXEC) || arg == d->optionName(EXEC, true) + || arg == d->optionName(EXEC_DISPLAY) || arg == d->optionName(EXEC_DISPLAY, true)) { // All following arguments (including ones beginning with '-') // belong to this option. QCommandLineParser can't handle this, so @@ -247,7 +321,7 @@ void CommandOptions::parse() { qCWarning(KALARM_LOG) << "CommandOptions::parse:" << mParser->errorText(); mError.clear(); - setError(mParser->errorText()); + d->setError(mParser->errorText()); } else mParser->process(mNonExecArguments); @@ -263,58 +337,58 @@ void CommandOptions::process() { const QString time = mParser->value(*mOptions.at(TEST_SET_TIME)); if (!KAlarm::convertTimeString(time.toLatin1(), mSimulationTime, KADateTime::realCurrentLocalDateTime(), true)) - setErrorParameter(TEST_SET_TIME); + d->setErrorParameter(TEST_SET_TIME); } #endif - if (checkCommand(OptTRAY, TRAY)) + if (d->checkCommand(OptTRAY, TRAY)) { } - if (checkCommand(OptLIST, LIST)) + if (d->checkCommand(OptLIST, LIST)) { if (!mParser->positionalArguments().empty()) - setErrorParameter(OptLIST); + d->setErrorParameter(OptLIST); } - if (checkCommand(OptTRIGGER_EVENT, TRIGGER_EVENT) - || checkCommand(OptCANCEL_EVENT, CANCEL_EVENT) - || checkCommand(OptEDIT, EDIT)) + if (d->checkCommand(OptTRIGGER_EVENT, TRIGGER_EVENT) + || d->checkCommand(OptCANCEL_EVENT, CANCEL_EVENT) + || d->checkCommand(OptEDIT, EDIT)) { // Fetch the resource and event IDs. The supplied ID is the event ID, // optionally prefixed by the resource ID followed by a colon delimiter. - mResourceId = EventId::extractIDs(mParser->value(*mOptions.at(mCommandOpt)), mEventId); + mResourceId = EventId::extractIDs(mParser->value(*mOptions.at(d->mCommandOpt)), mEventId); } - if (checkCommand(OptEDIT_NEW_PRESET, EDIT_NEW_PRESET)) + if (d->checkCommand(OptEDIT_NEW_PRESET, EDIT_NEW_PRESET)) { - mName = mParser->value(*mOptions.at(mCommandOpt)); + mName = mParser->value(*mOptions.at(d->mCommandOpt)); } - if (checkCommand(FILE, NEW)) + if (d->checkCommand(OptFILE, NEW)) { mEditType = EditAlarmDlg::DISPLAY; mEditAction = KAEvent::FILE; mEditActionSet = true; - mText = mParser->value(*mOptions.at(mCommandOpt)); + mText = mParser->value(*mOptions.at(d->mCommandOpt)); } - if (checkCommand(EXEC_DISPLAY, NEW)) + if (d->checkCommand(EXEC_DISPLAY, NEW)) { mEditType = EditAlarmDlg::DISPLAY; mEditAction = KAEvent::COMMAND; mEditActionSet = true; mFlags |= KAEvent::DISPLAY_COMMAND; - mText = mParser->value(*mOptions.at(mCommandOpt)) + QLatin1String(" ") + mExecArguments.join(QLatin1Char(' ')); + mText = mParser->value(*mOptions.at(d->mCommandOpt)) + QLatin1String(" ") + mExecArguments.join(QLatin1Char(' ')); } - if (checkCommand(EXEC, NEW)) + if (d->checkCommand(EXEC, NEW)) { mEditType = EditAlarmDlg::COMMAND; mEditAction = KAEvent::COMMAND; mEditActionSet = true; - mText = mParser->value(*mOptions.at(mCommandOpt)) + QLatin1String(" ") + mExecArguments.join(QLatin1Char(' ')); + mText = mParser->value(*mOptions.at(d->mCommandOpt)) + QLatin1String(" ") + mExecArguments.join(QLatin1Char(' ')); } - if (checkCommand(MAIL, NEW)) + if (d->checkCommand(MAIL, NEW)) { mEditType = EditAlarmDlg::EMAIL; mEditAction = KAEvent::EMAIL; mEditActionSet = true; } - if (checkCommand(EDIT_NEW_DISPLAY, EDIT_NEW, EditAlarmDlg::DISPLAY)) + if (d->checkCommand(EDIT_NEW_DISPLAY, EDIT_NEW, EditAlarmDlg::DISPLAY)) { mEditType = EditAlarmDlg::DISPLAY; if (!mEditActionSet || (mEditAction != KAEvent::COMMAND && mEditAction != KAEvent::FILE)) @@ -326,19 +400,19 @@ void CommandOptions::process() if (!args.empty()) mText = args[0]; } - if (checkCommand(EDIT_NEW_COMMAND, EDIT_NEW)) + if (d->checkCommand(EDIT_NEW_COMMAND, EDIT_NEW)) { mEditType = EditAlarmDlg::COMMAND; mEditAction = KAEvent::COMMAND; mEditActionSet = true; } - if (checkCommand(EDIT_NEW_EMAIL, EDIT_NEW, EditAlarmDlg::EMAIL)) + if (d->checkCommand(EDIT_NEW_EMAIL, EDIT_NEW, EditAlarmDlg::EMAIL)) { mEditType = EditAlarmDlg::EMAIL; mEditAction = KAEvent::EMAIL; mEditActionSet = true; } - if (checkCommand(EDIT_NEW_AUDIO, EDIT_NEW, EditAlarmDlg::AUDIO)) + if (d->checkCommand(EDIT_NEW_AUDIO, EDIT_NEW, EditAlarmDlg::AUDIO)) { mEditType = EditAlarmDlg::AUDIO; mEditAction = KAEvent::AUDIO; @@ -348,7 +422,7 @@ void CommandOptions::process() { if (mParser->positionalArguments().empty()) { - if (checkCommand(PLAY, NEW) || checkCommand(PLAY_REPEAT, NEW)) + if (d->checkCommand(PLAY, NEW) || d->checkCommand(PLAY_REPEAT, NEW)) { mEditType = EditAlarmDlg::AUDIO; mEditAction = KAEvent::AUDIO; @@ -359,7 +433,7 @@ void CommandOptions::process() { qCDebug(KALARM_LOG) << "CommandOptions::process: Message"; mCommand = NEW; - mCommandOpt = Opt_Message; + d->mCommandOpt = Opt_Message; mEditType = EditAlarmDlg::DISPLAY; mEditAction = KAEvent::MESSAGE; mEditActionSet = true; @@ -377,7 +451,7 @@ void CommandOptions::process() { QString a(addr); if (!KAMail::checkAddress(a)) - setError(xi18nc("@info:shell", "<icode>%1</icode>: invalid email address", optionName(MAIL))); + d->setError(xi18nc("@info:shell", "<icode>%1</icode>: invalid email address", d->optionName(MAIL))); KCalendarCore::Person person(QString(), addr); mAddressees += person; } @@ -391,34 +465,36 @@ void CommandOptions::process() if (mParser->isSet(*mOptions.at(DISABLE_ALL))) { if (mCommand == TRIGGER_EVENT || mCommand == LIST) - setErrorIncompatible(DISABLE_ALL, mCommandOpt); + d->setErrorIncompatible(DISABLE_ALL, d->mCommandOpt); mDisableAll = true; } // Check that other options are only specified for the // correct main command options. - checkEditType(EditAlarmDlg::DISPLAY, COLOUR); - checkEditType(EditAlarmDlg::DISPLAY, COLOURFG); - checkEditType(EditAlarmDlg::DISPLAY, EditAlarmDlg::AUDIO, PLAY); - checkEditType(EditAlarmDlg::DISPLAY, EditAlarmDlg::AUDIO, PLAY_REPEAT); - checkEditType(EditAlarmDlg::DISPLAY, EditAlarmDlg::AUDIO, VOLUME); - checkEditType(EditAlarmDlg::DISPLAY, SPEAK); - checkEditType(EditAlarmDlg::DISPLAY, BEEP); - checkEditType(EditAlarmDlg::DISPLAY, REMINDER); - checkEditType(EditAlarmDlg::DISPLAY, REMINDER_ONCE); - checkEditType(EditAlarmDlg::DISPLAY, ACK_CONFIRM); - checkEditType(EditAlarmDlg::DISPLAY, AUTO_CLOSE); - checkEditType(EditAlarmDlg::DISPLAY, NOTIFY); - checkEditType(EditAlarmDlg::EMAIL, SUBJECT); - checkEditType(EditAlarmDlg::EMAIL, FROM_ID); - checkEditType(EditAlarmDlg::EMAIL, ATTACH); - checkEditType(EditAlarmDlg::EMAIL, BCC); + d->checkEditType(EditAlarmDlg::DISPLAY, COLOUR); + d->checkEditType(EditAlarmDlg::DISPLAY, COLOURFG); + d->checkEditType(EditAlarmDlg::DISPLAY, EditAlarmDlg::AUDIO, PLAY); + d->checkEditType(EditAlarmDlg::DISPLAY, EditAlarmDlg::AUDIO, PLAY_REPEAT); + d->checkEditType(EditAlarmDlg::DISPLAY, EditAlarmDlg::AUDIO, VOLUME); +#if KPIMTEXTEDIT_TEXT_TO_SPEECH + d->checkEditType(EditAlarmDlg::DISPLAY, SPEAK); +#endif + d->checkEditType(EditAlarmDlg::DISPLAY, BEEP); + d->checkEditType(EditAlarmDlg::DISPLAY, REMINDER); + d->checkEditType(EditAlarmDlg::DISPLAY, REMINDER_ONCE); + d->checkEditType(EditAlarmDlg::DISPLAY, ACK_CONFIRM); + d->checkEditType(EditAlarmDlg::DISPLAY, AUTO_CLOSE); + d->checkEditType(EditAlarmDlg::DISPLAY, NOTIFY); + d->checkEditType(EditAlarmDlg::EMAIL, SUBJECT); + d->checkEditType(EditAlarmDlg::EMAIL, FROM_ID); + d->checkEditType(EditAlarmDlg::EMAIL, ATTACH); + d->checkEditType(EditAlarmDlg::EMAIL, BCC); switch (mCommand) { case EDIT_NEW: if (mParser->isSet(*mOptions.at(DISABLE))) - setErrorIncompatible(DISABLE, mCommandOpt); + d->setErrorIncompatible(DISABLE, d->mCommandOpt); // Fall through to NEW Q_FALLTHROUGH(); case NEW: @@ -438,7 +514,7 @@ void CommandOptions::process() colourText.replace(0, 2, QStringLiteral("#")); mBgColour.setNamedColor(colourText); if (!mBgColour.isValid()) - setErrorParameter(COLOUR); + d->setErrorParameter(COLOUR); } if (mParser->isSet(*mOptions.at(COLOURFG))) { @@ -449,14 +525,14 @@ void CommandOptions::process() colourText.replace(0, 2, QStringLiteral("#")); mFgColour.setNamedColor(colourText); if (!mFgColour.isValid()) - setErrorParameter(COLOURFG); + d->setErrorParameter(COLOURFG); } if (mParser->isSet(*mOptions.at(TIME))) { const QByteArray dateTime = mParser->value(*mOptions.at(TIME)).toLocal8Bit(); if (!KAlarm::convertTimeString(dateTime, mAlarmTime)) - setErrorParameter(TIME); + d->setErrorParameter(TIME); } else mAlarmTime = KADateTime::currentLocalDateTime(); @@ -465,9 +541,9 @@ void CommandOptions::process() if (haveRecurrence) { if (mParser->isSet(*mOptions.at(LOGIN))) - setErrorIncompatible(LOGIN, RECURRENCE); + d->setErrorIncompatible(LOGIN, RECURRENCE); else if (mParser->isSet(*mOptions.at(UNTIL))) - setErrorIncompatible(UNTIL, RECURRENCE); + d->setErrorIncompatible(UNTIL, RECURRENCE); const QString rule = mParser->value(*mOptions.at(RECURRENCE)); mRecurrence = new KARecurrence; mRecurrence->set(rule); @@ -478,16 +554,16 @@ void CommandOptions::process() int count = 0; KADateTime endTime; if (mParser->isSet(*mOptions.at(LOGIN))) - setErrorIncompatible(LOGIN, INTERVAL); + d->setErrorIncompatible(LOGIN, INTERVAL); bool ok; if (mParser->isSet(*mOptions.at(REPEAT))) { count = mParser->value(*mOptions.at(REPEAT)).toInt(&ok); if (!ok || !count || count < -1 || (count < 0 && haveRecurrence)) - setErrorParameter(REPEAT); + d->setErrorParameter(REPEAT); } else if (haveRecurrence) - setErrorRequires(INTERVAL, REPEAT); + d->setErrorRequires(INTERVAL, REPEAT); else if (mParser->isSet(*mOptions.at(UNTIL))) { count = 0; @@ -497,13 +573,13 @@ void CommandOptions::process() else ok = KAlarm::convertTimeString(dateTime, endTime); if (!ok) - setErrorParameter(UNTIL); + d->setErrorParameter(UNTIL); else if (mAlarmTime.isDateOnly() && !endTime.isDateOnly()) - setError(xi18nc("@info:shell", "Invalid <icode>%1</icode> parameter for date-only alarm", optionName(UNTIL))); + d->setError(xi18nc("@info:shell", "Invalid <icode>%1</icode> parameter for date-only alarm", d->optionName(UNTIL))); if (!mAlarmTime.isDateOnly() && endTime.isDateOnly()) endTime.setTime(QTime(23,59,59)); if (endTime < mAlarmTime) - setError(xi18nc("@info:shell", "<icode>%1</icode> earlier than <icode>%2</icode>", optionName(UNTIL), optionName(TIME))); + d->setError(xi18nc("@info:shell", "<icode>%1</icode> earlier than <icode>%2</icode>", d->optionName(UNTIL), d->optionName(TIME))); } else count = -1; @@ -512,9 +588,9 @@ void CommandOptions::process() int intervalOfType; KARecurrence::Type recurType; if (!convInterval(mParser->value(*mOptions.at(INTERVAL)), recurType, intervalOfType, !haveRecurrence)) - setErrorParameter(INTERVAL); + d->setErrorParameter(INTERVAL); else if (mAlarmTime.isDateOnly() && recurType == KARecurrence::MINUTELY) - setError(xi18nc("@info:shell", "Invalid <icode>%1</icode> parameter for date-only alarm", optionName(INTERVAL))); + d->setError(xi18nc("@info:shell", "Invalid <icode>%1</icode> parameter for date-only alarm", d->optionName(INTERVAL))); if (haveRecurrence) { @@ -525,8 +601,8 @@ void CommandOptions::process() mRepeatInterval = KCalendarCore::Duration(intervalOfType * 60); const KCalendarCore::Duration longestInterval = mRecurrence->longestInterval(); if (mRepeatInterval * count > longestInterval) - setError(xi18nc("@info:shell", "Invalid <icode>%1</icode> and <icode>%2</icode> parameters: repetition is longer than <icode>%3</icode> interval", - optionName(INTERVAL), optionName(REPEAT), optionName(RECURRENCE))); + d->setError(xi18nc("@info:shell", "Invalid <icode>%1</icode> and <icode>%2</icode> parameters: repetition is longer than <icode>%3</icode> interval", + d->optionName(INTERVAL), d->optionName(REPEAT), d->optionName(RECURRENCE))); mRepeatCount = count; } } @@ -541,9 +617,9 @@ void CommandOptions::process() else { if (mParser->isSet(*mOptions.at(REPEAT))) - setErrorRequires(REPEAT, INTERVAL); + d->setErrorRequires(REPEAT, INTERVAL); else if (mParser->isSet(*mOptions.at(UNTIL))) - setErrorRequires(UNTIL, INTERVAL); + d->setErrorRequires(UNTIL, INTERVAL); } const bool audioRepeat = mParser->isSet(*mOptions.at(PLAY_REPEAT)); @@ -552,40 +628,38 @@ void CommandOptions::process() // Play a sound with the alarm const Option opt = audioRepeat ? PLAY_REPEAT : PLAY; if (audioRepeat && mParser->isSet(*mOptions.at(PLAY))) - setErrorIncompatible(PLAY, PLAY_REPEAT); + d->setErrorIncompatible(PLAY, PLAY_REPEAT); if (mParser->isSet(*mOptions.at(BEEP))) - setErrorIncompatible(BEEP, opt); + d->setErrorIncompatible(BEEP, opt); +#if KPIMTEXTEDIT_TEXT_TO_SPEECH else if (mParser->isSet(*mOptions.at(SPEAK))) - setErrorIncompatible(SPEAK, opt); + d->setErrorIncompatible(SPEAK, opt); +#endif mAudioFile = mParser->value(*mOptions.at(audioRepeat ? PLAY_REPEAT : PLAY)); if (mParser->isSet(*mOptions.at(VOLUME))) { bool ok; const int volumepc = mParser->value(*mOptions.at(VOLUME)).toInt(&ok); if (!ok || volumepc < 0 || volumepc > 100) - setErrorParameter(VOLUME); + d->setErrorParameter(VOLUME); mAudioVolume = static_cast<float>(volumepc) / 100; } } else if (mParser->isSet(*mOptions.at(VOLUME))) - setErrorRequires(VOLUME, PLAY, PLAY_REPEAT); + d->setErrorRequires(VOLUME, PLAY, PLAY_REPEAT); +#if KPIMTEXTEDIT_TEXT_TO_SPEECH if (mParser->isSet(*mOptions.at(SPEAK))) { if (mParser->isSet(*mOptions.at(BEEP))) - setErrorIncompatible(BEEP, SPEAK); -#if KPIMTEXTEDIT_TEXT_TO_SPEECH - else if (!KPIMTextEdit::TextToSpeech::self()->isReady()) -#else - else -#endif - setError(xi18nc("@info:shell", "<icode>%1</icode> requires KAlarm to be compiled with QTextToSpeech support", optionName(SPEAK))); + d->setErrorIncompatible(BEEP, SPEAK); } +#endif const bool onceOnly = mParser->isSet(*mOptions.at(REMINDER_ONCE)); if (mParser->isSet(*mOptions.at(REMINDER)) || onceOnly) { // Issue a reminder alarm in advance of or after the main alarm if (onceOnly && mParser->isSet(*mOptions.at(REMINDER))) - setErrorIncompatible(REMINDER, REMINDER_ONCE); + d->setErrorIncompatible(REMINDER, REMINDER_ONCE); const Option opt = onceOnly ? REMINDER_ONCE : REMINDER; KARecurrence::Type recurType; QString optval = mParser->value(*mOptions.at(onceOnly ? REMINDER_ONCE : REMINDER)); @@ -593,9 +667,9 @@ void CommandOptions::process() if (after) optval.remove(0, 1); // it's a reminder after the main alarm if (!convInterval(optval, recurType, mReminderMinutes)) - setErrorParameter(opt); + d->setErrorParameter(opt); else if (recurType == KARecurrence::MINUTELY && mAlarmTime.isDateOnly()) - setError(xi18nc("@info:shell", "Invalid <icode>%1</icode> parameter for date-only alarm", optionName(opt))); + d->setError(xi18nc("@info:shell", "Invalid <icode>%1</icode> parameter for date-only alarm", d->optionName(opt))); if (after) mReminderMinutes = -mReminderMinutes; if (onceOnly) @@ -607,23 +681,23 @@ void CommandOptions::process() KARecurrence::Type recurType; const bool ok = convInterval(mParser->value(*mOptions.at(LATE_CANCEL)), recurType, mLateCancel); if (!ok) - setErrorParameter(LATE_CANCEL); + d->setErrorParameter(LATE_CANCEL); } else if (mParser->isSet(*mOptions.at(AUTO_CLOSE))) - setErrorRequires(AUTO_CLOSE, LATE_CANCEL); + d->setErrorRequires(AUTO_CLOSE, LATE_CANCEL); if (mParser->isSet(*mOptions.at(NOTIFY))) { if (mParser->isSet(*mOptions.at(COLOUR))) - setErrorIncompatible(NOTIFY, COLOUR); + d->setErrorIncompatible(NOTIFY, COLOUR); if (mParser->isSet(*mOptions.at(COLOURFG))) - setErrorIncompatible(NOTIFY, COLOURFG); + d->setErrorIncompatible(NOTIFY, COLOURFG); if (mParser->isSet(*mOptions.at(ACK_CONFIRM))) - setErrorIncompatible(NOTIFY, ACK_CONFIRM); + d->setErrorIncompatible(NOTIFY, ACK_CONFIRM); if (mParser->isSet(*mOptions.at(PLAY))) - setErrorIncompatible(NOTIFY, PLAY); + d->setErrorIncompatible(NOTIFY, PLAY); if (mParser->isSet(*mOptions.at(AUTO_CLOSE))) - setErrorIncompatible(NOTIFY, AUTO_CLOSE); + d->setErrorIncompatible(NOTIFY, AUTO_CLOSE); } if (mParser->isSet(*mOptions.at(ACK_CONFIRM))) @@ -632,8 +706,10 @@ void CommandOptions::process() mFlags |= KAEvent::AUTO_CLOSE; if (mParser->isSet(*mOptions.at(BEEP))) mFlags |= KAEvent::BEEP; +#if KPIMTEXTEDIT_TEXT_TO_SPEECH if (mParser->isSet(*mOptions.at(SPEAK))) mFlags |= KAEvent::SPEAK; +#endif if (mParser->isSet(*mOptions.at(NOTIFY))) mFlags |= KAEvent::NOTIFY; if (mParser->isSet(*mOptions.at(KORGANIZER))) @@ -658,51 +734,53 @@ void CommandOptions::process() qCDebug(KALARM_LOG) << "CommandOptions::process: Interactive"; QStringList errors; if (mParser->isSet(*mOptions.at(ACK_CONFIRM))) - errors << optionName(ACK_CONFIRM); + errors << d->optionName(ACK_CONFIRM); if (mParser->isSet(*mOptions.at(ATTACH))) - errors << optionName(ATTACH); + errors << d->optionName(ATTACH); if (mParser->isSet(*mOptions.at(AUTO_CLOSE))) - errors << optionName(AUTO_CLOSE); + errors << d->optionName(AUTO_CLOSE); if (mParser->isSet(*mOptions.at(BCC))) - errors << optionName(BCC); + errors << d->optionName(BCC); if (mParser->isSet(*mOptions.at(BEEP))) - errors << optionName(BEEP); + errors << d->optionName(BEEP); if (mParser->isSet(*mOptions.at(COLOUR))) - errors << optionName(COLOUR); + errors << d->optionName(COLOUR); if (mParser->isSet(*mOptions.at(COLOURFG))) - errors << optionName(COLOURFG); + errors << d->optionName(COLOURFG); if (mParser->isSet(*mOptions.at(DISABLE))) - errors << optionName(DISABLE); + errors << d->optionName(DISABLE); if (mParser->isSet(*mOptions.at(FROM_ID))) - errors << optionName(FROM_ID); + errors << d->optionName(FROM_ID); if (mParser->isSet(*mOptions.at(KORGANIZER))) - errors << optionName(KORGANIZER); + errors << d->optionName(KORGANIZER); if (mParser->isSet(*mOptions.at(LATE_CANCEL))) - errors << optionName(LATE_CANCEL); + errors << d->optionName(LATE_CANCEL); if (mParser->isSet(*mOptions.at(LOGIN))) - errors << optionName(LOGIN); + errors << d->optionName(LOGIN); if (mParser->isSet(*mOptions.at(NAME))) - errors << optionName(NAME); + errors << d->optionName(NAME); if (mParser->isSet(*mOptions.at(NOTIFY))) - errors << optionName(NOTIFY); + errors << d->optionName(NOTIFY); if (mParser->isSet(*mOptions.at(PLAY))) - errors << optionName(PLAY); + errors << d->optionName(PLAY); if (mParser->isSet(*mOptions.at(PLAY_REPEAT))) - errors << optionName(PLAY_REPEAT); + errors << d->optionName(PLAY_REPEAT); if (mParser->isSet(*mOptions.at(REMINDER))) - errors << optionName(REMINDER); + errors << d->optionName(REMINDER); if (mParser->isSet(*mOptions.at(REMINDER_ONCE))) - errors << optionName(REMINDER_ONCE); + errors << d->optionName(REMINDER_ONCE); +#if KPIMTEXTEDIT_TEXT_TO_SPEECH if (mParser->isSet(*mOptions.at(SPEAK))) - errors << optionName(SPEAK); + errors << d->optionName(SPEAK); +#endif if (mParser->isSet(*mOptions.at(NOTIFY))) - errors << optionName(NOTIFY); + errors << d->optionName(NOTIFY); if (mParser->isSet(*mOptions.at(SUBJECT))) - errors << optionName(SUBJECT); + errors << d->optionName(SUBJECT); if (mParser->isSet(*mOptions.at(TIME))) - errors << optionName(TIME); + errors << d->optionName(TIME); if (mParser->isSet(*mOptions.at(VOLUME))) - errors << optionName(VOLUME); + errors << d->optionName(VOLUME); if (!errors.isEmpty()) mError = errors.join(QLatin1Char(' ')) + i18nc("@info:shell", ": option(s) only valid with an appropriate action option or message"); break; @@ -712,15 +790,12 @@ void CommandOptions::process() } if (!mError.isEmpty()) - setError(mError); + d->setError(mError); } -void CommandOptions::setError(const QString& errmsg) +QString CommandOptions::commandName() const { - qCWarning(KALARM_LOG) << "CommandOptions::setError:" << errmsg; - mCommand = CMD_ERROR; - if (mError.isEmpty()) - mError = errmsg + i18nc("@info:shell", "\nUse --help to get a list of available command line options.\n"); + return d->optionName(d->mCommandOpt); } void CommandOptions::printError(const QString& errmsg) @@ -731,6 +806,23 @@ void CommandOptions::printError(const QString& errmsg) << i18nc("@info:shell", "\nUse --help to get a list of available command line options.\n").toLocal8Bit().data(); } +// Fetch one of the arguments (i.e. not belonging to any option). +QString CommandOptions::arg(int n) +{ + const QStringList args = mParser->positionalArguments(); + return (n < args.size()) ? args[n] : QString(); +} + +/*===========================================================================*/ + +void CommandOptions::Private::setError(const QString& errmsg) +{ + qCWarning(KALARM_LOG) << "CommandOptions::setError:" << errmsg; + p->mCommand = CMD_ERROR; + if (p->mError.isEmpty()) + p->mError = errmsg + i18nc("@info:shell", "\nUse --help to get a list of available command line options.\n"); +} + /****************************************************************************** * Check if the given command option is specified, and if so set mCommand etc. * If another command option has also been detected, issue an error. @@ -738,22 +830,22 @@ void CommandOptions::printError(const QString& errmsg) * edit type with the given command option - this allows, e.g., --mail to be * used along with --edit-new-email so the user can specify addressees. */ -bool CommandOptions::checkCommand(Option command, Command code, EditAlarmDlg::Type allowedEditType) +bool CommandOptions::Private::checkCommand(Option command, Command code, EditAlarmDlg::Type allowedEditType) { - if (!mError.isEmpty() - || !mParser->isSet(*mOptions.at(command))) + if (!p->mError.isEmpty() + || !p->mParser->isSet(*p->mOptions.at(command))) return false; - if (mCommand != NONE - && (allowedEditType == EditAlarmDlg::NO_TYPE || (mCommand != NEW || mEditType != allowedEditType))) + if (p->mCommand != NONE + && (allowedEditType == EditAlarmDlg::NO_TYPE || (p->mCommand != NEW || p->mEditType != allowedEditType))) setErrorIncompatible(mCommandOpt, command); qCDebug(KALARM_LOG).nospace() << "CommandOptions::checkCommand: " << optionName(command); - mCommand = code; + p->mCommand = code; mCommandOpt = command; return true; } // Set the error message to "--opt requires --opt2" or "--opt requires --opt2 or --opt3". -void CommandOptions::setErrorRequires(Option opt, Option opt2, Option opt3) +void CommandOptions::Private::setErrorRequires(Option opt, Option opt2, Option opt3) { if (opt3 == Num_Options) setError(xi18nc("@info:shell", "<icode>%1</icode> requires <icode>%2</icode>", optionName(opt), optionName(opt2))); @@ -761,35 +853,28 @@ void CommandOptions::setErrorRequires(Option opt, Option opt2, Option opt3) setError(xi18nc("@info:shell", "<icode>%1</icode> requires <icode>%2</icode> or <icode>%3</icode>", optionName(opt), optionName(opt2), optionName(opt3))); } -void CommandOptions::setErrorParameter(Option opt) +void CommandOptions::Private::setErrorParameter(Option opt) { setError(xi18nc("@info:shell", "Invalid <icode>%1</icode> parameter", optionName(opt))); } -void CommandOptions::setErrorIncompatible(Option opt1, Option opt2) +void CommandOptions::Private::setErrorIncompatible(Option opt1, Option opt2) { setError(xi18nc("@info:shell", "<icode>%1</icode> incompatible with <icode>%2</icode>", optionName(opt1), optionName(opt2))); } -void CommandOptions::checkEditType(EditAlarmDlg::Type type1, EditAlarmDlg::Type type2, Option opt) +void CommandOptions::Private::checkEditType(EditAlarmDlg::Type type1, EditAlarmDlg::Type type2, Option opt) { - if (mParser->isSet(*mOptions.at(opt)) && mCommand != NONE - && ((mCommand != NEW && mCommand != EDIT_NEW) || (mEditType != type1 && (type2 == EditAlarmDlg::NO_TYPE || mEditType != type2)))) + if (p->mParser->isSet(*p->mOptions.at(opt)) && p->mCommand != NONE + && ((p->mCommand != NEW && p->mCommand != EDIT_NEW) || (p->mEditType != type1 && (type2 == EditAlarmDlg::NO_TYPE || p->mEditType != type2)))) setErrorIncompatible(opt, mCommandOpt); } -// Fetch one of the arguments (i.e. not belonging to any option). -QString CommandOptions::arg(int n) -{ - const QStringList args = mParser->positionalArguments(); - return (n < args.size()) ? args[n] : QString(); -} - -QString CommandOptions::optionName(Option opt, bool shortName) const +QString CommandOptions::Private::optionName(Option opt, bool shortName) const { if (opt == Opt_Message) return QStringLiteral("message"); - const QStringList names = mOptions.at(opt)->names(); + const QStringList names = p->mOptions.at(opt)->names(); if (names.empty()) return {}; for (const QString& name : names) diff --git a/src/commandoptions.h b/src/commandoptions.h index f6cb5e60..23e3532e 100644 --- a/src/commandoptions.h +++ b/src/commandoptions.h @@ -9,8 +9,6 @@ #pragma once #include "editdlg.h" -#include "eventid.h" - #include "kalarmcalendar/kaevent.h" #include "kalarmcalendar/karecurrence.h" #include "kalarmcalendar/kadatetime.h" @@ -44,7 +42,7 @@ public: void parse(); void process(); Command command() const { return mCommand; } - QString commandName() const { return optionName(mCommandOpt); } + QString commandName() const; QString eventId() const { return mEventId; } QString resourceId() const { return mResourceId; } QString name() const { return mName; } @@ -74,66 +72,10 @@ public: static void printError(const QString& errmsg); private: - enum Option - { - ACK_CONFIRM, - ATTACH, - AUTO_CLOSE, - BCC, - BEEP, - COLOUR, - COLOURFG, - OptCANCEL_EVENT, - DISABLE, - DISABLE_ALL, - EXEC, - EXEC_DISPLAY, - OptEDIT, - EDIT_NEW_DISPLAY, - EDIT_NEW_COMMAND, - EDIT_NEW_EMAIL, - EDIT_NEW_AUDIO, - OptEDIT_NEW_PRESET, - FILE, - FROM_ID, - INTERVAL, - KORGANIZER, - LATE_CANCEL, - OptLIST, - LOGIN, - MAIL, - NAME, - NOTIFY, - PLAY, - PLAY_REPEAT, - RECURRENCE, - REMINDER, - REMINDER_ONCE, - REPEAT, - SPEAK, - SUBJECT, -#ifndef NDEBUG - TEST_SET_TIME, -#endif - TIME, - OptTRAY, - OptTRIGGER_EVENT, - UNTIL, - VOLUME, - Num_Options, // number of Option values - Opt_Message // special value representing "message" - }; - - bool checkCommand(Option, Command, EditAlarmDlg::Type = EditAlarmDlg::NO_TYPE); - void setError(const QString& error); - void setErrorRequires(Option opt, Option opt2, Option opt3 = Num_Options); - void setErrorParameter(Option); - void setErrorIncompatible(Option opt1, Option opt2); - void checkEditType(EditAlarmDlg::Type type, Option opt) - { checkEditType(type, EditAlarmDlg::NO_TYPE, opt); } - void checkEditType(EditAlarmDlg::Type, EditAlarmDlg::Type, Option); + class Private; + friend class Private; + Private* const d; QString arg(int n); - QString optionName(Option, bool shortName = false) const; static CommandOptions* mFirstInstance; // the first instance QCommandLineParser* mParser {nullptr}; @@ -142,7 +84,6 @@ private: QStringList mExecArguments; // arguments for --exec or --exec-display QString mError; // error message Command mCommand {NONE}; // the selected command - Option mCommandOpt; // option for the selected command QString mEventId; // TRIGGER_EVENT, CANCEL_EVENT, EDIT: event ID QString mResourceId; // TRIGGER_EVENT, CANCEL_EVENT, EDIT: optional resource ID QString mName; // NEW, EDIT_NEW_PRESET: alarm/template name diff --git a/src/data/kalarmconfig.kcfg b/src/data/kalarmconfig.kcfg index c1104db9..f17ff29e 100644 --- a/src/data/kalarmconfig.kcfg +++ b/src/data/kalarmconfig.kcfg @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="UTF-8"?> <!-- -SPDX-FileCopyrightText: 2007-2021 David Jarvie <[email protected]> +SPDX-FileCopyrightText: 2007-2022 David Jarvie <[email protected]> SPDX-License-Identifier: GPL-2.0-or-later --> <kcfg xmlns="http://www.kde.org/standards/kcfg/1.0" @@ -307,7 +307,7 @@ SPDX-License-Identifier: GPL-2.0-or-later <whatsthis context="@info:whatsthis">Default setting in the alarm edit dialog for "show in KOrganizer".</whatsthis> <default>false</default> </entry> - <entry name="DefaultSoundType" key="SoundType" type="Enum"> + <entry name="Base_DefaultSoundType" key="SoundType" type="Enum"> <label context="@label Label for audio options">Sound</label> <whatsthis context="@info:whatsthis">Default sound type in the alarm edit dialog.</whatsthis> <choices name="SoundType" prefix="Sound_"> diff --git a/src/prefdlg.cpp b/src/prefdlg.cpp index 3bb3c6f8..0773b554 100644 --- a/src/prefdlg.cpp +++ b/src/prefdlg.cpp @@ -1359,7 +1359,7 @@ EditPrefTab::EditPrefTab(StackedScrollGroup* scrollGroup) mSound->addItem(SoundPicker::i18n_combo_File()); // index 2 #if KPIMTEXTEDIT_TEXT_TO_SPEECH if (KPIMTextEdit::TextToSpeech::self()->isReady()) - mSound->addItem(SoundPicker::i18n_combo_Speak()); // index 3 + mSound->addItem(SoundPicker::i18n_combo_Speak()); // index 3 #endif mSound->setMinimumSize(mSound->sizeHint()); mSound->setWhatsThis(defsetting.subs(SoundPicker::i18n_label_Sound()).toString()); @@ -1543,7 +1543,9 @@ void EditPrefTab::apply(bool syncToDisc) Preferences::SoundType snd; switch (mSound->currentIndex()) { +#if KPIMTEXTEDIT_TEXT_TO_SPEECH case 3: snd = Preferences::Sound_Speak; break; +#endif case 2: snd = Preferences::Sound_File; break; case 1: snd = Preferences::Sound_Beep; break; case 0: @@ -1628,7 +1630,9 @@ int EditPrefTab::soundIndex(Preferences::SoundType type) { switch (type) { +#if KPIMTEXTEDIT_TEXT_TO_SPEECH case Preferences::Sound_Speak: return 3; +#endif case Preferences::Sound_File: return 2; case Preferences::Sound_Beep: return 1; case Preferences::Sound_None: diff --git a/src/preferences.cpp b/src/preferences.cpp index 546f56ff..b3868802 100644 --- a/src/preferences.cpp +++ b/src/preferences.cpp @@ -18,6 +18,7 @@ #include <KIdentityManagement/Identity> #include <KIdentityManagement/IdentityManager> #include <KHolidays/HolidayRegion> +#include <kpimtextedit/kpimtextedit-texttospeech.h> #include <KSharedConfig> #include <KConfigGroup> @@ -453,6 +454,24 @@ void Preferences::setCmdXTermCommand(const QString& cmd) self()->setBase_CmdXTermCommand(translateXTermPath(cmd, true)); } +Preferences::SoundType Preferences::defaultSoundType() +{ +#if KPIMTEXTEDIT_TEXT_TO_SPEECH + return self()->base_DefaultSoundType(); +#else + SoundType type = self()->base_DefaultSoundType(); + return (type == Sound_Speak) ? Sound_None : type; +#endif +} + +void Preferences::setDefaultSoundType(SoundType type) +{ +#if !KPIMTEXTEDIT_TEXT_TO_SPEECH + if (type == Sound_Speak) + return; +#endif + self()->setBase_DefaultSoundType(type); +} void Preferences::connect(const char* signal, const QObject* receiver, const char* member) { diff --git a/src/preferences.h b/src/preferences.h index 5065e7c1..d1c9a538 100644 --- a/src/preferences.h +++ b/src/preferences.h @@ -79,6 +79,8 @@ public: static bool emailBccUseSystemSettings(); static QString cmdXTermCommand(); static void setCmdXTermCommand(const QString& cmd); + static SoundType defaultSoundType(); + static void setDefaultSoundType(SoundType); static float defaultSoundVolume() { int vol = self()->mBase_DefaultSoundVolume; return (vol < 0) ? -1 : static_cast<float>(vol) / 100; } static void setDefaultSoundVolume(float v) { self()->setBase_DefaultSoundVolume(v < 0 ? -1 : static_cast<int>(v * 100)); } diff --git a/src/soundpicker.cpp b/src/soundpicker.cpp index f85071ea..24f70c5f 100644 --- a/src/soundpicker.cpp +++ b/src/soundpicker.cpp @@ -1,7 +1,7 @@ /* * soundpicker.cpp - widget to select a sound file or a beep * Program: kalarm - * SPDX-FileCopyrightText: 2002-2021 David Jarvie <[email protected]> + * SPDX-FileCopyrightText: 2002-2022 David Jarvie <[email protected]> * * SPDX-License-Identifier: GPL-2.0-or-later */ @@ -159,6 +159,10 @@ Preferences::SoundType SoundPicker::sound() const { if (mTypeCombo->currentIndex() < 0) return Preferences::Sound_None; +#if !KPIMTEXTEDIT_TEXT_TO_SPEECH + if (mTypeCombo->currentData().toInt() == Preferences::Sound_Speak) + return Preferences::Sound_None; +#endif return static_cast<Preferences::SoundType>(mTypeCombo->currentData().toInt()); }
