File indexing completed on 2024-05-12 05:21:24

0001 /*
0002   This file is part of KOrganizer.
0003 
0004   SPDX-FileCopyrightText: 2000, 2001 Cornelius Schumacher <schumacher@kde.org>
0005   SPDX-FileCopyrightText: 2003-2004 Reinhold Kainhofer <reinhold@kainhofer.com>
0006 
0007   SPDX-License-Identifier: GPL-2.0-or-later WITH Qt-Commercial-exception-1.0
0008 */
0009 
0010 #include "koeventpopupmenu.h"
0011 #include "korganizer_debug.h"
0012 
0013 #include <Akonadi/CalendarUtils>
0014 #include <Akonadi/ItemCreateJob>
0015 #include <Akonadi/NoteUtils>
0016 
0017 #include <CalendarSupport/CalPrinter>
0018 #include <CalendarSupport/NoteEditDialog>
0019 #include <CalendarSupport/Utils>
0020 
0021 #include <KCalendarCore/CalFormat>
0022 
0023 #include <IncidenceEditor/IncidenceDialog>
0024 #include <IncidenceEditor/IncidenceDialogFactory>
0025 
0026 #include <QCoreApplication>
0027 
0028 KOEventPopupMenu::KOEventPopupMenu(QWidget *parent)
0029     : QMenu(parent)
0030 {
0031     init(MenuStyle::NormalView);
0032 }
0033 
0034 KOEventPopupMenu::KOEventPopupMenu(MenuStyle menuStyle, QWidget *parent)
0035     : QMenu(parent)
0036 {
0037     init(menuStyle);
0038 }
0039 
0040 void KOEventPopupMenu::init(MenuStyle menuStyle)
0041 {
0042     // These actions are always shown, no matter what
0043     addAction(QIcon::fromTheme(QStringLiteral("document-preview")), i18nc("@action:inmenu", "&Show"), this, &KOEventPopupMenu::popupShow);
0044 
0045     addAction(QIcon::fromTheme(QStringLiteral("document-edit")), i18nc("@action:inmenu", "&Edit..."), this, &KOEventPopupMenu::popupEdit);
0046 
0047     addAction(QIcon::fromTheme(QStringLiteral("edit-delete")), i18nc("@action:inmenu delete this incidence", "&Delete"), this, &KOEventPopupMenu::popupDelete);
0048 
0049     addSeparator();
0050 
0051     addAction(QIcon::fromTheme(QStringLiteral("document-print")), i18nc("@action:inmenu", "&Print..."), this, &KOEventPopupMenu::slotPrint);
0052 
0053     addAction(QIcon::fromTheme(QStringLiteral("document-print-preview")), i18nc("@action:inmenu", "Print Previe&w..."), this, &KOEventPopupMenu::printPreview);
0054 
0055     // Add more menu actions according to Menu style
0056     switch (menuStyle) {
0057     case MenuStyle::NormalView:
0058         appendEditOnlyItems();
0059         appendEventOnlyItems();
0060         appendTodoOnlyItems();
0061         appendReminderOnlyItems();
0062         appendRecurrenceOnlyItems();
0063         appendShareOnlyItems();
0064     default:
0065         break;
0066     }
0067 }
0068 
0069 void KOEventPopupMenu::appendEditOnlyItems()
0070 {
0071     mEditOnlyItems.append(addSeparator());
0072 
0073     mEditOnlyItems.append(
0074         addAction(QIcon::fromTheme(QStringLiteral("edit-cut")), i18nc("@action:inmenu cut this incidence", "C&ut"), this, &KOEventPopupMenu::popupCut));
0075     mEditOnlyItems.append(
0076         addAction(QIcon::fromTheme(QStringLiteral("edit-copy")), i18nc("@action:inmenu copy this incidence", "&Copy"), this, &KOEventPopupMenu::popupCopy));
0077     mEditOnlyItems.append(addAction(QIcon::fromTheme(QStringLiteral("edit-paste")), i18nc("@action:inmenu", "&Paste"), this, &KOEventPopupMenu::popupPaste));
0078 }
0079 
0080 void KOEventPopupMenu::appendEventOnlyItems()
0081 {
0082     mEventOnlyItems.append(addSeparator());
0083 
0084     mEventOnlyItems.append(
0085         addAction(QIcon::fromTheme(QStringLiteral("task-new")), i18nc("@action:inmenu", "Create To-do from Event"), this, &KOEventPopupMenu::createTodo));
0086     mEventOnlyItems.last()->setObjectName(QLatin1StringView("createtodo")); // id used by unit test
0087 
0088     mEventOnlyItems.append(addAction(QIcon::fromTheme(QStringLiteral("view-pim-notes")),
0089                                      i18nc("@action:inmenu", "Create Note for Event"),
0090                                      this,
0091                                      qOverload<>(&KOEventPopupMenu::createNote)));
0092     mEventOnlyItems.last()->setObjectName(QLatin1StringView("createnoteforevent")); // id used by unit test
0093 }
0094 
0095 void KOEventPopupMenu::appendTodoOnlyItems()
0096 {
0097     mTodoOnlyItems.append(addSeparator());
0098 
0099     mTodoOnlyItems.append(addAction(QIcon::fromTheme(QStringLiteral("task-complete")),
0100                                     i18nc("@action:inmenu", "Togg&le To-do Completed"),
0101                                     this,
0102                                     &KOEventPopupMenu::toggleTodoCompleted));
0103 
0104     mTodoOnlyItems.append(addAction(QIcon::fromTheme(QStringLiteral("appointment-new")),
0105                                     i18nc("@action:inmenu", "Create Event from To-do"),
0106                                     this,
0107                                     qOverload<>(&KOEventPopupMenu::createEvent)));
0108     mTodoOnlyItems.last()->setObjectName(QLatin1StringView("createevent")); // id used by unit test
0109 
0110     mTodoOnlyItems.append(addAction(QIcon::fromTheme(QStringLiteral("view-pim-notes")),
0111                                     i18nc("@action:inmenu", "Create Note for To-do"),
0112                                     this,
0113                                     qOverload<>(&KOEventPopupMenu::createNote)));
0114     mTodoOnlyItems.last()->setObjectName(QLatin1StringView("createnotefortodo")); // id used by unit test
0115 }
0116 
0117 void KOEventPopupMenu::appendReminderOnlyItems()
0118 {
0119     mReminderOnlyItems.append(addSeparator());
0120 
0121     mToggleReminder =
0122         addAction(QIcon::fromTheme(QStringLiteral("appointment-reminder")), i18nc("@action:inmenu", "&Toggle Reminder"), this, &KOEventPopupMenu::toggleAlarm);
0123     mReminderOnlyItems.append(mToggleReminder);
0124 }
0125 
0126 void KOEventPopupMenu::appendRecurrenceOnlyItems()
0127 {
0128     mRecurrenceOnlyItems.append(addSeparator());
0129     mDissociateOccurrences = addAction(i18nc("@action:inmenu", "&Dissociate From Recurrence..."), this, &KOEventPopupMenu::dissociateOccurrences);
0130     mRecurrenceOnlyItems.append(mDissociateOccurrences);
0131 }
0132 
0133 void KOEventPopupMenu::appendShareOnlyItems()
0134 {
0135     mShareOnlyItems.append(addSeparator());
0136     mShareOnlyItems.append(
0137         addAction(QIcon::fromTheme(QStringLiteral("mail-forward")), i18nc("@action:inmenu", "Send as iCalendar..."), this, &KOEventPopupMenu::forward));
0138 }
0139 
0140 void KOEventPopupMenu::showIncidencePopup(const Akonadi::CollectionCalendar::Ptr &calendar, const Akonadi::Item &item, const QDate &qd)
0141 {
0142     mCurrentCalendar = calendar;
0143     mCurrentIncidence = item;
0144     mCurrentDate = qd;
0145 
0146     if (!CalendarSupport::hasIncidence(mCurrentIncidence) /*&& qd.isValid()*/) {
0147         qCDebug(KORGANIZER_LOG) << "No event selected";
0148         return;
0149     }
0150 
0151     if (!mCurrentCalendar) {
0152         // TODO fix it
0153         qCDebug(KORGANIZER_LOG) << "Calendar is unset";
0154         return;
0155     }
0156 
0157     KCalendarCore::Incidence::Ptr incidence = Akonadi::CalendarUtils::incidence(mCurrentIncidence);
0158     Q_ASSERT(incidence);
0159 
0160     // Determine if this Incidence's calendar is writeable.
0161     // Else all actions that might modify the Incidence are disabled.
0162     const bool hasChangeRights = mCurrentCalendar->hasRight(Akonadi::Collection::CanChangeItem);
0163 
0164     QList<QAction *>::Iterator it;
0165     QList<QAction *>::Iterator end;
0166 
0167     // Enable/Disabled menu edit items
0168     end = mEditOnlyItems.end();
0169     for (it = mEditOnlyItems.begin(); it != end; ++it) {
0170         (*it)->setEnabled(hasChangeRights);
0171     }
0172 
0173     // Enable/Disable menu items valid for Events only
0174     end = mEventOnlyItems.end();
0175     for (it = mEventOnlyItems.begin(); it != end; ++it) {
0176         (*it)->setVisible(incidence->type() == KCalendarCore::Incidence::TypeEvent);
0177         (*it)->setEnabled(true);
0178     }
0179 
0180     // Enable/Disable menu items valid for Todos only
0181     end = mTodoOnlyItems.end();
0182     for (it = mTodoOnlyItems.begin(); it != end; ++it) {
0183         (*it)->setVisible(incidence->type() == KCalendarCore::Incidence::TypeTodo);
0184         (*it)->setEnabled(true);
0185     }
0186     if (mToggleReminder) {
0187         mToggleReminder->setText(incidence->hasEnabledAlarms() ? i18nc("@action:inmenu", "&Toggle Reminder Off")
0188                                                                : i18nc("@action:inmenu", "&Toggle Reminder On"));
0189     }
0190 
0191     // Enable/Disable menu items valid for reminder Incidences only
0192     end = mReminderOnlyItems.end();
0193     for (it = mReminderOnlyItems.begin(); it != end; ++it) {
0194         (*it)->setVisible(incidence->type() != KCalendarCore::Incidence::TypeJournal);
0195         (*it)->setEnabled(hasChangeRights);
0196     }
0197 
0198     // Enable/Disable menu items valid for recurrent Incidences only
0199     end = mRecurrenceOnlyItems.end();
0200     for (it = mRecurrenceOnlyItems.begin(); it != end; ++it) {
0201         (*it)->setVisible(incidence->recurs());
0202         (*it)->setEnabled(hasChangeRights);
0203     }
0204     if (incidence->recurs()) {
0205         const QDateTime thisDateTime(qd, {}, Qt::LocalTime);
0206         const bool isLastOccurrence = !incidence->recurrence()->getNextDateTime(thisDateTime).isValid();
0207         const bool isFirstOccurrence = !incidence->recurrence()->getPreviousDateTime(thisDateTime).isValid();
0208         if (mDissociateOccurrences) {
0209             mDissociateOccurrences->setEnabled(!(isFirstOccurrence && isLastOccurrence) && hasChangeRights);
0210         }
0211     }
0212 
0213     // Enable/Disable menu items valid for sharing Incidences only
0214     end = mShareOnlyItems.end();
0215     for (it = mShareOnlyItems.begin(); it != end; ++it) {
0216         (*it)->setVisible(true);
0217         (*it)->setEnabled(true);
0218     }
0219 
0220     // Show the menu now
0221     popup(QCursor::pos());
0222 }
0223 
0224 void KOEventPopupMenu::popupShow()
0225 {
0226     if (CalendarSupport::hasIncidence(mCurrentIncidence)) {
0227         Q_EMIT showIncidenceSignal(mCurrentIncidence);
0228     }
0229 }
0230 
0231 void KOEventPopupMenu::popupEdit()
0232 {
0233     if (CalendarSupport::hasIncidence(mCurrentIncidence)) {
0234         Q_EMIT editIncidenceSignal(mCurrentIncidence);
0235     }
0236 }
0237 
0238 void KOEventPopupMenu::slotPrint()
0239 {
0240     print(false);
0241 }
0242 
0243 void KOEventPopupMenu::print(bool preview)
0244 {
0245     CalendarSupport::CalPrinter printer(this, mCurrentCalendar, true);
0246     connect(this, &KOEventPopupMenu::configChanged, &printer, &CalendarSupport::CalPrinter::updateConfig);
0247 
0248     KCalendarCore::Incidence::List selectedIncidences;
0249     Q_ASSERT(mCurrentIncidence.hasPayload<KCalendarCore::Incidence::Ptr>());
0250     selectedIncidences.append(mCurrentIncidence.payload<KCalendarCore::Incidence::Ptr>());
0251 
0252     printer.print(CalendarSupport::CalPrinterBase::Incidence, mCurrentDate, mCurrentDate, selectedIncidences, preview);
0253 }
0254 
0255 void KOEventPopupMenu::printPreview()
0256 {
0257     print(true);
0258 }
0259 
0260 void KOEventPopupMenu::popupDelete()
0261 {
0262     if (CalendarSupport::hasIncidence(mCurrentIncidence)) {
0263         Q_EMIT deleteIncidenceSignal(mCurrentIncidence);
0264     }
0265 }
0266 
0267 void KOEventPopupMenu::popupCut()
0268 {
0269     if (CalendarSupport::hasIncidence(mCurrentIncidence)) {
0270         Q_EMIT cutIncidenceSignal(mCurrentIncidence);
0271     }
0272 }
0273 
0274 void KOEventPopupMenu::popupCopy()
0275 {
0276     if (CalendarSupport::hasIncidence(mCurrentIncidence)) {
0277         Q_EMIT copyIncidenceSignal(mCurrentIncidence);
0278     }
0279 }
0280 
0281 void KOEventPopupMenu::popupPaste()
0282 {
0283     Q_EMIT pasteIncidenceSignal();
0284 }
0285 
0286 void KOEventPopupMenu::toggleAlarm()
0287 {
0288     if (CalendarSupport::hasIncidence(mCurrentIncidence)) {
0289         Q_EMIT toggleAlarmSignal(mCurrentIncidence);
0290     }
0291 }
0292 
0293 void KOEventPopupMenu::dissociateOccurrences()
0294 {
0295     if (CalendarSupport::hasIncidence(mCurrentIncidence)) {
0296         Q_EMIT dissociateOccurrencesSignal(mCurrentIncidence, mCurrentDate);
0297     }
0298 }
0299 
0300 void KOEventPopupMenu::forward()
0301 {
0302     if (CalendarSupport::hasIncidence(mCurrentIncidence)) {
0303         KCalendarCore::Incidence::Ptr incidence = Akonadi::CalendarUtils::incidence(mCurrentIncidence);
0304         if (incidence) {
0305             auto *handler = new Akonadi::ITIPHandler(this);
0306             connect(handler, &Akonadi::ITIPHandler::sentAsICalendar, this, [handler]() {
0307                 handler->deleteLater();
0308             });
0309             handler->setCalendar(mCurrentCalendar);
0310             handler->sendAsICalendar(incidence, this);
0311         }
0312     }
0313 }
0314 
0315 void KOEventPopupMenu::createEvent(const Akonadi::Item &item)
0316 {
0317     mCurrentIncidence = item;
0318     createEvent();
0319 }
0320 
0321 void KOEventPopupMenu::createEvent()
0322 {
0323     // Must be a Incidence
0324     if (!CalendarSupport::hasIncidence(mCurrentIncidence)) {
0325         return;
0326     }
0327     // Event ->event doesn't make sense
0328     if (CalendarSupport::hasEvent(mCurrentIncidence)) {
0329         return;
0330     }
0331 
0332     if (CalendarSupport::hasTodo(mCurrentIncidence)) {
0333         KCalendarCore::Todo::Ptr todo(Akonadi::CalendarUtils::todo(mCurrentIncidence));
0334         KCalendarCore::Event::Ptr event(new KCalendarCore::Event(*todo));
0335         event->setUid(KCalendarCore::CalFormat::createUniqueId());
0336         event->setDtStart(todo->dtStart());
0337         event->setAllDay(todo->allDay());
0338         event->setDtEnd(todo->dtDue());
0339         Akonadi::Item newEventItem;
0340         newEventItem.setMimeType(KCalendarCore::Event::eventMimeType());
0341         newEventItem.setPayload<KCalendarCore::Event::Ptr>(event);
0342 
0343         IncidenceEditorNG::IncidenceDialog *dlg =
0344             IncidenceEditorNG::IncidenceDialogFactory::create(true, KCalendarCore::IncidenceBase::TypeEvent, nullptr, this);
0345         dlg->setObjectName(QLatin1StringView("incidencedialog"));
0346         dlg->load(newEventItem);
0347         dlg->open();
0348     }
0349 }
0350 
0351 void KOEventPopupMenu::createNote(const Akonadi::Item &item)
0352 {
0353     mCurrentIncidence = item;
0354     createNote();
0355 }
0356 
0357 void KOEventPopupMenu::createNote()
0358 {
0359     // Must be a Incidence
0360     if (CalendarSupport::hasIncidence(mCurrentIncidence)) {
0361         KCalendarCore::Incidence::Ptr incidence(Akonadi::CalendarUtils::incidence(mCurrentIncidence));
0362         Akonadi::NoteUtils::NoteMessageWrapper note;
0363         note.setTitle(incidence->summary());
0364         note.setText(incidence->description(), incidence->descriptionIsRich() ? Qt::RichText : Qt::PlainText);
0365         note.setFrom(QCoreApplication::applicationName() + QCoreApplication::applicationVersion());
0366         note.setLastModifiedDate(QDateTime::currentDateTimeUtc());
0367         Akonadi::NoteUtils::Attachment attachment(mCurrentIncidence.url(), mCurrentIncidence.mimeType());
0368         note.attachments().append(attachment);
0369         Akonadi::Item newNoteItem;
0370         newNoteItem.setMimeType(Akonadi::NoteUtils::noteMimeType());
0371         newNoteItem.setPayload(note.message());
0372 
0373         auto noteedit = new CalendarSupport::NoteEditDialog(this);
0374         connect(noteedit, &CalendarSupport::NoteEditDialog::createNote, this, &KOEventPopupMenu::slotCreateNote);
0375         noteedit->load(newNoteItem);
0376         noteedit->show();
0377     }
0378 }
0379 
0380 void KOEventPopupMenu::slotCreateNote(const Akonadi::Item &noteItem, const Akonadi::Collection &collection)
0381 {
0382     auto createJob = new Akonadi::ItemCreateJob(noteItem, collection, this);
0383     connect(createJob, &Akonadi::ItemCreateJob::result, this, &KOEventPopupMenu::slotCreateNewNoteJobFinished);
0384     createJob->start();
0385 }
0386 
0387 void KOEventPopupMenu::slotCreateNewNoteJobFinished(KJob *job)
0388 {
0389     if (job->error()) {
0390         qCDebug(KORGANIZER_LOG) << "Error during create new Note " << job->errorString();
0391     }
0392 }
0393 
0394 void KOEventPopupMenu::createTodo()
0395 {
0396     // Must be a Incidence
0397     if (!CalendarSupport::hasIncidence(mCurrentIncidence)) {
0398         return;
0399     }
0400     // Todo->Todo doesn't make sense
0401     if (CalendarSupport::hasTodo(mCurrentIncidence)) {
0402         return;
0403     }
0404 
0405     if (CalendarSupport::hasEvent(mCurrentIncidence)) {
0406         KCalendarCore::Event::Ptr event(Akonadi::CalendarUtils::event(mCurrentIncidence));
0407         KCalendarCore::Todo::Ptr todo(new KCalendarCore::Todo(*event));
0408         todo->setUid(KCalendarCore::CalFormat::createUniqueId());
0409         todo->setDtStart(event->dtStart());
0410         todo->setAllDay(event->allDay());
0411         todo->setDtDue(event->dtEnd());
0412         Akonadi::Item newTodoItem;
0413         newTodoItem.setMimeType(KCalendarCore::Todo::todoMimeType());
0414         newTodoItem.setPayload<KCalendarCore::Todo::Ptr>(todo);
0415 
0416         IncidenceEditorNG::IncidenceDialog *dlg =
0417             IncidenceEditorNG::IncidenceDialogFactory::create(true, KCalendarCore::IncidenceBase::TypeTodo, nullptr, this);
0418         dlg->setObjectName(QLatin1StringView("incidencedialog"));
0419         dlg->load(newTodoItem);
0420         dlg->open();
0421     }
0422 }
0423 
0424 void KOEventPopupMenu::toggleTodoCompleted()
0425 {
0426     if (CalendarSupport::hasTodo(mCurrentIncidence)) {
0427         Q_EMIT toggleOccurrenceCompletedSignal(mCurrentIncidence, mCurrentDate);
0428     }
0429 }
0430 
0431 #include "moc_koeventpopupmenu.cpp"