File indexing completed on 2025-01-19 04:46:56

0001 /*
0002    SPDX-FileCopyrightText: 2014-2024 Laurent Montel <montel@kde.org>
0003 
0004    SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #include "eventedit.h"
0008 #include "createeventplugin_debug.h"
0009 #include "eventdatetimewidget.h"
0010 #include <CalendarSupport/KCalPrefs>
0011 #include <CalendarSupport/Utils>
0012 #include <messageviewer/globalsettings_messageviewer.h>
0013 
0014 #include <KLocalizedString>
0015 #include <QDateTime>
0016 #include <QIcon>
0017 #include <QLineEdit>
0018 
0019 #include <QEvent>
0020 #include <QHBoxLayout>
0021 #include <QKeyEvent>
0022 #include <QLabel>
0023 
0024 #include <Akonadi/CollectionComboBox>
0025 #include <QPushButton>
0026 
0027 #include <IncidenceEditor/IncidenceDialog>
0028 #include <IncidenceEditor/IncidenceDialogFactory>
0029 #include <KGuiItem>
0030 #include <KStandardGuiItem>
0031 
0032 namespace MessageViewer
0033 {
0034 QAbstractItemModel *_k_eventEditStubModel = nullptr;
0035 }
0036 
0037 using namespace MessageViewer;
0038 
0039 EventEdit::EventEdit(QWidget *parent)
0040     : QWidget(parent)
0041     , mEventEdit(new QLineEdit(this))
0042     , mStartDateTimeEdit(new EventDateTimeWidget(this))
0043     , mEndDateTimeEdit(new EventDateTimeWidget(this))
0044     , mSaveButton(new QPushButton(QIcon::fromTheme(QStringLiteral("appointment-new")), i18n("&Save"), this))
0045     , mOpenEditorButton(new QPushButton(i18n("Open &Editor..."), this))
0046 {
0047     auto vbox = new QVBoxLayout(this);
0048     vbox->setContentsMargins(5, 5, 5, 5);
0049     vbox->setSpacing(2);
0050 
0051     auto hbox = new QHBoxLayout;
0052     hbox->setContentsMargins({});
0053     hbox->setSpacing(2);
0054     vbox->addLayout(hbox);
0055 
0056     auto lab = new QLabel(i18n("Event:"), this);
0057     hbox->addWidget(lab);
0058 
0059     mEventEdit->setClearButtonEnabled(true);
0060     mEventEdit->setObjectName(QLatin1StringView("eventedit"));
0061     mEventEdit->setFocus();
0062     connect(mEventEdit, &QLineEdit::returnPressed, this, &EventEdit::slotReturnPressed);
0063     connect(mEventEdit, &QLineEdit::textChanged, this, &EventEdit::slotUpdateButtons);
0064     hbox->addWidget(mEventEdit);
0065 
0066     hbox->addSpacing(5);
0067 
0068     mCollectionCombobox = new Akonadi::CollectionComboBox(_k_eventEditStubModel, this);
0069     mCollectionCombobox->setAccessRightsFilter(Akonadi::Collection::CanCreateItem);
0070     mCollectionCombobox->setMinimumWidth(250);
0071     mCollectionCombobox->setMimeTypeFilter(QStringList() << KCalendarCore::Event::eventMimeType());
0072     mCollectionCombobox->setObjectName(QLatin1StringView("akonadicombobox"));
0073 #ifndef QT_NO_ACCESSIBILITY
0074     mCollectionCombobox->setAccessibleDescription(i18n("Calendar where the new event will be stored."));
0075 #endif
0076     mCollectionCombobox->setToolTip(i18n("Calendar where the new event will be stored"));
0077 
0078     connect(mCollectionCombobox, &Akonadi::CollectionComboBox::currentIndexChanged, this, &EventEdit::slotCollectionChanged);
0079     connect(mCollectionCombobox, &Akonadi::CollectionComboBox::activated, this, &EventEdit::slotCollectionChanged);
0080 
0081     connect(mCollectionCombobox->model(), &QAbstractItemModel::rowsInserted, this, &EventEdit::comboboxRowInserted);
0082     hbox->addWidget(mCollectionCombobox);
0083 
0084     hbox = new QHBoxLayout;
0085     hbox->setContentsMargins({});
0086     hbox->setSpacing(2);
0087     vbox->addLayout(hbox);
0088 
0089     lab = new QLabel(i18n("Start:"), this);
0090     hbox->addWidget(lab);
0091     QDateTime currentDateTime = QDateTime::currentDateTime();
0092     mStartDateTimeEdit->setObjectName(QLatin1StringView("startdatetimeedit"));
0093     mStartDateTimeEdit->setDateTime(currentDateTime);
0094 #ifndef QT_NO_ACCESSIBILITY
0095     mStartDateTimeEdit->setAccessibleDescription(i18n("Select start time for event."));
0096 #endif
0097     connect(mStartDateTimeEdit, &EventDateTimeWidget::dateTimeChanged, this, &EventEdit::slotStartDateTimeChanged);
0098     hbox->addWidget(mStartDateTimeEdit);
0099 
0100     hbox->addSpacing(5);
0101 
0102     lab = new QLabel(i18n("End:"), this);
0103     hbox->addWidget(lab);
0104     mEndDateTimeEdit->setObjectName(QLatin1StringView("enddatetimeedit"));
0105     mEndDateTimeEdit->setDateTime(currentDateTime.addSecs(3600));
0106 #ifndef QT_NO_ACCESSIBILITY
0107     mEndDateTimeEdit->setAccessibleDescription(i18n("Select end time for event."));
0108 #endif
0109     connect(mEndDateTimeEdit, &EventDateTimeWidget::dateTimeChanged, this, &EventEdit::slotEndDateTimeChanged);
0110 
0111     hbox->addWidget(mEndDateTimeEdit);
0112 
0113     hbox->addStretch(1);
0114 
0115     hbox = new QHBoxLayout;
0116     hbox->setSpacing(2);
0117     hbox->setContentsMargins({});
0118     vbox->addLayout(hbox);
0119 
0120     hbox->addStretch(1);
0121 
0122     mSaveButton->setObjectName(QLatin1StringView("save-button"));
0123     mSaveButton->setEnabled(false);
0124 #ifndef QT_NO_ACCESSIBILITY
0125     mSaveButton->setAccessibleDescription(i18n("Create new event and close this widget."));
0126 #endif
0127     connect(mSaveButton, &QPushButton::clicked, this, &EventEdit::slotReturnPressed);
0128     hbox->addWidget(mSaveButton);
0129 
0130 #ifndef QT_NO_ACCESSIBILITY
0131     mOpenEditorButton->setAccessibleDescription(i18n("Open event editor, where more details can be changed."));
0132 #endif
0133     mOpenEditorButton->setObjectName(QLatin1StringView("open-editor-button"));
0134     mOpenEditorButton->setEnabled(false);
0135     connect(mOpenEditorButton, &QPushButton::clicked, this, &EventEdit::slotOpenEditor);
0136     hbox->addWidget(mOpenEditorButton);
0137 
0138     auto btn = new QPushButton(this);
0139     KGuiItem::assign(btn, KStandardGuiItem::cancel());
0140     btn->setObjectName(QLatin1StringView("close-button"));
0141 #ifndef QT_NO_ACCESSIBILITY
0142     btn->setAccessibleDescription(i18n("Close the widget for creating new events."));
0143 #endif
0144     connect(btn, &QPushButton::clicked, this, &EventEdit::slotCloseWidget);
0145     hbox->addWidget(btn);
0146 
0147     readConfig();
0148     setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed));
0149     installEventFilter(this);
0150     mCollectionCombobox->installEventFilter(this);
0151 }
0152 
0153 EventEdit::~EventEdit()
0154 {
0155     writeConfig();
0156 }
0157 
0158 void EventEdit::comboboxRowInserted()
0159 {
0160     slotUpdateButtons(mEventEdit->text());
0161 }
0162 
0163 void EventEdit::writeConfig()
0164 {
0165     const Akonadi::Collection col = mCollectionCombobox->currentCollection();
0166     // col might not be valid if the collection wasn't found yet (the combo is async), skip saving in that case.
0167     if (col.isValid() && col.id() != MessageViewer::MessageViewerSettingsBase::self()->lastEventSelectedFolder()) {
0168         MessageViewer::MessageViewerSettingsBase::self()->setLastEventSelectedFolder(col.id());
0169         MessageViewer::MessageViewerSettingsBase::self()->save();
0170     }
0171 }
0172 
0173 void EventEdit::slotUpdateButtons(const QString &subject)
0174 {
0175     const bool subjectIsNotEmpty = !subject.trimmed().isEmpty();
0176     const bool collectionComboboxEmpty = (mCollectionCombobox->count() < 1);
0177     mSaveButton->setEnabled(subjectIsNotEmpty && !collectionComboboxEmpty);
0178     mOpenEditorButton->setEnabled(subjectIsNotEmpty && !collectionComboboxEmpty);
0179 }
0180 
0181 void EventEdit::showEventEdit()
0182 {
0183     mEventEdit->setFocus();
0184     QDateTime currentDateTime = QDateTime::currentDateTime();
0185     mStartDateTimeEdit->setDateTime(currentDateTime);
0186     mEndDateTimeEdit->setDateTime(currentDateTime.addSecs(3600));
0187     show();
0188 }
0189 
0190 void EventEdit::readConfig()
0191 {
0192     const qint64 id = MessageViewer::MessageViewerSettingsBase::self()->lastEventSelectedFolder();
0193     if (id >= 0) {
0194         mCollectionCombobox->setDefaultCollection(Akonadi::Collection(id));
0195     }
0196 }
0197 
0198 Akonadi::Collection EventEdit::collection() const
0199 {
0200     return mCollection;
0201 }
0202 
0203 void EventEdit::slotCollectionChanged(int /*index*/)
0204 {
0205     setCollection(mCollectionCombobox->currentCollection());
0206 }
0207 
0208 void EventEdit::setCollection(const Akonadi::Collection &value)
0209 {
0210     if (mCollection != value) {
0211         mCollection = value;
0212         Q_EMIT collectionChanged(mCollection);
0213     }
0214 }
0215 
0216 KMime::Message::Ptr EventEdit::message() const
0217 {
0218     return mMessage;
0219 }
0220 
0221 void EventEdit::setMessage(const KMime::Message::Ptr &value)
0222 {
0223     if (mMessage != value) {
0224         mMessage = value;
0225         const KMime::Headers::Subject *const subject = mMessage ? mMessage->subject(false) : nullptr;
0226         if (subject) {
0227             mEventEdit->setText(subject->asUnicodeString());
0228             mEventEdit->selectAll();
0229             mEventEdit->setFocus();
0230         } else {
0231             mEventEdit->clear();
0232         }
0233         Q_EMIT messageChanged(mMessage);
0234     }
0235 }
0236 
0237 void EventEdit::slotCloseWidget()
0238 {
0239     if (isVisible()) {
0240         writeConfig();
0241         mEventEdit->clear();
0242         mMessage = KMime::Message::Ptr();
0243         hide();
0244     }
0245 }
0246 
0247 void EventEdit::slotReturnPressed()
0248 {
0249     if (!mMessage) {
0250         qCDebug(CREATEEVENTPLUGIN_LOG) << " Message is null";
0251         return;
0252     }
0253     const Akonadi::Collection collection = mCollectionCombobox->currentCollection();
0254     if (!collection.isValid()) {
0255         qCDebug(CREATEEVENTPLUGIN_LOG) << " Collection is not valid";
0256         return;
0257     }
0258 
0259     const QDateTime dtstart = mStartDateTimeEdit->dateTime();
0260     const QDateTime dtend = mEndDateTimeEdit->dateTime();
0261     if (!dtstart.isValid() || !dtend.isValid()) {
0262         qCDebug(CREATEEVENTPLUGIN_LOG) << " date is not valid !";
0263         return;
0264     }
0265 
0266     if (!mEventEdit->text().trimmed().isEmpty()) {
0267         KCalendarCore::Event::Ptr event = createEventItem();
0268         Q_EMIT createEvent(event, collection);
0269         hide();
0270     }
0271 }
0272 
0273 bool EventEdit::eventFilter(QObject *object, QEvent *e)
0274 {
0275     // Close the bar when pressing Escape.
0276     // Not using a QShortcut for this because it could conflict with
0277     // window-global actions (e.g. Emil Sedgh binds Esc to "close tab").
0278     // With a shortcut override we can catch this before it gets to kactions.
0279     const bool shortCutOverride = (e->type() == QEvent::ShortcutOverride);
0280     if (shortCutOverride) {
0281         auto kev = static_cast<QKeyEvent *>(e);
0282         if (kev->key() == Qt::Key_Escape) {
0283             e->accept();
0284             slotCloseWidget();
0285             return true;
0286         } else if (kev->key() == Qt::Key_Enter || kev->key() == Qt::Key_Return || kev->key() == Qt::Key_Space) {
0287             e->accept();
0288             if (object == mCollectionCombobox) {
0289                 mCollectionCombobox->showPopup();
0290             }
0291             return true;
0292         }
0293     }
0294     return QWidget::eventFilter(object, e);
0295 }
0296 
0297 void EventEdit::slotEndDateTimeChanged(const QDateTime &newDateTime)
0298 {
0299     if (!newDateTime.isValid()) {
0300         return;
0301     }
0302     QDateTime currentDateTime = QDateTime::currentDateTime();
0303     if (newDateTime.date() > currentDateTime.date()) {
0304         QDateTime newDateDate = newDateTime;
0305         newDateDate.setTime(QTime(0, 0, 0));
0306         mEndDateTimeEdit->setMinimumDateTime(newDateDate);
0307     }
0308 }
0309 
0310 void EventEdit::slotStartDateTimeChanged(const QDateTime &newDateTime)
0311 {
0312     if (!newDateTime.isValid()) {
0313         return;
0314     }
0315 
0316     if (mEndDateTimeEdit->date() == newDateTime.date() && mEndDateTimeEdit->time() < newDateTime.time()) {
0317         mEndDateTimeEdit->setTime(newDateTime.time());
0318     }
0319     if (mEndDateTimeEdit->date() < newDateTime.date()) {
0320         mEndDateTimeEdit->setDate(newDateTime.date());
0321     }
0322 
0323     mEndDateTimeEdit->setMinimumDateTime(newDateTime);
0324 }
0325 
0326 KCalendarCore::Event::Ptr EventEdit::createEventItem()
0327 {
0328     if (mMessage) {
0329         KCalendarCore::Attachment attachment(mMessage->encodedContent().toBase64(), KMime::Message::mimeType());
0330         const KMime::Headers::Subject *const subject = mMessage->subject(false);
0331         if (subject) {
0332             attachment.setLabel(subject->asUnicodeString());
0333         }
0334         KCalendarCore::Event::Ptr event(new KCalendarCore::Event);
0335         event->setSummary(mEventEdit->text());
0336         event->setDtStart(mStartDateTimeEdit->dateTime());
0337         event->setDtEnd(mEndDateTimeEdit->dateTime());
0338         event->addAttachment(attachment);
0339 
0340         if (CalendarSupport::KCalPrefs::instance()->defaultEventReminders()) {
0341             KCalendarCore::Alarm::Ptr alm = event->newAlarm();
0342             CalendarSupport::createAlarmReminder(alm, event->type());
0343         }
0344 
0345         return event;
0346     }
0347     return {};
0348 }
0349 
0350 void EventEdit::slotOpenEditor()
0351 {
0352     KCalendarCore::Event::Ptr event = createEventItem();
0353     if (event) {
0354         Akonadi::Item item;
0355         item.setPayload<KCalendarCore::Event::Ptr>(event);
0356         item.setMimeType(KCalendarCore::Event::eventMimeType());
0357 
0358         IncidenceEditorNG::IncidenceDialog *dlg =
0359             IncidenceEditorNG::IncidenceDialogFactory::create(true, KCalendarCore::IncidenceBase::TypeEvent, nullptr, this);
0360         dlg->selectCollection(mCollectionCombobox->currentCollection());
0361         connect(dlg, &IncidenceEditorNG::IncidenceDialog::finished, this, &EventEdit::slotCloseWidget);
0362         dlg->load(item);
0363         dlg->open();
0364     }
0365 }
0366 
0367 #include "moc_eventedit.cpp"