editdlgtypes.cpp 73.3 KB
Newer Older
1
/*
2 3
 *  editdlgtypes.cpp  -  dialogs to create or edit alarm or alarm template types
 *  Program:  kalarm
4
 *  Copyright © 2001-2017 by David Jarvie <djarvie@kde.org>
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License along
 *  with this program; if not, write to the Free Software Foundation, Inc.,
 *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */
20 21

#include "kalarm.h"
Laurent Montel's avatar
Laurent Montel committed
22
#include "editdlgtypes.h"
David Jarvie's avatar
David Jarvie committed
23
#include "editdlg_p.h"
24

25
#include "autoqpointer.h"
26 27
#include "buttongroup.h"
#include "checkbox.h"
28
#include "colourbutton.h"
29 30 31 32 33 34 35 36
#include "emailidcombo.h"
#include "fontcolourbutton.h"
#include "functions.h"
#include "kalarmapp.h"
#include "kamail.h"
#include "latecancel.h"
#include "lineedit.h"
#include "mainwindow.h"
37
#include "messagebox.h"
38
#include "messagewin.h"
39 40 41 42 43 44
#include "pickfileradio.h"
#include "preferences.h"
#include "radiobutton.h"
#include "reminder.h"
#include "shellprocess.h"
#include "soundpicker.h"
45
#include "sounddlg.h"
46 47 48 49
#include "specialactions.h"
#include "templatepickdlg.h"
#include "timespinbox.h"

Laurent Montel's avatar
Laurent Montel committed
50
#include <kalarmcal/identities.h>
51

52
#include <Akonadi/Contact/EmailAddressSelectionDialog>
Christophe Giboudeaux's avatar
Christophe Giboudeaux committed
53
#include <KCalCore/Person>
Laurent Montel's avatar
Laurent Montel committed
54
#include <KCalUtils/kcalutils/icaldrag.h>
David Jarvie's avatar
David Jarvie committed
55 56
using namespace KCalCore;

57
#include <KLocalizedString>
58 59
#include <kfileitem.h>

60
#include <QComboBox>
David Jarvie's avatar
David Jarvie committed
61 62 63 64 65 66 67 68
#include <QLabel>
#include <QDir>
#include <QStyle>
#include <QGroupBox>
#include <QGridLayout>
#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QDragEnterEvent>
69
#include <QStandardItemModel>
Laurent Montel's avatar
Laurent Montel committed
70
#include "kalarm_debug.h"
71

72 73
using namespace KAlarmCal;

David Jarvie's avatar
David Jarvie committed
74 75 76
enum { tTEXT, tFILE, tCOMMAND };  // order of mTypeCombo items


77 78 79 80 81 82
/*=============================================================================
= Class PickLogFileRadio
=============================================================================*/
class PickLogFileRadio : public PickFileRadio
{
    public:
83 84
    PickLogFileRadio(QPushButton* b, LineEdit* e, const QString& text, ButtonGroup* group, QWidget* parent)
        : PickFileRadio(b, e, text, group, parent) { }
85
    QString pickFile() override    // called when browse button is pressed to select a log file
86 87 88 89
    {
        return KAlarm::browseFile(i18nc("@title:window", "Choose Log File"), mDefaultDir, fileEdit()->text(), QString(),
                                  KFile::LocalOnly, parentWidget());
    }
90
    private:
91
    QString mDefaultDir;   // default directory for log file browse button
92 93 94
};


95 96 97 98 99 100
/*=============================================================================
= Class EditDisplayAlarmDlg
= Dialog to edit display alarms.
=============================================================================*/

QString EditDisplayAlarmDlg::i18n_chk_ConfirmAck()    { return i18nc("@option:check", "Confirm acknowledgment"); }
101 102 103 104 105 106 107


/******************************************************************************
* Constructor.
* Parameters:
*   Template = true to edit/create an alarm template
*            = false to edit/create an alarm.
108
*   event   != to initialise the dialog to show the specified event's data.
109
*/
110
EditDisplayAlarmDlg::EditDisplayAlarmDlg(bool Template, QWidget* parent, GetResourceType getResource)
111
    : EditAlarmDlg(Template, KAEvent::MESSAGE, parent, getResource),
Laurent Montel's avatar
Laurent Montel committed
112
      mSpecialActionsButton(nullptr),
113 114
      mReminderDeferral(false),
      mReminderArchived(false)
115
{
Laurent Montel's avatar
Laurent Montel committed
116
    qCDebug(KALARM_LOG) << "New";
Laurent Montel's avatar
Laurent Montel committed
117
    init(nullptr);
118 119
}

120
EditDisplayAlarmDlg::EditDisplayAlarmDlg(bool Template, const KAEvent* event, bool newAlarm, QWidget* parent,
121
                                         GetResourceType getResource, bool readOnly)
122
    : EditAlarmDlg(Template, event, newAlarm, parent, getResource, readOnly),
Laurent Montel's avatar
Laurent Montel committed
123
      mSpecialActionsButton(nullptr),
124 125
      mReminderDeferral(false),
      mReminderArchived(false)
126
{
Laurent Montel's avatar
Laurent Montel committed
127
    qCDebug(KALARM_LOG) << "Event.id()";
128
    init(event);
129 130 131 132 133
}

/******************************************************************************
* Return the window caption.
*/
134
QString EditDisplayAlarmDlg::type_caption() const
135
{
136 137
    return isTemplate() ? (isNewAlarm() ? i18nc("@title:window", "New Display Alarm Template") : i18nc("@title:window", "Edit Display Alarm Template"))
                        : (isNewAlarm() ? i18nc("@title:window", "New Display Alarm") : i18nc("@title:window", "Edit Display Alarm"));
138 139 140 141 142 143 144
}

/******************************************************************************
* Set up the dialog controls common to display alarms.
*/
void EditDisplayAlarmDlg::type_init(QWidget* parent, QVBoxLayout* frameLayout)
{
145
    // Display type combo box
Laurent Montel's avatar
Laurent Montel committed
146
    QWidget* box = new QWidget(parent);    // to group widgets for QWhatsThis text
147

David Jarvie's avatar
Tidy up  
David Jarvie committed
148 149
    QHBoxLayout* boxHLayout = new QHBoxLayout(box);
    boxHLayout->setMargin(0);
150
    QLabel* label = new QLabel(i18nc("@label:listbox", "Display type:"), box);
David Jarvie's avatar
Tidy up  
David Jarvie committed
151
    boxHLayout->addWidget(label);
152 153
    label->setFixedSize(label->sizeHint());
    mTypeCombo = new ComboBox(box);
David Jarvie's avatar
Tidy up  
David Jarvie committed
154
    boxHLayout->addWidget(mTypeCombo);
155 156 157 158 159 160 161 162
    QString textItem    = i18nc("@item:inlistbox", "Text message");
    QString fileItem    = i18nc("@item:inlistbox", "File contents");
    QString commandItem = i18nc("@item:inlistbox", "Command output");
    mTypeCombo->addItem(textItem);     // index = tTEXT
    mTypeCombo->addItem(fileItem);     // index = tFILE
    mTypeCombo->addItem(commandItem);  // index = tCOMMAND
    mTypeCombo->setFixedSize(mTypeCombo->sizeHint());
    mTypeCombo->setCurrentIndex(-1);    // ensure slotAlarmTypeChanged() is called when index is set
163 164 165 166 167 168 169 170 171 172 173 174
    if (!ShellProcess::authorised())
    {
        // User not authorised to issue shell commands - disable Command Output option
        QStandardItemModel* model = qobject_cast<QStandardItemModel*>(mTypeCombo->model());
        if (model)
        {
            QModelIndex index = model->index(2, mTypeCombo->modelColumn(), mTypeCombo->rootModelIndex());
            QStandardItem* item = model->itemFromIndex(index);
            if (item)
                item->setEnabled(false);
        }
    }
Laurent Montel's avatar
Laurent Montel committed
175 176
    connect(mTypeCombo, static_cast<void (ComboBox::*)(int)>(&ComboBox::currentIndexChanged), this, &EditDisplayAlarmDlg::slotAlarmTypeChanged);
    connect(mTypeCombo, static_cast<void (ComboBox::*)(int)>(&ComboBox::currentIndexChanged), this, &EditDisplayAlarmDlg::contentsChanged);
177
    label->setBuddy(mTypeCombo);
Laurent Montel's avatar
Laurent Montel committed
178
    box->setWhatsThis(xi18nc("@info:whatsthis", "<para>Select what the alarm should display:"
179 180 181 182
          "<list><item><interface>%1</interface>: the alarm will display the text message you type in.</item>"
          "<item><interface>%2</interface>: the alarm will display the contents of a text or image file.</item>"
          "<item><interface>%3</interface>: the alarm will display the output from a command.</item></list></para>",
          textItem, fileItem, commandItem));
David Jarvie's avatar
Tidy up  
David Jarvie committed
183
    boxHLayout->setStretchFactor(new QWidget(box), 1);    // left adjust the control
184 185 186 187 188 189
    frameLayout->addWidget(box);

    // Text message edit box
    mTextMessageEdit = new TextEdit(parent);
    mTextMessageEdit->setLineWrapMode(KTextEdit::NoWrap);
    mTextMessageEdit->setWhatsThis(i18nc("@info:whatsthis", "Enter the text of the alarm message. It may be multi-line."));
Laurent Montel's avatar
Laurent Montel committed
190
    connect(mTextMessageEdit, &TextEdit::textChanged, this, &EditDisplayAlarmDlg::contentsChanged);
191 192 193
    frameLayout->addWidget(mTextMessageEdit);

    // File name edit box
Laurent Montel's avatar
Laurent Montel committed
194
    mFileBox = new QWidget(parent);
195
    frameLayout->addWidget(mFileBox);
David Jarvie's avatar
Tidy up  
David Jarvie committed
196 197 198
    QHBoxLayout* fileBoxHLayout = new QHBoxLayout(mFileBox);
    fileBoxHLayout->setMargin(0);
    fileBoxHLayout->setSpacing(0);
199
    mFileMessageEdit = new LineEdit(LineEdit::Url, mFileBox);
David Jarvie's avatar
Tidy up  
David Jarvie committed
200
    fileBoxHLayout->addWidget(mFileMessageEdit);
201 202
    mFileMessageEdit->setAcceptDrops(true);
    mFileMessageEdit->setWhatsThis(i18nc("@info:whatsthis", "Enter the name or URL of a text or image file to display."));
Laurent Montel's avatar
Laurent Montel committed
203
    connect(mFileMessageEdit, &LineEdit::textChanged, this, &EditDisplayAlarmDlg::contentsChanged);
204 205 206

    // File browse button
    mFileBrowseButton = new QPushButton(mFileBox);
David Jarvie's avatar
Tidy up  
David Jarvie committed
207
    fileBoxHLayout->addWidget(mFileBrowseButton);
208
    mFileBrowseButton->setIcon(QIcon::fromTheme(QStringLiteral("document-open")));
209 210 211 212
    int size = mFileBrowseButton->sizeHint().height();
    mFileBrowseButton->setFixedSize(size, size);
    mFileBrowseButton->setToolTip(i18nc("@info:tooltip", "Choose a file"));
    mFileBrowseButton->setWhatsThis(i18nc("@info:whatsthis", "Select a text or image file to display."));
Laurent Montel's avatar
Laurent Montel committed
213
    connect(mFileBrowseButton, &QPushButton::clicked, this, &EditDisplayAlarmDlg::slotPickFile);
214 215 216

    // Command type checkbox and edit box
    mCmdEdit = new CommandEdit(parent);
Laurent Montel's avatar
Laurent Montel committed
217 218
    connect(mCmdEdit, &CommandEdit::scriptToggled, this, &EditDisplayAlarmDlg::slotCmdScriptToggled);
    connect(mCmdEdit, &CommandEdit::changed, this, &EditDisplayAlarmDlg::contentsChanged);
219 220 221 222 223 224 225 226
    frameLayout->addWidget(mCmdEdit);

    // Sound checkbox and file selector
    QHBoxLayout* hlayout = new QHBoxLayout();
    hlayout->setMargin(0);
    frameLayout->addLayout(hlayout);
    mSoundPicker = new SoundPicker(parent);
    mSoundPicker->setFixedSize(mSoundPicker->sizeHint());
Laurent Montel's avatar
Laurent Montel committed
227
    connect(mSoundPicker, &SoundPicker::changed, this, &EditDisplayAlarmDlg::contentsChanged);
228
    hlayout->addWidget(mSoundPicker);
229
    hlayout->addSpacing(2 * style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing));
230 231 232 233 234 235
    hlayout->addStretch();

    // Font and colour choice button and sample text
    mFontColourButton = new FontColourButton(parent);
    mFontColourButton->setMaximumHeight(mFontColourButton->sizeHint().height() * 3/2);
    hlayout->addWidget(mFontColourButton);
Laurent Montel's avatar
Laurent Montel committed
236 237
    connect(mFontColourButton, &FontColourButton::selected, this, &EditDisplayAlarmDlg::setColours);
    connect(mFontColourButton, &FontColourButton::selected, this, &EditDisplayAlarmDlg::contentsChanged);
238 239 240 241 242 243

    if (ShellProcess::authorised())    // don't display if shell commands not allowed (e.g. kiosk mode)
    {
        // Special actions button
        mSpecialActionsButton = new SpecialActionsButton(false, parent);
        mSpecialActionsButton->setFixedSize(mSpecialActionsButton->sizeHint());
Laurent Montel's avatar
Laurent Montel committed
244
        connect(mSpecialActionsButton, &SpecialActionsButton::selected, this, &EditDisplayAlarmDlg::contentsChanged);
245 246 247 248
        frameLayout->addWidget(mSpecialActionsButton, 0, Qt::AlignRight);
    }

    // Top-adjust the controls
Laurent Montel's avatar
Laurent Montel committed
249
    mFilePadding = new QWidget(parent);
David Jarvie's avatar
Tidy up  
David Jarvie committed
250 251 252
    hlayout = new QHBoxLayout(mFilePadding);
    hlayout->setMargin(0);
    hlayout->setSpacing(0);
253 254
    frameLayout->addWidget(mFilePadding);
    frameLayout->setStretchFactor(mFilePadding, 1);
255 256 257 258 259 260 261
}

/******************************************************************************
* Create a reminder control.
*/
Reminder* EditDisplayAlarmDlg::createReminder(QWidget* parent)
{
262
    return new Reminder(i18nc("@info:whatsthis", "Check to additionally display a reminder in advance of or after the main alarm time(s)."),
Laurent Montel's avatar
Laurent Montel committed
263
                        xi18nc("@info:whatsthis", "<para>Enter how long in advance of or after the main alarm to display a reminder alarm.</para><para>%1</para>", TimeSpinBox::shiftWhatsThis()),
264
                        i18nc("@info:whatsthis", "Select whether the reminder should be triggered before or after the main alarm"),
265
                        true, true, parent);
266 267 268 269 270 271 272
}

/******************************************************************************
* Create an "acknowledgement confirmation required" checkbox.
*/
CheckBox* EditDisplayAlarmDlg::createConfirmAckCheckbox(QWidget* parent)
{
273 274 275
    CheckBox* confirmAck = new CheckBox(i18n_chk_ConfirmAck(), parent);
    confirmAck->setWhatsThis(i18nc("@info:whatsthis", "Check to be prompted for confirmation when you acknowledge the alarm."));
    return confirmAck;
276 277 278
}

/******************************************************************************
279
* Initialise the dialog controls from the specified event.
280 281 282
*/
void EditDisplayAlarmDlg::type_initValues(const KAEvent* event)
{
283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299
    mKMailSerialNumber = 0;
    lateCancel()->showAutoClose(true);
    if (event)
    {
        if (mAlarmType == KAEvent::MESSAGE  &&  event->kmailSerialNumber()
        &&  AlarmText::checkIfEmail(event->cleanText()))
            mKMailSerialNumber = event->kmailSerialNumber();
        lateCancel()->setAutoClose(event->autoClose());
        if (event->useDefaultFont())
            mFontColourButton->setDefaultFont();
        else
            mFontColourButton->setFont(event->font());
        mFontColourButton->setBgColour(event->bgColour());
        mFontColourButton->setFgColour(event->fgColour());
        setColours(event->fgColour(), event->bgColour());
        mConfirmAck->setChecked(event->confirmAck());
        bool recurs = event->recurs();
300 301 302
        int reminderMins = event->reminderMinutes();
        if (reminderMins > 0  &&  !event->reminderActive())
            reminderMins = 0;   // don't show advance reminder which has already passed
303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319
        if (!reminderMins)
        {
            if (event->reminderDeferral()  &&  !recurs)
            {
                reminderMins = event->deferDateTime().minsTo(event->mainDateTime());
                mReminderDeferral = true;
            }
            else if (event->reminderMinutes()  &&  recurs)
            {
                reminderMins = event->reminderMinutes();
                mReminderArchived = true;
            }
        }
        reminder()->setMinutes(reminderMins, dateOnly());
        reminder()->setOnceOnly(event->reminderOnceOnly());
        reminder()->enableOnceOnly(recurs);
        if (mSpecialActionsButton)
320
            mSpecialActionsButton->setActions(event->preAction(), event->postAction(), event->extraActionOptions());
321 322 323 324 325
        Preferences::SoundType soundType = event->speak()                ? Preferences::Sound_Speak
                                         : event->beep()                 ? Preferences::Sound_Beep
                                         : !event->audioFile().isEmpty() ? Preferences::Sound_File
                                         :                                 Preferences::Sound_None;
        mSoundPicker->set(soundType, event->audioFile(), event->soundVolume(),
326
                          event->fadeVolume(), event->fadeSeconds(), event->repeatSoundPause());
327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346
    }
    else
    {
        // Set the values to their defaults
        if (!ShellProcess::authorised())
        {
            // Don't allow shell commands in kiosk mode
            if (mSpecialActionsButton)
                mSpecialActionsButton->setEnabled(false);
        }
        lateCancel()->setAutoClose(Preferences::defaultAutoClose());
        mTypeCombo->setCurrentIndex(0);
        mFontColourButton->setDefaultFont();
        mFontColourButton->setBgColour(Preferences::defaultBgColour());
        mFontColourButton->setFgColour(Preferences::defaultFgColour());
        setColours(Preferences::defaultFgColour(), Preferences::defaultBgColour());
        mConfirmAck->setChecked(Preferences::defaultConfirmAck());
        reminder()->setMinutes(0, false);
        reminder()->enableOnceOnly(isTimedRecurrence());   // must be called after mRecurrenceEdit is set up
        if (mSpecialActionsButton)
347
        {
Laurent Montel's avatar
Laurent Montel committed
348
            KAEvent::ExtraActionOptions opts(nullptr);
349 350 351 352 353 354 355 356
            if (Preferences::defaultExecPreActionOnDeferral())
                opts |= KAEvent::ExecPreActOnDeferral;
            if (Preferences::defaultCancelOnPreActionError())
                opts |= KAEvent::CancelOnPreActError;
            if (Preferences::defaultDontShowPreActionError())
                opts |= KAEvent::DontShowPreActError;
            mSpecialActionsButton->setActions(Preferences::defaultPreAction(), Preferences::defaultPostAction(), opts);
        }
357
        mSoundPicker->set(Preferences::defaultSoundType(), Preferences::defaultSoundFile(),
358
                          Preferences::defaultSoundVolume(), -1, 0, (Preferences::defaultSoundRepeat() ? 0 : -1));
359
    }
360
}
361

362 363 364 365 366 367
/******************************************************************************
* Called when the More/Less Options button is clicked.
* Show/hide the optional options.
*/
void EditDisplayAlarmDlg::type_showOptions(bool more)
{
368 369 370 371 372 373 374
    if (mSpecialActionsButton)
    {
        if (more)
            mSpecialActionsButton->show();
        else
            mSpecialActionsButton->hide();
    }
375 376
}

377 378 379 380
/******************************************************************************
* Called when the font/color button has been clicked.
* Set the colors in the message text entry control.
*/
381
void EditDisplayAlarmDlg::setColours(const QColor& fgColour, const QColor& bgColour)
382
{
383 384 385 386 387 388 389 390 391 392 393 394 395
    QPalette pal = mTextMessageEdit->palette();
    pal.setColor(mTextMessageEdit->backgroundRole(), bgColour);
    pal.setColor(QPalette::Text, fgColour);
    mTextMessageEdit->setPalette(pal);
    pal = mTextMessageEdit->viewport()->palette();
    pal.setColor(mTextMessageEdit->viewport()->backgroundRole(), bgColour);
    pal.setColor(QPalette::Text, fgColour);
    mTextMessageEdit->viewport()->setPalette(pal);
    // Change the color of existing text
    QTextCursor cursor = mTextMessageEdit->textCursor();
    mTextMessageEdit->selectAll();
    mTextMessageEdit->setTextColor(fgColour);
    mTextMessageEdit->setTextCursor(cursor);
396 397 398 399 400
}

/******************************************************************************
* Set the dialog's action and the action's text.
*/
401
void EditDisplayAlarmDlg::setAction(KAEvent::SubAction action, const AlarmText& alarmText)
402
{
403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422
    QString text = alarmText.displayText();
    switch (action)
    {
        case KAEvent::MESSAGE:
            mTypeCombo->setCurrentIndex(tTEXT);
            mTextMessageEdit->setPlainText(text);
            mKMailSerialNumber = alarmText.isEmail() ? alarmText.kmailSerialNumber() : 0;
            break;
        case KAEvent::FILE:
            mTypeCombo->setCurrentIndex(tFILE);
            mFileMessageEdit->setText(text);
            break;
        case KAEvent::COMMAND:
            mTypeCombo->setCurrentIndex(tCOMMAND);
            mCmdEdit->setText(alarmText);
            break;
        default:
            Q_ASSERT(0);
            break;
    }
423 424
}

425 426 427 428 429
/******************************************************************************
* Initialise various values in the New Alarm dialogue.
*/
void EditDisplayAlarmDlg::setBgColour(const QColor& colour)
{
430 431
    mFontColourButton->setBgColour(colour);
    setColours(mFontColourButton->fgColour(), colour);
432 433 434
}
void EditDisplayAlarmDlg::setFgColour(const QColor& colour)
{
435 436
    mFontColourButton->setFgColour(colour);
    setColours(colour, mFontColourButton->bgColour());
437 438 439
}
void EditDisplayAlarmDlg::setConfirmAck(bool confirm)
{
440
    mConfirmAck->setChecked(confirm);
441 442 443
}
void EditDisplayAlarmDlg::setAutoClose(bool close)
{
444
    lateCancel()->setAutoClose(close);
445
}
446
void EditDisplayAlarmDlg::setAudio(Preferences::SoundType type, const QString& file, float volume, int repeatPause)
447
{
448
    mSoundPicker->set(type, file, volume, -1, 0, repeatPause);
449
}
450
void EditDisplayAlarmDlg::setReminder(int minutes, bool onceOnly)
451
{
452 453 454
    reminder()->setMinutes(minutes, dateOnly());
    reminder()->setOnceOnly(onceOnly);
    reminder()->enableOnceOnly(isTimedRecurrence());
455 456
}

457
/******************************************************************************
458
* Set the read-only status of all non-template controls.
459
*/
460
void EditDisplayAlarmDlg::setReadOnly(bool readOnly)
461
{
462 463 464 465 466 467 468 469 470 471 472 473 474 475 476
    mTypeCombo->setReadOnly(readOnly);
    mTextMessageEdit->setReadOnly(readOnly);
    mFileMessageEdit->setReadOnly(readOnly);
    mCmdEdit->setReadOnly(readOnly);
    mFontColourButton->setReadOnly(readOnly);
    mSoundPicker->setReadOnly(readOnly);
    mConfirmAck->setReadOnly(readOnly);
    reminder()->setReadOnly(readOnly);
    if (mSpecialActionsButton)
        mSpecialActionsButton->setReadOnly(readOnly);
    if (readOnly)
        mFileBrowseButton->hide();
    else
        mFileBrowseButton->show();
    EditAlarmDlg::setReadOnly(readOnly);
477 478 479 480 481 482 483
}

/******************************************************************************
* Save the state of all controls.
*/
void EditDisplayAlarmDlg::saveState(const KAEvent* event)
{
484 485 486 487 488 489
    EditAlarmDlg::saveState(event);
    mSavedType        = mTypeCombo->currentIndex();
    mSavedCmdScript   = mCmdEdit->isScript();
    mSavedSoundType   = mSoundPicker->sound();
    mSavedSoundFile   = mSoundPicker->file();
    mSavedSoundVolume = mSoundPicker->volume(mSavedSoundFadeVolume, mSavedSoundFadeSeconds);
490
    mSavedRepeatPause = mSoundPicker->repeatPause();
491 492 493 494 495 496 497 498 499
    mSavedConfirmAck  = mConfirmAck->isChecked();
    mSavedFont        = mFontColourButton->font();
    mSavedFgColour    = mFontColourButton->fgColour();
    mSavedBgColour    = mFontColourButton->bgColour();
    mSavedReminder    = reminder()->minutes();
    mSavedOnceOnly    = reminder()->isOnceOnly();
    mSavedAutoClose   = lateCancel()->isAutoClose();
    if (mSpecialActionsButton)
    {
500 501 502
        mSavedPreAction        = mSpecialActionsButton->preAction();
        mSavedPostAction       = mSpecialActionsButton->postAction();
        mSavedPreActionOptions = mSpecialActionsButton->options();
503
    }
504 505 506 507 508 509 510 511 512 513
}

