File indexing completed on 2024-04-28 05:11:33
0001 /* 0002 SPDX-FileCopyrightText: 2010 Casey Link <unnamedrambler@gmail.com> 0003 SPDX-FileCopyrightText: 2009-2010 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com> 0004 0005 Based on old attendeeeditor.cpp: 0006 SPDX-FileCopyrightText: 2000, 2001 Cornelius Schumacher <schumacher@kde.org> 0007 SPDX-FileCopyrightText: 2003-2004 Reinhold Kainhofer <reinhold@kainhofer.com> 0008 SPDX-FileCopyrightText: 2007 Volker Krause <vkrause@kde.org> 0009 0010 SPDX-License-Identifier: LGPL-2.0-or-later 0011 */ 0012 0013 #include "incidenceattendee.h" 0014 #include "attendeecomboboxdelegate.h" 0015 #include "attendeeeditor.h" 0016 #include "attendeelineeditdelegate.h" 0017 #include "attendeetablemodel.h" 0018 #include "conflictresolver.h" 0019 #include "editorconfig.h" 0020 #include "incidencedatetime.h" 0021 #include "schedulingdialog.h" 0022 #include "ui_dialogdesktop.h" 0023 #include <CalendarSupport/FreeBusyItemModel> 0024 0025 #include <Akonadi/AbstractEmailAddressSelectionDialog> 0026 #include <Akonadi/ContactGroupExpandJob> 0027 #include <Akonadi/ContactGroupSearchJob> 0028 #include <Akonadi/EmailAddressSelectionDialog> 0029 0030 #include <KCalUtils/Stringify> 0031 #include <KEmailAddress> 0032 #include <KPluginFactory> 0033 0034 #include "incidenceeditor_debug.h" 0035 #include <KLocalizedString> 0036 #include <KMessageBox> 0037 #include <QPointer> 0038 #include <QTreeView> 0039 0040 Q_DECLARE_METATYPE(IncidenceEditorNG::EditorConfig::Organizer) 0041 0042 using namespace IncidenceEditorNG; 0043 0044 IncidenceAttendee::IncidenceAttendee(QWidget *parent, IncidenceDateTime *dateTime, Ui::EventOrTodoDesktop *ui) 0045 : mUi(ui) 0046 , mParentWidget(parent) 0047 , mDateTime(dateTime) 0048 , mStateDelegate(new AttendeeComboBoxDelegate(this)) 0049 , mRoleDelegate(new AttendeeComboBoxDelegate(this)) 0050 , mResponseDelegate(new AttendeeComboBoxDelegate(this)) 0051 { 0052 mDataModel = new AttendeeTableModel(this); 0053 mDataModel->setKeepEmpty(true); 0054 mDataModel->setRemoveEmptyLines(true); 0055 mRoleDelegate->addItem(QIcon::fromTheme(QStringLiteral(":/meeting-participant.png")), 0056 KCalUtils::Stringify::attendeeRole(KCalendarCore::Attendee::ReqParticipant)); 0057 mRoleDelegate->addItem(QIcon::fromTheme(QStringLiteral(":/meeting-participant-optional.png")), 0058 KCalUtils::Stringify::attendeeRole(KCalendarCore::Attendee::OptParticipant)); 0059 mRoleDelegate->addItem(QIcon::fromTheme(QStringLiteral(":/meeting-observer.png")), 0060 KCalUtils::Stringify::attendeeRole(KCalendarCore::Attendee::NonParticipant)); 0061 mRoleDelegate->addItem(QIcon::fromTheme(QStringLiteral(":/meeting-chair.png")), KCalUtils::Stringify::attendeeRole(KCalendarCore::Attendee::Chair)); 0062 0063 mResponseDelegate->addItem(QIcon::fromTheme(QStringLiteral("meeting-participant-request-response")), i18nc("@item:inlistbox", "Request Response")); 0064 mResponseDelegate->addItem(QIcon::fromTheme(QStringLiteral("meeting-participant-no-response")), i18nc("@item:inlistbox", "Request No Response")); 0065 0066 mStateDelegate->setWhatsThis(i18nc("@info:whatsthis", "Edits the current attendance status of the attendee.")); 0067 0068 mRoleDelegate->setWhatsThis(i18nc("@info:whatsthis", "Edits the role of the attendee.")); 0069 0070 mResponseDelegate->setToolTip(i18nc("@info:tooltip", "Request a response from the attendee")); 0071 mResponseDelegate->setWhatsThis(i18nc("@info:whatsthis", 0072 "Edits whether to send an email to the " 0073 "attendee to request a response concerning " 0074 "attendance.")); 0075 0076 setObjectName(QLatin1StringView("IncidenceAttendee")); 0077 0078 auto filterProxyModel = new AttendeeFilterProxyModel(this); 0079 filterProxyModel->setDynamicSortFilter(true); 0080 filterProxyModel->setSourceModel(mDataModel); 0081 0082 connect(mUi->mGroupSubstitution, &QPushButton::clicked, this, &IncidenceAttendee::slotGroupSubstitutionPressed); 0083 0084 mUi->mAttendeeTable->setModel(filterProxyModel); 0085 0086 mAttendeeDelegate = new AttendeeLineEditDelegate(this); 0087 0088 mUi->mAttendeeTable->setItemDelegateForColumn(AttendeeTableModel::Role, roleDelegate()); 0089 mUi->mAttendeeTable->setItemDelegateForColumn(AttendeeTableModel::FullName, attendeeDelegate()); 0090 mUi->mAttendeeTable->setItemDelegateForColumn(AttendeeTableModel::Status, stateDelegate()); 0091 mUi->mAttendeeTable->setItemDelegateForColumn(AttendeeTableModel::Response, responseDelegate()); 0092 0093 mUi->mOrganizerStack->setCurrentIndex(0); 0094 0095 fillOrganizerCombo(); 0096 slotUpdateCryptoPreferences(); 0097 mUi->mSolveButton->setEnabled(false); 0098 mUi->mOrganizerLabel->setVisible(false); 0099 0100 mConflictResolver = new ConflictResolver(parent, parent); 0101 mConflictResolver->setEarliestDate(mDateTime->startDate()); 0102 mConflictResolver->setEarliestTime(mDateTime->startTime()); 0103 mConflictResolver->setLatestDate(mDateTime->endDate()); 0104 mConflictResolver->setLatestTime(mDateTime->endTime()); 0105 0106 connect(mUi->mSelectButton, &QPushButton::clicked, this, &IncidenceAttendee::slotSelectAddresses); 0107 connect(mUi->mSolveButton, &QPushButton::clicked, this, &IncidenceAttendee::slotSolveConflictPressed); 0108 /* Added as part of kolab/issue2297, which is currently under review 0109 connect(mUi->mOrganizerCombo, qOverload<const QString &>(&QComboBox::activated), 0110 this, &IncidenceAttendee::slotOrganizerChanged); 0111 */ 0112 connect(mUi->mOrganizerCombo, &QComboBox::currentIndexChanged, this, &IncidenceAttendee::checkDirtyStatus); 0113 connect(mUi->mOrganizerCombo, &QComboBox::currentIndexChanged, this, &IncidenceAttendee::slotUpdateCryptoPreferences); 0114 0115 connect(mDateTime, &IncidenceDateTime::startDateChanged, this, &IncidenceAttendee::slotEventDurationChanged); 0116 connect(mDateTime, &IncidenceDateTime::endDateChanged, this, &IncidenceAttendee::slotEventDurationChanged); 0117 connect(mDateTime, &IncidenceDateTime::startTimeChanged, this, &IncidenceAttendee::slotEventDurationChanged); 0118 connect(mDateTime, &IncidenceDateTime::endTimeChanged, this, &IncidenceAttendee::slotEventDurationChanged); 0119 0120 connect(mConflictResolver, &ConflictResolver::conflictsDetected, this, &IncidenceAttendee::slotUpdateConflictLabel); 0121 0122 connect(mConflictResolver->model(), &QAbstractItemModel::rowsInserted, this, &IncidenceAttendee::slotFreeBusyAdded); 0123 connect(mConflictResolver->model(), &QAbstractItemModel::layoutChanged, this, qOverload<>(&IncidenceAttendee::updateFBStatus)); 0124 connect(mConflictResolver->model(), &QAbstractItemModel::dataChanged, this, &IncidenceAttendee::slotFreeBusyChanged); 0125 0126 slotUpdateConflictLabel(0); // initialize label 0127 0128 // conflict resolver (should show also resources) 0129 connect(mDataModel, &AttendeeTableModel::layoutChanged, this, &IncidenceAttendee::slotConflictResolverLayoutChanged); 0130 connect(mDataModel, &AttendeeTableModel::modelReset, this, &IncidenceAttendee::slotConflictResolverLayoutChanged); 0131 connect(mDataModel, &AttendeeTableModel::rowsAboutToBeRemoved, this, &IncidenceAttendee::slotConflictResolverAttendeeRemoved); 0132 connect(mDataModel, &AttendeeTableModel::rowsInserted, this, &IncidenceAttendee::slotConflictResolverAttendeeAdded); 0133 connect(mDataModel, &AttendeeTableModel::dataChanged, this, &IncidenceAttendee::slotConflictResolverAttendeeChanged); 0134 0135 // Group substitution 0136 connect(filterProxyModel, &AttendeeFilterProxyModel::layoutChanged, this, &IncidenceAttendee::slotGroupSubstitutionLayoutChanged); 0137 connect(filterProxyModel, &AttendeeFilterProxyModel::modelReset, this, &IncidenceAttendee::slotGroupSubstitutionLayoutChanged); 0138 connect(filterProxyModel, &AttendeeFilterProxyModel::rowsAboutToBeRemoved, this, &IncidenceAttendee::slotGroupSubstitutionAttendeeRemoved); 0139 connect(filterProxyModel, &AttendeeFilterProxyModel::rowsInserted, this, &IncidenceAttendee::slotGroupSubstitutionAttendeeAdded); 0140 connect(filterProxyModel, &AttendeeFilterProxyModel::dataChanged, this, &IncidenceAttendee::slotGroupSubstitutionAttendeeChanged); 0141 0142 connect(filterProxyModel, &AttendeeFilterProxyModel::rowsInserted, this, &IncidenceAttendee::updateCount); 0143 connect(filterProxyModel, &AttendeeFilterProxyModel::rowsRemoved, this, &IncidenceAttendee::updateCount); 0144 // only update when FullName is changed 0145 connect(filterProxyModel, &AttendeeFilterProxyModel::dataChanged, this, &IncidenceAttendee::updateCount); 0146 connect(filterProxyModel, &AttendeeFilterProxyModel::layoutChanged, this, &IncidenceAttendee::updateCount); 0147 connect(filterProxyModel, &AttendeeFilterProxyModel::layoutChanged, this, &IncidenceAttendee::filterLayoutChanged); 0148 connect(filterProxyModel, &AttendeeFilterProxyModel::modelReset, this, &IncidenceAttendee::updateCount); 0149 connect(filterProxyModel, &AttendeeFilterProxyModel::modelReset, this, &IncidenceAttendee::filterLayoutChanged); 0150 } 0151 0152 IncidenceAttendee::~IncidenceAttendee() = default; 0153 0154 void IncidenceAttendee::load(const KCalendarCore::Incidence::Ptr &incidence) 0155 { 0156 mLoadedIncidence = incidence; 0157 0158 if (iAmOrganizer() || incidence->organizer().isEmpty()) { 0159 mUi->mOrganizerStack->setCurrentIndex(0); 0160 0161 int found = -1; 0162 const QString fullOrganizer = incidence->organizer().fullName(); 0163 const QString organizerEmail = incidence->organizer().email(); 0164 for (int i = 0; i < mUi->mOrganizerCombo->count(); ++i) { 0165 KCalendarCore::Person organizerCandidate = KCalendarCore::Person::fromFullName(mUi->mOrganizerCombo->itemText(i)); 0166 if (organizerCandidate.email() == organizerEmail) { 0167 found = i; 0168 mUi->mOrganizerCombo->setCurrentIndex(i); 0169 break; 0170 } 0171 } 0172 if (found < 0 && !fullOrganizer.isEmpty()) { 0173 mUi->mOrganizerCombo->insertItem(0, fullOrganizer); 0174 mUi->mOrganizerCombo->setCurrentIndex(0); 0175 } 0176 0177 mUi->mOrganizerLabel->setVisible(false); 0178 } else { // someone else is the organizer 0179 mUi->mOrganizerStack->setCurrentIndex(1); 0180 mUi->mOrganizerLabel->setText(incidence->organizer().fullName()); 0181 mUi->mOrganizerLabel->setVisible(true); 0182 } 0183 0184 KCalendarCore::Attendee::List attendees; 0185 const KCalendarCore::Attendee::List incidenceAttendees = incidence->attendees(); 0186 attendees.reserve(incidenceAttendees.count()); 0187 for (const KCalendarCore::Attendee &a : incidenceAttendees) { 0188 attendees << KCalendarCore::Attendee(a); 0189 } 0190 0191 mDataModel->setAttendees(attendees); 0192 slotUpdateConflictLabel(0); 0193 0194 setActions(incidence->type()); 0195 0196 mWasDirty = false; 0197 } 0198 0199 void IncidenceAttendee::save(const KCalendarCore::Incidence::Ptr &incidence) 0200 { 0201 incidence->clearAttendees(); 0202 const KCalendarCore::Attendee::List attendees = mDataModel->attendees(); 0203 0204 for (const KCalendarCore::Attendee &attendee : attendees) { 0205 bool skip = false; 0206 if (attendee.fullName().isEmpty()) { 0207 continue; 0208 } 0209 if (KEmailAddress::isValidAddress(attendee.email())) { 0210 if (KMessageBox::warningTwoActions(nullptr, 0211 i18nc("@info", 0212 "%1 does not look like a valid email address. " 0213 "Are you sure you want to invite this participant?", 0214 attendee.email()), 0215 i18nc("@title:window", "Invalid Email Address"), 0216 KGuiItem(i18nc("@action:button", "Invite"), QStringLiteral("dialog-ok")), 0217 KGuiItem(i18nc("@action:button", "Do Not Invite"), QStringLiteral("dialog-cancel"))) 0218 != KMessageBox::ButtonCode::PrimaryAction) { 0219 skip = true; 0220 } 0221 } 0222 if (!skip) { 0223 incidence->addAttendee(attendee); 0224 } 0225 } 0226 0227 // Must not have an organizer for items without attendees 0228 if (!incidence->attendeeCount()) { 0229 return; 0230 } 0231 0232 if (mUi->mOrganizerStack->currentIndex() == 0) { 0233 incidence->setOrganizer(mUi->mOrganizerCombo->currentText()); 0234 } else { 0235 incidence->setOrganizer(mUi->mOrganizerLabel->text()); 0236 } 0237 } 0238 0239 bool IncidenceAttendee::isDirty() const 0240 { 0241 if (iAmOrganizer()) { 0242 KCalendarCore::Event tmp; 0243 tmp.setOrganizer(mUi->mOrganizerCombo->currentText()); 0244 0245 if (mLoadedIncidence->organizer().email() != tmp.organizer().email()) { 0246 qCDebug(INCIDENCEEDITOR_LOG) << "Organizer changed. Old was " << mLoadedIncidence->organizer().name() << mLoadedIncidence->organizer().email() 0247 << "; new is " << tmp.organizer().name() << tmp.organizer().email(); 0248 return true; 0249 } 0250 } 0251 0252 const KCalendarCore::Attendee::List originalList = mLoadedIncidence->attendees(); 0253 KCalendarCore::Attendee::List newList; 0254 0255 const auto lstAttendees = mDataModel->attendees(); 0256 for (const KCalendarCore::Attendee &attendee : lstAttendees) { 0257 if (!attendee.fullName().isEmpty()) { 0258 newList.append(attendee); 0259 } 0260 } 0261 0262 // The lists sizes *must* be the same. When the organizer is attending the 0263 // event as well, he should be in the attendees list as well. 0264 if (originalList.size() != newList.size()) { 0265 return true; 0266 } 0267 0268 // Okay, again not the most efficient algorithm, but I'm assuming that in the 0269 // bulk of the use cases, the number of attendees is not much higher than 10 or so. 0270 for (const KCalendarCore::Attendee &attendee : originalList) { 0271 bool found = false; 0272 for (int i = 0; i < newList.count(); ++i) { 0273 if (newList[i] == attendee) { 0274 newList.remove(i); 0275 found = true; 0276 break; 0277 } 0278 } 0279 0280 if (!found) { 0281 // One of the attendees in the original list was not found in the new list. 0282 return true; 0283 } 0284 } 0285 0286 return false; 0287 } 0288 0289 void IncidenceAttendee::changeStatusForMe(KCalendarCore::Attendee::PartStat stat) 0290 { 0291 const IncidenceEditorNG::EditorConfig *config = IncidenceEditorNG::EditorConfig::instance(); 0292 Q_ASSERT(config); 0293 0294 for (int i = 0; i < mDataModel->rowCount(); ++i) { 0295 QModelIndex index = mDataModel->index(i, AttendeeTableModel::Email); 0296 if (config->thatIsMe(mDataModel->data(index, Qt::DisplayRole).toString())) { 0297 index = mDataModel->index(i, AttendeeTableModel::Status); 0298 mDataModel->setData(index, stat); 0299 break; 0300 } 0301 } 0302 0303 checkDirtyStatus(); 0304 } 0305 0306 void IncidenceAttendee::acceptForMe() 0307 { 0308 changeStatusForMe(KCalendarCore::Attendee::Accepted); 0309 } 0310 0311 void IncidenceAttendee::declineForMe() 0312 { 0313 changeStatusForMe(KCalendarCore::Attendee::Declined); 0314 } 0315 0316 void IncidenceAttendee::fillOrganizerCombo() 0317 { 0318 mUi->mOrganizerCombo->clear(); 0319 const auto organizers = IncidenceEditorNG::EditorConfig::instance()->allOrganizers(); 0320 for (auto organizer = organizers.cbegin(), end = organizers.cend(); organizer != end; ++organizer) { 0321 if (std::any_of(organizers.cbegin(), organizer, [organizer](const auto &pastOrg) { 0322 return organizer->email == pastOrg.email && organizer->name == pastOrg.name; 0323 })) { 0324 continue; 0325 } 0326 0327 mUi->mOrganizerCombo->addItem(QStringLiteral("%1 <%2>").arg(organizer->name, organizer->email), QVariant::fromValue(*organizer)); 0328 } 0329 } 0330 0331 void IncidenceAttendee::checkIfExpansionIsNeeded(const KCalendarCore::Attendee &attendee) 0332 { 0333 QString fullname = attendee.fullName(); 0334 0335 // stop old job 0336 KJob *oldJob = mMightBeGroupJobs.key(attendee.uid()); 0337 if (oldJob != nullptr) { 0338 disconnect(oldJob); 0339 oldJob->deleteLater(); 0340 mMightBeGroupJobs.remove(oldJob); 0341 } 0342 0343 mGroupList.remove(attendee.uid()); 0344 0345 if (!fullname.isEmpty()) { 0346 auto job = new Akonadi::ContactGroupSearchJob(); 0347 job->setQuery(Akonadi::ContactGroupSearchJob::Name, fullname); 0348 connect(job, &Akonadi::ContactGroupSearchJob::result, this, &IncidenceAttendee::groupSearchResult); 0349 0350 mMightBeGroupJobs.insert(job, attendee.uid()); 0351 } 0352 } 0353 0354 void IncidenceAttendee::groupSearchResult(KJob *job) 0355 { 0356 auto searchJob = qobject_cast<Akonadi::ContactGroupSearchJob *>(job); 0357 Q_ASSERT(searchJob); 0358 0359 Q_ASSERT(mMightBeGroupJobs.contains(job)); 0360 const auto uid = mMightBeGroupJobs.take(job); 0361 0362 const KContacts::ContactGroup::List contactGroups = searchJob->contactGroups(); 0363 if (contactGroups.isEmpty()) { 0364 updateGroupExpand(); 0365 return; // Nothing todo, probably a normal email address was entered 0366 } 0367 0368 // TODO: Give the user the possibility to choose a group when there is more than one?! 0369 KContacts::ContactGroup group = contactGroups.first(); 0370 0371 const int row = rowOfAttendee(uid); 0372 QModelIndex index = dataModel()->index(row, AttendeeTableModel::CuType); 0373 dataModel()->setData(index, KCalendarCore::Attendee::Group); 0374 0375 mGroupList.insert(uid, group); 0376 updateGroupExpand(); 0377 } 0378 0379 void IncidenceAttendee::updateGroupExpand() 0380 { 0381 mUi->mGroupSubstitution->setEnabled(!mGroupList.isEmpty()); 0382 } 0383 0384 void IncidenceAttendee::slotGroupSubstitutionPressed() 0385 { 0386 for (auto it = mGroupList.cbegin(), end = mGroupList.cend(); it != end; ++it) { 0387 auto expandJob = new Akonadi::ContactGroupExpandJob(it.value(), this); 0388 connect(expandJob, &Akonadi::ContactGroupExpandJob::result, this, &IncidenceAttendee::expandResult); 0389 mExpandGroupJobs.insert(expandJob, it.key()); 0390 expandJob->start(); 0391 } 0392 } 0393 0394 void IncidenceAttendee::expandResult(KJob *job) 0395 { 0396 auto expandJob = qobject_cast<Akonadi::ContactGroupExpandJob *>(job); 0397 Q_ASSERT(expandJob); 0398 Q_ASSERT(mExpandGroupJobs.contains(job)); 0399 const auto uid = mExpandGroupJobs.take(job); 0400 const int row = rowOfAttendee(uid); 0401 const auto attendee = dataModel()->attendees().at(row); 0402 const QString currentEmail = attendee.email(); 0403 const KContacts::Addressee::List groupMembers = expandJob->contacts(); 0404 bool wasACorrectEmail = false; 0405 for (const KContacts::Addressee &member : groupMembers) { 0406 if (member.preferredEmail() == currentEmail) { 0407 wasACorrectEmail = true; 0408 break; 0409 } 0410 } 0411 0412 if (!wasACorrectEmail) { 0413 dataModel()->removeRow(row); 0414 for (const KContacts::Addressee &member : groupMembers) { 0415 KCalendarCore::Attendee newAt(member.realName(), member.preferredEmail(), attendee.RSVP(), attendee.status(), attendee.role(), member.uid()); 0416 dataModel()->insertAttendee(row, newAt); 0417 } 0418 } 0419 } 0420 0421 void IncidenceAttendee::insertAddresses(const KContacts::Addressee::List &list) 0422 { 0423 for (const KContacts::Addressee &contact : list) { 0424 insertAttendeeFromAddressee(contact); 0425 } 0426 } 0427 0428 void IncidenceAttendee::slotSelectAddresses() 0429 { 0430 QPointer<Akonadi::AbstractEmailAddressSelectionDialog> dialog; 0431 const KPluginMetaData editWidgetPlugin(QStringLiteral("pim6/akonadi/emailaddressselectionldapdialogplugin")); 0432 0433 const auto result = KPluginFactory::instantiatePlugin<Akonadi::AbstractEmailAddressSelectionDialog>(editWidgetPlugin, mParentWidget); 0434 if (result) { 0435 dialog = result.plugin; 0436 0437 } else { 0438 dialog = new Akonadi::EmailAddressSelectionDialog(mParentWidget); 0439 } 0440 dialog->view()->view()->setSelectionMode(QAbstractItemView::ExtendedSelection); 0441 dialog->setWindowTitle(i18nc("@title:window", "Select Attendees")); 0442 connect(dialog.data(), &Akonadi::AbstractEmailAddressSelectionDialog::insertAddresses, this, &IncidenceEditorNG::IncidenceAttendee::insertAddresses); 0443 if (dialog->exec() == QDialog::Accepted) { 0444 const Akonadi::EmailAddressSelection::List list = dialog->selectedAddresses(); 0445 for (const Akonadi::EmailAddressSelection &selection : list) { 0446 if (selection.item().hasPayload<KContacts::ContactGroup>()) { 0447 auto job = new Akonadi::ContactGroupExpandJob(selection.item().payload<KContacts::ContactGroup>(), this); 0448 connect(job, &Akonadi::ContactGroupExpandJob::result, this, &IncidenceAttendee::expandResult); 0449 KCalendarCore::Attendee::PartStat partStat = KCalendarCore::Attendee::NeedsAction; 0450 bool rsvp = true; 0451 0452 int pos = 0; 0453 QString name; 0454 QString email; 0455 KEmailAddress::extractEmailAddressAndName(selection.email(), email, name); 0456 KCalendarCore::Attendee newAt(selection.name(), email, rsvp, partStat, KCalendarCore::Attendee::ReqParticipant); 0457 dataModel()->insertAttendee(pos, newAt); 0458 0459 mExpandGroupJobs.insert(job, newAt.uid()); 0460 job->start(); 0461 } else { 0462 KContacts::Addressee contact; 0463 contact.setName(selection.name()); 0464 contact.addEmail(KContacts::Email(selection.email())); 0465 0466 if (selection.item().hasPayload<KContacts::Addressee>()) { 0467 contact.setUid(selection.item().payload<KContacts::Addressee>().uid()); 0468 } 0469 insertAttendeeFromAddressee(contact); 0470 } 0471 } 0472 } 0473 delete dialog; 0474 } 0475 0476 void IncidenceEditorNG::IncidenceAttendee::slotSolveConflictPressed() 0477 { 0478 const int duration = mDateTime->startTime().secsTo(mDateTime->endTime()); 0479 QScopedPointer<SchedulingDialog> dialog(new SchedulingDialog(mDateTime->startDate(), mDateTime->startTime(), duration, mConflictResolver, mParentWidget)); 0480 dialog->slotUpdateIncidenceStartEnd(mDateTime->currentStartDateTime(), mDateTime->currentEndDateTime()); 0481 if (dialog->exec() == QDialog::Accepted) { 0482 qCDebug(INCIDENCEEDITOR_LOG) << dialog->selectedStartDate() << dialog->selectedStartTime(); 0483 if (dialog->selectedStartDate().isValid() && dialog->selectedStartTime().isValid()) { 0484 mDateTime->setStartDate(dialog->selectedStartDate()); 0485 mDateTime->setStartTime(dialog->selectedStartTime()); 0486 } 0487 } 0488 } 0489 0490 void IncidenceAttendee::slotConflictResolverAttendeeChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight) 0491 { 0492 if (AttendeeTableModel::FullName <= bottomRight.column() && AttendeeTableModel::FullName >= topLeft.column()) { 0493 for (int i = topLeft.row(); i <= bottomRight.row(); ++i) { 0494 QModelIndex email = dataModel()->index(i, AttendeeTableModel::Email); 0495 auto attendee = dataModel()->data(email, AttendeeTableModel::AttendeeRole).value<KCalendarCore::Attendee>(); 0496 if (mConflictResolver->containsAttendee(attendee)) { 0497 mConflictResolver->removeAttendee(attendee); 0498 } 0499 if (!dataModel()->data(email).toString().isEmpty()) { 0500 mConflictResolver->insertAttendee(attendee); 0501 } 0502 } 0503 } 0504 checkDirtyStatus(); 0505 } 0506 0507 void IncidenceAttendee::slotConflictResolverAttendeeAdded(const QModelIndex &index, int first, int last) 0508 { 0509 for (int i = first; i <= last; ++i) { 0510 QModelIndex email = dataModel()->index(i, AttendeeTableModel::Email, index); 0511 if (!dataModel()->data(email).toString().isEmpty()) { 0512 mConflictResolver->insertAttendee(dataModel()->data(email, AttendeeTableModel::AttendeeRole).value<KCalendarCore::Attendee>()); 0513 } 0514 } 0515 checkDirtyStatus(); 0516 } 0517 0518 void IncidenceAttendee::slotConflictResolverAttendeeRemoved(const QModelIndex &index, int first, int last) 0519 { 0520 for (int i = first; i <= last; ++i) { 0521 QModelIndex email = dataModel()->index(i, AttendeeTableModel::Email, index); 0522 if (!dataModel()->data(email).toString().isEmpty()) { 0523 mConflictResolver->removeAttendee(dataModel()->data(email, AttendeeTableModel::AttendeeRole).value<KCalendarCore::Attendee>()); 0524 } 0525 } 0526 checkDirtyStatus(); 0527 } 0528 0529 void IncidenceAttendee::slotConflictResolverLayoutChanged() 0530 { 0531 const KCalendarCore::Attendee::List attendees = mDataModel->attendees(); 0532 mConflictResolver->clearAttendees(); 0533 for (const KCalendarCore::Attendee &attendee : attendees) { 0534 if (!attendee.email().isEmpty()) { 0535 mConflictResolver->insertAttendee(attendee); 0536 } 0537 } 0538 checkDirtyStatus(); 0539 } 0540 0541 void IncidenceAttendee::slotFreeBusyAdded(const QModelIndex &parent, int first, int last) 0542 { 0543 // We are only interested in toplevel changes 0544 if (parent.isValid()) { 0545 return; 0546 } 0547 QAbstractItemModel *model = mConflictResolver->model(); 0548 for (int i = first; i <= last; ++i) { 0549 QModelIndex index = model->index(i, 0, parent); 0550 const KCalendarCore::Attendee &attendee = model->data(index, CalendarSupport::FreeBusyItemModel::AttendeeRole).value<KCalendarCore::Attendee>(); 0551 const KCalendarCore::FreeBusy::Ptr &fb = model->data(index, CalendarSupport::FreeBusyItemModel::FreeBusyRole).value<KCalendarCore::FreeBusy::Ptr>(); 0552 if (!attendee.isNull()) { 0553 updateFBStatus(attendee, fb); 0554 } 0555 } 0556 } 0557 0558 void IncidenceAttendee::slotFreeBusyChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight) 0559 { 0560 // We are only interested in toplevel changes 0561 if (topLeft.parent().isValid()) { 0562 return; 0563 } 0564 QAbstractItemModel *model = mConflictResolver->model(); 0565 for (int i = topLeft.row(); i <= bottomRight.row(); ++i) { 0566 QModelIndex index = model->index(i, 0); 0567 const KCalendarCore::Attendee &attendee = model->data(index, CalendarSupport::FreeBusyItemModel::AttendeeRole).value<KCalendarCore::Attendee>(); 0568 const KCalendarCore::FreeBusy::Ptr &fb = model->data(index, CalendarSupport::FreeBusyItemModel::FreeBusyRole).value<KCalendarCore::FreeBusy::Ptr>(); 0569 if (!attendee.isNull()) { 0570 updateFBStatus(attendee, fb); 0571 } 0572 } 0573 } 0574 0575 void IncidenceAttendee::updateFBStatus() 0576 { 0577 QAbstractItemModel *model = mConflictResolver->model(); 0578 for (int i = 0; i < model->rowCount(); ++i) { 0579 QModelIndex index = model->index(i, 0); 0580 const KCalendarCore::Attendee &attendee = model->data(index, CalendarSupport::FreeBusyItemModel::AttendeeRole).value<KCalendarCore::Attendee>(); 0581 const KCalendarCore::FreeBusy::Ptr &fb = model->data(index, CalendarSupport::FreeBusyItemModel::FreeBusyRole).value<KCalendarCore::FreeBusy::Ptr>(); 0582 if (!attendee.isNull()) { 0583 updateFBStatus(attendee, fb); 0584 } 0585 } 0586 } 0587 0588 void IncidenceAttendee::updateFBStatus(const KCalendarCore::Attendee &attendee, const KCalendarCore::FreeBusy::Ptr &fb) 0589 { 0590 KCalendarCore::Attendee::List attendees = mDataModel->attendees(); 0591 QDateTime startTime = mDateTime->currentStartDateTime(); 0592 QDateTime endTime = mDateTime->currentEndDateTime(); 0593 if (attendees.contains(attendee)) { 0594 int row = dataModel()->attendees().indexOf(attendee); 0595 QModelIndex attendeeIndex = dataModel()->index(row, AttendeeTableModel::Available); 0596 if (fb) { 0597 KCalendarCore::Period::List busyPeriods = fb->busyPeriods(); 0598 for (auto it = busyPeriods.begin(); it != busyPeriods.end(); ++it) { 0599 // periods started before and lapping into the incidence (s < startTime && e >= startTime) 0600 // periods starting in the time of incidence (s >= startTime && s <= endTime) 0601 if (((*it).start() < startTime && (*it).end() > startTime) || ((*it).start() >= startTime && (*it).start() <= endTime)) { 0602 switch (attendee.status()) { 0603 case KCalendarCore::Attendee::Accepted: 0604 dataModel()->setData(attendeeIndex, AttendeeTableModel::Accepted); 0605 return; 0606 default: 0607 dataModel()->setData(attendeeIndex, AttendeeTableModel::Busy); 0608 return; 0609 } 0610 } 0611 } 0612 dataModel()->setData(attendeeIndex, AttendeeTableModel::Free); 0613 } else { 0614 dataModel()->setData(attendeeIndex, AttendeeTableModel::Unknown); 0615 } 0616 } 0617 } 0618 0619 void IncidenceAttendee::slotUpdateConflictLabel(int count) 0620 { 0621 if (attendeeCount() > 0) { 0622 mUi->mSolveButton->setEnabled(true); 0623 if (count > 0) { 0624 QString label = i18ncp("@label Shows the number of scheduling conflicts", "%1 conflict", "%1 conflicts", count); 0625 mUi->mConflictsLabel->setText(label); 0626 mUi->mConflictsLabel->setVisible(true); 0627 } else { 0628 mUi->mConflictsLabel->setVisible(false); 0629 } 0630 } else { 0631 mUi->mSolveButton->setEnabled(false); 0632 mUi->mConflictsLabel->setVisible(false); 0633 } 0634 } 0635 0636 void IncidenceAttendee::slotGroupSubstitutionAttendeeChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight) 0637 { 0638 if (AttendeeTableModel::FullName <= bottomRight.column() && AttendeeTableModel::FullName >= topLeft.column()) { 0639 for (int i = topLeft.row(); i <= bottomRight.row(); ++i) { 0640 QModelIndex email = dataModel()->index(i, AttendeeTableModel::Email); 0641 auto attendee = dataModel()->data(email, AttendeeTableModel::AttendeeRole).value<KCalendarCore::Attendee>(); 0642 checkIfExpansionIsNeeded(attendee); 0643 } 0644 } 0645 updateGroupExpand(); 0646 } 0647 0648 void IncidenceAttendee::slotGroupSubstitutionAttendeeAdded(const QModelIndex &index, int first, int last) 0649 { 0650 Q_UNUSED(index) 0651 for (int i = first; i <= last; ++i) { 0652 QModelIndex email = dataModel()->index(i, AttendeeTableModel::Email); 0653 auto attendee = dataModel()->data(email, AttendeeTableModel::AttendeeRole).value<KCalendarCore::Attendee>(); 0654 checkIfExpansionIsNeeded(attendee); 0655 } 0656 updateGroupExpand(); 0657 } 0658 0659 void IncidenceAttendee::slotGroupSubstitutionAttendeeRemoved(const QModelIndex &index, int first, int last) 0660 { 0661 Q_UNUSED(index) 0662 for (int i = first; i <= last; ++i) { 0663 QModelIndex email = dataModel()->index(i, AttendeeTableModel::Email); 0664 auto attendee = dataModel()->data(email, AttendeeTableModel::AttendeeRole).value<KCalendarCore::Attendee>(); 0665 KJob *job = mMightBeGroupJobs.key(attendee.uid()); 0666 if (job) { 0667 disconnect(job); 0668 job->deleteLater(); 0669 mMightBeGroupJobs.remove(job); 0670 } 0671 job = mExpandGroupJobs.key(attendee.uid()); 0672 if (job) { 0673 disconnect(job); 0674 job->deleteLater(); 0675 mExpandGroupJobs.remove(job); 0676 } 0677 mGroupList.remove(attendee.uid()); 0678 } 0679 updateGroupExpand(); 0680 } 0681 0682 void IncidenceAttendee::slotGroupSubstitutionLayoutChanged() 0683 { 0684 for (auto it = mMightBeGroupJobs.cbegin(), end = mMightBeGroupJobs.cend(); it != end; ++it) { 0685 KJob *job = it.key(); 0686 disconnect(job); 0687 job->deleteLater(); 0688 } 0689 0690 for (auto it = mExpandGroupJobs.cbegin(), end = mExpandGroupJobs.cend(); it != end; ++it) { 0691 KJob *job = it.key(); 0692 disconnect(job); 0693 job->deleteLater(); 0694 } 0695 mMightBeGroupJobs.clear(); 0696 mExpandGroupJobs.clear(); 0697 mGroupList.clear(); 0698 0699 QAbstractItemModel *model = mUi->mAttendeeTable->model(); 0700 if (!model) { 0701 return; 0702 } 0703 for (int i = 0; i < model->rowCount(QModelIndex()); ++i) { 0704 QModelIndex index = model->index(i, AttendeeTableModel::FullName); 0705 if (!model->data(index).toString().isEmpty()) { 0706 QModelIndex email = dataModel()->index(i, AttendeeTableModel::Email); 0707 auto attendee = dataModel()->data(email, AttendeeTableModel::AttendeeRole).value<KCalendarCore::Attendee>(); 0708 checkIfExpansionIsNeeded(attendee); 0709 } 0710 } 0711 0712 updateGroupExpand(); 0713 } 0714 0715 bool IncidenceAttendee::iAmOrganizer() const 0716 { 0717 if (mLoadedIncidence) { 0718 const IncidenceEditorNG::EditorConfig *config = IncidenceEditorNG::EditorConfig::instance(); 0719 return config->thatIsMe(mLoadedIncidence->organizer().email()); 0720 } 0721 0722 return true; 0723 } 0724 0725 void IncidenceAttendee::insertAttendeeFromAddressee(const KContacts::Addressee &a, int pos /*=-1*/) 0726 { 0727 const bool sameAsOrganizer = mUi->mOrganizerCombo && KEmailAddress::compareEmail(a.preferredEmail(), mUi->mOrganizerCombo->currentText(), false); 0728 KCalendarCore::Attendee::PartStat partStat = KCalendarCore::Attendee::NeedsAction; 0729 bool rsvp = true; 0730 0731 if (iAmOrganizer() && sameAsOrganizer) { 0732 partStat = KCalendarCore::Attendee::Accepted; 0733 rsvp = false; 0734 } 0735 QString name; 0736 QString email; 0737 KEmailAddress::extractEmailAddressAndName(a.preferredEmail(), email, name); 0738 0739 KCalendarCore::Attendee newAt(a.realName(), email, rsvp, partStat, KCalendarCore::Attendee::ReqParticipant, a.uid()); 0740 if (pos < 0) { 0741 pos = dataModel()->rowCount() - 1; 0742 } 0743 0744 dataModel()->insertAttendee(pos, newAt); 0745 } 0746 0747 void IncidenceAttendee::slotEventDurationChanged() 0748 { 0749 const QDateTime start = mDateTime->currentStartDateTime(); 0750 const QDateTime end = mDateTime->currentEndDateTime(); 0751 0752 if (start >= end) { // This can happen, especially for todos. 0753 return; 0754 } 0755 0756 mConflictResolver->setEarliestDateTime(start); 0757 mConflictResolver->setLatestDateTime(end); 0758 updateFBStatus(); 0759 } 0760 0761 void IncidenceAttendee::slotOrganizerChanged(const QString &newOrganizer) 0762 { 0763 if (KEmailAddress::compareEmail(newOrganizer, mOrganizer, false)) { 0764 return; 0765 } 0766 0767 QString name; 0768 QString email; 0769 bool success = KEmailAddress::extractEmailAddressAndName(newOrganizer, email, name); 0770 0771 if (!success) { 0772 qCWarning(INCIDENCEEDITOR_LOG) << "Could not extract email address and name"; 0773 return; 0774 } 0775 0776 int currentOrganizerAttendee = -1; 0777 int newOrganizerAttendee = -1; 0778 0779 for (int i = 0; i < mDataModel->rowCount(); ++i) { 0780 QModelIndex index = mDataModel->index(i, AttendeeTableModel::FullName); 0781 QString fullName = mDataModel->data(index, Qt::DisplayRole).toString(); 0782 if (fullName == mOrganizer) { 0783 currentOrganizerAttendee = i; 0784 } 0785 0786 if (fullName == newOrganizer) { 0787 newOrganizerAttendee = i; 0788 } 0789 } 0790 0791 int answer; 0792 if (currentOrganizerAttendee > -1) { 0793 answer = KMessageBox::questionTwoActions(mParentWidget, 0794 i18nc("@option", 0795 "You are changing the organizer of this event. " 0796 "Since the organizer is also attending this event, would you " 0797 "like to change the corresponding attendee as well?"), 0798 QString(), 0799 KGuiItem(i18nc("@action:button", "Change Attendee"), QStringLiteral("dialog-ok")), 0800 KGuiItem(i18nc("@action:button", "Do Not Change"), QStringLiteral("dialog-cancel"))); 0801 } else { 0802 answer = KMessageBox::ButtonCode::PrimaryAction; 0803 } 0804 0805 if (answer == KMessageBox::ButtonCode::PrimaryAction) { 0806 if (currentOrganizerAttendee > -1) { 0807 mDataModel->removeRows(currentOrganizerAttendee, 1); 0808 } 0809 0810 if (newOrganizerAttendee == -1) { 0811 bool rsvp = !iAmOrganizer(); // if it is the user, don't make him rsvp. 0812 KCalendarCore::Attendee::PartStat status = iAmOrganizer() ? KCalendarCore::Attendee::Accepted : KCalendarCore::Attendee::NeedsAction; 0813 0814 KCalendarCore::Attendee newAt(name, email, rsvp, status, KCalendarCore::Attendee::ReqParticipant); 0815 0816 mDataModel->insertAttendee(mDataModel->rowCount(), newAt); 0817 } 0818 } 0819 mOrganizer = newOrganizer; 0820 } 0821 0822 AttendeeTableModel *IncidenceAttendee::dataModel() const 0823 { 0824 return mDataModel; 0825 } 0826 0827 AttendeeComboBoxDelegate *IncidenceAttendee::responseDelegate() const 0828 { 0829 return mResponseDelegate; 0830 } 0831 0832 AttendeeComboBoxDelegate *IncidenceAttendee::roleDelegate() const 0833 { 0834 return mRoleDelegate; 0835 } 0836 0837 AttendeeComboBoxDelegate *IncidenceAttendee::stateDelegate() const 0838 { 0839 return mStateDelegate; 0840 } 0841 0842 AttendeeLineEditDelegate *IncidenceAttendee::attendeeDelegate() const 0843 { 0844 return mAttendeeDelegate; 0845 } 0846 0847 void IncidenceAttendee::filterLayoutChanged() 0848 { 0849 QHeaderView *headerView = mUi->mAttendeeTable->horizontalHeader(); 0850 headerView->setSectionResizeMode(AttendeeTableModel::Role, QHeaderView::ResizeToContents); 0851 headerView->setSectionResizeMode(AttendeeTableModel::FullName, QHeaderView::Stretch); 0852 headerView->setSectionResizeMode(AttendeeTableModel::Status, QHeaderView::ResizeToContents); 0853 headerView->setSectionResizeMode(AttendeeTableModel::Response, QHeaderView::ResizeToContents); 0854 headerView->setSectionHidden(AttendeeTableModel::CuType, true); 0855 headerView->setSectionHidden(AttendeeTableModel::Name, true); 0856 headerView->setSectionHidden(AttendeeTableModel::Email, true); 0857 headerView->setSectionHidden(AttendeeTableModel::Available, true); 0858 } 0859 0860 void IncidenceAttendee::updateCount() 0861 { 0862 Q_EMIT attendeeCountChanged(attendeeCount()); 0863 0864 checkDirtyStatus(); 0865 } 0866 0867 int IncidenceAttendee::attendeeCount() const 0868 { 0869 int c = 0; 0870 QModelIndex index; 0871 QAbstractItemModel *model = mUi->mAttendeeTable->model(); 0872 if (!model) { 0873 return 0; 0874 } 0875 for (int i = 0; i < model->rowCount(QModelIndex()); ++i) { 0876 index = model->index(i, AttendeeTableModel::FullName); 0877 if (!model->data(index).toString().isEmpty()) { 0878 ++c; 0879 } 0880 } 0881 return c; 0882 } 0883 0884 void IncidenceAttendee::setActions(KCalendarCore::Incidence::IncidenceType actions) 0885 { 0886 mStateDelegate->clear(); 0887 if (actions == KCalendarCore::Incidence::TypeEvent) { 0888 mStateDelegate->addItem(QIcon::fromTheme(QStringLiteral(":/task-attention.png")), KCalUtils::Stringify::attendeeStatus(AttendeeData::NeedsAction)); 0889 mStateDelegate->addItem(QIcon::fromTheme(QStringLiteral(":/task-accepted.png")), KCalUtils::Stringify::attendeeStatus(AttendeeData::Accepted)); 0890 mStateDelegate->addItem(QIcon::fromTheme(QStringLiteral(":/task-reject.png")), KCalUtils::Stringify::attendeeStatus(AttendeeData::Declined)); 0891 mStateDelegate->addItem(QIcon::fromTheme(QStringLiteral(":/task-attempt.png")), KCalUtils::Stringify::attendeeStatus(AttendeeData::Tentative)); 0892 mStateDelegate->addItem(QIcon::fromTheme(QStringLiteral(":/task-delegate.png")), KCalUtils::Stringify::attendeeStatus(AttendeeData::Delegated)); 0893 } else { 0894 mStateDelegate->addItem(QIcon::fromTheme(QStringLiteral(":/task-attention.png")), KCalUtils::Stringify::attendeeStatus(AttendeeData::NeedsAction)); 0895 mStateDelegate->addItem(QIcon::fromTheme(QStringLiteral(":/task-accepted.png")), KCalUtils::Stringify::attendeeStatus(AttendeeData::Accepted)); 0896 mStateDelegate->addItem(QIcon::fromTheme(QStringLiteral(":/task-reject.png")), KCalUtils::Stringify::attendeeStatus(AttendeeData::Declined)); 0897 mStateDelegate->addItem(QIcon::fromTheme(QStringLiteral(":/task-attempt.png")), KCalUtils::Stringify::attendeeStatus(AttendeeData::Tentative)); 0898 mStateDelegate->addItem(QIcon::fromTheme(QStringLiteral(":/task-delegate.png")), KCalUtils::Stringify::attendeeStatus(AttendeeData::Delegated)); 0899 mStateDelegate->addItem(QIcon::fromTheme(QStringLiteral(":/task-complete.png")), KCalUtils::Stringify::attendeeStatus(AttendeeData::Completed)); 0900 mStateDelegate->addItem(QIcon::fromTheme(QStringLiteral(":/task-ongoing.png")), KCalUtils::Stringify::attendeeStatus(AttendeeData::InProcess)); 0901 } 0902 } 0903 0904 void IncidenceAttendee::printDebugInfo() const 0905 { 0906 qCDebug(INCIDENCEEDITOR_LOG) << "I'm organizer : " << iAmOrganizer(); 0907 qCDebug(INCIDENCEEDITOR_LOG) << "Loaded organizer: " << mLoadedIncidence->organizer().email(); 0908 0909 if (iAmOrganizer()) { 0910 KCalendarCore::Event tmp; 0911 tmp.setOrganizer(mUi->mOrganizerCombo->currentText()); 0912 qCDebug(INCIDENCEEDITOR_LOG) << "Organizer combo: " << tmp.organizer().email(); 0913 } 0914 0915 const KCalendarCore::Attendee::List originalList = mLoadedIncidence->attendees(); 0916 KCalendarCore::Attendee::List newList; 0917 qCDebug(INCIDENCEEDITOR_LOG) << "List sizes: " << originalList.count() << newList.count(); 0918 0919 const auto lstAttendees = mDataModel->attendees(); 0920 for (const KCalendarCore::Attendee &attendee : lstAttendees) { 0921 if (!attendee.fullName().isEmpty()) { 0922 newList.append(attendee); 0923 } 0924 } 0925 0926 // Okay, again not the most efficient algorithm, but I'm assuming that in the 0927 // bulk of the use cases, the number of attendees is not much higher than 10 or so. 0928 for (const KCalendarCore::Attendee &attendee : originalList) { 0929 bool found = false; 0930 for (int i = 0; i < newList.count(); ++i) { 0931 if (newList[i] == attendee) { 0932 newList.remove(i); 0933 found = true; 0934 break; 0935 } 0936 } 0937 0938 if (!found) { 0939 qCDebug(INCIDENCEEDITOR_LOG) << "Attendee not found: " << attendee.email() << attendee.name() << attendee.status() << attendee.RSVP() 0940 << attendee.role() << attendee.uid() << attendee.cuType() << attendee.delegate() << attendee.delegator() 0941 << "; we have:"; 0942 for (int i = 0, total = newList.count(); i < total; ++i) { 0943 const KCalendarCore::Attendee newAttendee = newList[i]; 0944 qCDebug(INCIDENCEEDITOR_LOG) << "Attendee: " << newAttendee.email() << newAttendee.name() << newAttendee.status() << newAttendee.RSVP() 0945 << newAttendee.role() << newAttendee.uid() << newAttendee.cuType() << newAttendee.delegate() 0946 << newAttendee.delegator(); 0947 } 0948 0949 return; 0950 } 0951 } 0952 } 0953 0954 int IncidenceAttendee::rowOfAttendee(const QString &uid) const 0955 { 0956 const auto attendees = dataModel()->attendees(); 0957 const auto it = std::find_if(attendees.begin(), attendees.end(), [uid](const KCalendarCore::Attendee &att) { 0958 return att.uid() == uid; 0959 }); 0960 return std::distance(attendees.begin(), it); 0961 } 0962 0963 void IncidenceAttendee::slotUpdateCryptoPreferences() 0964 { 0965 const auto idx = mUi->mOrganizerCombo->currentIndex(); 0966 if (idx < 0) { 0967 return; 0968 } 0969 const auto organizer = mUi->mOrganizerCombo->currentData().value<EditorConfig::Organizer>(); 0970 0971 mUi->mSignItip->setChecked(organizer.sign); 0972 mUi->mEncryptItip->setChecked(organizer.encrypt); 0973 } 0974 0975 #include "moc_incidenceattendee.cpp"