File indexing completed on 2024-05-12 05:14:58

0001 /*
0002  *  specialactions.cpp  -  widget to specify special alarm actions
0003  *  Program:  kalarm
0004  *  SPDX-FileCopyrightText: 2004-2021 David Jarvie <djarvie@kde.org>
0005  *
0006  *  SPDX-License-Identifier: GPL-2.0-or-later
0007  */
0008 
0009 #include "specialactions.h"
0010 
0011 #include "lib/autoqpointer.h"
0012 #include "lib/checkbox.h"
0013 #include "lib/config.h"
0014 
0015 #include <KLocalizedString>
0016 
0017 #include <QLabel>
0018 #include <QGroupBox>
0019 #include <QHBoxLayout>
0020 #include <QVBoxLayout>
0021 #include <QResizeEvent>
0022 #include <QDialogButtonBox>
0023 #include <QLineEdit>
0024 
0025 
0026 /*=============================================================================
0027 = Class SpecialActionsButton
0028 = Button to display the Special Alarm Actions dialog.
0029 =============================================================================*/
0030 
0031 SpecialActionsButton::SpecialActionsButton(bool enableCheckboxes, QWidget* parent)
0032     : QPushButton(i18nc("@action:button", "Special Actions..."), parent)
0033     , mOptions{}
0034     , mEnableCheckboxes(enableCheckboxes)
0035 {
0036     setCheckable(true);
0037     setChecked(false);
0038     connect(this, &SpecialActionsButton::clicked, this, &SpecialActionsButton::slotButtonPressed);
0039     setToolTip(i18nc("@info:tooltip", "Set commands to execute"));
0040     setWhatsThis(i18nc("@info:whatsthis", "Specify actions to execute before and after the alarm is displayed."));
0041 }
0042 
0043 /******************************************************************************
0044 * Set the pre- and post-alarm actions.
0045 * The button's pressed state is set to reflect whether any actions are set.
0046 */
0047 void SpecialActionsButton::setActions(const QString& pre, const QString& post, KAEvent::ExtraActionOptions options)
0048 {
0049     mPreAction  = pre;
0050     mPostAction = post;
0051     mOptions    = options;
0052     setChecked(!mPreAction.isEmpty() || !mPostAction.isEmpty());
0053 }
0054 
0055 /******************************************************************************
0056 * Called when the OK button is clicked.
0057 * Display a font and colour selection dialog and get the selections.
0058 */
0059 void SpecialActionsButton::slotButtonPressed()
0060 {
0061     // Use AutoQPointer to guard against crash on application exit while
0062     // the dialogue is still open. It prevents double deletion (both on
0063     // deletion of SpecialActionsButton, and on return from this function).
0064     AutoQPointer<SpecialActionsDlg> dlg = new SpecialActionsDlg(mPreAction, mPostAction, mOptions, mEnableCheckboxes, this);
0065     dlg->setReadOnly(mReadOnly);
0066     if (dlg->exec() == QDialog::Accepted)
0067     {
0068         mPreAction  = dlg->preAction();
0069         mPostAction = dlg->postAction();
0070         mOptions    = dlg->options();
0071         Q_EMIT selected();
0072     }
0073         if (dlg)
0074             setChecked(!mPreAction.isEmpty() || !mPostAction.isEmpty());
0075 }
0076 
0077 
0078 /*=============================================================================
0079 = Class SpecialActionsDlg
0080 = Pre- and post-alarm actions dialog.
0081 =============================================================================*/
0082 
0083 static const char SPEC_ACT_DIALOG_NAME[] = "SpecialActionsDialog";
0084 
0085 
0086 SpecialActionsDlg::SpecialActionsDlg(const QString& preAction, const QString& postAction,
0087                                      KAEvent::ExtraActionOptions options, bool enableCheckboxes,
0088                                      QWidget* parent)
0089     : QDialog(parent)
0090 {
0091     setWindowTitle(i18nc("@title:window", "Special Alarm Actions"));
0092     auto mainLayout = new QVBoxLayout;
0093     setLayout(mainLayout);
0094 
0095     QWidget* page = new QWidget(this);
0096     mainLayout->addWidget(page);
0097     auto layout = new QVBoxLayout(page);
0098     layout->setContentsMargins(0, 0, 0, 0);
0099 
0100     mActions = new SpecialActions(enableCheckboxes, page);
0101     layout->addWidget(mActions);
0102     mActions->setActions(preAction, postAction, options);
0103 
0104     QDialogButtonBox* buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Cancel);
0105     mainLayout->addWidget(buttonBox);
0106     QPushButton* okButton = buttonBox->button(QDialogButtonBox::Ok);
0107     okButton->setDefault(true);
0108     okButton->setShortcut(Qt::CTRL | Qt::Key_Return);
0109     connect(buttonBox, &QDialogButtonBox::rejected, this, &SpecialActionsDlg::reject);
0110     connect(okButton, &QPushButton::clicked, this, &SpecialActionsDlg::slotOk);
0111 
0112     QSize s;
0113     if (Config::readWindowSize(SPEC_ACT_DIALOG_NAME, s))
0114         resize(s);
0115 }
0116 
0117 /******************************************************************************
0118 * Called when the OK button is clicked.
0119 */
0120 void SpecialActionsDlg::slotOk()
0121 {
0122     if (mActions->isReadOnly())
0123         reject();
0124     accept();
0125 }
0126 
0127 /******************************************************************************
0128 * Called when the dialog's size has changed.
0129 * Records the new size in the config file.
0130 */
0131 void SpecialActionsDlg::resizeEvent(QResizeEvent* re)
0132 {
0133     if (isVisible())
0134         Config::writeWindowSize(SPEC_ACT_DIALOG_NAME, re->size());
0135     QDialog::resizeEvent(re);
0136 }
0137 
0138 
0139 /*=============================================================================
0140 = Class SpecialActions
0141 = Pre- and post-alarm actions widget.
0142 =============================================================================*/
0143 
0144 SpecialActions::SpecialActions(bool enableCheckboxes, QWidget* parent)
0145     : QWidget(parent)
0146     , mEnableCheckboxes(enableCheckboxes)
0147 {
0148     auto topLayout = new QVBoxLayout(this);
0149     topLayout->setContentsMargins(0, 0, 0, 0);
0150 
0151     // Pre-alarm action
0152     QGroupBox* group = new QGroupBox(i18nc("@title:group", "Pre-Alarm Action"), this);
0153     topLayout->addWidget(group);
0154     auto vlayout = new QVBoxLayout(group);
0155 
0156     QWidget* box = new QWidget(group);   // this is to control the QWhatsThis text display area
0157     vlayout->addWidget(box);
0158     auto boxLayout = new QHBoxLayout(box);
0159     boxLayout->setContentsMargins(0, 0, 0, 0);
0160     QLabel* label = new QLabel(i18nc("@label:textbox", "Command:"), box);
0161     boxLayout->addWidget(label);
0162     mPreAction = new QLineEdit(box);
0163     boxLayout->addWidget(mPreAction);
0164     label->setBuddy(mPreAction);
0165     connect(mPreAction, &QLineEdit::textChanged, this, &SpecialActions::slotPreActionChanged);
0166     box->setWhatsThis(xi18nc("@info:whatsthis",
0167                             "<para>Enter a shell command to execute before the alarm is displayed.</para>"
0168                             "<para>Note that it is executed only when the alarm proper is displayed, not when a reminder or deferred alarm is displayed.</para>"
0169                             "<para><note>KAlarm will wait for the command to complete before displaying the alarm.</note></para>"));
0170     boxLayout->setStretchFactor(mPreAction, 1);
0171 
0172     // Cancel if error in pre-alarm action
0173     mExecOnDeferral = new CheckBox(i18nc("@option:check", "Execute for deferred alarms"), group);
0174     mExecOnDeferral->setWhatsThis(xi18nc("@info:whatsthis", "<para>If unchecked, the command is only executed before the alarm proper is displayed.</para>"
0175                                                            "<para>If checked, the pre-alarm command is also executed before a deferred alarm is displayed.</para>"));
0176     vlayout->addWidget(mExecOnDeferral, 0, Qt::AlignLeft);
0177 
0178     mCancelOnError = new CheckBox(i18nc("@option:check", "Cancel alarm on error"), group);
0179     mCancelOnError->setWhatsThis(i18nc("@info:whatsthis", "Cancel the alarm if the pre-alarm command fails, i.e. do not display the alarm or execute any post-alarm action command."));
0180     vlayout->addWidget(mCancelOnError, 0, Qt::AlignLeft);
0181 
0182     mDontShowError = new CheckBox(i18nc("@option:check", "Do not notify errors"), group);
0183     mDontShowError->setWhatsThis(i18nc("@info:whatsthis", "Do not show error status or error message if the pre-alarm command fails."));
0184     vlayout->addWidget(mDontShowError, 0, Qt::AlignLeft);
0185 
0186     // Post-alarm action
0187     group = new QGroupBox(i18nc("@title:group", "Post-Alarm Action"), this);
0188     topLayout->addWidget(group);
0189     vlayout = new QVBoxLayout(group);
0190 
0191     box = new QWidget(group);   // this is to control the QWhatsThis text display area
0192     vlayout->addWidget(box);
0193     boxLayout = new QHBoxLayout(box);
0194     boxLayout->setContentsMargins(0, 0, 0, 0);
0195     label = new QLabel(i18nc("@label:textbox", "Command:"), box);
0196     boxLayout->addWidget(label);
0197     mPostAction = new QLineEdit(box);
0198     boxLayout->addWidget(mPostAction);
0199     label->setBuddy(mPostAction);
0200     box->setWhatsThis(xi18nc("@info:whatsthis",
0201                             "<para>Enter a shell command to execute after the alarm window is closed.</para>"
0202                             "<para>Note that it is not executed after closing a reminder window. If you defer "
0203                             "the alarm, it is not executed until the alarm is finally acknowledged or closed.</para>"));
0204     boxLayout->setStretchFactor(mPostAction, 1);
0205 
0206     mExecOnDeferral->setEnabled(enableCheckboxes);
0207     mCancelOnError->setEnabled(enableCheckboxes);
0208     mDontShowError->setEnabled(enableCheckboxes);
0209 }
0210 
0211 void SpecialActions::setActions(const QString& pre, const QString& post, KAEvent::ExtraActionOptions options)
0212 {
0213     mPreAction->setText(pre);
0214     mPostAction->setText(post);
0215     mExecOnDeferral->setChecked(options & KAEvent::ExecPreActOnDeferral);
0216     mCancelOnError->setChecked(options & KAEvent::CancelOnPreActError);
0217     mDontShowError->setChecked(options & KAEvent::DontShowPreActError);
0218 }
0219 
0220 QString SpecialActions::preAction() const
0221 {
0222     return mPreAction->text();
0223 }
0224 
0225 QString SpecialActions::postAction() const
0226 {
0227     return mPostAction->text();
0228 }
0229 
0230 KAEvent::ExtraActionOptions SpecialActions::options() const
0231 {
0232     KAEvent::ExtraActionOptions opts = {};
0233     if (mExecOnDeferral->isChecked())  opts |= KAEvent::ExecPreActOnDeferral;
0234     if (mCancelOnError->isChecked())   opts |= KAEvent::CancelOnPreActError;
0235     if (mDontShowError->isChecked())   opts |= KAEvent::DontShowPreActError;
0236     return opts;
0237 }
0238 
0239 void SpecialActions::setReadOnly(bool ro)
0240 {
0241     mReadOnly = ro;
0242     mPreAction->setReadOnly(mReadOnly);
0243     mPostAction->setReadOnly(mReadOnly);
0244     mExecOnDeferral->setReadOnly(mReadOnly);
0245     mCancelOnError->setReadOnly(mReadOnly);
0246     mDontShowError->setReadOnly(mReadOnly);
0247 }
0248 
0249 void SpecialActions::slotPreActionChanged(const QString& text)
0250 {
0251     if (!mEnableCheckboxes)
0252     {
0253         bool textValid = !text.isEmpty();
0254         mExecOnDeferral->setEnabled(textValid);
0255         mCancelOnError->setEnabled(textValid);
0256         mDontShowError->setEnabled(textValid);
0257     }
0258 }
0259 
0260 #include "moc_specialactions.cpp"
0261 
0262 // vim: et sw=4: