File indexing completed on 2025-01-19 04:51:19

0001 /*******************************************************************
0002  KNotes -- Notes for the KDE project
0003 
0004  SPDX-FileCopyrightText: 1997-2013 The KNotes Developers
0005 
0006  SPDX-License-Identifier: GPL-2.0-or-later
0007 *******************************************************************/
0008 
0009 #include <config-knotes.h>
0010 
0011 #include "akonadi/notesakonaditreemodel.h"
0012 #include "akonadi/noteschangerecorder.h"
0013 #include "apps/knotesakonaditray.h"
0014 #include "attributes/notealarmattribute.h"
0015 #include "attributes/notedisplayattribute.h"
0016 #include "attributes/notelockattribute.h"
0017 #include "attributes/showfoldernotesattribute.h"
0018 #include "configdialog/knoteconfigdialog.h"
0019 #include "dialog/knotedeleteselectednotesdialog.h"
0020 #include "dialog/knoteskeydialog.h"
0021 #include "finddialog/knotefinddialog.h"
0022 #include "job/createnewnotejob.h"
0023 #include "knotes_debug.h"
0024 #include "knotesadaptor.h"
0025 #include "knotesapp.h"
0026 #include "knotesglobalconfig.h"
0027 #include "notes/knote.h"
0028 #include "notesharedglobalconfig.h"
0029 #include "print/knoteprinter.h"
0030 #include "print/knoteprintselectednotesdialog.h"
0031 #include "resources/localresourcecreator.h"
0032 #include "utils/knoteutils.h"
0033 
0034 #include <Akonadi/ChangeRecorder>
0035 #include <Akonadi/ItemDeleteJob>
0036 #include <Akonadi/ItemFetchJob>
0037 #include <Akonadi/ItemFetchScope>
0038 #include <Akonadi/Session>
0039 
0040 #include <Akonadi/ControlGui>
0041 
0042 #include <KMime/KMimeMessage>
0043 
0044 #include <KActionCollection>
0045 #include <KDNSSD/PublicService>
0046 #include <KGlobalAccel>
0047 #include <KIconEffect>
0048 #include <KLocalizedString>
0049 #include <KMessageBox>
0050 #include <KWindowSystem>
0051 #include <KXMLGUIBuilder>
0052 #include <KXMLGUIFactory>
0053 
0054 #include <KAboutData>
0055 #include <KHelpMenu>
0056 #include <QAction>
0057 #include <QApplication>
0058 #include <QClipboard>
0059 #include <QDBusConnection>
0060 #include <QFileDialog>
0061 #include <QMenu>
0062 #include <QStyle>
0063 #if KDEPIM_HAVE_X11
0064 #include <KWindowInfo>
0065 #include <KX11Extras>
0066 #endif
0067 // signal handler for SIGINT & SIGTERM
0068 #ifdef Q_OS_UNIX
0069 #include <KSignalHandler>
0070 #include <signal.h>
0071 #include <unistd.h>
0072 #endif
0073 static bool qActionLessThan(const QAction *a1, const QAction *a2)
0074 {
0075     return a1->text() < a2->text();
0076 }
0077 
0078 KNotesApp::KNotesApp(QWidget *parent)
0079     : QWidget(parent)
0080 {
0081 #ifdef Q_OS_UNIX
0082     /**
0083      * Set up signal handler for SIGINT and SIGTERM
0084      */
0085     KSignalHandler::self()->watchSignal(SIGINT);
0086     KSignalHandler::self()->watchSignal(SIGTERM);
0087     connect(KSignalHandler::self(), &KSignalHandler::signalReceived, this, [this](int signal) {
0088         if (signal == SIGINT || signal == SIGTERM) {
0089             // Intercept console.
0090             printf("Shutting down... Intercept signal\n");
0091         }
0092     });
0093 #endif
0094 
0095     Akonadi::ControlGui::widgetNeedsAkonadi(this);
0096 
0097     mDebugAkonadiSearch = !qEnvironmentVariableIsEmpty("KDEPIM_DEBUGGING");
0098 
0099     if (KNotesGlobalConfig::self()->autoCreateResourceOnStart()) {
0100         auto creator = new NoteShared::LocalResourceCreator(this);
0101         creator->createIfMissing();
0102     }
0103 
0104     new KNotesAdaptor(this);
0105     QDBusConnection::sessionBus().registerObject(QStringLiteral("/KNotes"), this);
0106     qApp->setQuitOnLastWindowClosed(false);
0107     // create the GUI...
0108     auto action = new QAction(QIcon::fromTheme(QStringLiteral("document-new")), i18n("New Note"), this);
0109     actionCollection()->addAction(QStringLiteral("new_note"), action);
0110     KGlobalAccel::setGlobalShortcut(action, QKeySequence(Qt::ALT | Qt::SHIFT | Qt::Key_N));
0111     connect(action, &QAction::triggered, this, [this]() {
0112         newNote();
0113     });
0114 
0115     action = new QAction(QIcon::fromTheme(QStringLiteral("edit-paste")), i18n("New Note From Clipboard"), this);
0116     actionCollection()->addAction(QStringLiteral("new_note_clipboard"), action);
0117     KGlobalAccel::setGlobalShortcut(action, QKeySequence(Qt::ALT | Qt::SHIFT | Qt::Key_C));
0118     connect(action, &QAction::triggered, this, &KNotesApp::newNoteFromClipboard);
0119 
0120     action = new QAction(QIcon::fromTheme(QStringLiteral("document-open")), i18n("New Note From Text File..."), this);
0121     actionCollection()->addAction(QStringLiteral("new_note_from_text_file"), action);
0122     connect(action, &QAction::triggered, this, &KNotesApp::newNoteFromTextFile);
0123 
0124     action = new QAction(QIcon::fromTheme(QStringLiteral("knotes")), i18n("Show All Notes"), this);
0125     actionCollection()->addAction(QStringLiteral("show_all_notes"), action);
0126     KGlobalAccel::setGlobalShortcut(action, QKeySequence(Qt::ALT | Qt::SHIFT | Qt::Key_S));
0127     connect(action, &QAction::triggered, this, &KNotesApp::showAllNotes);
0128 
0129     action = new QAction(QIcon::fromTheme(QStringLiteral("window-close")), i18n("Hide All Notes"), this);
0130     actionCollection()->addAction(QStringLiteral("hide_all_notes"), action);
0131     KGlobalAccel::setGlobalShortcut(action, QKeySequence(Qt::ALT | Qt::SHIFT | Qt::Key_H));
0132     connect(action, &QAction::triggered, this, &KNotesApp::hideAllNotes);
0133 
0134     action = new QAction(QIcon::fromTheme(QStringLiteral("document-print")), i18nc("@action:inmenu", "Print Selected Notes..."), this);
0135     actionCollection()->addAction(QStringLiteral("print_selected_notes"), action);
0136     connect(action, &QAction::triggered, this, &KNotesApp::slotPrintSelectedNotes);
0137 
0138     QAction *act = KStandardAction::find(this, &KNotesApp::slotOpenFindDialog, actionCollection());
0139     action = new QAction(QIcon::fromTheme(QStringLiteral("edit-delete")), i18nc("@action:inmenu", "Delete Selected Notes..."), this);
0140     actionCollection()->addAction(QStringLiteral("delete_selected_notes"), action);
0141     connect(action, &QAction::triggered, this, &KNotesApp::slotDeleteSelectedNotes);
0142 
0143     // REmove shortcut here.
0144     act->setShortcut(0);
0145 
0146     auto menu = new KHelpMenu(this, KAboutData::applicationData(), false);
0147 
0148     KActionCollection *actions = actionCollection();
0149     QAction *helpContentsAction = menu->action(KHelpMenu::menuHelpContents);
0150     QAction *whatsThisAction = menu->action(KHelpMenu::menuWhatsThis);
0151     QAction *reportBugAction = menu->action(KHelpMenu::menuReportBug);
0152     QAction *switchLanguageAction = menu->action(KHelpMenu::menuSwitchLanguage);
0153     QAction *aboutAppAction = menu->action(KHelpMenu::menuAboutApp);
0154     QAction *aboutKdeAction = menu->action(KHelpMenu::menuAboutKDE);
0155     QAction *donateAction = menu->action(KHelpMenu::menuDonate);
0156 
0157     if (helpContentsAction) {
0158         actions->addAction(helpContentsAction->objectName(), helpContentsAction);
0159     }
0160     if (whatsThisAction) {
0161         actions->addAction(whatsThisAction->objectName(), whatsThisAction);
0162     }
0163     if (reportBugAction) {
0164         actions->addAction(reportBugAction->objectName(), reportBugAction);
0165     }
0166     if (switchLanguageAction) {
0167         actions->addAction(switchLanguageAction->objectName(), switchLanguageAction);
0168     }
0169     if (aboutAppAction) {
0170         actions->addAction(aboutAppAction->objectName(), aboutAppAction);
0171     }
0172     if (aboutKdeAction) {
0173         actions->addAction(aboutKdeAction->objectName(), aboutKdeAction);
0174     }
0175     if (donateAction) {
0176         actions->addAction(donateAction->objectName(), donateAction);
0177     }
0178 
0179     KStandardAction::preferences(this, &KNotesApp::slotPreferences, actionCollection());
0180     KStandardAction::keyBindings(this, &KNotesApp::slotConfigureAccels, actionCollection());
0181     // FIXME: no shortcut removing!?
0182     KStandardAction::quit(this, &KNotesApp::slotQuit, actionCollection())->setShortcut(0);
0183     setXMLFile(QStringLiteral("knotesappui.rc"));
0184 
0185     m_guiBuilder = new KXMLGUIBuilder(this);
0186     m_guiFactory = new KXMLGUIFactory(m_guiBuilder, this);
0187     m_guiFactory->addClient(this);
0188 
0189     QMenu *contextMenu = static_cast<QMenu *>(m_guiFactory->container(QStringLiteral("knotes_context"), this));
0190     m_noteMenu = static_cast<QMenu *>(m_guiFactory->container(QStringLiteral("notes_menu"), this));
0191 
0192     // get the most recent XML UI file
0193     QString xmlFileName(componentName() + QLatin1StringView("ui.rc"));
0194     QString filter(QStringLiteral("kxmlgui5/knotes/") + xmlFileName); // QT5 = componentData().componentName() + QLatin1Char('/') + xmlFileName;
0195     const QStringList fileList = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, filter)
0196         + QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, xmlFileName); // QT5 =
0197     qCDebug(KNOTES_LOG) << " fileList :" << fileList << " filter :" << filter;
0198     QString doc;
0199     KXMLGUIClient::findMostRecentXMLFile(fileList, doc);
0200     m_noteGUI.setContent(doc);
0201     // set up the alarm reminder - do it after loading the notes because this
0202     // is used as a check if updateNoteActions has to be called for a new note
0203     updateNetworkListener();
0204 
0205     auto session = new Akonadi::Session("KNotes Session", this);
0206     mNoteRecorder = new NoteShared::NotesChangeRecorder(this);
0207     mNoteRecorder->changeRecorder()->setSession(session);
0208     mTray = new KNotesAkonadiTray(nullptr);
0209 
0210     connect(mTray, &KStatusNotifierItem::activateRequested, this, &KNotesApp::slotActivateRequested);
0211 
0212     connect(mTray, &KStatusNotifierItem::secondaryActivateRequested, this, &KNotesApp::slotSecondaryActivateRequested);
0213 
0214     mTray->setContextMenu(contextMenu);
0215     mNoteTreeModel = new NoteShared::NotesAkonadiTreeModel(mNoteRecorder->changeRecorder(), this);
0216 
0217     connect(mNoteTreeModel, &QAbstractItemModel::rowsInserted, this, &KNotesApp::slotRowInserted);
0218 
0219     connect(mNoteRecorder->changeRecorder(), &Akonadi::Monitor::itemChanged, this, &KNotesApp::slotItemChanged);
0220 
0221     connect(mNoteRecorder->changeRecorder(), &Akonadi::Monitor::itemRemoved, this, &KNotesApp::slotItemRemoved);
0222 
0223     connect(mNoteRecorder->changeRecorder(),
0224             qOverload<const Akonadi::Collection &, const QSet<QByteArray> &>(&Akonadi::ChangeRecorder::collectionChanged),
0225             this,
0226             &KNotesApp::slotCollectionChanged);
0227 
0228     connect(qApp, &QGuiApplication::commitDataRequest, this, &KNotesApp::slotCommitData, Qt::DirectConnection);
0229 
0230     updateNoteActions();
0231 }
0232 
0233 KNotesApp::~KNotesApp()
0234 {
0235     qDeleteAll(m_noteActions);
0236     m_noteActions.clear();
0237     saveNotes();
0238     delete m_guiBuilder;
0239     delete mTray;
0240     qDeleteAll(mNotes);
0241     mNotes.clear();
0242     delete m_publisher;
0243     m_publisher = nullptr;
0244 }
0245 
0246 void KNotesApp::slotGeneralPaletteChanged()
0247 {
0248     mTray->slotGeneralPaletteChanged();
0249     mTray->updateNumberOfNotes(mNotes.count());
0250 }
0251 
0252 bool KNotesApp::event(QEvent *e)
0253 {
0254     if (e->type() == QEvent::ApplicationPaletteChange) {
0255         slotGeneralPaletteChanged();
0256     }
0257     return QWidget::event(e);
0258 }
0259 
0260 void KNotesApp::slotDeleteSelectedNotes()
0261 {
0262     QPointer<KNoteDeleteSelectedNotesDialog> dlg = new KNoteDeleteSelectedNotesDialog(this);
0263     Akonadi::Item::List lst;
0264     QHashIterator<Akonadi::Item::Id, KNote *> i(mNotes);
0265     while (i.hasNext()) {
0266         i.next();
0267         Akonadi::Item item = i.value()->item();
0268         if (!item.hasAttribute<NoteShared::NoteLockAttribute>()) {
0269             lst.append(item);
0270         }
0271     }
0272     dlg->setNotes(lst);
0273     if (dlg->exec()) {
0274         const Akonadi::Item::List lstItem = dlg->selectedNotes();
0275         if (!lstItem.isEmpty()) {
0276             auto deleteJob = new Akonadi::ItemDeleteJob(lstItem, this);
0277             connect(deleteJob, &KJob::result, this, &KNotesApp::slotNoteDeleteFinished);
0278         }
0279     }
0280     delete dlg;
0281 }
0282 
0283 void KNotesApp::slotItemRemoved(const Akonadi::Item &item)
0284 {
0285     qCDebug(KNOTES_LOG) << " note removed" << item.id();
0286     if (mNotes.contains(item.id())) {
0287         delete mNotes.find(item.id()).value();
0288         mNotes.remove(item.id());
0289         updateNoteActions();
0290         updateSystray();
0291     }
0292 }
0293 
0294 void KNotesApp::slotItemChanged(const Akonadi::Item &item, const QSet<QByteArray> &set)
0295 {
0296     if (mNotes.contains(item.id())) {
0297         qCDebug(KNOTES_LOG) << " item changed " << item.id() << " info " << set.values();
0298         KNote *note = mNotes.value(item.id());
0299         note->setChangeItem(item, set);
0300     }
0301 }
0302 
0303 void KNotesApp::slotRowInserted(const QModelIndex &parent, int start, int end)
0304 {
0305     bool needUpdate = false;
0306     for (int i = start; i <= end; ++i) {
0307         if (mNoteTreeModel->hasIndex(i, 0, parent)) {
0308             const QModelIndex child = mNoteTreeModel->index(i, 0, parent);
0309             auto item = mNoteTreeModel->data(child, Akonadi::EntityTreeModel::ItemRole).value<Akonadi::Item>();
0310             auto parentCollection = mNoteTreeModel->data(child, Akonadi::EntityTreeModel::ParentCollectionRole).value<Akonadi::Collection>();
0311             if (parentCollection.hasAttribute<NoteShared::ShowFolderNotesAttribute>()) {
0312                 createNote(item);
0313                 needUpdate = true;
0314             }
0315         }
0316     }
0317     if (needUpdate) {
0318         updateNoteActions();
0319         updateSystray();
0320     }
0321 }
0322 
0323 void KNotesApp::createNote(const Akonadi::Item &item)
0324 {
0325     if (item.hasPayload<KMime::Message::Ptr>() && !mNotes.contains(item.id())) {
0326         auto note = new KNote(m_noteGUI, item, mDebugAkonadiSearch);
0327         mNotes.insert(item.id(), note);
0328         connect(note, &KNote::sigShowNextNote, this, &KNotesApp::slotWalkThroughNotes);
0329         connect(note, &KNote::sigRequestNewNote, this, [this] {
0330             newNote();
0331         });
0332         connect(note, &KNote::sigNameChanged, this, &KNotesApp::updateNoteActions);
0333         connect(note, &KNote::sigColorChanged, this, &KNotesApp::updateNoteActions);
0334         connect(note, &KNote::sigKillNote, this, &KNotesApp::slotNoteKilled);
0335     }
0336 }
0337 
0338 void KNotesApp::updateSystray()
0339 {
0340     if (KNotesGlobalConfig::self()->systemTrayShowNotes()) {
0341         mTray->updateNumberOfNotes(mNotes.count());
0342     }
0343 }
0344 
0345 void KNotesApp::newNote(const QString &name, const QString &text)
0346 {
0347     auto job = new NoteShared::CreateNewNoteJob(this, this);
0348     job->setRichText(KNotesGlobalConfig::self()->richText());
0349     job->setNote(name, text);
0350     job->start();
0351 }
0352 
0353 void KNotesApp::showNote(Akonadi::Item::Id id) const
0354 {
0355     KNote *note = mNotes.value(id);
0356     if (note) {
0357         showNote(note);
0358     } else {
0359         qCWarning(KNOTES_LOG) << "hideNote: no note with id:" << id;
0360     }
0361 }
0362 
0363 void KNotesApp::showNote(KNote *note) const
0364 {
0365     note->show();
0366 #if KDEPIM_HAVE_X11
0367     if (!note->isDesktopAssigned()) {
0368         note->toDesktop(KX11Extras::currentDesktop());
0369     } else {
0370         KX11Extras::setCurrentDesktop(KWindowInfo(note->winId(), NET::WMDesktop).desktop());
0371     }
0372     KX11Extras::forceActiveWindow(note->winId());
0373 #endif
0374     note->setFocus();
0375 }
0376 
0377 void KNotesApp::hideNote(Akonadi::Item::Id id) const
0378 {
0379     KNote *note = mNotes.value(id);
0380     if (note) {
0381         note->hide();
0382     } else {
0383         qCWarning(KNOTES_LOG) << "hideNote: no note with id:" << id;
0384     }
0385 }
0386 
0387 void KNotesApp::hideAllNotes() const
0388 {
0389     QHashIterator<Akonadi::Item::Id, KNote *> i(mNotes);
0390     while (i.hasNext()) {
0391         i.next();
0392         i.value()->slotClose();
0393     }
0394 }
0395 
0396 void KNotesApp::showAllNotes() const
0397 {
0398     QHashIterator<Akonadi::Item::Id, KNote *> i(mNotes);
0399     while (i.hasNext()) {
0400         i.next();
0401         // workaround to BUG 149116
0402         i.value()->hide();
0403 
0404         i.value()->show();
0405     }
0406 }
0407 
0408 void KNotesApp::newNoteFromClipboard()
0409 {
0410     const QString &text = QApplication::clipboard()->text();
0411     newNote(QString(), text);
0412 }
0413 
0414 void KNotesApp::newNoteFromTextFile()
0415 {
0416     QString text;
0417     const QString filename = QFileDialog::getOpenFileName(this, i18n("Select Text File"), QString(), QStringLiteral("%1 (*.txt)").arg(i18n("Text File")));
0418     if (!filename.isEmpty()) {
0419         QFile f(filename);
0420         if (f.open(QIODevice::ReadOnly | QIODevice::Text)) {
0421             text = QString::fromUtf8(f.readAll());
0422         } else {
0423             KMessageBox::error(this, i18n("Error during open text file: %1", f.errorString()), i18n("Open Text File"));
0424             return;
0425         }
0426         newNote(i18n("Note from file '%1'", filename), text);
0427     }
0428 }
0429 
0430 void KNotesApp::updateNetworkListener()
0431 {
0432     delete m_publisher;
0433     m_publisher = nullptr;
0434 
0435     if (NoteShared::NoteSharedGlobalConfig::receiveNotes()) {
0436         // create the socket and start listening for connections
0437         m_publisher = new KDNSSD::PublicService(NoteShared::NoteSharedGlobalConfig::senderID(),
0438                                                 QStringLiteral("_knotes._tcp"),
0439                                                 NoteShared::NoteSharedGlobalConfig::port());
0440         m_publisher->publishAsync();
0441     }
0442 }
0443 
0444 QString KNotesApp::name(Akonadi::Item::Id id) const
0445 {
0446     KNote *note = mNotes.value(id);
0447     if (note) {
0448         return note->name();
0449     }
0450     return {};
0451 }
0452 
0453 QString KNotesApp::text(Akonadi::Item::Id id) const
0454 {
0455     KNote *note = mNotes.value(id);
0456     if (note) {
0457         return note->text();
0458     }
0459     return {};
0460 }
0461 
0462 void KNotesApp::setName(Akonadi::Item::Id id, const QString &newName)
0463 {
0464     KNote *note = mNotes.value(id);
0465     if (note) {
0466         note->setName(newName);
0467     } else {
0468         qCWarning(KNOTES_LOG) << "setName: no note with id:" << id;
0469     }
0470 }
0471 
0472 void KNotesApp::setText(Akonadi::Item::Id id, const QString &newText)
0473 {
0474     KNote *note = mNotes.value(id);
0475     if (note) {
0476         note->setText(newText);
0477     } else {
0478         qCWarning(KNOTES_LOG) << "setText: no note with id:" << id;
0479     }
0480 }
0481 
0482 void KNotesApp::updateNoteActions()
0483 {
0484     unplugActionList(QStringLiteral("notes"));
0485     m_noteActions.clear();
0486 
0487     QHashIterator<Akonadi::Item::Id, KNote *> i(mNotes);
0488     while (i.hasNext()) {
0489         i.next();
0490         KNote *note = i.value();
0491         QString replaceText;
0492         QString realName = note->name();
0493         if (realName.length() > 50) {
0494             replaceText = realName.left(50) + QLatin1StringView("...");
0495         } else {
0496             replaceText = realName;
0497         }
0498 
0499         auto action = new QAction(replaceText.replace(QLatin1StringView("&"), QStringLiteral("&&")), this);
0500         action->setToolTip(realName);
0501         action->setObjectName(QString::number(note->noteId()));
0502         connect(action, &QAction::triggered, this, &KNotesApp::slotShowNote);
0503         KIconEffect effect;
0504         QPixmap icon = effect.apply(qApp->windowIcon().pixmap(style()->pixelMetric(QStyle::PM_SmallIconSize)),
0505                                     KIconEffect::Colorize,
0506                                     1,
0507                                     note->palette().color(note->backgroundRole()),
0508                                     false);
0509 
0510         action->setIcon(icon);
0511         m_noteActions.append(action);
0512     }
0513 
0514     if (m_noteActions.isEmpty()) {
0515         actionCollection()->action(QStringLiteral("hide_all_notes"))->setEnabled(false);
0516         actionCollection()->action(QStringLiteral("show_all_notes"))->setEnabled(false);
0517         actionCollection()->action(QStringLiteral("print_selected_notes"))->setEnabled(false);
0518         actionCollection()->action(QStringLiteral("edit_find"))->setEnabled(false);
0519         auto action = new QAction(i18n("No Notes"), this);
0520         action->setEnabled(false);
0521         m_noteActions.append(action);
0522     } else {
0523         std::sort(m_noteActions.begin(), m_noteActions.end(), qActionLessThan);
0524         actionCollection()->action(QStringLiteral("hide_all_notes"))->setEnabled(true);
0525         actionCollection()->action(QStringLiteral("show_all_notes"))->setEnabled(true);
0526         actionCollection()->action(QStringLiteral("print_selected_notes"))->setEnabled(true);
0527         actionCollection()->action(QStringLiteral("edit_find"))->setEnabled(true);
0528     }
0529     plugActionList(QStringLiteral("notes"), m_noteActions);
0530 }
0531 
0532 void KNotesApp::slotActivateRequested(bool, const QPoint &)
0533 {
0534     if (mNotes.size() == 1) {
0535         showNote(mNotes.begin().value());
0536     } else {
0537         m_noteMenu->popup(QCursor::pos());
0538     }
0539 }
0540 
0541 void KNotesApp::slotSecondaryActivateRequested(const QPoint &)
0542 {
0543     newNote();
0544 }
0545 
0546 void KNotesApp::slotShowNote()
0547 {
0548     // tell the WM to give this note focus
0549     showNote(sender()->objectName().toLongLong());
0550 }
0551 
0552 void KNotesApp::slotWalkThroughNotes()
0553 {
0554     QHashIterator<Akonadi::Item::Id, KNote *> i(mNotes);
0555     while (i.hasNext()) {
0556         i.next();
0557         KNote *note = i.value();
0558         if (note->hasFocus()) {
0559             if (i.value() != mNotes.end().value()) {
0560                 showNote(i.value());
0561             } else {
0562                 showNote(mNotes.begin().value());
0563             }
0564             break;
0565         }
0566     }
0567 }
0568 
0569 void KNotesApp::slotPreferences()
0570 {
0571     // create a new preferences dialog...
0572     auto dialog = new KNoteConfigDialog(i18n("Settings"), this);
0573     connect(dialog, qOverload<>(&KCMultiDialog::configCommitted), this, &KNotesApp::slotConfigUpdated);
0574     dialog->show();
0575 }
0576 
0577 void KNotesApp::slotConfigUpdated()
0578 {
0579     updateNetworkListener();
0580     KNoteUtils::updateConfiguration();
0581     // Force update if we disable or enable show number in systray
0582     mTray->updateNumberOfNotes(mNotes.count());
0583 }
0584 
0585 void KNotesApp::slotCollectionChanged(const Akonadi::Collection &col, const QSet<QByteArray> &set)
0586 {
0587     if (set.contains("showfoldernotesattribute")) {
0588         // qCDebug(KNOTES_LOG)<<" collection Changed "<<set<<" col "<<col;
0589         if (col.hasAttribute<NoteShared::ShowFolderNotesAttribute>()) {
0590             fetchNotesFromCollection(col);
0591         } else {
0592             QHashIterator<Akonadi::Item::Id, KNote *> i(mNotes);
0593             while (i.hasNext()) {
0594                 i.next();
0595                 Akonadi::Item item = i.value()->item();
0596                 if (item.parentCollection() == col) {
0597                     slotItemRemoved(item);
0598                 }
0599             }
0600         }
0601     }
0602 }
0603 
0604 void KNotesApp::slotConfigureAccels()
0605 {
0606     QPointer<KNotesKeyDialog> keys = new KNotesKeyDialog(actionCollection(), this);
0607 
0608     KActionCollection *actionCollection = nullptr;
0609     if (!mNotes.isEmpty()) {
0610         actionCollection = mNotes.begin().value()->actionCollection();
0611         keys->insert(actionCollection);
0612     }
0613     if (keys->exec()) {
0614         keys->save();
0615         // update GUI doc for new notes
0616         m_noteGUI.setContent(KXMLGUIFactory::readConfigFile(componentName() + QLatin1StringView("ui.rc"), componentName()));
0617 
0618         if (actionCollection) {
0619             QHashIterator<Akonadi::Item::Id, KNote *> i(mNotes);
0620             while (i.hasNext()) {
0621                 i.next();
0622                 const auto lst = actionCollection->actions();
0623                 for (QAction *action : lst) {
0624                     QAction *toChange = i.value()->actionCollection()->action(action->objectName());
0625                     if (toChange) {
0626                         toChange->setShortcuts(action->shortcuts());
0627                     }
0628                 }
0629             }
0630         }
0631     }
0632     delete keys;
0633 }
0634 
0635 void KNotesApp::slotNoteKilled(Akonadi::Item::Id id)
0636 {
0637     auto deleteJob = new Akonadi::ItemDeleteJob(Akonadi::Item(id), this);
0638     connect(deleteJob, &KJob::result, this, &KNotesApp::slotNoteDeleteFinished);
0639 }
0640 
0641 void KNotesApp::slotNoteDeleteFinished(KJob *job)
0642 {
0643     if (job->error()) {
0644         qCWarning(KNOTES_LOG) << job->errorString();
0645         return;
0646     }
0647 }
0648 
0649 void KNotesApp::slotPrintSelectedNotes()
0650 {
0651     QPointer<KNotePrintSelectedNotesDialog> dlg = new KNotePrintSelectedNotesDialog(this);
0652     dlg->setNotes(mNotes);
0653     if (dlg->exec()) {
0654         const QList<KNotePrintObject *> lst = dlg->selectedNotes();
0655         if (!lst.isEmpty()) {
0656             const QString selectedTheme = dlg->selectedTheme();
0657             KNotePrinter printer;
0658             printer.printNotes(lst, selectedTheme, dlg->preview());
0659             qDeleteAll(lst);
0660         }
0661     }
0662     delete dlg;
0663 }
0664 
0665 void KNotesApp::saveNotes(bool force, bool sync)
0666 {
0667     KNotesGlobalConfig::self()->save();
0668     QHashIterator<Akonadi::Item::Id, KNote *> i(mNotes);
0669     while (i.hasNext()) {
0670         i.next();
0671         i.value()->saveNote(force, sync);
0672     }
0673 }
0674 
0675 void KNotesApp::slotQuit()
0676 {
0677     saveNotes(true, true);
0678     qApp->quit();
0679 }
0680 
0681 void KNotesApp::slotCommitData(QSessionManager &)
0682 {
0683     saveNotes(true, true);
0684 }
0685 
0686 void KNotesApp::slotSelectNote(Akonadi::Item::Id id)
0687 {
0688     showNote(id);
0689 }
0690 
0691 void KNotesApp::slotOpenFindDialog()
0692 {
0693     if (!mFindDialog) {
0694         mFindDialog = new KNoteFindDialog(this);
0695         connect(mFindDialog.data(), &KNoteFindDialog::noteSelected, this, &KNotesApp::slotSelectNote);
0696     }
0697     QHash<Akonadi::Item::Id, Akonadi::Item> lst;
0698 
0699     QHashIterator<Akonadi::Item::Id, KNote *> i(mNotes);
0700     while (i.hasNext()) {
0701         i.next();
0702         lst.insert(i.key(), i.value()->item());
0703     }
0704     mFindDialog->setExistingNotes(lst);
0705     mFindDialog->show();
0706 }
0707 
0708 void KNotesApp::fetchNotesFromCollection(const Akonadi::Collection &col)
0709 {
0710     auto job = new Akonadi::ItemFetchJob(col);
0711     job->fetchScope().fetchFullPayload(true);
0712     job->fetchScope().fetchAttribute<NoteShared::NoteLockAttribute>();
0713     job->fetchScope().fetchAttribute<NoteShared::NoteDisplayAttribute>();
0714     job->fetchScope().fetchAttribute<NoteShared::NoteAlarmAttribute>();
0715     job->fetchScope().setAncestorRetrieval(Akonadi::ItemFetchScope::Parent);
0716     connect(job, &KJob::result, this, &KNotesApp::slotItemFetchFinished);
0717 }
0718 
0719 void KNotesApp::slotItemFetchFinished(KJob *job)
0720 {
0721     if (job->error()) {
0722         qCDebug(KNOTES_LOG) << "Error occurred during item fetch:" << job->errorString();
0723         return;
0724     }
0725 
0726     auto fetchJob = qobject_cast<Akonadi::ItemFetchJob *>(job);
0727 
0728     const Akonadi::Item::List items = fetchJob->items();
0729     for (const Akonadi::Item &item : items) {
0730         createNote(item);
0731     }
0732     if (!items.isEmpty()) {
0733         updateNoteActions();
0734         updateSystray();
0735     }
0736 }
0737 
0738 #include "moc_knotesapp.cpp"