/******************************************************************************
* Check whether any of the controls has changed state since the dialog was
* first displayed.
* Reply = true if any controls have changed, or if it's a new event.
*       = false if no controls have changed.
*/
bool EditDisplayAlarmDlg::type_stateChanged() const
{
514 515 516 517 518 519 520 521 522 523 524 525 526
    if (mSavedType       != mTypeCombo->currentIndex()
    ||  mSavedCmdScript  != mCmdEdit->isScript()
    ||  mSavedSoundType  != mSoundPicker->sound()
    ||  mSavedConfirmAck != mConfirmAck->isChecked()
    ||  mSavedFont       != mFontColourButton->font()
    ||  mSavedFgColour   != mFontColourButton->fgColour()
    ||  mSavedBgColour   != mFontColourButton->bgColour()
    ||  mSavedReminder   != reminder()->minutes()
    ||  mSavedOnceOnly   != reminder()->isOnceOnly()
    ||  mSavedAutoClose  != lateCancel()->isAutoClose())
        return true;
    if (mSpecialActionsButton)
    {
527 528 529
        if (mSavedPreAction        != mSpecialActionsButton->preAction()
        ||  mSavedPostAction       != mSpecialActionsButton->postAction()
        ||  mSavedPreActionOptions != mSpecialActionsButton->options())
530 531 532 533 534 535 536 537 538 539
            return true;
    }
    if (mSavedSoundType == Preferences::Sound_File)
    {
        if (mSavedSoundFile != mSoundPicker->file())
            return true;
        if (!mSavedSoundFile.isEmpty())
        {
            float fadeVolume;
            int   fadeSecs;
540
            if (mSavedRepeatPause != mSoundPicker->repeatPause()
541 542 543 544 545 546 547
            ||  mSavedSoundVolume != mSoundPicker->volume(fadeVolume, fadeSecs)
            ||  mSavedSoundFadeVolume != fadeVolume
            ||  mSavedSoundFadeSeconds != fadeSecs)
                return true;
        }
    }
    return false;
548 549 550
}

/******************************************************************************
551
* Extract the data in the dialog specific to the alarm type and set up a
552 553 554 555
* KAEvent from it.
*/
void EditDisplayAlarmDlg::type_setEvent(KAEvent& event, const KDateTime& dt, const QString& text, int lateCancel, bool trial)
{
556
    KAEvent::SubAction type;
557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573
    switch (mTypeCombo->currentIndex())
    {
        case tFILE:     type = KAEvent::FILE; break;
        case tCOMMAND:  type = KAEvent::COMMAND; break;
        default:
        case tTEXT:     type = KAEvent::MESSAGE; break;
    }
    event.set(dt, text, mFontColourButton->bgColour(), mFontColourButton->fgColour(), mFontColourButton->font(),
              type, lateCancel, getAlarmFlags());
    if (type == KAEvent::MESSAGE)
    {
        if (AlarmText::checkIfEmail(text))
            event.setKMailSerialNumber(mKMailSerialNumber);
    }
    float fadeVolume;
    int   fadeSecs;
    float volume = mSoundPicker->volume(fadeVolume, fadeSecs);
574
    int   repeatPause = mSoundPicker->repeatPause();
Daniel Vrátil's avatar
Daniel Vrátil committed
575
    event.setAudioFile(mSoundPicker->file().toDisplayString(), volume, fadeVolume, fadeSecs, repeatPause);
576 577 578 579
    if (!trial  &&  reminder()->isEnabled())
        event.setReminder(reminder()->minutes(), reminder()->isOnceOnly());
    if (mSpecialActionsButton  &&  mSpecialActionsButton->isEnabled())
        event.setActions(mSpecialActionsButton->preAction(), mSpecialActionsButton->postAction(),
580
                         mSpecialActionsButton->options());
581 582 583 584 585
}

/******************************************************************************
* Get the currently specified alarm flag bits.
*/
586
KAEvent::Flags EditDisplayAlarmDlg::getAlarmFlags() const
587
{
588
    bool cmd = (mTypeCombo->currentIndex() == tCOMMAND);
589 590 591
    KAEvent::Flags flags = EditAlarmDlg::getAlarmFlags();
    if (mSoundPicker->sound() == Preferences::Sound_Beep)  flags |= KAEvent::BEEP;
    if (mSoundPicker->sound() == Preferences::Sound_Speak) flags |= KAEvent::SPEAK;
592
    if (mSoundPicker->repeatPause() >= 0)                  flags |= KAEvent::REPEAT_SOUND;
593 594 595 596 597 598
    if (mConfirmAck->isChecked())                          flags |= KAEvent::CONFIRM_ACK;
    if (lateCancel()->isAutoClose())                       flags |= KAEvent::AUTO_CLOSE;
    if (mFontColourButton->defaultFont())                  flags |= KAEvent::DEFAULT_FONT;
    if (cmd)                                               flags |= KAEvent::DISPLAY_COMMAND;
    if (cmd && mCmdEdit->isScript())                       flags |= KAEvent::SCRIPT;
    return flags;
599 600 601
}

/******************************************************************************
David Jarvie's avatar
David Jarvie committed
602 603
* Called when one of the alarm display type combo box is changed, to display
* the appropriate set of controls for that action type.
604
*/
David Jarvie's avatar
David Jarvie committed
605
void EditDisplayAlarmDlg::slotAlarmTypeChanged(int index)
606
{
Laurent Montel's avatar
Laurent Montel committed
607
    QWidget* focus = nullptr;
608 609 610 611 612 613 614 615
    switch (index)
    {
        case tTEXT:    // text message
            mFileBox->hide();
            mFilePadding->hide();
            mCmdEdit->hide();
            mTextMessageEdit->show();
            mSoundPicker->showSpeak(true);
616
            mTryButton->setWhatsThis(i18nc("@info:whatsthis", "Display the alarm message now"));
617 618 619 620 621 622 623 624
            focus = mTextMessageEdit;
            break;
        case tFILE:    // file contents
            mTextMessageEdit->hide();
            mFileBox->show();
            mFilePadding->show();
            mCmdEdit->hide();
            mSoundPicker->showSpeak(false);
625
            mTryButton->setWhatsThis(i18nc("@info:whatsthis", "Display the file now"));
626 627 628 629 630 631 632 633 634
            mFileMessageEdit->setNoSelect();
            focus = mFileMessageEdit;
            break;
        case tCOMMAND:    // command output
            mTextMessageEdit->hide();
            mFileBox->hide();
            slotCmdScriptToggled(mCmdEdit->isScript());  // show/hide mFilePadding
            mCmdEdit->show();
            mSoundPicker->showSpeak(true);
635
            mTryButton->setWhatsThis(i18nc("@info:whatsthis", "Display the command output now"));
636 637 638 639 640
            focus = mCmdEdit;
            break;
    }
    if (focus)
        focus->setFocus();
641 642
}

