File indexing completed on 2024-06-02 05:26:02

0001 /*
0002   This file is part of the KDE project
0003 
0004   SPDX-FileCopyrightText: 2002-2003 Daniel Molkentin <molkentin@kde.org>
0005   SPDX-FileCopyrightText: 2004-2006 Michael Brade <brade@kde.org>
0006   SPDX-FileCopyrightText: 2013-2024 Laurent Montel <montel@kde.org>
0007 
0008   SPDX-License-Identifier: GPL-2.0-or-later
0009 */
0010 
0011 #include "knotes_part.h"
0012 #include "akonadi/notesakonaditreemodel.h"
0013 #include "akonadi/noteschangerecorder.h"
0014 #include "alarms/notealarmdialog.h"
0015 #include "attributes/notealarmattribute.h"
0016 #include "attributes/notedisplayattribute.h"
0017 #include "attributes/notelockattribute.h"
0018 #include "attributes/showfoldernotesattribute.h"
0019 #include "configdialog/knoteconfigdialog.h"
0020 #include "configdialog/knotesimpleconfigdialog.h"
0021 #include "finddialog/knotefinddialog.h"
0022 #include "job/createnewnotejob.h"
0023 #include "knoteedit.h"
0024 #include "knotes_kontact_plugin_debug.h"
0025 #include "knotesadaptor.h"
0026 #include "knoteseditdialog.h"
0027 #include "knotesglobalconfig.h"
0028 #include "knotesiconview.h"
0029 #include "knotesselectdeletenotesdialog.h"
0030 #include "knoteswidget.h"
0031 #include "notesharedglobalconfig.h"
0032 #include "noteutils.h"
0033 #include "print/knoteprinter.h"
0034 #include "print/knoteprintobject.h"
0035 #include "print/knoteprintselectthemedialog.h"
0036 #include "resources/localresourcecreator.h"
0037 #include "utils/knoteutils.h"
0038 
0039 #include <Akonadi/ChangeRecorder>
0040 #include <Akonadi/ControlGui>
0041 #include <Akonadi/ETMViewStateSaver>
0042 #include <Akonadi/ItemDeleteJob>
0043 #include <Akonadi/ItemFetchJob>
0044 #include <Akonadi/ItemFetchScope>
0045 #include <Akonadi/ItemModifyJob>
0046 #include <Akonadi/Session>
0047 
0048 #include <KMime/KMimeMessage>
0049 
0050 #include <KActionCollection>
0051 #include <KCheckableProxyModel>
0052 #include <KDNSSD/PublicService>
0053 #include <KLocalizedString>
0054 #include <KMessageBox>
0055 
0056 #include <QApplication>
0057 #include <QCheckBox>
0058 #include <QClipboard>
0059 #include <QFileDialog>
0060 #include <QInputDialog>
0061 #include <QItemSelectionModel>
0062 #include <QMenu>
0063 
0064 KNotesPart::KNotesPart(QObject *parent)
0065     : KParts::Part(parent)
0066     , mNotesWidget(nullptr)
0067     , mPublisher(nullptr)
0068     , mNotePrintPreview(nullptr)
0069     , mNoteTreeModel(nullptr)
0070 {
0071     (void)new KNotesAdaptor(this);
0072     QDBusConnection::sessionBus().registerObject(QStringLiteral("/KNotes"), this);
0073 
0074     setComponentName(QStringLiteral("knotes"), i18n("KNotes"));
0075 
0076     Akonadi::ControlGui::widgetNeedsAkonadi(widget());
0077 
0078     if (KNotesGlobalConfig::self()->autoCreateResourceOnStart()) {
0079         auto creator = new NoteShared::LocalResourceCreator(this);
0080         creator->createIfMissing();
0081     }
0082 
0083     // create the actions
0084     mNewNote = new QAction(QIcon::fromTheme(QStringLiteral("knotes")), i18nc("@action:inmenu create new popup note", "&New"), this);
0085     actionCollection()->addAction(QStringLiteral("file_new"), mNewNote);
0086     connect(mNewNote, &QAction::triggered, this, [this]() {
0087         newNote();
0088     });
0089     actionCollection()->setDefaultShortcut(mNewNote, QKeySequence(Qt::CTRL | Qt::Key_N));
0090     // mNewNote->setHelpText(
0091     //            i18nc( "@info:status", "Create a new popup note" ) );
0092     mNewNote->setWhatsThis(i18nc("@info:whatsthis", "You will be presented with a dialog where you can add a new popup note."));
0093 
0094     mNoteEdit = new QAction(QIcon::fromTheme(QStringLiteral("document-edit")), i18nc("@action:inmenu", "Edit..."), this);
0095     actionCollection()->addAction(QStringLiteral("edit_note"), mNoteEdit);
0096     connect(mNoteEdit, &QAction::triggered, this, [this]() {
0097         editNote();
0098     });
0099     // mNoteEdit->setHelpText(
0100     //            i18nc( "@info:status", "Edit popup note" ) );
0101     mNoteEdit->setWhatsThis(i18nc("@info:whatsthis", "You will be presented with a dialog where you can modify an existing popup note."));
0102 
0103     mNoteRename = new QAction(QIcon::fromTheme(QStringLiteral("edit-rename")), i18nc("@action:inmenu", "Rename..."), this);
0104     actionCollection()->setDefaultShortcut(mNoteRename, QKeySequence(Qt::Key_F2));
0105     actionCollection()->addAction(QStringLiteral("edit_rename"), mNoteRename);
0106     connect(mNoteRename, &QAction::triggered, this, &KNotesPart::renameNote);
0107     // mNoteRename->setHelpText(
0108     //            i18nc( "@info:status", "Rename popup note" ) );
0109     mNoteRename->setWhatsThis(i18nc("@info:whatsthis", "You will be presented with a dialog where you can rename an existing popup note."));
0110 
0111     mNoteDelete = new QAction(QIcon::fromTheme(QStringLiteral("edit-delete")), i18nc("@action:inmenu", "Delete"), this);
0112     actionCollection()->addAction(QStringLiteral("edit_delete"), mNoteDelete);
0113     connect(mNoteDelete, &QAction::triggered, this, &KNotesPart::killSelectedNotes);
0114     actionCollection()->setDefaultShortcut(mNoteDelete, QKeySequence(Qt::Key_Delete));
0115     // mNoteDelete->setHelpText(
0116     //            i18nc( "@info:status", "Delete popup note" ) );
0117     mNoteDelete->setWhatsThis(i18nc("@info:whatsthis",
0118                                     "You will be prompted if you really want to permanently remove "
0119                                     "the selected popup note."));
0120 
0121     mNotePrint = new QAction(QIcon::fromTheme(QStringLiteral("document-print")), i18nc("@action:inmenu", "Print Selected Notes..."), this);
0122     actionCollection()->addAction(QStringLiteral("print_note"), mNotePrint);
0123     connect(mNotePrint, &QAction::triggered, this, &KNotesPart::slotPrintSelectedNotes);
0124     // mNotePrint->setHelpText(
0125     //            i18nc( "@info:status", "Print popup note" ) );
0126     mNotePrint->setWhatsThis(i18nc("@info:whatsthis", "You will be prompted to print the selected popup note."));
0127 
0128     mNotePrintPreview =
0129         new QAction(QIcon::fromTheme(QStringLiteral("document-print-preview")), i18nc("@action:inmenu", "Print Preview Selected Notes..."), this);
0130     actionCollection()->addAction(QStringLiteral("print_preview_note"), mNotePrintPreview);
0131 
0132     connect(mNotePrintPreview, &QAction::triggered, this, &KNotesPart::slotPrintPreviewSelectedNotes);
0133 
0134     mNoteConfigure = new QAction(QIcon::fromTheme(QStringLiteral("configure")), i18n("Note settings..."), this);
0135     actionCollection()->addAction(QStringLiteral("configure_note"), mNoteConfigure);
0136     connect(mNoteConfigure, &QAction::triggered, this, &KNotesPart::slotNotePreferences);
0137 
0138     auto act = new QAction(QIcon::fromTheme(QStringLiteral("configure")), i18n("Preferences KNotes..."), this);
0139     actionCollection()->addAction(QStringLiteral("knotes_configure"), act);
0140     connect(act, &QAction::triggered, this, &KNotesPart::slotPreferences);
0141 
0142     mNoteSendMail = new QAction(QIcon::fromTheme(QStringLiteral("mail-send")), i18n("Mail..."), this);
0143     actionCollection()->addAction(QStringLiteral("mail_note"), mNoteSendMail);
0144     connect(mNoteSendMail, &QAction::triggered, this, &KNotesPart::slotMail);
0145 
0146     mNoteSendNetwork = new QAction(QIcon::fromTheme(QStringLiteral("network-wired")), i18n("Send..."), this);
0147     actionCollection()->addAction(QStringLiteral("send_note"), mNoteSendNetwork);
0148     connect(mNoteSendNetwork, &QAction::triggered, this, &KNotesPart::slotSendToNetwork);
0149 
0150     mNoteSetAlarm = new QAction(QIcon::fromTheme(QStringLiteral("knotes_alarm")), i18n("Set Alarm..."), this);
0151     actionCollection()->addAction(QStringLiteral("set_alarm"), mNoteSetAlarm);
0152     connect(mNoteSetAlarm, &QAction::triggered, this, &KNotesPart::slotSetAlarm);
0153 
0154     act = new QAction(QIcon::fromTheme(QStringLiteral("edit-paste")), i18n("New Note From Clipboard"), this);
0155     actionCollection()->addAction(QStringLiteral("new_note_clipboard"), act);
0156     connect(act, &QAction::triggered, this, &KNotesPart::slotNewNoteFromClipboard);
0157 
0158     act = new QAction(QIcon::fromTheme(QStringLiteral("document-open")), i18n("New Note From Text File..."), this);
0159     actionCollection()->addAction(QStringLiteral("new_note_from_text_file"), act);
0160     connect(act, &QAction::triggered, this, &KNotesPart::slotNewNoteFromTextFile);
0161 
0162     mSaveAs = new QAction(QIcon::fromTheme(QStringLiteral("document-save-as")), i18n("Save As..."), this);
0163     actionCollection()->addAction(QStringLiteral("save_note"), mSaveAs);
0164     connect(mSaveAs, &QAction::triggered, this, &KNotesPart::slotSaveAs);
0165 
0166     mReadOnly = new KToggleAction(QIcon::fromTheme(QStringLiteral("object-locked")), i18n("Lock"), this);
0167     actionCollection()->addAction(QStringLiteral("lock_note"), mReadOnly);
0168     connect(mReadOnly, &KToggleAction::triggered, this, &KNotesPart::slotUpdateReadOnly);
0169     mReadOnly->setCheckedState(KGuiItem(i18n("Unlock"), QStringLiteral("object-unlocked")));
0170 
0171     KStandardAction::find(this, &KNotesPart::slotOpenFindDialog, actionCollection());
0172 
0173     auto session = new Akonadi::Session("KNotes Session", this);
0174     mNoteRecorder = new NoteShared::NotesChangeRecorder(this);
0175     mNoteRecorder->changeRecorder()->setSession(session);
0176     mNoteTreeModel = new NoteShared::NotesAkonadiTreeModel(mNoteRecorder->changeRecorder(), this);
0177 
0178     connect(mNoteTreeModel, &NoteShared::NotesAkonadiTreeModel::rowsInserted, this, &KNotesPart::slotRowInserted);
0179 
0180     connect(mNoteRecorder->changeRecorder(), &Akonadi::Monitor::itemChanged, this, &KNotesPart::slotItemChanged);
0181 
0182     connect(mNoteRecorder->changeRecorder(), &Akonadi::Monitor::itemRemoved, this, &KNotesPart::slotItemRemoved);
0183 
0184     connect(mNoteRecorder->changeRecorder(),
0185             qOverload<const Akonadi::Collection &, const QSet<QByteArray> &>(&Akonadi::ChangeRecorder::collectionChanged),
0186             this,
0187             &KNotesPart::slotCollectionChanged);
0188 
0189     mSelectionModel = new QItemSelectionModel(mNoteTreeModel);
0190     mModelProxy = new KCheckableProxyModel(this);
0191     mModelProxy->setSelectionModel(mSelectionModel);
0192     mModelProxy->setSourceModel(mNoteTreeModel);
0193 
0194     KSharedConfigPtr _config = KSharedConfig::openConfig(QStringLiteral("kcmknotessummaryrc"));
0195 
0196     mModelState = new KViewStateMaintainer<Akonadi::ETMViewStateSaver>(_config->group(QStringLiteral("CheckState")), this);
0197     mModelState->setSelectionModel(mSelectionModel);
0198 
0199     mNotesWidget = new KNotesWidget(this, widget());
0200 
0201     mQuickSearchAction = new QAction(i18n("Set Focus to Quick Search"), this);
0202     // If change shortcut change in quicksearchwidget->lineedit->setPlaceholderText
0203     actionCollection()->setDefaultShortcut(mQuickSearchAction, QKeySequence(Qt::ALT | Qt::Key_Q));
0204     actionCollection()->addAction(QStringLiteral("focus_to_quickseach"), mQuickSearchAction);
0205     connect(mQuickSearchAction, &QAction::triggered, mNotesWidget, &KNotesWidget::slotFocusQuickSearch);
0206 
0207     connect(mNotesWidget->notesView(), &QListWidget::itemDoubleClicked, this, qOverload<QListWidgetItem *>(&KNotesPart::editNote));
0208 
0209     connect(mNotesWidget->notesView(), &QListWidget::itemSelectionChanged, this, &KNotesPart::slotOnCurrentChanged);
0210     slotOnCurrentChanged();
0211 
0212     setWidget(mNotesWidget);
0213     setXMLFile(QStringLiteral("knotes_part.rc"));
0214     updateNetworkListener();
0215     updateClickMessage();
0216 }
0217 
0218 KNotesPart::~KNotesPart()
0219 {
0220     delete mPublisher;
0221     mPublisher = nullptr;
0222 }
0223 
0224 void KNotesPart::updateClickMessage()
0225 {
0226     mNotesWidget->updateClickMessage(mQuickSearchAction->shortcut().toString());
0227 }
0228 
0229 void KNotesPart::slotItemRemoved(const Akonadi::Item &item)
0230 {
0231     KNotesIconViewItem *iconView = mNotesWidget->notesView()->iconView(item.id());
0232     delete iconView;
0233 }
0234 
0235 void KNotesPart::slotRowInserted(const QModelIndex &parent, int start, int end)
0236 {
0237     for (int i = start; i <= end; ++i) {
0238         if (mNoteTreeModel->hasIndex(i, 0, parent)) {
0239             const QModelIndex child = mNoteTreeModel->index(i, 0, parent);
0240             auto parentCollection = mNoteTreeModel->data(child, Akonadi::EntityTreeModel::ParentCollectionRole).value<Akonadi::Collection>();
0241             if (parentCollection.hasAttribute<NoteShared::ShowFolderNotesAttribute>()) {
0242                 auto item = mNoteTreeModel->data(child, Akonadi::EntityTreeModel::ItemRole).value<Akonadi::Item>();
0243                 if (!item.hasPayload<KMime::Message::Ptr>()) {
0244                     continue;
0245                 }
0246                 mNotesWidget->notesView()->addNote(item);
0247             }
0248         }
0249     }
0250 }
0251 
0252 QStringList KNotesPart::notesList() const
0253 {
0254     QStringList notes;
0255     QHashIterator<Akonadi::Item::Id, KNotesIconViewItem *> i(mNotesWidget->notesView()->noteList());
0256     while (i.hasNext()) {
0257         i.next();
0258         notes.append(QString::number(i.key()));
0259     }
0260     return notes;
0261 }
0262 
0263 void KNotesPart::slotPrintPreviewSelectedNotes()
0264 {
0265     printSelectedNotes(true);
0266 }
0267 
0268 void KNotesPart::slotPrintSelectedNotes()
0269 {
0270     printSelectedNotes(false);
0271 }
0272 
0273 void KNotesPart::printSelectedNotes(bool preview)
0274 {
0275     const QList<QListWidgetItem *> lst = mNotesWidget->notesView()->selectedItems();
0276     if (lst.isEmpty()) {
0277         KMessageBox::information(mNotesWidget,
0278                                  i18nc("@info", "To print notes, first select the notes to print from the list."),
0279                                  i18nc("@title:window", "Print Popup Notes"));
0280         return;
0281     }
0282     KNotesGlobalConfig *globalConfig = KNotesGlobalConfig::self();
0283     QString printingTheme = globalConfig->theme();
0284     if (printingTheme.isEmpty()) {
0285         QPointer<KNotePrintSelectThemeDialog> dlg = new KNotePrintSelectThemeDialog(widget());
0286         if (dlg->exec()) {
0287             printingTheme = dlg->selectedTheme();
0288         }
0289         delete dlg;
0290     }
0291     if (!printingTheme.isEmpty()) {
0292         QList<KNotePrintObject *> listPrintObj;
0293         listPrintObj.reserve(lst.count());
0294         for (QListWidgetItem *item : lst) {
0295             listPrintObj.append(new KNotePrintObject(static_cast<KNotesIconViewItem *>(item)->item()));
0296         }
0297         KNotePrinter printer;
0298         printer.printNotes(listPrintObj, printingTheme, preview);
0299         qDeleteAll(listPrintObj);
0300     }
0301 }
0302 
0303 // public KNotes D-Bus interface implementation
0304 
0305 void KNotesPart::newNote(const QString &name, const QString &text)
0306 {
0307     auto job = new NoteShared::CreateNewNoteJob(this, widget());
0308     job->setRichText(KNotesGlobalConfig::self()->richText());
0309     job->setNote(name, text);
0310     job->start();
0311 }
0312 
0313 void KNotesPart::slotNoteCreationFinished(KJob *job)
0314 {
0315     if (job->error()) {
0316         qCWarning(KNOTES_KONTACT_PLUGIN_LOG) << job->errorString();
0317         NoteShared::NoteSharedGlobalConfig::self()->setDefaultFolder(-1);
0318         NoteShared::NoteSharedGlobalConfig::self()->save();
0319         KMessageBox::error(widget(), i18n("Note was not created."), i18n("Create new note"));
0320         return;
0321     }
0322 }
0323 
0324 void KNotesPart::newNoteFromClipboard(const QString &name)
0325 {
0326     const QString &text = QApplication::clipboard()->text();
0327     newNote(name, text);
0328 }
0329 
0330 void KNotesPart::killNote(Akonadi::Item::Id id)
0331 {
0332     killNote(id, false);
0333 }
0334 
0335 void KNotesPart::killNote(Akonadi::Item::Id id, bool force)
0336 {
0337     KNotesIconViewItem *note = mNotesWidget->notesView()->iconView(id);
0338     if (note
0339         && ((!force
0340              && KMessageBox::warningContinueCancelList(mNotesWidget,
0341                                                        i18nc("@info", "Do you really want to delete this note?"),
0342                                                        QStringList(note->realName()),
0343                                                        i18nc("@title:window", "Confirm Delete"),
0344                                                        KStandardGuiItem::del())
0345                  == KMessageBox::Continue)
0346             || force)) {
0347         auto job = new Akonadi::ItemDeleteJob(note->item());
0348         connect(job, &Akonadi::ItemDeleteJob::result, this, &KNotesPart::slotDeleteNotesFinished);
0349     }
0350 }
0351 
0352 QString KNotesPart::name(Akonadi::Item::Id id) const
0353 {
0354     KNotesIconViewItem *note = mNotesWidget->notesView()->iconView(id);
0355     if (note) {
0356         return note->text();
0357     } else {
0358         return {};
0359     }
0360 }
0361 
0362 QString KNotesPart::text(Akonadi::Item::Id id) const
0363 {
0364     // TODO return plaintext ?
0365     KNotesIconViewItem *note = mNotesWidget->notesView()->iconView(id);
0366     if (note) {
0367         return note->description();
0368     } else {
0369         return {};
0370     }
0371 }
0372 
0373 void KNotesPart::setName(Akonadi::Item::Id id, const QString &newName)
0374 {
0375     KNotesIconViewItem *note = mNotesWidget->notesView()->iconView(id);
0376     if (note) {
0377         note->setIconText(newName);
0378     }
0379 }
0380 
0381 void KNotesPart::setText(Akonadi::Item::Id id, const QString &newText)
0382 {
0383     KNotesIconViewItem *note = mNotesWidget->notesView()->iconView(id);
0384     if (note) {
0385         note->setDescription(newText);
0386     }
0387 }
0388 
0389 QMap<QString, QString> KNotesPart::notes() const
0390 {
0391     QMap<QString, QString> notes;
0392     QHashIterator<Akonadi::Item::Id, KNotesIconViewItem *> i(mNotesWidget->notesView()->noteList());
0393     while (i.hasNext()) {
0394         i.next();
0395         notes.insert(QString::number(i.key()), i.value()->realName());
0396     }
0397     return notes;
0398 }
0399 
0400 // private stuff
0401 
0402 void KNotesPart::killSelectedNotes()
0403 {
0404     const QList<QListWidgetItem *> lst = mNotesWidget->notesView()->selectedItems();
0405     if (lst.isEmpty()) {
0406         return;
0407     }
0408     QList<KNotesIconViewItem *> items;
0409     items.reserve(lst.count());
0410     for (QListWidgetItem *item : lst) {
0411         auto knivi = static_cast<KNotesIconViewItem *>(item);
0412         items.append(knivi);
0413     }
0414 
0415     if (items.isEmpty()) {
0416         return;
0417     }
0418     QPointer<KNotesSelectDeleteNotesDialog> dlg = new KNotesSelectDeleteNotesDialog(items, widget());
0419     if (dlg->exec()) {
0420         Akonadi::Item::List lstItem;
0421         QListIterator<KNotesIconViewItem *> kniviIt(items);
0422         while (kniviIt.hasNext()) {
0423             KNotesIconViewItem *iconViewIcon = kniviIt.next();
0424             if (!iconViewIcon->readOnly()) {
0425                 lstItem.append(iconViewIcon->item());
0426             }
0427         }
0428         if (!lstItem.isEmpty()) {
0429             auto job = new Akonadi::ItemDeleteJob(lstItem);
0430             connect(job, &Akonadi::ItemDeleteJob::result, this, &KNotesPart::slotDeleteNotesFinished);
0431         }
0432     }
0433     delete dlg;
0434 }
0435 
0436 void KNotesPart::slotDeleteNotesFinished(KJob *job)
0437 {
0438     if (job->error()) {
0439         qCDebug(KNOTES_KONTACT_PLUGIN_LOG) << " problem during delete job note:" << job->errorString();
0440     }
0441 }
0442 
0443 void KNotesPart::popupRMB(QListWidgetItem *item, const QPoint &pos, const QPoint &globalPos)
0444 {
0445     Q_UNUSED(item)
0446 
0447     auto contextMenu = new QMenu(widget());
0448     if (mNotesWidget->notesView()->itemAt(pos)) {
0449         contextMenu->addAction(mNewNote);
0450         const bool uniqueNoteSelected = (mNotesWidget->notesView()->selectedItems().count() == 1);
0451         const bool readOnly = uniqueNoteSelected ? static_cast<KNotesIconViewItem *>(mNotesWidget->notesView()->selectedItems().at(0))->readOnly() : false;
0452 
0453         if (uniqueNoteSelected) {
0454             if (!readOnly) {
0455                 contextMenu->addSeparator();
0456                 contextMenu->addAction(mNoteSetAlarm);
0457             }
0458             contextMenu->addSeparator();
0459             contextMenu->addAction(mSaveAs);
0460             contextMenu->addSeparator();
0461             contextMenu->addAction(mNoteEdit);
0462             contextMenu->addAction(mReadOnly);
0463             if (!readOnly) {
0464                 contextMenu->addAction(mNoteRename);
0465             }
0466             contextMenu->addSeparator();
0467             contextMenu->addAction(mNoteSendMail);
0468             contextMenu->addSeparator();
0469             contextMenu->addAction(mNoteSendNetwork);
0470         }
0471         contextMenu->addSeparator();
0472         contextMenu->addAction(mNotePrint);
0473         contextMenu->addAction(mNotePrintPreview);
0474 
0475         if (!readOnly) {
0476             contextMenu->addSeparator();
0477             contextMenu->addAction(mNoteConfigure);
0478             contextMenu->addSeparator();
0479             contextMenu->addAction(mNoteDelete);
0480         }
0481     } else {
0482         contextMenu->addAction(mNewNote);
0483     }
0484 
0485     contextMenu->exec(globalPos);
0486     delete contextMenu;
0487 }
0488 
0489 void KNotesPart::editNote(Akonadi::Item::Id id)
0490 {
0491     KNotesIconViewItem *knoteItem = mNotesWidget->notesView()->iconView(id);
0492     if (knoteItem) {
0493         mNotesWidget->notesView()->setCurrentItem(knoteItem);
0494         editNote(knoteItem);
0495     }
0496 }
0497 
0498 void KNotesPart::editNote(QListWidgetItem *item)
0499 {
0500     auto knotesItem = static_cast<KNotesIconViewItem *>(item);
0501     QPointer<KNoteEditDialog> dlg = new KNoteEditDialog(knotesItem->readOnly(), widget());
0502     dlg->setTitle(knotesItem->realName());
0503     dlg->setText(knotesItem->description());
0504     dlg->setColor(knotesItem->textForegroundColor(), knotesItem->textBackgroundColor());
0505 
0506     dlg->setAcceptRichText(knotesItem->isRichText());
0507     dlg->setTabSize(knotesItem->tabSize());
0508     dlg->setAutoIndentMode(knotesItem->autoIndent());
0509     dlg->setTextFont(knotesItem->textFont());
0510 
0511     dlg->setCursorPositionFromStart(knotesItem->cursorPositionFromStart());
0512 
0513     dlg->noteEdit()->setFocus();
0514     if (dlg->exec() == QDialog::Accepted) {
0515         knotesItem->setChangeIconTextAndDescription(dlg->title(), dlg->text(), dlg->cursorPositionFromStart());
0516     }
0517     delete dlg;
0518 }
0519 
0520 void KNotesPart::editNote()
0521 {
0522     QListWidgetItem *item = mNotesWidget->notesView()->currentItem();
0523     if (item) {
0524         editNote(item);
0525     }
0526 }
0527 
0528 void KNotesPart::renameNote()
0529 {
0530     auto knoteItem = static_cast<KNotesIconViewItem *>(mNotesWidget->notesView()->currentItem());
0531 
0532     const QString oldName = knoteItem->realName();
0533     bool ok = false;
0534     const QString newName =
0535         QInputDialog::getText(mNotesWidget, i18nc("@title:window", "Rename Popup Note"), i18nc("@label:textbox", "New Name:"), QLineEdit::Normal, oldName, &ok);
0536     if (ok && (newName != oldName)) {
0537         knoteItem->setIconText(newName);
0538     }
0539 }
0540 
0541 void KNotesPart::slotOnCurrentChanged()
0542 {
0543     const bool uniqueNoteSelected = (mNotesWidget->notesView()->selectedItems().count() == 1);
0544     const bool enabled(mNotesWidget->notesView()->currentItem());
0545     mNoteRename->setEnabled(enabled && uniqueNoteSelected);
0546     mNoteEdit->setEnabled(enabled && uniqueNoteSelected);
0547     mNoteConfigure->setEnabled(uniqueNoteSelected);
0548     mNoteSendMail->setEnabled(uniqueNoteSelected);
0549     mNoteSendNetwork->setEnabled(uniqueNoteSelected);
0550     mNoteSetAlarm->setEnabled(uniqueNoteSelected);
0551     mSaveAs->setEnabled(uniqueNoteSelected);
0552     mReadOnly->setEnabled(uniqueNoteSelected);
0553     if (uniqueNoteSelected) {
0554         const bool readOnly = static_cast<KNotesIconViewItem *>(mNotesWidget->notesView()->selectedItems().at(0))->readOnly();
0555         mReadOnly->setChecked(readOnly);
0556         mNoteEdit->setText(readOnly ? i18n("Show Note...") : i18nc("@action:inmenu", "Edit..."));
0557     } else {
0558         mNoteEdit->setText(i18nc("@action:inmenu", "Edit..."));
0559     }
0560 }
0561 
0562 void KNotesPart::slotNotePreferences()
0563 {
0564     if (!mNotesWidget->notesView()->currentItem()) {
0565         return;
0566     }
0567     auto knoteItem = static_cast<KNotesIconViewItem *>(mNotesWidget->notesView()->currentItem());
0568     QPointer<KNoteSimpleConfigDialog> dialog = new KNoteSimpleConfigDialog(knoteItem->realName(), widget());
0569     Akonadi::Item item = knoteItem->item();
0570     dialog->load(item, knoteItem->isRichText());
0571     if (dialog->exec()) {
0572         KNoteUtils::updateConfiguration();
0573         bool isRichText;
0574         dialog->save(item, isRichText);
0575         auto message = item.payload<KMime::Message::Ptr>();
0576         message->contentType(true)->setMimeType(isRichText ? "text/html" : "text/plain");
0577         message->assemble();
0578         auto job = new Akonadi::ItemModifyJob(item);
0579         connect(job, &Akonadi::ItemModifyJob::result, this, &KNotesPart::slotNoteSaved);
0580     }
0581     delete dialog;
0582 }
0583 
0584 void KNotesPart::slotPreferences()
0585 {
0586     // create a new preferences dialog...
0587     auto dialog = new KNoteConfigDialog(i18n("Settings"), widget());
0588     connect(dialog, qOverload<>(&KCMultiDialog::configCommitted), this, &KNotesPart::slotConfigUpdated);
0589     dialog->show();
0590 }
0591 
0592 void KNotesPart::updateConfig()
0593 {
0594     updateNetworkListener();
0595 }
0596 
0597 void KNotesPart::slotConfigUpdated()
0598 {
0599     updateNetworkListener();
0600 }
0601 
0602 void KNotesPart::slotMail()
0603 {
0604     if (!mNotesWidget->notesView()->currentItem()) {
0605         return;
0606     }
0607     auto knoteItem = static_cast<KNotesIconViewItem *>(mNotesWidget->notesView()->currentItem());
0608     NoteShared::NoteUtils noteUtils;
0609     noteUtils.sendToMail(widget(), knoteItem->realName(), knoteItem->description());
0610 }
0611 
0612 void KNotesPart::slotSendToNetwork()
0613 {
0614     if (!mNotesWidget->notesView()->currentItem()) {
0615         return;
0616     }
0617     auto knoteItem = static_cast<KNotesIconViewItem *>(mNotesWidget->notesView()->currentItem());
0618     NoteShared::NoteUtils noteUtils;
0619     noteUtils.sendToNetwork(widget(), knoteItem->realName(), knoteItem->description());
0620 }
0621 
0622 void KNotesPart::updateNetworkListener()
0623 {
0624     delete mPublisher;
0625     mPublisher = nullptr;
0626 
0627     if (NoteShared::NoteSharedGlobalConfig::receiveNotes()) {
0628         // create the socket and start listening for connections
0629         mPublisher = new KDNSSD::PublicService(NoteShared::NoteSharedGlobalConfig::senderID(),
0630                                                QStringLiteral("_knotes._tcp"),
0631                                                NoteShared::NoteSharedGlobalConfig::port());
0632         mPublisher->publishAsync();
0633     }
0634 }
0635 
0636 void KNotesPart::slotSetAlarm()
0637 {
0638     if (!mNotesWidget->notesView()->currentItem()) {
0639         return;
0640     }
0641     auto knoteItem = static_cast<KNotesIconViewItem *>(mNotesWidget->notesView()->currentItem());
0642     QPointer<NoteShared::NoteAlarmDialog> dlg = new NoteShared::NoteAlarmDialog(knoteItem->realName(), widget());
0643     Akonadi::Item item = knoteItem->item();
0644     if (item.hasAttribute<NoteShared::NoteAlarmAttribute>()) {
0645         dlg->setAlarm(item.attribute<NoteShared::NoteAlarmAttribute>()->dateTime());
0646     }
0647     if (dlg->exec()) {
0648         bool needToModify = true;
0649         QDateTime dateTime = dlg->alarm();
0650         if (dateTime.isValid()) {
0651             auto attribute = item.attribute<NoteShared::NoteAlarmAttribute>(Akonadi::Item::AddIfMissing);
0652             attribute->setDateTime(dateTime);
0653         } else {
0654             if (item.hasAttribute<NoteShared::NoteAlarmAttribute>()) {
0655                 item.removeAttribute<NoteShared::NoteAlarmAttribute>();
0656             } else {
0657                 needToModify = false;
0658             }
0659         }
0660         if (needToModify) {
0661             auto job = new Akonadi::ItemModifyJob(item);
0662             connect(job, &Akonadi::ItemModifyJob::result, this, &KNotesPart::slotNoteSaved);
0663         }
0664     }
0665     delete dlg;
0666 }
0667 
0668 void KNotesPart::slotNoteSaved(KJob *job)
0669 {
0670     qCDebug(KNOTES_KONTACT_PLUGIN_LOG) << " void KNote::slotNoteSaved(KJob *job)";
0671     if (job->error()) {
0672         qCDebug(KNOTES_KONTACT_PLUGIN_LOG) << " problem during save note:" << job->errorString();
0673     }
0674 }
0675 
0676 void KNotesPart::slotNewNoteFromClipboard()
0677 {
0678     const QString &text = QApplication::clipboard()->text();
0679     newNote(QString(), text);
0680 }
0681 
0682 void KNotesPart::slotSaveAs()
0683 {
0684     if (!mNotesWidget->notesView()->currentItem()) {
0685         return;
0686     }
0687     auto knoteItem = static_cast<KNotesIconViewItem *>(mNotesWidget->notesView()->currentItem());
0688 
0689     QStringList filters;
0690     if (knoteItem->isRichText()) {
0691         filters << i18n("Rich text (*.html)");
0692     }
0693     filters << i18n("Plain text (*.txt)");
0694 
0695     QString format;
0696     const QString fileName = QFileDialog::getSaveFileName(widget(), i18n("Save As"), QString(), filters.join(QLatin1StringView(";;")), &format);
0697     if (fileName.isEmpty()) {
0698         return;
0699     }
0700     const bool htmlFormatAndSaveAsHtml = (knoteItem->isRichText() && !format.contains(QLatin1StringView("(*.txt)")));
0701 
0702     QFile file(fileName);
0703     if (file.exists()
0704         && KMessageBox::warningContinueCancel(widget(),
0705                                               i18n("<qt>A file named <b>%1</b> already exists.<br />"
0706                                                    "Are you sure you want to overwrite it?</qt>",
0707                                                    QFileInfo(file).fileName()))
0708             != KMessageBox::Continue) {
0709         return;
0710     }
0711 
0712     if (file.open(QIODevice::WriteOnly)) {
0713         QTextStream stream(&file);
0714         QTextDocument doc;
0715         doc.setHtml(knoteItem->description());
0716         if (htmlFormatAndSaveAsHtml) {
0717             QString htmlStr = doc.toHtml();
0718             htmlStr.replace(QStringLiteral("meta name=\"qrichtext\" content=\"1\""),
0719                             QStringLiteral("meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\""));
0720             stream << htmlStr;
0721         } else {
0722             stream << knoteItem->realName() + QLatin1Char('\n');
0723             stream << doc.toPlainText();
0724         }
0725     }
0726 }
0727 
0728 void KNotesPart::slotUpdateReadOnly()
0729 {
0730     QListWidgetItem *item = mNotesWidget->notesView()->currentItem();
0731     if (!item) {
0732         return;
0733     }
0734     auto knoteItem = static_cast<KNotesIconViewItem *>(item);
0735 
0736     const bool readOnly = mReadOnly->isChecked();
0737 
0738     mNoteEdit->setText(readOnly ? i18n("Show Note...") : i18nc("@action:inmenu", "Edit..."));
0739     knoteItem->setReadOnly(readOnly);
0740 }
0741 
0742 void KNotesPart::slotItemChanged(const Akonadi::Item &item, const QSet<QByteArray> &set)
0743 {
0744     KNotesIconViewItem *knoteItem = mNotesWidget->notesView()->iconView(item.id());
0745     if (knoteItem) {
0746         knoteItem->setChangeItem(item, set);
0747     }
0748 }
0749 
0750 void KNotesPart::slotOpenFindDialog()
0751 {
0752     if (!mNoteFindDialog) {
0753         mNoteFindDialog = new KNoteFindDialog(widget());
0754         connect(mNoteFindDialog.data(), &KNoteFindDialog::noteSelected, this, &KNotesPart::slotSelectNote);
0755     }
0756     QHash<Akonadi::Item::Id, Akonadi::Item> lst;
0757     QHashIterator<Akonadi::Item::Id, KNotesIconViewItem *> i(mNotesWidget->notesView()->noteList());
0758     while (i.hasNext()) {
0759         i.next();
0760         lst.insert(i.key(), i.value()->item());
0761     }
0762     mNoteFindDialog->setExistingNotes(lst);
0763     mNoteFindDialog->show();
0764 }
0765 
0766 void KNotesPart::slotSelectNote(Akonadi::Item::Id id)
0767 {
0768     editNote(id);
0769 }
0770 
0771 void KNotesPart::slotCollectionChanged(const Akonadi::Collection &col, const QSet<QByteArray> &set)
0772 {
0773     if (set.contains("showfoldernotesattribute")) {
0774         // qCDebug(KNOTES_KONTACT_PLUGIN_LOG)<<" collection Changed "<<set<<" col "<<col;
0775         if (col.hasAttribute<NoteShared::ShowFolderNotesAttribute>()) {
0776             fetchNotesFromCollection(col);
0777         } else {
0778             QHashIterator<Akonadi::Item::Id, KNotesIconViewItem *> i(mNotesWidget->notesView()->noteList());
0779             while (i.hasNext()) {
0780                 i.next();
0781                 Akonadi::Item item = i.value()->item();
0782                 if (item.parentCollection() == col) {
0783                     slotItemRemoved(item);
0784                 }
0785             }
0786         }
0787     }
0788 }
0789 
0790 void KNotesPart::fetchNotesFromCollection(const Akonadi::Collection &col)
0791 {
0792     auto job = new Akonadi::ItemFetchJob(col);
0793     job->fetchScope().fetchFullPayload(true);
0794     job->fetchScope().fetchAttribute<NoteShared::NoteLockAttribute>();
0795     job->fetchScope().fetchAttribute<NoteShared::NoteDisplayAttribute>();
0796     job->fetchScope().fetchAttribute<NoteShared::NoteAlarmAttribute>();
0797     job->fetchScope().setAncestorRetrieval(Akonadi::ItemFetchScope::Parent);
0798     connect(job, &Akonadi::ItemFetchJob::result, this, &KNotesPart::slotItemFetchFinished);
0799 }
0800 
0801 void KNotesPart::slotItemFetchFinished(KJob *job)
0802 {
0803     if (job->error()) {
0804         qCDebug(KNOTES_KONTACT_PLUGIN_LOG) << "Error occurred during item fetch:" << job->errorString();
0805         return;
0806     }
0807 
0808     auto fetchJob = qobject_cast<Akonadi::ItemFetchJob *>(job);
0809 
0810     const Akonadi::Item::List items = fetchJob->items();
0811     for (const Akonadi::Item &item : items) {
0812         if (!item.hasPayload<KMime::Message::Ptr>()) {
0813             continue;
0814         }
0815         mNotesWidget->notesView()->addNote(item);
0816     }
0817 }
0818 
0819 void KNotesPart::slotNewNoteFromTextFile()
0820 {
0821     QString text;
0822     const QString filename = QFileDialog::getOpenFileName(widget(), i18n("Select Text File"), QString(), QStringLiteral("%1 (*.txt)").arg(i18n("Text File")));
0823     if (!filename.isEmpty()) {
0824         QFile f(filename);
0825         if (f.open(QIODevice::ReadOnly | QIODevice::Text)) {
0826             text = QString::fromUtf8(f.readAll());
0827         } else {
0828             KMessageBox::error(widget(), i18n("Error during open text file: %1", f.errorString()), i18n("Open Text File"));
0829             return;
0830         }
0831         newNote(i18n("Note from file '%1'", filename), text);
0832     }
0833 }
0834 
0835 #include "moc_knotes_part.cpp"