diff --git a/src/incidencedialog.cpp b/src/incidencedialog.cpp index 347ec64..6c751cc 100644 --- a/src/incidencedialog.cpp +++ b/src/incidencedialog.cpp @@ -1,869 +1,883 @@ /* Copyright (c) 2010 Bertjan Broeksema Copyright (c) 2010 Klarälvdalens Datakonsult AB, a KDAB Group company Copyright (C) 2012 Allen Winter This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "incidencedialog.h" #include "incidenceeditor_debug.h" #include "combinedincidenceeditor.h" #include "editorconfig.h" #include "incidencealarm.h" #include "incidenceattachment.h" #include "incidenceattendee.h" #include "incidencecategories.h" #include "incidencecompletionpriority.h" #include "incidencedatetime.h" #include "incidencedescription.h" #include "incidencerecurrence.h" #include "incidenceresource.h" #include "incidencesecrecy.h" #include "incidencewhatwhere.h" #include "templatemanagementdialog.h" #include "ui_dialogdesktop.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace IncidenceEditorNG; namespace IncidenceEditorNG { enum Tabs { GeneralTab = 0, AttendeesTab, ResourcesTab, AlarmsTab, RecurrenceTab, AttachmentsTab }; class IncidenceDialogPrivate : public ItemEditorUi { IncidenceDialog *q_ptr; Q_DECLARE_PUBLIC(IncidenceDialog) public: Ui::EventOrTodoDesktop *mUi = nullptr; Akonadi::CollectionComboBox *mCalSelector = nullptr; bool mCloseOnSave = false; EditorItemManager *mItemManager = nullptr; CombinedIncidenceEditor *mEditor = nullptr; IncidenceDateTime *mIeDateTime = nullptr; IncidenceAttendee *mIeAttendee = nullptr; IncidenceRecurrence *mIeRecurrence = nullptr; IncidenceResource *mIeResource = nullptr; bool mInitiallyDirty = false; Akonadi::Item mItem; QString typeToString(const int type) const; public: IncidenceDialogPrivate(Akonadi::IncidenceChanger *changer, IncidenceDialog *qq); ~IncidenceDialogPrivate(); /// General methods void handleAlarmCountChange(int newCount); void handleRecurrenceChange(IncidenceEditorNG::RecurrenceType type); void loadTemplate(const QString &templateName); void manageTemplates(); void saveTemplate(const QString &templateName); void storeTemplatesInConfig(const QStringList &newTemplates); void updateAttachmentCount(int newCount); void updateAttendeeCount(int newCount); void updateResourceCount(int newCount); void updateButtonStatus(bool isDirty); void showMessage(const QString &text, KMessageWidget::MessageType type); void slotInvalidCollection(); /// ItemEditorUi methods bool containsPayloadIdentifiers(const QSet &partIdentifiers) const override; void handleItemSaveFinish(EditorItemManager::SaveAction); void handleItemSaveFail(EditorItemManager::SaveAction, const QString &errorMessage); bool hasSupportedPayload(const Akonadi::Item &item) const override; bool isDirty() const override; bool isValid() const override; void load(const Akonadi::Item &item) override; Akonadi::Item save(const Akonadi::Item &item) override; Akonadi::Collection selectedCollection() const override; void reject(RejectReason reason, const QString &errorMessage = QString()) override; }; } IncidenceDialogPrivate::IncidenceDialogPrivate(Akonadi::IncidenceChanger *changer, IncidenceDialog *qq) : q_ptr(qq) , mUi(new Ui::EventOrTodoDesktop) , mCalSelector(new Akonadi::CollectionComboBox) , mCloseOnSave(false) , mItemManager(new EditorItemManager(this, changer)) , mEditor(new CombinedIncidenceEditor(qq)) , mInitiallyDirty(false) { Q_Q(IncidenceDialog); mUi->setupUi(q); mUi->mMessageWidget->hide(); QGridLayout *layout = new QGridLayout(mUi->mCalSelectorPlaceHolder); layout->setSpacing(0); layout->setContentsMargins(0, 0, 0, 0); layout->addWidget(mCalSelector); mCalSelector->setAccessRightsFilter(Akonadi::Collection::CanCreateItem); mUi->label->setBuddy(mCalSelector); q->connect(mCalSelector, &Akonadi::CollectionComboBox::currentChanged, q, &IncidenceDialog::handleSelectedCollectionChange); // Now instantiate the logic of the dialog. These editors update the ui, validate // fields and load/store incidences in the ui. IncidenceWhatWhere *ieGeneral = new IncidenceWhatWhere(mUi); mEditor->combine(ieGeneral); IncidenceCategories *ieCategories = new IncidenceCategories(mUi); mEditor->combine(ieCategories); mIeDateTime = new IncidenceDateTime(mUi); mEditor->combine(mIeDateTime); IncidenceCompletionPriority *ieCompletionPriority = new IncidenceCompletionPriority(mUi); mEditor->combine(ieCompletionPriority); IncidenceDescription *ieDescription = new IncidenceDescription(mUi); mEditor->combine(ieDescription); IncidenceAlarm *ieAlarm = new IncidenceAlarm(mIeDateTime, mUi); mEditor->combine(ieAlarm); IncidenceAttachment *ieAttachments = new IncidenceAttachment(mUi); mEditor->combine(ieAttachments); mIeRecurrence = new IncidenceRecurrence(mIeDateTime, mUi); mEditor->combine(mIeRecurrence); IncidenceSecrecy *ieSecrecy = new IncidenceSecrecy(mUi); mEditor->combine(ieSecrecy); mIeAttendee = new IncidenceAttendee(qq, mIeDateTime, mUi); mIeAttendee->setParent(qq); mEditor->combine(mIeAttendee); mIeResource = new IncidenceResource(mIeAttendee, mIeDateTime, mUi); mEditor->combine(mIeResource); q->connect(mEditor, SIGNAL(showMessage(QString,KMessageWidget::MessageType)), SLOT(showMessage(QString,KMessageWidget::MessageType))); q->connect(mEditor, SIGNAL(dirtyStatusChanged(bool)), SLOT(updateButtonStatus(bool))); q->connect(mItemManager, SIGNAL(itemSaveFinished(IncidenceEditorNG::EditorItemManager::SaveAction)), SLOT(handleItemSaveFinish(IncidenceEditorNG::EditorItemManager::SaveAction))); q->connect(mItemManager, SIGNAL(itemSaveFailed(IncidenceEditorNG::EditorItemManager::SaveAction,QString)), SLOT(handleItemSaveFail(IncidenceEditorNG::EditorItemManager::SaveAction,QString))); q->connect(ieAlarm, SIGNAL(alarmCountChanged(int)), SLOT(handleAlarmCountChange(int))); q->connect(mIeRecurrence, SIGNAL(recurrenceChanged(IncidenceEditorNG::RecurrenceType)), SLOT(handleRecurrenceChange(IncidenceEditorNG::RecurrenceType))); q->connect(ieAttachments, SIGNAL(attachmentCountChanged(int)), SLOT(updateAttachmentCount(int))); q->connect(mIeAttendee, SIGNAL(attendeeCountChanged(int)), SLOT(updateAttendeeCount(int))); q->connect(mIeResource, SIGNAL(resourceCountChanged(int)), SLOT(updateResourceCount(int))); } IncidenceDialogPrivate::~IncidenceDialogPrivate() { delete mItemManager; delete mEditor; delete mUi; } void IncidenceDialogPrivate::slotInvalidCollection() { showMessage(i18n("Select a valid collection first."), KMessageWidget::Warning); } void IncidenceDialogPrivate::showMessage(const QString &text, KMessageWidget::MessageType type) { mUi->mMessageWidget->setText(text); mUi->mMessageWidget->setMessageType(type); mUi->mMessageWidget->show(); } void IncidenceDialogPrivate::handleAlarmCountChange(int newCount) { QString tabText; if (newCount > 0) { tabText = i18nc("@title:tab Tab to configure the reminders of an event or todo", "Reminder (%1)", newCount); } else { tabText = i18nc("@title:tab Tab to configure the reminders of an event or todo", "Reminder"); } mUi->mTabWidget->setTabText(AlarmsTab, tabText); } void IncidenceDialogPrivate::handleRecurrenceChange(IncidenceEditorNG::RecurrenceType type) { QString tabText = i18nc("@title:tab Tab to configure the recurrence of an event or todo", "Rec&urrence"); // Keep this numbers in sync with the items in mUi->mRecurrenceTypeCombo. I // tried adding an enum to IncidenceRecurrence but for whatever reason I could // Qt not play nice with namespaced enums in signal/slot connections. // Anyways, I don't expect these values to change. switch (type) { case RecurrenceTypeNone: break; case RecurrenceTypeDaily: tabText += i18nc("@title:tab Daily recurring event, capital first letter only", " (D)"); break; case RecurrenceTypeWeekly: tabText += i18nc("@title:tab Weekly recurring event, capital first letter only", " (W)"); break; case RecurrenceTypeMonthly: tabText += i18nc("@title:tab Monthly recurring event, capital first letter only", " (M)"); break; case RecurrenceTypeYearly: tabText += i18nc("@title:tab Yearly recurring event, capital first letter only", " (Y)"); break; case RecurrenceTypeException: tabText += i18nc("@title:tab Exception to a recurring event, capital first letter only", " (E)"); break; default: Q_ASSERT_X(false, "handleRecurrenceChange", "Fix your program"); } mUi->mTabWidget->setTabText(RecurrenceTab, tabText); } QString IncidenceDialogPrivate::typeToString(const int type) const { // Do not translate. switch (type) { case KCalendarCore::Incidence::TypeEvent: return QStringLiteral("Event"); case KCalendarCore::Incidence::TypeTodo: return QStringLiteral("Todo"); case KCalendarCore::Incidence::TypeJournal: return QStringLiteral("Journal"); default: return QStringLiteral("Unknown"); } } void IncidenceDialogPrivate::loadTemplate(const QString &templateName) { Q_Q(IncidenceDialog); KCalendarCore::MemoryCalendar::Ptr cal(new KCalendarCore::MemoryCalendar(QTimeZone::systemTimeZone())); const QString fileName = QStandardPaths::locate( QStandardPaths::GenericDataLocation, QStringLiteral("/korganizer/templates/") +typeToString(mEditor->type()) + QLatin1Char('/') +templateName); if (fileName.isEmpty()) { KMessageBox::error( q, i18nc("@info", "Unable to find template '%1'.", templateName)); return; } KCalendarCore::ICalFormat format; if (!format.load(cal, fileName)) { KMessageBox::error( q, i18nc("@info", "Error loading template file '%1'.", fileName)); return; } KCalendarCore::Incidence::List incidences = cal->incidences(); if (incidences.isEmpty()) { KMessageBox::error( q, i18nc("@info", "Template does not contain a valid incidence.")); return; } mIeDateTime->setActiveDate(QDate()); KCalendarCore::Incidence::Ptr newInc = KCalendarCore::Incidence::Ptr(incidences.first()->clone()); newInc->setUid(KCalendarCore::CalFormat::createUniqueId()); // We add a custom property so that some fields aren't loaded, dates for example newInc->setCustomProperty(QByteArray("kdepim"), "isTemplate", QStringLiteral("true")); mEditor->load(newInc); newInc->removeCustomProperty(QByteArray(), "isTemplate"); } void IncidenceDialogPrivate::manageTemplates() { Q_Q(IncidenceDialog); QStringList &templates = IncidenceEditorNG::EditorConfig::instance()->templates(mEditor->type()); QPointer dialog( new IncidenceEditorNG::TemplateManagementDialog( q, templates, KCalUtils::Stringify::incidenceType(mEditor->type()))); q->connect(dialog, SIGNAL(loadTemplate(QString)), SLOT(loadTemplate(QString))); q->connect(dialog, SIGNAL(templatesChanged(QStringList)), SLOT(storeTemplatesInConfig(QStringList))); q->connect(dialog, SIGNAL(saveTemplate(QString)), SLOT(saveTemplate(QString))); dialog->exec(); delete dialog; } void IncidenceDialogPrivate::saveTemplate(const QString &templateName) { Q_ASSERT(!templateName.isEmpty()); KCalendarCore::MemoryCalendar::Ptr cal(new KCalendarCore::MemoryCalendar(QTimeZone::systemTimeZone())); switch (mEditor->type()) { case KCalendarCore::Incidence::TypeEvent: { KCalendarCore::Event::Ptr event(new KCalendarCore::Event()); mEditor->save(event); cal->addEvent(KCalendarCore::Event::Ptr(event->clone())); break; } case KCalendarCore::Incidence::TypeTodo: { KCalendarCore::Todo::Ptr todo(new KCalendarCore::Todo); mEditor->save(todo); cal->addTodo(KCalendarCore::Todo::Ptr(todo->clone())); break; } case KCalendarCore::Incidence::TypeJournal: { KCalendarCore::Journal::Ptr journal(new KCalendarCore::Journal); mEditor->save(journal); cal->addJournal(KCalendarCore::Journal::Ptr(journal->clone())); break; } default: Q_ASSERT_X(false, "saveTemplate", "Fix your program"); } QString fileName = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) +QStringLiteral("/korganizer/templates/") + typeToString(mEditor->type()) + QLatin1Char('/'); QDir().mkpath(fileName); fileName += templateName; KCalendarCore::ICalFormat format; format.save(cal, fileName); } void IncidenceDialogPrivate::storeTemplatesInConfig(const QStringList &templateNames) { // I find this somewhat broken. templates() returns a reference, maybe it should // be changed by adding a setTemplates method. const QStringList origTemplates = IncidenceEditorNG::EditorConfig::instance()->templates(mEditor->type()); const QString defaultPath = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) +QStringLiteral("korganizer/templates/") +typeToString(mEditor->type()) + QLatin1Char('/'); QDir().mkpath(defaultPath); for (const QString &tmpl : origTemplates) { if (!templateNames.contains(tmpl)) { const QString fileName = defaultPath + tmpl; QFile file(fileName); if (file.exists()) { file.remove(); } } } IncidenceEditorNG::EditorConfig::instance()->templates(mEditor->type()) = templateNames; IncidenceEditorNG::EditorConfig::instance()->config()->save(); } void IncidenceDialogPrivate::updateAttachmentCount(int newCount) { if (newCount > 0) { mUi->mTabWidget->setTabText( AttachmentsTab, i18nc("@title:tab Tab to modify attachments of an event or todo", "Attac&hments (%1)", newCount)); } else { mUi->mTabWidget->setTabText( AttachmentsTab, i18nc("@title:tab Tab to modify attachments of an event or todo", "Attac&hments")); } } void IncidenceDialogPrivate::updateAttendeeCount(int newCount) { if (newCount > 0) { mUi->mTabWidget->setTabText( AttendeesTab, i18nc("@title:tab Tab to modify attendees of an event or todo", "&Attendees (%1)", newCount)); } else { mUi->mTabWidget->setTabText( AttendeesTab, i18nc("@title:tab Tab to modify attendees of an event or todo", "&Attendees")); } } void IncidenceDialogPrivate::updateResourceCount(int newCount) { if (newCount > 0) { mUi->mTabWidget->setTabText( ResourcesTab, i18nc("@title:tab Tab to modify attendees of an event or todo", "&Resources (%1)", newCount)); } else { mUi->mTabWidget->setTabText( ResourcesTab, i18nc("@title:tab Tab to modify attendees of an event or todo", "&Resources")); } } void IncidenceDialogPrivate::updateButtonStatus(bool isDirty) { mUi->buttonBox->button(QDialogButtonBox::Apply)->setEnabled(isDirty || mInitiallyDirty); } bool IncidenceDialogPrivate::containsPayloadIdentifiers( const QSet &partIdentifiers) const { return partIdentifiers.contains(QByteArray("PLD:RFC822")); } void IncidenceDialogPrivate::handleItemSaveFail(EditorItemManager::SaveAction, const QString &errorMessage) { Q_Q(IncidenceDialog); bool retry = false; if (!errorMessage.isEmpty()) { const QString message = i18nc("@info", "Unable to store the incidence in the calendar. Try again?\n\n " "Reason: %1", errorMessage); retry = (KMessageBox::warningYesNo(q, message) == KMessageBox::Yes); } if (retry) { mItemManager->save(); } else { updateButtonStatus(isDirty()); mUi->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(true); mUi->buttonBox->button(QDialogButtonBox::Cancel)->setEnabled(true); } } void IncidenceDialogPrivate::handleItemSaveFinish(EditorItemManager::SaveAction saveAction) { Q_Q(IncidenceDialog); if (mCloseOnSave) { q->accept(); } else { const Akonadi::Item item = mItemManager->item(); Q_ASSERT(item.isValid()); Q_ASSERT(item.hasPayload()); Q_ASSERT(item.hasPayload()); // Now the item is successfully saved, reload it in the editor in order to // reset the dirty status of the editor. mEditor->load(item.payload()); mEditor->load(item); // Set the buttons to a reasonable state as well (ok and apply should be // disabled at this point). mUi->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(true); mUi->buttonBox->button(QDialogButtonBox::Cancel)->setEnabled(true); mUi->buttonBox->button(QDialogButtonBox::Apply)->setEnabled(isDirty()); } if (saveAction == EditorItemManager::Create) { Q_EMIT q->incidenceCreated(mItemManager->item()); } } bool IncidenceDialogPrivate::hasSupportedPayload(const Akonadi::Item &item) const { return CalendarSupport::incidence(item); } bool IncidenceDialogPrivate::isDirty() const { if (mItem.isValid()) { return mEditor->isDirty() || mCalSelector->currentCollection().id() != mItem.storageCollectionId(); } else { return mEditor->isDirty(); } } bool IncidenceDialogPrivate::isValid() const { Q_Q(const IncidenceDialog); if (mEditor->isValid()) { // Check if there's a selected collection. if (mCalSelector->currentCollection().isValid()) { return true; } else { qCWarning(INCIDENCEEDITOR_LOG) << "Select a collection first"; Q_EMIT q->invalidCollection(); } } return false; } void IncidenceDialogPrivate::load(const Akonadi::Item &item) { Q_Q(IncidenceDialog); Q_ASSERT(hasSupportedPayload(item)); if (CalendarSupport::hasJournal(item)) { //mUi->mTabWidget->removeTab(5); mUi->mTabWidget->removeTab(AttachmentsTab); mUi->mTabWidget->removeTab(RecurrenceTab); mUi->mTabWidget->removeTab(AlarmsTab); mUi->mTabWidget->removeTab(AttendeesTab); mUi->mTabWidget->removeTab(ResourcesTab); } mEditor->load(CalendarSupport::incidence(item)); mEditor->load(item); const KCalendarCore::Incidence::Ptr incidence = CalendarSupport::incidence(item); const QStringList allEmails = IncidenceEditorNG::EditorConfig::instance()->allEmails(); const KCalendarCore::Attendee me = incidence->attendeeByMails(allEmails); if (incidence->attendeeCount() > 1 // >1 because you won't drink alone && !me.isNull() && (me.status() == KCalendarCore::Attendee::NeedsAction || me.status() == KCalendarCore::Attendee::Tentative || me.status() == KCalendarCore::Attendee::InProcess)) { // Show the invitation bar: "You are invited [accept] [decline]" mUi->mInvitationBar->show(); } else { mUi->mInvitationBar->hide(); } qCDebug(INCIDENCEEDITOR_LOG) << "Loading item " << item.id() << "; parent " << item.parentCollection().id() << "; storage " << item.storageCollectionId(); if (item.storageCollectionId() > -1) { mCalSelector->setDefaultCollection(Akonadi::Collection(item.storageCollectionId())); } if (!mCalSelector->mimeTypeFilter().contains(QStringLiteral("text/calendar")) || !mCalSelector->mimeTypeFilter().contains(incidence->mimeType())) { mCalSelector->setMimeTypeFilter(QStringList() << incidence->mimeType() << QStringLiteral("text/calendar")); } if (mEditor->type() == KCalendarCore::Incidence::TypeTodo) { q->setWindowIcon(QIcon::fromTheme(QStringLiteral("view-calendar-tasks"))); } else if (mEditor->type() == KCalendarCore::Incidence::TypeEvent) { q->setWindowIcon(QIcon::fromTheme(QStringLiteral("view-calendar-day"))); } else if (mEditor->type() == KCalendarCore::Incidence::TypeJournal) { q->setWindowIcon(QIcon::fromTheme(QStringLiteral("view-pim-journal"))); } // Initialize tab's titles updateAttachmentCount(incidence->attachments().size()); updateResourceCount(mIeResource->resourceCount()); updateAttendeeCount(mIeAttendee->attendeeCount()); handleRecurrenceChange(mIeRecurrence->currentRecurrenceType()); handleAlarmCountChange(incidence->alarms().count()); mItem = item; q->show(); } Akonadi::Item IncidenceDialogPrivate::save(const Akonadi::Item &item) { Q_ASSERT(mEditor->incidence()); KCalendarCore::Incidence::Ptr incidenceInEditor = mEditor->incidence(); KCalendarCore::Incidence::Ptr newIncidence(incidenceInEditor->clone()); Akonadi::Item result = item; result.setMimeType(newIncidence->mimeType()); // There's no editor that has the relatedTo property. We must set it here, by hand. // Otherwise it gets lost. // FIXME: Why don't we clone() incidenceInEditor then pass the clone to save(), // I wonder if we're not leaking other properties. newIncidence->setRelatedTo(incidenceInEditor->relatedTo()); mEditor->save(newIncidence); mEditor->save(result); // Make sure that we don't loose uid for existing incidence newIncidence->setUid(mEditor->incidence()->uid()); // Mark the incidence as changed if (mItem.isValid()) { newIncidence->setRevision(newIncidence->revision() + 1); } result.setPayload(newIncidence); return result; } Akonadi::Collection IncidenceDialogPrivate::selectedCollection() const { return mCalSelector->currentCollection(); } void IncidenceDialogPrivate::reject(RejectReason reason, const QString &errorMessage) { Q_UNUSED(reason); Q_Q(IncidenceDialog); qCCritical(INCIDENCEEDITOR_LOG) << "Rejecting:" << errorMessage; q->deleteLater(); } /// IncidenceDialog IncidenceDialog::IncidenceDialog(Akonadi::IncidenceChanger *changer, QWidget *parent, Qt::WindowFlags flags) : QDialog(parent, flags) , d_ptr(new IncidenceDialogPrivate(changer, this)) { Q_D(IncidenceDialog); setAttribute(Qt::WA_DeleteOnClose); d->mUi->mTabWidget->setCurrentIndex(0); d->mUi->mSummaryEdit->setFocus(); d->mUi->buttonBox->button(QDialogButtonBox::Apply)->setToolTip(i18nc("@info:tooltip", "Save current changes")); d->mUi->buttonBox->button(QDialogButtonBox::Ok)->setToolTip(i18nc("@action:button", "Save changes and close dialog")); d->mUi->buttonBox->button(QDialogButtonBox::Cancel)->setToolTip(i18nc("@action:button", "Discard changes and close dialog")); d->mUi->buttonBox->button(QDialogButtonBox::Apply)->setEnabled(false); auto defaultButton = d->mUi->buttonBox->button(QDialogButtonBox::RestoreDefaults); defaultButton->setText(i18nc("@action:button", "&Templates...")); defaultButton->setIcon(QIcon::fromTheme(QStringLiteral("project-development-new-template"))); defaultButton->setToolTip(i18nc("@info:tooltip", "Manage templates for this item")); defaultButton->setWhatsThis( i18nc("@info:whatsthis", "Push this button to show a dialog that helps " "you manage a set of templates. Templates " "can make creating new items easier and faster " "by putting your favorite default values into " "the editor automatically.")); connect(d->mUi->buttonBox, &QDialogButtonBox::clicked, this, &IncidenceDialog::slotButtonClicked); setModal(false); connect(d->mUi->mAcceptInvitationButton, &QAbstractButton::clicked, d->mIeAttendee, &IncidenceAttendee::acceptForMe); connect(d->mUi->mAcceptInvitationButton, &QAbstractButton::clicked, d->mUi->mInvitationBar, &QWidget::hide); connect(d->mUi->mDeclineInvitationButton, &QAbstractButton::clicked, d->mIeAttendee, &IncidenceAttendee::declineForMe); connect(d->mUi->mDeclineInvitationButton, &QAbstractButton::clicked, d->mUi->mInvitationBar, &QWidget::hide); connect(this, SIGNAL(invalidCollection()), this, SLOT(slotInvalidCollection())); readConfig(); } IncidenceDialog::~IncidenceDialog() { writeConfig(); delete d_ptr; } void IncidenceDialog::writeConfig() { KConfigGroup group(KSharedConfig::openConfig(), "IncidenceDialog"); group.writeEntry("Size", size()); const Akonadi::Collection col = d_ptr->mCalSelector->currentCollection(); // col might not be valid if the collection wasn't found yet (the combo is async), skip saving in that case if (col.isValid() && col.id() != IncidenceEditorNG::IncidenceEditorSettings::self()->lastSelectedFolder()) { IncidenceEditorNG::IncidenceEditorSettings::self()->setLastSelectedFolder(col.id()); IncidenceEditorNG::IncidenceEditorSettings::self()->save(); } } void IncidenceDialog::readConfig() { KConfigGroup group(KSharedConfig::openConfig(), "IncidenceDialog"); const QSize size = group.readEntry("Size", QSize()); if (size.isValid()) { resize(size); } else { resize(QSize(500, 500).expandedTo(minimumSizeHint())); } } void IncidenceDialog::load(const Akonadi::Item &item, const QDate &activeDate) { Q_D(IncidenceDialog); d->mIeDateTime->setActiveDate(activeDate); if (item.isValid()) { // We're editing d->mItemManager->load(item); } else { // We're creating Q_ASSERT(d->hasSupportedPayload(item)); d->load(item); show(); } } void IncidenceDialog::selectCollection(const Akonadi::Collection &collection) { Q_D(IncidenceDialog); if (collection.isValid()) { d->mCalSelector->setDefaultCollection(collection); } else { d->mCalSelector->setCurrentIndex(0); } } void IncidenceDialog::setIsCounterProposal(bool isCounterProposal) { Q_D(IncidenceDialog); d->mItemManager->setIsCounterProposal(isCounterProposal); } QObject *IncidenceDialog::typeAheadReceiver() const { Q_D(const IncidenceDialog); return d->mUi->mSummaryEdit; } void IncidenceDialog::slotButtonClicked(QAbstractButton *button) { Q_D(IncidenceDialog); if (d->mUi->buttonBox->button(QDialogButtonBox::Ok) == button) { if (d->isDirty() || d->mInitiallyDirty) { d->mUi->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); d->mUi->buttonBox->button(QDialogButtonBox::Cancel)->setEnabled(false); d->mUi->buttonBox->button(QDialogButtonBox::Apply)->setEnabled(false); d->mCloseOnSave = true; d->mInitiallyDirty = false; d->mItemManager->save(); } else { close(); } } else if (d->mUi->buttonBox->button(QDialogButtonBox::Apply) == button) { d->mUi->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); d->mUi->buttonBox->button(QDialogButtonBox::Cancel)->setEnabled(false); d->mUi->buttonBox->button(QDialogButtonBox::Apply)->setEnabled(false); d->mCloseOnSave = false; d->mInitiallyDirty = false; d->mItemManager->save(); } else if (d->mUi->buttonBox->button(QDialogButtonBox::Cancel) == button) { if (d->isDirty() && KMessageBox::questionYesNo( this, i18nc("@info", "Do you really want to cancel?"), i18nc("@title:window", "KOrganizer Confirmation")) == KMessageBox::Yes) { QDialog::reject(); // Discard current changes } else if (!d->isDirty()) { QDialog::reject(); // No pending changes, just close the dialog. } // else { // the user wasn't finished editing after all } } else if (d->mUi->buttonBox->button(QDialogButtonBox::RestoreDefaults)) { d->manageTemplates(); } else { Q_ASSERT(false); // Shouldn't happen } } +void IncidenceDialog::reject() +{ + Q_D(IncidenceDialog); + if (d->isDirty() + && KMessageBox::questionYesNo( + this, + i18nc("@info", "Do you really want to cancel?"), + i18nc("@title:window", "KOrganizer Confirmation")) == KMessageBox::Yes) { + QDialog::reject(); // Discard current changes + } else if (!d->isDirty()) { + QDialog::reject(); // No pending changes, just close the dialog. + } +} + void IncidenceDialog::closeEvent(QCloseEvent *event) { Q_D(IncidenceDialog); if (d->isDirty() && KMessageBox::questionYesNo( this, i18nc("@info", "Do you really want to cancel?"), i18nc("@title:window", "KOrganizer Confirmation")) == KMessageBox::Yes) { QDialog::reject(); // Discard current changes QDialog::closeEvent(event); } else if (!d->isDirty()) { QDialog::reject(); // No pending changes, just close the dialog. QDialog::closeEvent(event); } else { event->ignore(); } } void IncidenceDialog::setInitiallyDirty(bool initiallyDirty) { Q_D(IncidenceDialog); d->mInitiallyDirty = initiallyDirty; } Akonadi::Item IncidenceDialog::item() const { Q_D(const IncidenceDialog); return d->mItemManager->item(); } void IncidenceDialog::handleSelectedCollectionChange(const Akonadi::Collection &collection) { Q_D(IncidenceDialog); if (d->mItem.parentCollection().isValid()) { d->mUi->buttonBox->button(QDialogButtonBox::Apply)->setEnabled( collection.id() != d->mItem.parentCollection().id()); } } #include "moc_incidencedialog.cpp" diff --git a/src/incidencedialog.h b/src/incidencedialog.h index 02cdebb..8d6a2e9 100644 --- a/src/incidencedialog.h +++ b/src/incidencedialog.h @@ -1,125 +1,126 @@ /* Copyright (c) 2010 Bertjan Broeksema Copyright (c) 2010 Klarälvdalens Datakonsult AB, a KDAB Group company This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef INCIDENCEEDITOR_INCIDENCEDIALOG_H #define INCIDENCEEDITOR_INCIDENCEDIALOG_H #include "incidenceeditor_export.h" #include "editoritemmanager.h" #include #include class QAbstractButton; namespace Akonadi { class IncidenceChanger; } namespace IncidenceEditorNG { class IncidenceDialogPrivate; class INCIDENCEEDITOR_EXPORT IncidenceDialog : public QDialog { Q_OBJECT public: explicit IncidenceDialog(Akonadi::IncidenceChanger *changer = nullptr, QWidget *parent = nullptr, Qt::WindowFlags flags = {}); ~IncidenceDialog() override; /** * Loads the @param item into the dialog. * * To create a new Incidence pass an invalid item with either an * KCalendarCore::Event:Ptr or a KCalendarCore::Todo:Ptr set as payload. Note: When the * item is invalid, i.e. it has an invalid id, a valid payload must * be set. * * When the item has is valid this method will fetch the payload when this is * not already set. */ virtual void load(const Akonadi::Item &item, const QDate &activeDate = QDate()); /** * Sets the Collection combobox to @param collection. */ virtual void selectCollection(const Akonadi::Collection &collection); virtual void setIsCounterProposal(bool isCounterProposal); /** Returns the object that will receive all key events. */ QObject *typeAheadReceiver() const; /** By default, if you load an incidence into the editor ( load(item) ), then press [OK] without changing anything, the dialog is dismissed, and the incidence isn't saved to akonadi. Call this method with @p initiallyDirty = true if you want the incidence to be saved, It's useful if you're creating a dialog with an already crafted content, like in kmail's "Create Todo/Reminder Feature". */ void setInitiallyDirty(bool initiallyDirty); Q_REQUIRED_RESULT Akonadi::Item item() const; Q_SIGNALS: /** * This signal is emitted when an incidence is created. * @param collection The collection where it was created. */ void incidenceCreated(const Akonadi::Item &); void invalidCollection() const; protected: void closeEvent(QCloseEvent *event) override; protected Q_SLOTS: void slotButtonClicked(QAbstractButton *button); void handleSelectedCollectionChange(const Akonadi::Collection &collection); + void reject() override; private: IncidenceDialogPrivate *const d_ptr; Q_DECLARE_PRIVATE(IncidenceDialog) Q_DISABLE_COPY(IncidenceDialog) void writeConfig(); void readConfig(); Q_PRIVATE_SLOT(d_ptr, void handleAlarmCountChange(int)) Q_PRIVATE_SLOT(d_ptr, void handleItemSaveFinish( IncidenceEditorNG::EditorItemManager::SaveAction)) Q_PRIVATE_SLOT(d_ptr, void handleItemSaveFail(IncidenceEditorNG::EditorItemManager::SaveAction, QString)) Q_PRIVATE_SLOT(d_ptr, void handleRecurrenceChange(IncidenceEditorNG::RecurrenceType)) Q_PRIVATE_SLOT(d_ptr, void loadTemplate(QString)) Q_PRIVATE_SLOT(d_ptr, void saveTemplate(QString)) Q_PRIVATE_SLOT(d_ptr, void storeTemplatesInConfig(QStringList)) Q_PRIVATE_SLOT(d_ptr, void updateAttachmentCount(int)) Q_PRIVATE_SLOT(d_ptr, void updateAttendeeCount(int)) Q_PRIVATE_SLOT(d_ptr, void updateResourceCount(int)) Q_PRIVATE_SLOT(d_ptr, void updateButtonStatus(bool)) Q_PRIVATE_SLOT(d_ptr, void showMessage(QString, KMessageWidget::MessageType)) Q_PRIVATE_SLOT(d_ptr, void slotInvalidCollection()) }; } #endif