diff --git a/CMakeLists.txt b/CMakeLists.txt index cdb9bac..5d78cc2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,114 +1,111 @@ cmake_minimum_required(VERSION 3.1) set(KDEPIM_VERSION_NUMBER "5.9.40") project(akonadiconsole VERSION ${KDEPIM_VERSION_NUMBER}) set(KF5_VERSION "5.50.0") find_package(ECM ${KF5_VERSION} CONFIG REQUIRED) set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH}) include(ECMInstallIcons) include(ECMOptionalAddSubdirectory) include(ECMSetupVersion) include(ECMAddTests) include(ECMMarkNonGuiExecutable) include(GenerateExportHeader) include(ECMGenerateHeaders) include(CMakePackageConfigHelpers) include(FeatureSummary) include(CheckFunctionExists) include(ECMGeneratePriFile) include(KDEInstallDirs) include(KDECMakeSettings) -include(KDECompilerSettings NO_POLICY_SCOPE) +include(KDEFrameworkCompilerSettings NO_POLICY_SCOPE) include(ECMAddAppIcon) include(ECMQtDeclareLoggingCategory) include(ECMCoverageOption) # Do NOT add quote set(KDEPIM_DEV_VERSION alpha) # add an extra space if(DEFINED KDEPIM_DEV_VERSION) set(KDEPIM_DEV_VERSION " ${KDEPIM_DEV_VERSION}") endif() set(KDEPIM_VERSION "${KDEPIM_VERSION_NUMBER}${KDEPIM_DEV_VERSION}") set(KDEPIM_LIB_VERSION "${KDEPIM_VERSION_NUMBER}") set(KDEPIM_LIB_SOVERSION "5") set(AKONADI_CONTACT_VERSION "5.9.40") set(CALENDARSUPPORT_LIB_VERSION_LIB "5.9.40") set(AKONADI_VERSION "5.9.42") set(AKONADI_SEARCH_VERSION "5.9.40") set(KDEPIM_LIB_VERSION "${KDEPIM_VERSION_NUMBER}") set(KDEPIM_LIB_SOVERSION "5") set(QT_REQUIRED_VERSION "5.9.0") find_package(Qt5 ${QT_REQUIRED_VERSION} CONFIG REQUIRED Widgets DBus Sql Test) set(MESSAGELIB_LIB_VERSION_LIB "5.9.40") set(LIBKDEPIM_LIB_VERSION_LIB "5.9.40") set(KCALENDARCORE_LIB_VERSION "5.9.40") set(KCONTACTS_LIB_VERSION "5.9.40") set(KMIME_LIB_VERSION "5.9.40") # Find KF5 package find_package(KF5Completion ${KF5_VERSION} CONFIG REQUIRED) find_package(KF5Config ${KF5_VERSION} CONFIG REQUIRED) find_package(KF5ConfigWidgets ${KF5_VERSION} CONFIG REQUIRED) find_package(KF5DBusAddons ${KF5_VERSION} CONFIG REQUIRED) find_package(KF5DocTools ${KF5_VERSION} REQUIRED) find_package(KF5I18n ${KF5_VERSION} CONFIG REQUIRED) find_package(KF5ItemModels ${KF5_VERSION} CONFIG REQUIRED) find_package(KF5TextWidgets ${KF5_VERSION} CONFIG REQUIRED) find_package(KF5WidgetsAddons ${KF5_VERSION} CONFIG REQUIRED) find_package(KF5XmlGui ${KF5_VERSION} CONFIG REQUIRED) find_package(KF5Crash ${KF5_VERSION} REQUIRED) find_package(KF5Completion ${KF5_VERSION} REQUIRED) find_package(KF5ItemViews ${KF5_VERSION} REQUIRED) find_package(KF5KIO ${KF5_VERSION} REQUIRED) # Find KdepimLibs Package find_package(KF5Akonadi ${AKONADI_VERSION} CONFIG REQUIRED) find_package(KF5Contacts ${KCONTACTS_LIB_VERSION} CONFIG REQUIRED) find_package(KF5CalendarCore ${KCALENDARCORE_LIB_VERSION} CONFIG REQUIRED) find_package(KF5Mime ${KMIME_LIB_VERSION} CONFIG REQUIRED) if (NOT WIN32) find_package(KF5AkonadiContact ${AKONADI_CONTACT_VERSION} CONFIG REQUIRED) find_package(KF5CalendarSupport ${CALENDARSUPPORT_LIB_VERSION_LIB} CONFIG REQUIRED) find_package(KF5MessageViewer ${MESSAGELIB_LIB_VERSION_LIB} CONFIG REQUIRED) find_package(KF5Libkdepim ${LIBKDEPIM_LIB_VERSION_LIB} CONFIG REQUIRED) find_package(KF5AkonadiSearch ${AKONADI_SEARCH_VERSION} CONFIG REQUIRED) find_package(Xapian CONFIG) set_package_properties(Xapian PROPERTIES DESCRIPTION "The Xapian search engine library" URL "http://xapian.org" TYPE REQUIRED ) set(ENABLE_LIBKDEPIM TRUE) set(ENABLE_SEARCH TRUE) set(ENABLE_CONTENTVIEWS TRUE) endif() include_directories(${akonadiconsole_SOURCE_DIR} ${akonadiconsole_BINARY_DIR} ${XAPIAN_INCLUDE_DIR}) configure_file(config-akonadiconsole.h.cmake ${CMAKE_BINARY_DIR}/config-akonadiconsole.h) -add_definitions(-DQT_NO_URL_CAST_FROM_STRING) -add_definitions(-DQT_USE_QSTRINGBUILDER) -add_definitions(-DQT_NO_NARROWING_CONVERSIONS_IN_CONNECT) install( FILES akonadiconsole.renamecategories akonadiconsole.categories DESTINATION ${KDE_INSTALL_CONFDIR} ) add_subdirectory(src) if(BUILD_TESTING) add_subdirectory(autotests) endif() feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES) diff --git a/autotests/jobtrackermodeltest.cpp b/autotests/jobtrackermodeltest.cpp index ec13cea..55d80ef 100644 --- a/autotests/jobtrackermodeltest.cpp +++ b/autotests/jobtrackermodeltest.cpp @@ -1,188 +1,188 @@ /* Copyright (C) 2017-2018 Montel Laurent Copyright (c) 2017 David Faure This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2, as published by the Free Software Foundation. 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 "jobtrackermodeltest.h" #include "jobtracker.h" #include "jobtrackermodel.h" //#include "modeltest.h" #include #include #include static QString rowSpyToText(const QSignalSpy &spy) { if (!spy.isValid()) { return QStringLiteral("THE SIGNALSPY IS INVALID!"); } QString str; for (int i = 0; i < spy.count(); ++i) { str += spy.at(i).at(1).toString() + QLatin1Char(',') + spy.at(i).at(2).toString(); if (i + 1 < spy.count()) { str += QLatin1Char(';'); } } return str; } JobTrackerModelTest::JobTrackerModelTest(QObject *parent) : QObject(parent) { } JobTrackerModelTest::~JobTrackerModelTest() { } void JobTrackerModelTest::initTestCase() { // Don't interfere with a running akonadiconsole - Akonadi::Instance::setIdentifier("jobtrackertest"); + Akonadi::Instance::setIdentifier(QStringLiteral("jobtrackertest")); } void JobTrackerModelTest::shouldBeEmpty() { JobTrackerModel model("jobtracker"); QCOMPARE(model.rowCount(), 0); QCOMPARE(model.columnCount(), 7); } void JobTrackerModelTest::shouldDisplayOneJob() { // GIVEN JobTrackerModel model("jobtracker"); //ModelTest modelTest(&model); - const QString jobName("job1"); + const QString jobName(QStringLiteral("job1")); QSignalSpy rowATBISpy(&model, &QAbstractItemModel::rowsAboutToBeInserted); QSignalSpy rowInsertedSpy(&model, &QAbstractItemModel::rowsInserted); connect(&model, &QAbstractItemModel::rowsAboutToBeInserted, this, [&](const QModelIndex & parent) { // rowsAboutToBeInserted is supposed to be emitted before the insert if (!parent.isValid()) { QCOMPARE(model.rowCount(), 0); } else { QCOMPARE(model.rowCount(parent), 0); } }); connect(&model, &QAbstractItemModel::rowsInserted, this, [&](const QModelIndex & parent) { if (!parent.isValid()) { QCOMPARE(model.rowCount(), 1); QVERIFY(model.index(0, 0).isValid()); } else { QCOMPARE(model.rowCount(parent), 1); } }); // WHEN - model.jobTracker().jobCreated("session1", jobName, QString(), "type1", "debugStr1"); + model.jobTracker().jobCreated(QStringLiteral("session1"), jobName, QString(), QStringLiteral("type1"), QStringLiteral("debugStr1")); // THEN QCOMPARE(model.rowCount(), 1); const QModelIndex sessionIndex = model.index(0, 0); QCOMPARE(sessionIndex.data().toString(), QStringLiteral("session1")); QCOMPARE(model.rowCount(sessionIndex), 1); const QModelIndex sessionCol1Index = model.index(0, 1); QVERIFY(sessionCol1Index.isValid()); QCOMPARE(model.rowCount(sessionCol1Index), 0); const QModelIndex jobIndex = model.index(0, 0, sessionIndex); QCOMPARE(jobIndex.data().toString(), QStringLiteral("job1")); QCOMPARE(model.rowCount(jobIndex), 0); QCOMPARE(rowSpyToText(rowATBISpy), QStringLiteral("0,0;0,0")); QCOMPARE(rowSpyToText(rowInsertedSpy), QStringLiteral("0,0;0,0")); } void JobTrackerModelTest::shouldSignalDataChanges() { // GIVEN JobTrackerModel model("jobtracker"); - const QString jobName("job1"); - model.jobTracker().jobCreated("session1", jobName, QString(), "type1", "debugStr1"); + const QString jobName(QStringLiteral("job1")); + model.jobTracker().jobCreated(QStringLiteral("session1"), jobName, QString(), QStringLiteral("type1"), QStringLiteral("debugStr1")); QSignalSpy dataChangedSpy(&model, &JobTrackerModel::dataChanged); // WHEN model.jobTracker().jobStarted(jobName); model.jobTracker().signalUpdates(); // THEN QCOMPARE(dataChangedSpy.count(), 1); // AND WHEN model.jobTracker().jobEnded(jobName, QString()); model.jobTracker().signalUpdates(); // THEN QCOMPARE(dataChangedSpy.count(), 2); } void JobTrackerModelTest::shouldHandleReset() { // GIVEN JobTrackerModel model("jobtracker"); - const QString jobName("job1"); - model.jobTracker().jobCreated("session1", jobName, QString(), "type1", "debugStr1"); + const QString jobName(QStringLiteral("job1")); + model.jobTracker().jobCreated(QStringLiteral("session1"), jobName, QString(), QStringLiteral("type1"), QStringLiteral("debugStr1")); QSignalSpy modelATBResetSpy(&model, &JobTrackerModel::modelAboutToBeReset); QSignalSpy modelResetSpy(&model, &JobTrackerModel::modelReset); QSignalSpy dataChangedSpy(&model, &JobTrackerModel::dataChanged); // WHEN model.resetTracker(); QCOMPARE(modelATBResetSpy.count(), 1); QCOMPARE(modelResetSpy.count(), 1); // AND then an update comes for that job which has been removed model.jobTracker().jobStarted(jobName); model.jobTracker().jobEnded(jobName, QString()); model.jobTracker().signalUpdates(); // THEN it should be ignored QCOMPARE(dataChangedSpy.count(), 0); } void JobTrackerModelTest::shouldHandleDuplicateJob() { // GIVEN JobTrackerModel model("jobtracker"); - const QString jobName("job1"); - model.jobTracker().jobCreated("session1", jobName, QString(), "type1", "debugStr1"); + const QString jobName(QStringLiteral("job1")); + model.jobTracker().jobCreated(QStringLiteral("session1"), jobName, QString(), QStringLiteral("type1"), QStringLiteral("debugStr1")); model.jobTracker().jobStarted(jobName); model.jobTracker().jobEnded(jobName, QString()); model.jobTracker().signalUpdates(); // WHEN QSignalSpy rowATBISpy(&model, &QAbstractItemModel::rowsAboutToBeInserted); QSignalSpy rowInsertedSpy(&model, &QAbstractItemModel::rowsInserted); - model.jobTracker().jobCreated("session1", jobName, QString(), "type1", "debugStr1"); + model.jobTracker().jobCreated(QStringLiteral("session1"), jobName, QString(), QStringLiteral("type1"), QStringLiteral("debugStr1")); // THEN QCOMPARE(model.rowCount(), 1); // 1 session const QModelIndex sessionIndex = model.index(0, 0); QCOMPARE(rowSpyToText(rowATBISpy), QStringLiteral("1,1")); QCOMPARE(rowSpyToText(rowInsertedSpy), QStringLiteral("1,1")); QCOMPARE(model.rowCount(sessionIndex), 2); // AND WHEN model.jobTracker().jobStarted(jobName); model.jobTracker().jobEnded(jobName, QStringLiteral("error")); model.jobTracker().signalUpdates(); // THEN QCOMPARE(sessionIndex.child(0, JobTrackerModel::ColumnState).data().toString(), QStringLiteral("Ended")); QCOMPARE(sessionIndex.child(1, JobTrackerModel::ColumnState).data().toString(), QStringLiteral("Failed: error")); } QTEST_GUILESS_MAIN(JobTrackerModelTest) diff --git a/autotests/jobtrackermodeltest.h b/autotests/jobtrackermodeltest.h index 37f55e7..8c169b2 100644 --- a/autotests/jobtrackermodeltest.h +++ b/autotests/jobtrackermodeltest.h @@ -1,40 +1,40 @@ /* Copyright (C) 2017-2018 Montel Laurent Copyright (c) 2017 David Faure This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2, as published by the Free Software Foundation. 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 JOBTRACKERMODELTEST_H #define JOBTRACKERMODELTEST_H #include class JobTracker; class JobTrackerModelTest : public QObject { Q_OBJECT public: explicit JobTrackerModelTest(QObject *parent = nullptr); ~JobTrackerModelTest(); -private slots: +private Q_SLOTS: void initTestCase(); void shouldBeEmpty(); void shouldDisplayOneJob(); void shouldSignalDataChanges(); void shouldHandleReset(); void shouldHandleDuplicateJob(); }; #endif // JOBTRACKERTEST_H diff --git a/autotests/jobtrackertest.cpp b/autotests/jobtrackertest.cpp index f97216a..74d3480 100644 --- a/autotests/jobtrackertest.cpp +++ b/autotests/jobtrackertest.cpp @@ -1,144 +1,144 @@ /* Copyright (C) 2017-2018 Montel Laurent Copyright (c) 2017 David Faure This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2, as published by the Free Software Foundation. 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 "jobtrackertest.h" #include "jobtracker.h" #include #include #include static QString intPairListToString(const QVariant &var) { auto arg = var.value>>(); QString ret; for (const auto &pair : arg) { if (!ret.isEmpty()) { - ret += ' '; + ret += QLatin1Char(' '); } - ret += QString::number(pair.first) + ',' + QString::number(pair.second); + ret += QString::number(pair.first) + QLatin1Char(',') + QString::number(pair.second); } return ret; } JobTrackerTest::JobTrackerTest(QObject *parent) : QObject(parent) { } JobTrackerTest::~JobTrackerTest() { } void JobTrackerTest::initTestCase() { // Don't interfere with a running akonadiconsole - Akonadi::Instance::setIdentifier("jobtrackertest"); + Akonadi::Instance::setIdentifier(QStringLiteral("jobtrackertest")); qRegisterMetaType>>(); } void JobTrackerTest::shouldBeEmpty() { JobTracker tracker("jobtracker"); QVERIFY(tracker.sessions().isEmpty()); QCOMPARE(tracker.parentId(0), -1); } void JobTrackerTest::shouldDisplayOneJob() { // GIVEN JobTracker tracker("jobtracker"); - const QString jobName("job1"); + const QString jobName(QStringLiteral("job1")); QSignalSpy spyAboutToAdd(&tracker, &JobTracker::aboutToAdd); QSignalSpy spyUpdated(&tracker, &JobTracker::updated); // WHEN - tracker.jobCreated("session1", jobName, QString(), "type1", "debugStr1"); + tracker.jobCreated(QStringLiteral("session1"), jobName, QString(), QStringLiteral("type1"), QStringLiteral("debugStr1")); // THEN QCOMPARE(tracker.sessions().count(), 1); QCOMPARE(tracker.sessions().at(0), QStringLiteral("session1")); - QCOMPARE(tracker.idForSession("session1"), -2); + QCOMPARE(tracker.idForSession(QStringLiteral("session1")), -2); QCOMPARE(tracker.sessionForId(-2), QStringLiteral("session1")); QCOMPARE(tracker.parentId(-2), -1); QCOMPARE(tracker.jobCount(-2), 1); QCOMPARE(tracker.jobIdAt(0, -2), 42); // job is child of session QCOMPARE(tracker.info(42).name, jobName); QCOMPARE(tracker.info(42).state, JobInfo::Initial); QCOMPARE(tracker.parentId(42), -2); QCOMPARE(tracker.rowForJob(42, -2), 0); QCOMPARE(tracker.jobCount(42), 0); // no child QCOMPARE(spyAboutToAdd.count(), 2); QCOMPARE(spyAboutToAdd.at(0).at(0).toInt(), 0); QCOMPARE(spyAboutToAdd.at(0).at(1).toInt(), -1); QCOMPARE(spyAboutToAdd.at(1).at(0).toInt(), 0); QCOMPARE(spyAboutToAdd.at(1).at(1).toInt(), -2); QCOMPARE(spyUpdated.count(), 0); } void JobTrackerTest::shouldHandleJobStart() { // GIVEN JobTracker tracker("jobtracker"); - const QString jobName("job1"); - tracker.jobCreated("session1", jobName, QString(), "type1", "debugStr1"); + const QString jobName(QStringLiteral("job1")); + tracker.jobCreated(QStringLiteral("session1"), jobName, QString(), QStringLiteral("type1"), QStringLiteral("debugStr1")); tracker.signalUpdates(); QSignalSpy spyAdded(&tracker, &JobTracker::added); QSignalSpy spyUpdated(&tracker, &JobTracker::updated); // WHEN tracker.jobStarted(jobName); // THEN QCOMPARE(tracker.info(42).state, JobInfo::Running); tracker.signalUpdates(); QCOMPARE(spyAdded.count(), 0); QCOMPARE(spyUpdated.count(), 1); QCOMPARE(intPairListToString(spyUpdated.at(0).at(0)), QStringLiteral("0,-2")); } void JobTrackerTest::shouldHandleJobEnd() { // GIVEN JobTracker tracker("jobtracker"); - const QString jobName("job1"); - tracker.jobCreated("session1", jobName, QString(), "type1", "debugStr1"); + const QString jobName(QStringLiteral("job1")); + tracker.jobCreated(QStringLiteral("session1"), jobName, QString(), QStringLiteral("type1"), QStringLiteral("debugStr1")); tracker.jobStarted(jobName); tracker.signalUpdates(); QSignalSpy spyAdded(&tracker, &JobTracker::added); QSignalSpy spyUpdated(&tracker, &JobTracker::updated); // WHEN - tracker.jobEnded("job1", "errorString"); + tracker.jobEnded(QStringLiteral("job1"), QStringLiteral("errorString")); // THEN QCOMPARE(tracker.info(42).state, JobInfo::Failed); QCOMPARE(tracker.info(42).error, QStringLiteral("errorString")); tracker.signalUpdates(); QCOMPARE(spyAdded.count(), 0); QCOMPARE(spyUpdated.count(), 1); QCOMPARE(intPairListToString(spyUpdated.at(0).at(0)), QStringLiteral("0,-2")); } QTEST_GUILESS_MAIN(JobTrackerTest) diff --git a/autotests/jobtrackertest.h b/autotests/jobtrackertest.h index ba870aa..0d22e0f 100644 --- a/autotests/jobtrackertest.h +++ b/autotests/jobtrackertest.h @@ -1,39 +1,39 @@ /* Copyright (C) 2017-2018 Montel Laurent Copyright (c) 2017 David Faure This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2, as published by the Free Software Foundation. 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 JOBTRACKERTEST_H #define JOBTRACKERTEST_H #include class JobTracker; class JobTrackerTest : public QObject { Q_OBJECT public: explicit JobTrackerTest(QObject *parent = nullptr); ~JobTrackerTest(); -private slots: +private Q_SLOTS: void initTestCase(); void shouldBeEmpty(); void shouldDisplayOneJob(); void shouldHandleJobStart(); void shouldHandleJobEnd(); }; #endif // JOBTRACKERTEST_H diff --git a/src/searchwidget.cpp b/src/searchwidget.cpp index 7ff912c..52489df 100644 --- a/src/searchwidget.cpp +++ b/src/searchwidget.cpp @@ -1,244 +1,244 @@ /* This file is part of Akonadi. Copyright (c) 2009 Tobias Koenig 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 #include "searchwidget.h" #include "akonadiconsole_debug.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include SearchWidget::SearchWidget(QWidget *parent) : QWidget(parent) , mDatabase(nullptr) { Akonadi::ControlGui::widgetNeedsAkonadi(this); QVBoxLayout *layout = new QVBoxLayout(this); QHBoxLayout *hbox = new QHBoxLayout; - hbox->addWidget(new QLabel(QStringLiteral("Search store:")), 0, 0); + hbox->addWidget(new QLabel(QStringLiteral("Search store:")), 0, {}); mStoreCombo = new KComboBox; mStoreCombo->setObjectName(QStringLiteral("SearchStoreCombo")); hbox->addWidget(mStoreCombo); hbox->addStretch(); auto button = new QPushButton(QStringLiteral("Search")); hbox->addWidget(button); layout->addLayout(hbox); mVSplitter = new QSplitter(Qt::Vertical); mVSplitter->setObjectName(QStringLiteral("SearchVSplitter")); auto w = new QWidget; QVBoxLayout *vbox = new QVBoxLayout(w); vbox->addWidget(new QLabel(QStringLiteral("Search query:"))); mQueryWidget = new QPlainTextEdit; vbox->addWidget(mQueryWidget); mVSplitter->addWidget(w); mHSplitter = new QSplitter(Qt::Horizontal); mHSplitter->setObjectName(QStringLiteral("SearchHSplitter")); w = new QWidget; vbox = new QVBoxLayout(w); vbox->addWidget(new QLabel(QStringLiteral("Results (Documents):"))); mDatabaseView = new QListView; mDatabaseView->setEditTriggers(QListView::NoEditTriggers); mDocumentModel = new QStandardItemModel(this); mDatabaseView->setModel(mDocumentModel); vbox->addWidget(mDatabaseView); mHSplitter->addWidget(w); w = new QWidget; vbox = new QVBoxLayout(w); vbox->addWidget(new QLabel(QStringLiteral("Document:"))); mDocumentView = new QTreeView; mDocumentView->setEditTriggers(QTreeView::NoEditTriggers); mTermModel = new QStandardItemModel(this); mDocumentView->setModel(mTermModel); vbox->addWidget(mDocumentView); mHSplitter->addWidget(w); w = new QWidget; vbox = new QVBoxLayout(w); vbox->addWidget(new QLabel(QStringLiteral("Item:"))); mItemView = new QTextBrowser; vbox->addWidget(mItemView); mHSplitter->addWidget(w); mVSplitter->addWidget(mHSplitter); layout->addWidget(mVSplitter); const auto stores = Akonadi::Search::SearchStore::searchStores(); for (const auto &store : stores) { mStoreCombo->addItem(store->types().last(), QVariant::fromValue(store)); } connect(button, &QPushButton::clicked, this, &SearchWidget::search); connect(mDatabaseView, &QListView::activated, this, &SearchWidget::fetchItem); connect(mStoreCombo, QOverload::of(&KComboBox::currentIndexChanged), this, &SearchWidget::openStore); openStore(0); KConfigGroup config(KSharedConfig::openConfig(), "SearchWidget"); mQueryWidget->setPlainText(config.readEntry("query")); } SearchWidget::~SearchWidget() { KConfigGroup config(KSharedConfig::openConfig(), "SearchWidget"); config.writeEntry("query", mQueryWidget->toPlainText()); config.sync(); } void SearchWidget::openStore(int idx) { auto store = mStoreCombo->itemData(idx, Qt::UserRole).value>(); auto xapianStore = store.objectCast(); Q_ASSERT(xapianStore); if (mDatabase) { mDatabase->close(); delete mDatabase; } try { qCDebug(AKONADICONSOLE_LOG) << "Opening store" << xapianStore->dbPath(); mDatabase = new Xapian::Database(xapianStore->dbPath().toStdString()); } catch (Xapian::Error &e) { xapianError(e); delete mDatabase; mDatabase = nullptr; } } void SearchWidget::xapianError(const Xapian::Error &e) { qCWarning(AKONADICONSOLE_LOG) << e.get_type() << QString::fromStdString(e.get_description()) << e.get_error_string(); QMessageBox::critical(this, QStringLiteral("Xapian error"), QStringLiteral("%1: %2").arg(QString::fromUtf8(e.get_type()), QString::fromStdString(e.get_msg()))); } void SearchWidget::search() { if (!mDatabase) { QMessageBox::critical(this, QStringLiteral("Error"), QStringLiteral("No Xapian database is opened")); return; } mDocumentModel->clear(); try { const auto q = mQueryWidget->toPlainText().toStdString(); auto it = mDatabase->postlist_begin(q); const auto end = mDatabase->postlist_end(q); for (; it != end; ++it) { auto item = new QStandardItem(QString::number(*it)); item->setData(*it, Qt::UserRole); mDocumentModel->appendRow(item); } } catch (Xapian::Error &e) { xapianError(e); return; } } void SearchWidget::fetchItem(const QModelIndex &index) { if (!index.isValid()) { return; } const auto docId = index.data(Qt::UserRole).value(); try { const auto doc = mDatabase->get_document(docId); mTermModel->clear(); mTermModel->setColumnCount(2); mTermModel->setHorizontalHeaderLabels({ QStringLiteral("Term/Value"), QStringLiteral("WDF/Slot") }); auto termsRoot = new QStandardItem(QStringLiteral("Terms")); mTermModel->appendRow(termsRoot); for (auto it = doc.termlist_begin(), end = doc.termlist_end(); it != end; ++it) { termsRoot->appendRow({ new QStandardItem(QString::fromStdString(*it)), new QStandardItem(QString::number(it.get_wdf())) }); } auto valuesRoot = new QStandardItem(QStringLiteral("Values")); mTermModel->appendRow(valuesRoot); const auto end = doc.values_end(); // Xapian 1.2 has different type for _begin() and _end() iters for (auto it = doc.values_begin(); it != end; ++it) { valuesRoot->appendRow({ new QStandardItem(QString::fromStdString(*it)), new QStandardItem(QString::number(it.get_valueno())) }); } } catch (const Xapian::Error &e) { xapianError(e); return; } Akonadi::ItemFetchJob *fetchJob = new Akonadi::ItemFetchJob(Akonadi::Item(docId)); fetchJob->fetchScope().fetchFullPayload(); connect(fetchJob, &Akonadi::ItemFetchJob::result, this, &SearchWidget::itemFetched); } void SearchWidget::itemFetched(KJob *job) { mItemView->clear(); if (job->error()) { KMessageBox::error(this, QStringLiteral("Error on fetching item")); return; } Akonadi::ItemFetchJob *fetchJob = qobject_cast(job); if (!fetchJob->items().isEmpty()) { const Akonadi::Item item = fetchJob->items().first(); mItemView->setPlainText(QString::fromUtf8(item.payloadData())); } }