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 ¬eItem, 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"