diff --git a/autotests/CMakeLists.txt b/autotests/CMakeLists.txt index fa48fec..5cfee34 100644 --- a/autotests/CMakeLists.txt +++ b/autotests/CMakeLists.txt @@ -1,7 +1,10 @@ set(EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_BINARY_DIR}) set(KDEPIMLIBS_RUN_ISOLATED_TESTS TRUE) set(KDEPIMLIBS_RUN_SQLITE_ISOLATED_TESTS TRUE) 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 index 0000000..2dd04a3 --- /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 index 464422c..b2bb360 100644 --- 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 index 2f5aab4..c362c28 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,492 +1,494 @@ add_definitions(-DTRANSLATION_DOMAIN=\"libmailcommon\") #add_definitions( -DQT_NO_CAST_FROM_ASCII ) #add_definitions( -DQT_NO_CAST_TO_ASCII ) if(BUILD_TESTING) add_definitions(-DBUILD_TESTING) endif(BUILD_TESTING) set(libmailcommon_filter_SRCS filter/kmfilteraccountlist.cpp filter/kmfilterlistbox.cpp filter/filterselectiondialog.cpp filter/filterconverter/filterconverttosieve.cpp filter/filterconverter/filterconverttosieveresultdialog.cpp filter/filteractions/filteraction.cpp filter/filteractions/filteractionaddheader.cpp filter/filteractions/filteractionaddtag.cpp filter/filteractions/filteractionaddtoaddressbook.cpp filter/filteractions/filteractioncopy.cpp filter/filteractions/filteractiondecrypt.cpp filter/filteractions/filteractiondelete.cpp filter/filteractions/filteractiondict.cpp filter/filteractions/filteractionencrypt.cpp filter/filteractions/filteractionexec.cpp filter/filteractions/filteractionforward.cpp filter/filteractions/filteractionmove.cpp filter/filteractions/filteractionpipethrough.cpp filter/filteractions/filteractionplaysound.cpp filter/filteractions/filteractionredirect.cpp filter/filteractions/filteractionremoveheader.cpp filter/filteractions/filteractionreplyto.cpp filter/filteractions/filteractionrewriteheader.cpp filter/filteractions/filteractionsendfakedisposition.cpp filter/filteractions/filteractionsendreceipt.cpp filter/filteractions/filteractionsetidentity.cpp filter/filteractions/filteractionsetstatus.cpp filter/filteractions/filteractionstatus.cpp filter/filteractions/filteractionunsetstatus.cpp filter/filteractions/filteractionsettransport.cpp filter/filteractions/filteractionwidget.cpp filter/filteractions/filteractionwithaddress.cpp filter/filteractions/filteractionwithcommand.cpp filter/filteractions/filteractionwithcrypto.cpp filter/filteractions/filteractionwithfolder.cpp filter/filteractions/filteractionwithnone.cpp filter/filteractions/filteractionwithstring.cpp filter/filteractions/filteractionwithstringlist.cpp filter/filteractions/filteractionwithtest.cpp filter/filteractions/filteractionwithuoid.cpp filter/filteractions/filteractionwithurl.cpp filter/filterimporterexporter.cpp filter/filterimporter/filterimporterabstract.cpp filter/filterimporter/filterimporterevolution.cpp filter/filterimporter/filterimportersylpheed.cpp filter/filterimporter/filterimporterthunderbird.cpp filter/filterimporter/filterimporterprocmail.cpp filter/filterimporter/filterimporterbalsa.cpp filter/filterimporter/filterimporterclawsmail.cpp filter/filterimporter/filterimportergmail.cpp filter/filterlog.cpp filter/filtermanager.cpp filter/itemcontext.cpp filter/kmfilterdialog.cpp filter/mailfilter.cpp filter/mdnadvicedialog.cpp filter/filterimporterpathcache.cpp ) set(libmailcommon_filter_dialog filter/dialog/selectthunderbirdfilterfilesdialog.cpp filter/dialog/selectthunderbirdfilterfileswidget.cpp filter/dialog/filteractionmissingfolderdialog.cpp filter/dialog/filteractionmissingsoundurldialog.cpp filter/dialog/filteractionmissingtagdialog.cpp filter/dialog/filteractionmissingaccountdialog.cpp filter/dialog/filteractionmissingtemplatedialog.cpp filter/dialog/filteractionmissingtransportdialog.cpp filter/dialog/filteractionmissingidentitydialog.cpp ) set(libmailcommon_invalidfilters filter/invalidfilters/invalidfilterdialog.cpp filter/invalidfilters/invalidfilterlistview.cpp filter/invalidfilters/invalidfilterwidget.cpp filter/invalidfilters/invalidfilterinfo.cpp filter/invalidfilters/invalidfilterlistitemdelegate.cpp filter/invalidfilters/invalidfilterlistmodel.cpp filter/invalidfilters/invalidfilterinfowidget.cpp ) set(libmailcommon_collection_SRCS collectionpage/attributeregistrar.cpp collectionpage/collectiongeneralpage.cpp collectionpage/collectionexpirypage.cpp collectionpage/attributes/expirecollectionattribute.cpp ) set(libmailcommon_folder_SRCS folder/foldersettings.cpp folder/foldercollectionmonitor.cpp folder/folderrequester.cpp folder/folderselectiondialog.cpp folder/foldertreeview.cpp folder/foldertreewidget.cpp folder/foldertreewidgetproxymodel.cpp folder/entitycollectionorderproxymodel.cpp folder/accountconfigorderdialog.cpp + folder/favoritecollectionorderproxymodel.cpp ) set(libmailcommon_job_SRCS job/jobscheduler.cpp job/folderjob.cpp job/expirejob.cpp job/backupjob.cpp ) set(libmailcommon_search_SRCS search/widgethandler/rulewidgethandlermanager.cpp search/searchpattern.cpp search/searchpatternedit.cpp search/widgethandler/encryptionwidgethandler.cpp search/widgethandler/textrulerwidgethandler.cpp search/widgethandler/statusrulewidgethandler.cpp search/widgethandler/messagerulewidgethandler.cpp search/widgethandler/tagrulewidgethandler.cpp search/widgethandler/numericrulewidgethandler.cpp search/widgethandler/daterulewidgethandler.cpp search/widgethandler/numericdoublerulewidgethandler.cpp search/widgethandler/headersrulerwidgethandler.cpp search/searchrule/searchrulenumerical.cpp search/searchrule/searchruledate.cpp search/searchrule/searchrulestring.cpp search/searchrule/searchrulestatus.cpp search/searchrule/searchruleencryption.cpp search/searchrule/searchrule.cpp ) set(libmailcommon_snippets_SRCS snippets/snippetdialog.cpp snippets/snippetsmanager.cpp snippets/snippetsmodel.cpp snippets/snippetvariabledialog.cpp ) set(libmailcommon_tag_SRCS tag/tagwidget.cpp tag/tag.cpp tag/addtagdialog.cpp ) set(libmailcommon_widget_SRCS widgets/redirectdialog.cpp widgets/redirectwidget.cpp widgets/favoritecollectionwidget.cpp ) set(libmailcommon_mdn_SRCS mdn/sendmdnhandler.cpp mdn/mdnstateattribute.cpp ) set(libmailcommon_util_SRCS util/cryptoutils.cpp util/mailutil.cpp util/resourcereadconfigfile.cpp ) set(libmailcommon_SRCS kernel/mailkernel.cpp ${libmailcommon_filter_dialog} ${libmailcommon_util_SRCS} ${libmailcommon_mdn_SRCS} ${libmailcommon_invalidfilters} ${libmailcommon_widget_SRCS} ${libmailcommon_tag_SRCS} ${libmailcommon_snippets_SRCS} ${libmailcommon_search_SRCS} ${libmailcommon_job_SRCS} ${libmailcommon_collection_SRCS} ${libmailcommon_folder_SRCS} ${libmailcommon_filter_SRCS} ) kconfig_add_kcfg_files(libmailcommon_SRCS settings/mailcommonsettings_base.kcfgc ) ecm_qt_declare_logging_category(libmailcommon_SRCS HEADER mailcommon_debug.h IDENTIFIER MAILCOMMON_LOG CATEGORY_NAME org.kde.pim.mailcommon) set_source_files_properties(${CMAKE_CURRENT_SOURCE_DIR}/dbusinterfaces/org.freedesktop.Akonadi.MailFilterAgent.xml PROPERTIES INCLUDE "dbusoperators.h") qt5_add_dbus_interfaces(libmailcommon_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/dbusinterfaces/org.freedesktop.Akonadi.MailFilterAgent.xml ) ki18n_wrap_ui(libmailcommon_SRCS filter/ui/filterconfigwidget.ui snippets/ui/snippetdialog.ui filter/ui/selectthunderbirdfilterfileswidget.ui) set(libmailcommon_SRCS ${libmailcommon_SRCS} filter/soundtestwidget.cpp) macro(add_resource_iface _kcfgFile _ifaceName _className) kcfg_generate_dbus_interface(${CMAKE_CURRENT_SOURCE_DIR}/${_kcfgFile} ${_ifaceName}) string(TOLOWER ${_className} _codeFile) set_source_files_properties(${CMAKE_CURRENT_BINARY_DIR}/${_ifaceName}.xml PROPERTIES INCLUDE "pimcommon/metatype.h") qt5_add_dbus_interface(libmailcommon_SRCS ${CMAKE_CURRENT_BINARY_DIR}/${_ifaceName}.xml ${_codeFile} ${_className} ) endmacro() add_resource_iface(settings.kcfg org.kde.Akonadi.POP3.Settings Pop3Settings) qt5_add_dbus_interfaces(libmailcommon_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/dbusinterfaces/org.kde.Korganizer.Calendar.xml ) add_library(KF5MailCommon ${libmailcommon_SRCS}) generate_export_header(KF5MailCommon BASE_NAME mailcommon) add_library(KF5::MailCommon ALIAS KF5MailCommon) target_link_libraries(KF5MailCommon PUBLIC KF5::AkonadiCore KF5::AkonadiMime KF5::MessageComposer KF5::PimCommonAkonadi PRIVATE KF5::TextWidgets KF5::I18n KF5::LibkdepimAkonadi KF5::MessageViewer KF5::MailImporter KF5::MessageCore KF5::TemplateParser KF5::Mime KF5::Codecs KF5::MailTransport Phonon::phonon4qt5 KF5::XmlGui KF5::KIOWidgets KF5::WindowSystem KF5::IconThemes KF5::Archive KF5::ItemViews KF5::SyntaxHighlighting ) target_include_directories(KF5MailCommon INTERFACE "$") -target_include_directories(KF5MailCommon PUBLIC "$") +target_include_directories(KF5MailCommon PUBLIC "$") set_target_properties(KF5MailCommon PROPERTIES VERSION ${MAILCOMMON_VERSION_STRING} SOVERSION ${MAILCOMMON_SOVERSION} EXPORT_NAME MailCommon ) install(TARGETS KF5MailCommon EXPORT KF5MailCommonTargets ${KF5_INSTALL_TARGETS_DEFAULT_ARGS} ${LIBRARY_NAMELINK} ) if (BUILD_TESTING) add_subdirectory(filter/filterimporter/autotests) add_subdirectory(collectionpage/autotests) add_subdirectory(filter/autotests) add_subdirectory(snippets/autotests) add_subdirectory(filter/tests) add_subdirectory(search/autotests) add_subdirectory(mdn/autotests/) endif() ecm_generate_headers(MailCommon_CamelCase_HEADERS HEADER_NAMES CryptoUtils MailUtil MetaType ResourceReadConfigFile REQUIRED_HEADERS MailCommon_HEADERS PREFIX MailCommon RELATIVE util ) ecm_generate_headers(MailCommon_Camelcasetop_HEADERS HEADER_NAMES DBusOperators REQUIRED_HEADERS MailCommon_top_HEADERS PREFIX MailCommon ) ecm_generate_headers(MailCommon_Camelcasetag_HEADERS HEADER_NAMES TagWidget Tag AddTagDialog REQUIRED_HEADERS MailCommon_tag_HEADERS PREFIX MailCommon RELATIVE tag ) ecm_generate_headers(MailCommon_Camelcasefolder_HEADERS HEADER_NAMES FolderSettings FolderRequester FolderCollectionMonitor AccountConfigOrderDialog FolderTreeView FolderTreeWidget FolderSelectionDialog FolderTreeWidgetProxyModel + FavoriteCollectionOrderProxyModel REQUIRED_HEADERS MailCommon_folder_HEADERS PREFIX MailCommon RELATIVE folder ) ecm_generate_headers(MailCommon_Camelcasekernel_HEADERS HEADER_NAMES MailKernel REQUIRED_HEADERS MailCommon_kernel_HEADERS PREFIX MailCommon RELATIVE kernel ) ecm_generate_headers(MailCommon_Camelcaseinterfaces_HEADERS HEADER_NAMES MailInterfaces REQUIRED_HEADERS MailCommon_interfaces_HEADERS PREFIX MailCommon RELATIVE interfaces ) ecm_generate_headers(MailCommon_Camelcasefilter_HEADERS HEADER_NAMES FilterLog MailFilter FilterImporterExporter FilterManager KMFilterDialog FilterImporterPathCache ItemContext REQUIRED_HEADERS MailCommon_filter_HEADERS PREFIX MailCommon RELATIVE filter ) ecm_generate_headers(MailCommon_Camelcasejob_HEADERS HEADER_NAMES BackupJob JobScheduler FolderJob REQUIRED_HEADERS MailCommon_job_HEADERS PREFIX MailCommon RELATIVE job ) ecm_generate_headers(MailCommon_Camelcasefilteraction_HEADERS HEADER_NAMES FilterAction FilterActionDict REQUIRED_HEADERS MailCommon_filteraction_HEADERS PREFIX MailCommon RELATIVE filter/filteractions/ ) ecm_generate_headers(MailCommon_Camelcasemdn_HEADERS HEADER_NAMES SendMdnHandler MDNStateAttribute REQUIRED_HEADERS MailCommon_mdn_HEADERS PREFIX MailCommon RELATIVE mdn ) ecm_generate_headers(MailCommon_Camelcasecollectionpage_HEADERS HEADER_NAMES CollectionGeneralPage CollectionExpiryPage REQUIRED_HEADERS MailCommon_collectionpage_HEADERS PREFIX MailCommon RELATIVE collectionpage ) ecm_generate_headers(MailCommon_Camelcasecollectionpageattributes_HEADERS HEADER_NAMES ExpireCollectionAttribute REQUIRED_HEADERS MailCommon_collectionpageattributes_HEADERS PREFIX MailCommon RELATIVE collectionpage/attributes ) ecm_generate_headers(MailCommon_Camelcasefilterimporter_HEADERS HEADER_NAMES FilterImporterBalsa FilterImporterClawsMail FilterImporterAbstract REQUIRED_HEADERS MailCommon_filterimporter_HEADERS PREFIX MailCommon RELATIVE filter/filterimporter/ ) ecm_generate_headers(MailCommon_Camelcasesnippets_HEADERS HEADER_NAMES SnippetsManager REQUIRED_HEADERS MailCommon_snippets_HEADERS PREFIX MailCommon RELATIVE snippets ) ecm_generate_headers(MailCommon_Camelcasekernel_HEADERS HEADER_NAMES SearchPattern SearchPatternEdit REQUIRED_HEADERS MailCommon_kernel_HEADERS PREFIX MailCommon RELATIVE search ) ecm_generate_headers(MailCommon_Camelcasewidgets_HEADERS HEADER_NAMES RedirectDialog FavoriteCollectionWidget REQUIRED_HEADERS MailCommon_widgets_HEADERS PREFIX MailCommon RELATIVE widgets ) ecm_generate_headers(MailCommon_Camelcasesearchrule_HEADERS HEADER_NAMES SearchRule SearchRuleStatus REQUIRED_HEADERS MailCommon_searchrule_HEADERS PREFIX MailCommon RELATIVE search/searchrule ) ecm_generate_pri_file(BASE_NAME MailCommon LIB_NAME KF5MailCommon DEPS "AkonadiCore AkonadiMime MessageComposer PimCommon" FILENAME_VAR PRI_FILENAME INCLUDE_INSTALL_DIR ${KDE_INSTALL_INCLUDEDIR_KF5}/MailCommon ) install(FILES ${MailCommon_Camelcasesearchrule_HEADERS} ${MailCommon_CamelCase_HEADERS} ${MailCommon_Camelcasetop_HEADERS} ${MailCommon_Camelcasetag_HEADERS} ${MailCommon_Camelcasefolder_HEADERS} ${MailCommon_Camelcasekernel_HEADERS} ${MailCommon_Camelcaseinterfaces_HEADERS} ${MailCommon_Camelcasefilter_HEADERS} ${MailCommon_Camelcasejob_HEADERS} ${MailCommon_Camelcasefilteraction_HEADERS} ${MailCommon_Camelcasemdn_HEADERS} ${MailCommon_Camelcasecollectionpage_HEADERS} ${MailCommon_Camelcasecollectionpageattributes_HEADERS} ${MailCommon_Camelcasefilterimporter_HEADERS} ${MailCommon_Camelcasesnippets_HEADERS} ${MailCommon_Camelcasewidgets_HEADERS} DESTINATION ${KDE_INSTALL_INCLUDEDIR_KF5}/MailCommon COMPONENT Devel ) install(FILES ${MailCommon_searchrule_HEADERS} ${CMAKE_CURRENT_BINARY_DIR}/mailcommon_export.h ${CMAKE_CURRENT_BINARY_DIR}/pop3settings.h ${CMAKE_CURRENT_BINARY_DIR}/mailcommonsettings_base.h ${MailCommon_HEADERS} ${MailCommon_top_HEADERS} ${MailCommon_tag_HEADERS} ${MailCommon_folder_HEADERS} ${MailCommon_kernel_HEADERS} ${MailCommon_interfaces_HEADERS} ${MailCommon_filter_HEADERS} ${MailCommon_job_HEADERS} ${MailCommon_filteraction_HEADERS} ${MailCommon_mdn_HEADERS} ${MailCommon_collectionpage_HEADERS} ${MailCommon_collectionpageattributes_HEADERS} ${MailCommon_filterimporter_HEADERS} ${MailCommon_snippets_HEADERS} ${MailCommon_widgets_HEADERS} DESTINATION ${KDE_INSTALL_INCLUDEDIR_KF5}/mailcommon COMPONENT Devel ) install(FILES ${PRI_FILENAME} DESTINATION ${ECM_MKSPECS_INSTALL_DIR}) add_subdirectory(designer) diff --git a/src/folder/favoritecollectionorderproxymodel.cpp b/src/folder/favoritecollectionorderproxymodel.cpp new file mode 100644 index 0000000..686f708 --- /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/folder/favoritecollectionorderproxymodel.h b/src/folder/favoritecollectionorderproxymodel.h new file mode 100644 index 0000000..e3a7a1a --- /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/widgets/favoritecollectionwidget.cpp b/src/widgets/favoritecollectionwidget.cpp index fc4e4c2..a3e96bd 100644 --- a/src/widgets/favoritecollectionwidget.cpp +++ b/src/widgets/favoritecollectionwidget.cpp @@ -1,246 +1,281 @@ /* Copyright (c) 2012-2017 Montel Laurent 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 "favoritecollectionwidget.h" #include "kernel/mailkernel.h" #include "mailcommonsettings_base.h" #include #include #include #include #include #include #include #include #include using namespace MailCommon; class Q_DECL_HIDDEN FavoriteCollectionWidget::Private { public: Private() { } QColor textColor; QAction *listMode = nullptr; QAction *iconMode = nullptr; MailCommonSettings *settings = nullptr; }; FavoriteCollectionWidget::FavoriteCollectionWidget(MailCommon::MailCommonSettings *settings, KXMLGUIClient *xmlGuiClient, QWidget *parent) : Akonadi::EntityListView(xmlGuiClient, parent) , d(new Private) { d->settings = settings; setFocusPolicy(Qt::NoFocus); Akonadi::CollectionStatisticsDelegate *delegate = new Akonadi::CollectionStatisticsDelegate(this); delegate->setProgressAnimationEnabled(true); setItemDelegate(delegate); delegate->setUnreadCountShown(true); readConfig(); createMenu(xmlGuiClient->actionCollection()); } FavoriteCollectionWidget::~FavoriteCollectionWidget() { delete d; } void FavoriteCollectionWidget::mousePressEvent(QMouseEvent *e) { const bool buttonPressedIsMiddle = (e->button() == Qt::MidButton); Q_EMIT newTabRequested(buttonPressedIsMiddle); Akonadi::EntityListView::mousePressEvent(e); } void FavoriteCollectionWidget::updateMode() { switch (viewMode()) { case ListMode: d->listMode->setChecked(true); d->iconMode->setChecked(false); break; case IconMode: d->listMode->setChecked(false); d->iconMode->setChecked(true); break; } } void FavoriteCollectionWidget::createMenu(KActionCollection *ac) { KActionMenu *iconSizeMenu = new KActionMenu(i18n("Icon size"), this); ac->addAction(QStringLiteral("favorite_icon_size"), iconSizeMenu); static const int icon_sizes[] = { 16, 22, 32 /*, 48, 64, 128 */ }; QActionGroup *grp = new QActionGroup(iconSizeMenu); const int nbElement((int)(sizeof(icon_sizes) / sizeof(int))); QAction *act = nullptr; for (int i = 0; i < nbElement; ++i) { act = new QAction(QStringLiteral("%1x%2").arg(icon_sizes[ i ]).arg(icon_sizes[ i ]), iconSizeMenu); iconSizeMenu->addAction(act); act->setCheckable(true); grp->addAction(act); if (iconSize().width() == icon_sizes[ i ]) { act->setChecked(true); } act->setData(QVariant(icon_sizes[ i ])); connect(act, &QAction::triggered, this, &FavoriteCollectionWidget::slotChangeIconSize); } KActionMenu *modeFavoriteMenu = new KActionMenu(i18n("Mode"), this); ac->addAction(QStringLiteral("favorite_mode"), modeFavoriteMenu); grp = new QActionGroup(modeFavoriteMenu); d->listMode = new QAction(i18n("List Mode"), modeFavoriteMenu); modeFavoriteMenu->addAction(d->listMode); d->listMode->setCheckable(true); grp->addAction(d->listMode); if (viewMode() == ListMode) { d->listMode->setChecked(true); } d->listMode->setData(QVariant(MailCommon::MailCommonSettings::EnumFavoriteCollectionViewMode::ListMode)); connect(d->listMode, &QAction::triggered, this, &FavoriteCollectionWidget::slotChangeMode); d->iconMode = new QAction(i18n("Icon Mode"), modeFavoriteMenu); modeFavoriteMenu->addAction(d->iconMode); grp->addAction(d->iconMode); d->iconMode->setCheckable(true); if (viewMode() == IconMode) { d->iconMode->setChecked(true); } d->iconMode->setData(QVariant(MailCommon::MailCommonSettings::EnumFavoriteCollectionViewMode::IconMode)); connect(d->iconMode, &QAction::triggered, this, &FavoriteCollectionWidget::slotChangeMode); } void FavoriteCollectionWidget::slotChangeMode(bool) { QAction *act = qobject_cast< QAction * >(sender()); if (!act) { return; } QVariant data = act->data(); bool ok; const int mode = data.toInt(&ok); if (!ok) { return; } switch (mode) { case MailCommon::MailCommonSettings::EnumFavoriteCollectionViewMode::IconMode: changeViewMode(IconMode); break; case MailCommon::MailCommonSettings::EnumFavoriteCollectionViewMode::ListMode: changeViewMode(ListMode); break; } d->settings->setFavoriteCollectionViewMode(mode); d->settings->save(); } void FavoriteCollectionWidget::changeViewMode(QListView::ViewMode mode) { setViewMode(mode); setDragEnabled(true); setAcceptDrops(true); } void FavoriteCollectionWidget::slotChangeIconSize(bool) { QAction *act = qobject_cast< QAction * >(sender()); if (!act) { return; } QVariant data = act->data(); bool ok; const int size = data.toInt(&ok); if (!ok) { return; } const QSize newIconSize(QSize(size, size)); if (newIconSize == iconSize()) { return; } setIconSize(newIconSize); d->settings->setIconSize(iconSize().width()); d->settings->save(); } void FavoriteCollectionWidget::slotGeneralPaletteChanged() { const QPalette palette = viewport()->palette(); QColor color = palette.text().color(); color.setAlpha(128); d->textColor = color; } void FavoriteCollectionWidget::slotGeneralFontChanged() { // Custom/System font support if (MessageCore::MessageCoreSettings::self()->useDefaultFonts()) { setFont(QFontDatabase::systemFont(QFontDatabase::GeneralFont)); } } void FavoriteCollectionWidget::readConfig() { setFont(QFontDatabase::systemFont(QFontDatabase::GeneralFont)); int iIconSize = d->settings->iconSize(); if (iIconSize < 16 || iIconSize > 32) { iIconSize = 22; } setIconSize(QSize(iIconSize, iIconSize)); } void FavoriteCollectionWidget::paintEvent(QPaintEvent *event) { if (!model() || model()->rowCount() == 0) { QPainter p(viewport()); QFont font = p.font(); font.setItalic(true); p.setFont(font); if (!d->textColor.isValid()) { slotGeneralPaletteChanged(); } p.setPen(d->textColor); p.drawText(QRect(0, 0, width(), height()), Qt::AlignCenter, i18n("Drop your favorite folders here...")); } else { 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); +} diff --git a/src/widgets/favoritecollectionwidget.h b/src/widgets/favoritecollectionwidget.h index 2b7420a..83ee9e3 100644 --- a/src/widgets/favoritecollectionwidget.h +++ b/src/widgets/favoritecollectionwidget.h @@ -1,63 +1,67 @@ /* Copyright (c) 2012-2017 Montel Laurent 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_FAVORITECOLLECTIONWIDGET_H #define MAILCOMMON_FAVORITECOLLECTIONWIDGET_H #include "mailcommon_export.h" #include class KXMLGUIClient; class KActionCollection; namespace MailCommon { class MailCommonSettings; class MAILCOMMON_EXPORT FavoriteCollectionWidget : public Akonadi::EntityListView { Q_OBJECT public: explicit FavoriteCollectionWidget(MailCommon::MailCommonSettings *settings, KXMLGUIClient *xmlGuiClient, QWidget *parent = nullptr); ~FavoriteCollectionWidget(); void readConfig(); void updateMode(); void changeViewMode(QListView::ViewMode mode); protected Q_SLOTS: void slotGeneralFontChanged(); void slotGeneralPaletteChanged(); void slotChangeIconSize(bool); void slotChangeMode(bool); 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: void newTabRequested(bool); private: void createMenu(KActionCollection *ac); class Private; Private *const d; }; } #endif /* MAILCOMMON_FAVORITECOLLECTIONWIDGET_H */