David Jarvie's avatar
David Jarvie committed
643 644 645 646 647
/******************************************************************************
* Called when the file browse button is pressed to select a file to display.
*/
void EditDisplayAlarmDlg::slotPickFile()
{
648 649 650 651 652 653 654 655
    static QString defaultDir;   // default directory for file browse button
    QString file = KAlarm::browseFile(i18nc("@title:window", "Choose Text or Image File to Display"),
                                      defaultDir, mFileMessageEdit->text(), QString(), KFile::ExistingOnly, this);
    if (!file.isEmpty())
    {
        mFileMessageEdit->setText(KAlarm::pathOrUrl(file));
        contentsChanged();
    }
David Jarvie's avatar
David Jarvie committed
656 657
}

658 659 660 661 662 663
/******************************************************************************
* Called when one of the command type radio buttons is clicked,
* to display the appropriate edit field.
*/
void EditDisplayAlarmDlg::slotCmdScriptToggled(bool on)
{
664 665 666 667
    if (on)
        mFilePadding->hide();
    else
        mFilePadding->show();
668 669
}

670
/******************************************************************************
671
* Clean up the alarm text, and if it's a file, check whether it's valid.
672
*/
673
bool EditDisplayAlarmDlg::checkText(QString& result, bool showErrorMessage) const
674
{
675 676 677 678 679 680 681 682 683
    switch (mTypeCombo->currentIndex())
    {
        case tTEXT:
            result = mTextMessageEdit->toPlainText();
            break;

        case tFILE:
        {
            QString alarmtext = mFileMessageEdit->text().trimmed();
Daniel Vrátil's avatar
Daniel Vrátil committed
684
            QUrl url;
685 686 687
            KAlarm::FileErr err = KAlarm::checkFileExists(alarmtext, url);
            if (err == KAlarm::FileErr_None)
            {
Daniel Vrátil's avatar
Daniel Vrátil committed
688 689
                KFileItem fi(url);
                switch (KAlarm::fileType(fi.currentMimeType()))
690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716
                {
                    case KAlarm::TextFormatted:
                    case KAlarm::TextPlain:
                    case KAlarm::TextApplication:
                    case KAlarm::Image:
                        break;
                    default:
                        err = KAlarm::FileErr_NotTextImage;
                        break;
                }
            }
            if (err != KAlarm::FileErr_None  &&  showErrorMessage)
            {
                mFileMessageEdit->setFocus();
                if (!KAlarm::showFileErrMessage(alarmtext, err, KAlarm::FileErr_BlankDisplay, const_cast<EditDisplayAlarmDlg*>(this)))
                    return false;
            }
            result = alarmtext;
            break;
        }
        case tCOMMAND:
            result = mCmdEdit->text(const_cast<EditDisplayAlarmDlg*>(this), showErrorMessage);
            if (result.isEmpty())
                return false;
            break;
    }
    return true;
717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734
}


/*=============================================================================
= Class EditCommandAlarmDlg
= Dialog to edit command alarms.
=============================================================================*/

QString EditCommandAlarmDlg::i18n_chk_EnterScript()        { return i18nc("@option:check", "Enter a script"); }
QString EditCommandAlarmDlg::i18n_radio_ExecInTermWindow() { return i18nc("@option:radio", "Execute in terminal window"); }
QString EditCommandAlarmDlg::i18n_chk_ExecInTermWindow()   { return i18nc("@option:check", "Execute in terminal window"); }


/******************************************************************************
* Constructor.
* Parameters:
*   Template = true to edit/create an alarm template
*            = false to edit/create an alarm.
735
*   event   != to initialise the dialog to show the specified event's data.
736
*/
737
EditCommandAlarmDlg::EditCommandAlarmDlg(bool Template, QWidget* parent, GetResourceType getResource)
738
    : EditAlarmDlg(Template, KAEvent::COMMAND, parent, getResource)
739
{
Laurent Montel's avatar
Laurent Montel committed
740
    qCDebug(KALARM_LOG) << "New";
Laurent Montel's avatar
Laurent Montel committed
741
    init(nullptr);
742 743
}

744
EditCommandAlarmDlg::EditCommandAlarmDlg(bool Template, const KAEvent* event, bool newAlarm, QWidget* parent,
745
                                         GetResourceType getResource, bool readOnly)
746
    : EditAlarmDlg(Template, event, newAlarm, parent, getResource, readOnly)
747
{
Laurent Montel's avatar
Laurent Montel committed
748
    qCDebug(KALARM_LOG) << "Event.id()";
749
    init(event);
750 751 752 753 754
}

/******************************************************************************
* Return the window caption.
*/
755
QString EditCommandAlarmDlg::type_caption() const
756
{
757 758
    return isTemplate() ? (isNewAlarm() ? i18nc("@title:window", "New Command Alarm Template") : i18nc("@title:window", "Edit Command Alarm Template"))
                        : (isNewAlarm() ? i18nc("@title:window", "New Command Alarm") : i18nc("@title:window", "Edit Command Alarm"));
759 760 761 762 763 764 765
}

/******************************************************************************
* Set up the command alarm dialog controls.
*/
void EditCommandAlarmDlg::type_init(QWidget* parent, QVBoxLayout* frameLayout)
{
766
    mTryButton->setWhatsThis(i18nc("@info:whatsthis", "Execute the specified command now"));
767 768

    mCmdEdit = new CommandEdit(parent);
Laurent Montel's avatar
Laurent Montel committed
769 770
    connect(mCmdEdit, &CommandEdit::scriptToggled, this, &EditCommandAlarmDlg::slotCmdScriptToggled);
    connect(mCmdEdit, &CommandEdit::changed, this, &EditCommandAlarmDlg::contentsChanged);
771 772 773 774 775 776 777
    frameLayout->addWidget(mCmdEdit);

    // What to do with command output

    mCmdOutputBox = new QGroupBox(i18nc("@title:group", "Command Output"), parent);
    frameLayout->addWidget(mCmdOutputBox);
    QVBoxLayout* vlayout = new QVBoxLayout(mCmdOutputBox);
778 779
    vlayout->setMargin(style()->pixelMetric(QStyle::PM_DefaultChildMargin));
    vlayout->setSpacing(style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing));
780
    mCmdOutputGroup = new ButtonGroup(mCmdOutputBox);
Laurent Montel's avatar
Laurent Montel committed
781
    connect(mCmdOutputGroup, &ButtonGroup::buttonSet, this, &EditCommandAlarmDlg::contentsChanged);
