From c9f99699761e97b16bdb44f584f07baec5488b47 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Tue, 24 Sep 2019 19:56:31 +0900 Subject: Room: cleanup --- lib/room.cpp | 35 ++++++++++++++++++----------------- lib/room.h | 2 +- 2 files changed, 19 insertions(+), 18 deletions(-) diff --git a/lib/room.cpp b/lib/room.cpp index 2f697589..e2195193 100644 --- a/lib/room.cpp +++ b/lib/room.cpp @@ -327,7 +327,7 @@ public: * content passed in \p newMessage. * \return true if the event has been found and replaced; false otherwise */ - bool processReplacement(const RoomMessageEvent& newMessage); + bool processReplacement(const RoomMessageEvent& newEvent); void setTags(TagsMap newTags); @@ -879,10 +879,10 @@ std::pair validatedTag(QString name) if (name.contains('.')) return { false, name }; - qWarning(MAIN) << "The tag" << name + qCWarning(MAIN) << "The tag" << name << "doesn't follow the CS API conventions"; name.prepend("u."); - qWarning(MAIN) << "Using " << name << "instead"; + qCWarning(MAIN) << "Using " << name << "instead"; return { true, name }; } @@ -916,7 +916,7 @@ void Room::removeTag(const QString& name) } else if (!name.startsWith("u.")) removeTag("u." + name); else - qWarning(MAIN) << "Tag" << name << "on room" << objectName() + qCWarning(MAIN) << "Tag" << name << "on room" << objectName() << "not found, nothing to remove"; } @@ -971,7 +971,7 @@ Room::Private::getEventWithFile(const QString& eventId) const if (event->hasFileContent()) return event; } - qWarning() << "No files to download in event" << eventId; + qCWarning(MAIN) << "No files to download in event" << eventId; return nullptr; } @@ -1014,7 +1014,7 @@ QUrl Room::urlToThumbnail(const QString& eventId) const thumbnail->url, thumbnail->imageSize); } - qDebug() << "Event" << eventId << "has no thumbnail"; + qCDebug(MAIN) << "Event" << eventId << "has no thumbnail"; return {}; } @@ -1149,18 +1149,18 @@ QString Room::decryptMessage(QJsonObject personalCipherObject, try { decrypted = session->decrypt(&preKeyMessage); } catch (std::runtime_error& e) { - qWarning(EVENTS) << "Decrypt failed:" << e.what(); + qCWarning(EVENTS) << "Decrypt failed:" << e.what(); } } else if (type == 1) { Message message { body }; if (!session->matches(&preKeyMessage, senderKey)) { - qWarning(EVENTS) << "Invalid encrypted message"; + qCWarning(EVENTS) << "Invalid encrypted message"; } try { decrypted = session->decrypt(&message); } catch (std::runtime_error& e) { - qWarning(EVENTS) << "Decrypt failed:" << e.what(); + qCWarning(EVENTS) << "Decrypt failed:" << e.what(); } } @@ -1443,11 +1443,12 @@ QString Room::Private::doSendEvent(const RoomEvent* pEvent) Room::connect(call, &BaseJob::sentRequest, q, [this, txnId] { auto it = q->findPendingEvent(txnId); if (it == unsyncedEvents.end()) { - qWarning(EVENTS) << "Pending event for transaction" << txnId + qCWarning(EVENTS) << "Pending event for transaction" << txnId << "not found - got synced so soon?"; return; } it->setDeparted(); + qCDebug(EVENTS) << "Event txn" << txnId << "has departed"; emit q->pendingEventChanged(int(it - unsyncedEvents.begin())); }); Room::connect(call, &BaseJob::failure, q, @@ -1457,7 +1458,7 @@ QString Room::Private::doSendEvent(const RoomEvent* pEvent) emit q->messageSent(txnId, call->eventId()); auto it = q->findPendingEvent(txnId); if (it == unsyncedEvents.end()) { - qDebug(EVENTS) << "Pending event for transaction" << txnId + qCDebug(EVENTS) << "Pending event for transaction" << txnId << "already merged"; return; } @@ -1489,7 +1490,7 @@ QString Room::retryMessage(const QString& txnId) { const auto it = findPendingEvent(txnId); Q_ASSERT(it != d->unsyncedEvents.end()); - qDebug(EVENTS) << "Retrying transaction" << txnId; + qCDebug(EVENTS) << "Retrying transaction" << txnId; const auto& transferIt = d->fileTransfers.find(txnId); if (transferIt != d->fileTransfers.end()) { Q_ASSERT(transferIt->isUpload); @@ -1527,7 +1528,7 @@ void Room::discardMessage(const QString& txnId) return evt->transactionId() == txnId; }); Q_ASSERT(it != d->unsyncedEvents.end()); - qDebug(EVENTS) << "Discarding transaction" << txnId; + qCDebug(EVENTS) << "Discarding transaction" << txnId; const auto& transferIt = d->fileTransfers.find(txnId); if (transferIt != d->fileTransfers.end()) { Q_ASSERT(transferIt->isUpload); @@ -2192,7 +2193,7 @@ Room::Changes Room::Private::addNewMessageEvents(RoomEvents&& events) emit q->pendingEventChanged(pendingEvtIdx); } emit q->pendingEventAboutToMerge(nextPendingEvt, pendingEvtIdx); - qDebug(MESSAGES) << "Merging pending event from transaction" + qCDebug(MESSAGES) << "Merging pending event from transaction" << nextPendingEvt->transactionId() << "into" << nextPendingEvt->id(); auto transfer = fileTransfers.take(nextPendingEvt->transactionId()); @@ -2323,13 +2324,13 @@ Room::Changes Room::processStateEvent(const RoomEvent& e) , [this,oldStateEvent] (const RoomAliasesEvent& ae) { // clang-format on if (ae.aliases().isEmpty()) { - qDebug(STATE).noquote() + qCDebug(STATE).noquote() << ae.stateKey() << "no more has aliases for room" << objectName(); d->aliasServers.remove(ae.stateKey()); } else { d->aliasServers.insert(ae.stateKey()); - qDebug(STATE).nospace().noquote() + qCDebug(STATE).nospace().noquote() << "New server with aliases for room " << objectName() << ": " << ae.stateKey(); } @@ -2665,7 +2666,7 @@ void Room::Private::updateDisplayname() if (swappedName != displayname) { emit q->displaynameAboutToChange(q); swap(displayname, swappedName); - qDebug(MAIN) << q->objectName() << "has changed display name from" + qCDebug(MAIN) << q->objectName() << "has changed display name from" << swappedName << "to" << displayname; emit q->displaynameChanged(q, swappedName); } diff --git a/lib/room.h b/lib/room.h index 9113654b..ac169550 100644 --- a/lib/room.h +++ b/lib/room.h @@ -557,7 +557,7 @@ signals: * Aside from all changes in the room state * @param changes a set of flags describing what changes occurred * upon the last sync - * \sa StateChange + * \sa Changes */ void changed(Changes changes); /** -- cgit v1.2.3 From 8f1bfa5c5764e399259c13b260661647363cf5d9 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Sun, 29 Sep 2019 17:46:28 +0900 Subject: Fix formatting weirdness Alas, clang-format is not terrifically good around [[attributes]] --- lib/room.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/room.h b/lib/room.h index ac169550..698f74c8 100644 --- a/lib/room.h +++ b/lib/room.h @@ -187,8 +187,8 @@ public: Q_INVOKABLE QList users() const; QStringList memberNames() const; - [[deprecated("Use joinedCount(), invitedCount(), totalMemberCount()")]] int - memberCount() const; + [[deprecated("Use joinedCount(), invitedCount(), totalMemberCount()")]] + int memberCount() const; int timelineSize() const; bool usesEncryption() const; RoomEventPtr decryptMessage(EncryptedEvent* encryptedEvent); -- cgit v1.2.3 From be53652eb30edd6e096c172a97609070d1e74746 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Sun, 29 Sep 2019 17:50:28 +0900 Subject: Settings: childGroups() should merge legacy and current groups; QMC->QTNT in macros Because otherwise migrating one group to the current location leads to all other groups suddenly disappearing from Settings view. --- lib/networksettings.cpp | 6 +++--- lib/networksettings.h | 6 +++--- lib/settings.cpp | 11 +++++++---- lib/settings.h | 10 +++++----- 4 files changed, 18 insertions(+), 15 deletions(-) diff --git a/lib/networksettings.cpp b/lib/networksettings.cpp index cb071483..40ecba11 100644 --- a/lib/networksettings.cpp +++ b/lib/networksettings.cpp @@ -26,9 +26,9 @@ void NetworkSettings::setupApplicationProxy() const { proxyType(), proxyHostName(), proxyPort() }); } -QMC_DEFINE_SETTING(NetworkSettings, QNetworkProxy::ProxyType, proxyType, +QTNT_DEFINE_SETTING(NetworkSettings, QNetworkProxy::ProxyType, proxyType, "proxy_type", QNetworkProxy::DefaultProxy, setProxyType) -QMC_DEFINE_SETTING(NetworkSettings, QString, proxyHostName, "proxy_hostname", +QTNT_DEFINE_SETTING(NetworkSettings, QString, proxyHostName, "proxy_hostname", {}, setProxyHostName) -QMC_DEFINE_SETTING(NetworkSettings, quint16, proxyPort, "proxy_port", -1, +QTNT_DEFINE_SETTING(NetworkSettings, quint16, proxyPort, "proxy_port", -1, setProxyPort) diff --git a/lib/networksettings.h b/lib/networksettings.h index a6a13f93..2399cf5f 100644 --- a/lib/networksettings.h +++ b/lib/networksettings.h @@ -27,9 +27,9 @@ Q_DECLARE_METATYPE(QNetworkProxy::ProxyType) namespace Quotient { class NetworkSettings : public SettingsGroup { Q_OBJECT - QMC_DECLARE_SETTING(QNetworkProxy::ProxyType, proxyType, setProxyType) - QMC_DECLARE_SETTING(QString, proxyHostName, setProxyHostName) - QMC_DECLARE_SETTING(quint16, proxyPort, setProxyPort) + QTNT_DECLARE_SETTING(QNetworkProxy::ProxyType, proxyType, setProxyType) + QTNT_DECLARE_SETTING(QString, proxyHostName, setProxyHostName) + QTNT_DECLARE_SETTING(quint16, proxyPort, setProxyPort) Q_PROPERTY(QString proxyHost READ proxyHostName WRITE setProxyHostName) public: template diff --git a/lib/settings.cpp b/lib/settings.cpp index 3bf61605..0349e699 100644 --- a/lib/settings.cpp +++ b/lib/settings.cpp @@ -49,7 +49,10 @@ bool Settings::contains(const QString& key) const QStringList Settings::childGroups() const { auto l = QSettings::childGroups(); - return !l.isEmpty() ? l : legacySettings.childGroups(); + for (const auto& g: legacySettings.childGroups()) + if (!l.contains(g)) + l.push_back(g); + return l; } void SettingsGroup::setValue(const QString& key, const QVariant& value) @@ -88,11 +91,11 @@ void SettingsGroup::remove(const QString& key) Settings::remove(fullKey); } -QMC_DEFINE_SETTING(AccountSettings, QString, deviceId, "device_id", {}, +QTNT_DEFINE_SETTING(AccountSettings, QString, deviceId, "device_id", {}, setDeviceId) -QMC_DEFINE_SETTING(AccountSettings, QString, deviceName, "device_name", {}, +QTNT_DEFINE_SETTING(AccountSettings, QString, deviceName, "device_name", {}, setDeviceName) -QMC_DEFINE_SETTING(AccountSettings, bool, keepLoggedIn, "keep_logged_in", false, +QTNT_DEFINE_SETTING(AccountSettings, bool, keepLoggedIn, "keep_logged_in", false, setKeepLoggedIn) static const auto HomeserverKey = QStringLiteral("homeserver"); diff --git a/lib/settings.h b/lib/settings.h index d317ec2a..79ebba6c 100644 --- a/lib/settings.h +++ b/lib/settings.h @@ -119,7 +119,7 @@ private: QString groupPath; }; -#define QMC_DECLARE_SETTING(type, propname, setter) \ +#define QTNT_DECLARE_SETTING(type, propname, setter) \ Q_PROPERTY(type propname READ propname WRITE setter) \ public: \ type propname() const; \ @@ -127,7 +127,7 @@ public: \ \ private: -#define QMC_DEFINE_SETTING(classname, type, propname, qsettingname, \ +#define QTNT_DEFINE_SETTING(classname, type, propname, qsettingname, \ defaultValue, setter) \ type classname::propname() const \ { \ @@ -142,9 +142,9 @@ private: class AccountSettings : public SettingsGroup { Q_OBJECT Q_PROPERTY(QString userId READ userId CONSTANT) - QMC_DECLARE_SETTING(QString, deviceId, setDeviceId) - QMC_DECLARE_SETTING(QString, deviceName, setDeviceName) - QMC_DECLARE_SETTING(bool, keepLoggedIn, setKeepLoggedIn) + QTNT_DECLARE_SETTING(QString, deviceId, setDeviceId) + QTNT_DECLARE_SETTING(QString, deviceName, setDeviceName) + QTNT_DECLARE_SETTING(bool, keepLoggedIn, setKeepLoggedIn) /** \deprecated \sa setAccessToken */ Q_PROPERTY(QString accessToken READ accessToken WRITE setAccessToken) Q_PROPERTY(QByteArray encryptionAccountPickle READ encryptionAccountPickle -- cgit v1.2.3 From 1e6cbd34eff9817b99e02d2dfee4e0b0c8250794 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Sun, 29 Sep 2019 17:52:11 +0900 Subject: .clang-format: don't add a whitespace before a colon in range-for --- .clang-format | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.clang-format b/.clang-format index e114580e..1869dfe6 100644 --- a/.clang-format +++ b/.clang-format @@ -115,7 +115,7 @@ SortUsingDeclarations: false #SpaceBeforeCtorInitializerColon: true #SpaceBeforeInheritanceColon: true #SpaceBeforeParens: ControlStatements -#SpaceBeforeRangeBasedForLoopColon: true +SpaceBeforeRangeBasedForLoopColon: false #SpaceInEmptyParentheses: false #SpacesBeforeTrailingComments: 1 #SpacesInAngles: false -- cgit v1.2.3 From 36589cd6e4c557da7d694c5973546f29a09dee7c Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Sun, 29 Sep 2019 18:18:39 +0900 Subject: Make connectSingleShot work wherever QObject::connect works Also: doc-comment connectUntil and unify implementation of both functions. --- lib/qt_connection_util.h | 114 +++++++++++++++++++++++++++++++++++------------ 1 file changed, 86 insertions(+), 28 deletions(-) diff --git a/lib/qt_connection_util.h b/lib/qt_connection_util.h index 159e7522..9baf8c69 100644 --- a/lib/qt_connection_util.h +++ b/lib/qt_connection_util.h @@ -24,10 +24,15 @@ namespace Quotient { namespace _impl { + template + using decorated_slot_tt = + std::function; + template inline QMetaObject::Connection - connectUntil(SenderT* sender, SignalT signal, ContextT* context, - std::function slot, Qt::ConnectionType connType) + connectDecorated(SenderT* sender, SignalT signal, ContextT* context, + decorated_slot_tt decoratedSlot, + Qt::ConnectionType connType) { // See https://bugreports.qt.io/browse/QTBUG-60339 #if QT_VERSION < QT_VERSION_CHECK(5, 10, 0) @@ -36,52 +41,105 @@ namespace _impl { auto pc = std::make_unique(); #endif auto& c = *pc; // Resolve a reference before pc is moved to lambda - c = QObject::connect( - sender, signal, context, - [pc = std::move(pc), slot](ArgTs... args) { + + // Perfect forwarding doesn't work through signal-slot connections - + // arguments are always copied (at best - COWed) to the context of + // the slot. Therefore the slot decorator receives const ArgTs&... + // rather than ArgTs&&... + // TODO: std::bind_front() instead of lambda. + c = QObject::connect(sender, signal, context, + [pc = std::move(pc), + decoratedSlot = std::move(decoratedSlot)](const ArgTs&... args) { Q_ASSERT(*pc); // If it's been triggered, it should exist - if (slot(std::forward(args)...)) - QObject::disconnect(*pc); + decoratedSlot(*pc, args...); }, connType); return c; } + template + inline QMetaObject::Connection + connectUntil(SenderT* sender, SignalT signal, ContextT* context, + std::function functor, + Qt::ConnectionType connType) + { + return connectDecorated(sender, signal, context, + decorated_slot_tt( + [functor = std::move(functor)](QMetaObject::Connection& c, + const ArgTs&... args) { + if (functor(args...)) + QObject::disconnect(c); + }), + connType); + } + template + inline QMetaObject::Connection + connectSingleShot(SenderT* sender, SignalT signal, ContextT* context, + std::function slot, + Qt::ConnectionType connType) + { + return connectDecorated(sender, signal, context, + decorated_slot_tt( + [slot = std::move(slot)](QMetaObject::Connection& c, + const ArgTs&... args) { + QObject::disconnect(c); + slot(args...); + }), + connType); + } } // namespace _impl +/// Create a connection that self-disconnects when its "slot" returns true +/*! A slot accepted by connectUntil() is different from classic Qt slots + * in that its return value must be bool, not void. The slot's return value + * controls whether the connection should be kept; if the slot returns false, + * the connection remains; upon returning true, the slot is disconnected from + * the signal. Because of a different slot signature connectUntil() doesn't + * accept member functions as QObject::connect or Quotient::connectSingleShot + * do; you should pass a lambda or a pre-bound member function to it. + */ template inline auto connectUntil(SenderT* sender, SignalT signal, ContextT* context, const FunctorT& slot, Qt::ConnectionType connType = Qt::AutoConnection) { - return _impl::connectUntil( - sender, signal, context, - typename function_traits::function_type(slot), connType); + return _impl::connectUntil(sender, signal, context, std::function(slot), + connType); } -/** Create a single-shot connection that triggers on the signal and - * then self-disconnects - * - * Only supports DirectConnection type. - */ -template +/// Create a connection that self-disconnects after triggering on the signal +template +inline auto connectSingleShot(SenderT* sender, SignalT signal, + ContextT* context, const FunctorT& slot, + Qt::ConnectionType connType = Qt::AutoConnection) +{ + return _impl::connectSingleShot( + sender, signal, context, std::function(slot), connType); +} + +// Specialisation for usual Qt slots passed as pointers-to-members. +template inline auto connectSingleShot(SenderT* sender, SignalT signal, - ReceiverT* receiver, SlotT slot) + ReceiverT* receiver, + void (SlotObjectT::*slot)(ArgTs...), + Qt::ConnectionType connType = Qt::AutoConnection) { - QMetaObject::Connection connection; - connection = QObject::connect(sender, signal, receiver, slot, - Qt::DirectConnection); - Q_ASSERT(connection); - QObject::connect( - sender, signal, receiver, - [connection] { QObject::disconnect(connection); }, Qt::DirectConnection); - return connection; + // TODO: when switching to C++20, use std::bind_front() instead + return _impl::connectSingleShot(sender, signal, receiver, + std::function( + [receiver, slot](const ArgTs&... args) { + (receiver->*slot)(args...); + }), + connType); } -/** A guard pointer that disconnects an interested object upon destruction - * It's almost QPointer<> except that you have to initialise it with one +/// A guard pointer that disconnects an interested object upon destruction +/*! It's almost QPointer<> except that you have to initialise it with one * more additional parameter - a pointer to a QObject that will be * disconnected from signals of the underlying pointer upon the guard's - * destruction. + * destruction. Note that destructing the guide doesn't destruct either QObject. */ template class ConnectionsGuard : public QPointer { -- cgit v1.2.3 From 72dd1eb7c1986c23a7630205e2f2a0280b7c2a2b Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Sun, 29 Sep 2019 18:03:31 +0900 Subject: Simplify visit() signature Checking that BaseEventT descends from Event is really extraneous. --- lib/events/event.h | 30 ++++++++++++------------------ 1 file changed, 12 insertions(+), 18 deletions(-) diff --git a/lib/events/event.h b/lib/events/event.h index 25362786..f985ae92 100644 --- a/lib/events/event.h +++ b/lib/events/event.h @@ -334,22 +334,18 @@ inline auto visit(const BaseEventT& event, FnT&& visitor) return visitor(event); } -template -constexpr auto is_event() -{ - return std::is_base_of>::value; -} - -template -constexpr auto needs_cast() -{ - return !std::is_convertible>::value; +namespace _impl { + template + constexpr auto needs_downcast() + { + return !std::is_convertible_v>; + } } // A single type-specific void visitor template -inline std::enable_if_t() && needs_cast() - && std::is_void>::value> +inline std::enable_if_t<_impl::needs_downcast() + && std::is_void_v>> visit(const BaseEventT& event, FnT&& visitor) { using event_type = fn_arg_t; @@ -358,10 +354,9 @@ visit(const BaseEventT& event, FnT&& visitor) } // A single type-specific non-void visitor with an optional default value +// non-voidness is guarded by defaultValue type template -inline std::enable_if_t() && needs_cast(), - fn_return_t> // non-voidness is guarded by - // defaultValue type +inline std::enable_if_t<_impl::needs_downcast(), fn_return_t> visit(const BaseEventT& event, FnT&& visitor, fn_return_t&& defaultValue = {}) { @@ -373,9 +368,8 @@ visit(const BaseEventT& event, FnT&& visitor, // A chain of 2 or more visitors template -inline std::enable_if_t(), fn_return_t> -visit(const BaseEventT& event, FnT1&& visitor1, FnT2&& visitor2, - FnTs&&... visitors) +inline fn_return_t visit(const BaseEventT& event, FnT1&& visitor1, + FnT2&& visitor2, FnTs&&... visitors) { using event_type1 = fn_arg_t; if (is>(event)) -- cgit v1.2.3 From 534a15d05e0b6e1b44b6387003e1475150e848e8 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Sun, 29 Sep 2019 18:01:45 +0900 Subject: function_traits: drop unused pieces is_callable won't ever be needed because std::is_invokable is here; arg_number and returns() didn't find its users; and function_type has been just broken all along for member functions. --- lib/util.cpp | 38 ++------------------------------------ lib/util.h | 22 ++++------------------ 2 files changed, 6 insertions(+), 54 deletions(-) diff --git a/lib/util.cpp b/lib/util.cpp index cc18d9ab..041a8aba 100644 --- a/lib/util.cpp +++ b/lib/util.cpp @@ -141,62 +141,28 @@ int f(); static_assert(std::is_same, int>::value, "Test fn_return_t<>"); -void f1(int); -static_assert(function_traits::arg_number == 1, - "Test fn_arg_number"); - -void f2(int, QString); -static_assert(std::is_same, QString>::value, +void f1(int, QString); +static_assert(std::is_same, QString>::value, "Test fn_arg_t<>"); -struct S { - int mf(); -}; -static_assert(is_callable_v, "Test member function"); -static_assert(returns(), - "Test returns<> with member function"); - struct Fo { int operator()(); }; -static_assert(is_callable_v, "Test is_callable<> with function object"); -static_assert(function_traits::arg_number == 0, "Test function object"); static_assert(std::is_same, int>::value, "Test return type of function object"); struct Fo1 { void operator()(int); }; -static_assert(function_traits::arg_number == 1, "Test function object 1"); -static_assert(is_callable_v, "Test is_callable<> with function object 1"); static_assert(std::is_same, int>(), "Test fn_arg_t defaulting to first argument"); #if (!defined(_MSC_VER) || _MSC_VER >= 1910) static auto l = [] { return 1; }; -static_assert(is_callable_v, "Test is_callable_v<> with lambda"); static_assert(std::is_same, int>::value, "Test fn_return_t<> with lambda"); #endif -template -struct fn_object { - static int smf(double) { return 0; } -}; -template <> -struct fn_object { - void operator()(QString); -}; -static_assert(is_callable_v>, "Test function object"); -static_assert(returns>(), - "Test returns<> with function object"); -static_assert(!is_callable_v>, "Test non-function object"); -// FIXME: These two don't work -// static_assert(is_callable_v::smf)>, -// "Test static member function"); -// static_assert(returns::smf)>(), -// "Test returns<> with static member function"); - template QString ft(T&&) { diff --git a/lib/util.h b/lib/util.h index 788ce5bc..4631570f 100644 --- a/lib/util.h +++ b/lib/util.h @@ -154,23 +154,19 @@ struct function_traits : public _impl::fn_traits {}; // Specialisation for a function template struct function_traits { - static constexpr auto is_callable = true; using return_type = ReturnT; using arg_types = std::tuple; - using function_type = std::function; - static constexpr auto arg_number = std::tuple_size::value; }; namespace _impl { template - struct fn_traits { - static constexpr auto is_callable = false; - }; + struct fn_traits; + // Specialisation for function objects with (non-overloaded) operator() + // (this includes non-generic lambdas) template struct fn_traits - : public fn_traits { - }; // A generic function object that has (non-overloaded) operator() + : public fn_traits {}; // Specialisation for a member function template @@ -190,16 +186,6 @@ template using fn_arg_t = std::tuple_element_t::arg_types>; -template -constexpr bool returns() -{ - return std::is_same, R>::value; -} - -// Poor-man's is_invokable -template -constexpr auto is_callable_v = function_traits::is_callable; - inline auto operator"" _ls(const char* s, std::size_t size) { return QLatin1String(s, int(size)); -- cgit v1.2.3 From f9c0e04259be9fd3be70486bc1eb76bf8f2612fe Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Sun, 29 Sep 2019 18:05:17 +0900 Subject: Rename pieces with qmc/qmatrixclient --- .appveyor.yml | 6 +++--- lib/jobs/downloadfilejob.cpp | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.appveyor.yml b/.appveyor.yml index 1dbdf237..a9f67ee4 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -1,7 +1,7 @@ image: Visual Studio 2017 environment: - #DEPLOY_DIR: libqmatrixclient-%APPVEYOR_BUILD_VERSION% + #DEPLOY_DIR: quotient-%APPVEYOR_BUILD_VERSION% matrix: - QTDIR: C:\Qt\5.13\msvc2017_64 VCVARS: "C:\\Program Files (x86)\\Microsoft Visual Studio\\2017\\Community\\VC\\Auxiliary\\Build\\vcvars64.bat" @@ -30,7 +30,7 @@ build_script: #after_build: #- cmake --build build --target install -#- 7z a libqmatrixclient.zip "%DEPLOY_DIR%\" +#- 7z a quotient.zip "%DEPLOY_DIR%\" # Uncomment this to connect to the AppVeyor build worker #on_finish: @@ -39,4 +39,4 @@ build_script: test: off #artifacts: -#- path: libqmatrixclient.zip +#- path: quotient.zip diff --git a/lib/jobs/downloadfilejob.cpp b/lib/jobs/downloadfilejob.cpp index 4e997326..3e037680 100644 --- a/lib/jobs/downloadfilejob.cpp +++ b/lib/jobs/downloadfilejob.cpp @@ -12,7 +12,7 @@ public: explicit Private(const QString& localFilename) : targetFile(new QFile(localFilename)) - , tempFile(new QFile(targetFile->fileName() + ".qmcdownload")) + , tempFile(new QFile(targetFile->fileName() + ".qtntdownload")) {} QScopedPointer targetFile; -- cgit v1.2.3 From 7802bcb4dd44c7523cbc687b52dd2e65b900c636 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Sun, 29 Sep 2019 18:11:35 +0900 Subject: BaseJob, urlForLog() -> dumpRequest(): include HTTP verb into log lines --- lib/jobs/basejob.cpp | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/lib/jobs/basejob.cpp b/lib/jobs/basejob.cpp index 54931c83..6a70bc40 100644 --- a/lib/jobs/basejob.cpp +++ b/lib/jobs/basejob.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -105,11 +106,17 @@ public: errorStrategy.size() - 1)]; } - QString urlForLog() const + QString dumpRequest() const { - return reply - ? reply->url().toString(QUrl::RemoveQuery) - : makeRequestUrl(connection->baseUrl(), apiEndpoint).toString(); + // Thanks to C++17, std::array's type and bounds are deduced + static const auto verbs = + std::array { QStringLiteral("GET"), QStringLiteral("PUT"), + QStringLiteral("POST"), QStringLiteral("DELETE") }; + const auto verbWord = verbs.at(size_t(verb)); + return verbWord % ' ' + % (reply ? reply->url().toString(QUrl::RemoveQuery) + : makeRequestUrl(connection->baseUrl(), apiEndpoint) + .toString()); } }; @@ -260,7 +267,7 @@ void BaseJob::sendRequest() if (status().code == Abandoned) return; Q_ASSERT(d->connection && status().code == Pending); - qCDebug(d->logCat) << "Making request to" << d->urlForLog(); + qCDebug(d->logCat).noquote() << "Making" << d->dumpRequest(); emit aboutToSendRequest(); d->sendRequest(); Q_ASSERT(d->reply); @@ -273,12 +280,12 @@ void BaseJob::sendRequest() connect(d->reply.data(), &QNetworkReply::downloadProgress, this, &BaseJob::downloadProgress); d->timer.start(getCurrentTimeout()); - qCInfo(d->logCat).noquote() << "Request sent to" << d->urlForLog(); + qCInfo(d->logCat).noquote() << "Sent" << d->dumpRequest(); onSentRequest(d->reply.data()); emit sentRequest(); } else qCWarning(d->logCat).noquote() - << "Request could not start:" << d->urlForLog(); + << "Request could not start:" << d->dumpRequest(); } void BaseJob::checkReply() { setStatus(doCheckReply(d->reply.data())); } @@ -378,7 +385,8 @@ BaseJob::Status BaseJob::doCheckReply(QNetworkReply* reply) const const auto httpCodeHeader = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute); if (!httpCodeHeader.isValid()) { - qCWarning(d->logCat) << "No valid HTTP headers from" << d->urlForLog(); + qCWarning(d->logCat).noquote() + << "No valid HTTP headers from" << d->dumpRequest(); return { NetworkError, reply->errorString() }; } @@ -386,7 +394,7 @@ BaseJob::Status BaseJob::doCheckReply(QNetworkReply* reply) const if (httpCode / 100 == 2) // 2xx { if (reply->isFinished()) - qCInfo(d->logCat) << httpCode << "<-" << d->urlForLog(); + qCInfo(d->logCat).noquote() << httpCode << "<-" << d->dumpRequest(); if (!checkContentType(reply->rawHeader("Content-Type"), d->expectedContentTypes)) return { UnexpectedResponseTypeWarning, @@ -394,7 +402,7 @@ BaseJob::Status BaseJob::doCheckReply(QNetworkReply* reply) const return NoError; } if (reply->isFinished()) - qCWarning(d->logCat) << httpCode << "<-" << d->urlForLog(); + qCWarning(d->logCat).noquote() << httpCode << "<-" << d->dumpRequest(); auto message = reply->errorString(); if (message.isEmpty()) -- cgit v1.2.3 From 508f4362b7b5e1863153cca6fe6756adedfa33ee Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Sun, 29 Sep 2019 18:20:54 +0900 Subject: Connection: allRooms(), rooms(), roomsCount(); deprecate roomMap() Closes #354. --- lib/connection.cpp | 49 +++++++++++++++++++++++++++++++++++++++---------- lib/connection.h | 29 ++++++++++++++++++++++++++++- lib/user.cpp | 6 ++---- 3 files changed, 69 insertions(+), 15 deletions(-) diff --git a/lib/connection.cpp b/lib/connection.cpp index c3e46356..2c5bf574 100644 --- a/lib/connection.cpp +++ b/lib/connection.cpp @@ -955,6 +955,33 @@ QHash, Room*> Connection::roomMap() const return roomMap; } +QVector Connection::allRooms() const +{ + QVector result; + result.resize(d->roomMap.size()); + std::copy(d->roomMap.cbegin(), d->roomMap.cend(), result.begin()); + return result; +} + +QVector Connection::rooms(JoinStates joinStates) const +{ + QVector result; + for (auto* r: qAsConst(d->roomMap)) + if (joinStates.testFlag(r->joinState())) + result.push_back(r); + return result; +} + +int Connection::roomsCount(JoinStates joinStates) const +{ + // Using int to maintain compatibility with QML + // (consider also that QHash<>::size() returns int anyway). + return int(std::count_if(d->roomMap.begin(), d->roomMap.end(), + [joinStates](Room* r) { + return joinStates.testFlag(r->joinState()); + })); +} + bool Connection::hasAccountData(const QString& type) const { return d->accountData.find(type) != d->accountData.cend(); @@ -1262,18 +1289,20 @@ void Connection::saveState() const { QStringLiteral("minor"), SyncData::cacheVersion().second } } } }; { - QJsonObject rooms; - QJsonObject inviteRooms; - const auto& rs = roomMap(); // Pass on rooms in Leave state - for (const auto* i : rs) - (i->joinState() == JoinState::Invite ? inviteRooms : rooms) - .insert(i->id(), QJsonValue::Null); + QJsonObject roomsJson; + QJsonObject inviteRoomsJson; + for (const auto* r: qAsConst(d->roomMap)) { + if (r->joinState() == JoinState::Leave) + continue; + (r->joinState() == JoinState::Invite ? inviteRoomsJson : roomsJson) + .insert(r->id(), QJsonValue::Null); + } QJsonObject roomObj; - if (!rooms.isEmpty()) - roomObj.insert(QStringLiteral("join"), rooms); - if (!inviteRooms.isEmpty()) - roomObj.insert(QStringLiteral("invite"), inviteRooms); + if (!roomsJson.isEmpty()) + roomObj.insert(QStringLiteral("join"), roomsJson); + if (!inviteRoomsJson.isEmpty()) + roomObj.insert(QStringLiteral("invite"), inviteRoomsJson); rootObj.insert(QStringLiteral("next_batch"), d->data->lastEvent()); rootObj.insert(QStringLiteral("rooms"), roomObj); diff --git a/lib/connection.h b/lib/connection.h index b4b16679..1f1d4cd5 100644 --- a/lib/connection.h +++ b/lib/connection.h @@ -135,12 +135,39 @@ public: explicit Connection(const QUrl& server, QObject* parent = nullptr); ~Connection() override; - /** Get all Invited and Joined rooms + /// Get all Invited and Joined rooms + /*! * \return a hashmap from a composite key - room name and whether * it's an Invite rather than Join - to room pointers + * \sa allRooms, rooms, roomsWithTag */ + [[deprecated("Use allRooms(), roomsWithTag() or rooms(joinStates) instead")]] QHash, Room*> roomMap() const; + /// Get all rooms known within this Connection + /*! + * This includes Invite, Join and Leave rooms, in no particular order. + * \note Leave rooms will only show up in the list if they have been left + * in the same running session. The library doesn't cache left rooms + * between runs and it doesn't retrieve the full list of left rooms + * from the server. + * \sa rooms, room, roomsWithTag + */ + Q_INVOKABLE QVector allRooms() const; + + /// Get rooms that have either of the given join state(s) + /*! + * This method returns, in no particular order, rooms which join state + * matches the mask passed in \p joinStates. + * \note Similar to allRooms(), this won't retrieve the full list of + * Leave rooms from the server. + * \sa allRooms, room, roomsWithTag + */ + Q_INVOKABLE QVector rooms(JoinStates joinStates) const; + + /// Get the total number of rooms in the given join state(s) + Q_INVOKABLE int roomsCount(JoinStates joinStates) const; + /** Check whether the account has data of the given type * Direct chats map is not supported by this method _yet_. */ diff --git a/lib/user.cpp b/lib/user.cpp index 641f6a6b..67cd1117 100644 --- a/lib/user.cpp +++ b/lib/user.cpp @@ -111,8 +111,7 @@ void User::Private::setNameForRoom(const Room* r, QString newName, et.start(); } - const auto& roomMap = connection->roomMap(); - for (auto* r1 : roomMap) + for (auto* r1: connection->allRooms()) if (nameForRoom(r1) == mostUsedName) otherNames.insert(mostUsedName, r1); @@ -176,8 +175,7 @@ void User::Private::setAvatarForRoom(const Room* r, const QUrl& newUrl, nextMostUsedIt = otherAvatars.end() - 1; } std::swap(mostUsedAvatar, *nextMostUsedIt); - const auto& roomMap = connection->roomMap(); - for (const auto* r1 : roomMap) + for (const auto* r1: connection->allRooms()) if (avatarUrlForRoom(r1) == nextMostUsedIt->url()) avatarsToRooms.insert(nextMostUsedIt->url(), r1); -- cgit v1.2.3 From d0a0f8cda88d644ca38b2b90024303fbe362602c Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Mon, 30 Sep 2019 08:11:24 +0900 Subject: Move C++ standard requirement to target_compile_features ...so that users of the library wouldn't need to redefine that they need C++17. --- CMakeLists.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 951ef8c9..ce4af9a8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -225,11 +225,12 @@ set_target_properties(${PROJECT_NAME} PROPERTIES VERSION "${PROJECT_VERSION}" SOVERSION ${API_VERSION} INTERFACE_${PROJECT_NAME}_MAJOR_VERSION ${API_VERSION} - CXX_STANDARD 17 ) set_property(TARGET ${PROJECT_NAME} APPEND PROPERTY COMPATIBLE_INTERFACE_STRING ${PROJECT_NAME}_MAJOR_VERSION) +target_compile_features(${PROJECT_NAME} PUBLIC cxx_std_17) + target_include_directories(${PROJECT_NAME} PUBLIC $ $ -- cgit v1.2.3 From f71d16b56ab90e494d6a41c276210a4ce593987e Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Tue, 1 Oct 2019 13:14:30 +0900 Subject: Room::getCurrentState() --- lib/room.cpp | 24 ++++++++++++++++++------ lib/room.h | 18 ++++++++++++++++++ 2 files changed, 36 insertions(+), 6 deletions(-) diff --git a/lib/room.cpp b/lib/room.cpp index e2195193..aae84416 100644 --- a/lib/room.cpp +++ b/lib/room.cpp @@ -202,25 +202,31 @@ public: void getPreviousContent(int limit = 10); - template - const EventT* getCurrentState(const QString& stateKey = {}) const + const StateEventBase* getCurrentState(const StateEventKey& evtKey) const { - const StateEventKey evtKey { EventT::matrixTypeId(), stateKey }; const auto* evt = currentState.value(evtKey, nullptr); if (!evt) { if (stubbedState.find(evtKey) == stubbedState.end()) { // In the absence of a real event, make a stub as-if an event // with empty content has been received. Event classes should be // prepared for empty/invalid/malicious content anyway. - stubbedState.emplace(evtKey, - loadStateEvent(EventT::matrixTypeId(), {}, - stateKey)); + stubbedState.emplace(evtKey, loadStateEvent(evtKey.first, {}, + evtKey.second)); qCDebug(STATE) << "A new stub event created for key {" << evtKey.first << evtKey.second << "}"; } evt = stubbedState[evtKey].get(); Q_ASSERT(evt); } + Q_ASSERT(evt->matrixType() == evtKey.first + && evt->stateKey() == evtKey.second); + return evt; + } + + template + const EventT* getCurrentState(const QString& stateKey = {}) const + { + const auto* evt = getCurrentState({ EventT::matrixTypeId(), stateKey }); Q_ASSERT(evt->type() == EventT::typeId() && evt->matrixType() == EventT::matrixTypeId()); return static_cast(evt); @@ -1105,6 +1111,12 @@ bool Room::usesEncryption() const return !d->getCurrentState()->algorithm().isEmpty(); } +const StateEventBase* Room::getCurrentState(const QString& evtType, + const QString& stateKey) const +{ + return d->getCurrentState({ evtType, stateKey }); +} + RoomEventPtr Room::decryptMessage(EncryptedEvent* encryptedEvent) { if (encryptedEvent->algorithm() == OlmV1Curve25519AesSha2AlgoKey) { diff --git a/lib/room.h b/lib/room.h index 698f74c8..cded7eb9 100644 --- a/lib/room.h +++ b/lib/room.h @@ -434,6 +434,24 @@ public: Q_INVOKABLE bool supportsCalls() const; + /// Get a state event with the given event type and state key + /*! This method returns a (potentially empty) state event corresponding + * to the pair of event type \p evtType and state key \p stateKey. + */ + Q_INVOKABLE const StateEventBase* + getCurrentState(const QString& evtType, const QString& stateKey = {}) const; + + template + const EvT* getCurrentState(const QString& stateKey = {}) const + { + const auto* evt = + eventCast(getCurrentState(EvT::matrixTypeId(), stateKey)); + Q_ASSERT(evt); + Q_ASSERT(evt->matrixTypeId() == EvT::matrixTypeId() + && evt->stateKey() == stateKey); + return evt; + } + template auto setState(ArgTs&&... args) const { -- cgit v1.2.3