File indexing completed on 2024-05-12 05:14:44
0001 /* 0002 * editdlgtypes.cpp - dialogs to create or edit alarm or alarm template types 0003 * Program: kalarm 0004 * SPDX-FileCopyrightText: 2001-2023 David Jarvie <djarvie@kde.org> 0005 * 0006 * SPDX-License-Identifier: GPL-2.0-or-later 0007 */ 0008 0009 #include "editdlgtypes.h" 0010 #include "editdlg_p.h" 0011 0012 #include "emailidcombo.h" 0013 #include "fontcolourbutton.h" 0014 #include "functions.h" 0015 #include "kalarmapp.h" 0016 #include "kamail.h" 0017 #include "latecancel.h" 0018 #include "mainwindow.h" 0019 #include "messagewindow.h" 0020 #include "pickfileradio.h" 0021 #include "reminder.h" 0022 #include "soundpicker.h" 0023 #include "sounddlg.h" 0024 #include "specialactions.h" 0025 #include "lib/buttongroup.h" 0026 #include "lib/checkbox.h" 0027 #include "lib/dragdrop.h" 0028 #include "lib/file.h" 0029 #include "lib/lineedit.h" 0030 #include "lib/messagebox.h" 0031 #include "lib/radiobutton.h" 0032 #include "lib/shellprocess.h" 0033 #include "lib/timespinbox.h" 0034 #include "kalarmcalendar/identities.h" 0035 #include "akonadiplugin/akonadiplugin.h" 0036 #include "kalarm_debug.h" 0037 0038 #include <KCalUtils/ICalDrag> 0039 #include <KCalendarCore/Person> 0040 0041 #include <KLocalizedString> 0042 #include <KFileItem> 0043 0044 #include <QComboBox> 0045 #include <QLabel> 0046 #include <QDir> 0047 #include <QStyle> 0048 #include <QGroupBox> 0049 #include <QGridLayout> 0050 #include <QHBoxLayout> 0051 #include <QVBoxLayout> 0052 #include <QDragEnterEvent> 0053 #include <QMimeData> 0054 #include <QStandardItemModel> 0055 0056 using namespace KAlarmCal; 0057 using namespace KCalendarCore; 0058 0059 enum { tTEXT, tFILE, tCOMMAND }; // order of mTypeCombo items 0060 enum { dWINDOW, dNOTIFY }; // order of mDisplayMethodCombo items 0061 0062 0063 /*============================================================================= 0064 = Class PickLogFileRadio 0065 =============================================================================*/ 0066 class PickLogFileRadio : public PickFileRadio 0067 { 0068 public: 0069 PickLogFileRadio(QPushButton* b, LineEdit* e, const QString& text, ButtonGroup* group, QWidget* parent) 0070 : PickFileRadio(b, e, text, group, parent) { } 0071 bool pickFile(QString& file) override // called when browse button is pressed to select a log file 0072 { 0073 return File::browseFile(file, i18nc("@title:window", "Choose Log File"), mDefaultDir, fileEdit()->text(), 0074 false, parentWidget()); 0075 } 0076 private: 0077 QString mDefaultDir; // default directory for log file browse button 0078 }; 0079 0080 0081 /*============================================================================= 0082 = Class EditDisplayAlarmDlg 0083 = Dialog to edit display alarms. 0084 =============================================================================*/ 0085 0086 QString EditDisplayAlarmDlg::i18n_lbl_DisplayMethod() { return i18nc("@label:listbox How to display alarms", "Display method:"); } 0087 QString EditDisplayAlarmDlg::i18n_combo_Window() { return i18nc("@item:inlistbox", "Window"); } 0088 QString EditDisplayAlarmDlg::i18n_combo_Notify() { return i18nc("@item:inlistbox", "Notification"); } 0089 QString EditDisplayAlarmDlg::i18n_chk_ConfirmAck() { return i18nc("@option:check", "Confirm acknowledgment"); } 0090 0091 /****************************************************************************** 0092 * Constructor. 0093 * Parameters: 0094 * Template = true to edit/create an alarm template 0095 * = false to edit/create an alarm. 0096 * event != to initialise the dialog to show the specified event's data. 0097 */ 0098 EditDisplayAlarmDlg::EditDisplayAlarmDlg(bool Template, QWidget* parent, GetResourceType getResource) 0099 : EditAlarmDlg(Template, KAEvent::SubAction::Message, parent, getResource) 0100 { 0101 qCDebug(KALARM_LOG) << "EditDisplayAlarmDlg: New"; 0102 init(KAEvent()); 0103 } 0104 0105 EditDisplayAlarmDlg::EditDisplayAlarmDlg(bool Template, const KAEvent& event, bool newAlarm, QWidget* parent, 0106 GetResourceType getResource, bool readOnly) 0107 : EditAlarmDlg(Template, event, newAlarm, parent, getResource, readOnly) 0108 { 0109 qCDebug(KALARM_LOG) << "EditDisplayAlarmDlg: Event.id()"; 0110 init(event); 0111 } 0112 0113 /****************************************************************************** 0114 * Return the window caption. 0115 */ 0116 QString EditDisplayAlarmDlg::type_caption() const 0117 { 0118 return isTemplate() ? (isNewAlarm() ? i18nc("@title:window", "New Display Alarm Template") : i18nc("@title:window", "Edit Display Alarm Template")) 0119 : (isNewAlarm() ? i18nc("@title:window", "New Display Alarm") : i18nc("@title:window", "Edit Display Alarm")); 0120 } 0121 0122 /****************************************************************************** 0123 * Set up the dialog controls common to display alarms. 0124 */ 0125 void EditDisplayAlarmDlg::type_init(QWidget* parent, QVBoxLayout* frameLayout) 0126 { 0127 // Display type combo box 0128 QWidget* box = new QWidget(parent); // to group widgets for QWhatsThis text 0129 0130 auto boxHLayout = new QHBoxLayout(box); 0131 boxHLayout->setContentsMargins(0, 0, 0, 0); 0132 QLabel* label = new QLabel(i18nc("@label:listbox", "Display type:"), box); 0133 boxHLayout->addWidget(label); 0134 mTypeCombo = new ComboBox(box); 0135 boxHLayout->addWidget(mTypeCombo); 0136 const QString textItem = i18nc("@item:inlistbox", "Text message"); 0137 const QString fileItem = i18nc("@item:inlistbox", "File contents"); 0138 const QString commandItem = i18nc("@item:inlistbox", "Command output"); 0139 mTypeCombo->addItem(textItem); // index = tTEXT 0140 mTypeCombo->addItem(fileItem); // index = tFILE 0141 mTypeCombo->addItem(commandItem); // index = tCOMMAND 0142 mTypeCombo->setCurrentIndex(-1); // ensure slotAlarmTypeChanged() is called when index is set 0143 if (!ShellProcess::authorised()) 0144 { 0145 // User not authorised to issue shell commands - disable Command Output option 0146 auto model = qobject_cast<QStandardItemModel*>(mTypeCombo->model()); 0147 if (model) 0148 { 0149 QModelIndex index = model->index(2, mTypeCombo->modelColumn(), mTypeCombo->rootModelIndex()); 0150 QStandardItem* item = model->itemFromIndex(index); 0151 if (item) 0152 item->setEnabled(false); 0153 } 0154 } 0155 connect(mTypeCombo, static_cast<void (ComboBox::*)(int)>(&ComboBox::currentIndexChanged), this, &EditDisplayAlarmDlg::slotAlarmTypeChanged); 0156 connect(mTypeCombo, static_cast<void (ComboBox::*)(int)>(&ComboBox::currentIndexChanged), this, &EditDisplayAlarmDlg::contentsChanged); 0157 label->setBuddy(mTypeCombo); 0158 box->setWhatsThis(xi18nc("@info:whatsthis", "<para>Select what the alarm should display:" 0159 "<list><item><interface>%1</interface>: the alarm will display the text message you type in.</item>" 0160 "<item><interface>%2</interface>: the alarm will display the contents of a text or image file.</item>" 0161 "<item><interface>%3</interface>: the alarm will display the output from a command.</item></list></para>", 0162 textItem, fileItem, commandItem)); 0163 auto hlayout = new QHBoxLayout(); 0164 hlayout->setContentsMargins(0, 0, 0, 0); 0165 frameLayout->addLayout(hlayout); 0166 hlayout->addWidget(box); 0167 hlayout->addStretch(); // left adjust the control 0168 0169 // Text message edit box 0170 mTextMessageEdit = new TextEdit(parent); 0171 mTextMessageEdit->setLineWrapMode(KTextEdit::NoWrap); 0172 mTextMessageEdit->enableEmailDrop(); // allow drag-and-drop of emails onto this widget 0173 mTextMessageEdit->setWhatsThis(i18nc("@info:whatsthis", "Enter the text of the alarm message. It may be multi-line.")); 0174 connect(mTextMessageEdit, &TextEdit::textChanged, this, &EditDisplayAlarmDlg::contentsChanged); 0175 frameLayout->addWidget(mTextMessageEdit); 0176 0177 // File name edit box 0178 mFileBox = new QWidget(parent); 0179 frameLayout->addWidget(mFileBox); 0180 auto fileBoxHLayout = new QHBoxLayout(mFileBox); 0181 fileBoxHLayout->setContentsMargins(0, 0, 0, 0); 0182 fileBoxHLayout->setSpacing(0); 0183 mFileMessageEdit = new LineEdit(LineEdit::Type::Url, mFileBox); 0184 fileBoxHLayout->addWidget(mFileMessageEdit); 0185 mFileMessageEdit->setAcceptDrops(true); 0186 mFileMessageEdit->setWhatsThis(i18nc("@info:whatsthis", "Enter the name or URL of a text or image file to display.")); 0187 connect(mFileMessageEdit, &LineEdit::textChanged, this, &EditDisplayAlarmDlg::contentsChanged); 0188 0189 // File browse button 0190 mFileBrowseButton = new QPushButton(mFileBox); 0191 fileBoxHLayout->addWidget(mFileBrowseButton); 0192 mFileBrowseButton->setIcon(QIcon::fromTheme(QStringLiteral("document-open"))); 0193 mFileBrowseButton->setToolTip(i18nc("@info:tooltip", "Choose a file")); 0194 mFileBrowseButton->setWhatsThis(i18nc("@info:whatsthis", "Select a text or image file to display.")); 0195 connect(mFileBrowseButton, &QPushButton::clicked, this, &EditDisplayAlarmDlg::slotPickFile); 0196 0197 // Command type checkbox and edit box 0198 mCmdEdit = new CommandEdit(parent); 0199 connect(mCmdEdit, &CommandEdit::scriptToggled, this, &EditDisplayAlarmDlg::slotCmdScriptToggled); 0200 connect(mCmdEdit, &CommandEdit::changed, this, &EditDisplayAlarmDlg::contentsChanged); 0201 frameLayout->addWidget(mCmdEdit); 0202 0203 // Sound checkbox and file selector 0204 hlayout = new QHBoxLayout(); 0205 hlayout->setContentsMargins(0, 0, 0, 0); 0206 frameLayout->addLayout(hlayout); 0207 mSoundPicker = new SoundPicker(parent); 0208 connect(mSoundPicker, &SoundPicker::changed, this, &EditDisplayAlarmDlg::contentsChanged); 0209 hlayout->addWidget(mSoundPicker); 0210 hlayout->addSpacing(2 * style()->pixelMetric(QStyle::PM_LayoutHorizontalSpacing)); 0211 hlayout->addStretch(); 0212 0213 // Font and colour choice button and sample text 0214 mFontColourButton = new FontColourButton(parent); 0215 mFontColourButton->setMaximumHeight(mFontColourButton->sizeHint().height() * 3/2); 0216 hlayout->addWidget(mFontColourButton); 0217 connect(mFontColourButton, &FontColourButton::selected, this, &EditDisplayAlarmDlg::setColours); 0218 connect(mFontColourButton, &FontColourButton::selected, this, &EditDisplayAlarmDlg::contentsChanged); 0219 0220 // Display method selector 0221 hlayout = new QHBoxLayout(); 0222 hlayout->setContentsMargins(0, 0, 0, 0); 0223 frameLayout->addLayout(hlayout); 0224 mDisplayMethodBox = new QWidget(parent); // to group widgets for QWhatsThis text 0225 boxHLayout = new QHBoxLayout(mDisplayMethodBox); 0226 boxHLayout->setContentsMargins(0, 0, 0, 0); 0227 label = new QLabel(i18n_lbl_DisplayMethod(), mDisplayMethodBox); 0228 boxHLayout->addWidget(label); 0229 mDisplayMethodCombo = new ComboBox(mDisplayMethodBox); 0230 boxHLayout->addWidget(mDisplayMethodCombo); 0231 const QString windowItem = i18n_combo_Window(); 0232 const QString notifyItem = i18n_combo_Notify(); 0233 mDisplayMethodCombo->addItem(windowItem); // index = dWINDOW 0234 mDisplayMethodCombo->addItem(notifyItem); // index = dNOTIFY 0235 connect(mDisplayMethodCombo, static_cast<void (ComboBox::*)(int)>(&ComboBox::currentIndexChanged), this, &EditDisplayAlarmDlg::slotDisplayMethodChanged); 0236 connect(mDisplayMethodCombo, static_cast<void (ComboBox::*)(int)>(&ComboBox::currentIndexChanged), this, &EditDisplayAlarmDlg::contentsChanged); 0237 label->setBuddy(mDisplayMethodCombo); 0238 mDisplayMethodBox->setWhatsThis(i18nc("@info:whatsthis", "Select whether to display the alarm in a window or by the notification system.")); 0239 hlayout->addWidget(mDisplayMethodBox); 0240 hlayout->addSpacing(2 * style()->pixelMetric(QStyle::PM_LayoutHorizontalSpacing)); 0241 hlayout->addStretch(); 0242 0243 if (ShellProcess::authorised()) // don't display if shell commands not allowed (e.g. kiosk mode) 0244 { 0245 // Special actions button 0246 mSpecialActionsButton = new SpecialActionsButton(false, parent); 0247 connect(mSpecialActionsButton, &SpecialActionsButton::selected, this, &EditDisplayAlarmDlg::contentsChanged); 0248 hlayout->addWidget(mSpecialActionsButton); 0249 } 0250 0251 // Top-adjust the controls 0252 mFilePadding = new QWidget(parent); 0253 hlayout = new QHBoxLayout(mFilePadding); 0254 hlayout->setContentsMargins(0, 0, 0, 0); 0255 hlayout->setSpacing(0); 0256 frameLayout->addWidget(mFilePadding); 0257 frameLayout->setStretchFactor(mFilePadding, 1); 0258 } 0259 0260 /****************************************************************************** 0261 * Create a reminder control. 0262 */ 0263 Reminder* EditDisplayAlarmDlg::createReminder(QWidget* parent) 0264 { 0265 return new Reminder(i18nc("@info:whatsthis", "Check to additionally display a reminder in advance of or after the main alarm time(s)."), 0266 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()), 0267 i18nc("@info:whatsthis", "Select whether the reminder should be triggered before or after the main alarm"), 0268 true, true, parent); 0269 } 0270 0271 /****************************************************************************** 0272 * Create an "acknowledgement confirmation required" checkbox. 0273 */ 0274 CheckBox* EditDisplayAlarmDlg::createConfirmAckCheckbox(QWidget* parent) 0275 { 0276 CheckBox* confirmAck = new CheckBox(i18n_chk_ConfirmAck(), parent); 0277 confirmAck->setWhatsThis(i18nc("@info:whatsthis", "Check to be prompted for confirmation when you acknowledge the alarm.")); 0278 return confirmAck; 0279 } 0280 0281 /****************************************************************************** 0282 * Initialise the dialog controls from the specified event. 0283 */ 0284 void EditDisplayAlarmDlg::type_initValues(const KAEvent& event) 0285 { 0286 mTryButton->setToolTip(i18nc("@info:tooltip", "Display the alarm now")); 0287 mEmailId = -1; 0288 lateCancel()->showAutoClose(true); 0289 if (event.isValid()) 0290 { 0291 if (mAlarmType == KAEvent::SubAction::Message && event.emailId() 0292 && AlarmText::checkIfEmail(event.cleanText())) 0293 mEmailId = event.emailId(); 0294 lateCancel()->setAutoClose(event.autoClose()); 0295 mFontColourButton->setFont(event.font(), event.useDefaultFont()); 0296 mFontColourButton->setBgColour(event.bgColour()); 0297 mFontColourButton->setFgColour(event.fgColour()); 0298 setColours(event.fgColour(), event.bgColour()); 0299 mDisplayMethodCombo->setCurrentIndex(event.notify() ? dNOTIFY : dWINDOW); 0300 mConfirmAck->setChecked(event.confirmAck()); 0301 const bool recurs = event.recurs(); 0302 int reminderMins = event.reminderMinutes(); 0303 if (reminderMins > 0 && !event.reminderActive()) 0304 reminderMins = 0; // don't show advance reminder which has already passed 0305 if (!reminderMins) 0306 { 0307 if (event.reminderDeferral() && !recurs) 0308 { 0309 reminderMins = event.deferDateTime().minsTo(event.mainDateTime()); 0310 mReminderDeferral = true; 0311 } 0312 else if (event.reminderMinutes() && recurs) 0313 { 0314 reminderMins = event.reminderMinutes(); 0315 mReminderArchived = true; 0316 } 0317 } 0318 reminder()->setMinutes(reminderMins, dateOnly()); 0319 reminder()->setOnceOnly(event.reminderOnceOnly()); 0320 reminder()->enableOnceOnly(recurs); 0321 if (mSpecialActionsButton) 0322 mSpecialActionsButton->setActions(event.preAction(), event.postAction(), event.extraActionOptions()); 0323 Preferences::SoundType soundType = event.speak() ? Preferences::Sound_Speak 0324 : event.beep() ? Preferences::Sound_Beep 0325 : !event.audioFile().isEmpty() ? Preferences::Sound_File 0326 : Preferences::Sound_None; 0327 mSoundPicker->set(soundType, event.audioFile(), event.soundVolume(), 0328 event.fadeVolume(), event.fadeSeconds(), event.repeatSoundPause()); 0329 } 0330 else 0331 { 0332 // Set the values to their defaults 0333 if (!ShellProcess::authorised()) 0334 { 0335 // Don't allow shell commands in kiosk mode 0336 if (mSpecialActionsButton) 0337 mSpecialActionsButton->setEnabled(false); 0338 } 0339 lateCancel()->setAutoClose(Preferences::defaultAutoClose()); 0340 mTypeCombo->setCurrentIndex(0); 0341 mFontColourButton->setFont(Preferences::messageFont(), true); 0342 mFontColourButton->setBgColour(Preferences::defaultBgColour()); 0343 mFontColourButton->setFgColour(Preferences::defaultFgColour()); 0344 setColours(Preferences::defaultFgColour(), Preferences::defaultBgColour()); 0345 mDisplayMethodCombo->setCurrentIndex(Preferences::defaultDisplayMethod() == Preferences::Display_Window ? dWINDOW : dNOTIFY); 0346 mConfirmAck->setChecked(Preferences::defaultConfirmAck()); 0347 reminder()->setMinutes(0, false); 0348 reminder()->enableOnceOnly(isTimedRecurrence()); // must be called after mRecurrenceEdit is set up 0349 if (mSpecialActionsButton) 0350 { 0351 KAEvent::ExtraActionOptions opts({}); 0352 if (Preferences::defaultExecPreActionOnDeferral()) 0353 opts |= KAEvent::ExecPreActOnDeferral; 0354 if (Preferences::defaultCancelOnPreActionError()) 0355 opts |= KAEvent::CancelOnPreActError; 0356 if (Preferences::defaultDontShowPreActionError()) 0357 opts |= KAEvent::DontShowPreActError; 0358 mSpecialActionsButton->setActions(Preferences::defaultPreAction(), Preferences::defaultPostAction(), opts); 0359 } 0360 mSoundPicker->set(Preferences::defaultSoundType(), Preferences::defaultSoundFile(), 0361 Preferences::defaultSoundVolume(), -1, 0, (Preferences::defaultSoundRepeat() ? 0 : -1)); 0362 } 0363 slotDisplayMethodChanged(mDisplayMethodCombo->currentIndex()); 0364 } 0365 0366 /****************************************************************************** 0367 * Called when the More/Less Options button is clicked. 0368 * Show/hide the optional options. 0369 */ 0370 void EditDisplayAlarmDlg::type_showOptions(bool more) 0371 { 0372 if (mSpecialActionsButton) 0373 { 0374 if (more) 0375 { 0376 mDisplayMethodBox->show(); 0377 mSpecialActionsButton->show(); 0378 } 0379 else 0380 { 0381 mDisplayMethodBox->hide(); 0382 mSpecialActionsButton->hide(); 0383 } 0384 } 0385 } 0386 0387 /****************************************************************************** 0388 * Called when the font/color button has been clicked. 0389 * Set the colors in the message text entry control. 0390 */ 0391 void EditDisplayAlarmDlg::setColours(const QColor& fgColour, const QColor& bgColour) 0392 { 0393 QColor fg(fgColour); 0394 if (mDisplayMethodCombo->currentIndex() == dNOTIFY) 0395 { 0396 const QPalette pal = mFileMessageEdit->palette(); 0397 mTextMessageEdit->setPalette(pal); 0398 mTextMessageEdit->viewport()->setPalette(pal); 0399 fg = pal.color(QPalette::Text); 0400 } 0401 else 0402 { 0403 QPalette pal = mTextMessageEdit->palette(); 0404 pal.setColor(mTextMessageEdit->backgroundRole(), bgColour); 0405 pal.setColor(QPalette::Text, fgColour); 0406 mTextMessageEdit->setPalette(pal); 0407 pal = mTextMessageEdit->viewport()->palette(); 0408 pal.setColor(mTextMessageEdit->viewport()->backgroundRole(), bgColour); 0409 pal.setColor(QPalette::Text, fgColour); 0410 mTextMessageEdit->viewport()->setPalette(pal); 0411 } 0412 // Change the color of existing text 0413 const QTextCursor cursor = mTextMessageEdit->textCursor(); 0414 mTextMessageEdit->selectAll(); 0415 mTextMessageEdit->setTextColor(fg); 0416 mTextMessageEdit->setTextCursor(cursor); 0417 } 0418 0419 /****************************************************************************** 0420 * Set the dialog's action and the action's text. 0421 */ 0422 void EditDisplayAlarmDlg::setAction(KAEvent::SubAction action, const AlarmText& alarmText) 0423 { 0424 const QString text = alarmText.displayText(); 0425 switch (action) 0426 { 0427 case KAEvent::SubAction::Message: 0428 mTypeCombo->setCurrentIndex(tTEXT); 0429 mTextMessageEdit->setPlainText(text); 0430 mEmailId = alarmText.isEmail() ? alarmText.emailId() : -1; 0431 break; 0432 case KAEvent::SubAction::File: 0433 mTypeCombo->setCurrentIndex(tFILE); 0434 mFileMessageEdit->setText(text); 0435 break; 0436 case KAEvent::SubAction::Command: 0437 mTypeCombo->setCurrentIndex(tCOMMAND); 0438 mCmdEdit->setText(alarmText); 0439 break; 0440 default: 0441 Q_ASSERT(0); 0442 break; 0443 } 0444 } 0445 0446 /****************************************************************************** 0447 * Initialise various values in the New Alarm dialogue. 0448 */ 0449 void EditDisplayAlarmDlg::setBgColour(const QColor& colour) 0450 { 0451 mFontColourButton->setBgColour(colour); 0452 setColours(mFontColourButton->fgColour(), colour); 0453 } 0454 void EditDisplayAlarmDlg::setFgColour(const QColor& colour) 0455 { 0456 mFontColourButton->setFgColour(colour); 0457 setColours(colour, mFontColourButton->bgColour()); 0458 } 0459 void EditDisplayAlarmDlg::setNotify(bool notify) 0460 { 0461 mDisplayMethodCombo->setCurrentIndex(notify ? dNOTIFY : dWINDOW); 0462 } 0463 void EditDisplayAlarmDlg::setConfirmAck(bool confirm) 0464 { 0465 mConfirmAck->setChecked(confirm); 0466 } 0467 void EditDisplayAlarmDlg::setAutoClose(bool close) 0468 { 0469 lateCancel()->setAutoClose(close); 0470 } 0471 void EditDisplayAlarmDlg::setAudio(Preferences::SoundType type, const QString& file, float volume, int repeatPause) 0472 { 0473 mSoundPicker->set(type, file, volume, -1, 0, repeatPause); 0474 } 0475 void EditDisplayAlarmDlg::setReminder(int minutes, bool onceOnly) 0476 { 0477 reminder()->setMinutes(minutes, dateOnly()); 0478 reminder()->setOnceOnly(onceOnly); 0479 reminder()->enableOnceOnly(isTimedRecurrence()); 0480 } 0481 0482 /****************************************************************************** 0483 * Set the read-only status of all non-template controls. 0484 */ 0485 void EditDisplayAlarmDlg::setReadOnly(bool readOnly) 0486 { 0487 mTypeCombo->setReadOnly(readOnly); 0488 mTextMessageEdit->setReadOnly(readOnly); 0489 mFileMessageEdit->setReadOnly(readOnly); 0490 mCmdEdit->setReadOnly(readOnly); 0491 mFontColourButton->setReadOnly(readOnly); 0492 mSoundPicker->setReadOnly(readOnly); 0493 mDisplayMethodCombo->setReadOnly(readOnly); 0494 mConfirmAck->setReadOnly(readOnly); 0495 reminder()->setReadOnly(readOnly); 0496 if (mSpecialActionsButton) 0497 mSpecialActionsButton->setReadOnly(readOnly); 0498 if (readOnly) 0499 mFileBrowseButton->hide(); 0500 else 0501 mFileBrowseButton->show(); 0502 EditAlarmDlg::setReadOnly(readOnly); 0503 } 0504 0505 /****************************************************************************** 0506 * Save the state of all controls. 0507 */ 0508 void EditDisplayAlarmDlg::saveState(const KAEvent* event) 0509 { 0510 EditAlarmDlg::saveState(event); 0511 mSavedType = mTypeCombo->currentIndex(); 0512 mSavedCmdScript = mCmdEdit->isScript(); 0513 mSavedSoundType = mSoundPicker->sound(); 0514 mSavedSoundFile = mSoundPicker->file(); 0515 mSavedSoundVolume = mSoundPicker->volume(mSavedSoundFadeVolume, mSavedSoundFadeSeconds); 0516 mSavedRepeatPause = mSoundPicker->repeatPause(); 0517 mSavedDisplayMethod = mDisplayMethodCombo->currentIndex(); 0518 mSavedConfirmAck = mConfirmAck->isChecked(); 0519 mSavedFont = mFontColourButton->font(); 0520 mSavedFgColour = mFontColourButton->fgColour(); 0521 mSavedBgColour = mFontColourButton->bgColour(); 0522 mSavedReminder = reminder()->minutes(); 0523 mSavedOnceOnly = reminder()->isOnceOnly(); 0524 mSavedAutoClose = lateCancel()->isAutoClose(); 0525 if (mSpecialActionsButton) 0526 { 0527 mSavedPreAction = mSpecialActionsButton->preAction(); 0528 mSavedPostAction = mSpecialActionsButton->postAction(); 0529 mSavedPreActionOptions = mSpecialActionsButton->options(); 0530 } 0531 } 0532 0533 /****************************************************************************** 0534 * Check whether any of the controls has changed state since the dialog was 0535 * first displayed. 0536 * Reply = true if any controls have changed, or if it's a new event. 0537 * = false if no controls have changed. 0538 */ 0539 bool EditDisplayAlarmDlg::type_stateChanged() const 0540 { 0541 if (mSavedType != mTypeCombo->currentIndex() 0542 || mSavedCmdScript != mCmdEdit->isScript() 0543 || mSavedSoundType != mSoundPicker->sound() 0544 || mSavedDisplayMethod != mDisplayMethodCombo->currentIndex() 0545 || mSavedReminder != reminder()->minutes() 0546 || mSavedOnceOnly != reminder()->isOnceOnly()) 0547 return true; 0548 if (mDisplayMethodCombo->currentIndex() == dWINDOW) 0549 { 0550 if (mSavedConfirmAck != mConfirmAck->isChecked() 0551 || mSavedFont != mFontColourButton->font() 0552 || mSavedFgColour != mFontColourButton->fgColour() 0553 || mSavedBgColour != mFontColourButton->bgColour() 0554 || mSavedAutoClose != lateCancel()->isAutoClose()) 0555 return true; 0556 } 0557 if (mSpecialActionsButton) 0558 { 0559 if (mSavedPreAction != mSpecialActionsButton->preAction() 0560 || mSavedPostAction != mSpecialActionsButton->postAction() 0561 || mSavedPreActionOptions != mSpecialActionsButton->options()) 0562 return true; 0563 } 0564 if (mSavedSoundType == Preferences::Sound_File) 0565 { 0566 if (mSavedSoundFile != mSoundPicker->file()) 0567 return true; 0568 if (!mSavedSoundFile.isEmpty()) 0569 { 0570 float fadeVolume; 0571 int fadeSecs; 0572 if (mSavedRepeatPause != mSoundPicker->repeatPause() 0573 || mSavedSoundVolume != mSoundPicker->volume(fadeVolume, fadeSecs) 0574 || mSavedSoundFadeVolume != fadeVolume 0575 || mSavedSoundFadeSeconds != fadeSecs) 0576 return true; 0577 } 0578 } 0579 return false; 0580 } 0581 0582 /****************************************************************************** 0583 * Extract the data in the dialog specific to the alarm type and set up a 0584 * KAEvent from it. 0585 */ 0586 void EditDisplayAlarmDlg::type_setEvent(KAEvent& event, const KADateTime& dt, const QString& name, const QString& text, int lateCancel, bool trial) 0587 { 0588 KAEvent::SubAction type; 0589 switch (mTypeCombo->currentIndex()) 0590 { 0591 case tFILE: type = KAEvent::SubAction::File; break; 0592 case tCOMMAND: type = KAEvent::SubAction::Command; break; 0593 default: 0594 case tTEXT: type = KAEvent::SubAction::Message; break; 0595 } 0596 QColor fgColour, bgColour; 0597 QFont font; 0598 if (mDisplayMethodCombo->currentIndex() == dNOTIFY) 0599 { 0600 bgColour = Preferences::defaultBgColour(); 0601 fgColour = Preferences::defaultFgColour(); 0602 } 0603 else 0604 { 0605 bgColour = mFontColourButton->bgColour(); 0606 fgColour = mFontColourButton->fgColour(); 0607 font = mFontColourButton->font(); 0608 } 0609 event = KAEvent(dt, name, text, bgColour, fgColour, font, type, lateCancel, getAlarmFlags()); 0610 if (type == KAEvent::SubAction::Message) 0611 { 0612 if (AlarmText::checkIfEmail(text)) 0613 event.setEmailId(mEmailId); 0614 } 0615 float fadeVolume; 0616 int fadeSecs; 0617 const float volume = mSoundPicker->volume(fadeVolume, fadeSecs); 0618 const int repeatPause = mSoundPicker->repeatPause(); 0619 event.setAudioFile(mSoundPicker->file().toDisplayString(), volume, fadeVolume, fadeSecs, repeatPause); 0620 if (!trial && reminder()->isEnabled()) 0621 event.setReminder(reminder()->minutes(), reminder()->isOnceOnly()); 0622 if (mSpecialActionsButton && mSpecialActionsButton->isEnabled()) 0623 event.setActions(mSpecialActionsButton->preAction(), mSpecialActionsButton->postAction(), 0624 mSpecialActionsButton->options()); 0625 } 0626 0627 /****************************************************************************** 0628 * Get the currently specified alarm flag bits. 0629 */ 0630 KAEvent::Flags EditDisplayAlarmDlg::getAlarmFlags() const 0631 { 0632 const bool cmd = (mTypeCombo->currentIndex() == tCOMMAND); 0633 KAEvent::Flags flags = EditAlarmDlg::getAlarmFlags(); 0634 if (mSoundPicker->sound() == Preferences::Sound_Beep) flags |= KAEvent::BEEP; 0635 if (mSoundPicker->sound() == Preferences::Sound_Speak) flags |= KAEvent::SPEAK; 0636 if (mSoundPicker->repeatPause() >= 0) flags |= KAEvent::REPEAT_SOUND; 0637 if (cmd) flags |= KAEvent::DISPLAY_COMMAND; 0638 if (cmd && mCmdEdit->isScript()) flags |= KAEvent::SCRIPT; 0639 if (mDisplayMethodCombo->currentIndex() == dNOTIFY) 0640 { 0641 flags |= KAEvent::NOTIFY; 0642 flags |= KAEvent::DEFAULT_FONT; 0643 } 0644 else 0645 { 0646 if (mFontColourButton->defaultFont()) flags |= KAEvent::DEFAULT_FONT; 0647 if (mConfirmAck->isChecked()) flags |= KAEvent::CONFIRM_ACK; 0648 if (lateCancel()->isAutoClose()) flags |= KAEvent::AUTO_CLOSE; 0649 } 0650 return flags; 0651 } 0652 0653 /****************************************************************************** 0654 * Called when the alarm display type combo box is changed, to display the 0655 * appropriate set of controls for that action type. 0656 */ 0657 void EditDisplayAlarmDlg::slotAlarmTypeChanged(int index) 0658 { 0659 QWidget* focus = nullptr; 0660 switch (index) 0661 { 0662 case tTEXT: // text message 0663 mFileBox->hide(); 0664 mFilePadding->hide(); 0665 mCmdEdit->hide(); 0666 mTextMessageEdit->show(); 0667 mSoundPicker->showSpeak(true); 0668 mTryButton->setWhatsThis(i18nc("@info:whatsthis", "Display the alarm message now")); 0669 focus = mTextMessageEdit; 0670 break; 0671 case tFILE: // file contents 0672 mTextMessageEdit->hide(); 0673 mFileBox->show(); 0674 mFilePadding->show(); 0675 mCmdEdit->hide(); 0676 mSoundPicker->showSpeak(false); 0677 mTryButton->setWhatsThis(i18nc("@info:whatsthis", "Display the file now")); 0678 mFileMessageEdit->setNoSelect(); 0679 focus = mFileMessageEdit; 0680 break; 0681 case tCOMMAND: // command output 0682 mTextMessageEdit->hide(); 0683 mFileBox->hide(); 0684 slotCmdScriptToggled(mCmdEdit->isScript()); // show/hide mFilePadding 0685 mCmdEdit->show(); 0686 mSoundPicker->showSpeak(true); 0687 mTryButton->setWhatsThis(i18nc("@info:whatsthis", "Display the command output now")); 0688 focus = mCmdEdit; 0689 break; 0690 } 0691 if (focus) 0692 focus->setFocus(); 0693 } 0694 0695 /****************************************************************************** 0696 * Called when the display method combo box is changed, to enable/disable the 0697 * appropriate set of controls for that display method. 0698 */ 0699 void EditDisplayAlarmDlg::slotDisplayMethodChanged(int index) 0700 { 0701 const bool enable = (index == dWINDOW); 0702 mConfirmAck->setVisible(enable); 0703 mFontColourButton->setVisible(enable); 0704 mSoundPicker->showFile(enable); 0705 // Because notifications automatically time out after 10 seconds, 0706 // auto-close would always occur after a notification closes. 0707 lateCancel()->showAutoClose(enable); 0708 // Set the text message edit box colours according to the display method. 0709 setColours(mFontColourButton->fgColour(), mFontColourButton->bgColour()); 0710 } 0711 0712 /****************************************************************************** 0713 * Called when the file browse button is pressed to select a file to display. 0714 */ 0715 void EditDisplayAlarmDlg::slotPickFile() 0716 { 0717 static QString defaultDir; // default directory for file browse button 0718 QString file; 0719 if (File::browseFile(file, i18nc("@title:window", "Choose Text or Image File to Display"), 0720 defaultDir, mFileMessageEdit->text(), true, this)) 0721 { 0722 if (!file.isEmpty()) 0723 { 0724 mFileMessageEdit->setText(File::pathOrUrl(file)); 0725 contentsChanged(); 0726 } 0727 } 0728 } 0729 0730 /****************************************************************************** 0731 * Called when one of the command type radio buttons is clicked, 0732 * to display the appropriate edit field. 0733 */ 0734 void EditDisplayAlarmDlg::slotCmdScriptToggled(bool on) 0735 { 0736 if (on) 0737 mFilePadding->hide(); 0738 else 0739 mFilePadding->show(); 0740 } 0741 0742 /****************************************************************************** 0743 * Clean up the alarm text, and if it's a file, check whether it's valid. 0744 */ 0745 bool EditDisplayAlarmDlg::checkText(QString& result, bool showErrorMessage) const 0746 { 0747 switch (mTypeCombo->currentIndex()) 0748 { 0749 case tTEXT: 0750 result = mTextMessageEdit->toPlainText(); 0751 break; 0752 0753 case tFILE: 0754 { 0755 QString fileName = mFileMessageEdit->text().trimmed(); 0756 QUrl url; 0757 File::Error err = File::checkFileExists(fileName, url, MainWindow::mainMainWindow()); 0758 if (err == File::Error::None) 0759 { 0760 KFileItem fi(url); 0761 switch (File::fileType(fi.currentMimeType())) 0762 { 0763 case File::Type::TextFormatted: 0764 case File::Type::TextPlain: 0765 case File::Type::TextApplication: 0766 case File::Type::Image: 0767 break; 0768 default: 0769 err = File::Error::NotTextImage; 0770 break; 0771 } 0772 } 0773 if (err != File::Error::None && showErrorMessage) 0774 { 0775 mFileMessageEdit->setFocus(); 0776 if (!File::showFileErrMessage(fileName, err, File::Error::BlankDisplay, const_cast<EditDisplayAlarmDlg*>(this))) 0777 return false; 0778 } 0779 result = fileName; 0780 break; 0781 } 0782 case tCOMMAND: 0783 result = mCmdEdit->text(const_cast<EditDisplayAlarmDlg*>(this), showErrorMessage); 0784 if (result.isEmpty()) 0785 return false; 0786 break; 0787 } 0788 return true; 0789 } 0790 0791 0792 /*============================================================================= 0793 = Class EditCommandAlarmDlg 0794 = Dialog to edit command alarms. 0795 =============================================================================*/ 0796 0797 QString EditCommandAlarmDlg::i18n_chk_EnterScript() { return i18nc("@option:check", "Enter a script"); } 0798 QString EditCommandAlarmDlg::i18n_radio_ExecInTermWindow() { return i18nc("@option:radio", "Execute in terminal window"); } 0799 QString EditCommandAlarmDlg::i18n_chk_ExecInTermWindow() { return i18nc("@option:check", "Execute in terminal window"); } 0800 0801 0802 /****************************************************************************** 0803 * Constructor. 0804 * Parameters: 0805 * Template = true to edit/create an alarm template 0806 * = false to edit/create an alarm. 0807 * event != to initialise the dialog to show the specified event's data. 0808 */ 0809 EditCommandAlarmDlg::EditCommandAlarmDlg(bool Template, QWidget* parent, GetResourceType getResource) 0810 : EditAlarmDlg(Template, KAEvent::SubAction::Command, parent, getResource) 0811 { 0812 qCDebug(KALARM_LOG) << "EditCommandAlarmDlg: New"; 0813 init(KAEvent()); 0814 } 0815 0816 EditCommandAlarmDlg::EditCommandAlarmDlg(bool Template, const KAEvent& event, bool newAlarm, QWidget* parent, 0817 GetResourceType getResource, bool readOnly) 0818 : EditAlarmDlg(Template, event, newAlarm, parent, getResource, readOnly) 0819 { 0820 qCDebug(KALARM_LOG) << "EditCommandAlarmDlg: Event.id()"; 0821 init(event); 0822 } 0823 0824 /****************************************************************************** 0825 * Return the window caption. 0826 */ 0827 QString EditCommandAlarmDlg::type_caption() const 0828 { 0829 return isTemplate() ? (isNewAlarm() ? i18nc("@title:window", "New Command Alarm Template") : i18nc("@title:window", "Edit Command Alarm Template")) 0830 : (isNewAlarm() ? i18nc("@title:window", "New Command Alarm") : i18nc("@title:window", "Edit Command Alarm")); 0831 } 0832 0833 /****************************************************************************** 0834 * Set up the command alarm dialog controls. 0835 */ 0836 void EditCommandAlarmDlg::type_init(QWidget* parent, QVBoxLayout* frameLayout) 0837 { 0838 mTryButton->setWhatsThis(i18nc("@info:whatsthis", "Execute the specified command now")); 0839 mTryButton->setToolTip(i18nc("@info:tooltip", "Execute the specified command now")); 0840 0841 mCmdEdit = new CommandEdit(parent); 0842 connect(mCmdEdit, &CommandEdit::scriptToggled, this, &EditCommandAlarmDlg::slotCmdScriptToggled); 0843 connect(mCmdEdit, &CommandEdit::changed, this, &EditCommandAlarmDlg::contentsChanged); 0844 frameLayout->addWidget(mCmdEdit); 0845 0846 mCmdDontShowError = new CheckBox(i18nc("@option:check", "Do not notify errors"), parent); 0847 mCmdDontShowError->setWhatsThis(i18nc("@info:whatsthis", "Do not show error message if the command fails.")); 0848 frameLayout->addWidget(mCmdDontShowError, 0, Qt::AlignLeft); 0849 connect(mCmdDontShowError, &CheckBox::toggled, this, &EditCommandAlarmDlg::contentsChanged); 0850 0851 // What to do with command output 0852 0853 mCmdOutputBox = new QGroupBox(i18nc("@title:group", "Command Output"), parent); 0854 frameLayout->addWidget(mCmdOutputBox); 0855 auto vlayout = new QVBoxLayout(mCmdOutputBox); 0856 mCmdOutputGroup = new ButtonGroup(mCmdOutputBox); 0857 connect(mCmdOutputGroup, &ButtonGroup::buttonSet, this, &EditCommandAlarmDlg::contentsChanged); 0858 0859 // Execute in terminal window 0860 mCmdExecInTerm = new RadioButton(i18n_radio_ExecInTermWindow(), mCmdOutputBox); 0861 mCmdExecInTerm->setWhatsThis(i18nc("@info:whatsthis", "Check to execute the command in a terminal window")); 0862 mCmdOutputGroup->addButton(mCmdExecInTerm, Preferences::Log_Terminal); 0863 vlayout->addWidget(mCmdExecInTerm, 0, Qt::AlignLeft); 0864 0865 // Log file name edit box 0866 QWidget* box = new QWidget(mCmdOutputBox); 0867 auto boxHLayout = new QHBoxLayout(box); 0868 boxHLayout->setContentsMargins(0, 0, 0, 0); 0869 boxHLayout->setSpacing(0); 0870 (new QWidget(box))->setFixedWidth(mCmdExecInTerm->style()->pixelMetric(QStyle::PM_ExclusiveIndicatorWidth)); // indent the edit box 0871 mCmdLogFileEdit = new LineEdit(LineEdit::Type::Url, box); 0872 boxHLayout->addWidget(mCmdLogFileEdit); 0873 mCmdLogFileEdit->setAcceptDrops(true); 0874 mCmdLogFileEdit->setWhatsThis(i18nc("@info:whatsthis", "Enter the name or path of the log file.")); 0875 connect(mCmdLogFileEdit, &LineEdit::textChanged, this, &EditCommandAlarmDlg::contentsChanged); 0876 0877 // Log file browse button. 0878 // The file browser dialog is activated by the PickLogFileRadio class. 0879 auto browseButton = new QPushButton(box); 0880 boxHLayout->addWidget(browseButton); 0881 browseButton->setIcon(QIcon::fromTheme(QStringLiteral("document-open"))); 0882 browseButton->setToolTip(i18nc("@info:tooltip", "Choose a file")); 0883 browseButton->setWhatsThis(i18nc("@info:whatsthis", "Select a log file.")); 0884 0885 // Log output to file 0886 mCmdLogToFile = new PickLogFileRadio(browseButton, mCmdLogFileEdit, i18nc("@option:radio", "Log to file"), mCmdOutputGroup, mCmdOutputBox); 0887 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.")); 0888 connect(mCmdLogToFile, &PickLogFileRadio::fileChanged, this, &EditCommandAlarmDlg::contentsChanged); 0889 mCmdOutputGroup->addButton(mCmdLogToFile, Preferences::Log_File); 0890 vlayout->addWidget(mCmdLogToFile, 0, Qt::AlignLeft); 0891 vlayout->addWidget(box); 0892 0893 // Discard output 0894 mCmdDiscardOutput = new RadioButton(i18nc("@option:radio", "Discard"), mCmdOutputBox); 0895 mCmdDiscardOutput->setWhatsThis(i18nc("@info:whatsthis", "Check to discard command output.")); 0896 mCmdOutputGroup->addButton(mCmdDiscardOutput, Preferences::Log_Discard); 0897 vlayout->addWidget(mCmdDiscardOutput, 0, Qt::AlignLeft); 0898 0899 // Top-adjust the controls 0900 mCmdPadding = new QWidget(parent); 0901 auto hlayout = new QHBoxLayout(mCmdPadding); 0902 hlayout->setContentsMargins(0, 0, 0, 0); 0903 hlayout->setSpacing(0); 0904 frameLayout->addWidget(mCmdPadding); 0905 frameLayout->setStretchFactor(mCmdPadding, 1); 0906 } 0907 0908 /****************************************************************************** 0909 * Initialise the dialog controls from the specified event. 0910 */ 0911 void EditCommandAlarmDlg::type_initValues(const KAEvent& event) 0912 { 0913 if (event.isValid()) 0914 { 0915 // Set the values to those for the specified event 0916 RadioButton* logType = event.commandXterm() ? mCmdExecInTerm 0917 : !event.logFile().isEmpty() ? mCmdLogToFile 0918 : mCmdDiscardOutput; 0919 if (logType == mCmdLogToFile) 0920 mCmdLogFileEdit->setText(event.logFile()); // set file name before setting radio button 0921 logType->setChecked(true); 0922 mCmdDontShowError->setChecked(event.commandHideError()); 0923 } 0924 else 0925 { 0926 // Set the values to their defaults 0927 mCmdEdit->setScript(Preferences::defaultCmdScript()); 0928 mCmdLogFileEdit->setText(Preferences::defaultCmdLogFile()); // set file name before setting radio button 0929 mCmdOutputGroup->setButton(Preferences::defaultCmdLogType()); 0930 mCmdDontShowError->setChecked(false); 0931 } 0932 slotCmdScriptToggled(mCmdEdit->isScript()); 0933 } 0934 0935 /****************************************************************************** 0936 * Called when the More/Less Options button is clicked. 0937 * Show/hide the optional options. 0938 */ 0939 void EditCommandAlarmDlg::type_showOptions(bool more) 0940 { 0941 if (more) 0942 mCmdOutputBox->show(); 0943 else 0944 mCmdOutputBox->hide(); 0945 } 0946 0947 /****************************************************************************** 0948 * Set the dialog's action and the action's text. 0949 */ 0950 void EditCommandAlarmDlg::setAction(KAEvent::SubAction action, const AlarmText& alarmText) 0951 { 0952 Q_UNUSED(action); 0953 Q_ASSERT(action == KAEvent::SubAction::Command); 0954 mCmdEdit->setText(alarmText); 0955 } 0956 0957 /****************************************************************************** 0958 * Set the read-only status of all non-template controls. 0959 */ 0960 void EditCommandAlarmDlg::setReadOnly(bool readOnly) 0961 { 0962 if (!isTemplate() && !ShellProcess::authorised()) 0963 readOnly = true; // don't allow editing of existing command alarms in kiosk mode 0964 mCmdEdit->setReadOnly(readOnly); 0965 mCmdDontShowError->setReadOnly(readOnly); 0966 mCmdExecInTerm->setReadOnly(readOnly); 0967 mCmdLogToFile->setReadOnly(readOnly); 0968 mCmdDiscardOutput->setReadOnly(readOnly); 0969 EditAlarmDlg::setReadOnly(readOnly); 0970 } 0971 0972 /****************************************************************************** 0973 * Save the state of all controls. 0974 */ 0975 void EditCommandAlarmDlg::saveState(const KAEvent* event) 0976 { 0977 EditAlarmDlg::saveState(event); 0978 mSavedCmdScript = mCmdEdit->isScript(); 0979 mSavedCmdDontShowError = mCmdDontShowError->isChecked(); 0980 mSavedCmdOutputRadio = mCmdOutputGroup->checkedButton(); 0981 mSavedCmdLogFile = mCmdLogFileEdit->text(); 0982 } 0983 0984 /****************************************************************************** 0985 * Check whether any of the controls has changed state since the dialog was 0986 * first displayed. 0987 * Reply = true if any controls have changed, or if it's a new event. 0988 * = false if no controls have changed. 0989 */ 0990 bool EditCommandAlarmDlg::type_stateChanged() const 0991 { 0992 if (mSavedCmdScript != mCmdEdit->isScript() 0993 || mSavedCmdOutputRadio != mCmdOutputGroup->checkedButton() 0994 || mSavedCmdDontShowError != mCmdDontShowError->isChecked()) 0995 return true; 0996 if (mCmdOutputGroup->checkedButton() == mCmdLogToFile) 0997 { 0998 if (mSavedCmdLogFile != mCmdLogFileEdit->text()) 0999 return true; 1000 } 1001 return false; 1002 } 1003 1004 /****************************************************************************** 1005 * Extract the data in the dialog specific to the alarm type and set up a 1006 * KAEvent from it. 1007 */ 1008 void EditCommandAlarmDlg::type_setEvent(KAEvent& event, const KADateTime& dt, const QString& name, const QString& text, int lateCancel, bool trial) 1009 { 1010 Q_UNUSED(trial); 1011 event = KAEvent(dt, name, text, QColor(), QColor(), QFont(), KAEvent::SubAction::Command, lateCancel, getAlarmFlags()); 1012 if (mCmdOutputGroup->checkedButton() == mCmdLogToFile) 1013 event.setLogFile(mCmdLogFileEdit->text()); 1014 } 1015 1016 /****************************************************************************** 1017 * Get the currently specified alarm flag bits. 1018 */ 1019 KAEvent::Flags EditCommandAlarmDlg::getAlarmFlags() const 1020 { 1021 KAEvent::Flags flags = EditAlarmDlg::getAlarmFlags(); 1022 if (mCmdEdit->isScript()) flags |= KAEvent::SCRIPT; 1023 if (mCmdOutputGroup->checkedButton() == mCmdExecInTerm) flags |= KAEvent::EXEC_IN_XTERM; 1024 if (mCmdDontShowError->isChecked()) flags |= KAEvent::DONT_SHOW_ERROR; 1025 return flags; 1026 } 1027 1028 /****************************************************************************** 1029 * Validate and convert command alarm data. 1030 */ 1031 bool EditCommandAlarmDlg::type_validate(bool trial) 1032 { 1033 Q_UNUSED(trial); 1034 if (mCmdOutputGroup->checkedButton() == mCmdLogToFile) 1035 { 1036 // Validate the log file name 1037 const QString file = mCmdLogFileEdit->text(); 1038 const QFileInfo info(file); 1039 QDir::setCurrent(QDir::homePath()); 1040 bool err = file.isEmpty() || info.isDir(); 1041 if (!err) 1042 { 1043 if (info.exists()) 1044 { 1045 err = !info.isWritable(); 1046 } 1047 else 1048 { 1049 const QFileInfo dirinfo(info.absolutePath()); // get absolute directory path 1050 err = (!dirinfo.isDir() || !dirinfo.isWritable()); 1051 } 1052 } 1053 if (err) 1054 { 1055 showMainPage(); 1056 mCmdLogFileEdit->setFocus(); 1057 KAMessageBox::error(this, i18nc("@info", "Log file must be the name or path of a local file, with write permission.")); 1058 return false; 1059 } 1060 // Convert the log file to an absolute path 1061 mCmdLogFileEdit->setText(info.absoluteFilePath()); 1062 } 1063 else if (mCmdOutputGroup->checkedButton() == mCmdExecInTerm) 1064 { 1065 if (Preferences::cmdXTermCommand().isEmpty()) 1066 { 1067 if (KAMessageBox::warningContinueCancel(this, xi18nc("@info", "<para>No terminal is selected for command alarms.</para>" 1068 "<para>Please set it in the <application>KAlarm</application> Configuration dialog.</para>")) 1069 != KMessageBox::Continue) 1070 return false; 1071 } 1072 } 1073 return true; 1074 } 1075 1076 /****************************************************************************** 1077 * Called when the Try action has been executed. 1078 * Tell the user the result of the Try action. 1079 */ 1080 void EditCommandAlarmDlg::type_executedTry(const QString& text, void* result) 1081 { 1082 auto* proc = (ShellProcess*)result; 1083 if (proc && proc != (void*)-1 1084 && mCmdOutputGroup->checkedButton() != mCmdExecInTerm) 1085 { 1086 theApp()->commandMessage(proc, this); 1087 KAMessageBox::information(this, xi18nc("@info", "Command executed: <icode>%1</icode>", text)); 1088 theApp()->commandMessage(proc, nullptr); 1089 } 1090 } 1091 1092 /****************************************************************************** 1093 * Called when one of the command type radio buttons is clicked, 1094 * to display the appropriate edit field. 1095 */ 1096 void EditCommandAlarmDlg::slotCmdScriptToggled(bool on) 1097 { 1098 if (on) 1099 mCmdPadding->hide(); 1100 else 1101 mCmdPadding->show(); 1102 } 1103 1104 /****************************************************************************** 1105 * Clean up the alarm text. 1106 */ 1107 bool EditCommandAlarmDlg::checkText(QString& result, bool showErrorMessage) const 1108 { 1109 result = mCmdEdit->text(const_cast<EditCommandAlarmDlg*>(this), showErrorMessage); 1110 if (result.isEmpty()) 1111 return false; 1112 return true; 1113 } 1114 1115 1116 /*============================================================================= 1117 = Class EditEmailAlarmDlg 1118 = Dialog to edit email alarms. 1119 =============================================================================*/ 1120 1121 QString EditEmailAlarmDlg::i18n_chk_CopyEmailToSelf() { return i18nc("@option:check", "Copy email to self"); } 1122 1123 1124 /****************************************************************************** 1125 * Constructor. 1126 * Parameters: 1127 * Template = true to edit/create an alarm template 1128 * = false to edit/create an alarm. 1129 * event != to initialise the dialog to show the specified event's data. 1130 */ 1131 EditEmailAlarmDlg::EditEmailAlarmDlg(bool Template, QWidget* parent, GetResourceType getResource) 1132 : EditAlarmDlg(Template, KAEvent::SubAction::Email, parent, getResource) 1133 { 1134 qCDebug(KALARM_LOG) << "EditEmailAlarmDlg: New"; 1135 init(KAEvent()); 1136 } 1137 1138 EditEmailAlarmDlg::EditEmailAlarmDlg(bool Template, const KAEvent& event, bool newAlarm, QWidget* parent, 1139 GetResourceType getResource, bool readOnly) 1140 : EditAlarmDlg(Template, event, newAlarm, parent, getResource, readOnly) 1141 { 1142 qCDebug(KALARM_LOG) << "EditEmailAlarmDlg: Event.id()"; 1143 init(event); 1144 } 1145 1146 /****************************************************************************** 1147 * Return the window caption. 1148 */ 1149 QString EditEmailAlarmDlg::type_caption() const 1150 { 1151 return isTemplate() ? (isNewAlarm() ? i18nc("@title:window", "New Email Alarm Template") : i18nc("@title:window", "Edit Email Alarm Template")) 1152 : (isNewAlarm() ? i18nc("@title:window", "New Email Alarm") : i18nc("@title:window", "Edit Email Alarm")); 1153 } 1154 1155 /****************************************************************************** 1156 * Set up the email alarm dialog controls. 1157 */ 1158 void EditEmailAlarmDlg::type_init(QWidget* parent, QVBoxLayout* frameLayout) 1159 { 1160 mTryButton->setWhatsThis(i18nc("@info:whatsthis", "Send the email to the specified addressees now")); 1161 mTryButton->setToolTip(i18nc("@info:tooltip", "Send the email now")); 1162 1163 auto grid = new QGridLayout(); 1164 grid->setContentsMargins(0, 0, 0, 0); 1165 grid->setColumnStretch(1, 1); 1166 frameLayout->addLayout(grid); 1167 1168 mEmailFromList = nullptr; 1169 if (Preferences::emailFrom() == Preferences::MAIL_FROM_KMAIL) 1170 { 1171 // Email sender identity 1172 QLabel* label = new QLabel(i18nc("@label:listbox 'From' email address", "From:"), parent); 1173 grid->addWidget(label, 0, 0); 1174 1175 mEmailFromList = new EmailIdCombo(Identities::identityManager(), parent); 1176 mEmailFromList->setMinimumSize(mEmailFromList->sizeHint()); 1177 label->setBuddy(mEmailFromList); 1178 mEmailFromList->setWhatsThis(i18nc("@info:whatsthis", "Your email identity, used to identify you as the sender when sending email alarms.")); 1179 connect(mEmailFromList, &EmailIdCombo::identityChanged, this, &EditEmailAlarmDlg::contentsChanged); 1180 grid->addWidget(mEmailFromList, 0, 1, 1, 2); 1181 } 1182 1183 // Email recipients 1184 QLabel* label = new QLabel(i18nc("@label:textbox Email addressee", "To:"), parent); 1185 grid->addWidget(label, 1, 0); 1186 1187 mEmailToEdit = new LineEdit(LineEdit::Type::Emails, parent); 1188 mEmailToEdit->setMinimumSize(mEmailToEdit->sizeHint()); 1189 mEmailToEdit->setWhatsThis(i18nc("@info:whatsthis", "Enter the addresses of the email recipients. Separate multiple addresses by " 1190 "commas or semicolons.")); 1191 connect(mEmailToEdit, &LineEdit::textChanged, this, &EditEmailAlarmDlg::contentsChanged); 1192 1193 if (Preferences::useAkonadi()) 1194 { 1195 grid->addWidget(mEmailToEdit, 1, 1); 1196 1197 mEmailAddressButton = new QPushButton(parent); 1198 mEmailAddressButton->setIcon(QIcon::fromTheme(QStringLiteral("help-contents"))); 1199 connect(mEmailAddressButton, &QPushButton::clicked, this, &EditEmailAlarmDlg::openAddressBook); 1200 mEmailAddressButton->setToolTip(i18nc("@info:tooltip", "Open address book")); 1201 mEmailAddressButton->setWhatsThis(i18nc("@info:whatsthis", "Select email addresses from your address book.")); 1202 grid->addWidget(mEmailAddressButton, 1, 2); 1203 } 1204 else 1205 grid->addWidget(mEmailToEdit, 1, 1, 1, 2); 1206 1207 // Email subject 1208 label = new QLabel(i18nc("@label:textbox Email subject", "Subject:"), parent); 1209 grid->addWidget(label, 2, 0); 1210 1211 mEmailSubjectEdit = new LineEdit(parent); 1212 mEmailSubjectEdit->setMinimumSize(mEmailSubjectEdit->sizeHint()); 1213 label->setBuddy(mEmailSubjectEdit); 1214 mEmailSubjectEdit->setWhatsThis(i18nc("@info:whatsthis", "Enter the email subject.")); 1215 connect(mEmailSubjectEdit, &LineEdit::textChanged, this, &EditEmailAlarmDlg::contentsChanged); 1216 grid->addWidget(mEmailSubjectEdit, 2, 1, 1, 2); 1217 1218 // Email body 1219 mEmailMessageEdit = new TextEdit(parent); 1220 mEmailMessageEdit->setWhatsThis(i18nc("@info:whatsthis", "Enter the email message.")); 1221 connect(mEmailMessageEdit, &TextEdit::textChanged, this, &EditEmailAlarmDlg::contentsChanged); 1222 frameLayout->addWidget(mEmailMessageEdit); 1223 1224 // Email attachments 1225 grid = new QGridLayout(); 1226 grid->setContentsMargins(0, 0, 0, 0); 1227 frameLayout->addLayout(grid); 1228 label = new QLabel(i18nc("@label:listbox", "Attachments:"), parent); 1229 grid->addWidget(label, 0, 0); 1230 1231 mEmailAttachList = new QComboBox(parent); 1232 mEmailAttachList->setEditable(true); 1233 mEmailAttachList->setMinimumSize(mEmailAttachList->sizeHint()); 1234 if (mEmailAttachList->lineEdit()) 1235 mEmailAttachList->lineEdit()->setReadOnly(true); 1236 label->setBuddy(mEmailAttachList); 1237 mEmailAttachList->setWhatsThis(i18nc("@info:whatsthis", "Files to send as attachments to the email.")); 1238 grid->addWidget(mEmailAttachList, 0, 1); 1239 grid->setColumnStretch(1, 1); 1240 1241 mEmailAddAttachButton = new QPushButton(i18nc("@action:button", "Add..."), parent); 1242 connect(mEmailAddAttachButton, &QPushButton::clicked, this, &EditEmailAlarmDlg::slotAddAttachment); 1243 mEmailAddAttachButton->setWhatsThis(i18nc("@info:whatsthis", "Add an attachment to the email.")); 1244 grid->addWidget(mEmailAddAttachButton, 0, 2); 1245 1246 mEmailRemoveButton = new QPushButton(i18nc("@action:button", "Remove"), parent); 1247 connect(mEmailRemoveButton, &QPushButton::clicked, this, &EditEmailAlarmDlg::slotRemoveAttachment); 1248 mEmailRemoveButton->setWhatsThis(i18nc("@info:whatsthis", "Remove the highlighted attachment from the email.")); 1249 grid->addWidget(mEmailRemoveButton, 1, 2); 1250 1251 // BCC email to sender 1252 mEmailBcc = new CheckBox(i18n_chk_CopyEmailToSelf(), parent); 1253 mEmailBcc->setWhatsThis(i18nc("@info:whatsthis", "If checked, the email will be blind copied to you.")); 1254 connect(mEmailBcc, &CheckBox::toggled, this, &EditEmailAlarmDlg::contentsChanged); 1255 grid->addWidget(mEmailBcc, 1, 0, 1, 2, Qt::AlignLeft); 1256 } 1257 1258 /****************************************************************************** 1259 * Initialise the dialog controls from the specified event. 1260 */ 1261 void EditEmailAlarmDlg::type_initValues(const KAEvent& event) 1262 { 1263 if (event.isValid()) 1264 { 1265 // Set the values to those for the specified event 1266 mEmailAttachList->addItems(event.emailAttachments()); 1267 mEmailToEdit->setText(event.emailAddresses(QStringLiteral(", "))); 1268 mEmailSubjectEdit->setText(event.emailSubject()); 1269 mEmailBcc->setChecked(event.emailBcc()); 1270 if (mEmailFromList) 1271 mEmailFromList->setCurrentIdentity(event.emailFromId()); 1272 } 1273 else 1274 { 1275 // Set the values to their defaults 1276 mEmailBcc->setChecked(Preferences::defaultEmailBcc()); 1277 } 1278 attachmentEnable(); 1279 } 1280 1281 /****************************************************************************** 1282 * Enable/disable controls depending on whether any attachments are entered. 1283 */ 1284 void EditEmailAlarmDlg::attachmentEnable() 1285 { 1286 const bool enable = mEmailAttachList->count(); 1287 mEmailAttachList->setEnabled(enable); 1288 if (mEmailRemoveButton) 1289 mEmailRemoveButton->setEnabled(enable); 1290 } 1291 1292 /****************************************************************************** 1293 * Set the dialog's action and the action's text. 1294 */ 1295 void EditEmailAlarmDlg::setAction(KAEvent::SubAction action, const AlarmText& alarmText) 1296 { 1297 Q_UNUSED(action); 1298 Q_ASSERT(action == KAEvent::SubAction::Email); 1299 if (alarmText.isEmail()) 1300 { 1301 mEmailToEdit->setText(alarmText.to()); 1302 mEmailSubjectEdit->setText(alarmText.subject()); 1303 mEmailMessageEdit->setPlainText(alarmText.body()); 1304 } 1305 else 1306 mEmailMessageEdit->setPlainText(alarmText.displayText()); 1307 } 1308 1309 /****************************************************************************** 1310 * Initialise various values in the New Alarm dialogue. 1311 */ 1312 void EditEmailAlarmDlg::setEmailFields(uint fromID, const KCalendarCore::Person::List& addresses, 1313 const QString& subject, const QStringList& attachments) 1314 { 1315 if (fromID && mEmailFromList) 1316 mEmailFromList->setCurrentIdentity(fromID); 1317 if (!addresses.isEmpty()) 1318 mEmailToEdit->setText(KAEvent::joinEmailAddresses(addresses, QStringLiteral(", "))); 1319 if (!subject.isEmpty()) 1320 mEmailSubjectEdit->setText(subject); 1321 if (!attachments.isEmpty()) 1322 { 1323 mEmailAttachList->addItems(attachments); 1324 attachmentEnable(); 1325 } 1326 } 1327 void EditEmailAlarmDlg::setBcc(bool bcc) 1328 { 1329 mEmailBcc->setChecked(bcc); 1330 } 1331 1332 /****************************************************************************** 1333 * Set the read-only status of all non-template controls. 1334 */ 1335 void EditEmailAlarmDlg::setReadOnly(bool readOnly) 1336 { 1337 mEmailToEdit->setReadOnly(readOnly); 1338 mEmailSubjectEdit->setReadOnly(readOnly); 1339 mEmailMessageEdit->setReadOnly(readOnly); 1340 mEmailBcc->setReadOnly(readOnly); 1341 if (mEmailFromList) 1342 mEmailFromList->setReadOnly(readOnly); 1343 if (readOnly) 1344 { 1345 if (mEmailAddressButton) 1346 mEmailAddressButton->hide(); 1347 mEmailAddAttachButton->hide(); 1348 mEmailRemoveButton->hide(); 1349 } 1350 else 1351 { 1352 if (mEmailAddressButton) 1353 mEmailAddressButton->show(); 1354 mEmailAddAttachButton->show(); 1355 mEmailRemoveButton->show(); 1356 } 1357 EditAlarmDlg::setReadOnly(readOnly); 1358 } 1359 1360 /****************************************************************************** 1361 * Save the state of all controls. 1362 */ 1363 void EditEmailAlarmDlg::saveState(const KAEvent* event) 1364 { 1365 EditAlarmDlg::saveState(event); 1366 if (mEmailFromList) 1367 mSavedEmailFrom = mEmailFromList->currentIdentityName(); 1368 mSavedEmailTo = mEmailToEdit->text(); 1369 mSavedEmailSubject = mEmailSubjectEdit->text(); 1370 mSavedEmailAttach.clear(); 1371 for (int i = 0, end = mEmailAttachList->count(); i < end; ++i) 1372 mSavedEmailAttach += mEmailAttachList->itemText(i); 1373 mSavedEmailBcc = mEmailBcc->isChecked(); 1374 } 1375 1376 /****************************************************************************** 1377 * Check whether any of the controls has changed state since the dialog was 1378 * first displayed. 1379 * Reply = true if any controls have changed, or if it's a new event. 1380 * = false if no controls have changed. 1381 */ 1382 bool EditEmailAlarmDlg::type_stateChanged() const 1383 { 1384 int count = mEmailAttachList->count(); 1385 QStringList emailAttach; 1386 emailAttach.reserve(count); 1387 for (int i = 0; i < count; ++i) 1388 emailAttach += mEmailAttachList->itemText(i); 1389 if ((mEmailFromList && mSavedEmailFrom != mEmailFromList->currentIdentityName()) 1390 || mSavedEmailTo != mEmailToEdit->text() 1391 || mSavedEmailSubject != mEmailSubjectEdit->text() 1392 || mSavedEmailAttach != emailAttach 1393 || mSavedEmailBcc != mEmailBcc->isChecked()) 1394 return true; 1395 return false; 1396 } 1397 1398 /****************************************************************************** 1399 * Extract the data in the dialog specific to the alarm type and set up a 1400 * KAEvent from it. 1401 */ 1402 void EditEmailAlarmDlg::type_setEvent(KAEvent& event, const KADateTime& dt, const QString& name, const QString& text, int lateCancel, bool trial) 1403 { 1404 Q_UNUSED(trial); 1405 event = KAEvent(dt, name, text, QColor(), QColor(), QFont(), KAEvent::SubAction::Email, lateCancel, getAlarmFlags()); 1406 const uint from = mEmailFromList ? mEmailFromList->currentIdentity() : 0; 1407 event.setEmail(from, mEmailAddresses, mEmailSubjectEdit->text(), mEmailAttachments); 1408 } 1409 1410 /****************************************************************************** 1411 * Get the currently specified alarm flag bits. 1412 */ 1413 KAEvent::Flags EditEmailAlarmDlg::getAlarmFlags() const 1414 { 1415 KAEvent::Flags flags = EditAlarmDlg::getAlarmFlags(); 1416 if (mEmailBcc->isChecked()) flags |= KAEvent::EMAIL_BCC; 1417 return flags; 1418 } 1419 1420 /****************************************************************************** 1421 * Convert the email addresses to a list, and validate them. Convert the email 1422 * attachments to a list. 1423 */ 1424 bool EditEmailAlarmDlg::type_validate(bool trial) 1425 { 1426 const QString addrs = mEmailToEdit->text(); 1427 if (addrs.isEmpty()) 1428 mEmailAddresses.clear(); 1429 else 1430 { 1431 const QString bad = KAMail::convertAddresses(addrs, mEmailAddresses); 1432 if (!bad.isEmpty()) 1433 { 1434 mEmailToEdit->setFocus(); 1435 KAMessageBox::error(this, xi18nc("@info", "Invalid email address: <email>%1</email>", bad)); 1436 return false; 1437 } 1438 } 1439 if (mEmailAddresses.isEmpty()) 1440 { 1441 mEmailToEdit->setFocus(); 1442 KAMessageBox::error(this, i18nc("@info", "No email address specified")); 1443 return false; 1444 } 1445 1446 mEmailAttachments.clear(); 1447 for (int i = 0, end = mEmailAttachList->count(); i < end; ++i) 1448 { 1449 QString att = mEmailAttachList->itemText(i); 1450 switch (KAMail::checkAttachment(att)) 1451 { 1452 case 1: 1453 mEmailAttachments.append(att); 1454 break; 1455 case 0: 1456 break; // empty 1457 case -1: 1458 mEmailAttachList->setFocus(); 1459 KAMessageBox::error(this, xi18nc("@info", "Invalid email attachment: <filename>%1</filename>", att)); 1460 return false; 1461 } 1462 } 1463 if (trial && KAMessageBox::warningContinueCancel(this, i18nc("@info", "Do you really want to send the email now to the specified recipient(s)?"), 1464 i18nc("@action:button", "Confirm Email"), KGuiItem(i18nc("@action:button", "Send"))) != KMessageBox::Continue) 1465 return false; 1466 return true; 1467 } 1468 1469 /****************************************************************************** 1470 * Called when the Try action is about to be executed. 1471 */ 1472 void EditEmailAlarmDlg::type_aboutToTry() 1473 { 1474 // Disconnect any previous connections, to prevent multiple messages being output 1475 disconnect(theApp(), &KAlarmApp::execAlarmSuccess, this, &EditEmailAlarmDlg::slotTrySuccess); 1476 connect(theApp(), &KAlarmApp::execAlarmSuccess, this, &EditEmailAlarmDlg::slotTrySuccess); 1477 } 1478 1479 /****************************************************************************** 1480 * Tell the user the result of the Try action. 1481 */ 1482 void EditEmailAlarmDlg::slotTrySuccess() 1483 { 1484 disconnect(theApp(), &KAlarmApp::execAlarmSuccess, this, &EditEmailAlarmDlg::slotTrySuccess); 1485 QString msg; 1486 QString to = KAEvent::joinEmailAddresses(mEmailAddresses, QStringLiteral("<nl/>")); 1487 to.replace(QLatin1Char('<'), QStringLiteral("<")); 1488 to.replace(QLatin1Char('>'), QStringLiteral(">")); 1489 if (mEmailBcc->isChecked()) 1490 msg = QLatin1String("<qt>") + xi18nc("@info", "Email sent to:<nl/>%1<nl/>Bcc: <email>%2</email>", 1491 to, Preferences::emailBccAddress()) + QLatin1String("</qt>"); 1492 else 1493 msg = QLatin1String("<qt>") + xi18nc("@info", "Email sent to:<nl/>%1", to) + QLatin1String("</qt>"); 1494 KAMessageBox::information(this, msg); 1495 } 1496 1497 /****************************************************************************** 1498 * Get a selection from the Address Book. 1499 */ 1500 void EditEmailAlarmDlg::openAddressBook() 1501 { 1502 AkonadiPlugin* akonadiPlugin = Preferences::akonadiPlugin(); 1503 if (akonadiPlugin) 1504 { 1505 Person person; 1506 if (!akonadiPlugin->getAddressBookSelection(person, this)) 1507 return; 1508 QString addrs = mEmailToEdit->text().trimmed(); 1509 if (!addrs.isEmpty()) 1510 addrs += QLatin1String(", "); 1511 addrs += person.fullName(); 1512 mEmailToEdit->setText(addrs); 1513 } 1514 } 1515 1516 /****************************************************************************** 1517 * Select a file to attach to the email. 1518 */ 1519 void EditEmailAlarmDlg::slotAddAttachment() 1520 { 1521 QString file; 1522 if (File::browseFile(file, i18nc("@title:window", "Choose File to Attach"), 1523 mAttachDefaultDir, QString(), true, this)) 1524 { 1525 if (!file.isEmpty()) 1526 { 1527 mEmailAttachList->addItem(file); 1528 mEmailAttachList->setCurrentIndex(mEmailAttachList->count() - 1); // select the new item 1529 mEmailRemoveButton->setEnabled(true); 1530 mEmailAttachList->setEnabled(true); 1531 contentsChanged(); 1532 } 1533 } 1534 } 1535 1536 /****************************************************************************** 1537 * Remove the currently selected attachment from the email. 1538 */ 1539 void EditEmailAlarmDlg::slotRemoveAttachment() 1540 { 1541 const int item = mEmailAttachList->currentIndex(); 1542 mEmailAttachList->removeItem(item); 1543 const int count = mEmailAttachList->count(); 1544 if (item >= count) 1545 mEmailAttachList->setCurrentIndex(count - 1); 1546 if (!count) 1547 { 1548 mEmailRemoveButton->setEnabled(false); 1549 mEmailAttachList->setEnabled(false); 1550 } 1551 contentsChanged(); 1552 } 1553 1554 /****************************************************************************** 1555 * Clean up the alarm text. 1556 */ 1557 bool EditEmailAlarmDlg::checkText(QString& result, bool showErrorMessage) const 1558 { 1559 Q_UNUSED(showErrorMessage); 1560 result = mEmailMessageEdit->toPlainText(); 1561 return true; 1562 } 1563 1564 1565 /*============================================================================= 1566 = Class EditAudioAlarmDlg 1567 = Dialog to edit audio alarms with no display window. 1568 =============================================================================*/ 1569 1570 /****************************************************************************** 1571 * Constructor. 1572 * Parameters: 1573 * Template = true to edit/create an alarm template 1574 * = false to edit/create an alarm. 1575 * event != to initialise the dialog to show the specified event's data. 1576 */ 1577 EditAudioAlarmDlg::EditAudioAlarmDlg(bool Template, QWidget* parent, GetResourceType getResource) 1578 : EditAlarmDlg(Template, KAEvent::SubAction::Audio, parent, getResource) 1579 { 1580 qCDebug(KALARM_LOG) << "EditAudioAlarmDlg: New"; 1581 init(KAEvent()); 1582 } 1583 1584 EditAudioAlarmDlg::EditAudioAlarmDlg(bool Template, const KAEvent& event, bool newAlarm, QWidget* parent, 1585 GetResourceType getResource, bool readOnly) 1586 : EditAlarmDlg(Template, event, newAlarm, parent, getResource, readOnly) 1587 { 1588 qCDebug(KALARM_LOG) << "EditAudioAlarmDlg: Event.id()"; 1589 init(event); 1590 mTryButton->setEnabled(!MessageDisplay::isAudioPlaying()); 1591 connect(theApp(), &KAlarmApp::audioPlaying, this, &EditAudioAlarmDlg::slotAudioPlaying); 1592 } 1593 1594 EditAudioAlarmDlg::~EditAudioAlarmDlg() 1595 { 1596 if (mMessageWindow) 1597 MessageDisplay::stopAudio(); 1598 } 1599 1600 /****************************************************************************** 1601 * Return the window caption. 1602 */ 1603 QString EditAudioAlarmDlg::type_caption() const 1604 { 1605 return isTemplate() ? (isNewAlarm() ? i18nc("@title:window", "New Audio Alarm Template") : i18nc("@title:window", "Edit Audio Alarm Template")) 1606 : (isNewAlarm() ? i18nc("@title:window", "New Audio Alarm") : i18nc("@title:window", "Edit Audio Alarm")); 1607 } 1608 1609 /****************************************************************************** 1610 * Set up the dialog controls common to display alarms. 1611 */ 1612 void EditAudioAlarmDlg::type_init(QWidget* parent, QVBoxLayout* frameLayout) 1613 { 1614 mTryButton->setWhatsThis(i18nc("@info:whatsthis", "Play the audio file now")); 1615 mTryButton->setToolTip(i18nc("@info:tooltip", "Play the audio file now")); 1616 // File name edit box 1617 const QString repWhatsThis = i18nc("@info:whatsthis", "If checked, the sound file will be played repeatedly until %1 is clicked.", KAlarm::i18n_act_StopPlay()); 1618 mSoundConfig = new SoundWidget(false, repWhatsThis, parent); 1619 if (isTemplate()) 1620 mSoundConfig->setAllowEmptyFile(); 1621 connect(mSoundConfig, &SoundWidget::changed, this, &EditAudioAlarmDlg::contentsChanged); 1622 frameLayout->addWidget(mSoundConfig); 1623 1624 // Top-adjust the controls 1625 mPadding = new QWidget(parent); 1626 auto hlayout = new QHBoxLayout(mPadding); 1627 hlayout->setContentsMargins(0, 0, 0, 0); 1628 hlayout->setSpacing(0); 1629 frameLayout->addWidget(mPadding); 1630 frameLayout->setStretchFactor(mPadding, 1); 1631 } 1632 1633 /****************************************************************************** 1634 * Initialise the dialog controls from the specified event. 1635 */ 1636 void EditAudioAlarmDlg::type_initValues(const KAEvent& event) 1637 { 1638 if (event.isValid()) 1639 { 1640 mSoundConfig->set(event.audioFile(), event.soundVolume(), event.fadeVolume(), event.fadeSeconds(), 1641 (event.flags() & KAEvent::REPEAT_SOUND) ? event.repeatSoundPause() : -1); 1642 } 1643 else 1644 { 1645 // Set the values to their defaults 1646 mSoundConfig->set(Preferences::defaultSoundFile(), Preferences::defaultSoundVolume(), 1647 -1, 0, (Preferences::defaultSoundRepeat() ? 0 : -1)); 1648 } 1649 } 1650 1651 /****************************************************************************** 1652 * Initialise various values in the New Alarm dialogue. 1653 */ 1654 void EditAudioAlarmDlg::setAudio(const QString& file, float volume) 1655 { 1656 mSoundConfig->set(file, volume); 1657 } 1658 1659 /****************************************************************************** 1660 * Set the dialog's action and the action's text. 1661 */ 1662 void EditAudioAlarmDlg::setAction(KAEvent::SubAction action, const AlarmText& alarmText) 1663 { 1664 Q_UNUSED(action); 1665 Q_ASSERT(action == KAEvent::SubAction::Audio); 1666 mSoundConfig->set(alarmText.displayText(), Preferences::defaultSoundVolume()); 1667 } 1668 1669 /****************************************************************************** 1670 * Set the read-only status of all non-template controls. 1671 */ 1672 void EditAudioAlarmDlg::setReadOnly(bool readOnly) 1673 { 1674 mSoundConfig->setReadOnly(readOnly); 1675 EditAlarmDlg::setReadOnly(readOnly); 1676 } 1677 1678 /****************************************************************************** 1679 * Save the state of all controls. 1680 */ 1681 void EditAudioAlarmDlg::saveState(const KAEvent* event) 1682 { 1683 EditAlarmDlg::saveState(event); 1684 mSavedFile = mSoundConfig->fileName(); 1685 mSoundConfig->getVolume(mSavedVolume, mSavedFadeVolume, mSavedFadeSeconds); 1686 mSavedRepeatPause = mSoundConfig->repeatPause(); 1687 } 1688 1689 /****************************************************************************** 1690 * Check whether any of the controls has changed state since the dialog was 1691 * first displayed. 1692 * Reply = true if any controls have changed, or if it's a new event. 1693 * = false if no controls have changed. 1694 */ 1695 bool EditAudioAlarmDlg::type_stateChanged() const 1696 { 1697 if (mSavedFile != mSoundConfig->fileName()) 1698 return true; 1699 if (!mSavedFile.isEmpty() || isTemplate()) 1700 { 1701 float volume, fadeVolume; 1702 int fadeSecs; 1703 mSoundConfig->getVolume(volume, fadeVolume, fadeSecs); 1704 if (mSavedRepeatPause != mSoundConfig->repeatPause() 1705 || mSavedVolume != volume 1706 || mSavedFadeVolume != fadeVolume 1707 || mSavedFadeSeconds != fadeSecs) 1708 return true; 1709 } 1710 return false; 1711 } 1712 1713 /****************************************************************************** 1714 * Extract the data in the dialog specific to the alarm type and set up a 1715 * KAEvent from it. 1716 */ 1717 void EditAudioAlarmDlg::type_setEvent(KAEvent& event, const KADateTime& dt, const QString& name, const QString& text, int lateCancel, bool trial) 1718 { 1719 Q_UNUSED(text); 1720 Q_UNUSED(trial); 1721 event = KAEvent(dt, name, QString(), QColor(), QColor(), QFont(), KAEvent::SubAction::Audio, lateCancel, getAlarmFlags()); 1722 float volume, fadeVolume; 1723 int fadeSecs; 1724 mSoundConfig->getVolume(volume, fadeVolume, fadeSecs); 1725 const int repeatPause = mSoundConfig->repeatPause(); 1726 QUrl url; 1727 mSoundConfig->file(url, false); 1728 event.setAudioFile(url.toString(), volume, fadeVolume, fadeSecs, repeatPause, isTemplate()); 1729 } 1730 1731 /****************************************************************************** 1732 * Get the currently specified alarm flag bits. 1733 */ 1734 KAEvent::Flags EditAudioAlarmDlg::getAlarmFlags() const 1735 { 1736 KAEvent::Flags flags = EditAlarmDlg::getAlarmFlags(); 1737 if (mSoundConfig->repeatPause() >= 0) flags |= KAEvent::REPEAT_SOUND; 1738 return flags; 1739 } 1740 1741 /****************************************************************************** 1742 * Check whether the file name is valid. 1743 */ 1744 bool EditAudioAlarmDlg::checkText(QString& result, bool showErrorMessage) const 1745 { 1746 QUrl url; 1747 if (!mSoundConfig->file(url, showErrorMessage)) 1748 { 1749 result.clear(); 1750 return false; 1751 } 1752 result = url.isLocalFile() ? url.toLocalFile() : url.toString(); 1753 return true; 1754 } 1755 1756 /****************************************************************************** 1757 * Called when the Try button is clicked. 1758 * If the audio file is currently playing (as a result of previously clicking 1759 * the Try button), cancel playback. Otherwise, play the audio file. 1760 */ 1761 void EditAudioAlarmDlg::slotTry() 1762 { 1763 if (!MessageDisplay::isAudioPlaying()) 1764 EditAlarmDlg::slotTry(); // play the audio file 1765 else if (mMessageWindow) 1766 { 1767 MessageDisplay::stopAudio(); 1768 mMessageWindow = nullptr; 1769 } 1770 } 1771 1772 /****************************************************************************** 1773 * Called when the Try action has been executed. 1774 */ 1775 void EditAudioAlarmDlg::type_executedTry(const QString&, void* result) 1776 { 1777 mMessageWindow = (MessageWindow*)result; // note which MessageWindow controls the audio playback 1778 if (mMessageWindow) 1779 { 1780 slotAudioPlaying(true); 1781 connect(mMessageWindow, &QObject::destroyed, this, &EditAudioAlarmDlg::audioWinDestroyed); 1782 } 1783 } 1784 1785 /****************************************************************************** 1786 * Called when audio playing starts or stops. 1787 * Enable/disable/toggle the Try button. 1788 */ 1789 void EditAudioAlarmDlg::slotAudioPlaying(bool playing) 1790 { 1791 if (!playing) 1792 { 1793 // Nothing is playing, so enable the Try button 1794 mTryButton->setEnabled(true); 1795 mTryButton->setCheckable(false); 1796 mTryButton->setChecked(false); 1797 mMessageWindow = nullptr; 1798 } 1799 else if (mMessageWindow) 1800 { 1801 // The test sound file is playing, so enable the Try button and depress it 1802 mTryButton->setEnabled(true); 1803 mTryButton->setCheckable(true); 1804 mTryButton->setChecked(true); 1805 } 1806 else 1807 { 1808 // An alarm is playing, so disable the Try button 1809 mTryButton->setEnabled(false); 1810 mTryButton->setCheckable(false); 1811 mTryButton->setChecked(false); 1812 } 1813 } 1814 1815 1816 /*============================================================================= 1817 = Class CommandEdit 1818 = A widget to allow entry of a command or a command script. 1819 =============================================================================*/ 1820 CommandEdit::CommandEdit(QWidget* parent) 1821 : QWidget(parent) 1822 { 1823 auto vlayout = new QVBoxLayout(this); 1824 vlayout->setContentsMargins(0, 0, 0, 0); 1825 mTypeScript = new CheckBox(EditCommandAlarmDlg::i18n_chk_EnterScript(), this); 1826 mTypeScript->setWhatsThis(i18nc("@info:whatsthis", "Check to enter the contents of a script instead of a shell command line")); 1827 connect(mTypeScript, &CheckBox::toggled, this, &CommandEdit::slotCmdScriptToggled); 1828 connect(mTypeScript, &CheckBox::toggled, this, &CommandEdit::changed); 1829 vlayout->addWidget(mTypeScript, 0, Qt::AlignLeft); 1830 1831 mCommandEdit = new LineEdit(LineEdit::Type::Url, this); 1832 mCommandEdit->setWhatsThis(i18nc("@info:whatsthis", "Enter a shell command to execute.")); 1833 connect(mCommandEdit, &LineEdit::textChanged, this, &CommandEdit::changed); 1834 vlayout->addWidget(mCommandEdit); 1835 1836 mScriptEdit = new TextEdit(this); 1837 mScriptEdit->setWhatsThis(i18nc("@info:whatsthis", "Enter the contents of a script to execute")); 1838 connect(mScriptEdit, &TextEdit::textChanged, this, &CommandEdit::changed); 1839 vlayout->addWidget(mScriptEdit); 1840 1841 slotCmdScriptToggled(mTypeScript->isChecked()); 1842 } 1843 1844 /****************************************************************************** 1845 * Initialise the widget controls from the specified event. 1846 */ 1847 void CommandEdit::setScript(bool script) 1848 { 1849 mTypeScript->setChecked(script); 1850 } 1851 1852 bool CommandEdit::isScript() const 1853 { 1854 return mTypeScript->isChecked(); 1855 } 1856 1857 /****************************************************************************** 1858 * Set the widget's text. 1859 */ 1860 void CommandEdit::setText(const AlarmText& alarmText) 1861 { 1862 const QString text = alarmText.displayText(); 1863 const bool script = alarmText.isScript(); 1864 mTypeScript->setChecked(script); 1865 if (script) 1866 mScriptEdit->setPlainText(text); 1867 else 1868 mCommandEdit->setText(File::pathOrUrl(text)); 1869 } 1870 1871 /****************************************************************************** 1872 * Return the widget's text. 1873 * If it's a command line, it must not start with environment variable 1874 * specifications. 1875 * If 'showErrorMessage' is true and the text is empty, an error message is 1876 * displayed. 1877 * Reply = text, or empty if a command line starting with environment vars. 1878 */ 1879 QString CommandEdit::text(EditAlarmDlg* dlg, bool showErrorMessage) const 1880 { 1881 QString result; 1882 if (mTypeScript->isChecked()) 1883 result = mScriptEdit->toPlainText().trimmed(); 1884 else 1885 { 1886 result = mCommandEdit->text().trimmed(); 1887 QString params = result; 1888 const QString cmd = ShellProcess::splitCommandLine(params); 1889 if (cmd.contains(QLatin1Char('='))) 1890 { 1891 if (showErrorMessage) 1892 KAMessageBox::error(dlg, xi18nc("@info", "<para>The command cannot set environment variables:</para><para><icode>%1</icode></para>", cmd)); 1893 return {}; 1894 } 1895 } 1896 if (showErrorMessage && result.isEmpty()) 1897 KAMessageBox::error(dlg, i18nc("@info", "Please enter a command or script to execute")); 1898 return result; 1899 } 1900 1901 /****************************************************************************** 1902 * Set the read-only status of all controls. 1903 */ 1904 void CommandEdit::setReadOnly(bool readOnly) 1905 { 1906 mTypeScript->setReadOnly(readOnly); 1907 mCommandEdit->setReadOnly(readOnly); 1908 mScriptEdit->setReadOnly(readOnly); 1909 } 1910 1911 /****************************************************************************** 1912 * Called when one of the command type radio buttons is clicked, 1913 * to display the appropriate edit field. 1914 */ 1915 void CommandEdit::slotCmdScriptToggled(bool on) 1916 { 1917 if (on) 1918 { 1919 mCommandEdit->hide(); 1920 mScriptEdit->show(); 1921 mScriptEdit->setFocus(); 1922 } 1923 else 1924 { 1925 mScriptEdit->hide(); 1926 mCommandEdit->show(); 1927 mCommandEdit->setFocus(); 1928 } 1929 Q_EMIT scriptToggled(on); 1930 } 1931 1932 /****************************************************************************** 1933 * Returns the minimum size of the widget. 1934 */ 1935 QSize CommandEdit::minimumSizeHint() const 1936 { 1937 const QSize t(mTypeScript->minimumSizeHint()); 1938 QSize s(mCommandEdit->minimumSizeHint().expandedTo(mScriptEdit->minimumSizeHint())); 1939 s.setHeight(s.height() + style()->pixelMetric(QStyle::PM_LayoutVerticalSpacing) + t.height()); 1940 if (s.width() < t.width()) 1941 s.setWidth(t.width()); 1942 return s; 1943 } 1944 1945 1946 1947 /*============================================================================= 1948 = Class TextEdit 1949 = A text edit field with a minimum height of 3 text lines. 1950 =============================================================================*/ 1951 TextEdit::TextEdit(QWidget* parent) 1952 : KTextEdit(parent) 1953 { 1954 QSize tsize = TextEdit::minimumSizeHint(); // avoid calling virtual method from constructor 1955 tsize.setHeight(fontMetrics().lineSpacing()*13/4 + 2*frameWidth()); 1956 setMinimumSize(tsize); 1957 } 1958 1959 void TextEdit::enableEmailDrop() 1960 { 1961 mEmailDrop = true; 1962 setAcceptDrops(true); // allow drag-and-drop onto this widget 1963 } 1964 1965 void TextEdit::dragEnterEvent(QDragEnterEvent* e) 1966 { 1967 if (KCalUtils::ICalDrag::canDecode(e->mimeData())) 1968 { 1969 e->ignore(); // don't accept "text/calendar" objects 1970 return; 1971 } 1972 if (mEmailDrop && DragDrop::mayHaveRFC822(e->mimeData())) 1973 { 1974 e->acceptProposedAction(); 1975 return; 1976 } 1977 KTextEdit::dragEnterEvent(e); 1978 } 1979 1980 void TextEdit::dragMoveEvent(QDragMoveEvent* e) 1981 { 1982 if (mEmailDrop && DragDrop::mayHaveRFC822(e->mimeData())) 1983 { 1984 e->acceptProposedAction(); 1985 return; 1986 } 1987 KTextEdit::dragMoveEvent(e); 1988 } 1989 1990 /****************************************************************************** 1991 * Called when an object is dropped on the widget. 1992 */ 1993 void TextEdit::dropEvent(QDropEvent* e) 1994 { 1995 const QMimeData* data = e->mimeData(); 1996 if (mEmailDrop) 1997 { 1998 AlarmText alarmText; 1999 bool haveEmail = false; 2000 if (DragDrop::dropRFC822(data, alarmText)) 2001 { 2002 // Email message(s). Ignore all but the first. 2003 qCDebug(KALARM_LOG) << "TextEdit::dropEvent: email"; 2004 haveEmail = true; 2005 } 2006 else 2007 { 2008 QUrl url; 2009 if (KAlarm::dropAkonadiEmail(data, url, alarmText)) 2010 { 2011 // It's an email held in Akonadi 2012 qCDebug(KALARM_LOG) << "TextEdit::dropEvent: Akonadi email"; 2013 haveEmail = true; 2014 } 2015 } 2016 if (haveEmail) 2017 { 2018 if (!alarmText.isEmpty()) 2019 setPlainText(alarmText.displayText()); 2020 return; 2021 } 2022 } 2023 QString text; 2024 if (DragDrop::dropPlainText(data, text)) 2025 { 2026 setPlainText(text); 2027 return; 2028 } 2029 KTextEdit::dropEvent(e); 2030 } 2031 2032 #include "moc_editdlgtypes.cpp" 2033 2034 // vim: et sw=4: