[pim/kalarm] /: Remove all 'speak' functions if text-to-speech not available at build time

David Jarvie null at kde.org
Thu Mar 10 16:22:54 GMT 2022


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 <djarvie at kde.org>
+SPDX-FileCopyrightText: 2007-2022 David Jarvie <djarvie at kde.org>
 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 <djarvie at kde.org>
+ *  SPDX-FileCopyrightText: 2002-2022 David Jarvie <djarvie at kde.org>
  *
  *  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());
 }
 


More information about the kde-doc-english mailing list