diff --git a/autotests/CMakeLists.txt b/autotests/CMakeLists.txt --- a/autotests/CMakeLists.txt +++ b/autotests/CMakeLists.txt @@ -5,3 +5,6 @@ add_akonadi_isolated_test(foldertreewidgettest.cpp) target_link_libraries(foldertreewidgettest KF5::Mime KF5::AkonadiWidgets KF5::MailCommon) +add_akonadi_isolated_test(favoritestest.cpp) +target_link_libraries(favoritestest KF5::Mime KF5::AkonadiWidgets KF5::MailCommon) + diff --git a/autotests/favoritestest.cpp b/autotests/favoritestest.cpp new file mode 100644 --- /dev/null +++ b/autotests/favoritestest.cpp @@ -0,0 +1,131 @@ +/* + Copyright (c) 2013 Christian Mollekopf + Copyright (c) 2017 David Faure + + 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 +#include +#include +#include +#include +#include + +using namespace Akonadi; +using namespace MailCommon; + +class FavoriteProxyTest : public QObject +{ + Q_OBJECT + +private Q_SLOTS: + void initTestCase(); + void testReordering(); +private: + EntityTreeModel *createETM(); +}; + +void FavoriteProxyTest::initTestCase() +{ + AkonadiTest::checkTestIsIsolated(); + Akonadi::Control::start(); + AkonadiTest::setAllResourcesOffline(); +} + +static QModelIndex getIndex(const QString &string, EntityTreeModel *model) +{ + QModelIndexList list = model->match(model->index(0, 0), Qt::DisplayRole, string, 1, Qt::MatchRecursive); + if (list.isEmpty()) { + return QModelIndex(); + } + return list.first(); +} + +/** + * Since we have no sensible way to figure out if the model is fully populated, + * we use the brute force approach. + */ +static bool waitForPopulation(const QModelIndex &idx, EntityTreeModel *model, int count) +{ + for (int i = 0; i < 500; i++) { + if (model->rowCount(idx) >= count) { + return true; + } + QTest::qWait(10); + } + return false; +} + +EntityTreeModel *FavoriteProxyTest::createETM() +{ + ChangeRecorder *changeRecorder = new ChangeRecorder(this); + changeRecorder->setCollectionMonitored(Collection::root()); + EntityTreeModel *model = new EntityTreeModel(changeRecorder, this); + model->setItemPopulationStrategy(Akonadi::EntityTreeModel::LazyPopulation); + return model; +} + +static const int numberOfRootCollections = 4; + +void FavoriteProxyTest::testReordering() +{ + // GIVEN an ETM, a favorite proxy, and an EntityOrderProxyModel on top + EntityTreeModel *model = createETM(); + QVERIFY(waitForPopulation(QModelIndex(), model, numberOfRootCollections)); + + QList collectionIds; + QStringList labels; + QStringList order; + for (const QString &folderName : { QStringLiteral("res2"), QStringLiteral("res3") }) { + const QModelIndex index = getIndex(folderName, model); + QVERIFY(index.isValid()); + const Akonadi::Collection favoriteCollection = index.data(EntityTreeModel::CollectionRole).value(); + QVERIFY(favoriteCollection.isValid()); + collectionIds.push_back(favoriteCollection.id()); + order.push_back("c" + QString::number(favoriteCollection.id())); + labels << folderName; + } + + KConfigGroup configGroup(KSharedConfig::openConfig(), "favoritecollectionsmodeltest"); + configGroup.writeEntry("FavoriteCollectionIds", collectionIds); + configGroup.writeEntry("FavoriteCollectionLabels", labels); + configGroup.writeEntry("0", order); + + FavoriteCollectionsModel *favoriteModel = new FavoriteCollectionsModel(model, configGroup, this); + QTRY_COMPARE(favoriteModel->rowCount(), 2); + + FavoriteCollectionOrderProxyModel *orderProxy = new FavoriteCollectionOrderProxyModel(this); + orderProxy->setOrderConfig(configGroup); + orderProxy->setSourceModel(favoriteModel); + orderProxy->sort(0, Qt::AscendingOrder); + + QCOMPARE(orderProxy->rowCount(), 2); + + const QModelIndex firstRowIndex = orderProxy->index(0, 0); + QVERIFY(firstRowIndex.isValid()); + QCOMPARE(firstRowIndex.data().toString(), QStringLiteral("res2")); + QVERIFY((orderProxy->flags(firstRowIndex) & Qt::ItemIsDropEnabled) == 0); +} + +#include "favoritestest.moc" + +QTEST_AKONADIMAIN(FavoriteProxyTest) diff --git a/autotests/unittestenv/config.xml b/autotests/unittestenv/config.xml --- a/autotests/unittestenv/config.xml +++ b/autotests/unittestenv/config.xml @@ -1,6 +1,8 @@ xdglocal akonadi_knut_resource + akonadi_knut_resource + akonadi_knut_resource true akonadi_test_searchplugin diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -108,6 +108,7 @@ folder/foldertreewidgetproxymodel.cpp folder/entitycollectionorderproxymodel.cpp folder/accountconfigorderdialog.cpp + folder/favoritecollectionorderproxymodel.cpp ) set(libmailcommon_job_SRCS @@ -247,7 +248,7 @@ KF5::SyntaxHighlighting ) target_include_directories(KF5MailCommon INTERFACE "$") -target_include_directories(KF5MailCommon PUBLIC "$") +target_include_directories(KF5MailCommon PUBLIC "$") set_target_properties(KF5MailCommon PROPERTIES @@ -309,6 +310,7 @@ FolderTreeWidget FolderSelectionDialog FolderTreeWidgetProxyModel + FavoriteCollectionOrderProxyModel REQUIRED_HEADERS MailCommon_folder_HEADERS PREFIX MailCommon RELATIVE folder diff --git a/src/folder/favoritecollectionorderproxymodel.h b/src/folder/favoritecollectionorderproxymodel.h new file mode 100644 --- /dev/null +++ b/src/folder/favoritecollectionorderproxymodel.h @@ -0,0 +1,47 @@ +/* + + 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 MAILCOMMON_FAVORITECOLLECTIONORDERPROXYMODEL_H +#define MAILCOMMON_FAVORITECOLLECTIONORDERPROXYMODEL_H + +#include +#include "mailcommon_export.h" + +namespace MailCommon { +/** + * @brief The FavoriteCollectionOrderProxyModel class implements ordering of favorite collections. + */ +class MAILCOMMON_EXPORT FavoriteCollectionOrderProxyModel : public Akonadi::EntityOrderProxyModel +{ + Q_OBJECT +public: + explicit FavoriteCollectionOrderProxyModel(QObject *parent = nullptr); + virtual ~FavoriteCollectionOrderProxyModel(); + + Qt::ItemFlags flags(const QModelIndex &index) const override; + +protected: + Akonadi::Collection parentCollection(const QModelIndex &index) const override; + +private: + class FavoriteCollectionOrderProxyModelPrivate; + FavoriteCollectionOrderProxyModelPrivate *const d; +}; +} + +#endif diff --git a/src/folder/favoritecollectionorderproxymodel.cpp b/src/folder/favoritecollectionorderproxymodel.cpp new file mode 100644 --- /dev/null +++ b/src/folder/favoritecollectionorderproxymodel.cpp @@ -0,0 +1,59 @@ +/* + + 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 "favoritecollectionorderproxymodel.h" +#include "mailcommon_debug.h" +#include +#include + +using namespace MailCommon; + +class Q_DECL_HIDDEN FavoriteCollectionOrderProxyModel::FavoriteCollectionOrderProxyModelPrivate +{ +public: + FavoriteCollectionOrderProxyModelPrivate() + { + } +}; + +FavoriteCollectionOrderProxyModel::FavoriteCollectionOrderProxyModel(QObject *parent) + : EntityOrderProxyModel(parent) + , d(nullptr) //, d(new FavoriteCollectionOrderProxyModelPrivate()) +{ +} + +FavoriteCollectionOrderProxyModel::~FavoriteCollectionOrderProxyModel() +{ + delete d; +} + +Qt::ItemFlags FavoriteCollectionOrderProxyModel::flags(const QModelIndex &index) const +{ + Qt::ItemFlags flags = KRecursiveFilterProxyModel::flags(index); + // Don't allow dropping on folders + if (index.isValid()) { + flags &= ~Qt::ItemIsDropEnabled; + } + return flags; +} + +Akonadi::Collection FavoriteCollectionOrderProxyModel::parentCollection(const QModelIndex &index) const +{ + Q_UNUSED(index); + return {}; +} diff --git a/src/widgets/favoritecollectionwidget.h b/src/widgets/favoritecollectionwidget.h --- a/src/widgets/favoritecollectionwidget.h +++ b/src/widgets/favoritecollectionwidget.h @@ -48,6 +48,10 @@ protected: void paintEvent(QPaintEvent *) override; + void dragEnterEvent(QDragEnterEvent *event) override; + void dragMoveEvent(QDragMoveEvent *event) override; + void dropEvent(QDropEvent *event) override; + void startDrag(Qt::DropActions) override; void mousePressEvent(QMouseEvent *e) override; Q_SIGNALS: diff --git a/src/widgets/favoritecollectionwidget.cpp b/src/widgets/favoritecollectionwidget.cpp --- a/src/widgets/favoritecollectionwidget.cpp +++ b/src/widgets/favoritecollectionwidget.cpp @@ -244,3 +244,38 @@ Akonadi::EntityListView::paintEvent(event); } } +void FavoriteCollectionWidget::dragEnterEvent(QDragEnterEvent *event) +{ + if (event->source() == this) { + // skip EntityListView logic (we want to reorder favorites, not trigger moving/copying of actual folders) + QListView::dragEnterEvent(event); + } else { + Akonadi::EntityListView::dragEnterEvent(event); + } +} + +void FavoriteCollectionWidget::dragMoveEvent(QDragMoveEvent *event) +{ + if (event->source() == this) { + // skip EntityListView logic (we want to reorder favorites, not trigger moving/copying of actual folders) + QListView::dragMoveEvent(event); + } else { + Akonadi::EntityListView::dragMoveEvent(event); + } +} + +void FavoriteCollectionWidget::dropEvent(QDropEvent *event) +{ + if (event->source() == this) { + // skip EntityListView logic (we want to reorder favorites, not trigger moving/copying of actual folders) + QListView::dropEvent(event); + } else { + Akonadi::EntityListView::dropEvent(event); + } +} + +void FavoriteCollectionWidget::startDrag(Qt::DropActions supportedActions) +{ + // skip EntityListView logic (we want to reorder favorites, not trigger moving/copying of actual folders) + QListView::startDrag(supportedActions); +}