782 783 784 785 786 787 788 789 790

    // Execute in terminal window
    mCmdExecInTerm = new RadioButton(i18n_radio_ExecInTermWindow(), mCmdOutputBox);
    mCmdExecInTerm->setFixedSize(mCmdExecInTerm->sizeHint());
    mCmdExecInTerm->setWhatsThis(i18nc("@info:whatsthis", "Check to execute the command in a terminal window"));
    mCmdOutputGroup->addButton(mCmdExecInTerm, Preferences::Log_Terminal);
    vlayout->addWidget(mCmdExecInTerm, 0, Qt::AlignLeft);

    // Log file name edit box
Laurent Montel's avatar
Laurent Montel committed
791
    QWidget* box = new QWidget(mCmdOutputBox);
David Jarvie's avatar
Tidy up  
David Jarvie committed
792 793 794
    QHBoxLayout* boxHLayout = new QHBoxLayout(box);
    boxHLayout->setMargin(0);
    boxHLayout->setSpacing(0);
795 796
    (new QWidget(box))->setFixedWidth(mCmdExecInTerm->style()->pixelMetric(QStyle::PM_ExclusiveIndicatorWidth));   // indent the edit box
    mCmdLogFileEdit = new LineEdit(LineEdit::Url, box);
David Jarvie's avatar
Tidy up  
David Jarvie committed
797
    boxHLayout->addWidget(mCmdLogFileEdit);
798 799
    mCmdLogFileEdit->setAcceptDrops(true);
    mCmdLogFileEdit->setWhatsThis(i18nc("@info:whatsthis", "Enter the name or path of the log file."));
Laurent Montel's avatar
Laurent Montel committed
800
    connect(mCmdLogFileEdit, &LineEdit::textChanged, this, &EditCommandAlarmDlg::contentsChanged);
801 802 803 804

    // Log file browse button.
    // The file browser dialog is activated by the PickLogFileRadio class.
    QPushButton* browseButton = new QPushButton(box);
David Jarvie's avatar
Tidy up  
David Jarvie committed
805
    boxHLayout->addWidget(browseButton);
806
    browseButton->setIcon(QIcon::fromTheme(QStringLiteral("document-open")));
807 808 809 810 811 812 813 814 815
    int size = browseButton->sizeHint().height();
    browseButton->setFixedSize(size, size);
    browseButton->setToolTip(i18nc("@info:tooltip", "Choose a file"));
    browseButton->setWhatsThis(i18nc("@info:whatsthis", "Select a log file."));

    // Log output to file
    mCmdLogToFile = new PickLogFileRadio(browseButton, mCmdLogFileEdit, i18nc("@option:radio", "Log to file"), mCmdOutputGroup, mCmdOutputBox);
    mCmdLogToFile->setFixedSize(mCmdLogToFile->sizeHint());
    mCmdLogToFile->setWhatsThis(i18nc("@info:whatsthis", "Check to log the command output to a local file. The output will be appended to any existing contents of the file."));
Laurent Montel's avatar
Laurent Montel committed
816
    connect(mCmdLogToFile, &PickLogFileRadio::fileChanged, this, &EditCommandAlarmDlg::contentsChanged);
817 818 819 820 821 822 823 824 825 826 827 828
    mCmdOutputGroup->addButton(mCmdLogToFile, Preferences::Log_File);
    vlayout->addWidget(mCmdLogToFile, 0, Qt::AlignLeft);
    vlayout->addWidget(box);

    // Discard output
    mCmdDiscardOutput = new RadioButton(i18nc("@option:radio", "Discard"), mCmdOutputBox);
    mCmdDiscardOutput->setFixedSize(mCmdDiscardOutput->sizeHint());
    mCmdDiscardOutput->setWhatsThis(i18nc("@info:whatsthis", "Check to discard command output."));
    mCmdOutputGroup->addButton(mCmdDiscardOutput, Preferences::Log_Discard);
    vlayout->addWidget(mCmdDiscardOutput, 0, Qt::AlignLeft);

    // Top-adjust the controls
Laurent Montel's avatar
Laurent Montel committed
829
    mCmdPadding = new QWidget(parent);
David Jarvie's avatar
Tidy up  
David Jarvie committed
830 831 832
    QHBoxLayout* hlayout = new QHBoxLayout(mCmdPadding);
    hlayout->setMargin(0);
    hlayout->setSpacing(0);
833 834
    frameLayout->addWidget(mCmdPadding);
    frameLayout->setStretchFactor(mCmdPadding, 1);
835 836 837
}

/******************************************************************************
838
* Initialise the dialog controls from the specified event.
839 840 841
*/
void EditCommandAlarmDlg::type_initValues(const KAEvent* event)
{
842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859
    if (event)
    {
        // Set the values to those for the specified event
        RadioButton* logType = event->commandXterm()       ? mCmdExecInTerm
                             : !event->logFile().isEmpty() ? mCmdLogToFile
                             :                               mCmdDiscardOutput;
        if (logType == mCmdLogToFile)
            mCmdLogFileEdit->setText(event->logFile());    // set file name before setting radio button
        logType->setChecked(true);
    }
    else
    {
        // Set the values to their defaults
        mCmdEdit->setScript(Preferences::defaultCmdScript());
        mCmdLogFileEdit->setText(Preferences::defaultCmdLogFile());    // set file name before setting radio button
        mCmdOutputGroup->setButton(Preferences::defaultCmdLogType());
    }
    slotCmdScriptToggled(mCmdEdit->isScript());
860 861
}

862 863 864 865 866 867
/******************************************************************************
* Called when the More/Less Options button is clicked.
* Show/hide the optional options.
*/
void EditCommandAlarmDlg::type_showOptions(bool more)
{
868 869 870 871
    if (more)
        mCmdOutputBox->show();
    else
        mCmdOutputBox->hide();
872 873
}

874 875 876
/******************************************************************************
* Set the dialog's action and the action's text.
*/
877
void EditCommandAlarmDlg::setAction(KAEvent::SubAction action, const AlarmText& alarmText)
878
{
879 880 881
    Q_UNUSED(action);
    Q_ASSERT(action == KAEvent::COMMAND);
    mCmdEdit->setText(alarmText);
882 883 884 885 886 887 888
}

