aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt2
-rw-r--r--connection.cpp2
-rw-r--r--connection.h5
-rw-r--r--events/event.cpp145
-rw-r--r--events/event.h103
-rw-r--r--events/receiptevent.cpp40
-rw-r--r--events/receiptevent.h35
-rw-r--r--events/roomaliasesevent.cpp42
-rw-r--r--events/roomaliasesevent.h14
-rw-r--r--events/roomcanonicalaliasevent.cpp32
-rw-r--r--events/roomcanonicalaliasevent.h17
-rw-r--r--events/roommemberevent.cpp77
-rw-r--r--events/roommemberevent.h29
-rw-r--r--events/roommessageevent.cpp121
-rw-r--r--events/roommessageevent.h67
-rw-r--r--events/roomnameevent.cpp30
-rw-r--r--events/roomnameevent.h17
-rw-r--r--events/roomtopicevent.cpp29
-rw-r--r--events/roomtopicevent.h17
-rw-r--r--events/typingevent.cpp42
-rw-r--r--events/typingevent.h12
-rw-r--r--examples/qmc-example.cpp11
-rw-r--r--jobs/passwordlogin.h2
-rw-r--r--jobs/roommessagesjob.cpp9
-rw-r--r--jobs/roommessagesjob.h2
-rw-r--r--jobs/syncjob.cpp6
-rw-r--r--jobs/syncjob.h55
-rw-r--r--room.cpp201
-rw-r--r--room.h24
-rw-r--r--user.cpp2
-rw-r--r--util.h160
31 files changed, 617 insertions, 733 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 11cf015d..ad7c5a34 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -50,7 +50,6 @@ set(libqmatrixclient_SRCS
logging.cpp
room.cpp
user.cpp
- state.cpp
settings.cpp
events/event.cpp
events/roommessageevent.cpp
@@ -61,7 +60,6 @@ set(libqmatrixclient_SRCS
events/roomtopicevent.cpp
events/typingevent.cpp
events/receiptevent.cpp
- events/unknownevent.cpp
jobs/basejob.cpp
jobs/checkauthmethods.cpp
jobs/passwordlogin.cpp
diff --git a/connection.cpp b/connection.cpp
index f9e2e7ae..56628a07 100644
--- a/connection.cpp
+++ b/connection.cpp
@@ -191,7 +191,7 @@ void Connection::postMessage(Room* room, const QString& type, const QString& mes
callApi<PostMessageJob>(room->id(), type, message);
}
-PostReceiptJob* Connection::postReceipt(Room* room, Event* event) const
+PostReceiptJob* Connection::postReceipt(Room* room, RoomEvent* event) const
{
return callApi<PostReceiptJob>(room->id(), event->id());
}
diff --git a/connection.h b/connection.h
index 08184d0d..f0b097fd 100644
--- a/connection.h
+++ b/connection.h
@@ -26,7 +26,7 @@ namespace QMatrixClient
{
class Room;
class User;
- class Event;
+ class RoomEvent;
class ConnectionPrivate;
class ConnectionData;
@@ -61,7 +61,8 @@ namespace QMatrixClient
Q_INVOKABLE virtual void postMessage(Room* room, const QString& type,
const QString& message) const;
/** @deprecated Use callApi<PostReceiptJob>() or Room::postReceipt() instead */
- Q_INVOKABLE virtual PostReceiptJob* postReceipt( Room* room, Event* event ) const;
+ Q_INVOKABLE virtual PostReceiptJob* postReceipt(Room* room,
+ RoomEvent* event) const;
Q_INVOKABLE virtual JoinRoomJob* joinRoom(const QString& roomAlias);
/** @deprecated Use callApi<LeaveRoomJob>() or Room::leaveRoom() instead */
Q_INVOKABLE virtual void leaveRoom( Room* room );
diff --git a/events/event.cpp b/events/event.cpp
index 07649b02..bd7e1b03 100644
--- a/events/event.cpp
+++ b/events/event.cpp
@@ -18,9 +18,6 @@
#include "event.h"
-#include <QtCore/QJsonArray>
-#include <QtCore/QJsonDocument>
-
#include "roommessageevent.h"
#include "roomnameevent.h"
#include "roomaliasesevent.h"
@@ -31,121 +28,101 @@
#include "receiptevent.h"
#include "unknownevent.h"
#include "logging.h"
-#include "util.h"
-using namespace QMatrixClient;
+#include <QtCore/QJsonDocument>
-class Event::Private
-{
- public:
- EventType type;
- QString id;
- QDateTime timestamp;
- QString roomId;
- QString senderId;
- QString originalJson;
-};
+using namespace QMatrixClient;
-Event::Event(EventType type)
- : d(new Private)
+Event::Event(Type type, const QJsonObject& rep)
+ : _type(type), _originalJson(rep)
{
- d->type = type;
+ if (!rep.contains("content"))
+ {
+ qCWarning(EVENTS) << "Event without 'content' node";
+ qCWarning(EVENTS) << formatJson << rep;
+ }
}
-Event::~Event()
+QByteArray Event::originalJson() const
{
- delete d;
+ return QJsonDocument(_originalJson).toJson();
}
-EventType Event::type() const
+QDateTime Event::toTimestamp(const QJsonValue& v)
{
- return d->type;
+ Q_ASSERT(v.isDouble());
+ return QDateTime::fromMSecsSinceEpoch(
+ static_cast<long long int>(v.toDouble()), Qt::UTC);
}
-QString Event::id() const
+QStringList Event::toStringList(const QJsonValue& v)
{
- return d->id;
-}
+ Q_ASSERT(v.isArray());
-QDateTime Event::timestamp() const
-{
- return d->timestamp;
+ QStringList l;
+ for( const QJsonValue& e : v.toArray() )
+ l.push_back(e.toString());
+ return l;
}
-QString Event::roomId() const
+const QJsonObject Event::contentJson() const
{
- return d->roomId;
+ return _originalJson["content"].toObject();
}
-QString Event::senderId() const
+template <typename EventT>
+EventT* make(const QJsonObject& o)
{
- return d->senderId;
+ return new EventT(o);
}
-QString Event::originalJson() const
+Event* Event::fromJson(const QJsonObject& obj)
{
- return d->originalJson;
+ // Check more specific event types first
+ if (auto e = RoomEvent::fromJson(obj))
+ return e;
+
+ return dispatch<Event*>(obj).to(obj["type"].toString(),
+ "m.typing", make<TypingEvent>,
+ "m.receipt", make<ReceiptEvent>,
+ /* Insert new event types (except room events) BEFORE this line */
+ nullptr
+ );
}
-template <typename T>
-Event* make(const QJsonObject& obj)
+RoomEvent::RoomEvent(Type type, const QJsonObject& rep)
+ : Event(type, rep), _id(rep["event_id"].toString())
+ , _serverTimestamp(toTimestamp(rep["origin_server_ts"]))
+ , _roomId(rep["room_id"].toString())
+ , _senderId(rep["sender"].toString())
{
- return T::fromJson(obj);
+ if (_id.isEmpty())
+ {
+ qCWarning(EVENTS) << "Can't find event_id in a room event";
+ qCWarning(EVENTS) << formatJson << rep;
+ }
+ if (!rep.contains("origin_server_ts"))
+ {
+ qCWarning(EVENTS) << "Event: can't find server timestamp in a room event";
+ qCWarning(EVENTS) << formatJson << rep;
+ }
+ if (_senderId.isEmpty())
+ {
+ qCWarning(EVENTS) << "user_id not found in a room event";
+ qCWarning(EVENTS) << formatJson << rep;
+ }
}
-Event* Event::fromJson(const QJsonObject& obj)
+RoomEvent* RoomEvent::fromJson(const QJsonObject& obj)
{
- auto delegate = lookup(obj.value("type").toString(),
+ return dispatch<RoomEvent*>(obj).to(obj["type"].toString(),
"m.room.message", make<RoomMessageEvent>,
"m.room.name", make<RoomNameEvent>,
"m.room.aliases", make<RoomAliasesEvent>,
"m.room.canonical_alias", make<RoomCanonicalAliasEvent>,
"m.room.member", make<RoomMemberEvent>,
"m.room.topic", make<RoomTopicEvent>,
- "m.typing", make<TypingEvent>,
- "m.receipt", make<ReceiptEvent>,
- /* Insert new event types BEFORE this line */
- make<UnknownEvent>
+ /* Insert new ROOM event types BEFORE this line */
+ nullptr
);
- return delegate(obj);
-}
-
-bool Event::parseJson(const QJsonObject& obj)
-{
- d->originalJson = QString::fromUtf8(QJsonDocument(obj).toJson());
- d->id = obj.value("event_id").toString();
- d->roomId = obj.value("room_id").toString();
- d->senderId = obj.value("sender").toString();
- bool correct = (d->type != EventType::Unknown);
- if ( d->type != EventType::Typing &&
- d->type != EventType::Receipt )
- {
- if (d->id.isEmpty())
- {
- correct = false;
- qCDebug(EVENTS) << "Event: can't find event_id; event dump follows";
- qCDebug(EVENTS) << formatJson << obj;
- }
- if( obj.contains("origin_server_ts") )
- {
- d->timestamp = QDateTime::fromMSecsSinceEpoch(
- static_cast<qint64>(obj.value("origin_server_ts").toDouble()), Qt::UTC );
- }
- else if (d->type != EventType::Unknown)
- {
- correct = false;
- qCDebug(EVENTS) << "Event: can't find ts; event dump follows";
- qCDebug(EVENTS) << formatJson << obj;
- }
- }
- return correct;
-}
-
-Events QMatrixClient::eventsFromJson(const QJsonArray& json)
-{
- Events evs;
- evs.reserve(json.size());
- for (auto event: json)
- evs.push_back(Event::fromJson(event.toObject()));
- return evs;
}
diff --git a/events/event.h b/events/event.h
index f60dfb64..fd2f6feb 100644
--- a/events/event.h
+++ b/events/event.h
@@ -21,43 +21,92 @@
#include <QtCore/QString>
#include <QtCore/QDateTime>
#include <QtCore/QJsonObject>
-#include <QtCore/QVector>
+#include <QtCore/QJsonArray>
-class QJsonArray;
+#include "util.h"
namespace QMatrixClient
{
- enum class EventType
- {
- RoomMessage, RoomName, RoomAliases, RoomCanonicalAlias,
- RoomMember, RoomTopic, Typing, Receipt, Unknown
- };
-
class Event
{
+ Q_GADGET
public:
- explicit Event(EventType type);
- Event(Event&) = delete;
- virtual ~Event();
-
- EventType type() const;
- QString id() const;
- QDateTime timestamp() const;
- QString roomId() const;
- QString senderId() const;
- // only for debug purposes!
- QString originalJson() const;
+ enum class Type
+ {
+ RoomMessage, RoomName, RoomAliases, RoomCanonicalAlias,
+ RoomMember, RoomTopic, Typing, Receipt, Unknown
+ };
+
+ explicit Event(Type type, const QJsonObject& rep);
+ Event(const Event&) = delete;
+
+ Type type() const { return _type; }
+ QByteArray originalJson() const;
+
+ // Every event also has a "content" object but since its structure is
+ // different for different types, we're implementing it per-event type
+ // (and in most cases it will be a combination of other fields
+ // instead of "content" field).
static Event* fromJson(const QJsonObject& obj);
-
+
protected:
- bool parseJson(const QJsonObject& obj);
-
+ static QDateTime toTimestamp(const QJsonValue& v);
+ static QStringList toStringList(const QJsonValue& v);
+
+ const QJsonObject contentJson() const;
+
private:
- class Private;
- Private* d;
+ Type _type;
+ QJsonObject _originalJson;
+
+ REGISTER_ENUM(Type)
};
- using Events = QVector<Event*>;
+ using EventType = Event::Type;
+ template <typename EventT>
+ using EventsBatch = std::vector<EventT*>;
+ using Events = EventsBatch<Event>;
+
+ template <typename BaseEventT>
+ BaseEventT* makeEvent(const QJsonObject& obj)
+ {
+ if (auto e = BaseEventT::fromJson(obj))
+ return e;
+
+ return new BaseEventT(EventType::Unknown, obj);
+ }
+
+ template <typename BaseEventT = Event,
+ typename BatchT = EventsBatch<BaseEventT> >
+ BatchT makeEvents(const QJsonArray& objs)
+ {
+ BatchT evs;
+ // The below line accommodates the difference in size types of
+ // STL and Qt containers.
+ evs.reserve(static_cast<typename BatchT::size_type>(objs.size()));
+ for (auto obj: objs)
+ evs.push_back(makeEvent<BaseEventT>(obj.toObject()));
+ return evs;
+ }
- Events eventsFromJson(const QJsonArray& json);
-}
+ class RoomEvent : public Event
+ {
+ public:
+ RoomEvent(Type type, const QJsonObject& rep);
+
+ const QString& id() const { return _id; }
+ const QDateTime& timestamp() const { return _serverTimestamp; }
+ const QString& roomId() const { return _roomId; }
+ const QString& senderId() const { return _senderId; }
+
+ // "Static override" of the one in Event
+ static RoomEvent* fromJson(const QJsonObject& obj);
+
+ private:
+ QString _id;
+ QDateTime _serverTimestamp;
+ QString _roomId;
+ QString _senderId;
+ };
+ using RoomEvents = EventsBatch<RoomEvent>;
+} // namespace QMatrixClient
diff --git a/events/receiptevent.cpp b/events/receiptevent.cpp
index c163424f..e3478cf1 100644
--- a/events/receiptevent.cpp
+++ b/events/receiptevent.cpp
@@ -41,34 +41,13 @@ Example of a Receipt Event:
using namespace QMatrixClient;
-class ReceiptEvent::Private
+ReceiptEvent::ReceiptEvent(const QJsonObject& obj)
+ : Event(Type::Receipt, obj)
{
- public:
- EventsToReceipts eventsToReceipts;
-};
+ Q_ASSERT(obj["type"].toString() == jsonType);
-ReceiptEvent::ReceiptEvent()
- : Event(EventType::Receipt)
- , d(new Private)
-{
-}
-
-ReceiptEvent::~ReceiptEvent()
-{
- delete d;
-}
-
-EventsToReceipts ReceiptEvent::events() const
-{
- return d->eventsToReceipts;
-}
-
-ReceiptEvent* ReceiptEvent::fromJson(const QJsonObject& obj)
-{
- ReceiptEvent* e = new ReceiptEvent();
- e->parseJson(obj);
const QJsonObject contents = obj["content"].toObject();
- e->d->eventsToReceipts.reserve(contents.size());
+ _eventsWithReceipts.reserve(static_cast<size_t>(contents.size()));
for( auto eventIt = contents.begin(); eventIt != contents.end(); ++eventIt )
{
if (eventIt.key().isEmpty())
@@ -78,15 +57,14 @@ ReceiptEvent* ReceiptEvent::fromJson(const QJsonObject& obj)
continue;
}
const QJsonObject reads = eventIt.value().toObject().value("m.read").toObject();
- Receipts receipts; receipts.reserve(reads.size());
+ std::vector<Receipt> receipts;
+ receipts.reserve(static_cast<size_t>(reads.size()));
for( auto userIt = reads.begin(); userIt != reads.end(); ++userIt )
{
const QJsonObject user = userIt.value().toObject();
- const auto time = QDateTime::fromMSecsSinceEpoch(
- static_cast<qint64>(user["ts"].toDouble()), Qt::UTC );
- receipts.push_back({ userIt.key(), time });
+ receipts.push_back({userIt.key(), toTimestamp(user["ts"])});
}
- e->d->eventsToReceipts.push_back({ eventIt.key(), receipts });
+ _eventsWithReceipts.push_back({eventIt.key(), receipts});
}
- return e;
}
+
diff --git a/events/receiptevent.h b/events/receiptevent.h
index 40c0384f..1d280822 100644
--- a/events/receiptevent.h
+++ b/events/receiptevent.h
@@ -22,32 +22,29 @@
namespace QMatrixClient
{
- class Receipt
+ struct Receipt
{
- public:
- QString userId;
- QDateTime timestamp;
+ QString userId;
+ QDateTime timestamp;
};
-}
-Q_DECLARE_TYPEINFO(QMatrixClient::Receipt, Q_MOVABLE_TYPE);
-
-namespace QMatrixClient
-{
- using Receipts = QVector<Receipt>;
- using EventsToReceipts = QVector< QPair<QString, Receipts> >;
+ struct ReceiptsForEvent
+ {
+ QString evtId;
+ std::vector<Receipt> receipts;
+ };
+ using EventsWithReceipts = std::vector<ReceiptsForEvent>;
class ReceiptEvent: public Event
{
public:
- ReceiptEvent();
- virtual ~ReceiptEvent();
+ explicit ReceiptEvent(const QJsonObject& obj);
- EventsToReceipts events() const;
-
- static ReceiptEvent* fromJson(const QJsonObject& obj);
+ EventsWithReceipts eventsWithReceipts() const
+ { return _eventsWithReceipts; }
private:
- class Private;
- Private* d;
+ EventsWithReceipts _eventsWithReceipts;
+
+ static constexpr const char * jsonType = "m.receipt";
};
-}
+} // namespace QMatrixClient
diff --git a/events/roomaliasesevent.cpp b/events/roomaliasesevent.cpp
index ab414498..344b4367 100644
--- a/events/roomaliasesevent.cpp
+++ b/events/roomaliasesevent.cpp
@@ -34,44 +34,10 @@
#include "roomaliasesevent.h"
-#include "logging.h"
-
-#include <QtCore/QJsonArray>
-
using namespace QMatrixClient;
-class RoomAliasesEvent::Private
-{
- public:
- QStringList aliases;
-};
-
-RoomAliasesEvent::RoomAliasesEvent()
- : Event(EventType::RoomAliases)
- , d(new Private)
-{
-}
-
-RoomAliasesEvent::~RoomAliasesEvent()
-{
- delete d;
-}
-
-QStringList RoomAliasesEvent::aliases() const
-{
- return d->aliases;
-}
+RoomAliasesEvent::RoomAliasesEvent(const QJsonObject& obj)
+ : RoomEvent(Type::RoomAliases, obj)
+ , _aliases(toStringList(contentJson()["aliases"]))
+{ }
-RoomAliasesEvent* RoomAliasesEvent::fromJson(const QJsonObject& obj)
-{
- RoomAliasesEvent* e = new RoomAliasesEvent();
- e->parseJson(obj);
- const QJsonObject contents = obj.value("content").toObject();
- const QJsonArray aliases = contents.value("aliases").toArray();
- for( const QJsonValue& alias : aliases )
- {
- e->d->aliases << alias.toString();
- }
- qCDebug(EVENTS) << "RoomAliasesEvent:" << e->d->aliases;
- return e;
-}
diff --git a/events/roomaliasesevent.h b/events/roomaliasesevent.h
index 8f638be2..efafcb30 100644
--- a/events/roomaliasesevent.h
+++ b/events/roomaliasesevent.h
@@ -24,18 +24,14 @@
namespace QMatrixClient
{
- class RoomAliasesEvent: public Event
+ class RoomAliasesEvent: public RoomEvent
{
public:
- RoomAliasesEvent();
- virtual ~RoomAliasesEvent();
+ explicit RoomAliasesEvent(const QJsonObject& obj);
- QStringList aliases() const;
-
- static RoomAliasesEvent* fromJson(const QJsonObject& obj);
+ QStringList aliases() const { return _aliases; }
private:
- class Private;
- Private* d;
+ QStringList _aliases;
};
-}
+} // namespace QMatrixClient
diff --git a/events/roomcanonicalaliasevent.cpp b/events/roomcanonicalaliasevent.cpp
index d84c07fc..6884bc15 100644
--- a/events/roomcanonicalaliasevent.cpp
+++ b/events/roomcanonicalaliasevent.cpp
@@ -19,35 +19,3 @@
#include "roomcanonicalaliasevent.h"
using namespace QMatrixClient;
-
-class RoomCanonicalAliasEvent::Private
-{
- public:
- QString alias;
-};
-
-RoomCanonicalAliasEvent::RoomCanonicalAliasEvent()
- : Event(EventType::RoomCanonicalAlias)
- , d(new Private)
-{
-}
-
-RoomCanonicalAliasEvent::~RoomCanonicalAliasEvent()
-{
- delete d;
-}
-
-QString RoomCanonicalAliasEvent::alias()
-{
- return d->alias;
-}
-
-RoomCanonicalAliasEvent* RoomCanonicalAliasEvent::fromJson(const QJsonObject& obj)
-{
- RoomCanonicalAliasEvent* e = new RoomCanonicalAliasEvent();
- e->parseJson(obj);
- const QJsonObject contents = obj.value("content").toObject();
- e->d->alias = contents.value("alias").toString();
- return e;
-}
-
diff --git a/events/roomcanonicalaliasevent.h b/events/roomcanonicalaliasevent.h
index 87219be6..72620d74 100644
--- a/events/roomcanonicalaliasevent.h
+++ b/events/roomcanonicalaliasevent.h
@@ -22,18 +22,17 @@
namespace QMatrixClient
{
- class RoomCanonicalAliasEvent: public Event
+ class RoomCanonicalAliasEvent : public RoomEvent
{
public:
- RoomCanonicalAliasEvent();
- virtual ~RoomCanonicalAliasEvent();
+ explicit RoomCanonicalAliasEvent(const QJsonObject& obj)
+ : RoomEvent(Type::RoomCanonicalAlias, obj)
+ , _canonicalAlias(contentJson()["alias"].toString())
+ { }
- QString alias();
-
- static RoomCanonicalAliasEvent* fromJson(const QJsonObject& obj);
+ QString alias() const { return _canonicalAlias; }
private:
- class Private;
- Private* d;
+ QString _canonicalAlias;
};
-}
+} // namespace QMatrixClient
diff --git a/events/roommemberevent.cpp b/events/roommemberevent.cpp
index 51dbbbab..5973acc7 100644
--- a/events/roommemberevent.cpp
+++ b/events/roommemberevent.cpp
@@ -22,66 +22,19 @@
using namespace QMatrixClient;
-class RoomMemberEvent::Private
-{
- public:
- MembershipType membership;
- QString userId;
- QString displayname;
- QUrl avatarUrl;
-};
-
-RoomMemberEvent::RoomMemberEvent()
- : Event(EventType::RoomMember)
- , d(new Private)
-{
-}
-
-RoomMemberEvent::~RoomMemberEvent()
-{
- delete d;
-}
-
-MembershipType RoomMemberEvent::membership() const
-{
- return d->membership;
-}
-
-QString RoomMemberEvent::userId() const
-{
- return d->userId;
-}
-
-QString RoomMemberEvent::displayName() const
-{
- return d->displayname;
-}
-
-QUrl RoomMemberEvent::avatarUrl() const
-{
- return d->avatarUrl;
-}
-
-RoomMemberEvent* RoomMemberEvent::fromJson(const QJsonObject& obj)
-{
- RoomMemberEvent* e = new RoomMemberEvent();
- e->parseJson(obj);
- e->d->userId = obj.value("state_key").toString();
- QJsonObject content = obj.value("content").toObject();
- e->d->displayname = content.value("displayname").toString();
- QString membershipString = content.value("membership").toString();
- if( membershipString == "invite" )
- e->d->membership = MembershipType::Invite;
- else if( membershipString == "join" )
- e->d->membership = MembershipType::Join;
- else if( membershipString == "knock" )
- e->d->membership = MembershipType::Knock;
- else if( membershipString == "leave" )
- e->d->membership = MembershipType::Leave;
- else if( membershipString == "ban" )
- e->d->membership = MembershipType::Ban;
- else
- qCDebug(EVENTS) << "Unknown MembershipType: " << membershipString;
- e->d->avatarUrl = QUrl(content.value("avatar_url").toString());
- return e;
+RoomMemberEvent::RoomMemberEvent(const QJsonObject& obj)
+ : RoomEvent(Type::RoomMember, obj), _userId(obj["state_key"].toString())
+{
+ const auto contentObj = contentJson();
+ _displayName = contentObj["displayname"].toString();
+ _avatarUrl = contentObj["avatar_url"].toString();
+ QString membershipString = contentObj["membership"].toString();
+ const auto supportedStrings = { "invite", "join", "knock", "leave", "ban" };
+ for (auto it = supportedStrings.begin(); it != supportedStrings.end(); ++it)
+ if (membershipString == *it)
+ {
+ _membership = MembershipType(it - supportedStrings.begin());
+ return;
+ }
+ qCWarning(EVENTS) << "Unknown MembershipType: " << membershipString;
}
diff --git a/events/roommemberevent.h b/events/roommemberevent.h
index a33c2982..9ebb75ee 100644
--- a/events/roommemberevent.h
+++ b/events/roommemberevent.h
@@ -24,23 +24,26 @@
namespace QMatrixClient
{
- enum class MembershipType {Invite, Join, Knock, Leave, Ban};
-
- class RoomMemberEvent: public Event
+ class RoomMemberEvent: public RoomEvent
{
+ Q_GADGET
public:
- RoomMemberEvent();
- virtual ~RoomMemberEvent();
+ enum MembershipType : int {Invite = 0, Join, Knock, Leave, Ban};
- MembershipType membership() const;
- QString userId() const;
- QString displayName() const;
- QUrl avatarUrl() const;
+ explicit RoomMemberEvent(const QJsonObject& obj);
- static RoomMemberEvent* fromJson(const QJsonObject& obj);
+ MembershipType membership() const { return _membership; }
+ const QString& userId() const { return _userId; }
+ const QString& displayName() const { return _displayName; }
+ const QUrl& avatarUrl() const { return _avatarUrl; }
private:
- class Private;
- Private* d;
+ MembershipType _membership;
+ QString _userId;
+ QString _displayName;
+ QUrl _avatarUrl;
+
+ REGISTER_ENUM(MembershipType)
};
-}
+ using MembershipType = RoomMemberEvent::MembershipType;
+} // namespace QMatrixClient
diff --git a/events/roommessageevent.cpp b/events/roommessageevent.cpp
index 677bb79f..19da8827 100644
--- a/events/roommessageevent.cpp
+++ b/events/roommessageevent.cpp
@@ -19,64 +19,15 @@
#include "roommessageevent.h"
#include "logging.h"
-#include "util.h"
#include <QtCore/QMimeDatabase>
using namespace QMatrixClient;
-
-class RoomMessageEvent::Private
-{
- public:
- Private() : msgtype(MessageEventType::Unknown), content(nullptr) {}
- ~Private() { if (content) delete content; }
-
- QString userId;
- MessageEventType msgtype;
- QString plainBody;
- MessageEventContent::Base* content;
-};
-
-RoomMessageEvent::RoomMessageEvent()
- : Event(EventType::RoomMessage)
- , d(new Private)
-{ }
-
-RoomMessageEvent::~RoomMessageEvent()
-{
- delete d;
-}
-
-QString RoomMessageEvent::userId() const
-{
- return d->userId;
-}
-
-MessageEventType RoomMessageEvent::msgtype() const
-{
- return d->msgtype;
-}
-
-QString RoomMessageEvent::plainBody() const
-{
- return d->plainBody;
-}
-
-QString RoomMessageEvent::body() const
-{
- return plainBody();
-}
-
using namespace MessageEventContent;
-Base* RoomMessageEvent::content() const
-{
- return d->content;
-}
-
-using ContentPair = std::pair<MessageEventType, MessageEventContent::Base*>;
+using ContentPair = std::pair<CType, Base*>;
-template <MessageEventType EnumType, typename ContentT>
+template <CType EnumType, typename ContentT>
ContentPair make(const QJsonObject& json)
{
return { EnumType, new ContentT(json) };
@@ -91,59 +42,53 @@ ContentPair makeVideo(const QJsonObject& json)
if (infoJson.contains("thumbnail_url"))
{
c->thumbnail = ImageInfo(infoJson["thumbnail_url"].toString(),
- infoJson["thumbnail_info"].toObject());
+ infoJson["thumbnail_info"].toObject());
}
- return { MessageEventType::Video, c };
+ return { CType::Video, c };
};
ContentPair makeUnknown(const QJsonObject& json)
{
qCDebug(EVENTS) << "RoomMessageEvent: couldn't resolve msgtype, JSON follows:";
qCDebug(EVENTS) << json;
- return { MessageEventType::Unknown, new Base };
+ return { CType::Unknown, new Base() };
}
-RoomMessageEvent* RoomMessageEvent::fromJson(const QJsonObject& obj)
+RoomMessageEvent::RoomMessageEvent(const QJsonObject& obj)
+ : RoomEvent(Type::RoomMessage, obj), _msgtype(CType::Unknown)
+ , _content(nullptr)
{
- RoomMessageEvent* e = new RoomMessageEvent();
- e->parseJson(obj);
- if( obj.contains("sender") )
+ const QJsonObject content = contentJson();
+ if ( content.contains("msgtype") && content.contains("body") )
{
- e->d->userId = obj.value("sender").toString();
- } else {
- qCDebug(EVENTS) << "RoomMessageEvent: user_id not found";
+ _plainBody = content["body"].toString();
+
+ auto factory = lookup(content["msgtype"].toString(),
+ "m.text", &make<CType::Text, TextContent>,
+ "m.emote", &make<CType::Emote, TextContent>,
+ "m.notice", &make<CType::Notice, TextContent>,
+ "m.image", &make<CType::Image, ImageContent>,
+ "m.file", &make<CType::File, FileContent>,
+ "m.location", &make<CType::Location, LocationContent>,
+ "m.video", &makeVideo,
+ "m.audio", &make<CType::Audio, AudioContent>,
+ // Insert new message types before this line
+ &makeUnknown
+ );
+ std::tie(_msgtype, _content) = factory(content);
}
- if( obj.contains("content") )
+ else
{
- const QJsonObject content = obj["content"].toObject();
- if ( content.contains("msgtype") && content.contains("body") )
- {
- e->d->plainBody = content["body"].toString();
-
- auto delegate = lookup(content.value("msgtype").toString(),
- "m.text", make<MessageEventType::Text, TextContent>,
- "m.emote", make<MessageEventType::Emote, TextContent>,
- "m.notice", make<MessageEventType::Notice, TextContent>,
- "m.image", make<MessageEventType::Image, ImageContent>,
- "m.file", make<MessageEventType::File, FileContent>,
- "m.location", make<MessageEventType::Location, LocationContent>,
- "m.video", makeVideo,
- "m.audio", make<MessageEventType::Audio, AudioContent>,
- // Insert new message types before this line
- makeUnknown
- );
- std::tie(e->d->msgtype, e->d->content) = delegate(content);
- }
- else
- {
- qCWarning(EVENTS) << "RoomMessageEvent(" << e->id() << "): no body or msgtype";
- qCDebug(EVENTS) << obj;
- }
+ qCWarning(EVENTS) << "No body or msgtype in room message event";
+ qCWarning(EVENTS) << formatJson << obj;
}
- return e;
}
-using namespace MessageEventContent;
+RoomMessageEvent::~RoomMessageEvent()
+{
+ if (_content)
+ delete _content;
+}
TextContent::TextContent(const QJsonObject& json)
{
diff --git a/events/roommessageevent.h b/events/roommessageevent.h
index 5d5336aa..6acaad6f 100644
--- a/events/roommessageevent.h
+++ b/events/roommessageevent.h
@@ -24,44 +24,45 @@
#include <QtCore/QMimeType>
#include <QtCore/QSize>
+#include <memory>
+
namespace QMatrixClient
{
- enum class MessageEventType
- {
- Text, Emote, Notice, Image, File, Location, Video, Audio, Unknown
- };
-
namespace MessageEventContent
{
- class Base { };
- }
-
- class RoomMessageEvent: public Event
- {
- public:
- RoomMessageEvent();
- virtual ~RoomMessageEvent();
-
- QString userId() const;
- MessageEventType msgtype() const;
+ class Base
+ {
+ Q_GADGET
+ public:
+ enum class Type
+ {
+ Text, Emote, Notice, Image, File, Location, Video, Audio, Unknown
+ };
- QString plainBody() const;
+ virtual ~Base() = default;
- /**
- * Same as plainBody() for now; might change for "best-looking body"
- * in the future. For richer contents, use content-specific data.
- *
- * @deprecated
- */
- QString body() const;
+ REGISTER_ENUM(Type)
+ };
+ using CType = Base::Type;
+ } // namespace MessageEventContent
+ using MessageEventType = MessageEventContent::CType;
- MessageEventContent::Base* content() const;
+ class RoomMessageEvent: public RoomEvent
+ {
+ public:
+ explicit RoomMessageEvent(const QJsonObject& obj);
+ ~RoomMessageEvent();
- static RoomMessageEvent* fromJson( const QJsonObject& obj );
+ const QString& userId() const { return _userId; }
+ MessageEventType msgtype() const { return _msgtype; }
+ const QString& plainBody() const { return _plainBody; }
+ const MessageEventContent::Base* content() const { return _content; }
private:
- class Private;
- Private* d;
+ QString _userId;
+ MessageEventType _msgtype;
+ QString _plainBody;
+ MessageEventContent::Base* _content;
};
namespace MessageEventContent
@@ -73,7 +74,7 @@ namespace QMatrixClient
class TextContent: public Base
{
public:
- TextContent(const QJsonObject& json);
+ explicit TextContent(const QJsonObject& json);
QMimeType mimeType;
QString body;
@@ -103,7 +104,7 @@ namespace QMatrixClient
class ThumbnailedContent: public ContentInfoT
{
public:
- ThumbnailedContent(const QJsonObject& json)
+ explicit ThumbnailedContent(const QJsonObject& json)
: ContentInfoT(json["url"].toString(), json["info"].toObject())
, thumbnail(json["thumbnail_url"].toString(),
json["thumbnail_info"].toObject())
@@ -118,7 +119,7 @@ namespace QMatrixClient
class LocationContent: public Base
{
public:
- LocationContent(const QJsonObject& json);
+ explicit LocationContent(const QJsonObject& json);
QString geoUri;
ImageInfo thumbnail;
@@ -142,5 +143,5 @@ namespace QMatrixClient
int duration;
};
using AudioContent = ThumbnailedContent<AudioInfo>;
- }
-}
+ } // namespace MessageEventContent
+} // namespace QMatrixClient
diff --git a/events/roomnameevent.cpp b/events/roomnameevent.cpp
index c94cb2c3..c202d17a 100644
--- a/events/roomnameevent.cpp
+++ b/events/roomnameevent.cpp
@@ -20,33 +20,3 @@
using namespace QMatrixClient;
-class RoomNameEvent::Private
-{
- public:
- QString name;
-};
-
-RoomNameEvent::RoomNameEvent() :
- Event(EventType::RoomName),
- d(new Private)
-{
-}
-
-RoomNameEvent::~RoomNameEvent()
-{
- delete d;
-}
-
-QString RoomNameEvent::name() const
-{
- return d->name;
-}
-
-RoomNameEvent* RoomNameEvent::fromJson(const QJsonObject& obj)
-{
- RoomNameEvent* e = new RoomNameEvent();
- e->parseJson(obj);
- const QJsonObject contents = obj.value("content").toObject();
- e->d->name = contents.value("name").toString();
- return e;
-}
diff --git a/events/roomnameevent.h b/events/roomnameevent.h
index 8748c4be..bb823933 100644
--- a/events/roomnameevent.h
+++ b/events/roomnameevent.h
@@ -22,18 +22,17 @@
namespace QMatrixClient
{
- class RoomNameEvent : public Event
+ class RoomNameEvent : public RoomEvent
{
public:
- RoomNameEvent();
- virtual ~RoomNameEvent();
+ explicit RoomNameEvent(const QJsonObject& obj)
+ : RoomEvent(Type::RoomName, obj)
+ , _name(contentJson()["name"].toString())
+ { }
- QString name() const;
-
- static RoomNameEvent* fromJson(const QJsonObject& obj);
+ QString name() const { return _name; }
private:
- class Private;
- Private *d;
+ QString _name{};
};
-}
+} // namespace QMatrixClient
diff --git a/events/roomtopicevent.cpp b/events/roomtopicevent.cpp
index 2e186c4b..26677e78 100644
--- a/events/roomtopicevent.cpp
+++ b/events/roomtopicevent.cpp
@@ -20,32 +20,3 @@
using namespace QMatrixClient;
-class RoomTopicEvent::Private
-{
- public:
- QString topic;
-};
-
-RoomTopicEvent::RoomTopicEvent()
- : Event(EventType::RoomTopic)
- , d(new Private)
-{
-}
-
-RoomTopicEvent::~RoomTopicEvent()
-{
- delete d;
-}
-
-QString RoomTopicEvent::topic() const
-{
- return d->topic;
-}
-
-RoomTopicEvent* RoomTopicEvent::fromJson(const QJsonObject& obj)
-{
- auto e = new RoomTopicEvent();
- e->parseJson(obj);
- e->d->topic = obj.value("content").toObject().value("topic").toString();
- return e;
-}
diff --git a/events/roomtopicevent.h b/events/roomtopicevent.h
index 4b0a24b0..fb849afe 100644
--- a/events/roomtopicevent.h
+++ b/events/roomtopicevent.h
@@ -22,18 +22,17 @@
namespace QMatrixClient
{
- class RoomTopicEvent: public Event
+ class RoomTopicEvent: public RoomEvent
{
public:
- RoomTopicEvent();
- virtual ~RoomTopicEvent();
+ explicit RoomTopicEvent(const QJsonObject& obj)
+ : RoomEvent(Type::RoomTopic, obj)
+ , _topic(contentJson()["topic"].toString())
+ { }
- QString topic() const;
-
- static RoomTopicEvent* fromJson(const QJsonObject& obj);
+ QString topic() const { return _topic; }
private:
- class Private;
- Private* d;
+ QString _topic;
};
-}
+} // namespace QMatrixClient
diff --git a/events/typingevent.cpp b/events/typingevent.cpp
index 009059af..a4d3bae4 100644
--- a/events/typingevent.cpp
+++ b/events/typingevent.cpp
@@ -18,43 +18,15 @@
#include "typingevent.h"
-#include "logging.h"
-
-#include <QtCore/QJsonArray>
-
using namespace QMatrixClient;
-class TypingEvent::Private
-{
- public:
- QStringList users;
-};
-
-TypingEvent::TypingEvent()
- : Event(EventType::Typing)
- , d( new Private )
-{
-}
-
-TypingEvent::~TypingEvent()
+TypingEvent::TypingEvent(const QJsonObject& obj)
+ : Event(Type::Typing, obj)
{
- delete d;
-}
-
-QStringList TypingEvent::users()
-{
- return d->users;
-}
-
-TypingEvent* TypingEvent::fromJson(const QJsonObject& obj)
-{
- TypingEvent* e = new TypingEvent();
- e->parseJson(obj);
- QJsonArray array = obj.value("content").toObject().value("user_ids").toArray();
+ QJsonValue result;
+ result= contentJson()["user_ids"];
+ QJsonArray array = result.toArray();
for( const QJsonValue& user: array )
- {
- e->d->users << user.toString();
- }
- qCDebug(EPHEMERAL) << "Typing:" << e->d->users;
- return e;
+ _users.push_back(user.toString());
}
+
diff --git a/events/typingevent.h b/events/typingevent.h
index da57a389..b12d224e 100644
--- a/events/typingevent.h
+++ b/events/typingevent.h
@@ -27,15 +27,11 @@ namespace QMatrixClient
class TypingEvent: public Event
{
public:
- TypingEvent();
- virtual ~TypingEvent();
+ TypingEvent(const QJsonObject& obj);
- QStringList users();
-
- static TypingEvent* fromJson(const QJsonObject& obj);
+ QStringList users() const { return _users; }
private:
- class Private;
- Private* d;
+ QStringList _users;
};
-}
+} // namespace QMatrixClient
diff --git a/examples/qmc-example.cpp b/examples/qmc-example.cpp
index 7b45a785..a6da6aba 100644
--- a/examples/qmc-example.cpp
+++ b/examples/qmc-example.cpp
@@ -1,12 +1,15 @@
-#include <iostream>
-#include <QCoreApplication>
#include "connection.h"
#include "room.h"
+#include <QCoreApplication>
+#include <iostream>
+#include <string>
+
using namespace QMatrixClient;
using std::cout;
using std::endl;
+using std::string;
void onNewRoom(Room* r)
{
@@ -16,11 +19,11 @@ void onNewRoom(Room* r)
cout << " Name: " << r->name().toStdString() << endl;
cout << " Canonical alias: " << r->canonicalAlias().toStdString() << endl;
});
- QObject::connect(r, &Room::aboutToAddNewMessages, [=] (Events evs) {
+ QObject::connect(r, &Room::aboutToAddNewMessages, [=] (RoomEvents evs) {
cout << "New events in room " << r->id().toStdString() << ":" << endl;
for (auto e: evs)
{
- cout << e->originalJson().toStdString() << endl;
+ cout << string(e->originalJson()) << endl;
}
});
}
diff --git a/jobs/passwordlogin.h b/jobs/passwordlogin.h
index 713a1821..6b7db0b3 100644
--- a/jobs/passwordlogin.h
+++ b/jobs/passwordlogin.h
@@ -40,4 +40,4 @@ namespace QMatrixClient
class Private;
Private* d;
};
-}
+} // namespace QMatrixClient
diff --git a/jobs/roommessagesjob.cpp b/jobs/roommessagesjob.cpp
index a48403c8..3e603a50 100644
--- a/jobs/roommessagesjob.cpp
+++ b/jobs/roommessagesjob.cpp
@@ -17,16 +17,15 @@
*/
#include "roommessagesjob.h"
-#include "../util.h"
-#include <QtCore/QJsonArray>
+#include "util.h"
using namespace QMatrixClient;
class RoomMessagesJob::Private
{
public:
- Owning<Events> events;
+ Owning<RoomEvents> events;
QString end;
};
@@ -49,7 +48,7 @@ RoomMessagesJob::~RoomMessagesJob()
delete d;
}
-Events RoomMessagesJob::releaseEvents()
+RoomEvents RoomMessagesJob::releaseEvents()
{
return d->events.release();
}
@@ -62,7 +61,7 @@ QString RoomMessagesJob::end()
BaseJob::Status RoomMessagesJob::parseJson(const QJsonDocument& data)
{
QJsonObject obj = data.object();
- d->events.assign(eventsFromJson(obj.value("chunk").toArray()));
+ d->events.assign(makeEvents<RoomEvent>(obj.value("chunk").toArray()));
d->end = obj.value("end").toString();
return Success;
}
diff --git a/jobs/roommessagesjob.h b/jobs/roommessagesjob.h
index 2d15d9d4..a029c27c 100644
--- a/jobs/roommessagesjob.h
+++ b/jobs/roommessagesjob.h
@@ -34,7 +34,7 @@ namespace QMatrixClient
FetchDirection dir = FetchDirection::Backward);
virtual ~RoomMessagesJob();
- Events releaseEvents();
+ RoomEvents releaseEvents();
QString end();
protected:
diff --git a/jobs/syncjob.cpp b/jobs/syncjob.cpp
index 5984128f..29ddc2e6 100644
--- a/jobs/syncjob.cpp
+++ b/jobs/syncjob.cpp
@@ -18,7 +18,6 @@
#include "syncjob.h"
-#include <QtCore/QJsonArray>
#include <QtCore/QElapsedTimer>
using namespace QMatrixClient;
@@ -96,11 +95,6 @@ BaseJob::Status SyncJob::parseJson(const QJsonDocument& data)
return Success;
}
-void SyncRoomData::EventList::fromJson(const QJsonObject& roomContents)
-{
- assign(eventsFromJson(roomContents[jsonKey].toObject()["events"].toArray()));
-}
-
SyncRoomData::SyncRoomData(const QString& roomId_, JoinState joinState_,
const QJsonObject& room_)
: roomId(roomId_)
diff --git a/jobs/syncjob.h b/jobs/syncjob.h
index 48be9423..07824e23 100644
--- a/jobs/syncjob.h
+++ b/jobs/syncjob.h
@@ -20,39 +20,46 @@
#include "basejob.h"
-#include "../joinstate.h"
-#include "../events/event.h"
+#include "joinstate.h"
+#include "events/event.h"
#include "util.h"
namespace QMatrixClient
{
class SyncRoomData
{
- public:
- class EventList : public Owning<Events>
- {
- private:
- QString jsonKey;
- public:
- explicit EventList(QString k) : jsonKey(std::move(k)) { }
- void fromJson(const QJsonObject& roomContents);
- };
+ public:
+ template <typename EventT>
+ class Batch : public Owning<EventsBatch<EventT>>
+ {
+ public:
+ explicit Batch(QString k) : jsonKey(std::move(k)) { }
+ void fromJson(const QJsonObject& roomContents)
+ {
+ this->assign(makeEvents<EventT>(
+ roomContents[jsonKey].toObject()["events"].toArray()));
+ }
+
+
+ private:
+ QString jsonKey;
+ };
- QString roomId;
- JoinState joinState;
- EventList state;
- EventList timeline;
- EventList ephemeral;
- EventList accountData;
- EventList inviteState;
+ QString roomId;
+ JoinState joinState;
+ Batch<RoomEvent> state;
+ Batch<RoomEvent> timeline;
+ Batch<Event> ephemeral;
+ Batch<Event> accountData;
+ Batch<Event> inviteState;
- bool timelineLimited;
- QString timelinePrevBatch;
- int highlightCount;
- int notificationCount;
+ bool timelineLimited;
+ QString timelinePrevBatch;
+ int highlightCount;
+ int notificationCount;
- SyncRoomData(const QString& roomId, JoinState joinState_,
- const QJsonObject& room_);
+ SyncRoomData(const QString& roomId, JoinState joinState_,
+ const QJsonObject& room_);
};
} // namespace QMatrixClient
Q_DECLARE_TYPEINFO(QMatrixClient::SyncRoomData, Q_MOVABLE_TYPE);
diff --git a/room.cpp b/room.cpp
index 9f57f3fd..6c22b338 100644
--- a/room.cpp
+++ b/room.cpp
@@ -96,18 +96,18 @@ class Room::Private
void getPreviousContent(int limit = 10);
- bool isEventNotable(const Event* e) const
+ bool isEventNotable(const RoomEvent* e) const
{
return e->senderId() != connection->userId() &&
e->type() == EventType::RoomMessage;
}
- void appendEvent(Event* e)
+ void appendEvent(RoomEvent* e)
{
insertEvent(e, timeline.end(),
timeline.empty() ? 0 : q->maxTimelineIndex() + 1);
}
- void prependEvent(Event* e)
+ void prependEvent(RoomEvent* e)
{
insertEvent(e, timeline.begin(),
timeline.empty() ? 0 : q->minTimelineIndex() - 1);
@@ -116,7 +116,7 @@ class Room::Private
/**
* Removes events from the passed container that are already in the timeline
*/
- void dropDuplicateEvents(Events* events) const;
+ void dropDuplicateEvents(RoomEvents* events) const;
void setLastReadEvent(User* u, const QString& eventId);
rev_iter_pair_t promoteReadMarker(User* u, rev_iter_t newMarker);
@@ -128,7 +128,7 @@ class Room::Private
void insertMemberIntoMap(User* u);
void removeMemberFromMap(const QString& username, User* u);
- void insertEvent(Event* e, Timeline::iterator where,
+ void insertEvent(RoomEvent* e, Timeline::iterator where,
TimelineItem::index_t index);
};
@@ -394,10 +394,10 @@ void Room::Private::removeMemberFromMap(const QString& username, User* u)
inline QByteArray makeErrorStr(const Event* e, const char* msg)
{
return QString("%1; event dump follows:\n%2")
- .arg(msg, e->originalJson()).toUtf8();
+ .arg(msg, QString(e->originalJson())).toUtf8();
}
-void Room::Private::insertEvent(Event* e, Timeline::iterator where,
+void Room::Private::insertEvent(RoomEvent* e, Timeline::iterator where,
TimelineItem::index_t index)
{
Q_ASSERT_X(e, __FUNCTION__, "Attempt to add nullptr to timeline");
@@ -530,7 +530,7 @@ void Room::updateData(SyncRoomData&& data)
qCDebug(PROFILER) << "*** Room::addNewMessageEvents():" << et.elapsed() << "ms";
et.restart();
- for( Event* ephemeralEvent: data.ephemeral )
+ for( auto ephemeralEvent: data.ephemeral )
{
processEphemeralEvent(ephemeralEvent);
}
@@ -586,12 +586,12 @@ void Room::leaveRoom() const
connection()->callApi<LeaveRoomJob>(id());
}
-void Room::Private::dropDuplicateEvents(Events* events) const
+void Room::Private::dropDuplicateEvents(RoomEvents* events) const
{
// Collect all duplicate events at the end of the container
auto dupsBegin =
std::stable_partition(events->begin(), events->end(),
- [&] (Event* e) { return !eventsIndex.contains(e->id()); });
+ [&] (RoomEvent* e) { return !eventsIndex.contains(e->id()); });
// Dispose of those dups
std::for_each(dupsBegin, events->end(), [] (Event* e) { delete e; });
events->erase(dupsBegin, events->end());
@@ -602,7 +602,7 @@ Connection* Room::connection() const
return d->connection;
}
-void Room::addNewMessageEvents(Events events)
+void Room::addNewMessageEvents(RoomEvents events)
{
d->dropDuplicateEvents(&events);
if (events.empty())
@@ -612,9 +612,9 @@ void Room::addNewMessageEvents(Events events)
emit addedMessages();
}
-void Room::doAddNewMessageEvents(const Events& events)
+void Room::doAddNewMessageEvents(const RoomEvents& events)
{
- Q_ASSERT(!events.isEmpty());
+ Q_ASSERT(!events.empty());
Timeline::size_type newUnreadMessages = 0;
for (auto e: events)
@@ -647,7 +647,7 @@ void Room::doAddNewMessageEvents(const Events& events)
}
}
-void Room::addHistoricalMessageEvents(Events events)
+void Room::addHistoricalMessageEvents(RoomEvents events)
{
d->dropDuplicateEvents(&events);
if (events.empty())
@@ -657,9 +657,9 @@ void Room::addHistoricalMessageEvents(Events events)
emit addedMessages();
}
-void Room::doAddHistoricalMessageEvents(const Events& events)
+void Room::doAddHistoricalMessageEvents(const RoomEvents& events)
{
- Q_ASSERT(!events.isEmpty());
+ Q_ASSERT(!events.empty());
// Historical messages arrive in newest-to-oldest order
for (auto e: events)
d->prependEvent(e);
@@ -667,52 +667,57 @@ void Room::doAddHistoricalMessageEvents(const Events& events)
<< "past events; the oldest event is now" << d->timeline.front();
}
-void Room::processStateEvents(const Events& events)
+void Room::processStateEvents(const RoomEvents& events)
{
bool emitNamesChanged = false;
for (auto event: events)
{
- if( event->type() == EventType::RoomName )
+ switch (event->type())
{
- auto nameEvent = static_cast<RoomNameEvent*>(event);
- d->name = nameEvent->name();
- qCDebug(MAIN) << "room name:" << d->name;
- emitNamesChanged = true;
- }
- if( event->type() == EventType::RoomAliases )
- {
- auto aliasesEvent = static_cast<RoomAliasesEvent*>(event);
- d->aliases = aliasesEvent->aliases();
- qCDebug(MAIN) << "room aliases:" << d->aliases;
- emitNamesChanged = true;
- }
- if( event->type() == EventType::RoomCanonicalAlias )
- {
- auto aliasEvent = static_cast<RoomCanonicalAliasEvent*>(event);
- d->canonicalAlias = aliasEvent->alias();
- qCDebug(MAIN) << "room canonical alias:" << d->canonicalAlias;
- emitNamesChanged = true;
- }
- if( event->type() == EventType::RoomTopic )
- {
- auto topicEvent = static_cast<RoomTopicEvent*>(event);
- d->topic = topicEvent->topic();
- emit topicChanged();
- }
- if( event->type() == EventType::RoomMember )
- {
- auto memberEvent = static_cast<RoomMemberEvent*>(event);
- // Can't use d->member() below because the user may be not a member (yet)
- User* u = d->connection->user(memberEvent->userId());
- u->processEvent(event);
- if( memberEvent->membership() == MembershipType::Join )
- {
- d->addMember(u);
+ case EventType::RoomName: {
+ auto nameEvent = static_cast<RoomNameEvent*>(event);
+ d->name = nameEvent->name();
+ qCDebug(MAIN) << "Room name updated:" << d->name;
+ emitNamesChanged = true;
+ break;
}
- else if( memberEvent->membership() == MembershipType::Leave )
- {
- d->removeMember(u);
+ case EventType::RoomAliases: {
+ auto aliasesEvent = static_cast<RoomAliasesEvent*>(event);
+ d->aliases = aliasesEvent->aliases();
+ qCDebug(MAIN) << "Room aliases updated:" << d->aliases;
+ emitNamesChanged = true;
+ break;
+ }
+ case EventType::RoomCanonicalAlias: {
+ auto aliasEvent = static_cast<RoomCanonicalAliasEvent*>(event);
+ d->canonicalAlias = aliasEvent->alias();
+ qCDebug(MAIN) << "Room canonical alias updated:" << d->canonicalAlias;
+ emitNamesChanged = true;
+ break;
}
+ case EventType::RoomTopic: {
+ auto topicEvent = static_cast<RoomTopicEvent*>(event);
+ d->topic = topicEvent->topic();
+ qCDebug(MAIN) << "Room topic updated:" << d->topic;
+ emit topicChanged();
+ break;
+ }
+ case EventType::RoomMember: {
+ auto memberEvent = static_cast<RoomMemberEvent*>(event);
+ // Can't use d->member() below because the user may be not a member (yet)
+ User* u = d->connection->user(memberEvent->userId());
+ u->processEvent(event);
+ if( memberEvent->membership() == MembershipType::Join )
+ {
+ d->addMember(u);
+ }
+ else if( memberEvent->membership() == MembershipType::Leave )
+ {
+ d->removeMember(u);
+ }
+ break;
+ }
+ default: /* Ignore events of other types */;
}
}
if (emitNamesChanged) {
@@ -723,53 +728,57 @@ void Room::processStateEvents(const Events& events)
void Room::processEphemeralEvent(Event* event)
{
- if( event->type() == EventType::Typing )
- {
- auto typingEvent = static_cast<TypingEvent*>(event);
- d->usersTyping.clear();
- for( const QString& userId: typingEvent->users() )
- {
- if (auto m = d->member(userId))
- d->usersTyping.append(m);
- }
- emit typingChanged();
- }
- if( event->type() == EventType::Receipt )
+ switch (event->type())
{
- auto receiptEvent = static_cast<ReceiptEvent*>(event);
- for( const auto &eventReceiptPair: receiptEvent->events() )
- {
- const auto& eventId = eventReceiptPair.first;
- const auto& receipts = eventReceiptPair.second;
+ case EventType::Typing: {
+ auto typingEvent = static_cast<TypingEvent*>(event);
+ d->usersTyping.clear();
+ for( const QString& userId: typingEvent->users() )
{
- if (receipts.size() == 1)
- qCDebug(EPHEMERAL) << "Marking" << eventId
- << "as read for" << receipts[0].userId;
- else
- qCDebug(EPHEMERAL) << "Marking" << eventId
- << "as read for"
- << receipts.size() << "users";
+ if (auto m = d->member(userId))
+ d->usersTyping.append(m);
}
- if (d->eventsIndex.contains(eventId))
- {
- const auto newMarker = findInTimeline(eventId);
- for( const Receipt& r: receipts )
- if (auto m = d->member(r.userId))
- d->promoteReadMarker(m, newMarker);
- } else
+ emit typingChanged();
+ break;
+ }
+ case EventType::Receipt: {
+ auto receiptEvent = static_cast<ReceiptEvent*>(event);
+ for( const auto &p: receiptEvent->eventsWithReceipts() )
{
- qCDebug(EPHEMERAL) << "Event" << eventId
- << "not found; saving read receipts anyway";
- // If the event is not found (most likely, because it's too old
- // and hasn't been fetched from the server yet), but there is
- // a previous marker for a user, keep the previous marker.
- // Otherwise, blindly store the event id for this user.
- for( const Receipt& r: receipts )
- if (auto m = d->member(r.userId))
- if (readMarker(m) == timelineEdge())
- d->setLastReadEvent(m, eventId);
+ {
+ if (p.receipts.size() == 1)
+ qCDebug(EPHEMERAL) << "Marking" << p.evtId
+ << "as read for" << p.receipts[0].userId;
+ else
+ qCDebug(EPHEMERAL) << "Marking" << p.evtId
+ << "as read for"
+ << p.receipts.size() << "users";
+ }
+ if (d->eventsIndex.contains(p.evtId))
+ {
+ const auto newMarker = findInTimeline(p.evtId);
+ for( const Receipt& r: p.receipts )
+ if (auto m = d->member(r.userId))
+ d->promoteReadMarker(m, newMarker);
+ } else
+ {
+ qCDebug(EPHEMERAL) << "Event" << p.evtId
+ << "not found; saving read receipts anyway";
+ // If the event is not found (most likely, because it's too old
+ // and hasn't been fetched from the server yet), but there is
+ // a previous marker for a user, keep the previous marker.
+ // Otherwise, blindly store the event id for this user.
+ for( const Receipt& r: p.receipts )
+ if (auto m = d->member(r.userId))
+ if (readMarker(m) == timelineEdge())
+ d->setLastReadEvent(m, p.evtId);
+ }
}
+ break;
}
+ default:
+ qCWarning(EPHEMERAL) << "Unexpected event type in 'ephemeral' batch:"
+ << event->type();
}
}
diff --git a/room.h b/room.h
index 60be33b9..4f7a3c97 100644
--- a/room.h
+++ b/room.h
@@ -43,14 +43,14 @@ namespace QMatrixClient
// a std:: container now
using index_t = int;
- TimelineItem(Event* e, index_t number) : evt(e), idx(number) { }
+ TimelineItem(RoomEvent* e, index_t number) : evt(e), idx(number) { }
- Event* event() const { return evt.get(); }
- Event* operator->() const { return event(); } //< Synonym for event()
+ RoomEvent* event() const { return evt.get(); }
+ RoomEvent* operator->() const { return event(); } //< Synonym for event()
index_t index() const { return idx; }
private:
- std::unique_ptr<Event> evt;
+ std::unique_ptr<RoomEvent> evt;
index_t idx;
};
inline QDebug& operator<<(QDebug& d, const TimelineItem& ti)
@@ -152,8 +152,8 @@ namespace QMatrixClient
void userRenamed(User* user, QString oldName);
signals:
- void aboutToAddHistoricalMessages(const Events& events);
- void aboutToAddNewMessages(const Events& events);
+ void aboutToAddHistoricalMessages(const RoomEvents& events);
+ void aboutToAddNewMessages(const RoomEvents& events);
void addedMessages();
/**
@@ -177,17 +177,17 @@ namespace QMatrixClient
void unreadMessagesChanged(Room* room);
protected:
- virtual void doAddNewMessageEvents(const Events& events);
- virtual void doAddHistoricalMessageEvents(const Events& events);
- virtual void processStateEvents(const Events& events);
+ virtual void doAddNewMessageEvents(const RoomEvents& events);
+ virtual void doAddHistoricalMessageEvents(const RoomEvents& events);
+ virtual void processStateEvents(const RoomEvents& events);
virtual void processEphemeralEvent(Event* event);
private:
class Private;
Private* d;
- void addNewMessageEvents(Events events);
- void addHistoricalMessageEvents(Events events);
+ void addNewMessageEvents(RoomEvents events);
+ void addHistoricalMessageEvents(RoomEvents events);
void markMessagesAsRead(rev_iter_t upToMarker);
};
@@ -209,4 +209,4 @@ namespace QMatrixClient
private:
const Room* room;
};
-}
+} // namespace QMatrixClient
diff --git a/user.cpp b/user.cpp
index 17714bee..f2a3a33b 100644
--- a/user.cpp
+++ b/user.cpp
@@ -133,7 +133,7 @@ void User::processEvent(Event* event)
{
if( event->type() == EventType::RoomMember )
{
- RoomMemberEvent* e = static_cast<RoomMemberEvent*>(event);
+ auto e = static_cast<RoomMemberEvent*>(event);
if (e->membership() == MembershipType::Leave)
return;
diff --git a/util.h b/util.h
index 79f76860..c9e2d1c9 100644
--- a/util.h
+++ b/util.h
@@ -18,6 +18,11 @@
#pragma once
+#include <QtCore/QMetaEnum>
+#include <QtCore/QDebug>
+
+#include <functional>
+
namespace QMatrixClient
{
/**
@@ -71,14 +76,19 @@ namespace QMatrixClient
* @brief Lookup a value by a key in a varargs list
*
* This function template takes the value of its first argument (selector)
- * as a key and searches for it in the key-value map passed in a varargs list
- * (every next pair of arguments forms a key-value pair). If a match is found,
- * the respective value is returned; if no pairs matched, the last value
- * (fallback) is returned.
+ * as a key and searches for it in the key-value map passed in
+ * a parameter pack (every next pair of arguments forms a key-value pair).
+ * If a match is found, the respective value is returned; if no pairs
+ * matched, the last value (fallback) is returned.
*
* All options should be of the same type or implicitly castable to the
- * type of the first option. Note that pointers to methods of different
- * classes are of different object types, in particular.
+ * type of the first option. If you need some specific type to cast to
+ * you can explicitly provide it as the ValueT template parameter
+ * (e.g. <code>lookup<void*>(parameters...)</code>). Note that pointers
+ * to methods of different classes and even to functions with different
+ * signatures are of different types. If their return types are castable
+ * to some common one, @see dispatch that deals with this by swallowing
+ * the method invocation.
*
* Below is an example of usage to select a parser depending on contents of
* a JSON object:
@@ -91,7 +101,7 @@ namespace QMatrixClient
* }
*
* The implementation is based on tail recursion; every recursion step
- * removes 2 arguments (match and option). There's no selector value for the
+ * removes 2 arguments (match and value). There's no selector value for the
* fallback option (the last one); therefore, the total number of lookup()
* arguments should be even: selector + n key-value pairs + fallback
*
@@ -99,20 +109,144 @@ namespace QMatrixClient
* (the first parameter) - most likely it won't do what you expect because
* of shallow comparison.
*/
+ template <typename ValueT, typename SelectorT>
+ ValueT lookup(SelectorT/*unused*/, ValueT&& fallback)
+ {
+ return std::forward<ValueT>(fallback);
+ }
+
template <typename ValueT, typename SelectorT, typename KeyT, typename... Ts>
- ValueT lookup(SelectorT selector, KeyT key, ValueT value, Ts... remainingMapping)
+ ValueT lookup(SelectorT&& selector, KeyT&& key, ValueT&& value, Ts&&... remainder)
{
if( selector == key )
- return value;
+ return std::forward<ValueT>(value);
// Drop the failed key-value pair and recurse with 2 arguments less.
- return lookup(selector, remainingMapping...);
+ return lookup<ValueT>(std::forward<SelectorT>(selector),
+ std::forward<Ts>(remainder)...);
}
- template <typename SelectorT, typename ValueT>
- ValueT lookup(SelectorT/*unused*/, ValueT fallback)
+ /**
+ * A wrapper around lookup() for functions of different types castable
+ * to a common std::function<> form
+ *
+ * This class uses std::function<> magic to first capture arguments of
+ * a yet-unknown function or function object, and then to coerce types of
+ * all functions/function objects passed for lookup to the type
+ * std::function<ResultT(ArgTs...). Without Dispatch<>, you would have
+ * to pass the specific function type to lookup, since your functions have
+ * different signatures. The type is not always obvious, and the resulting
+ * construct in client code would almost always be rather cumbersome.
+ * Dispatch<> deduces the necessary function type (well, almost - you still
+ * have to specify the result type) and hides the clumsiness. For more
+ * information on what std::function<> can wrap around, see
+ * https://cpptruths.blogspot.jp/2015/11/covariance-and-contravariance-in-c.html
+ *
+ * The function arguments are captured by value (i.e. copied) to avoid
+ * hard-to-find issues with dangling references in cases when a Dispatch<>
+ * object is passed across different contexts (e.g. returned from another
+ * function).
+ *
+ * \tparam ResultT - the desired type of a picked function invocation (mandatory)
+ * \tparam ArgTs - function argument types (deduced)
+ */
+#if __GNUC__ < 5 && __GNUC_MINOR__ < 9
+ // GCC 4.8 cannot cope with parameter packs inside lambdas; so provide a single
+ // argument version of Dispatch<> that we only need so far.
+ template <typename ResultT, typename ArgT>
+#else
+ template <typename ResultT, typename... ArgTs>
+#endif
+ class Dispatch
+ {
+ // The implementation takes a chapter from functional programming:
+ // Dispatch<> uses a function that in turn accepts a function as its
+ // argument. The sole purpose of the outer function (initialized by
+ // a lambda-expression in the constructor) is to store the arguments
+ // to any of the functions later looked up. The inner function (its
+ // type is defined by fn_t alias) is the one returned by lookup()
+ // invocation inside to().
+ //
+ // It's a bit counterintuitive to specify function parameters before
+ // the list of functions but otherwise it would take several overloads
+ // here to match all the ways a function-like behaviour can be done:
+ // reference-to-function, pointer-to-function, function object. This
+ // probably could be done as well but I preferred a more compact
+ // solution: you show what you have and if it's possible to bring all
+ // your functions to the same std::function<> based on what you have
+ // as parameters, the code will compile. If it's not possible, modern
+ // compilers are already good enough at pinpointing a specific place
+ // where types don't match.
+ public:
+#if __GNUC__ < 5 && __GNUC_MINOR__ < 9
+ using fn_t = std::function<ResultT(ArgT)>;
+ explicit Dispatch(ArgT&& arg)
+ : boundArgs([=](fn_t &&f) { return f(std::move(arg)); })
+ { }
+#else
+ using fn_t = std::function<ResultT(ArgTs...)>;
+ explicit Dispatch(ArgTs&&... args)
+ : boundArgs([=](fn_t &&f) { return f(std::move(args)...); })
+ { }
+#endif
+
+ template <typename... LookupParamTs>
+ ResultT to(LookupParamTs&&... lookupParams)
+ {
+ // Here's the magic, two pieces of it:
+ // 1. Specifying fn_t in lookup() wraps all functions in
+ // \p lookupParams into the same std::function<> type. This
+ // includes conversion of return types from more specific to more
+ // generic (because std::function is covariant by return types and
+ // contravariant by argument types (see the link in the Doxygen
+ // part of the comments).
+ auto fn = lookup<fn_t>(std::forward<LookupParamTs>(lookupParams)...);
+ // 2. Passing the result of lookup() to boundArgs() invokes the
+ // lambda-expression mentioned in the constructor, which simply
+ // invokes this passed function with a set of arguments captured
+ // by lambda.
+ if (fn)
+ return boundArgs(std::move(fn));
+
+ // A shortcut to allow passing nullptr for a function;
+ // a default-constructed ResultT will be returned
+ // (for pointers, it will be nullptr)
+ return {};
+ }
+
+ private:
+ std::function<ResultT(fn_t&&)> boundArgs;
+ };
+
+ /**
+ * Dispatch a set of parameters to one of a set of functions, depending on
+ * a selector value
+ *
+ * Use <code>dispatch<CommonType>(parameters).to(lookup parameters)</code>
+ * instead of lookup() if you need to pick one of several functions returning
+ * types castable to the same CommonType. See event.cpp for a typical use case.
+ *
+ * \see Dispatch
+ */
+ template <typename ResultT, typename... ArgTs>
+ Dispatch<ResultT, ArgTs...> dispatch(ArgTs&& ... args)
{
- return fallback;
+ return Dispatch<ResultT, ArgTs...>(std::forward<ArgTs...>(args)...);
+ };
+
+ // The below enables pretty-printing of enums in logs
+#if (QT_VERSION >= QT_VERSION_CHECK(5, 5, 0))
+#define REGISTER_ENUM(EnumName) Q_ENUM(EnumName)
+#else
+ // Thanks to Olivier for spelling it and for making Q_ENUM to replace it:
+ // https://woboq.com/blog/q_enum.html
+#define REGISTER_ENUM(EnumName) \
+ Q_ENUMS(EnumName) \
+ friend QDebug operator<<(QDebug dbg, EnumName val) \
+ { \
+ static int enumIdx = staticMetaObject.indexOfEnumerator(#EnumName); \
+ return dbg << Event::staticMetaObject.enumerator(enumIdx).valueToKey(int(val)); \
}
+#endif
} // namespace QMatrixClient