File indexing completed on 2024-05-12 05:12:47
0001 /* 0002 SPDX-FileCopyrightText: 2009 Volker Krause <vkrause@kde.org> 0003 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "notificationmonitor.h" 0008 #include "notificationfiltermodel.h" 0009 #include "notificationmodel.h" 0010 #include "utils.h" 0011 0012 #include <Akonadi/ControlGui> 0013 0014 #include <QCheckBox> 0015 #include <QFile> 0016 #include <QFileDialog> 0017 #include <QHeaderView> 0018 #include <QItemSelectionModel> 0019 #include <QJsonArray> 0020 #include <QJsonDocument> 0021 #include <QJsonObject> 0022 #include <QLabel> 0023 #include <QMenu> 0024 #include <QMessageBox> 0025 #include <QPushButton> 0026 #include <QSplitter> 0027 #include <QStandardItem> 0028 #include <QStandardItemModel> 0029 #include <QTreeView> 0030 #include <QVBoxLayout> 0031 0032 #include <KConfigGroup> 0033 #include <KLocalizedString> 0034 #include <KSharedConfig> 0035 #include <Libkdepim/KCheckComboBox> 0036 0037 using KPIM::KCheckComboBox; 0038 0039 #ifndef COMPILE_WITH_UNITY_CMAKE_SUPPORT 0040 Q_DECLARE_METATYPE(Akonadi::ChangeNotification) 0041 #endif 0042 0043 NotificationMonitor::NotificationMonitor(QWidget *parent) 0044 : QWidget(parent) 0045 { 0046 m_model = new NotificationModel(this); 0047 m_model->setEnabled(false); // since it can be slow, default to off 0048 0049 m_filterModel = new NotificationFilterModel(this); 0050 m_filterModel->setSourceModel(m_model); 0051 0052 auto layout = new QVBoxLayout(this); 0053 auto hLayout = new QHBoxLayout; 0054 layout->addLayout(hLayout); 0055 0056 auto enableCB = new QCheckBox(this); 0057 enableCB->setText(i18n("Enable notification monitor")); 0058 enableCB->setChecked(m_model->isEnabled()); 0059 connect(enableCB, &QCheckBox::toggled, m_model, &NotificationModel::setEnabled); 0060 hLayout->addWidget(enableCB); 0061 0062 hLayout->addWidget(new QLabel(i18n("Types:"), this)); 0063 hLayout->addWidget(mTypeFilterCombo = new KCheckComboBox(this)); 0064 hLayout->addStretch(); 0065 mTypeFilterCombo->setSizeAdjustPolicy(QComboBox::AdjustToContents); 0066 mTypeFilterCombo->setMinimumWidth(fontMetrics().boundingRect(QStringLiteral("Subscription,Items,Collections")).width() 0067 + 60); // make it wide enough for most use cases 0068 m_filterModel->setTypeFilter(mTypeFilterCombo); 0069 0070 m_splitter = new QSplitter(this); 0071 layout->addWidget(m_splitter); 0072 0073 m_treeView = new QTreeView(this); 0074 m_treeView->setModel(m_filterModel); 0075 m_treeView->expandAll(); 0076 m_treeView->setAlternatingRowColors(true); 0077 m_treeView->setContextMenuPolicy(Qt::CustomContextMenu); 0078 m_treeView->header()->setSectionResizeMode(QHeaderView::ResizeToContents); 0079 connect(m_treeView, &QTreeView::customContextMenuRequested, this, &NotificationMonitor::contextMenu); 0080 connect(m_treeView->selectionModel(), &QItemSelectionModel::currentChanged, this, &NotificationMonitor::onNotificationSelected); 0081 m_splitter->addWidget(m_treeView); 0082 0083 m_ntfView = new QTreeView(this); 0084 m_ntfView->setModel(new QStandardItemModel(this)); 0085 m_ntfView->setEditTriggers(QAbstractItemView::NoEditTriggers); 0086 m_splitter->addWidget(m_ntfView); 0087 0088 auto h = new QHBoxLayout; 0089 layout->addLayout(h); 0090 0091 auto btn = new QPushButton(i18n("Save to File...")); 0092 connect(btn, &QPushButton::clicked, this, &NotificationMonitor::saveToFile); 0093 h->addWidget(btn); 0094 h->addStretch(1); 0095 0096 onNotificationSelected({}); 0097 0098 KConfigGroup config(KSharedConfig::openConfig(), QStringLiteral("NotificationMonitor")); 0099 m_treeView->header()->restoreState(config.readEntry<QByteArray>("tv", QByteArray())); 0100 m_ntfView->header()->restoreState(config.readEntry<QByteArray>("ntfView", QByteArray())); 0101 m_splitter->restoreState(config.readEntry<QByteArray>("splitter", QByteArray())); 0102 0103 Akonadi::ControlGui::widgetNeedsAkonadi(this); 0104 } 0105 0106 NotificationMonitor::~NotificationMonitor() 0107 { 0108 KConfigGroup config(KSharedConfig::openConfig(), QStringLiteral("NotificationMonitor")); 0109 config.writeEntry("tv", m_treeView->header()->saveState()); 0110 config.writeEntry("ntfView", m_ntfView->header()->saveState()); 0111 config.writeEntry("splitter", m_splitter->saveState()); 0112 } 0113 0114 void NotificationMonitor::contextMenu(const QPoint & /*pos*/) 0115 { 0116 QMenu menu; 0117 menu.addAction(i18n("Clear View"), m_model, &NotificationModel::clear); 0118 menu.exec(QCursor::pos()); 0119 } 0120 0121 void NotificationMonitor::onNotificationSelected(const QModelIndex &index) 0122 { 0123 const auto state = m_ntfView->header()->saveState(); 0124 populateNtfModel(index); 0125 m_ntfView->header()->restoreState(state); 0126 m_ntfView->expandAll(); 0127 } 0128 0129 void NotificationMonitor::populateNtfModel(const QModelIndex &index) 0130 { 0131 auto model = qobject_cast<QStandardItemModel *>(m_ntfView->model()); 0132 model->clear(); 0133 model->setHorizontalHeaderLabels({i18n("Properties"), i18n("Values")}); 0134 0135 const auto ntf = index.data(NotificationModel::NotificationRole).value<Akonadi::ChangeNotification>(); 0136 if (!ntf.isValid()) { 0137 return; 0138 } 0139 0140 appendRow(model, i18n("Timestamp"), ntf.timestamp().toString(Qt::ISODateWithMs)); 0141 appendRow(model, i18n("Type"), index.sibling(index.row(), NotificationModel::TypeColumn).data().toString()); 0142 appendRow(model, i18n("Listeners"), index.sibling(index.row(), NotificationModel::ListenersColumn).data().toString()); 0143 switch (ntf.type()) { 0144 case Akonadi::ChangeNotification::Items: 0145 populateItemNtfTree(model, Akonadi::Protocol::cmdCast<Akonadi::Protocol::ItemChangeNotification>(ntf.notification())); 0146 break; 0147 case Akonadi::ChangeNotification::Collection: 0148 populateCollectionNtfTree(model, Akonadi::Protocol::cmdCast<Akonadi::Protocol::CollectionChangeNotification>(ntf.notification())); 0149 break; 0150 case Akonadi::ChangeNotification::Tag: 0151 populateTagNtfTree(model, Akonadi::Protocol::cmdCast<Akonadi::Protocol::TagChangeNotification>(ntf.notification())); 0152 break; 0153 case Akonadi::ChangeNotification::Relation: 0154 populateRelationNtfTree(model, Akonadi::Protocol::cmdCast<Akonadi::Protocol::RelationChangeNotification>(ntf.notification())); 0155 break; 0156 case Akonadi::ChangeNotification::Subscription: 0157 populateSubscriptionNtfTree(model, Akonadi::Protocol::cmdCast<Akonadi::Protocol::SubscriptionChangeNotification>(ntf.notification())); 0158 break; 0159 } 0160 } 0161 0162 void NotificationMonitor::populateItemNtfTree(QStandardItemModel *model, const Akonadi::Protocol::ItemChangeNotification &ntf) 0163 { 0164 QString operation; 0165 switch (ntf.operation()) { 0166 case Akonadi::Protocol::ItemChangeNotification::Add: 0167 operation = i18n("Add"); 0168 break; 0169 case Akonadi::Protocol::ItemChangeNotification::Modify: 0170 operation = i18n("Modify"); 0171 break; 0172 case Akonadi::Protocol::ItemChangeNotification::Move: 0173 operation = i18n("Move"); 0174 break; 0175 case Akonadi::Protocol::ItemChangeNotification::Remove: 0176 operation = i18n("Remove"); 0177 break; 0178 case Akonadi::Protocol::ItemChangeNotification::Link: 0179 operation = i18n("Link"); 0180 break; 0181 case Akonadi::Protocol::ItemChangeNotification::Unlink: 0182 operation = i18n("Unlink"); 0183 break; 0184 case Akonadi::Protocol::ItemChangeNotification::ModifyFlags: 0185 operation = i18n("ModifyFlags"); 0186 break; 0187 case Akonadi::Protocol::ItemChangeNotification::ModifyTags: 0188 operation = i18n("ModifyTags"); 0189 break; 0190 case Akonadi::Protocol::ItemChangeNotification::ModifyRelations: 0191 operation = i18n("ModifyRelations"); 0192 break; 0193 case Akonadi::Protocol::ItemChangeNotification::InvalidOp: 0194 operation = i18n("InvalidOp"); 0195 break; 0196 } 0197 0198 appendRow(model, i18n("Operation"), operation); 0199 appendRow(model, i18n("Resource"), QString::fromUtf8(ntf.resource())); 0200 appendRow(model, i18n("Parent Collection"), QString::number(ntf.parentCollection())); 0201 appendRow(model, i18n("Parent Dest Col"), QString::number(ntf.parentDestCollection())); 0202 appendRow(model, i18n("Destination Resource"), QString::fromUtf8(ntf.destinationResource())); 0203 appendRow(model, i18n("Item Parts"), toString(ntf.itemParts())); 0204 appendRow(model, i18n("Added Flags"), toString(ntf.addedFlags())); 0205 appendRow(model, i18n("Removed Flags"), toString(ntf.removedFlags())); 0206 appendRow(model, i18n("Added Tags"), toString(ntf.addedTags())); 0207 appendRow(model, i18n("Removed Tags"), toString(ntf.removedTags())); 0208 auto relationsItem = new QStandardItem(QStringLiteral("Added Relations")); 0209 const auto addedRelations = ntf.addedRelations(); 0210 for (const auto &addedRelation : addedRelations) { 0211 auto item = new QStandardItem( 0212 QStringLiteral("%lld-%lld %s").arg(QString::number(addedRelation.leftId), QString::number(addedRelation.rightId), addedRelation.type)); 0213 relationsItem->appendRow(item); 0214 } 0215 model->appendRow(relationsItem); 0216 0217 relationsItem = new QStandardItem(i18n("Removed Relations")); 0218 const auto removedRelations = ntf.removedRelations(); 0219 for (const auto &removedRelation : removedRelations) { 0220 auto item = new QStandardItem( 0221 QStringLiteral("%lld-%lld %s").arg(QString::number(removedRelation.leftId), QString::number(removedRelation.rightId), removedRelation.type)); 0222 relationsItem->appendRow(item); 0223 } 0224 model->appendRow(relationsItem); 0225 0226 appendRow(model, i18n("Must retrieve"), toString(ntf.mustRetrieve())); 0227 0228 auto itemsItem = new QStandardItem(i18n("Items")); 0229 const auto &items = ntf.items(); 0230 for (const auto &item : items) { 0231 auto i = new QStandardItem(QString::number(item.id())); 0232 populateItemTree(i, item); 0233 itemsItem->appendRow(i); 0234 } 0235 model->appendRow(itemsItem); 0236 } 0237 0238 QStandardItem *NotificationMonitor::populateAncestorTree(QStandardItem *parent, const Akonadi::Protocol::Ancestor &ancestor) 0239 { 0240 appendRow(parent, i18n("id"), QString::number(ancestor.id())); 0241 appendRow(parent, i18n("remoteId"), ancestor.remoteId()); 0242 appendRow(parent, i18n("name"), ancestor.name()); 0243 populateAttributesTree(parent, ancestor.attributes()); 0244 auto ancestorItem = new QStandardItem(i18n("Ancestor")); 0245 parent->appendRow(ancestorItem); 0246 return ancestorItem; 0247 } 0248 0249 void NotificationMonitor::populateTagTree(QStandardItem *parent, const Akonadi::Protocol::FetchTagsResponse &tag) 0250 { 0251 appendRow(parent, i18n("ID"), QString::number(tag.id())); 0252 appendRow(parent, i18n("Parent ID"), QString::number(tag.parentId())); 0253 appendRow(parent, i18n("GID"), QString::fromUtf8(tag.gid())); 0254 appendRow(parent, i18n("Type"), QString::fromUtf8(tag.type())); 0255 appendRow(parent, i18n("Remote ID"), QString::fromUtf8(tag.remoteId())); 0256 populateAttributesTree(parent, tag.attributes()); 0257 } 0258 0259 void NotificationMonitor::populateAttributesTree(QStandardItem *parent, const Akonadi::Protocol::Attributes &attributes) 0260 { 0261 auto attributesItem = new QStandardItem(i18n("Attributes")); 0262 for (auto it = attributes.cbegin(), end = attributes.cend(); it != end; ++it) { 0263 appendRow(attributesItem, QString::fromUtf8(it.key()), QString::fromUtf8(it.value())); 0264 } 0265 parent->appendRow(attributesItem); 0266 } 0267 0268 void NotificationMonitor::populateItemTree(QStandardItem *parent, const Akonadi::Protocol::FetchItemsResponse &item) 0269 { 0270 appendRow(parent, i18n("Revision"), QString::number(item.revision())); 0271 appendRow(parent, i18n("ParentID"), QString::number(item.parentId())); 0272 appendRow(parent, i18n("RemoteID"), item.remoteId()); 0273 appendRow(parent, i18n("RemoteRev"), item.remoteRevision()); 0274 appendRow(parent, i18n("GID"), item.gid()); 0275 appendRow(parent, i18n("Size"), QString::number(item.size())); 0276 appendRow(parent, i18n("MimeType"), item.mimeType()); 0277 appendRow(parent, i18n("MTime"), item.mTime().toString(Qt::ISODate)); 0278 appendRow(parent, i18n("Flags"), toString(item.flags())); 0279 auto tagItem = new QStandardItem(i18n("Tags")); 0280 const auto tags = item.tags(); 0281 for (const auto &tag : tags) { 0282 auto item = new QStandardItem(QString::number(tag.id())); 0283 populateTagTree(item, tag); 0284 tagItem->appendRow(item); 0285 } 0286 parent->appendRow(tagItem); 0287 0288 appendRow(parent, i18n("VRefs"), toString(item.virtualReferences())); 0289 auto relationItem = new QStandardItem(i18n("Relations")); 0290 const auto relations = item.relations(); 0291 for (const auto &relation : relations) { 0292 auto item = new QStandardItem( 0293 QStringLiteral("%lld-%lld %s").arg(QString::number(relation.left()), QString::number(relation.right()), QString::fromUtf8(relation.type()))); 0294 relationItem->appendRow(item); 0295 } 0296 parent->appendRow(relationItem); 0297 0298 const auto ancestors = item.ancestors(); 0299 auto i = new QStandardItem(i18n("Ancestor")); 0300 parent->appendRow(i); 0301 for (const auto &ancestor : ancestors) { 0302 i = populateAncestorTree(i, ancestor); 0303 } 0304 0305 auto partsItem = new QStandardItem(i18n("Parts")); 0306 const auto parts = item.parts(); 0307 for (const auto &part : parts) { 0308 auto item = new QStandardItem(QString::fromUtf8(part.payloadName())); 0309 QString type; 0310 switch (part.metaData().storageType()) { 0311 case Akonadi::Protocol::PartMetaData::External: 0312 type = i18n("External"); 0313 break; 0314 case Akonadi::Protocol::PartMetaData::Internal: 0315 type = i18n("Internal"); 0316 break; 0317 case Akonadi::Protocol::PartMetaData::Foreign: 0318 type = i18n("Foreign"); 0319 break; 0320 } 0321 appendRow(item, i18n("Size"), QString::number(part.metaData().size())); 0322 appendRow(item, i18n("Storage Type"), type); 0323 appendRow(item, i18n("Version"), QString::number(part.metaData().version())); 0324 appendRow(item, i18n("Data"), QString::fromUtf8(part.data().toHex())); 0325 0326 partsItem->appendRow(item); 0327 } 0328 parent->appendRow(partsItem); 0329 } 0330 0331 void NotificationMonitor::populateCollectionTree(QStandardItem *parent, const Akonadi::Protocol::FetchCollectionsResponse &collection) 0332 { 0333 appendRow(parent, i18n("ID"), QString::number(collection.id())); 0334 appendRow(parent, i18n("Parent ID"), QString::number(collection.parentId())); 0335 appendRow(parent, i18n("Name"), collection.name()); 0336 appendRow(parent, i18n("Mime Types"), toString(static_cast<QList<QString>>(collection.mimeTypes()))); 0337 appendRow(parent, i18n("Remote ID"), collection.remoteId()); 0338 appendRow(parent, i18n("Remote Revision"), collection.remoteRevision()); 0339 auto statsItem = new QStandardItem(i18n("Statistics")); 0340 const auto stats = collection.statistics(); 0341 appendRow(statsItem, i18n("Count"), QString::number(stats.count())); 0342 appendRow(statsItem, i18n("Unseen"), QString::number(stats.unseen())); 0343 appendRow(statsItem, i18n("Size"), QString::number(stats.size())); 0344 parent->appendRow(statsItem); 0345 appendRow(parent, i18n("Search Query"), collection.searchQuery()); 0346 appendRow(parent, i18n("Search Collections"), toString(collection.searchCollections())); 0347 auto i = new QStandardItem(i18n("Ancestor")); 0348 parent->appendRow(i); 0349 const auto ancestors = collection.ancestors(); 0350 for (const auto &ancestor : ancestors) { 0351 i = populateAncestorTree(i, ancestor); 0352 } 0353 auto cpItem = new QStandardItem(i18n("Cache Policy")); 0354 const auto cp = collection.cachePolicy(); 0355 appendRow(cpItem, i18n("Inherit"), toString(cp.inherit())); 0356 appendRow(cpItem, i18n("Check Interval"), QString::number(cp.checkInterval())); 0357 appendRow(cpItem, i18n("Cache Timeout"), QString::number(cp.cacheTimeout())); 0358 appendRow(cpItem, i18n("Sync on Demand"), toString(cp.syncOnDemand())); 0359 appendRow(cpItem, i18n("Local Parts"), toString(static_cast<QList<QString>>(cp.localParts()))); 0360 parent->appendRow(cpItem); 0361 0362 populateAttributesTree(parent, collection.attributes()); 0363 0364 appendRow(parent, i18n("Enabled"), toString(collection.enabled())); 0365 appendRow(parent, i18n("DisplayPref"), toString(collection.displayPref())); 0366 appendRow(parent, i18n("SyncPref"), toString(collection.syncPref())); 0367 appendRow(parent, i18n("IndexPref"), toString(collection.indexPref())); 0368 appendRow(parent, i18n("Virtual"), toString(collection.isVirtual())); 0369 } 0370 0371 void NotificationMonitor::populateCollectionNtfTree(QStandardItemModel *model, const Akonadi::Protocol::CollectionChangeNotification &ntf) 0372 { 0373 QString operation; 0374 switch (ntf.operation()) { 0375 case Akonadi::Protocol::CollectionChangeNotification::Add: 0376 operation = i18n("Add"); 0377 break; 0378 case Akonadi::Protocol::CollectionChangeNotification::Modify: 0379 operation = i18n("Modify"); 0380 break; 0381 case Akonadi::Protocol::CollectionChangeNotification::Move: 0382 operation = i18n("Move"); 0383 break; 0384 case Akonadi::Protocol::CollectionChangeNotification::Remove: 0385 operation = i18n("Remove"); 0386 break; 0387 case Akonadi::Protocol::CollectionChangeNotification::Subscribe: 0388 operation = i18n("Subscribe"); 0389 break; 0390 case Akonadi::Protocol::CollectionChangeNotification::Unsubscribe: 0391 operation = i18n("Unsubscribe"); 0392 break; 0393 case Akonadi::Protocol::CollectionChangeNotification::InvalidOp: 0394 operation = i18n("InvalidIp"); 0395 break; 0396 } 0397 appendRow(model, i18n("Operation"), operation); 0398 appendRow(model, i18n("Resource"), QString::fromUtf8(ntf.resource())); 0399 appendRow(model, i18n("Parent Collection"), QString::number(ntf.parentCollection())); 0400 appendRow(model, i18n("Parent Dest Collection"), QString::number(ntf.parentDestCollection())); 0401 appendRow(model, i18n("Destination Resource"), QString::fromUtf8(ntf.destinationResource())); 0402 appendRow(model, i18n("Changed Parts"), toString(ntf.changedParts())); 0403 auto item = new QStandardItem(i18n("Collection")); 0404 populateCollectionTree(item, ntf.collection()); 0405 model->appendRow(item); 0406 } 0407 0408 void NotificationMonitor::populateRelationNtfTree(QStandardItemModel *model, const Akonadi::Protocol::RelationChangeNotification &ntf) 0409 { 0410 QString operation; 0411 switch (ntf.operation()) { 0412 case Akonadi::Protocol::RelationChangeNotification::Add: 0413 operation = i18n("Add"); 0414 break; 0415 case Akonadi::Protocol::RelationChangeNotification::Remove: 0416 operation = i18n("Remove"); 0417 break; 0418 case Akonadi::Protocol::RelationChangeNotification::InvalidOp: 0419 operation = i18n("InvalidOp"); 0420 break; 0421 } 0422 appendRow(model, i18n("Operation"), operation); 0423 auto item = new QStandardItem(i18n("Relation")); 0424 const auto rel = ntf.relation(); 0425 appendRow(item, i18n("Left ID"), QString::number(rel.left())); 0426 appendRow(item, i18n("Left MimeType"), QString::fromUtf8(rel.leftMimeType())); 0427 appendRow(item, i18n("Right ID"), QString::number(rel.right())); 0428 appendRow(item, i18n("Right MimeType"), QString::fromUtf8(rel.rightMimeType())); 0429 appendRow(item, i18n("Remote ID"), QString::fromUtf8(rel.remoteId())); 0430 model->appendRow(item); 0431 } 0432 0433 void NotificationMonitor::populateTagNtfTree(QStandardItemModel *model, const Akonadi::Protocol::TagChangeNotification &ntf) 0434 { 0435 QString operation; 0436 switch (ntf.operation()) { 0437 case Akonadi::Protocol::TagChangeNotification::Add: 0438 operation = i18n("Add"); 0439 break; 0440 case Akonadi::Protocol::TagChangeNotification::Modify: 0441 operation = i18n("Modify"); 0442 break; 0443 case Akonadi::Protocol::TagChangeNotification::Remove: 0444 operation = i18n("Remove"); 0445 break; 0446 case Akonadi::Protocol::TagChangeNotification::InvalidOp: 0447 operation = i18n("InvalidOp"); 0448 break; 0449 } 0450 appendRow(model, i18n("Operation"), operation); 0451 appendRow(model, i18n("Resource"), QString::fromUtf8(ntf.resource())); 0452 auto tagItem = new QStandardItem(i18n("Tag")); 0453 populateTagTree(tagItem, ntf.tag()); 0454 model->appendRow(tagItem); 0455 } 0456 0457 void NotificationMonitor::populateSubscriptionNtfTree(QStandardItemModel *model, const Akonadi::Protocol::SubscriptionChangeNotification &ntf) 0458 { 0459 QString operation; 0460 switch (ntf.operation()) { 0461 case Akonadi::Protocol::SubscriptionChangeNotification::Add: 0462 operation = i18n("Add"); 0463 break; 0464 case Akonadi::Protocol::SubscriptionChangeNotification::Modify: 0465 operation = i18n("Modify"); 0466 break; 0467 case Akonadi::Protocol::SubscriptionChangeNotification::Remove: 0468 operation = i18n("Remove"); 0469 break; 0470 case Akonadi::Protocol::SubscriptionChangeNotification::InvalidOp: 0471 operation = i18n("InvalidOp"); 0472 break; 0473 } 0474 appendRow(model, i18n("Operation"), operation); 0475 appendRow(model, i18n("Subscriber"), QString::fromUtf8(ntf.subscriber())); 0476 appendRow(model, i18n("Monitored Collections"), toString(ntf.collections())); 0477 appendRow(model, i18n("Monitored Items"), toString(ntf.items())); 0478 appendRow(model, i18n("Monitored Tags"), toString(ntf.tags())); 0479 QStringList types; 0480 const auto typesSet = ntf.types(); 0481 for (const auto &type : typesSet) { 0482 switch (type) { 0483 case Akonadi::Protocol::ModifySubscriptionCommand::ItemChanges: 0484 types.push_back(i18n("Items")); 0485 break; 0486 case Akonadi::Protocol::ModifySubscriptionCommand::CollectionChanges: 0487 types.push_back(i18n("Collections")); 0488 break; 0489 case Akonadi::Protocol::ModifySubscriptionCommand::TagChanges: 0490 types.push_back(i18n("Tags")); 0491 break; 0492 case Akonadi::Protocol::ModifySubscriptionCommand::RelationChanges: 0493 types.push_back(i18n("Relations")); 0494 break; 0495 case Akonadi::Protocol::ModifySubscriptionCommand::SubscriptionChanges: 0496 types.push_back(i18n("Subscriptions")); 0497 break; 0498 case Akonadi::Protocol::ModifySubscriptionCommand::ChangeNotifications: 0499 types.push_back(i18n("Changes")); 0500 break; 0501 case Akonadi::Protocol::ModifySubscriptionCommand::NoType: 0502 types.push_back(i18n("No Type")); 0503 break; 0504 } 0505 } 0506 appendRow(model, i18n("Monitored Types"), types.join(QLatin1StringView(", "))); 0507 appendRow(model, i18n("Monitored Mime Types"), toString(ntf.mimeTypes())); 0508 appendRow(model, i18n("Monitored Resources"), toString(ntf.resources())); 0509 appendRow(model, i18n("Ignored Sessions"), toString(ntf.ignoredSessions())); 0510 appendRow(model, i18n("All Monitored"), toString(ntf.allMonitored())); 0511 appendRow(model, i18n("Exclusive"), toString(ntf.exclusive())); 0512 0513 auto item = new QStandardItem(i18n("Item Fetch Scope")); 0514 const auto ifs = ntf.itemFetchScope(); 0515 appendRow(item, i18n("Requested Parts"), toString(ifs.requestedParts())); 0516 appendRow(item, i18n("Changed Since"), ifs.changedSince().toString(Qt::ISODateWithMs)); 0517 QString ancestorDepth; 0518 switch (ifs.ancestorDepth()) { 0519 case Akonadi::Protocol::ItemFetchScope::NoAncestor: 0520 ancestorDepth = i18n("No Ancestor"); 0521 break; 0522 case Akonadi::Protocol::ItemFetchScope::ParentAncestor: 0523 ancestorDepth = i18n("Parent Ancestor"); 0524 break; 0525 case Akonadi::Protocol::ItemFetchScope::AllAncestors: 0526 ancestorDepth = i18n("All Ancestors"); 0527 break; 0528 } 0529 appendRow(item, i18n("Ancestor Depth"), ancestorDepth); 0530 appendRow(item, i18n("Cache Only"), toString(ifs.cacheOnly())); 0531 appendRow(item, i18n("Check Cached Payload Parts Only"), toString(ifs.checkCachedPayloadPartsOnly())); 0532 appendRow(item, i18n("Full Payload"), toString(ifs.fullPayload())); 0533 appendRow(item, i18n("All Attributes"), toString(ifs.allAttributes())); 0534 appendRow(item, i18n("Fetch Size"), toString(ifs.fetchSize())); 0535 appendRow(item, i18n("Fetch MTime"), toString(ifs.fetchMTime())); 0536 appendRow(item, i18n("Fetch Remote Revision"), toString(ifs.fetchRemoteRevision())); 0537 appendRow(item, i18n("Ignore Errors"), toString(ifs.ignoreErrors())); 0538 appendRow(item, i18n("Fetch Flags"), toString(ifs.fetchFlags())); 0539 appendRow(item, i18n("Fetch RemoteID"), toString(ifs.fetchRemoteId())); 0540 appendRow(item, i18n("Fetch GID"), toString(ifs.fetchGID())); 0541 appendRow(item, i18n("Fetch Tags"), toString(ifs.fetchTags())); 0542 appendRow(item, i18n("Fetch Relations"), toString(ifs.fetchRelations())); 0543 appendRow(item, i18n("Fetch VRefs"), toString(ifs.fetchVirtualReferences())); 0544 model->appendRow(item); 0545 0546 item = new QStandardItem(QStringLiteral("Collection Fetch Scope")); 0547 const auto cfs = ntf.collectionFetchScope(); 0548 QString listFilter; 0549 switch (cfs.listFilter()) { 0550 case Akonadi::Protocol::CollectionFetchScope::NoFilter: 0551 listFilter = i18n("No Filter"); 0552 break; 0553 case Akonadi::Protocol::CollectionFetchScope::Display: 0554 listFilter = i18n("Display"); 0555 break; 0556 case Akonadi::Protocol::CollectionFetchScope::Enabled: 0557 listFilter = i18n("Enabled"); 0558 break; 0559 case Akonadi::Protocol::CollectionFetchScope::Index: 0560 listFilter = i18n("Index"); 0561 break; 0562 case Akonadi::Protocol::CollectionFetchScope::Sync: 0563 listFilter = i18n("Sync"); 0564 break; 0565 } 0566 appendRow(item, i18n("List Filter"), listFilter); 0567 appendRow(item, i18n("Include Statistics"), toString(cfs.includeStatistics())); 0568 appendRow(item, i18n("Resource"), cfs.resource()); 0569 appendRow(item, i18n("Content Mime Types"), cfs.contentMimeTypes().join(QLatin1StringView(", "))); 0570 appendRow(item, i18n("Attributes"), toString(cfs.attributes())); 0571 appendRow(item, i18n("Fetch ID Only"), toString(cfs.fetchIdOnly())); 0572 QString ancestorRetrieval; 0573 switch (cfs.ancestorRetrieval()) { 0574 case Akonadi::Protocol::CollectionFetchScope::All: 0575 ancestorRetrieval = i18n("All"); 0576 break; 0577 case Akonadi::Protocol::CollectionFetchScope::Parent: 0578 ancestorRetrieval = i18n("Parent"); 0579 break; 0580 case Akonadi::Protocol::CollectionFetchScope::None: 0581 ancestorRetrieval = i18n("None"); 0582 break; 0583 } 0584 appendRow(item, i18n("Ancestor Retrieval"), ancestorRetrieval); 0585 appendRow(item, i18n("Ancestor Fetch ID Only"), toString(cfs.ancestorFetchIdOnly())); 0586 appendRow(item, i18n("Ancestor Attributes"), toString(cfs.ancestorAttributes())); 0587 appendRow(item, i18n("Ignore Retrieval Errors"), toString(cfs.ignoreRetrievalErrors())); 0588 model->appendRow(item); 0589 0590 item = new QStandardItem(i18n("Tag Fetch Scope")); 0591 const Akonadi::Protocol::TagFetchScope tfs = ntf.tagFetchScope(); 0592 appendRow(item, i18n("Fetch ID Only"), toString(tfs.fetchIdOnly())); 0593 appendRow(item, i18n("Fetch RemoteID"), toString(tfs.fetchRemoteID())); 0594 appendRow(item, i18n("Fetch All Attributes"), toString(tfs.fetchAllAttributes())); 0595 appendRow(item, i18n("Attributes"), toString(tfs.attributes())); 0596 model->appendRow(item); 0597 } 0598 0599 void NotificationMonitor::saveToFile() 0600 { 0601 const auto filename = QFileDialog::getSaveFileName(this, QStringLiteral("Save to File...")); 0602 if (filename.isEmpty()) { 0603 return; 0604 } 0605 0606 QFile file(filename); 0607 if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate)) { 0608 QMessageBox::warning(this, QStringLiteral("Error"), i18n("Failed to open file: %1").arg(file.errorString())); 0609 return; 0610 } 0611 0612 QJsonObject json; 0613 auto ntfModel = qobject_cast<QStandardItemModel *>(m_ntfView->model()); 0614 Q_ASSERT(ntfModel); 0615 0616 QJsonArray rowArray; 0617 // Note that the use of m_model here means we save everything, not just what's visible (in case of filtering). 0618 const int rows = m_model->rowCount(); 0619 for (int row = 0; row < rows; ++row) { 0620 QJsonObject rowObject; 0621 // The Ntf model has all the data that m_model has in its columns, apart from the session 0622 rowObject.insert(QStringLiteral("Session"), m_model->index(row, NotificationModel::SessionColumn).data().toString()); 0623 0624 populateNtfModel(m_model->index(row, 0)); 0625 for (int r = 0, cnt = ntfModel->rowCount(); r < cnt; ++r) { 0626 const auto idx0 = ntfModel->index(r, 0); 0627 const auto idx1 = ntfModel->index(r, 1); 0628 rowObject.insert(idx0.data().toString(), QJsonValue::fromVariant(idx1.data())); 0629 } 0630 0631 rowArray.append(rowObject); 0632 } 0633 json.insert(QStringLiteral("notifications"), rowArray); 0634 QJsonDocument saveDoc(json); 0635 file.write(saveDoc.toJson()); 0636 } 0637 0638 #include "moc_notificationmonitor.cpp"