diff options
-rw-r--r-- | events/event.cpp | 23 | ||||
-rw-r--r-- | events/event.h | 54 | ||||
-rw-r--r-- | examples/qmc-example.cpp | 12 | ||||
-rw-r--r-- | jobs/roommessagesjob.cpp | 6 | ||||
-rw-r--r-- | jobs/roommessagesjob.h | 2 | ||||
-rw-r--r-- | jobs/syncjob.h | 2 | ||||
-rw-r--r-- | room.cpp | 244 | ||||
-rw-r--r-- | room.h | 36 | ||||
-rw-r--r-- | util.h | 47 |
9 files changed, 205 insertions, 221 deletions
diff --git a/events/event.cpp b/events/event.cpp index 1e2d89f2..01f473ce 100644 --- a/events/event.cpp +++ b/events/event.cpp @@ -42,6 +42,8 @@ Event::Event(Type type, const QJsonObject& rep) } } +Event::~Event() = default; + QByteArray Event::originalJson() const { return QJsonDocument(_originalJson).toJson(); @@ -72,14 +74,15 @@ inline BaseEventT* makeIfMatches(const QJsonObject& o, const QString& selector) return makeIfMatches<BaseEventT, EventTs...>(o, selector); } -Event* Event::fromJson(const QJsonObject& obj) +template <> +EventPtr QMatrixClient::makeEvent<Event>(const QJsonObject& obj) { // Check more specific event types first - if (auto e = RoomEvent::fromJson(obj)) - return e; + if (auto e = makeEvent<RoomEvent>(obj)) + return EventPtr(move(e)); - return makeIfMatches<Event, - TypingEvent, ReceiptEvent>(obj, obj["type"].toString()); + return EventPtr { makeIfMatches<Event, + TypingEvent, ReceiptEvent>(obj, obj["type"].toString()) }; } RoomEvent::RoomEvent(Event::Type type) : Event(type) { } @@ -119,8 +122,7 @@ RoomEvent::RoomEvent(Type type, const QJsonObject& rep) qCDebug(EVENTS) << "Event transactionId:" << _txnId; } -RoomEvent::~RoomEvent() -{ /* Let QScopedPointer<RedactionEvent> do its job */ } +RoomEvent::~RoomEvent() = default; // Let the smart pointer do its job QString RoomEvent::redactionReason() const { @@ -133,11 +135,12 @@ void RoomEvent::addId(const QString& id) _id = id; } -RoomEvent* RoomEvent::fromJson(const QJsonObject& obj) +template <> +RoomEventPtr QMatrixClient::makeEvent<RoomEvent>(const QJsonObject& obj) { - return makeIfMatches<RoomEvent, + return RoomEventPtr { makeIfMatches<RoomEvent, RoomMessageEvent, RoomNameEvent, RoomAliasesEvent, RoomCanonicalAliasEvent, RoomMemberEvent, RoomTopicEvent, RoomAvatarEvent, EncryptionEvent, RedactionEvent> - (obj, obj["type"].toString()); + (obj, obj["type"].toString()) }; } diff --git a/events/event.h b/events/event.h index bd33bb50..2b18bb46 100644 --- a/events/event.h +++ b/events/event.h @@ -25,8 +25,21 @@ #include "util.h" +#include <memory> + namespace QMatrixClient { + template <typename EventT> + using event_ptr_tt = std::unique_ptr<EventT>; + + /** Create an event with proper type from a JSON object + * Use this factory template to detect the type from the JSON object + * contents (the detected event type should derive from the template + * parameter type) and create an event object of that type. + */ + template <typename EventT> + event_ptr_tt<EventT> makeEvent(const QJsonObject& obj); + class Event { Q_GADGET @@ -64,12 +77,6 @@ namespace QMatrixClient // (and in most cases it will be a combination of other fields // instead of "content" field). - /** Create an event with proper type from a JSON object - * Use this factory to detect the type from the JSON object contents - * and create an event object of that type. - */ - static Event* fromJson(const QJsonObject& obj); - protected: const QJsonObject contentJson() const; @@ -82,6 +89,10 @@ namespace QMatrixClient Q_PROPERTY(QJsonObject contentJson READ contentJson CONSTANT) }; using EventType = Event::Type; + using EventPtr = event_ptr_tt<Event>; + + template <> + EventPtr makeEvent<Event>(const QJsonObject& obj); /** * \brief A vector of pointers to events with deserialisation capabilities @@ -94,7 +105,7 @@ namespace QMatrixClient * \tparam EventT base type of all events in the vector */ template <typename EventT> - class EventsBatch : public std::vector<EventT*> + class EventsBatch : public std::vector<event_ptr_tt<EventT>> { public: /** @@ -120,8 +131,10 @@ namespace QMatrixClient for (auto objValue: objs) { const auto o = objValue.toObject(); - auto e = EventT::fromJson(o); - this->push_back(e ? e : new EventT(EventType::Unknown, o)); + auto e { makeEvent<EventT>(o) }; + if (!e) + e.reset(new EventT(EventType::Unknown, o)); + this->emplace_back(std::move(e)); } } }; @@ -151,10 +164,10 @@ namespace QMatrixClient const QDateTime& timestamp() const { return _serverTimestamp; } const QString& roomId() const { return _roomId; } const QString& senderId() const { return _senderId; } - bool isRedacted() const { return redactedBecause(); } - RedactionEvent* redactedBecause() const + bool isRedacted() const { return bool(_redactedBecause); } + const RedactionEvent* redactedBecause() const { - return _redactedBecause.data(); + return _redactedBecause.get(); } QString redactionReason() const; const QString& transactionId() const { return _txnId; } @@ -180,18 +193,19 @@ namespace QMatrixClient */ void addId(const QString& id); - // "Static override" of the one in Event - static RoomEvent* fromJson(const QJsonObject& obj); - private: QString _id; QString _roomId; QString _senderId; QDateTime _serverTimestamp; - QScopedPointer<RedactionEvent> _redactedBecause; + event_ptr_tt<RedactionEvent> _redactedBecause; QString _txnId; }; using RoomEvents = EventsBatch<RoomEvent>; + using RoomEventPtr = event_ptr_tt<RoomEvent>; + + template <> + RoomEventPtr makeEvent<RoomEvent>(const QJsonObject& obj); /** * Conceptually similar to QStringView (but much more primitive), it's a @@ -199,10 +213,10 @@ namespace QMatrixClient * referring to the beginning and the end of a range in a RoomEvents * container. */ - struct RoomEventsView + struct RoomEventsRange { - RoomEvents::const_iterator from; - RoomEvents::const_iterator to; + RoomEvents::iterator from; + RoomEvents::iterator to; RoomEvents::size_type size() const { @@ -212,6 +226,8 @@ namespace QMatrixClient bool empty() const { return from == to; } RoomEvents::const_iterator begin() const { return from; } RoomEvents::const_iterator end() const { return to; } + RoomEvents::iterator begin() { return from; } + RoomEvents::iterator end() { return to; } }; template <typename ContentT> diff --git a/examples/qmc-example.cpp b/examples/qmc-example.cpp index ff7944ce..dbb9912b 100644 --- a/examples/qmc-example.cpp +++ b/examples/qmc-example.cpp @@ -20,16 +20,16 @@ void onNewRoom(Room* r) << " Canonical alias: " << r->canonicalAlias().toStdString() << endl << endl; }); - QObject::connect(r, &Room::aboutToAddNewMessages, [=] (RoomEventsView events) { - cout << events.size() << " new event(s) in room " + QObject::connect(r, &Room::aboutToAddNewMessages, [=] (RoomEventsRange timeline) { + cout << timeline.size() << " new event(s) in room " << r->id().toStdString() << ":" << endl; - for (auto e: events) + for (const auto& item: timeline) { cout << "From: " - << r->roomMembername(e->senderId()).toStdString() + << r->roomMembername(item->senderId()).toStdString() << endl << "Timestamp:" - << e->timestamp().toString().toStdString() << endl - << "JSON:" << endl << string(e->originalJson()) << endl; + << item->timestamp().toString().toStdString() << endl + << "JSON:" << endl << string(item->originalJson()) << endl; } }); } diff --git a/jobs/roommessagesjob.cpp b/jobs/roommessagesjob.cpp index 9af1b3a6..e5568f17 100644 --- a/jobs/roommessagesjob.cpp +++ b/jobs/roommessagesjob.cpp @@ -23,7 +23,7 @@ using namespace QMatrixClient; class RoomMessagesJob::Private { public: - Owning<RoomEvents> events; + RoomEvents events; QString end; }; @@ -46,9 +46,9 @@ RoomMessagesJob::~RoomMessagesJob() delete d; } -RoomEvents RoomMessagesJob::releaseEvents() +RoomEvents&& RoomMessagesJob::releaseEvents() { - return d->events.release(); + return move(d->events); } QString RoomMessagesJob::end() const diff --git a/jobs/roommessagesjob.h b/jobs/roommessagesjob.h index 9680d52c..7b3fd9c9 100644 --- a/jobs/roommessagesjob.h +++ b/jobs/roommessagesjob.h @@ -34,7 +34,7 @@ namespace QMatrixClient FetchDirection dir = FetchDirection::Backward); virtual ~RoomMessagesJob(); - RoomEvents releaseEvents(); + RoomEvents&& releaseEvents(); QString end() const; protected: diff --git a/jobs/syncjob.h b/jobs/syncjob.h index 08bd773e..e9288486 100644 --- a/jobs/syncjob.h +++ b/jobs/syncjob.h @@ -30,7 +30,7 @@ namespace QMatrixClient { public: template <typename EventT> - class Batch : public Owning<EventsBatch<EventT>> + class Batch : public EventsBatch<EventT> { public: explicit Batch(QString k) : jsonKey(std::move(k)) { } @@ -44,6 +44,7 @@ #include <array> using namespace QMatrixClient; +using namespace std::placeholders; enum EventsPlacement : int { Older = -1, Newer = 1 }; @@ -101,20 +102,34 @@ class Room::Private void getPreviousContent(int limit = 10); - bool isEventNotable(const RoomEvent* e) const + bool isEventNotable(const TimelineItem& ti) const { - return !e->isRedacted() && - e->senderId() != connection->userId() && - e->type() == EventType::RoomMessage; + return !ti->isRedacted() && + ti->senderId() != connection->userId() && + ti->type() == EventType::RoomMessage; } - void insertEvents(RoomEventsView events, EventsPlacement placement); + void addNewMessageEvents(RoomEvents&& events); + void addHistoricalMessageEvents(RoomEvents&& events); + + /** + * @brief Move events into the timeline + * + * Insert events into the timeline, either new or historical. + * Pointers in the original container become empty, the ownership + * is passed to the timeline container. + * @param events - the range of events to be inserted + * @param placement - position and direction of insertion: Older for + * historical messages, Newer for new ones + */ + Timeline::size_type insertEvents(RoomEventsRange&& events, + EventsPlacement placement); /** * Removes events from the passed container that are already in the timeline */ void dropDuplicateEvents(RoomEvents* events) const; - void checkUnreadMessages(RoomEventsView events); + void checkUnreadMessages(timeline_iter_t from); void setLastReadEvent(User* u, const QString& eventId); rev_iter_pair_t promoteReadMarker(User* u, rev_iter_t newMarker, @@ -122,7 +137,13 @@ class Room::Private void markMessagesAsRead(rev_iter_t upToMarker); - void processRedaction(const RedactionEvent* redaction); + /** + * @brief Apply redaction to the timeline + * + * Tries to find an event in the timeline and redact it; deletes the + * redaction event whether the redacted event was found or not. + */ + void processRedaction(RoomEventPtr redactionEvent); QJsonObject toJson() const; @@ -250,9 +271,8 @@ Room::Private::promoteReadMarker(User* u, Room::rev_iter_t newMarker, setLastReadEvent(u, (*(eagerMarker - 1))->id()); if (isLocalUser(u) && unreadMessages) { - auto stillUnreadMessagesCount = - count_if(eagerMarker, timeline.cend(), - [=](const TimelineItem& ti) { return isEventNotable(ti.event()); }); + auto stillUnreadMessagesCount = count_if(eagerMarker, timeline.cend(), + bind(&Room::Private::isEventNotable, this, _1)); if (stillUnreadMessagesCount == 0) { @@ -437,12 +457,13 @@ void Room::Private::removeMemberFromMap(const QString& username, User* u) emit q->memberRenamed(formerNamesakes[0]); } -inline QByteArray makeErrorStr(const Event* e, QByteArray msg) +inline QByteArray makeErrorStr(const Event& e, QByteArray msg) { - return msg.append("; event dump follows:\n").append(e->originalJson()); + return msg.append("; event dump follows:\n").append(e.originalJson()); } -void Room::Private::insertEvents(RoomEventsView events, EventsPlacement placement) +Room::Timeline::size_type Room::Private::insertEvents(RoomEventsRange&& events, + EventsPlacement placement) { // Historical messages arrive in newest-to-oldest order, so the process for // them is symmetric to the one for new messages. @@ -450,22 +471,26 @@ void Room::Private::insertEvents(RoomEventsView events, EventsPlacement placemen placement == Older ? timeline.front().index() : timeline.back().index(); auto baseIndex = index; - for (const auto e: events) + for (auto&& e: events) { + const auto eId = e->id(); Q_ASSERT_X(e, __FUNCTION__, "Attempt to add nullptr to timeline"); - Q_ASSERT_X(!e->id().isEmpty(), __FUNCTION__, - makeErrorStr(e, "Event with empty id cannot be in the timeline")); - Q_ASSERT_X(!eventsIndex.contains(e->id()), __FUNCTION__, - makeErrorStr(e, "Event is already in the timeline; " + Q_ASSERT_X(!eId.isEmpty(), __FUNCTION__, + makeErrorStr(*e, + "Event with empty id cannot be in the timeline")); + Q_ASSERT_X(!eventsIndex.contains(eId), __FUNCTION__, + makeErrorStr(*e, "Event is already in the timeline; " "incoming events were not properly deduplicated")); if (placement == Older) - timeline.emplace_front(e, --index); + timeline.emplace_front(move(e), --index); else - timeline.emplace_back(e, ++index); - eventsIndex.insert(e->id(), index); - Q_ASSERT(q->findInTimeline(e->id())->event() == e); + timeline.emplace_back(move(e), ++index); + eventsIndex.insert(eId, index); + Q_ASSERT(q->findInTimeline(eId)->event()->id() == eId); } + // Pointers in "events" are empty now, but events.size() didn't change Q_ASSERT(int(events.size()) == (index - baseIndex) * int(placement)); + return events.size(); } void Room::Private::addMember(User *u) @@ -578,15 +603,15 @@ void Room::updateData(SyncRoomData&& data) << et.elapsed() << "ms," << data.timeline.size() << "events"; et.restart(); - addNewMessageEvents(data.timeline.release()); + d->addNewMessageEvents(move(data.timeline)); qCDebug(PROFILER) << "*** Room::addNewMessageEvents():" << et.elapsed() << "ms"; } if (!data.ephemeral.empty()) { et.restart(); - for( auto ephemeralEvent: data.ephemeral ) - processEphemeralEvent(ephemeralEvent); + for( auto&& ephemeralEvent: data.ephemeral ) + processEphemeralEvent(move(ephemeralEvent)); qCDebug(PROFILER) << "*** Room::processEphemeralEvents():" << et.elapsed() << "ms"; } @@ -638,7 +663,7 @@ void Room::Private::getPreviousContent(int limit) connect( roomMessagesJob, &RoomMessagesJob::result, [=]() { if( !roomMessagesJob->error() ) { - q->addHistoricalMessageEvents(roomMessagesJob->releaseEvents()); + addHistoricalMessageEvents(roomMessagesJob->releaseEvents()); prevBatch = roomMessagesJob->end(); } roomMessagesJob = nullptr; @@ -682,35 +707,35 @@ void Room::Private::dropDuplicateEvents(RoomEvents* events) const if (events->empty()) return; - // Collect all duplicate events at the end of the container - auto dupsBegin = - std::stable_partition(events->begin(), events->end(), - [&] (RoomEvent* e) { return !eventsIndex.contains(e->id()); }); - - if (dupsBegin != events->begin()) - { - // Check the batch itself for dups - auto eIt = events->begin(); - for (auto baseId = (*eIt)->id(); ++eIt < dupsBegin; baseId = (*eIt)->id()) - { - dupsBegin = - std::stable_partition(eIt, dupsBegin, - [&] (const RoomEvent* e) { return e->id() != baseId; }); - } - } + // Multiple-remove (by different criteria), single-erase + // 1. Check for duplicates against the timeline. + auto dupsBegin = remove_if(events->begin(), events->end(), + [&] (const RoomEventPtr& e) + { return eventsIndex.contains(e->id()); }); + + // 2. Check for duplicates within the batch if there are still events. + for (auto eIt = events->begin(); distance(eIt, dupsBegin) > 1; ++eIt) + dupsBegin = remove_if(eIt + 1, dupsBegin, + [&] (const RoomEventPtr& e) + { return e->id() == (*eIt)->id(); }); if (dupsBegin == events->end()) return; qCDebug(EVENTS) << "Dropping" << distance(dupsBegin, events->end()) << "duplicate event(s)"; - // Dispose of those dups - std::for_each(dupsBegin, events->end(), [] (Event* e) { delete e; }); events->erase(dupsBegin, events->end()); } -void Room::Private::processRedaction(const RedactionEvent* redaction) +inline bool isRedaction(const RoomEventPtr& e) { - Q_ASSERT(redaction && redaction->type() == EventType::Redaction); + return e->type() == EventType::Redaction; +} + +void Room::Private::processRedaction(RoomEventPtr redactionEvent) +{ + Q_ASSERT(redactionEvent && isRedaction(redactionEvent)); + const auto& redaction = + static_cast<const RedactionEvent*>(redactionEvent.get()); const auto pIdx = eventsIndex.find(redaction->redactedEvent()); if (pIdx == eventsIndex.end()) @@ -778,11 +803,10 @@ void Room::Private::processRedaction(const RedactionEvent* redaction) // Make a new event from the redacted JSON, exchange events, // notify everyone and delete the old event - auto oldEvent = ti.replaceEvent(RoomEvent::fromJson(originalJson)); - q->onRedaction(oldEvent, ti); + auto oldEvent { ti.replaceEvent(makeEvent<RoomEvent>(originalJson)) }; + q->onRedaction(oldEvent.get(), ti.event()); qCDebug(MAIN) << "Redacted" << oldEvent->id() << "with" << redaction->id(); - emit q->replacedEvent(oldEvent, ti.event()); - delete oldEvent; + emit q->replacedEvent(ti.event(), oldEvent.get()); } Connection* Room::connection() const @@ -796,62 +820,55 @@ User* Room::localUser() const return connection()->user(); } -inline bool isRedaction(Event* e) +void Room::Private::addNewMessageEvents(RoomEvents&& events) { - return e->type() == EventType::Redaction; -} - -void Room::addNewMessageEvents(RoomEvents events) -{ - auto timelineSize = d->timeline.size(); + auto timelineSize = timeline.size(); - d->dropDuplicateEvents(&events); + dropDuplicateEvents(&events); // We want to process redactions in the order of arrival (covering the // case of one redaction superseding another one), hence stable partition. const auto normalsBegin = - std::stable_partition(events.begin(), events.end(), isRedaction); - RoomEventsView redactions { events.begin(), normalsBegin }, + stable_partition(events.begin(), events.end(), isRedaction); + RoomEventsRange redactions { events.begin(), normalsBegin }, normalEvents { normalsBegin, events.end() }; + if (!normalEvents.empty()) + emit q->aboutToAddNewMessages(normalEvents); + const auto insertedSize = insertEvents(std::move(normalEvents), Newer); + if (insertedSize > 0) { - emit aboutToAddNewMessages(normalEvents); - doAddNewMessageEvents(normalEvents); + qCDebug(MAIN) + << "Room" << displayname << "received" << insertedSize + << "new events; the last event is now" << timeline.back(); + q->onAddNewTimelineEvents(timeline.cend() - insertedSize); } - for (auto* r: redactions) - d->processRedaction(static_cast<const RedactionEvent*>(r)); - if (!normalEvents.empty()) + for (auto&& r: redactions) + processRedaction(move(r)); + if (insertedSize > 0) { - d->checkUnreadMessages(normalEvents); - emit addedMessages(); + checkUnreadMessages(timeline.cend() - insertedSize); + emit q->addedMessages(); } - Q_ASSERT(d->timeline.size() == timelineSize + normalEvents.size()); -} - -void Room::doAddNewMessageEvents(RoomEventsView events) -{ - Q_ASSERT(!events.empty()); - d->insertEvents(events, Newer); - qCDebug(MAIN) - << "Room" << displayName() << "received" << events.size() - << "new events; the last event is now" << d->timeline.back(); + Q_ASSERT(timeline.size() == timelineSize + insertedSize); } -void Room::Private::checkUnreadMessages(RoomEventsView events) +void Room::Private::checkUnreadMessages(timeline_iter_t from) { - auto newUnreadMessages = - count_if(events.from, events.to, - [=] (const RoomEvent* e) { return isEventNotable(e); }); + Q_ASSERT(from < timeline.cend()); + const auto newUnreadMessages = count_if(from, timeline.cend(), + bind(&Room::Private::isEventNotable, this, _1)); - // The first event in the batch defines whose read marker can possibly be - // promoted any further over the same author's events newly arrived. - // Others will need explicit read receipts from the server (or, for - // the local user, markMessagesAsRead() invocation) to promote their - // read markers over the new message events. - User* firstWriter = connection->user((*events.from)->senderId()); + // The first event in the just-added batch (referred to by upTo.base()) + // defines whose read marker can possibly be promoted any further over + // the same author's events newly arrived. Others will need explicit + // read receipts from the server (or, for the local user, + // markMessagesAsRead() invocation) to promote their read markers over + // the new message events. + auto firstWriter = connection->user((*from)->senderId()); if (q->readMarker(firstWriter) != timeline.crend()) { - promoteReadMarker(firstWriter, q->findInTimeline((*events.from)->id())); + promoteReadMarker(firstWriter, q->findInTimeline((*from)->id())); qCDebug(MAIN) << "Auto-promoted read marker for" << firstWriter->id() << "to" << *q->readMarker(firstWriter); } @@ -864,50 +881,45 @@ void Room::Private::checkUnreadMessages(RoomEventsView events) } } -void Room::addHistoricalMessageEvents(RoomEvents events) +void Room::Private::addHistoricalMessageEvents(RoomEvents&& events) { - auto timelineSize = d->timeline.size(); + const auto timelineSize = timeline.size(); - d->dropDuplicateEvents(&events); - auto redactionsBegin = - std::remove_if(events.begin(), events.end(), isRedaction); - RoomEventsView normalEvents { events.begin(), redactionsBegin }; + dropDuplicateEvents(&events); + const auto redactionsBegin = + remove_if(events.begin(), events.end(), isRedaction); + RoomEventsRange normalEvents { events.begin(), redactionsBegin }; if (normalEvents.empty()) return; - emit aboutToAddHistoricalMessages(normalEvents); - doAddHistoricalMessageEvents(normalEvents); - emit addedMessages(); - - Q_ASSERT(d->timeline.size() == timelineSize + normalEvents.size()); -} - -void Room::doAddHistoricalMessageEvents(RoomEventsView events) -{ - Q_ASSERT(!events.empty()); - - const bool thereWasNoReadMarker = readMarker() == timelineEdge(); - d->insertEvents(events, Older); + emit q->aboutToAddHistoricalMessages(normalEvents); + const bool thereWasNoReadMarker = q->readMarker() == timeline.crend(); + const auto insertedSize = insertEvents(std::move(normalEvents), Older); // Catch a special case when the last read event id refers to an event // that was outside the loaded timeline and has just arrived. Depending on // other messages next to the last read one, we might need to promote // the read marker and update unreadMessages flag. - const auto curReadMarker = readMarker(); - if (thereWasNoReadMarker && curReadMarker != timelineEdge()) + const auto curReadMarker = q->readMarker(); + if (thereWasNoReadMarker && curReadMarker != timeline.crend()) { qCDebug(MAIN) << "Discovered last read event in a historical batch"; - d->promoteReadMarker(localUser(), curReadMarker, true); + promoteReadMarker(q->localUser(), curReadMarker, true); } - qCDebug(MAIN) << "Room" << displayName() << "received" << events.size() - << "past events; the oldest event is now" << d->timeline.front(); + qCDebug(MAIN) << "Room" << displayname << "received" << insertedSize + << "past events; the oldest event is now" << timeline.front(); + q->onAddHistoricalTimelineEvents(timeline.crend() - insertedSize); + emit q->addedMessages(); + + Q_ASSERT(timeline.size() == timelineSize + insertedSize); } void Room::processStateEvents(const RoomEvents& events) { bool emitNamesChanged = false; - for (auto event: events) + for (const auto& e: events) { + auto* event = e.get(); switch (event->type()) { case EventType::RoomName: { @@ -973,12 +985,12 @@ void Room::processStateEvents(const RoomEvents& events) d->updateDisplayname(); } -void Room::processEphemeralEvent(Event* event) +void Room::processEphemeralEvent(EventPtr event) { switch (event->type()) { case EventType::Typing: { - auto typingEvent = static_cast<TypingEvent*>(event); + auto typingEvent = static_cast<TypingEvent*>(event.get()); d->usersTyping.clear(); for( const QString& userId: typingEvent->users() ) { @@ -989,7 +1001,7 @@ void Room::processEphemeralEvent(Event* event) break; } case EventType::Receipt: { - auto receiptEvent = static_cast<ReceiptEvent*>(event); + auto receiptEvent = static_cast<ReceiptEvent*>(event.get()); for( const auto &p: receiptEvent->eventsWithReceipts() ) { { @@ -45,25 +45,25 @@ namespace QMatrixClient { public: // For compatibility with Qt containers, even though we use - // a std:: container now + // a std:: container now for the room timeline using index_t = int; - TimelineItem(RoomEvent* e, index_t number) : evt(e), idx(number) { } + TimelineItem(RoomEventPtr&& e, index_t number) + : evt(move(e)), idx(number) { } RoomEvent* event() const { return evt.get(); } - RoomEvent* operator->() const { return event(); } //< Synonym for event()-> + RoomEvent* operator->() const { return evt.operator->(); } index_t index() const { return idx; } // Used for event redaction - RoomEvent* replaceEvent(RoomEvent* other) + RoomEventPtr replaceEvent(RoomEventPtr&& other) { - auto* old = evt.release(); - evt.reset(other); - return old; + evt.swap(other); + return move(other); } private: - std::unique_ptr<RoomEvent> evt; + RoomEventPtr evt; index_t idx; }; inline QDebug& operator<<(QDebug& d, const TimelineItem& ti) @@ -88,6 +88,7 @@ namespace QMatrixClient public: using Timeline = std::deque<TimelineItem>; using rev_iter_t = Timeline::const_reverse_iterator; + using timeline_iter_t = Timeline::const_iterator; Room(Connection* connection, QString id, JoinState initialJoinState); ~Room() override; @@ -188,8 +189,8 @@ namespace QMatrixClient void markAllMessagesAsRead(); signals: - void aboutToAddHistoricalMessages(RoomEventsView events); - void aboutToAddNewMessages(RoomEventsView events); + void aboutToAddHistoricalMessages(RoomEventsRange events); + void aboutToAddNewMessages(RoomEventsRange events); void addedMessages(); /** @@ -212,21 +213,20 @@ namespace QMatrixClient void lastReadEventChanged(User* user); void readMarkerMoved(); void unreadMessagesChanged(Room* room); - void replacedEvent(RoomEvent* before, RoomEvent* after); + void replacedEvent(const RoomEvent* newEvent, + const RoomEvent* oldEvent); protected: - virtual void doAddNewMessageEvents(RoomEventsView events); - virtual void doAddHistoricalMessageEvents(RoomEventsView events); virtual void processStateEvents(const RoomEvents& events); - virtual void processEphemeralEvent(Event* event); - virtual void onRedaction(RoomEvent*, TimelineItem&) { } + virtual void processEphemeralEvent(EventPtr event); + virtual void onAddNewTimelineEvents(timeline_iter_t from) { } + virtual void onAddHistoricalTimelineEvents(rev_iter_t from) { } + virtual void onRedaction(const RoomEvent* prevEvent, + const RoomEvent* after) { } private: class Private; Private* d; - - void addNewMessageEvents(RoomEvents events); - void addHistoricalMessageEvents(RoomEvents events); }; class MemberSorter @@ -26,53 +26,6 @@ namespace QMatrixClient { /** - * @brief A crude wrapper around a container of pointers that owns pointers - * to contained objects - * - * Similar to vector<unique_ptr<>>, upon deletion, this wrapper - * will delete all events contained in it. This wrapper can be used - * over Qt containers, which are incompatible with unique_ptr and even - * with QScopedPointer (which is the reason of its creation). - */ - template <typename ContainerT> - class Owning : public ContainerT - { - public: - Owning() = default; - Owning(const Owning&) = delete; - Owning(Owning&&) = default; - Owning& operator=(Owning&& other) - { - assign(other.release()); - return *this; - } - - ~Owning() { cleanup(); } - - void assign(ContainerT&& other) - { - if (&other == this) - return; - cleanup(); - ContainerT::operator=(other); - } - - /** - * @brief returns the underlying container and releases the ownership - * - * Acts similar to unique_ptr::release. - */ - ContainerT release() - { - ContainerT c; - ContainerT::swap(c); - return c; - } - private: - void cleanup() { for (auto e: *this) delete e; } - }; - - /** * @brief Lookup a value by a key in a varargs list * * This function template takes the value of its first argument (selector) |