From 6a61d3a127db1e253821bfb2ebb7f433bd534c4a Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Sat, 31 Mar 2018 14:55:41 +0900 Subject: Make and install CMake config package; provide examples/CMakeLists.txt using it --- examples/CMakeLists.txt | 69 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 examples/CMakeLists.txt (limited to 'examples') diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt new file mode 100644 index 00000000..49e0089a --- /dev/null +++ b/examples/CMakeLists.txt @@ -0,0 +1,69 @@ +cmake_minimum_required(VERSION 3.1) + +# This CMakeLists file assumes that the library is installed to CMAKE_INSTALL_PREFIX +# and ignores the in-tree library code. You can use this to start work on your own client. + +project(qmc-example CXX) + +include(CheckCXXCompilerFlag) +if (NOT WIN32) + include(GNUInstallDirs) +endif(NOT WIN32) + +# Find includes in corresponding build directories +set(CMAKE_INCLUDE_CURRENT_DIR ON) +# Instruct CMake to run moc automatically when needed. +set(CMAKE_AUTOMOC ON) + +# Set a default build type if none was specified +if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + message(STATUS "Setting build type to 'Debug' as none was specified") + set(CMAKE_BUILD_TYPE Debug CACHE STRING "Choose the type of build" FORCE) + # Set the possible values of build type for cmake-gui + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" + "MinSizeRel" "RelWithDebInfo") +endif() + +if (NOT CMAKE_INSTALL_LIBDIR) + set(CMAKE_INSTALL_LIBDIR ".") +endif() + +if (NOT CMAKE_INSTALL_BINDIR) + set(CMAKE_INSTALL_BINDIR ".") +endif() + +if (NOT CMAKE_INSTALL_INCLUDEDIR) + set(CMAKE_INSTALL_INCLUDEDIR "include") +endif() + +set(CMAKE_CXX_STANDARD 14) + +foreach (FLAG all "" pedantic extra error=return-type no-unused-parameter no-gnu-zero-variadic-macro-arguments) + CHECK_CXX_COMPILER_FLAG("-W${FLAG}" WARN_${FLAG}_SUPPORTED) + if ( WARN_${FLAG}_SUPPORTED AND NOT CMAKE_CXX_FLAGS MATCHES "(^| )-W?${FLAG}($| )") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -W${FLAG}") + endif () +endforeach () + +find_package(Qt5 5.6 REQUIRED Network Gui) +get_filename_component(Qt5_Prefix "${Qt5_DIR}/../../../.." ABSOLUTE) + +find_package(QMatrixClient REQUIRED) +get_filename_component(QMC_Prefix "${QMatrixClient_DIR}/../.." ABSOLUTE) + +message( STATUS "qmc-example configuration:" ) +if (CMAKE_BUILD_TYPE) + message( STATUS " Build type: ${CMAKE_BUILD_TYPE}") +endif(CMAKE_BUILD_TYPE) +message( STATUS " Compiler: ${CMAKE_CXX_COMPILER_ID} ${CMAKE_CXX_COMPILER_VERSION}" ) +message( STATUS " Qt: ${Qt5_VERSION} at ${Qt5_Prefix}" ) +message( STATUS " QMatrixClient: ${QMatrixClient_VERSION} at ${QMC_Prefix}" ) + +set(example_SRCS qmc-example.cpp) + +add_executable(qmc-example ${example_SRCS}) +target_link_libraries(qmc-example Qt5::Core QMatrixClient) + +# Installation + +install (TARGETS qmc-example RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) -- cgit v1.2.3 From 9b856f28f2745a6d1f0425b8e7ac9c91119f3f36 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Sun, 1 Apr 2018 14:19:56 +0900 Subject: Pass actual changes with Connection::directChatsListChanged() Also: provide Connection::directChats() to get the whole direct chats map. --- examples/qmc-example.cpp | 19 +++++++++--- lib/connection.cpp | 81 ++++++++++++++++++++++++++++++++++-------------- lib/connection.h | 11 +++++-- 3 files changed, 80 insertions(+), 31 deletions(-) (limited to 'examples') diff --git a/examples/qmc-example.cpp b/examples/qmc-example.cpp index 23a1bff1..7226c060 100644 --- a/examples/qmc-example.cpp +++ b/examples/qmc-example.cpp @@ -25,9 +25,11 @@ class QMCTest : public QObject void doTests(); void addAndRemoveTag(); void sendAndRedact(); - void checkRedactionOutcome(QString evtIdToRedact, RoomEventsRange events); + void checkRedactionOutcome(QString evtIdToRedact, + RoomEventsRange events); void markDirectChat(); - void checkDirectChatOutcome(); + void checkDirectChatOutcome( + const Connection::DirectChatsMap& added); void finalize(); private: @@ -211,11 +213,11 @@ void QMCTest::checkRedactionOutcome(QString evtIdToRedact, void QMCTest::markDirectChat() { - if (c->isDirectChat(targetRoom->id())) + if (targetRoom->directChatUsers().contains(c->user())) { cout << "Warning: the room is already a direct chat," " only unmarking will be tested" << endl; - checkDirectChatOutcome(); + checkDirectChatOutcome({{ c->user(), targetRoom->id() }}); } // Connect first because the signal is emitted synchronously. connect(c.data(), &Connection::directChatsListChanged, @@ -224,11 +226,18 @@ void QMCTest::markDirectChat() c->addToDirectChats(targetRoom, c->user()); } -void QMCTest::checkDirectChatOutcome() +void QMCTest::checkDirectChatOutcome(const Connection::DirectChatsMap& added) { disconnect(c.data(), &Connection::directChatsListChanged, nullptr, nullptr); if (!c->isDirectChat(targetRoom->id())) { + cout << "The room has not been marked as a direct chat" << endl; + QMC_CHECK("Direct chat test", false); + return; + } + if (!added.contains(c->user(), targetRoom->id())) + { + cout << "The room has not been listed in new direct chats" << endl; QMC_CHECK("Direct chat test", false); return; } diff --git a/lib/connection.cpp b/lib/connection.cpp index 2d7235b9..a5c74d88 100644 --- a/lib/connection.cpp +++ b/lib/connection.cpp @@ -47,7 +47,22 @@ using namespace QMatrixClient; -using DirectChatsMap = QMultiHash; +// This is very much Qt-specific; STL iterators don't have key() and value() +template +HashT erase_if(HashT& hashMap, Pred pred) +{ + HashT removals; + for (auto it = hashMap.begin(); it != hashMap.end();) + { + if (pred(it)) + { + removals.insert(it.key(), it.value()); + it = hashMap.erase(it); + } else + ++it; + } + return removals; +} class Connection::Private { @@ -80,7 +95,8 @@ class Connection::Private void connectWithToken(const QString& user, const QString& accessToken, const QString& deviceId); - void broadcastDirectChatUpdates(); + void broadcastDirectChatUpdates(const DirectChatsMap& additions, + const DirectChatsMap& removals); }; Connection::Connection(const QUrl& server, QObject* parent) @@ -290,20 +306,32 @@ void Connection::onSyncSuccess(SyncData &&data) { { if (accountEvent->type() == EventType::DirectChat) { - DirectChatsMap newDirectChats; const auto* event = static_cast(accountEvent.get()); auto usersToDCs = event->usersToDirectChats(); + DirectChatsMap removals = + erase_if(d->directChats, [&usersToDCs] (auto it) { + return !usersToDCs.contains(it.key()->id(), it.value()); + }); + if (MAIN().isDebugEnabled()) + for (auto it = removals.begin(); it != removals.end(); ++it) + qCDebug(MAIN) << it.value() + << "is no more a direct chat with" << it.key()->id(); + + DirectChatsMap additions; for (auto it = usersToDCs.begin(); it != usersToDCs.end(); ++it) { - newDirectChats.insert(user(it.key()), it.value()); - qCDebug(MAIN) << "Marked room" << it.value() - << "as a direct chat with" << it.key(); - } - if (newDirectChats != d->directChats) - { - d->directChats = newDirectChats; - emit directChatsListChanged(); + const auto* u = user(it.key()); + if (!d->directChats.contains(u, it.value())) + { + additions.insert(u, it.value()); + d->directChats.insert(u, it.value()); + qCDebug(MAIN) << "Marked room" << it.value() + << "as a direct chat with" << u->id(); + } } + if (!additions.isEmpty() || !removals.isEmpty()) + emit directChatsListChanged(additions, removals); + continue; } d->accountData[accountEvent->jsonType()] = @@ -654,7 +682,12 @@ QVector Connection::roomsWithTag(const QString& tagName) const return rooms; } -QJsonObject toJson(const DirectChatsMap& directChats) +Connection::DirectChatsMap Connection::directChats() const +{ + return d->directChats; +} + +QJsonObject toJson(const Connection::DirectChatsMap& directChats) { QJsonObject json; for (auto it = directChats.keyBegin(); it != directChats.keyEnd(); ++it) @@ -662,11 +695,12 @@ QJsonObject toJson(const DirectChatsMap& directChats) return json; } -void Connection::Private::broadcastDirectChatUpdates() +void Connection::Private::broadcastDirectChatUpdates(const DirectChatsMap& additions, + const DirectChatsMap& removals) { q->callApi(userId, QStringLiteral("m.direct"), toJson(directChats)); - emit q->directChatsListChanged(); + emit q->directChatsListChanged(additions, removals); } void Connection::addToDirectChats(const Room* room, const User* user) @@ -675,7 +709,8 @@ void Connection::addToDirectChats(const Room* room, const User* user) if (d->directChats.contains(user, room->id())) return; d->directChats.insert(user, room->id()); - d->broadcastDirectChatUpdates(); + DirectChatsMap additions { { user, room->id() } }; + d->broadcastDirectChatUpdates(additions, {}); } void Connection::removeFromDirectChats(const QString& roomId, const User* user) @@ -684,17 +719,17 @@ void Connection::removeFromDirectChats(const QString& roomId, const User* user) if ((user != nullptr && !d->directChats.contains(user, roomId)) || d->directChats.key(roomId) == nullptr) return; + + DirectChatsMap removals; if (user != nullptr) + { + removals.insert(user, roomId); d->directChats.remove(user, roomId); + } else - for (auto it = d->directChats.begin(); it != d->directChats.end();) - { - if (it.value() == roomId) - it = d->directChats.erase(it); - else - ++it; - } - d->broadcastDirectChatUpdates(); + removals = erase_if(d->directChats, + [&roomId] (auto it) { return it.value() == roomId; }); + d->broadcastDirectChatUpdates({}, removals); } bool Connection::isDirectChat(const QString& roomId) const diff --git a/lib/connection.h b/lib/connection.h index c6d543ec..016c7e3c 100644 --- a/lib/connection.h +++ b/lib/connection.h @@ -64,6 +64,8 @@ namespace QMatrixClient using user_factory_t = std::function; + using DirectChatsMap = QMultiHash; + enum RoomVisibility { PublishRoom, UnpublishRoom }; // FIXME: Should go inside CreateRoomJob explicit Connection(QObject* parent = nullptr); @@ -114,16 +116,18 @@ namespace QMatrixClient /** Check whether the room id corresponds to a direct chat */ bool isDirectChat(const QString& roomId) const; + /** Get the whole map from users to direct chat rooms */ + DirectChatsMap directChats() const; + /** Retrieve the list of users the room is a direct chat with * @return The list of users for which this room is marked as * a direct chat; an empty list if the room is not a direct chat */ QList directChatUsers(const Room* room) const; + /** Get the full list of users known to this account */ QMap users() const; - // FIXME: Convert Q_INVOKABLEs to Q_PROPERTIES - // (breaks back-compatibility) QUrl homeserver() const; Q_INVOKABLE Room* room(const QString& roomId, JoinStates states = JoinState::Invite|JoinState::Join) const; @@ -421,7 +425,8 @@ namespace QMatrixClient * to direct chat rooms is changed (because of either local updates * or a different list arrived from the server). */ - void directChatsListChanged(); + void directChatsListChanged(DirectChatsMap additions, + DirectChatsMap removals); void cacheStateChanged(); -- cgit v1.2.3 From cf5b5e74b20de2a0579de6a176d76f96bbe87603 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Thu, 5 Apr 2018 20:41:19 +0900 Subject: qmc-example: fix false negatives due to a missing return --- examples/qmc-example.cpp | 1 + 1 file changed, 1 insertion(+) (limited to 'examples') diff --git a/examples/qmc-example.cpp b/examples/qmc-example.cpp index 7226c060..e2ec1d12 100644 --- a/examples/qmc-example.cpp +++ b/examples/qmc-example.cpp @@ -218,6 +218,7 @@ void QMCTest::markDirectChat() cout << "Warning: the room is already a direct chat," " only unmarking will be tested" << endl; checkDirectChatOutcome({{ c->user(), targetRoom->id() }}); + return; } // Connect first because the signal is emitted synchronously. connect(c.data(), &Connection::directChatsListChanged, -- cgit v1.2.3