diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index cdcfe5c6..23cde41a 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -1,242 +1,244 @@ set(Ruqola_ddpapi_SRCS ddpapi/ddpclient.cpp ) set (Ruqola_model_core_srcs model/messagemodel.cpp model/roommodel.cpp model/roomfilterproxymodel.cpp model/usersforroommodel.cpp model/usersforroomfilterproxymodel.cpp model/usersmodel.cpp model/usercompleterfilterproxymodel.cpp model/usercompletermodel.cpp model/statusmodel.cpp model/filesforroommodel.cpp model/filesforroomfilterproxymodel.cpp model/searchchannelmodel.cpp model/searchchannelfilterproxymodel.cpp model/inputcompletermodel.cpp model/loginmethodmodel.cpp model/searchmessagemodel.cpp model/searchmessagefilterproxymodel.cpp model/rocketchataccountmodel.cpp model/rocketchataccountfilterproxymodel.cpp model/emoticonfiltermodel.cpp model/emoticonmodel.cpp model/notificationpreferencemodel.cpp model/notificationdesktopdurationpreferencemodel.cpp model/notificationdesktopsoundpreferencemodel.cpp model/discussionsmodel.cpp model/discussionsfilterproxymodel.cpp model/emoticoncategoriesmodel.cpp model/threadmessagemodel.cpp model/listmessagesmodel.cpp model/listmessagesmodelfilterproxymodel.cpp model/autotranslatelanguagesmodel.cpp model/commandsmodel.cpp + + model/accountschannelsmodel.cpp ) set(Ruqola_plugins_srcs plugins/pluginauthenticationinterface.cpp plugins/pluginauthentication.cpp ) set(Ruqola_emoticons_srcs emoticons/emoji.cpp emoticons/emojimanager.cpp emoticons/unicodeemoticon.cpp emoticons/unicodeemoticonparser.cpp ) set(Ruqola_messages_srcs messages/message.cpp messages/messageattachment.cpp messages/messageurl.cpp messages/messagestarred.cpp messages/reactions.cpp messages/reaction.cpp messages/messagepinned.cpp messages/messagetranslation.cpp ) set(Ruqola_message_convertertextjob_SRCS convertertextjob/convertertextabstractjob.cpp convertertextjob/translatetextjob.cpp ) set(Ruqola_autotranslatelanguage_SRCS autotranslatelanguage.cpp autotranslatelanguages.cpp ) set(Ruqola_command_SRCS command.cpp commands.cpp ) set( Ruqola_kcfg_SRCS settings/ruqolaglobalconfig.kcfgc ) kconfig_add_kcfg_files(Ruqola_kcfg_SRCS ${Ruqola_kcfg_SRCS}) set (Ruqola_core_srcs ${Ruqola_kcfg_SRCS} ${Ruqola_command_SRCS} ${Ruqola_autotranslatelanguage_SRCS} ${Ruqola_message_convertertextjob_SRCS} ${Ruqola_emoticons_srcs} ${Ruqola_messages_srcs} ${Ruqola_model_core_srcs} ${Ruqola_plugins_srcs} ${Ruqola_ddpapi_SRCS} authenticationinfo.cpp room.cpp roomwrapper.cpp notificationoptionswrapper.cpp ruqola.cpp ruqolautils.cpp rocketchatbackend.cpp messagequeue.cpp rocketchatmessage.cpp typingnotification.cpp changetemporarystatus.cpp user.cpp utils.cpp clipboardproxy.cpp otr.cpp otrmanager.cpp abstractwebsocket.cpp ruqolawebsocket.cpp rocketchataccount.cpp rocketchataccountsettings.cpp ruqolalogger.cpp ruqolaregisterengine.cpp ruqolaserverconfig.cpp rocketchatcache.cpp texthighlighter.cpp textconverter.cpp loadrecenthistorymanager.cpp file.cpp channel.cpp inputtextmanager.cpp authenticationmanager.cpp accountmanager.cpp managerdatapaths.cpp messagecache.cpp notificationoptions.cpp syntaxhighlightingmanager.cpp receivetypingnotificationmanager.cpp serverconfiginfo.cpp notificationpreferences.cpp roles.cpp role.cpp avatarmanager.cpp discussion.cpp discussions.cpp emoticoncategory.cpp listmessages.cpp messagedownloadmanager.cpp fileattachments.cpp commands.cpp accountroomsettings.cpp ) if (NOT WIN32) set(Ruqola_core_srcs ${Ruqola_core_srcs} unityservicemanager.cpp) endif() if (NOT ANDROID) list(APPEND Ruqola_core_srcs notification.cpp) endif() ecm_qt_declare_logging_category(Ruqola_core_srcs HEADER ruqola_debug.h IDENTIFIER RUQOLA_LOG CATEGORY_NAME org.kde.ruqola) ecm_qt_declare_logging_category(Ruqola_core_srcs HEADER ruqola_message_debug.h IDENTIFIER RUQOLA_MESSAGE_LOG CATEGORY_NAME org.kde.ruqola.message) ecm_qt_declare_logging_category(Ruqola_core_srcs HEADER ruqola_ddpapi_debug.h IDENTIFIER RUQOLA_DDPAPI_LOG CATEGORY_NAME org.kde.ruqola.ddpapi) ecm_qt_declare_logging_category(Ruqola_core_srcs HEADER ruqola_ddpapi_command_debug.h IDENTIFIER RUQOLA_DDPAPI_COMMAND_LOG CATEGORY_NAME org.kde.ruqola.ddpapi.command) ecm_qt_declare_logging_category(Ruqola_core_srcs HEADER ruqola_unknown_collectiontype_debug.h IDENTIFIER RUQOLA_UNKNOWN_COLLECTIONTYPE_LOG CATEGORY_NAME org.kde.ruqola.ddp.collectiontype) qt5_add_resources(libruqolacore_RSC ruqolacore.qrc) add_library(libruqolacore ${Ruqola_core_srcs} ${libruqolacore_RSC}) generate_export_header(libruqolacore BASE_NAME libruqolacore) target_link_libraries(libruqolacore Qt5::Core Qt5::Gui Qt5::Widgets Qt5::Qml Qt5::Quick Qt5::WebSockets Qt5::Network Qt5::NetworkAuth KF5::CoreAddons KF5::I18n KF5::Notifications KF5::SyntaxHighlighting librocketchatrestapi-qt5 KF5::ConfigGui KF5::ConfigWidgets ) if (NOT WIN32) target_link_libraries(libruqolacore Qt5::DBus) endif() if (WIN32 OR APPLE) target_link_libraries(libruqolacore KF5::IconThemes) endif() if (Qt5Keychain_FOUND) target_link_libraries(libruqolacore qt5keychain) target_include_directories(libruqolacore PRIVATE ${QTKEYCHAIN_INCLUDE_DIRS}) endif() set_target_properties(libruqolacore PROPERTIES OUTPUT_NAME ruqolacore VERSION ${RUQOLA_LIB_VERSION} SOVERSION ${RUQOLA_LIB_SOVERSION} ) if (BUILD_TESTING) add_subdirectory(autotests) endif() install(TARGETS libruqolacore ${KDE_INSTALL_TARGETS_DEFAULT_ARGS} LIBRARY NAMELINK_SKIP) if (ANDROID) install(FILES ruqolacore-android-dependencies.xml DESTINATION ${KDE_INSTALL_LIBDIR}) endif() diff --git a/src/core/autotests/CMakeLists.txt b/src/core/autotests/CMakeLists.txt index 9303f926..31b575c4 100644 --- a/src/core/autotests/CMakeLists.txt +++ b/src/core/autotests/CMakeLists.txt @@ -1,95 +1,96 @@ add_definitions( -DRUQOLA_DATA_DIR="${CMAKE_CURRENT_SOURCE_DIR}/data") macro(add_ruqola_test _source) set( _test ${_source}) get_filename_component( _name ${_source} NAME_WE ) add_executable( ${_name} ${_test} ) add_test(NAME ${_name} COMMAND ${_name} ) ecm_mark_as_test(${_name}) target_link_libraries( ${_name} Qt5::Test libruqolacore) endmacro() +add_ruqola_test(accountschannelsmodeltest.cpp) add_ruqola_test(rocketchatmessagetest.cpp) add_ruqola_test(roommodeltest.cpp) add_ruqola_test(messagemodeltest.cpp) add_ruqola_test(typingnotificationtest.cpp) add_ruqola_test(utilstest.cpp) add_ruqola_test(usertest.cpp) add_ruqola_test(messageattachmenttest.cpp) add_ruqola_test(rocketchataccountsettingstest.cpp) add_ruqola_test(messagetest.cpp) add_ruqola_test(messageurltest.cpp) add_ruqola_test(roomtest.cpp) add_ruqola_test(ruqolaserverconfigtest.cpp) add_ruqola_test(statusmodeltest.cpp) add_ruqola_test(rocketchatcachetest.cpp) add_ruqola_test(textconvertertest.cpp) add_ruqola_test(loadrecenthistorymanagertest.cpp) add_ruqola_test(notificationtest.cpp) add_ruqola_test(emojitest.cpp) add_ruqola_test(emojimanagertest.cpp) add_ruqola_test(otrtest.cpp) add_ruqola_test(otrmanagertest.cpp) add_ruqola_test(rocketchataccounttest.cpp) add_ruqola_test(usersmodeltest.cpp) add_ruqola_test(usersforroommodeltest.cpp) add_ruqola_test(filetest.cpp) add_ruqola_test(filesforroommodeltest.cpp) add_ruqola_test(filesforroomfilterproxymodeltest.cpp) add_ruqola_test(channeltest.cpp) add_ruqola_test(usersforroomfilterproxymodeltest.cpp) add_ruqola_test(usercompletermodeltest.cpp) add_ruqola_test(roomfilterproxymodeltest.cpp) add_ruqola_test(usercompleterfilterproxymodeltest.cpp) add_ruqola_test(searchchannelmodeltest.cpp) add_ruqola_test(searchchannelfilterproxymodeltest.cpp) add_ruqola_test(inputcompletermodeltest.cpp) add_ruqola_test(loginmethodmodeltest.cpp) add_ruqola_test(inputtextmanagertest.cpp) add_ruqola_test(clipboardproxytest.cpp) add_ruqola_test(authenticationinfotest.cpp) add_ruqola_test(searchmessagemodeltest.cpp) add_ruqola_test(searchmessagefilterproxymodeltest.cpp) add_ruqola_test(accountmanagertest.cpp) add_ruqola_test(rocketchataccountmodeltest.cpp) add_ruqola_test(ruqolatest.cpp) add_ruqola_test(managerdatapathstest.cpp) add_ruqola_test(rocketchataccountfilterproxymodeltest.cpp) add_ruqola_test(roomwrappertest.cpp) add_ruqola_test(notificationoptionstest.cpp) add_ruqola_test(reactionstest.cpp) add_ruqola_test(reactiontest.cpp) add_ruqola_test(emoticonmodeltest.cpp) add_ruqola_test(unicodeemoticontest.cpp) add_ruqola_test(receivetypingnotificationmanagertest.cpp) add_ruqola_test(serverconfiginfotest.cpp) add_ruqola_test(notificationpreferencemodeltest.cpp) add_ruqola_test(notificationpreferencestest.cpp) add_ruqola_test(roletest.cpp) add_ruqola_test(rolestest.cpp) add_ruqola_test(messagestarredtest.cpp) add_ruqola_test(messagepinnedtest.cpp) add_ruqola_test(notificationdesktopdurationpreferencemodeltest.cpp) add_ruqola_test(notificationdesktopsoundpreferencemodeltest.cpp) add_ruqola_test(unicodeemoticonparsertest.cpp) add_ruqola_test(discussionsmodeltest.cpp) add_ruqola_test(discussionsfilterproxymodeltest.cpp) add_ruqola_test(discussiontest.cpp) add_ruqola_test(discussionstest.cpp) add_ruqola_test(emoticoncategoriesmodeltest.cpp) add_ruqola_test(emoticoncategorytest.cpp) add_ruqola_test(threadmessagestest.cpp) add_ruqola_test(threadmessagetest.cpp) add_ruqola_test(fileattachmentstest.cpp) add_ruqola_test(threadmessagemodeltest.cpp) add_ruqola_test(convertertextabstractjobtest.cpp) add_ruqola_test(translatetextjobtest.cpp) add_ruqola_test(listmessagesmodeltest.cpp) add_ruqola_test(listmessagesmodelfilterproxymodeltest.cpp) add_ruqola_test(autotranslatelanguagesmodeltest.cpp) add_ruqola_test(autotranslatelanguagetest.cpp) add_ruqola_test(autotranslatelanguagestest.cpp) add_ruqola_test(messagetranslationtest.cpp) add_ruqola_test(accountroomsettingstest.cpp) add_ruqola_test(messagecachetest.cpp) add_ruqola_test(commandtest.cpp) add_ruqola_test(commandstest.cpp) diff --git a/src/core/autotests/accountschannelsmodeltest.cpp b/src/core/autotests/accountschannelsmodeltest.cpp new file mode 100644 index 00000000..72bf1738 --- /dev/null +++ b/src/core/autotests/accountschannelsmodeltest.cpp @@ -0,0 +1,88 @@ +/* + Copyright (c) 2020 Olivier de Gaalon + + 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 ) version 3 or, at the discretion of KDE e.V. + ( which shall act as a proxy as in section 14 of the GPLv3 ), 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 "accountschannelsmodeltest.h" + +#include "accountmanager.h" +#include "model/accountschannelsmodel.h" +#include "model/roomfilterproxymodel.h" +#include "rocketchataccount.h" +#include "ruqola.h" + +#include +#include +#include + +QTEST_MAIN(AccountsChannelsModelTest) + +AccountsChannelsModelTest::AccountsChannelsModelTest(QObject *parent) + : QObject(parent) +{ +} + +void AccountsChannelsModelTest::initTestCase() +{ + QStandardPaths::setTestModeEnabled(true); +} + +void AccountsChannelsModelTest::accountsAndChannels() +{ + AccountsChannelsModel model; + QAbstractItemModelTester tester(&model, QAbstractItemModelTester::FailureReportingMode::QtTest); + + QCOMPARE(model.rowCount(), 1); // Ruqola creates one account by default + QCOMPARE(model.data(model.index(1, 0)).toString(), QStringLiteral("")); + QCOMPARE(model.rowCount(model.index(1, 0)), 0); + + const auto newAcctName = QStringLiteral("Test Account"); + const auto acct = new RocketChatAccount; + Ruqola::self()->accountManager()->addAccount(acct); + const auto newAcctIndex = model.index(1, 0); + QCOMPARE(model.rowCount(), 2); + QVERIFY(!model.hasChildren(newAcctIndex)); + QCOMPARE(model.data(newAcctIndex).toString(), QString()); + acct->setAccountName(newAcctName); + QCOMPARE(model.data(newAcctIndex).toString(), newAcctName); + + Ruqola::self()->setCurrentAccount(newAcctName); + + const auto newRoomId = QStringLiteral("RoomId"); + const auto newRoomName = QStringLiteral("Room Name"); + acct->roomModel()->addRoom(newRoomId, newRoomName); + QCOMPARE(model.rowCount(newAcctIndex), 0); // Room not yet open + + // FIXME: RoomModel should probably emit dataChanged to allow the sort/filter to update + acct->roomModel()->findRoom(newRoomId)->setOpen(true); + QEXPECT_FAIL("", "RoomModel missing dataChanged", Continue); + QCOMPARE(model.rowCount(newAcctIndex), 1); + // ... workaround for the above + acct->roomFilterProxyModel()->invalidate(); + // ... and try again + QCOMPARE(model.rowCount(newAcctIndex), 1); + + const auto newRoomIndex = model.index(0, 0, newAcctIndex); + QVERIFY(!model.hasChildren(newRoomIndex)); + QCOMPARE(model.data(newRoomIndex).toString(), newRoomName); + + // TODO: RoomsModel currently has no API for removing rooms + + Ruqola::self()->accountManager()->removeAccount(newAcctName); + QCOMPARE(model.rowCount(), 1); // Only the default account remains +} diff --git a/src/core/autotests/accountschannelsmodeltest.h b/src/core/autotests/accountschannelsmodeltest.h new file mode 100644 index 00000000..49c5304c --- /dev/null +++ b/src/core/autotests/accountschannelsmodeltest.h @@ -0,0 +1,37 @@ +/* + Copyright (c) 2020 Olivier de Gaalon + + 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 ) version 3 or, at the discretion of KDE e.V. + ( which shall act as a proxy as in section 14 of the GPLv3 ), 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 ACCOUNTSCHANNELSMODELTEST_H +#define ACCOUNTSCHANNELSMODELTEST_H + +#include + +class AccountsChannelsModelTest : public QObject +{ + Q_OBJECT +public: + explicit AccountsChannelsModelTest(QObject *parent = nullptr); + +private Q_SLOTS: + void initTestCase(); + void accountsAndChannels(); +}; + +#endif // ACCOUNTSCHANNELSMODELTEST_H diff --git a/src/core/model/accountschannelsmodel.cpp b/src/core/model/accountschannelsmodel.cpp new file mode 100644 index 00000000..5438ea91 --- /dev/null +++ b/src/core/model/accountschannelsmodel.cpp @@ -0,0 +1,181 @@ +/* + Copyright (c) 2020 Olivier de Gaalon + + 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 ) version 3 or, at the discretion of KDE e.V. + ( which shall act as a proxy as in section 14 of the GPLv3 ), 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 "accountschannelsmodel.h" + +#include "accountmanager.h" +#include "ruqola.h" +#include "rocketchataccount.h" +#include "roomfilterproxymodel.h" +#include "rocketchataccountmodel.h" +#include "rocketchataccountfilterproxymodel.h" + +AccountsChannelsModel::AccountsChannelsModel(QObject *parent) + : QAbstractItemModel(parent) +{ + const auto src = Ruqola::self()->accountManager()->rocketChatAccountModel(); + const auto acctsProxy = Ruqola::self()->accountManager()->rocketChatAccountProxyModel(); + + auto roomsModel = [src, acctsProxy](int i) { + const auto acctIndex = acctsProxy->mapToSource(acctsProxy->index(i, 0)).row(); + return src->account(acctIndex)->roomFilterProxyModel(); + }; + + auto mapRoomsModel = [roomsModel, acctsProxy, this](int roomsModelIndex) { + auto rooms = roomsModel(roomsModelIndex); + mapModelToIndex(rooms, [roomsModel, acctsProxy, rooms, this] { + for (int i = 0, count = acctsProxy->rowCount(); i < count; ++i) + if (roomsModel(i) == rooms) + return index(i, 0); + return QModelIndex(); + }); + }; + + connect(acctsProxy, &QAbstractItemModel::rowsInserted, this, + [mapRoomsModel](const QModelIndex &, int first, int last) { + for (int i = first; i <= last; ++i) + mapRoomsModel(i); + }); + + connect(acctsProxy, &QAbstractItemModel::rowsAboutToBeRemoved, this, + [roomsModel, this](const QModelIndex &, int first, int last) { + for (int i = first; i <= last; ++i) + unproxyModel(roomsModel(i)); + }); + + connect(acctsProxy, &QAbstractItemModel::modelReset, this, + [mapRoomsModel, acctsProxy, this]() { + while (!mProxied.isEmpty()) + unproxyModel(mProxied.begin()->model); + for (int i = 0, count = acctsProxy->rowCount(); i < count; ++i) + mapRoomsModel(i); + }); + + + mapModelToIndex(acctsProxy, []{ return QModelIndex(); }); + for (int i = 0, count = acctsProxy->rowCount(); i < count; ++i) + mapRoomsModel(i); +} + +QModelIndex AccountsChannelsModel::index(int row, int column, const QModelIndex &parent) const +{ + if (auto model = rootModel(parent)) + return createIndex(row, column, model); + return {}; +} + +QModelIndex AccountsChannelsModel::parent(const QModelIndex &child) const +{ + if (!child.isValid()) + return {}; + + if (auto model = static_cast(child.internalPointer())) + return modelRoot(model); + + return {}; +} + +int AccountsChannelsModel::rowCount(const QModelIndex &parent) const +{ + if (auto model = rootModel(parent)) + return model->rowCount(); + return 0; +} + +int AccountsChannelsModel::columnCount(const QModelIndex &) const +{ + return 1; +} + +QVariant AccountsChannelsModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid()) + return {}; + + const auto model = static_cast(index.internalPointer()); + if (!model) + return {}; + + return model->index(index.row(), index.column()).data(role); +} + +QModelIndex AccountsChannelsModel::modelRoot(QAbstractItemModel *model) const +{ + const auto find = [model](const ProxyIndex &i){ return i.model == model; }; + const auto it = std::find_if(mProxied.begin(), mProxied.end(), find); + return (it == mProxied.end()) ? QModelIndex() : it->root(); +} + +QAbstractItemModel *AccountsChannelsModel::rootModel(const QModelIndex &root) const +{ + const auto find = [&root](const ProxyIndex &i){ return i.root() == root; }; + const auto it = std::find_if(mProxied.begin(), mProxied.end(), find); + return (it == mProxied.end()) ? nullptr : it->model; +} + +void AccountsChannelsModel::mapModelToIndex(QAbstractItemModel *model, const std::function &root) +{ + connect(model, &QAbstractItemModel::rowsAboutToBeInserted, this, + [this, model](const QModelIndex &parent, int first, int last) { + Q_ASSERT(!parent.isValid()); + beginInsertRows(modelRoot(model), first, last); + }); + connect(model, &QAbstractItemModel::rowsInserted, this, &AccountsChannelsModel::endInsertRows); + + connect(model, &QAbstractItemModel::rowsAboutToBeRemoved, this, + [this, model](const QModelIndex &parent, int first, int last) { + Q_ASSERT(!parent.isValid()); + beginRemoveRows(modelRoot(model), first, last); + }); + connect(model, &QAbstractItemModel::rowsRemoved, this, &AccountsChannelsModel::endRemoveRows); + + connect(model, &QAbstractItemModel::rowsAboutToBeMoved, this, + [this, model](const QModelIndex &src, int sf, int sl, const QModelIndex &dst, int df) { + Q_ASSERT(!src.isValid() && !dst.isValid()); + const auto idx = modelRoot(model); + beginMoveRows(idx, sf, sl, idx, df); + }); + connect(model, &QAbstractItemModel::rowsMoved, this, &AccountsChannelsModel::endMoveRows); + + connect(model, &QAbstractItemModel::modelAboutToBeReset, this, &AccountsChannelsModel::beginResetModel); + connect(model, &QAbstractItemModel::modelReset, this, &AccountsChannelsModel::endResetModel); + + connect(model, &QAbstractItemModel::layoutAboutToBeChanged, this, &AccountsChannelsModel::layoutAboutToBeChanged); + connect(model, &QAbstractItemModel::layoutChanged, this, &AccountsChannelsModel::layoutChanged); + + connect(model, &QAbstractItemModel::dataChanged, this, + [this, model](const QModelIndex &tl, const QModelIndex &br) { + const auto parent = modelRoot(model); + emit dataChanged(index(tl.row(), tl.column(), parent), index(br.row(), br.column(), parent)); + }); + + mProxied.append({model, root}); +} + +void AccountsChannelsModel::unproxyModel(QAbstractItemModel *model) +{ + const auto find = [model](const ProxyIndex &i){ return i.model == model; }; + const auto it = std::find_if(mProxied.begin(), mProxied.end(), find); + if (it != mProxied.end()) + { + model->disconnect(this); + mProxied.erase(it); + } +} diff --git a/src/core/model/accountschannelsmodel.h b/src/core/model/accountschannelsmodel.h new file mode 100644 index 00000000..09acf44b --- /dev/null +++ b/src/core/model/accountschannelsmodel.h @@ -0,0 +1,57 @@ +/* + Copyright (c) 2020 Olivier de Gaalon + + 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 ) version 3 or, at the discretion of KDE e.V. + ( which shall act as a proxy as in section 14 of the GPLv3 ), 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 ACCOUNTSCHANNELSMODEL_H +#define ACCOUNTSCHANNELSMODEL_H + +#include +#include + +#include "libruqolacore_export.h" + +class RocketChatAccount; +class RoomFilterProxyModel; + +class LIBRUQOLACORE_EXPORT AccountsChannelsModel : public QAbstractItemModel +{ +public: + explicit AccountsChannelsModel(QObject *parent = nullptr); + + QModelIndex index(int row, int column, const QModelIndex &parent = {}) const override; + QModelIndex parent(const QModelIndex &child) const override; + int rowCount(const QModelIndex &parent = {}) const override; + int columnCount(const QModelIndex &parent = {}) const override; + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + +private: + QModelIndex modelRoot(QAbstractItemModel *model) const; + QAbstractItemModel *rootModel(const QModelIndex &root) const; + void mapModelToIndex(QAbstractItemModel *model, const std::function &root); + void unproxyModel(QAbstractItemModel *model); + + struct ProxyIndex + { + QAbstractItemModel *model; + std::function root; + }; + QVector mProxied; +}; + +#endif // ACCOUNTSCHANNELSMODEL_H diff --git a/src/core/model/rocketchataccountmodel.cpp b/src/core/model/rocketchataccountmodel.cpp index c043d34c..b59813d5 100644 --- a/src/core/model/rocketchataccountmodel.cpp +++ b/src/core/model/rocketchataccountmodel.cpp @@ -1,161 +1,162 @@ /* Copyright (c) 2018-2020 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 ) version 3 or, at the discretion of KDE e.V. ( which shall act as a proxy as in section 14 of the GPLv3 ), 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 "rocketchataccountmodel.h" #include "rocketchataccount.h" #include "ruqolaserverconfig.h" #include "ruqola_debug.h" RocketChatAccountModel::RocketChatAccountModel(QObject *parent) : QAbstractListModel(parent) { } RocketChatAccountModel::~RocketChatAccountModel() { qDeleteAll(mRocketChatAccount); } void RocketChatAccountModel::clear() { if (!mRocketChatAccount.isEmpty()) { beginRemoveRows(QModelIndex(), 0, rowCount() - 1); qDeleteAll(mRocketChatAccount); mRocketChatAccount.clear(); endRemoveRows(); } Q_EMIT accountNumberChanged(); } void RocketChatAccountModel::setAccounts(const QVector &accounts) { if (rowCount() != 0) { beginRemoveRows(QModelIndex(), 0, mRocketChatAccount.count() - 1); mRocketChatAccount.clear(); endRemoveRows(); } if (!accounts.isEmpty()) { beginInsertRows(QModelIndex(), 0, accounts.count() - 1); mRocketChatAccount = accounts; endInsertRows(); } Q_EMIT accountNumberChanged(); } RocketChatAccount *RocketChatAccountModel::account(const QString &accountName) const { if (mRocketChatAccount.isEmpty()) { qCWarning(RUQOLA_LOG) << " Empty account"; return nullptr; } for (int i = 0, total = mRocketChatAccount.count(); i < total; ++i) { RocketChatAccount *model = mRocketChatAccount.at(i); if (model->accountName() == accountName) { return model; } } return nullptr; } int RocketChatAccountModel::accountNumber() const { return mRocketChatAccount.count(); } QStringList RocketChatAccountModel::accountsName() const { QStringList accounts; for (int i = 0, total = mRocketChatAccount.count(); i < total; ++i) { RocketChatAccount *model = mRocketChatAccount.at(i); accounts << model->accountName(); } return accounts; } RocketChatAccount *RocketChatAccountModel::account(int index) const { if (mRocketChatAccount.isEmpty() || (index > mRocketChatAccount.count() - 1)) { qCWarning(RUQOLA_LOG) << " Empty account"; return nullptr; } return mRocketChatAccount.at(index); } int RocketChatAccountModel::rowCount(const QModelIndex &parent) const { Q_UNUSED(parent) return mRocketChatAccount.count(); } QVariant RocketChatAccountModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) { qCWarning(RUQOLA_LOG) << "ERROR: invalid index"; return {}; } const int idx = index.row(); RocketChatAccount *account = mRocketChatAccount.at(idx); switch (role) { + case Qt::DisplayRole: case Name: return account->accountName(); case SiteUrl: return account->ruqolaServerConfig()->siteUrl(); case UserName: return account->userName(); } //Add icon ??? return {}; } void RocketChatAccountModel::insertAccount(RocketChatAccount *account) { //Verify that we have it ? const int accountCount = mRocketChatAccount.count(); beginInsertRows(QModelIndex(), accountCount, accountCount); mRocketChatAccount.append(account); endInsertRows(); Q_EMIT accountNumberChanged(); } void RocketChatAccountModel::removeAccount(const QString &name) { //qDebug() << " void RocketChatAccountModel::removeAccount(const QString &name)"<accountName() == name) { beginRemoveRows(QModelIndex(), i, i); RocketChatAccount *account = mRocketChatAccount.takeAt(i); account->removeSettings(); delete account; endRemoveRows(); Q_EMIT accountNumberChanged(); break; } } } QHash RocketChatAccountModel::roleNames() const { QHash roles; roles[Name] = QByteArrayLiteral("name"); roles[SiteUrl] = QByteArrayLiteral("siteurl"); roles[UserName] = QByteArrayLiteral("username"); return roles; }