diff --git a/CMakeLists.txt b/CMakeLists.txt index 0b48eaf3c..46ff7ebdc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,361 +1,361 @@ cmake_minimum_required(VERSION 3.5) -set(PIM_VERSION "5.13.40") +set(PIM_VERSION "5.13.41") project(Akonadi VERSION ${PIM_VERSION}) if (MSVC) # On Windows our target compiler is MSVC 14.14, which supports C++17, so # we can enable it. MSVC does not ship experimental/optional and similar # and disallows use of C++17 classes in C++14 mode. set(CMAKE_CXX_STANDARD 17) else() # On Linux, MacOS and BSD we need to support older compilers that only # offer C++14. set(CMAKE_CXX_STANDARD 14) set(WITH_3RDPARTY_OPTIONAL TRUE) endif() # ECM setup set(KF5_MIN_VERSION "5.64.0") find_package(ECM ${KF5_MIN_VERSION} CONFIG REQUIRED) set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules) include(KDEInstallDirs) include(KDECMakeSettings) include(KDEFrameworkCompilerSettings NO_POLICY_SCOPE) include(GenerateExportHeader) include(ECMGenerateHeaders) include(ECMGeneratePriFile) include(ECMSetupVersion) include(FeatureSummary) include(CheckIncludeFiles) include(ECMQtDeclareLoggingCategory) include(CheckSymbolExists) include(KDEPackageAppTemplates) include(ECMMarkNonGuiExecutable) include(ECMAddTests) include(AkonadiMacros) set(QT_REQUIRED_VERSION "5.12.0") set(KDE_APPLICATIONS_VERSION "20.03.70") set(AKONADI_FULL_VERSION "${PIM_VERSION} (${KDE_APPLICATIONS_VERSION})") configure_file(akonadifull-version.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/akonadifull-version.h @ONLY) ecm_setup_version(PROJECT VARIABLE_PREFIX AKONADI VERSION_HEADER "${CMAKE_CURRENT_BINARY_DIR}/akonadi_version.h" PACKAGE_VERSION_FILE "${CMAKE_CURRENT_BINARY_DIR}/KF5AkonadiConfigVersion.cmake" SOVERSION 5) # Find packages find_package(Qt5Core ${QT_REQUIRED_VERSION} REQUIRED COMPONENTS Private) find_package(Qt5Sql ${QT_REQUIRED_VERSION} REQUIRED COMPONENTS Private) find_package(Qt5DBus ${QT_REQUIRED_VERSION} REQUIRED) find_package(Qt5Network ${QT_REQUIRED_VERSION} REQUIRED) find_package(Qt5Test ${QT_REQUIRED_VERSION} REQUIRED) find_package(Qt5Widgets ${QT_REQUIRED_VERSION} REQUIRED) find_package(Qt5Xml ${QT_REQUIRED_VERSION} REQUIRED) find_package(KF5Completion ${KF5_MIN_VERSION} CONFIG REQUIRED) find_package(KF5Config ${KF5_MIN_VERSION} CONFIG REQUIRED) find_package(KF5ConfigWidgets ${KF5_MIN_VERSION} CONFIG REQUIRED) find_package(KF5CoreAddons ${KF5_MIN_VERSION} CONFIG REQUIRED) find_package(KF5DBusAddons ${KF5_MIN_VERSION} CONFIG REQUIRED) find_package(KF5I18n ${KF5_MIN_VERSION} CONFIG REQUIRED) find_package(KF5IconThemes ${KF5_MIN_VERSION} CONFIG REQUIRED) find_package(KF5ItemModels ${KF5_MIN_VERSION} CONFIG REQUIRED) find_package(KF5ItemViews ${KF5_MIN_VERSION} CONFIG REQUIRED) find_package(KF5KIO ${KF5_MIN_VERSION} CONFIG REQUIRED) find_package(KF5WidgetsAddons ${KF5_MIN_VERSION} CONFIG REQUIRED) find_package(KF5WindowSystem ${KF5_MIN_VERSION} CONFIG REQUIRED) find_package(KF5XmlGui ${KF5_MIN_VERSION} CONFIG REQUIRED) find_package(KF5Crash ${KF5_MIN_VERSION} CONFIG REQUIRED) find_package(Qt5Designer NO_MODULE) set_package_properties(Qt5Designer PROPERTIES PURPOSE "Required to build the Qt Designer plugins" TYPE OPTIONAL ) option(BUILD_DESIGNERPLUGIN "Build plugin for Qt Designer" ON) add_feature_info(DESIGNERPLUGIN ${BUILD_DESIGNERPLUGIN} "Build plugin for Qt Designer") set(Boost_MINIMUM_VERSION "1.34.0") find_package(Boost ${Boost_MINIMUM_VERSION}) set_package_properties(Boost PROPERTIES DESCRIPTION "Boost C++ Libraries" URL "https://www.boost.org" TYPE REQUIRED ) set(AccountsQt5_MINIMUM_VERSION "1.15") find_package(AccountsQt5 ${AccountsQt5_MINIMUM_VERSION}) set_package_properties(AccountsQt5 PROPERTIES DESCRIPTION "Qt bindings for the Accounts framework" URL "https://gitlab.com/accounts-sso/libaccounts-qt" TYPE OPTIONAL ) set(KAccounts_MINIMUM_VERSION "19.08.0") find_package(KAccounts ${KAccounts_MINIMUM_VERSION}) set_package_properties(KAccounts PROPERTIES DESCRIPTION "KDE library for Accounts framework integration" URL "https://cgit.kde.org/kaccounts-integration.git" TYPE OPTIONAL ) if (${AccountsQt5_FOUND} AND ${KAccounts_FOUND}) set(WITH_ACCOUNTS TRUE) endif() if(BUILD_TESTING) set(AKONADI_TESTS_EXPORT AKONADICORE_EXPORT) set(AKONADIWIDGET_TESTS_EXPORT AKONADIWIDGETS_EXPORT) add_definitions(-DBUILD_TESTING) endif() configure_file(akonaditests_export.h.in "${CMAKE_CURRENT_BINARY_DIR}/akonaditests_export.h") configure_file(akonadiwidgetstests_export.h.in "${CMAKE_CURRENT_BINARY_DIR}/akonadiwidgetstests_export.h") # Make sure the KF5Akonadi_DATA_DIR is absolute before passing it to KF5AkonadiConfig.cmake.in # otherwise build fails either on OSX CI, or for normal users if (IS_ABSOLUTE "${KDE_INSTALL_DATADIR_KF5}") set(KF5Akonadi_DATA_DIR "${KDE_INSTALL_DATADIR_KF5}/akonadi") else() set(KF5Akonadi_DATA_DIR "${CMAKE_INSTALL_PREFIX}/${KDE_INSTALL_DATADIR_KF5}/akonadi") endif() check_symbol_exists(malloc_trim "malloc.h" HAVE_MALLOC_TRIM) ############### Build Options ############### option(AKONADI_BUILD_QSQLITE "Build the Sqlite backend." TRUE) option(BUILD_TOOLS "Build and install tools for development and testing purposes." TRUE) option(NO_REGENERATE_MIME "Don't regenerate mime file (developer-only option)" FALSE ) option(INSTALL_APPARMOR "Install AppArmor profiles" TRUE) if(BUILD_TESTING) set(BUILD_TOOLS TRUE) endif() set(SMI_MIN_VERSION "1.3") find_package(SharedMimeInfo ${SMI_MIN_VERSION} REQUIRED) find_program(XSLTPROC_EXECUTABLE xsltproc) if(NOT XSLTPROC_EXECUTABLE) message(FATAL_ERROR "\nThe command line XSLT processor program 'xsltproc' could not be found.\nPlease install xsltproc.\n") endif() find_program(MYSQLD_EXECUTABLE NAMES mysqld PATHS /usr/sbin /usr/local/sbin /usr/libexec /usr/local/libexec /opt/mysql/libexec /usr/mysql/bin /opt/mysql/sbin DOC "The mysqld executable path. ONLY needed at runtime" ) if(MYSQLD_EXECUTABLE) message(STATUS "MySQL Server found: ${MYSQLD_EXECUTABLE}") else() message(STATUS "MySQL Server wasn't found. it is required to use the MySQL backend.") endif() find_path(POSTGRES_PATH NAMES pg_ctl HINTS /usr/lib${LIB_SUFFIX}/postgresql/8.4/bin /usr/lib${LIB_SUFFIX}/postgresql/9.0/bin /usr/lib${LIB_SUFFIX}/postgresql/9.1/bin DOC "The pg_ctl executable path. ONLY needed at runtime by the PostgreSQL backend" ) if(POSTGRES_PATH) message(STATUS "PostgreSQL Server found.") else() message(STATUS "PostgreSQL wasn't found. it is required to use the Postgres backend.") endif() if("${DATABASE_BACKEND}" STREQUAL "SQLITE") set(SQLITE_TYPE "REQUIRED") else() set(SQLITE_TYPE "OPTIONAL") endif() if(AKONADI_BUILD_QSQLITE) set(SQLITE_MIN_VERSION 3.6.23) find_package(Sqlite ${SQLITE_MIN_VERSION}) set_package_properties(Sqlite PROPERTIES URL "https://www.sqlite.org" DESCRIPTION "The Sqlite database library" TYPE ${SQLITE_TYPE} PURPOSE "Required by the Sqlite backend" ) endif() find_program(XMLLINT_EXECUTABLE xmllint) if(NOT XMLLINT_EXECUTABLE) message(STATUS "xmllint not found, skipping akonadidb.xml schema validation") endif() check_include_files(unistd.h HAVE_UNISTD_H) if(HAVE_UNISTD_H) add_definitions(-DHAVE_UNISTD_H) endif() if (IS_ABSOLUTE "${DBUS_INTERFACES_INSTALL_DIR}") set(AKONADI_DBUS_INTERFACES_INSTALL_DIR "${DBUS_INTERFACES_INSTALL_DIR}") else() set(AKONADI_DBUS_INTERFACES_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/${DBUS_INTERFACES_INSTALL_DIR}") endif() if (IS_ABSOLUTE "${KDE_INSTALL_INCLUDEDIR_KF5}") set(AKONADI_INCLUDE_DIR "${KDE_INSTALL_INCLUDEDIR_KF5}") else() set(AKONADI_INCLUDE_DIR "${CMAKE_INSTALL_PREFIX}/${KDE_INSTALL_INCLUDEDIR_KF5}") endif() ############### Build Options ############### include(CTest) # Calls enable_testing(). include(CTestConfig.cmake) if(NOT DEFINED DATABASE_BACKEND) set(DATABASE_BACKEND "MYSQL" CACHE STRING "The default database backend to use for Akonadi. Can be either MYSQL, POSTGRES or SQLITE") endif() ############### CTest options ############### # Set a timeout value of 1 minute per test set(DART_TESTING_TIMEOUT 60) # CTestCustom.cmake has to be in the CTEST_BINARY_DIR. # in the KDE build system, this is the same as CMAKE_BINARY_DIR. configure_file(${CMAKE_CURRENT_SOURCE_DIR}/CTestCustom.cmake ${CMAKE_CURRENT_BINARY_DIR}/CTestCustom.cmake COPYONLY) ############### Macros ############### macro(SET_DEFAULT_DB_BACKEND) set(_backend ${ARGV0}) if("${_backend}" STREQUAL "SQLITE") set(AKONADI_DATABASE_BACKEND QSQLITE3) set(AKONADI_BUILD_QSQLITE TRUE) else() if("${_backend}" STREQUAL "POSTGRES") set(AKONADI_DATABASE_BACKEND QPSQL) else() set(AKONADI_DATABASE_BACKEND QMYSQL) endif() endif() message(STATUS "Using default db backend ${AKONADI_DATABASE_BACKEND}") add_definitions(-DAKONADI_DATABASE_BACKEND="${AKONADI_DATABASE_BACKEND}") endmacro() #### DB BACKEND DEFAULT #### set_default_db_backend(${DATABASE_BACKEND}) ############### Compilers flags ############### if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_C_COMPILER MATCHES "icc" OR (CMAKE_CXX_COMPILER_ID MATCHES "Clang")) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-long-long -std=iso9899:1990 -Wundef -Wcast-align -Werror-implicit-function-declaration -Wchar-subscripts -Wall -Wextra -Wpointer-arith -Wwrite-strings -Wformat-security -Wmissing-format-attribute -fno-common") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wnon-virtual-dtor -Wundef -Wcast-align -Wchar-subscripts -Wall -Wextra -Wpointer-arith -Wformat-security -fno-common -pedantic") set(CMAKE_CXX_FLAGS_DEBUGFULL "-g3 -fno-inline" CACHE STRING "Flags used by the C++ compiler during debugfull builds." FORCE) set(CMAKE_C_FLAGS_DEBUGFULL "-g3 -fno-inline" CACHE STRING "Flags used by the C compiler during debugfull builds." FORCE) mark_as_advanced(CMAKE_CXX_FLAGS_DEBUGFULL CMAKE_C_FLAGS_DEBUGFULL) elseif (MSVC) # This sets the __cplusplus macro to a real value based on the version of C++ specified by # the /std switch. Without it MSVC keeps reporting C++98, so feature detection doesn't work. set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /Zc:__cplusplus") endif() if(MSVC) set(_ENABLE_EXCEPTIONS -EHsc) else() set(_ENABLE_EXCEPTIONS -fexceptions) endif() set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${_ENABLE_EXCEPTIONS}") ############### Configure files ############# configure_file(akonadi-prefix.h.cmake ${Akonadi_BINARY_DIR}/akonadi-prefix.h) configure_file(config-akonadi.h.cmake ${Akonadi_BINARY_DIR}/config-akonadi.h) ############### build targets ############### add_definitions(-DTRANSLATION_DOMAIN=\"libakonadi5\") include_directories( ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR} src ) #add_definitions(-DQT_DISABLE_DEPRECATED_BEFORE=0x060000) add_subdirectory(src) add_subdirectory(icons) add_subdirectory(templates) if(BUILD_TOOLS) # add testrunner (application for managing a self-contained # test environment) add_subdirectory(autotests/libs/testrunner) add_subdirectory(autotests/libs/testresource) add_subdirectory(autotests/libs/testsearchplugin) endif() if(BUILD_TESTING) add_subdirectory(autotests) add_subdirectory(tests) endif() if(INSTALL_APPARMOR) add_subdirectory(apparmor) endif() ############### install stuff ############### install(FILES akonadi-mime.xml DESTINATION ${XDG_MIME_INSTALL_DIR}) if (NOT NO_REGENERATE_MIME) update_xdg_mimetypes(${XDG_MIME_INSTALL_DIR}) endif() ############### CMake Config Files ############### set(CMAKECONFIG_INSTALL_DIR "${KDE_INSTALL_CMAKEPACKAGEDIR}/KF5Akonadi") configure_package_config_file( "${CMAKE_CURRENT_SOURCE_DIR}/KF5AkonadiConfig.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/KF5AkonadiConfig.cmake" INSTALL_DESTINATION ${CMAKECONFIG_INSTALL_DIR} PATH_VARS AKONADI_DBUS_INTERFACES_INSTALL_DIR AKONADI_INCLUDE_DIR KF5Akonadi_DATA_DIR ) install(FILES "${CMAKE_CURRENT_BINARY_DIR}/KF5AkonadiConfig.cmake" "${CMAKE_CURRENT_BINARY_DIR}/KF5AkonadiConfigVersion.cmake" "${CMAKE_CURRENT_SOURCE_DIR}/KF5AkonadiMacros.cmake" DESTINATION "${CMAKECONFIG_INSTALL_DIR}" COMPONENT Devel ) install(EXPORT KF5AkonadiTargets DESTINATION "${CMAKECONFIG_INSTALL_DIR}" FILE KF5AkonadiTargets.cmake NAMESPACE KF5::) install(FILES akonadi.categories akonadi.renamecategories DESTINATION ${KDE_INSTALL_LOGGINGCATEGORIESDIR}) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/akonadi_version.h DESTINATION ${KDE_INSTALL_INCLUDEDIR_KF5} COMPONENT Devel ) feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES) diff --git a/autotests/libs/proxymodelstest.cpp b/autotests/libs/proxymodelstest.cpp index 88ca9036f..22354b0ab 100644 --- a/autotests/libs/proxymodelstest.cpp +++ b/autotests/libs/proxymodelstest.cpp @@ -1,126 +1,125 @@ /* Copyright (c) 2010 Till Adam 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. */ #include -#include +#include #include -class KRFPTestModel : public KRecursiveFilterProxyModel +class KRFPTestModel : public QSortFilterProxyModel { public: - KRFPTestModel(QObject *parent) : KRecursiveFilterProxyModel(parent) { } - - bool acceptRow(int sourceRow, const QModelIndex &sourceParent) const override + KRFPTestModel(QObject *parent) : QSortFilterProxyModel(parent) { } + bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override { const QModelIndex modelIndex = sourceModel()->index(sourceRow, 0, sourceParent); return !modelIndex.data().toString().contains(QLatin1String("three")); } - }; class ProxyModelsTest : public QObject { Q_OBJECT private Q_SLOTS: void initTestCase(); void init(); void testMatch(); private: QStandardItemModel m_model; - KRecursiveFilterProxyModel *m_krfp = nullptr; + QSortFilterProxyModel *m_krfp = nullptr; KRFPTestModel *m_krfptest = nullptr; }; void ProxyModelsTest::initTestCase() { } void ProxyModelsTest::init() { m_model.setRowCount(5); m_model.setColumnCount(1); m_model.setData(m_model.index(0, 0, QModelIndex()), QStringLiteral("one")); QModelIndex idx = m_model.index(1, 0, QModelIndex()); m_model.setData(idx, QStringLiteral("two")); m_model.insertRows(0, 1, idx); m_model.insertColumns(0, 1, idx); m_model.setData(m_model.index(0, 0, idx), QStringLiteral("three")); m_model.setData(m_model.index(2, 0, QModelIndex()), QStringLiteral("three")); m_model.setData(m_model.index(3, 0, QModelIndex()), QStringLiteral("four")); m_model.setData(m_model.index(4, 0, QModelIndex()), QStringLiteral("five")); m_model.setData(m_model.index(4, 0, QModelIndex()), QStringLiteral("mystuff"), Qt::UserRole + 42); - m_krfp = new KRecursiveFilterProxyModel(this); + m_krfp = new QSortFilterProxyModel(this); m_krfp->setSourceModel(&m_model); + m_krfp->setRecursiveFilteringEnabled(true); m_krfptest = new KRFPTestModel(this); m_krfptest->setSourceModel(m_krfp); // some sanity checks that setup worked QCOMPARE(m_model.rowCount(QModelIndex()), 5); QCOMPARE(m_model.data(m_model.index(0, 0)).toString(), QStringLiteral("one")); QCOMPARE(m_krfp->rowCount(QModelIndex()), 5); QCOMPARE(m_krfp->data(m_krfp->index(0, 0)).toString(), QStringLiteral("one")); QCOMPARE(m_krfptest->rowCount(QModelIndex()), 4); QCOMPARE(m_krfptest->data(m_krfptest->index(0, 0)).toString(), QStringLiteral("one")); QCOMPARE(m_krfp->rowCount(m_krfp->index(1, 0)), 1); QCOMPARE(m_krfptest->rowCount(m_krfptest->index(1, 0)), 0); } void ProxyModelsTest::testMatch() { QModelIndexList results = m_model.match(m_model.index(0, 0), Qt::DisplayRole, QStringLiteral("three")); QCOMPARE(results.size(), 1); results = m_model.match(m_model.index(0, 0), Qt::DisplayRole, QStringLiteral("fourtytwo")); QCOMPARE(results.size(), 0); results = m_model.match(m_model.index(0, 0), Qt::UserRole + 42, QStringLiteral("mystuff")); QCOMPARE(results.size(), 1); results = m_krfp->match(m_krfp->index(0, 0), Qt::DisplayRole, QStringLiteral("three")); QCOMPARE(results.size(), 1); results = m_krfp->match(m_krfp->index(0, 0), Qt::UserRole + 42, QStringLiteral("mystuff")); QCOMPARE(results.size(), 1); results = m_krfptest->match(m_krfptest->index(0, 0), Qt::DisplayRole, QStringLiteral("three")); QCOMPARE(results.size(), 0); results = m_krfptest->match(m_krfptest->index(0, 0), Qt::UserRole + 42, QStringLiteral("mystuff")); QCOMPARE(results.size(), 1); results = m_model.match(QModelIndex(), Qt::DisplayRole, QStringLiteral("three")); QCOMPARE(results.size(), 0); results = m_krfp->match(QModelIndex(), Qt::DisplayRole, QStringLiteral("three")); QCOMPARE(results.size(), 0); results = m_krfptest->match(QModelIndex(), Qt::DisplayRole, QStringLiteral("three")); QCOMPARE(results.size(), 0); const QModelIndex index = m_model.index(0, 0, QModelIndex()); results = m_model.match(index, Qt::DisplayRole, QStringLiteral("three"), -1, Qt::MatchRecursive | Qt::MatchStartsWith | Qt::MatchWrap); QCOMPARE(results.size(), 2); } #include "proxymodelstest.moc" QTEST_MAIN(ProxyModelsTest) diff --git a/src/core/models/entityorderproxymodel.cpp b/src/core/models/entityorderproxymodel.cpp index 5cdee4ce1..d00f63df6 100644 --- a/src/core/models/entityorderproxymodel.cpp +++ b/src/core/models/entityorderproxymodel.cpp @@ -1,337 +1,337 @@ /* Copyright (C) 2010 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.net, author Stephen Kelly 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. */ #include "entityorderproxymodel.h" #include #include #include #include "collection.h" #include "item.h" #include "entitytreemodel.h" namespace Akonadi { class EntityOrderProxyModelPrivate { public: EntityOrderProxyModelPrivate(EntityOrderProxyModel *qq) : q_ptr(qq) { - } void saveOrder(const QModelIndex &index); KConfigGroup m_orderConfig; Q_DECLARE_PUBLIC(EntityOrderProxyModel) EntityOrderProxyModel *const q_ptr; }; } using namespace Akonadi; EntityOrderProxyModel::EntityOrderProxyModel(QObject *parent) - : KRecursiveFilterProxyModel(parent) + : QSortFilterProxyModel(parent) , d_ptr(new EntityOrderProxyModelPrivate(this)) { + setRecursiveFilteringEnabled(true); setDynamicSortFilter(true); //setSortCaseSensitivity( Qt::CaseInsensitive ); } EntityOrderProxyModel::~EntityOrderProxyModel() { delete d_ptr; } void EntityOrderProxyModel::setOrderConfig(const KConfigGroup &configGroup) { Q_D(EntityOrderProxyModel); Q_EMIT layoutAboutToBeChanged(); d->m_orderConfig = configGroup; Q_EMIT layoutChanged(); } // reimplemented in FavoriteCollectionOrderProxyModel Collection EntityOrderProxyModel::parentCollection(const QModelIndex &index) const { return index.data(EntityTreeModel::ParentCollectionRole).value(); } static QString configKey(const Collection &col) { return !col.isValid() ? QStringLiteral("0") : QString::number(col.id()); } bool EntityOrderProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const { Q_D(const EntityOrderProxyModel); if (!d->m_orderConfig.isValid()) { - return KRecursiveFilterProxyModel::lessThan(left, right); + return QSortFilterProxyModel::lessThan(left, right); } const Collection col = parentCollection(left); const QStringList list = d->m_orderConfig.readEntry(configKey(col), QStringList()); if (list.isEmpty()) { - return KRecursiveFilterProxyModel::lessThan(left, right); + return QSortFilterProxyModel::lessThan(left, right); } const QString leftValue = configString(left); const QString rightValue = configString(right); const int leftPosition = list.indexOf(leftValue); const int rightPosition = list.indexOf(rightValue); if (leftPosition < 0 || rightPosition < 0) { - return KRecursiveFilterProxyModel::lessThan(left, right); + return QSortFilterProxyModel::lessThan(left, right); } return leftPosition < rightPosition; } QStringList EntityOrderProxyModel::configStringsForDroppedUrls(const QList &urls, const Akonadi::Collection &parentCol, bool *containsMove) const { QStringList droppedList; droppedList.reserve(urls.count()); for (const QUrl &url : urls) { Collection col = Collection::fromUrl(url); if (!col.isValid()) { Item item = Item::fromUrl(url); if (!item.isValid()) { continue; } const QModelIndexList list = EntityTreeModel::modelIndexesForItem(this, item); if (list.isEmpty()) { continue; } if (!*containsMove && parentCollection(list.first()).id() != parentCol.id()) { *containsMove = true; } droppedList << configString(list.first()); } else { const QModelIndex idx = EntityTreeModel::modelIndexForCollection(this, col); if (!idx.isValid()) { continue; } if (!*containsMove && parentCollection(idx).id() != parentCol.id()) { *containsMove = true; } droppedList << configString(idx); } } return droppedList; } bool EntityOrderProxyModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) { Q_D(EntityOrderProxyModel); if (!d->m_orderConfig.isValid()) { - return KRecursiveFilterProxyModel::dropMimeData(data, action, row, column, parent); + return QSortFilterProxyModel::dropMimeData(data, action, row, column, parent); } if (!data->hasFormat(QStringLiteral("text/uri-list"))) { - return KRecursiveFilterProxyModel::dropMimeData(data, action, row, column, parent); + return QSortFilterProxyModel::dropMimeData(data, action, row, column, parent); } if (row == -1) { - return KRecursiveFilterProxyModel::dropMimeData(data, action, row, column, parent); + return QSortFilterProxyModel::dropMimeData(data, action, row, column, parent); } const QList urls = data->urls(); if (urls.isEmpty()) { return false; } Collection parentCol; if (parent.isValid()) { parentCol = parent.data(EntityTreeModel::CollectionRole).value(); } else { if (!hasChildren(parent)) { - return KRecursiveFilterProxyModel::dropMimeData(data, action, row, column, parent); + return QSortFilterProxyModel::dropMimeData(data, action, row, column, parent); } const QModelIndex targetIndex = index(0, column, parent); parentCol = parentCollection(targetIndex); } bool containsMove = false; QStringList droppedList = configStringsForDroppedUrls(urls, parentCol, &containsMove); // Dropping new favorite folders if (droppedList.isEmpty()) { - const bool ok = KRecursiveFilterProxyModel::dropMimeData(data, action, row, column, parent); + const bool ok = QSortFilterProxyModel::dropMimeData(data, action, row, column, parent); if (ok) { droppedList = configStringsForDroppedUrls(urls, parentCol, &containsMove); } } QStringList existingList; if (d->m_orderConfig.hasKey(QString::number(parentCol.id()))) { existingList = d->m_orderConfig.readEntry(configKey(parentCol), QStringList()); } else { const int rowCount = this->rowCount(parent); existingList.reserve(rowCount); for (int row = 0; row < rowCount; ++row) { static const int column = 0; const QModelIndex idx = this->index(row, column, parent); existingList.append(configString(idx)); } } const int numberOfDroppedElement(droppedList.size()); for (int i = 0; i < numberOfDroppedElement; ++i) { const QString droppedItem = droppedList.at(i); const int existingIndex = existingList.indexOf(droppedItem); existingList.removeAt(existingIndex); existingList.insert(row + i - (existingIndex > row ? 0 : 1), droppedList.at(i)); } d->m_orderConfig.writeEntry(configKey(parentCol), existingList); if (containsMove) { - bool result = KRecursiveFilterProxyModel::dropMimeData(data, action, row, column, parent); + bool result = QSortFilterProxyModel::dropMimeData(data, action, row, column, parent); invalidate(); return result; } invalidate(); return true; } QModelIndexList EntityOrderProxyModel::match(const QModelIndex &start, int role, const QVariant &value, int hits, Qt::MatchFlags flags) const { if (role < Qt::UserRole) { - return KRecursiveFilterProxyModel::match(start, role, value, hits, flags); + return QSortFilterProxyModel::match(start, role, value, hits, flags); } QModelIndexList list; QModelIndex proxyIndex; foreach (const QModelIndex &idx, sourceModel()->match(mapToSource(start), role, value, hits, flags)) { proxyIndex = mapFromSource(idx); if (proxyIndex.isValid()) { list << proxyIndex; } } return list; } void EntityOrderProxyModelPrivate::saveOrder(const QModelIndex &parent) { Q_Q(const EntityOrderProxyModel); int rowCount = q->rowCount(parent); if (rowCount == 0) { return; } static const int column = 0; QModelIndex childIndex = q->index(0, column, parent); const QString parentKey = q->parentConfigString(childIndex); if (parentKey.isEmpty()) { return; } QStringList list; list << q->configString(childIndex); saveOrder(childIndex); list.reserve(list.count() + rowCount); for (int row = 1; row < rowCount; ++row) { childIndex = q->index(row, column, parent); list << q->configString(childIndex); saveOrder(childIndex); } m_orderConfig.writeEntry(parentKey, list); } QString EntityOrderProxyModel::parentConfigString(const QModelIndex &index) const { const Collection col = parentCollection(index); Q_ASSERT(col.isValid()); if (!col.isValid()) { return QString(); } return QString::number(col.id()); } QString EntityOrderProxyModel::configString(const QModelIndex &index) const { Item::Id iId = index.data(EntityTreeModel::ItemIdRole).toLongLong(); if (iId != -1) { return QLatin1Char('i') + QString::number(iId); } Collection::Id cId = index.data(EntityTreeModel::CollectionIdRole).toLongLong(); if (cId != -1) { return QLatin1Char('c') + QString::number(cId); } Q_ASSERT(!"Invalid entity"); return QString(); } void EntityOrderProxyModel::saveOrder() { Q_D(EntityOrderProxyModel); d->saveOrder(QModelIndex()); d->m_orderConfig.sync(); } void EntityOrderProxyModel::clearOrder(const QModelIndex &parent) { Q_D(EntityOrderProxyModel); const QString parentKey = parentConfigString(index(0, 0, parent)); if (parentKey.isEmpty()) { return; } d->m_orderConfig.deleteEntry(parentKey); invalidate(); } void EntityOrderProxyModel::clearTreeOrder() { Q_D(EntityOrderProxyModel); d->m_orderConfig.deleteGroup(); invalidate(); } diff --git a/src/core/models/entityorderproxymodel.h b/src/core/models/entityorderproxymodel.h index 244276f4b..661febb09 100644 --- a/src/core/models/entityorderproxymodel.h +++ b/src/core/models/entityorderproxymodel.h @@ -1,108 +1,108 @@ /* Copyright (C) 2010 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.net, author Stephen Kelly 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 AKONADI_ENTITYORDERPROXYMODEL_H #define AKONADI_ENTITYORDERPROXYMODEL_H -#include +#include "akonadicore_export.h" #include "collection.h" -#include "akonadicore_export.h" +#include class KConfigGroup; namespace Akonadi { class EntityOrderProxyModelPrivate; /** * @short A model that keeps the order of entities persistent. * * This proxy maintains the order of entities in a tree. The user can re-order * items and the new order will be persisted restored on reset or restart. * * @author Stephen Kelly * @since 4.6 */ -class AKONADICORE_EXPORT EntityOrderProxyModel : public KRecursiveFilterProxyModel +class AKONADICORE_EXPORT EntityOrderProxyModel : public QSortFilterProxyModel { Q_OBJECT public: /** * Creates a new entity order proxy model. * * @param parent The parent object. */ explicit EntityOrderProxyModel(QObject *parent = nullptr); /** * Destroys the entity order proxy model. */ ~EntityOrderProxyModel() override; /** * Sets the config @p group that will be used for storing the order. */ void setOrderConfig(const KConfigGroup &group); /** * Saves the order. */ void saveOrder(); void clearOrder(const QModelIndex &index); void clearTreeOrder(); /** * @reimp */ Q_REQUIRED_RESULT bool lessThan(const QModelIndex &left, const QModelIndex &right) const override; /** * @reimp */ Q_REQUIRED_RESULT bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) override; /** * @reimp */ Q_REQUIRED_RESULT QModelIndexList match(const QModelIndex &start, int role, const QVariant &value, int hits = 1, Qt::MatchFlags flags = Qt::MatchFlags(Qt::MatchStartsWith | Qt::MatchWrap)) const override; protected: EntityOrderProxyModelPrivate *const d_ptr; virtual QString parentConfigString(const QModelIndex &index) const; virtual QString configString(const QModelIndex &index) const; virtual Akonadi::Collection parentCollection(const QModelIndex &index) const; private: QStringList configStringsForDroppedUrls(const QList &urls, const Akonadi::Collection &parentCol, bool *containsMove) const; //@cond PRIVATE Q_DECLARE_PRIVATE(EntityOrderProxyModel) //@endcond }; } #endif diff --git a/src/core/models/entityrightsfiltermodel.cpp b/src/core/models/entityrightsfiltermodel.cpp index 1172a72d0..e17dd9b88 100644 --- a/src/core/models/entityrightsfiltermodel.cpp +++ b/src/core/models/entityrightsfiltermodel.cpp @@ -1,132 +1,132 @@ /* Copyright (c) 2007 Bruno Virlet Copyright (c) 2009 Stephen Kelly 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. */ #include "entityrightsfiltermodel.h" - #include "entitytreemodel.h" - using namespace Akonadi; namespace Akonadi { /** * @internal */ class EntityRightsFilterModelPrivate { public: EntityRightsFilterModelPrivate(EntityRightsFilterModel *parent) : q_ptr(parent) , mAccessRights(Collection::AllRights) { } bool rightsMatches(const QModelIndex &index) const { if (mAccessRights == Collection::AllRights || mAccessRights == Collection::ReadOnly) { return true; } const Collection collection = index.data(EntityTreeModel::CollectionRole).value(); if (collection.isValid()) { return (mAccessRights & collection.rights()); } else { const Item item = index.data(EntityTreeModel::ItemRole).value(); if (item.isValid()) { const Collection collection = index.data(EntityTreeModel::ParentCollectionRole).value(); return (mAccessRights & collection.rights()); } else { return false; } } } Q_DECLARE_PUBLIC(EntityRightsFilterModel) EntityRightsFilterModel *q_ptr; Collection::Rights mAccessRights; }; } EntityRightsFilterModel::EntityRightsFilterModel(QObject *parent) - : KRecursiveFilterProxyModel(parent) + : QSortFilterProxyModel(parent) , d_ptr(new EntityRightsFilterModelPrivate(this)) { + setRecursiveFilteringEnabled(true); } EntityRightsFilterModel::~EntityRightsFilterModel() { delete d_ptr; } + +bool EntityRightsFilterModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const +{ + Q_D(const EntityRightsFilterModel); + + const QModelIndex modelIndex = sourceModel()->index(sourceRow, 0, sourceParent); + + return d->rightsMatches(modelIndex); +} + void EntityRightsFilterModel::setAccessRights(Collection::Rights rights) { Q_D(EntityRightsFilterModel); d->mAccessRights = rights; invalidateFilter(); } Collection::Rights EntityRightsFilterModel::accessRights() const { Q_D(const EntityRightsFilterModel); return d->mAccessRights; } -bool EntityRightsFilterModel::acceptRow(int sourceRow, const QModelIndex &sourceParent) const -{ - Q_D(const EntityRightsFilterModel); - - const QModelIndex modelIndex = sourceModel()->index(sourceRow, 0, sourceParent); - - return d->rightsMatches(modelIndex); -} - Qt::ItemFlags EntityRightsFilterModel::flags(const QModelIndex &index) const { Q_D(const EntityRightsFilterModel); if (d->rightsMatches(index)) { - return KRecursiveFilterProxyModel::flags(index); + return QSortFilterProxyModel::flags(index); } else { - return KRecursiveFilterProxyModel::flags(index) & ~(Qt::ItemIsSelectable | Qt::ItemIsEnabled); + return QSortFilterProxyModel::flags(index) & ~(Qt::ItemIsSelectable | Qt::ItemIsEnabled); } } QModelIndexList EntityRightsFilterModel::match(const QModelIndex &start, int role, const QVariant &value, int hits, Qt::MatchFlags flags) const { if (role < Qt::UserRole) { return QSortFilterProxyModel::match(start, role, value, hits, flags); } QModelIndexList list; QModelIndex proxyIndex; foreach (const QModelIndex &idx, sourceModel()->match(mapToSource(start), role, value, hits, flags)) { proxyIndex = mapFromSource(idx); if (proxyIndex.isValid()) { list << proxyIndex; } } return list; } diff --git a/src/core/models/entityrightsfiltermodel.h b/src/core/models/entityrightsfiltermodel.h index aaa90223e..63a943032 100644 --- a/src/core/models/entityrightsfiltermodel.h +++ b/src/core/models/entityrightsfiltermodel.h @@ -1,114 +1,113 @@ /* Copyright (c) 2009 Tobias Koenig 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 AKONADI_ENTITYRIGHTSFILTERMODEL_H #define AKONADI_ENTITYRIGHTSFILTERMODEL_H +#include "akonadicore_export.h" #include "entitytreemodel.h" -#include - -#include "akonadicore_export.h" +#include namespace Akonadi { class EntityRightsFilterModelPrivate; /** * @short A proxy model that filters entities by access rights. * * This class can be used on top of an EntityTreeModel to exclude entities by access type * or to include only certain entities with special access rights. * * @code * * using namespace Akonadi; * * EntityTreeModel *model = new EntityTreeModel( this ); * * EntityRightsFilterModel *filter = new EntityRightsFilterModel(); * filter->setAccessRights( Collection::CanCreateItem | Collection::CanCreateCollection ); * filter->setSourceModel( model ); * * EntityTreeView *view = new EntityTreeView( this ); * view->setModel( filter ); * * @endcode * * @li For collections the access rights are checked against the collections own rights. * @li For items the access rights are checked against the item's parent collection rights. * * @author Tobias Koenig * @since 4.6 */ -class AKONADICORE_EXPORT EntityRightsFilterModel : public KRecursiveFilterProxyModel +class AKONADICORE_EXPORT EntityRightsFilterModel : public QSortFilterProxyModel { Q_OBJECT public: /** * Creates a new entity rights filter model. * * @param parent The parent object. */ explicit EntityRightsFilterModel(QObject *parent = nullptr); /** * Destroys the entity rights filter model. */ ~EntityRightsFilterModel() override; /** * Sets the access @p rights the entities shall be filtered * against. If no rights are set explicitly, Collection::AllRights * is assumed. * @param rights the access rights filter values */ void setAccessRights(Collection::Rights rights); /** * Returns the access rights that are used for filtering. */ Q_REQUIRED_RESULT Collection::Rights accessRights() const; /** * @reimp */ Q_REQUIRED_RESULT Qt::ItemFlags flags(const QModelIndex &index) const override; /** * @reimp */ Q_REQUIRED_RESULT QModelIndexList match(const QModelIndex &start, int role, const QVariant &value, int hits = 1, Qt::MatchFlags flags = Qt::MatchFlags(Qt::MatchStartsWith | Qt::MatchWrap)) const override; protected: - bool acceptRow(int sourceRow, const QModelIndex &sourceParent) const override; + bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override; private: //@cond PRIVATE Q_DECLARE_PRIVATE(EntityRightsFilterModel) EntityRightsFilterModelPrivate *const d_ptr; //@endcond }; } #endif diff --git a/src/core/models/favoritecollectionsmodel.cpp b/src/core/models/favoritecollectionsmodel.cpp index 67e991f1b..5d8d5e2a3 100644 --- a/src/core/models/favoritecollectionsmodel.cpp +++ b/src/core/models/favoritecollectionsmodel.cpp @@ -1,491 +1,491 @@ /* Copyright (c) 2009 Kevin Ottens 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. */ #include "favoritecollectionsmodel.h" #include "akonadicore_debug.h" #include #include #include #include #include #include #include #include "entitytreemodel.h" #include "mimetypechecker.h" #include "pastehelper_p.h" #include "favoritecollectionattribute.h" #include "collectionmodifyjob.h" using namespace Akonadi; /** * @internal */ class Q_DECL_HIDDEN FavoriteCollectionsModel::Private { public: Private(const KConfigGroup &group, FavoriteCollectionsModel *parent) : q(parent) , configGroup(group) { } QString labelForCollection(Collection::Id collectionId) const { if (labelMap.contains(collectionId)) { return labelMap[collectionId]; } const QModelIndex collectionIdx = EntityTreeModel::modelIndexForCollection(q->sourceModel(), Collection(collectionId)); QString accountName; const QString nameOfCollection = collectionIdx.data().toString(); QModelIndex idx = collectionIdx.parent(); while (idx != QModelIndex()) { accountName = idx.data(EntityTreeModel::OriginalCollectionNameRole).toString(); idx = idx.parent(); } if (accountName.isEmpty()) { return nameOfCollection; } else { return nameOfCollection + QStringLiteral(" (") + accountName + QLatin1Char(')'); } } void insertIfAvailable(Collection::Id col) { if (collectionIds.contains(col)) { select(col); if (!referencedCollections.contains(col)) { reference(col); } auto idx = EntityTreeModel::modelIndexForCollection(q, Collection{ col }); if (idx.isValid()) { auto c = q->data(idx, EntityTreeModel::CollectionRole).value(); if (c.isValid() && !c.hasAttribute()) { c.addAttribute(new FavoriteCollectionAttribute()); new CollectionModifyJob(c, q); } } } } void insertIfAvailable(const QModelIndex &idx) { insertIfAvailable(idx.data(EntityTreeModel::CollectionIdRole).value()); } /** * Stuff changed (e.g. new rows inserted into sorted model), reload everything. */ void reload() { //don't clear the selection model here. Otherwise we mess up the users selection as collections get removed and re-inserted. for (const Collection::Id &collectionId : qAsConst(collectionIds)) { insertIfAvailable(collectionId); } // If a favorite folder was removed then surely it's gone from the selection model, so no need to do anything about that. } void rowsInserted(const QModelIndex &parent, int begin, int end) { for (int row = begin; row <= end; row++) { const QModelIndex child = q->sourceModel()->index(row, 0, parent); if (!child.isValid()) { continue; } insertIfAvailable(child); const int childRows = q->sourceModel()->rowCount(child); if (childRows > 0) { rowsInserted(child, 0, childRows - 1); } } } void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight) { for (int row = topLeft.row(); row <= bottomRight.row(); row++) { const QModelIndex idx = topLeft.sibling(row, 0); insertIfAvailable(idx); } } /** * Selects the index in the internal selection model to make the collection visible in the model */ void select(const Collection::Id &collectionId) { const QModelIndex index = EntityTreeModel::modelIndexForCollection(q->sourceModel(), Collection(collectionId)); if (index.isValid()) { q->selectionModel()->select(index, QItemSelectionModel::Select); } } void deselect(const Collection::Id &collectionId) { const QModelIndex idx = EntityTreeModel::modelIndexForCollection(q->sourceModel(), Collection(collectionId)); if (idx.isValid()) { q->selectionModel()->select(idx, QItemSelectionModel::Deselect); } } void reference(const Collection::Id &collectionId) { if (referencedCollections.contains(collectionId)) { qCWarning(AKONADICORE_LOG) << "already referenced " << collectionId; return; } const QModelIndex index = EntityTreeModel::modelIndexForCollection(q->sourceModel(), Collection(collectionId)); if (index.isValid()) { if (q->sourceModel()->setData(index, QVariant(), EntityTreeModel::CollectionRefRole)) { referencedCollections << collectionId; } else { qCWarning(AKONADICORE_LOG) << "failed to reference collection"; } q->sourceModel()->fetchMore(index); } } void dereference(const Collection::Id &collectionId) { if (!referencedCollections.contains(collectionId)) { qCWarning(AKONADICORE_LOG) << "not referenced " << collectionId; return; } const QModelIndex index = EntityTreeModel::modelIndexForCollection(q->sourceModel(), Collection(collectionId)); if (index.isValid()) { q->sourceModel()->setData(index, QVariant(), EntityTreeModel::CollectionDerefRole); referencedCollections.remove(collectionId); } } void clearReferences() { for (const Collection::Id &collectionId : qAsConst(referencedCollections)) { dereference(collectionId); } } /** * Adds a collection to the favorite collections */ void add(const Collection::Id &collectionId) { if (collectionIds.contains(collectionId)) { qCDebug(AKONADICORE_LOG) << "already in model " << collectionId; return; } collectionIds << collectionId; reference(collectionId); select(collectionId); const auto idx = EntityTreeModel::modelIndexForCollection(q, Collection{ collectionId }); if (idx.isValid()) { auto col = q->data(idx, EntityTreeModel::CollectionRole).value(); if (col.isValid() && !col.hasAttribute()) { col.addAttribute(new FavoriteCollectionAttribute()); new CollectionModifyJob(col, q); } } } void remove(const Collection::Id &collectionId) { collectionIds.removeAll(collectionId); labelMap.remove(collectionId); dereference(collectionId); deselect(collectionId); const auto idx = EntityTreeModel::modelIndexForCollection(q, Collection{ collectionId }); if (idx.isValid()) { auto col = q->data(idx, EntityTreeModel::CollectionRole).value(); if (col.isValid() && col.hasAttribute()) { col.removeAttribute(); new CollectionModifyJob(col, q); } } } void set(const QList &collections) { QList colIds = collectionIds; for (const Collection::Id &col : collections) { const int removed = colIds.removeAll(col); const bool isNewCollection = removed <= 0; if (isNewCollection) { add(col); } } //Remove what's left for (Akonadi::Collection::Id colId : qAsConst(colIds)) { remove(colId); } } void set(const Akonadi::Collection::List &collections) { QList colIds; colIds.reserve(collections.count()); for (const Akonadi::Collection &col : collections) { colIds << col.id(); } set(colIds); } void loadConfig() { const QList collections = configGroup.readEntry("FavoriteCollectionIds", QList()); const QStringList labels = configGroup.readEntry("FavoriteCollectionLabels", QStringList()); const int numberOfLabels(labels.size()); for (int i = 0; i < collections.size(); ++i) { if (i < numberOfLabels) { labelMap[collections[i]] = labels[i]; } add(collections[i]); } } void saveConfig() { QStringList labels; labels.reserve(collectionIds.count()); for (const Collection::Id &collectionId : qAsConst(collectionIds)) { labels << labelForCollection(collectionId); } configGroup.writeEntry("FavoriteCollectionIds", collectionIds); configGroup.writeEntry("FavoriteCollectionLabels", labels); configGroup.config()->sync(); } FavoriteCollectionsModel *const q; QList collectionIds; QSet referencedCollections; QHash labelMap; KConfigGroup configGroup; }; /* Implementation note: * * We use KSelectionProxyModel in order to make a flat list of selected folders from the folder tree. * - * Attempts to use QSortFilterProxyModel / KRecursiveFilterProxyModel make code somewhat simpler, + * Attempts to use QSortFilterProxyModel make code somewhat simpler, * but don't work since we then get a filtered tree, not a flat list. Stacking a KDescendantsProxyModel * on top would likely remove explicitly selected parents when one of their child is selected too. */ FavoriteCollectionsModel::FavoriteCollectionsModel(QAbstractItemModel *source, const KConfigGroup &group, QObject *parent) : KSelectionProxyModel(new QItemSelectionModel(source, parent), parent) , d(new Private(group, this)) { setSourceModel(source); setFilterBehavior(ExactSelection); d->loadConfig(); //React to various changes in the source model connect(source, &QAbstractItemModel::modelReset, this, [this]() { d->reload(); }); connect(source, &QAbstractItemModel::layoutChanged, this, [this]() { d->reload(); }); connect(source, &QAbstractItemModel::rowsInserted, this, [this](const QModelIndex &parent, int begin, int end) { d->rowsInserted(parent, begin, end); }); connect(source, &QAbstractItemModel::dataChanged, this, [this](const QModelIndex &tl, const QModelIndex &br) { d->dataChanged(tl, br); }); } FavoriteCollectionsModel::~FavoriteCollectionsModel() { delete d; } void FavoriteCollectionsModel::setCollections(const Collection::List &collections) { d->set(collections); d->saveConfig(); } void FavoriteCollectionsModel::addCollection(const Collection &collection) { d->add(collection.id()); d->saveConfig(); } void FavoriteCollectionsModel::removeCollection(const Collection &collection) { d->remove(collection.id()); d->saveConfig(); } Akonadi::Collection::List FavoriteCollectionsModel::collections() const { Collection::List cols; cols.reserve(d->collectionIds.count()); for (const Collection::Id &colId : qAsConst(d->collectionIds)) { const QModelIndex idx = EntityTreeModel::modelIndexForCollection(sourceModel(), Collection(colId)); const Collection collection = sourceModel()->data(idx, EntityTreeModel::CollectionRole).value(); cols << collection; } return cols; } QList FavoriteCollectionsModel::collectionIds() const { return d->collectionIds; } void Akonadi::FavoriteCollectionsModel::setFavoriteLabel(const Collection &collection, const QString &label) { Q_ASSERT(d->collectionIds.contains(collection.id())); d->labelMap[collection.id()] = label; d->saveConfig(); const QModelIndex idx = EntityTreeModel::modelIndexForCollection(sourceModel(), collection); if (!idx.isValid()) { return; } const QModelIndex index = mapFromSource(idx); Q_EMIT dataChanged(index, index); } QVariant Akonadi::FavoriteCollectionsModel::data(const QModelIndex &index, int role) const { if (index.column() == 0 && (role == Qt::DisplayRole || role == Qt::EditRole)) { const QModelIndex sourceIndex = mapToSource(index); const Collection::Id collectionId = sourceModel()->data(sourceIndex, EntityTreeModel::CollectionIdRole).toLongLong(); return d->labelForCollection(collectionId); } else { return KSelectionProxyModel::data(index, role); } } bool FavoriteCollectionsModel::setData(const QModelIndex &index, const QVariant &value, int role) { if (index.isValid() && index.column() == 0 && role == Qt::EditRole) { const QString newLabel = value.toString(); if (newLabel.isEmpty()) { return false; } const QModelIndex sourceIndex = mapToSource(index); const Collection collection = sourceModel()->data(sourceIndex, EntityTreeModel::CollectionRole).value(); setFavoriteLabel(collection, newLabel); return true; } return KSelectionProxyModel::setData(index, value, role); } QString Akonadi::FavoriteCollectionsModel::favoriteLabel(const Akonadi::Collection &collection) { if (!collection.isValid()) { return QString(); } return d->labelForCollection(collection.id()); } QVariant FavoriteCollectionsModel::headerData(int section, Qt::Orientation orientation, int role) const { if (section == 0 && orientation == Qt::Horizontal && role == Qt::DisplayRole) { return i18n("Favorite Folders"); } else { return KSelectionProxyModel::headerData(section, orientation, role); } } bool FavoriteCollectionsModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) { Q_UNUSED(action); Q_UNUSED(row); Q_UNUSED(column); if (data->hasFormat(QStringLiteral("text/uri-list"))) { const QList urls = data->urls(); const QModelIndex sourceIndex = mapToSource(parent); const Collection destCollection = sourceModel()->data(sourceIndex, EntityTreeModel::CollectionRole).value(); MimeTypeChecker mimeChecker; mimeChecker.setWantedMimeTypes(destCollection.contentMimeTypes()); for (const QUrl &url : urls) { const Collection col = Collection::fromUrl(url); if (col.isValid()) { addCollection(col); } else { const Item item = Item::fromUrl(url); if (item.isValid()) { if (item.parentCollection().id() == destCollection.id() && action != Qt::CopyAction) { qCDebug(AKONADICORE_LOG) << "Error: source and destination of move are the same."; return false; } #if 0 if (!mimeChecker.isWantedItem(item)) { qCDebug(AKONADICORE_LOG) << "unwanted item" << mimeChecker.wantedMimeTypes() << item.mimeType(); return false; } #endif KJob *job = PasteHelper::pasteUriList(data, destCollection, action); if (!job) { return false; } connect(job, &KJob::result, this, &FavoriteCollectionsModel::pasteJobDone); // Accept the event so that it doesn't propagate. return true; } } } return true; } return false; } QStringList FavoriteCollectionsModel::mimeTypes() const { QStringList mts = KSelectionProxyModel::mimeTypes(); if (!mts.contains(QLatin1String("text/uri-list"))) { mts.append(QStringLiteral("text/uri-list")); } return mts; } Qt::ItemFlags FavoriteCollectionsModel::flags(const QModelIndex &index) const { Qt::ItemFlags fs = KSelectionProxyModel::flags(index); if (!index.isValid()) { fs |= Qt::ItemIsDropEnabled; } return fs; } void FavoriteCollectionsModel::pasteJobDone(KJob *job) { if (job->error()) { qCDebug(AKONADICORE_LOG) << "Paste job error:" << job->errorString(); } } #include "moc_favoritecollectionsmodel.cpp" diff --git a/src/core/models/recursivecollectionfilterproxymodel.cpp b/src/core/models/recursivecollectionfilterproxymodel.cpp index 842d1a1e6..9fd4e2ce5 100644 --- a/src/core/models/recursivecollectionfilterproxymodel.cpp +++ b/src/core/models/recursivecollectionfilterproxymodel.cpp @@ -1,148 +1,149 @@ /* Copyright (c) 2009 Stephen Kelly Copyright (C) 2012-2019 Laurent Montel 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. */ #include "recursivecollectionfilterproxymodel.h" #include "entitytreemodel.h" #include "mimetypechecker.h" using namespace Akonadi; namespace Akonadi { class RecursiveCollectionFilterProxyModelPrivate { Q_DECLARE_PUBLIC(RecursiveCollectionFilterProxyModel) RecursiveCollectionFilterProxyModel *q_ptr; public: RecursiveCollectionFilterProxyModelPrivate(RecursiveCollectionFilterProxyModel *model) : q_ptr(model) { } QSet includedMimeTypes; Akonadi::MimeTypeChecker checker; QString pattern; bool checkOnlyChecked = false; }; } RecursiveCollectionFilterProxyModel::RecursiveCollectionFilterProxyModel(QObject *parent) - : KRecursiveFilterProxyModel(parent) + : QSortFilterProxyModel(parent) , d_ptr(new RecursiveCollectionFilterProxyModelPrivate(this)) { - + setRecursiveFilteringEnabled(true); } RecursiveCollectionFilterProxyModel::~RecursiveCollectionFilterProxyModel() { delete d_ptr; } -bool RecursiveCollectionFilterProxyModel::acceptRow(int sourceRow, const QModelIndex &sourceParent) const +bool RecursiveCollectionFilterProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const { Q_D(const RecursiveCollectionFilterProxyModel); const QModelIndex rowIndex = sourceModel()->index(sourceRow, 0, sourceParent); const Akonadi::Collection collection = rowIndex.data(Akonadi::EntityTreeModel::CollectionRole).value(); if (!collection.isValid()) { return false; } const bool checked = (rowIndex.data(Qt::CheckStateRole).toInt() == Qt::Checked); const bool isCheckable = sourceModel()->flags(rowIndex) & Qt::ItemIsUserCheckable; if (isCheckable && (d->checkOnlyChecked && !checked)) { return false; } const bool collectionWanted = d->checker.isWantedCollection(collection); if (collectionWanted) { if (!d->pattern.isEmpty()) { const QString text = rowIndex.data(Qt::DisplayRole).toString(); return text.contains(d->pattern, Qt::CaseInsensitive); } } return collectionWanted; } + void RecursiveCollectionFilterProxyModel::addContentMimeTypeInclusionFilter(const QString &mimeType) { Q_D(RecursiveCollectionFilterProxyModel); d->includedMimeTypes << mimeType; d->checker.setWantedMimeTypes(d->includedMimeTypes.values()); invalidateFilter(); } void RecursiveCollectionFilterProxyModel::addContentMimeTypeInclusionFilters(const QStringList &mimeTypes) { Q_D(RecursiveCollectionFilterProxyModel); d->includedMimeTypes.unite(mimeTypes.toSet()); d->checker.setWantedMimeTypes(d->includedMimeTypes.values()); invalidateFilter(); } void RecursiveCollectionFilterProxyModel::clearFilters() { Q_D(RecursiveCollectionFilterProxyModel); d->includedMimeTypes.clear(); d->checker.setWantedMimeTypes(QStringList()); invalidateFilter(); } void RecursiveCollectionFilterProxyModel::setContentMimeTypeInclusionFilters(const QStringList &mimeTypes) { Q_D(RecursiveCollectionFilterProxyModel); d->includedMimeTypes = mimeTypes.toSet(); d->checker.setWantedMimeTypes(d->includedMimeTypes.values()); invalidateFilter(); } QStringList RecursiveCollectionFilterProxyModel::contentMimeTypeInclusionFilters() const { Q_D(const RecursiveCollectionFilterProxyModel); return d->includedMimeTypes.values(); } int Akonadi::RecursiveCollectionFilterProxyModel::columnCount(const QModelIndex &index) const { // Optimization: we know that we're not changing the number of columns, so skip QSortFilterProxyModel return sourceModel()->columnCount(mapToSource(index)); } void Akonadi::RecursiveCollectionFilterProxyModel::setSearchPattern(const QString &pattern) { Q_D(RecursiveCollectionFilterProxyModel); if (d->pattern != pattern) { d->pattern = pattern; invalidate(); } } void Akonadi::RecursiveCollectionFilterProxyModel::setIncludeCheckedOnly(bool checked) { Q_D(RecursiveCollectionFilterProxyModel); if (d->checkOnlyChecked != checked) { d->checkOnlyChecked = checked; invalidate(); } } diff --git a/src/core/models/recursivecollectionfilterproxymodel.h b/src/core/models/recursivecollectionfilterproxymodel.h index 7f85855ad..154d55aa5 100644 --- a/src/core/models/recursivecollectionfilterproxymodel.h +++ b/src/core/models/recursivecollectionfilterproxymodel.h @@ -1,112 +1,111 @@ /* Copyright (c) 2009 Stephen Kelly Copyright (C) 2012-2019 Laurent Montel 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 AKONADI_RECURSIVECOLLECTIONFILTERPROXYMODEL_H #define AKONADI_RECURSIVECOLLECTIONFILTERPROXYMODEL_H -#include - #include "akonadicore_export.h" +#include + namespace Akonadi { class RecursiveCollectionFilterProxyModelPrivate; /** * @short A model to filter out collections of non-matching content types. * * @author Stephen Kelly * @since 4.6 */ -class AKONADICORE_EXPORT RecursiveCollectionFilterProxyModel : public KRecursiveFilterProxyModel +class AKONADICORE_EXPORT RecursiveCollectionFilterProxyModel : public QSortFilterProxyModel { Q_OBJECT public: /** * Creates a new recursive collection filter proxy model. * * @param parent The parent object. */ explicit RecursiveCollectionFilterProxyModel(QObject *parent = nullptr); /** * Destroys the recursive collection filter proxy model. */ ~RecursiveCollectionFilterProxyModel() override; /** * Add content mime type to be shown by the filter. * * @param mimeType A mime type to be shown. */ void addContentMimeTypeInclusionFilter(const QString &mimeType); /** * Add content mime types to be shown by the filter. * * @param mimeTypes A list of content mime types to be included. */ void addContentMimeTypeInclusionFilters(const QStringList &mimeTypes); /** * Clears the current filters. */ void clearFilters(); /** * Replace the content mime types to be shown by the filter. * * @param mimeTypes A list of content mime types to be included. */ void setContentMimeTypeInclusionFilters(const QStringList &mimeTypes); /** * Returns the currently included mimetypes in the filter. */ Q_REQUIRED_RESULT QStringList contentMimeTypeInclusionFilters() const; /** * Add search pattern * @param pattern the search pattern to add * @since 4.8.1 */ void setSearchPattern(const QString &pattern); /** * Show only checked item * @param checked only shows checked item if set as @c true * @since 4.9 */ void setIncludeCheckedOnly(bool checked); protected: - bool acceptRow(int sourceRow, const QModelIndex &sourceParent) const override; int columnCount(const QModelIndex &index) const override; - + bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override; protected: RecursiveCollectionFilterProxyModelPrivate *const d_ptr; Q_DECLARE_PRIVATE(RecursiveCollectionFilterProxyModel) }; } #endif diff --git a/src/core/models/trashfilterproxymodel.cpp b/src/core/models/trashfilterproxymodel.cpp index 6ff295815..b0dd6720c 100644 --- a/src/core/models/trashfilterproxymodel.cpp +++ b/src/core/models/trashfilterproxymodel.cpp @@ -1,77 +1,77 @@ /* Copyright (c) 2011 Christian Mollekopf 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. */ #include "trashfilterproxymodel.h" #include "entitydeletedattribute.h" #include "item.h" #include "entitytreemodel.h" using namespace Akonadi; class TrashFilterProxyModel::TrashFilterProxyModelPrivate { public: TrashFilterProxyModelPrivate() { } bool mTrashIsShown = false; }; TrashFilterProxyModel::TrashFilterProxyModel(QObject *parent) - : KRecursiveFilterProxyModel(parent) + : QSortFilterProxyModel(parent) , d_ptr(new TrashFilterProxyModelPrivate()) { - + setRecursiveFilteringEnabled(true); } TrashFilterProxyModel::~TrashFilterProxyModel() { delete d_ptr; } void TrashFilterProxyModel::showTrash(bool enable) { Q_D(TrashFilterProxyModel); d->mTrashIsShown = enable; invalidateFilter(); } bool TrashFilterProxyModel::trashIsShown() const { Q_D(const TrashFilterProxyModel); return d->mTrashIsShown; } -bool TrashFilterProxyModel::acceptRow(int sourceRow, const QModelIndex &sourceParent) const +bool TrashFilterProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const { Q_D(const TrashFilterProxyModel); const QModelIndex &index = sourceModel()->index(sourceRow, 0, sourceParent); const Item &item = index.data(EntityTreeModel::ItemRole).value(); if (item.isValid()) { if (item.hasAttribute()) { return d->mTrashIsShown; } } const Collection &collection = index.data(EntityTreeModel::CollectionRole).value(); if (collection.isValid()) { if (collection.hasAttribute()) { return d->mTrashIsShown; } } return !d->mTrashIsShown; } diff --git a/src/core/models/trashfilterproxymodel.h b/src/core/models/trashfilterproxymodel.h index ddbbc6338..a2003fa76 100644 --- a/src/core/models/trashfilterproxymodel.h +++ b/src/core/models/trashfilterproxymodel.h @@ -1,83 +1,83 @@ /* Copyright (c) 2011 Christian Mollekopf 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 AKONADI_TRASHFILTERPROXYMODEL_H #define AKONADI_TRASHFILTERPROXYMODEL_H #include "akonadicore_export.h" -#include +#include namespace Akonadi { /** * @short Filter model which hides/shows entities marked as trash * * Filter model which either hides all entities marked as trash, or the ones not marked. * Subentities of collections marked as trash are also shown in the trash. * * The Base model must be an EntityTreeModel and the EntityDeletedAttribute must be available. * * Example: * * @code * * ChangeRecorder *monitor = new Akonadi::ChangeRecorder( this ); * monitor->itemFetchScope().fetchAttribute(true); * * Akonadi::EntityTreeModel *sourcemodel = new Akonadi::EntityTreeModel(monitor, this); * * TrashFilterProxyModel *model = new TrashFilterProxyModel(this); * model->setDynamicSortFilter(true); * model->setSourceModel(sourcemodel); * * @endcode * * @author Christian Mollekopf * @since 4.8 */ -class AKONADICORE_EXPORT TrashFilterProxyModel : public KRecursiveFilterProxyModel +class AKONADICORE_EXPORT TrashFilterProxyModel : public QSortFilterProxyModel { Q_OBJECT public: explicit TrashFilterProxyModel(QObject *parent = nullptr); ~TrashFilterProxyModel() override; void showTrash(bool enable); Q_REQUIRED_RESULT bool trashIsShown() const; protected: /** * Sort filter criterias, according to how expensive the operation is */ - bool acceptRow(int sourceRow, const QModelIndex &sourceParent) const override; + bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override; private: //@cond PRIVATE class TrashFilterProxyModelPrivate; TrashFilterProxyModelPrivate *const d_ptr; Q_DECLARE_PRIVATE(TrashFilterProxyModel) //@endcond }; } #endif