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"