diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 9803d29..cd6d23f 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,164 +1,165 @@ include_directories(BEFORE ${CMAKE_CURRENT_BINARY_DIR}) add_definitions(-DQT_NO_CAST_FROM_ASCII) add_definitions(-DQT_NO_CAST_TO_ASCII) add_subdirectory(icons) add_subdirectory(kconf_update) set(libakonadiconsole_tracker_SRCS jobtracker.cpp jobtrackerwidget.cpp jobtrackermodel.cpp jobtrackerfilterproxymodel.cpp jobtrackersearchwidget.cpp ) set(libakonadiconsole_SRCS agentwidget.cpp agentconfigdialog.cpp agentconfigmodel.cpp akonadibrowsermodel.cpp browserwidget.cpp collectionattributespage.cpp collectioninternalspage.cpp collectionaclpage.cpp connectionpage.cpp dbaccess.cpp dbbrowser.cpp dbconsole.cpp debugfiltermodel.cpp debugmodel.cpp debugwidget.cpp instanceselector.cpp logging.cpp loggingfiltermodel.cpp loggingmodel.cpp mainwidget.cpp mainwindow.cpp monitorswidget.cpp monitorsmodel.cpp notificationmodel.cpp + notificationfiltermodel.cpp notificationmonitor.cpp querydebugger.cpp tagpropertiesdialog.cpp ${libakonadiconsole_tracker_SRCS} ) if (ENABLE_SEARCH) set(libakonadiconsole_SRCS ${libakonadiconsole_SRCS} searchwidget.cpp ) endif() qt5_generate_dbus_interface(jobtracker.h org.freedesktop.Akonadi.JobTracker.xml) qt5_add_dbus_adaptor(libakonadiconsole_SRCS ${CMAKE_CURRENT_BINARY_DIR}/org.freedesktop.Akonadi.JobTracker.xml jobtracker.h JobTracker) qt5_add_dbus_adaptor(libakonadiconsole_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/org.kde.akonadiconsole.logger.xml logging.h Logging) qt5_add_dbus_interfaces(libakonadiconsole_SRCS ${AKONADI_DBUS_INTERFACES_DIR}/org.freedesktop.Akonadi.TracerNotification.xml ) # Use intalled interface once we can depend on Akoandi 1.1 set_source_files_properties(org.freedesktop.Akonadi.DebugInterface.xml PROPERTIES CLASSNAME DebugInterface) qt5_add_dbus_interface(libakonadiconsole_SRCS org.freedesktop.Akonadi.DebugInterface.xml debuginterface ) set_source_files_properties(${AKONADI_DBUS_INTERFACES_DIR}/org.freedesktop.Akonadi.StorageDebugger.xml PROPERTIES INCLUDE querydebugger.h ) qt5_add_dbus_interface(libakonadiconsole_SRCS ${AKONADI_DBUS_INTERFACES_DIR}/org.freedesktop.Akonadi.StorageDebugger.xml storagedebuggerinterface ) qt5_wrap_ui(libakonadiconsole_SRCS agentwidget.ui agentconfigdialog.ui browserwidget_itemview.ui collectionattributespage.ui collectionaclpage.ui dbbrowser.ui dbconsoletab.ui instanceselector.ui browserwidget_contentview.ui collectioninternalspage.ui tagpropertiesdialog.ui querydebugger.ui queryviewdialog.ui ) ecm_qt_declare_logging_category(libakonadiconsole_SRCS HEADER akonadiconsole_debug.h IDENTIFIER AKONADICONSOLE_LOG CATEGORY_NAME org.kde.pim.akonadiconsole) add_library(libakonadiconsole ${libakonadiconsole_SRCS}) generate_export_header(libakonadiconsole BASE_NAME libakonadiconsole) target_link_libraries(libakonadiconsole KF5::AkonadiCore KF5::AkonadiPrivate KF5::AkonadiWidgets KF5::AkonadiXml KF5::Mime KF5::Contacts KF5::CalendarCore Qt5::Sql KF5::Completion KF5::ItemViews KF5::TextWidgets KF5::XmlGui ) if (ENABLE_SEARCH) target_link_libraries(libakonadiconsole KF5::AkonadiSearchCore KF5::AkonadiSearchXapian ) endif() if (ENABLE_CONTENTVIEWS) target_link_libraries(libakonadiconsole KF5::AkonadiContact KF5::CalendarSupport KF5::MessageViewer ) endif() if (ENABLE_LIBKDEPIM) target_link_libraries(libakonadiconsole KF5::Libkdepim ) endif() set_target_properties(libakonadiconsole PROPERTIES OUTPUT_NAME akonadiconsole VERSION ${KDEPIM_LIB_VERSION} SOVERSION ${KDEPIM_LIB_SOVERSION} ) set(akonadiconsole_SRCS main.cpp ) qt5_add_resources(akonadiconsole_SRCS akonadiconsole.qrc) file(GLOB ICONS_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/icons/*-apps-akonadiconsole.png") ecm_add_app_icon(akonadiconsole_SRCS ICONS ${ICONS_SRCS}) add_executable(akonadiconsole ${akonadiconsole_SRCS}) target_link_libraries(akonadiconsole libakonadiconsole KF5::DBusAddons KF5::CoreAddons KF5::Crash ) install(TARGETS akonadiconsole ${KDE_INSTALL_TARGETS_DEFAULT_ARGS}) install(PROGRAMS org.kde.akonadiconsole.desktop DESTINATION ${KDE_INSTALL_APPDIR}) install(TARGETS libakonadiconsole ${KDE_INSTALL_TARGETS_DEFAULT_ARGS} LIBRARY NAMELINK_SKIP) diff --git a/src/loggingmodel.h b/src/loggingmodel.h index a5224f0..937b399 100644 --- a/src/loggingmodel.h +++ b/src/loggingmodel.h @@ -1,92 +1,92 @@ /* Copyright (c) 2018 Daniel Vrátil This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef AKONADICONSOLE_LOGGINGMODEL_H_ #define AKONADICONSOLE_LOGGINGMODEL_H_ #include #include class QStandardItemModel; class LoggingModel : public QAbstractItemModel { Q_OBJECT public: struct Message { qint64 timestamp; QString app; qint64 pid; QString category; QString file; QString function; QString message; QtMsgType type; int line; }; enum Roles { MessageRole = Qt::UserRole }; enum Columns { TimeColumn, AppColumn, TypeColumn, CategoryColumn, FileColumn, FunctionColumn, MessageColumn, _ColumnCount }; explicit LoggingModel(QObject *parent = nullptr); ~LoggingModel() override; void addMessage(qint64 timestamp, const QString &app, qint64 pid, QtMsgType type, const QString &category, const QString &file, const QString &function, int line, const QString &message); void setAppFilterModel(QStandardItemModel *appFilterModel); void setCategoryFilterModel(QStandardItemModel *categoryFilterModel); int rowCount(const QModelIndex &parent = {}) const override; int columnCount(const QModelIndex &parent = {}) const override; QModelIndex index(int row, int column, const QModelIndex &parent = {}) const override; QModelIndex parent(const QModelIndex &child = {}) const override; QVariant headerData(int section, Qt::Orientation orientation, int role) const override; QVariant data(const QModelIndex &index, int role) const override; private: - QString cacheString(const QString &str, QSet &cache, QStandardItemModel *model = nullptr); + static QString cacheString(const QString &str, QSet &cache, QStandardItemModel *model = nullptr); QVector mMessages; QStandardItemModel *mAppFilterModel = nullptr; QSet mAppCache; QStandardItemModel *mCategoryFilterModel = nullptr; QSet mCategoryCache; QSet mFileCache; QSet mFunctionCache; }; #endif diff --git a/src/notificationfiltermodel.cpp b/src/notificationfiltermodel.cpp new file mode 100644 index 0000000..c9eb453 --- /dev/null +++ b/src/notificationfiltermodel.cpp @@ -0,0 +1,98 @@ +/* + * + * Copyright 2018 David Faure + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License or (at your option) version 3 or any later version + * accepted by the membership of KDE e.V. (or its successor approved + * by the membership of KDE e.V.), which shall act as a proxy + * defined in Section 14 of version 3 of the license. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "notificationfiltermodel.h" +#include "notificationmodel.h" + +#include +#include + +using KPIM::KCheckComboBox; + +Q_DECLARE_METATYPE(Akonadi::ChangeNotification) + +NotificationFilterModel::NotificationFilterModel(QObject *parent) + : QSortFilterProxyModel(parent) +{ + mInvalidateTimer.setInterval(50); + mInvalidateTimer.setSingleShot(true); + connect(&mInvalidateTimer, &QTimer::timeout, + this, &NotificationFilterModel::invalidateFilter); +} + +NotificationFilterModel::~NotificationFilterModel() +{ +} + +void NotificationFilterModel::setTypeFilter(KPIM::KCheckComboBox *typeFilter) +{ + if (mTypeFilter) { + mTypeFilter->disconnect(this); + } + mTypeFilter = typeFilter; + connect(mTypeFilter, &KCheckComboBox::checkedItemsChanged, + this, [this](const QStringList &items) { + mCheckedTypes.clear(); + mCheckedTypes.reserve(items.count()); + // it sucks a bit that KCheckComboBox::checkedItems can't return a QVariantList instead of a QStringList + for (const QString &item : mTypeFilter->checkedItems(Qt::UserRole)) { + mCheckedTypes.insert(static_cast(item.toInt())); + } + if (!mInvalidateTimer.isActive()) { + mInvalidateTimer.start(); + } + }); +} + +bool NotificationFilterModel::filterAcceptsRow(int source_row, const QModelIndex &) const +{ + const auto source_idx = sourceModel()->index(source_row, 0); + const auto notification = sourceModel()->data(source_idx, NotificationModel::NotificationRole).value(); + return mCheckedTypes.contains(notification.type()); +} + +void NotificationFilterModel::setSourceModel(QAbstractItemModel* model) +{ + if (sourceModel()) + disconnect(sourceModel(), &QAbstractItemModel::rowsInserted, this, &NotificationFilterModel::slotRowsInserted); + connect(model, &QAbstractItemModel::rowsInserted, this, &NotificationFilterModel::slotRowsInserted); + QSortFilterProxyModel::setSourceModel(model); +} + +void NotificationFilterModel::slotRowsInserted(const QModelIndex& source_parent, int start, int end) +{ + // insert new types (if any) into the type filter combo + Q_ASSERT(!source_parent.isValid()); + for (int row = start; row <= end; ++row) { + const QModelIndex source_idx = sourceModel()->index(start, NotificationModel::TypeColumn); + const QString text = source_idx.data().toString(); + if (!mTypes.contains(text)) { + mTypes.insert(text); + auto *comboModel = qobject_cast(mTypeFilter->model()); + Q_ASSERT(comboModel); + auto item = new QStandardItem(text); + const auto notification = sourceModel()->data(source_idx, NotificationModel::NotificationRole).value(); + item->setData(int(notification.type()), Qt::UserRole); + item->setCheckState(Qt::Checked); + comboModel->appendRow(item); + } + } +} diff --git a/src/notificationfiltermodel.h b/src/notificationfiltermodel.h new file mode 100644 index 0000000..c7a9305 --- /dev/null +++ b/src/notificationfiltermodel.h @@ -0,0 +1,63 @@ +/* + * Copyright 2018 David Faure + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License or (at your option) version 3 or any later version + * accepted by the membership of KDE e.V. (or its successor approved + * by the membership of KDE e.V.), which shall act as a proxy + * defined in Section 14 of version 3 of the license. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef NOTIFICATIONFILTERMODEL_H +#define NOTIFICATIONFILTERMODEL_H + +#include + +#include + +#include +#include + +namespace KPIM { +class KCheckComboBox; +} + +/** + * Filtering for NotificationModel + */ +class NotificationFilterModel : public QSortFilterProxyModel +{ + Q_OBJECT +public: + explicit NotificationFilterModel(QObject *parent = nullptr); + + ~NotificationFilterModel(); + + void setTypeFilter(KPIM::KCheckComboBox *typeFilter); + + void setSourceModel(QAbstractItemModel *model) override; + +protected: + bool filterAcceptsRow(int source_row, const QModelIndex & source_parent) const override; + +private: + void slotRowsInserted(const QModelIndex &source_parent, int start, int end); + + KPIM::KCheckComboBox *mTypeFilter = nullptr; + QSet mCheckedTypes; + + QSet mTypes; + QTimer mInvalidateTimer; +}; + +#endif // NOTIFICATIONFILTERMODEL_H diff --git a/src/notificationmonitor.cpp b/src/notificationmonitor.cpp index ef66988..538c42f 100644 --- a/src/notificationmonitor.cpp +++ b/src/notificationmonitor.cpp @@ -1,589 +1,603 @@ /* Copyright (c) 2009 Volker Krause This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "notificationmonitor.h" #include "notificationmodel.h" +#include "notificationfiltermodel.h" #include "utils.h" #include #include - #include #include +#include #include #include #include #include #include #include #include +#include #include #include +using KPIM::KCheckComboBox; + Q_DECLARE_METATYPE(Akonadi::ChangeNotification) NotificationMonitor::NotificationMonitor(QWidget *parent) : QWidget(parent) { m_model = new NotificationModel(this); m_model->setEnabled(false); // since it can be slow, default to off + m_filterModel = new NotificationFilterModel(this); + m_filterModel->setSourceModel(m_model); + QVBoxLayout *layout = new QVBoxLayout(this); + auto hLayout = new QHBoxLayout; + layout->addLayout(hLayout); QCheckBox *enableCB = new QCheckBox(this); enableCB->setText(QStringLiteral("Enable notification monitor")); enableCB->setChecked(m_model->isEnabled()); connect(enableCB, &QCheckBox::toggled, m_model, &NotificationModel::setEnabled); - layout->addWidget(enableCB); + hLayout->addWidget(enableCB); + hLayout->addWidget(new QLabel(QStringLiteral("Types:"), this)); + hLayout->addWidget(mTypeFilterCombo = new KCheckComboBox(this)); + hLayout->addStretch(); + mTypeFilterCombo->setSizeAdjustPolicy(QComboBox::AdjustToContents); + m_filterModel->setTypeFilter(mTypeFilterCombo); m_splitter = new QSplitter(this); layout->addWidget(m_splitter); m_treeView = new QTreeView(this); - m_treeView->setModel(m_model); + m_treeView->setModel(m_filterModel); m_treeView->expandAll(); m_treeView->setAlternatingRowColors(true); m_treeView->setContextMenuPolicy(Qt::CustomContextMenu); m_treeView->header()->setSectionResizeMode(QHeaderView::ResizeToContents); connect(m_treeView, &QTreeView::customContextMenuRequested, this, &NotificationMonitor::contextMenu); connect(m_treeView->selectionModel(), &QItemSelectionModel::currentChanged, this, &NotificationMonitor::onNotificationSelected); m_splitter->addWidget(m_treeView); m_ntfView = new QTreeView(this); m_ntfView->setModel(new QStandardItemModel(this)); m_ntfView->setEditTriggers(QAbstractItemView::NoEditTriggers); m_splitter->addWidget(m_ntfView); onNotificationSelected({}); KConfigGroup config(KSharedConfig::openConfig(), "NotificationMonitor"); m_treeView->header()->restoreState(config.readEntry("tv", QByteArray())); m_ntfView->header()->restoreState(config.readEntry("ntfView", QByteArray())); m_splitter->restoreState(config.readEntry("splitter", QByteArray())); Akonadi::ControlGui::widgetNeedsAkonadi(this); } NotificationMonitor::~NotificationMonitor() { KConfigGroup config(KSharedConfig::openConfig(), "NotificationMonitor"); config.writeEntry("tv", m_treeView->header()->saveState()); config.writeEntry("ntfView", m_ntfView->header()->saveState()); config.writeEntry("splitter", m_splitter->saveState()); } void NotificationMonitor::contextMenu(const QPoint & /*pos*/) { QMenu menu; menu.addAction(QStringLiteral("Clear View"), m_model, &NotificationModel::clear); menu.exec(QCursor::pos()); } void NotificationMonitor::onNotificationSelected(const QModelIndex &index) { auto model = qobject_cast(m_ntfView->model()); const auto state = m_ntfView->header()->saveState(); model->clear(); model->setHorizontalHeaderLabels({ QStringLiteral("Properties"), QStringLiteral("Values") }); m_ntfView->header()->restoreState(state); const auto ntf = index.data(NotificationModel::NotificationRole).value(); if (!ntf.isValid()) { return; } appendRow(model, QStringLiteral("Timestamp"), ntf.timestamp().toString(Qt::ISODateWithMs)); appendRow(model, QStringLiteral("Type"), index.sibling(index.row(), NotificationModel::TypeColumn).data().toString()); appendRow(model, QStringLiteral("Listeners"), index.sibling(index.row(), NotificationModel::ListenersColumn).data().toString()); switch (ntf.type()) { case Akonadi::ChangeNotification::Items: populateItemNtfTree(model, Akonadi::Protocol::cmdCast(ntf.notification())); break; case Akonadi::ChangeNotification::Collection: populateCollectionNtfTree(model, Akonadi::Protocol::cmdCast(ntf.notification())); break; case Akonadi::ChangeNotification::Tag: populateTagNtfTree(model, Akonadi::Protocol::cmdCast(ntf.notification())); break; case Akonadi::ChangeNotification::Relation: populateRelationNtfTree(model, Akonadi::Protocol::cmdCast(ntf.notification())); break; case Akonadi::ChangeNotification::Subscription: populateSubscriptionNtfTree(model, Akonadi::Protocol::cmdCast(ntf.notification())); break; } m_ntfView->expandAll(); } void NotificationMonitor::populateItemNtfTree(QStandardItemModel *model, const Akonadi::Protocol::ItemChangeNotification &ntf) { QString operation; switch (ntf.operation()) { case Akonadi::Protocol::ItemChangeNotification::Add: operation = QStringLiteral("Add"); break; case Akonadi::Protocol::ItemChangeNotification::Modify: operation = QStringLiteral("Modify"); break; case Akonadi::Protocol::ItemChangeNotification::Move: operation = QStringLiteral("Move"); break; case Akonadi::Protocol::ItemChangeNotification::Remove: operation = QStringLiteral("Remove"); break; case Akonadi::Protocol::ItemChangeNotification::Link: operation = QStringLiteral("Link"); break; case Akonadi::Protocol::ItemChangeNotification::Unlink: operation = QStringLiteral("Unlink"); break; case Akonadi::Protocol::ItemChangeNotification::ModifyFlags: operation = QStringLiteral("ModifyFlags"); break; case Akonadi::Protocol::ItemChangeNotification::ModifyTags: operation = QStringLiteral("ModifyTags"); break; case Akonadi::Protocol::ItemChangeNotification::ModifyRelations: operation = QStringLiteral("ModifyRelations"); break; case Akonadi::Protocol::ItemChangeNotification::InvalidOp: operation = QStringLiteral("InvalidOp"); break; } appendRow(model, QStringLiteral("Operation"), operation); appendRow(model, QStringLiteral("Resource"), QString::fromUtf8(ntf.resource())); appendRow(model, QStringLiteral("Parent Collection"), QString::number(ntf.parentCollection())); appendRow(model, QStringLiteral("Parent Dest Col"), QString::number(ntf.parentDestCollection())); appendRow(model, QStringLiteral("Destination Resource"), QString::fromUtf8(ntf.destinationResource())); appendRow(model, QStringLiteral("Item Parts"), toString(ntf.itemParts())); appendRow(model, QStringLiteral("Added Flags"), toString(ntf.addedFlags())); appendRow(model, QStringLiteral("Removed Flags"), toString(ntf.removedFlags())); appendRow(model, QStringLiteral("Added Tags"), toString(ntf.addedTags()));; appendRow(model, QStringLiteral("Removed Tags"), toString(ntf.removedTags())); auto relationsItem = new QStandardItem(QStringLiteral("Added Relations")); const auto addedRelations = ntf.addedRelations(); for (const auto &addedRelation : addedRelations) { auto item = new QStandardItem(QStringLiteral("%lld-%lld %s").arg( QString::number(addedRelation.leftId), QString::number(addedRelation.rightId), addedRelation.type)); relationsItem->appendRow(item); } model->appendRow(relationsItem); relationsItem = new QStandardItem(QStringLiteral("Removed Relations")); const auto removedRelations = ntf.removedRelations(); for (const auto &removedRelation : removedRelations) { auto item = new QStandardItem(QStringLiteral("%lld-%lld %s").arg( QString::number(removedRelation.leftId), QString::number(removedRelation.rightId), removedRelation.type)); relationsItem->appendRow(item); } model->appendRow(relationsItem); appendRow(model, QStringLiteral("Must retrieve"), toString(ntf.mustRetrieve())); auto itemsItem = new QStandardItem(QStringLiteral("Items")); const auto &items = ntf.items(); for (const auto &item : items) { auto i = new QStandardItem(QString::number(item.id())); populateItemTree(i, item); itemsItem->appendRow(i); } model->appendRow(itemsItem); } QStandardItem *NotificationMonitor::populateAncestorTree(QStandardItem *parent, const Akonadi::Protocol::Ancestor &ancestor) { appendRow(parent, QStringLiteral("id"), QString::number(ancestor.id())); appendRow(parent, QStringLiteral("remoteId"), ancestor.remoteId()); appendRow(parent, QStringLiteral("name"), ancestor.name()); populateAttributesTree(parent, ancestor.attributes()); auto ancestorItem = new QStandardItem(QStringLiteral("Ancestor")); parent->appendRow(ancestorItem); return ancestorItem; } void NotificationMonitor::populateTagTree(QStandardItem *parent, const Akonadi::Protocol::FetchTagsResponse &tag) { appendRow(parent, QStringLiteral("ID"), QString::number(tag.id())); appendRow(parent, QStringLiteral("Parent ID"), QString::number(tag.parentId())); appendRow(parent, QStringLiteral("GID"), QString::fromUtf8(tag.gid())); appendRow(parent, QStringLiteral("Type"), QString::fromUtf8(tag.type())); appendRow(parent, QStringLiteral("Remote ID"), QString::fromUtf8(tag.remoteId())); populateAttributesTree(parent, tag.attributes()); } void NotificationMonitor::populateAttributesTree(QStandardItem *parent, const Akonadi::Protocol::Attributes &attributes) { auto attributesItem = new QStandardItem(QStringLiteral("Attributes")); for (auto it = attributes.cbegin(), end = attributes.cend(); it != end; ++it) { appendRow(attributesItem, QString::fromUtf8(it.key()), QString::fromUtf8(it.value())); } parent->appendRow(attributesItem); } void NotificationMonitor::populateItemTree(QStandardItem *parent, const Akonadi::Protocol::FetchItemsResponse &item) { appendRow(parent, QStringLiteral("Revision"), QString::number(item.revision())); appendRow(parent, QStringLiteral("ParentID"), QString::number(item.parentId())); appendRow(parent, QStringLiteral("RemoteID"), item.remoteId()); appendRow(parent, QStringLiteral("RemoteRev"), item.remoteRevision()); appendRow(parent, QStringLiteral("GID"), item.gid()); appendRow(parent, QStringLiteral("Size"), QString::number(item.size())); appendRow(parent, QStringLiteral("MimeType"), item.mimeType()); appendRow(parent, QStringLiteral("MTime"), item.mTime().toString(Qt::ISODate)); appendRow(parent, QStringLiteral("Flags"), toString(item.flags())); auto tagItem = new QStandardItem(QStringLiteral("Tags")); const auto tags = item.tags(); for (const auto tag : tags) { auto item = new QStandardItem(QString::number(tag.id())); populateTagTree(item, tag); tagItem->appendRow(item); } parent->appendRow(tagItem); appendRow(parent, QStringLiteral("VRefs"), toString(item.virtualReferences())); auto relationItem = new QStandardItem(QStringLiteral("Relations")); const auto relations = item.relations(); for (const auto relation : relations) { auto item = new QStandardItem(QStringLiteral("%lld-%lld %s").arg(QString::number(relation.left()), QString::number(relation.right()), QString::fromUtf8(relation.type()))); relationItem->appendRow(item); } parent->appendRow(relationItem); const auto ancestors = item.ancestors(); auto i = new QStandardItem(QStringLiteral("Ancestor")); parent->appendRow(i); for (const auto &ancestor : ancestors) { i = populateAncestorTree(i, ancestor); } auto partsItem = new QStandardItem(QStringLiteral("Parts")); const auto parts = item.parts(); for (const auto &part : parts) { auto item = new QStandardItem(QString::fromUtf8(part.payloadName())); QString type; switch (part.metaData().storageType()) { case Akonadi::Protocol::PartMetaData::External: type = QStringLiteral("External"); break; case Akonadi::Protocol::PartMetaData::Internal: type = QStringLiteral("Internal"); break; case Akonadi::Protocol::PartMetaData::Foreign: type = QStringLiteral("Foreign"); break; } appendRow(item, QStringLiteral("Size"), QString::number(part.metaData().size())); appendRow(item, QStringLiteral("Storage Type"), type); appendRow(item, QStringLiteral("Version"), QString::number(part.metaData().version())); appendRow(item, QStringLiteral("Data"), QString::fromUtf8( part.data().toHex())); partsItem->appendRow(item); } parent->appendRow(partsItem); } void NotificationMonitor::populateCollectionTree(QStandardItem *parent, const Akonadi::Protocol::FetchCollectionsResponse &collection) { appendRow(parent, QStringLiteral("ID"), QString::number(collection.id())); appendRow(parent, QStringLiteral("Parent ID"), QString::number(collection.parentId())); appendRow(parent, QStringLiteral("Name"), collection.name()); appendRow(parent, QStringLiteral("Mime Types"), toString(static_cast>(collection.mimeTypes()))); appendRow(parent, QStringLiteral("Remote ID"), collection.remoteId()); appendRow(parent, QStringLiteral("Remote Revision"), collection.remoteRevision()); auto statsItem = new QStandardItem(QStringLiteral("Statistics")); const auto stats = collection.statistics(); appendRow(statsItem, QStringLiteral("Count"), QString::number(stats.count())); appendRow(statsItem, QStringLiteral("Unseen"), QString::number(stats.unseen())); appendRow(statsItem, QStringLiteral("Size"), QString::number(stats.size())); parent->appendRow(statsItem); appendRow(parent, QStringLiteral("Search Query"), collection.searchQuery()); appendRow(parent, QStringLiteral("Search Collections"), toString(collection.searchCollections())); auto i = new QStandardItem(QStringLiteral("Ancestor")); parent->appendRow(i); const auto ancestors = collection.ancestors(); for (const auto &ancestor : ancestors) { i = populateAncestorTree(i, ancestor); } auto cpItem = new QStandardItem(QStringLiteral("Cache Policy")); const auto cp = collection.cachePolicy(); appendRow(cpItem, QStringLiteral("Inherit"), toString(cp.inherit())); appendRow(cpItem, QStringLiteral("Check Interval"), QString::number(cp.checkInterval())); appendRow(cpItem, QStringLiteral("Cache Timeout"), QString::number(cp.cacheTimeout())); appendRow(cpItem, QStringLiteral("Sync on Demand"), toString(cp.syncOnDemand())); appendRow(cpItem, QStringLiteral("Local Parts"), toString(static_cast>(cp.localParts()))); parent->appendRow(cpItem); populateAttributesTree(parent, collection.attributes()); appendRow(parent, QStringLiteral("Enabled"), toString(collection.enabled())); appendRow(parent, QStringLiteral("DisplayPref"), toString(collection.displayPref())); appendRow(parent, QStringLiteral("SyncPref"), toString(collection.syncPref())); appendRow(parent, QStringLiteral("IndexPref"), toString(collection.indexPref())); appendRow(parent, QStringLiteral("Referenced"), toString(collection.referenced())); appendRow(parent, QStringLiteral("Virtual"), toString(collection.isVirtual())); } void NotificationMonitor::populateCollectionNtfTree(QStandardItemModel *model, const Akonadi::Protocol::CollectionChangeNotification &ntf) { QString operation; switch (ntf.operation()) { case Akonadi::Protocol::CollectionChangeNotification::Add: operation = QStringLiteral("Add"); break; case Akonadi::Protocol::CollectionChangeNotification::Modify: operation = QStringLiteral("Modify"); break; case Akonadi::Protocol::CollectionChangeNotification::Move: operation = QStringLiteral("Move"); break; case Akonadi::Protocol::CollectionChangeNotification::Remove: operation = QStringLiteral("Remove"); break; case Akonadi::Protocol::CollectionChangeNotification::Subscribe: operation = QStringLiteral("Subscribe"); break; case Akonadi::Protocol::CollectionChangeNotification::Unsubscribe: operation = QStringLiteral("Unsubscribe"); break; case Akonadi::Protocol::CollectionChangeNotification::InvalidOp: operation = QStringLiteral("InvalidIp"); break; } appendRow(model, QStringLiteral("Operation"), operation); appendRow(model, QStringLiteral("Resource"), QString::fromUtf8(ntf.resource())); appendRow(model, QStringLiteral("Parent Collection"), QString::number(ntf.parentCollection())); appendRow(model, QStringLiteral("Parent Dest Collection"), QString::number(ntf.parentDestCollection())); appendRow(model, QStringLiteral("Destination Resource"), QString::fromUtf8(ntf.destinationResource())); appendRow(model, QStringLiteral("Changed Parts"), toString(ntf.changedParts())); auto item = new QStandardItem(QStringLiteral("Collection")); populateCollectionTree(item, ntf.collection()); model->appendRow(item); } void NotificationMonitor::populateRelationNtfTree(QStandardItemModel *model, const Akonadi::Protocol::RelationChangeNotification &ntf) { QString operation; switch (ntf.operation()) { case Akonadi::Protocol::RelationChangeNotification::Add: operation = QStringLiteral("Add"); break; case Akonadi::Protocol::RelationChangeNotification::Remove: operation = QStringLiteral("Remove"); break; case Akonadi::Protocol::RelationChangeNotification::InvalidOp: operation = QStringLiteral("InvalidOp"); break; } appendRow(model, QStringLiteral("Operation"), operation); auto item = new QStandardItem(QStringLiteral("Relation")); const auto rel = ntf.relation(); appendRow(item, QStringLiteral("Left ID"), QString::number(rel.left())); appendRow(item, QStringLiteral("Left MimeType"), QString::fromUtf8(rel.leftMimeType())); appendRow(item, QStringLiteral("Right ID"), QString::number(rel.right())); appendRow(item, QStringLiteral("Right MimeType"), QString::fromUtf8(rel.rightMimeType())); appendRow(item, QStringLiteral("Remote ID"), QString::fromUtf8(rel.remoteId())); model->appendRow(item); } void NotificationMonitor::populateTagNtfTree(QStandardItemModel *model, const Akonadi::Protocol::TagChangeNotification &ntf) { QString operation; switch (ntf.operation()) { case Akonadi::Protocol::TagChangeNotification::Add: operation = QStringLiteral("Add"); break; case Akonadi::Protocol::TagChangeNotification::Modify: operation = QStringLiteral("Modify"); break; case Akonadi::Protocol::TagChangeNotification::Remove: operation = QStringLiteral("Remove"); break; case Akonadi::Protocol::TagChangeNotification::InvalidOp: operation = QStringLiteral("InvalidOp"); break; } appendRow(model, QStringLiteral("Operation"), operation); appendRow(model, QStringLiteral("Resource"), QString::fromUtf8(ntf.resource())); auto tagItem = new QStandardItem(QStringLiteral("Tag")); populateTagTree(tagItem, ntf.tag()); model->appendRow(tagItem); } void NotificationMonitor::populateSubscriptionNtfTree(QStandardItemModel *model, const Akonadi::Protocol::SubscriptionChangeNotification &ntf) { QString operation; switch (ntf.operation()) { case Akonadi::Protocol::SubscriptionChangeNotification::Add: operation = QStringLiteral("Add"); break; case Akonadi::Protocol::SubscriptionChangeNotification::Modify: operation = QStringLiteral("Modify"); break; case Akonadi::Protocol::SubscriptionChangeNotification::Remove: operation = QStringLiteral("Remove"); break; case Akonadi::Protocol::SubscriptionChangeNotification::InvalidOp: operation = QStringLiteral("InvalidOp"); break; } appendRow(model, QStringLiteral("Operation"), operation); appendRow(model, QStringLiteral("Subscriber"), QString::fromUtf8(ntf.subscriber())); appendRow(model, QStringLiteral("Monitored Collections"), toString(ntf.collections())); appendRow(model, QStringLiteral("Monitored Items"), toString(ntf.items())); appendRow(model, QStringLiteral("Monitored Tags"), toString(ntf.tags())); QStringList types; const auto typesSet = ntf.types(); for (const auto &type : typesSet) { switch (type) { case Akonadi::Protocol::ModifySubscriptionCommand::ItemChanges: types.push_back(QStringLiteral("Items")); break; case Akonadi::Protocol::ModifySubscriptionCommand::CollectionChanges: types.push_back(QStringLiteral("Collections")); break; case Akonadi::Protocol::ModifySubscriptionCommand::TagChanges: types.push_back(QStringLiteral("Tags")); break; case Akonadi::Protocol::ModifySubscriptionCommand::RelationChanges: types.push_back(QStringLiteral("Relations")); break; case Akonadi::Protocol::ModifySubscriptionCommand::SubscriptionChanges: types.push_back(QStringLiteral("Subscriptions")); break; case Akonadi::Protocol::ModifySubscriptionCommand::ChangeNotifications: types.push_back(QStringLiteral("Changes")); break; case Akonadi::Protocol::ModifySubscriptionCommand::NoType: types.push_back(QStringLiteral("No Type")); break; } } appendRow(model, QStringLiteral("Monitored Types"), types.join(QStringLiteral(", "))); appendRow(model, QStringLiteral("Monitored Mime Types"), toString(ntf.mimeTypes())); appendRow(model, QStringLiteral("Monitored Resources"), toString(ntf.resources())); appendRow(model, QStringLiteral("Ignored Sessions"), toString(ntf.ignoredSessions())); appendRow(model, QStringLiteral("All Monitored"), toString(ntf.allMonitored())); appendRow(model, QStringLiteral("Exclusive"), toString(ntf.exclusive())); auto item = new QStandardItem(QStringLiteral("Item Fetch Scope")); const auto ifs = ntf.itemFetchScope(); appendRow(item, QStringLiteral("Requested Parts"), toString(ifs.requestedParts())); appendRow(item, QStringLiteral("Changed Since"), ifs.changedSince().toString(Qt::ISODateWithMs)); appendRow(item, QStringLiteral("Tag Fetch Scope"), toString(ifs.tagFetchScope())); QString ancestorDepth; switch (ifs.ancestorDepth()) { case Akonadi::Protocol::ItemFetchScope::NoAncestor: ancestorDepth = QStringLiteral("No Ancestor"); break; case Akonadi::Protocol::ItemFetchScope::ParentAncestor: ancestorDepth = QStringLiteral("Parent Ancestor"); break; case Akonadi::Protocol::ItemFetchScope::AllAncestors: ancestorDepth = QStringLiteral("All Ancestors"); break; } appendRow(item, QStringLiteral("Ancestor Depth"), ancestorDepth); appendRow(item, QStringLiteral("Cache Only"), toString(ifs.cacheOnly())); appendRow(item, QStringLiteral("Check Cached Payload Parts Only"), toString(ifs.checkCachedPayloadPartsOnly())); appendRow(item, QStringLiteral("Full Payload"), toString(ifs.fullPayload())); appendRow(item, QStringLiteral("All Attributes"), toString(ifs.allAttributes())); appendRow(item, QStringLiteral("Fetch Size"), toString(ifs.fetchSize())); appendRow(item, QStringLiteral("Fetch MTime"), toString(ifs.fetchMTime())); appendRow(item, QStringLiteral("Fetch Remote Revision"), toString(ifs.fetchRemoteRevision())); appendRow(item, QStringLiteral("Ignore Errors"), toString(ifs.ignoreErrors())); appendRow(item, QStringLiteral("Fetch Flags"), toString(ifs.fetchFlags())); appendRow(item, QStringLiteral("Fetch RemoteID"), toString(ifs.fetchRemoteId())); appendRow(item, QStringLiteral("Fetch GID"), toString(ifs.fetchGID())); appendRow(item, QStringLiteral("Fetch Tags"), toString(ifs.fetchTags())); appendRow(item, QStringLiteral("Fetch Relations"), toString(ifs.fetchRelations())); appendRow(item, QStringLiteral("Fetch VRefs"), toString(ifs.fetchVirtualReferences())); model->appendRow(item); item = new QStandardItem(QStringLiteral("Collection Fetch Scope")); const auto cfs = ntf.collectionFetchScope(); QString listFilter; switch (cfs.listFilter()) { case Akonadi::Protocol::CollectionFetchScope::NoFilter: listFilter = QStringLiteral("No Filter"); break; case Akonadi::Protocol::CollectionFetchScope::Display: listFilter = QStringLiteral("Display"); break; case Akonadi::Protocol::CollectionFetchScope::Enabled: listFilter = QStringLiteral("Enabled"); break; case Akonadi::Protocol::CollectionFetchScope::Index: listFilter = QStringLiteral("Index"); break; case Akonadi::Protocol::CollectionFetchScope::Sync: listFilter = QStringLiteral("Sync"); break; } appendRow(item, QStringLiteral("List Filter"), listFilter); appendRow(item, QStringLiteral("Include Statistics"), toString(cfs.includeStatistics())); appendRow(item, QStringLiteral("Resource"), cfs.resource()); appendRow(item, QStringLiteral("Content Mime Types"), cfs.contentMimeTypes().join(QStringLiteral(", "))); appendRow(item, QStringLiteral("Attributes"), toString(cfs.attributes())); appendRow(item, QStringLiteral("Fetch ID Only"), toString(cfs.fetchIdOnly())); QString ancestorRetrieval; switch (cfs.ancestorRetrieval()) { case Akonadi::Protocol::CollectionFetchScope::All: ancestorRetrieval = QStringLiteral("All"); break; case Akonadi::Protocol::CollectionFetchScope::Parent: ancestorRetrieval = QStringLiteral("Parent"); break; case Akonadi::Protocol::CollectionFetchScope::None: ancestorRetrieval = QStringLiteral("None"); break; } appendRow(item, QStringLiteral("Ancestor Retrieval"), ancestorRetrieval); appendRow(item, QStringLiteral("Ancestor Fetch ID Only"), toString(cfs.ancestorFetchIdOnly())); appendRow(item, QStringLiteral("Ancestor Attributes"), toString(cfs.ancestorAttributes())); appendRow(item, QStringLiteral("Ignore Retrieval Errors"), toString(cfs.ignoreRetrievalErrors())); model->appendRow(item); } diff --git a/src/notificationmonitor.h b/src/notificationmonitor.h index 6ef55c7..02020be 100644 --- a/src/notificationmonitor.h +++ b/src/notificationmonitor.h @@ -1,65 +1,71 @@ /* Copyright (c) 2009 Volker Krause This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef AKONADICONSOLE_NOTIFICATIONMONITOR_H #define AKONADICONSOLE_NOTIFICATIONMONITOR_H #include #include class QModelIndex; class NotificationModel; +class NotificationFilterModel; class QFile; class QTreeView; class QStandardItem; class QStandardItemModel; class QSplitter; +namespace KPIM { +class KCheckComboBox; +} class NotificationMonitor : public QWidget { Q_OBJECT public: explicit NotificationMonitor(QWidget *parent); ~NotificationMonitor() override; private Q_SLOTS: void contextMenu(const QPoint &pos); private: void onNotificationSelected(const QModelIndex &index); void populateItemNtfTree(QStandardItemModel *model, const Akonadi::Protocol::ItemChangeNotification &ntf); void populateCollectionNtfTree(QStandardItemModel *mddel, const Akonadi::Protocol::CollectionChangeNotification &ntf); void populateTagNtfTree(QStandardItemModel *model, const Akonadi::Protocol::TagChangeNotification &ntf); void populateRelationNtfTree(QStandardItemModel *model, const Akonadi::Protocol::RelationChangeNotification &ntf); void populateSubscriptionNtfTree(QStandardItemModel *model, const Akonadi::Protocol::SubscriptionChangeNotification &ntf); void populateItemTree(QStandardItem *parent, const Akonadi::Protocol::FetchItemsResponse &item); void populateCollectionTree(QStandardItem *parent, const Akonadi::Protocol::FetchCollectionsResponse &collection); void populateTagTree(QStandardItem *parent, const Akonadi::Protocol::FetchTagsResponse &tag); void populateAttributesTree(QStandardItem *parent, const Akonadi::Protocol::Attributes &attributes); QStandardItem *populateAncestorTree(QStandardItem *parent, const Akonadi::Protocol::Ancestor &ancestor); NotificationModel *m_model = nullptr; QSplitter *m_splitter = nullptr; QTreeView *m_treeView = nullptr; QTreeView *m_ntfView = nullptr; + KPIM::KCheckComboBox *mTypeFilterCombo = nullptr; + NotificationFilterModel *m_filterModel = nullptr; }; #endif