/******************************************************************************
* Set the read-only status of all non-template controls.
*/
void EditCommandAlarmDlg::setReadOnly(bool readOnly)
{
889 890 891 892 893 894 895
    if (!isTemplate()  &&  !ShellProcess::authorised())
        readOnly = true;     // don't allow editing of existing command alarms in kiosk mode
    mCmdEdit->setReadOnly(readOnly);
    mCmdExecInTerm->setReadOnly(readOnly);
    mCmdLogToFile->setReadOnly(readOnly);
    mCmdDiscardOutput->setReadOnly(readOnly);
    EditAlarmDlg::setReadOnly(readOnly);
896 897 898 899 900 901 902
}

/******************************************************************************
* Save the state of all controls.
*/
void EditCommandAlarmDlg::saveState(const KAEvent* event)
{
903 904 905 906
    EditAlarmDlg::saveState(event);
    mSavedCmdScript      = mCmdEdit->isScript();
    mSavedCmdOutputRadio = mCmdOutputGroup->checkedButton();
    mSavedCmdLogFile     = mCmdLogFileEdit->text();
907 908 909 910 911 912 913 914 915 916
}

/******************************************************************************
* Check whether any of the controls has changed state since the dialog was
* first displayed.
* Reply = true if any controls have changed, or if it's a new event.
*       = false if no controls have changed.
*/
bool EditCommandAlarmDlg::type_stateChanged() const
{
917 918 919 920 921 922 923 924 925
    if (mSavedCmdScript      != mCmdEdit->isScript()
    ||  mSavedCmdOutputRadio != mCmdOutputGroup->checkedButton())
        return true;
    if (mCmdOutputGroup->checkedButton() == mCmdLogToFile)
    {
        if (mSavedCmdLogFile != mCmdLogFileEdit->text())
            return true;
    }
    return false;
926 927 928
}

/******************************************************************************
929
* Extract the data in the dialog specific to the alarm type and set up a
930 931 932 933
* KAEvent from it.
*/
void EditCommandAlarmDlg::type_setEvent(KAEvent& event, const KDateTime& dt, const QString& text, int lateCancel, bool trial)
{
934 935 936 937
    Q_UNUSED(trial);
    event.set(dt, text, QColor(), QColor(), QFont(), KAEvent::COMMAND, lateCancel, getAlarmFlags());
    if (mCmdOutputGroup->checkedButton() == mCmdLogToFile)
        event.setLogFile(mCmdLogFileEdit->text());
938 939 940 941 942
}

/******************************************************************************
* Get the currently specified alarm flag bits.
*/
943
KAEvent::Flags EditCommandAlarmDlg::getAlarmFlags() const
944
{
945 946 947 948
    KAEvent::Flags flags = EditAlarmDlg::getAlarmFlags();
    if (mCmdEdit->isScript())                               flags |= KAEvent::SCRIPT;
    if (mCmdOutputGroup->checkedButton() == mCmdExecInTerm) flags |= KAEvent::EXEC_IN_XTERM;
    return flags;
949 950 951 952 953 954 955
}

/******************************************************************************
* Validate and convert command alarm data.
*/
bool EditCommandAlarmDlg::type_validate(bool trial)
{
956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979
    Q_UNUSED(trial);
    if (mCmdOutputGroup->checkedButton() == mCmdLogToFile)
    {
        // Validate the log file name
        QString file = mCmdLogFileEdit->text();
        QFileInfo info(file);
        QDir::setCurrent(QDir::homePath());
        bool err = file.isEmpty()  ||  info.isDir();
        if (!err)
        {
            if (info.exists())
            {
                err = !info.isWritable();
            }
            else
            {
                QFileInfo dirinfo(info.absolutePath());    // get absolute directory path
                err = (!dirinfo.isDir()  ||  !dirinfo.isWritable());
            }
        }
        if (err)
        {
            showMainPage();
            mCmdLogFileEdit->setFocus();
980
            KAMessageBox::sorry(this, i18nc("@info", "Log file must be the name or path of a local file, with write permission."));
981 982 983 984 985
            return false;
        }
        // Convert the log file to an absolute path
        mCmdLogFileEdit->setText(info.absoluteFilePath());
    }
986 987 988 989 990 991 992
    else if (mCmdOutputGroup->checkedButton() == mCmdExecInTerm)
    {
        if (KAMessageBox::warningContinueCancel(this, xi18nc("@info", "<para>No terminal is selected for command alarms.</para>"
                                                             "<para>Please set it in the <application>KAlarm</application> Configuration dialog.</para>"))
                != KMessageBox::Continue)
            return false;
    }
993
    return true;
994 995 996
}

/******************************************************************************
997
* Called when the Try action has been executed.
998 999
* Tell the user the result of the Try action.
*/
1000
void EditCommandAlarmDlg::type_executedTry(const QString& text, void* result)
1001
{
1002 1003 1004
    ShellProcess* proc = (ShellProcess*)result;
    if (proc  &&  proc != (void*)-1
    &&  mCmdOutputGroup->checkedButton() != mCmdExecInTerm)
1005 1006
    {
        theApp()->commandMessage(proc, this);
Laurent Montel's avatar
Laurent Montel committed
1007
        KAMessageBox::information(this, xi18nc("@info", "Command executed: <icode>%1</icode>", text));
Laurent Montel's avatar
Laurent Montel committed
1008
        theApp()->commandMessage(proc, nullptr);
1009
    }
1010 1011 1012 1013 1014 1015 1016 1017
}

/******************************************************************************
* Called when one of the command type radio buttons is clicked,
* to display the appropriate edit field.
*/
void EditCommandAlarmDlg::slotCmdScriptToggled(bool on)
{
1018 1019 1020 1021
    if (on)
        mCmdPadding->hide();
    else
        mCmdPadding->show();
1022 1023 1024 1025 1026 1027 1028
}

/******************************************************************************
* Clean up the alarm text.
*/
bool EditCommandAlarmDlg::checkText(QString& result, bool showErrorMessage) const
{
1029 1030 1031 1032
    result = mCmdEdit->text(const_cast<EditCommandAlarmDlg*>(this), showErrorMessage);
    if (result.isEmpty())
        return false;
    return true;
1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048
}


/*=============================================================================
= Class EditEmailAlarmDlg
= Dialog to edit email alarms.
=============================================================================*/

QString EditEmailAlarmDlg::i18n_chk_CopyEmailToSelf()    { return i18nc("@option:check", "Copy email to self"); }


/******************************************************************************
* Constructor.
* Parameters:
*   Template = true to edit/create an alarm template
*            = false to edit/create an alarm.
1049
*   event   != to initialise the dialog to show the specified event's data.
1050
*/
1051
EditEmailAlarmDlg::EditEmailAlarmDlg(bool Template, QWidget* parent, GetResourceType getResource)
1052
    : EditAlarmDlg(Template, KAEvent::