/****************************************************************************** * Copyright (C) 2015 Felix Rohrbach * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #pragma once #include "event.h" #include #include #include namespace QMatrixClient { namespace MessageEventContent { /** * A base class for all content types that can be stored * in a RoomMessageEvent * * Each content type class should have a constructor taking * a QJsonObject and override fillJson() with an implementation * that will fill the target QJsonObject with stored values. It is * assumed but not required that a content object can also be created * from plain data. fillJson() should only fill the main JSON object * but not the "info" subobject if it exists for a certain content type; * use \p InfoBase to de/serialize "info" parts with an optional URL * on the top level. */ class Base { public: virtual ~Base() = default; QJsonObject toJson() const; QMimeType mimeType; protected: Base() = default; explicit Base(const QMimeType& type) : mimeType(type) { } virtual void fillJson(QJsonObject* o) const = 0; }; /** * A base class for content types that have an "info" object in their * JSON representation * * These include most multimedia types currently in the CS API spec. * Derived classes should override fillInfoJson() to fill the "info" * subobject, BUT NOT the main JSON object. Most but not all "info" * classes (specifically, those deriving from UrlInfo) should also * have a constructor that accepts two parameters, QUrl and QJsonObject, * in order to load the URL+info part from JSON. */ class InfoBase: public Base { public: QJsonObject toInfoJson() const; protected: using Base::Base; virtual void fillInfoJson(QJsonObject* /*infoJson*/) const { } }; } // namespace MessageEventContent /** * The event class corresponding to m.room.message events */ class RoomMessageEvent: public RoomEvent { Q_GADGET public: enum class MsgType { Text, Emote, Notice, Image, File, Location, Video, Audio, Unknown }; RoomMessageEvent(const QString& plainBody, const QString& jsonMsgType, MessageEventContent::Base* content = nullptr) : RoomEvent(Type::RoomMessage) , _msgtype(jsonMsgType), _plainBody(plainBody), _content(content) { } explicit RoomMessageEvent(const QString& plainBody, MsgType msgType = MsgType::Text, MessageEventContent::Base* content = nullptr); explicit RoomMessageEvent(const QJsonObject& obj); MsgType msgtype() const; QString rawMsgtype() const { return _msgtype; } const QString& plainBody() const { return _plainBody; } const MessageEventContent::Base* content() const { return _content.data(); } QMimeType mimeType() const; QJsonObject toJson() const; static constexpr const char* TypeId = "m.room.message"; private: QString _msgtype; QString _plainBody; QScopedPointer _content; REGISTER_ENUM(MsgType) }; using MessageEventType = RoomMessageEvent::MsgType; namespace MessageEventContent { // The below structures fairly follow CS spec 11.2.1.6. The overall // set of attributes for each content types is a superset of the spec // but specific aggregation structure is altered. See doc comments to // each type for the list of available attributes. /** * Rich text content for m.text, m.emote, m.notice * * Available fields: mimeType, body. The body can be either rich text * or plain text, depending on what mimeType specifies. */ class TextContent: public Base { public: TextContent(const QString& text, const QString& contentType); explicit TextContent(const QJsonObject& json); void fillJson(QJsonObject* json) const override; QString body; }; /** * Base class for content types that consist of a URL along with * additional information * * All message types except the (hyper)text mentioned above and * m.location fall under this category. */ class FileInfo: public InfoBase { public: explicit FileInfo(const QUrl& u, int payloadSize = -1, const QMimeType& mimeType = {}, const QString& originalFilename = {}); FileInfo(const QUrl& u, const QJsonObject& infoJson, const QString& originalFilename = {}); QUrl url; int payloadSize; QString originalName; protected: void fillJson(QJsonObject* json) const override; void fillInfoJson(QJsonObject* infoJson) const override; }; /** * A base class for image info types: image, thumbnail, video * * \tparam InfoT base info class; should derive from \p InfoBase */ template class ImageInfo : public InfoT { public: explicit ImageInfo(const QUrl& u, int fileSize = -1, QMimeType mimeType = {}, const QSize& imageSize = {}) : InfoT(u, fileSize, mimeType), imageSize(imageSize) { } ImageInfo(const QUrl& u, const QJsonObject& infoJson, const QString& originalFilename = {}) : InfoT(u, infoJson, originalFilename) , imageSize(infoJson["w"].toInt(), infoJson["h"].toInt()) { } void fillInfoJson(QJsonObject* infoJson) const /* override */ { InfoT::fillInfoJson(infoJson); infoJson->insert("w", imageSize.width()); infoJson->insert("h", imageSize.height()); } QSize imageSize; }; /** * A base class for an info type that carries a thumbnail * * This class provides a means to save/load a thumbnail to/from "info" * subobject of the JSON representation of a message; namely, * "info/thumbnail_url" and "info/thumbnail_info" fields are used. * * \tparam InfoT base info class; should derive from \p InfoBase */ template class Thumbnailed : public InfoT { public: template explicit Thumbnailed(const ImageInfo<>& thumbnail, ArgTs&&... infoArgs) : InfoT(std::forward(infoArgs)...) , thumbnail(thumbnail) { } explicit Thumbnailed(const QJsonObject& infoJson) : thumbnail(infoJson["thumbnail_url"].toString(), infoJson["thumbnail_info"].toObject()) { } Thumbnailed(const QUrl& u, const QJsonObject& infoJson, const QString& originalFilename = {}) : InfoT(u, infoJson, originalFilename) , thumbnail(infoJson["thumbnail_url"].toString(), infoJson["thumbnail_info"].toObject()) { } void fillInfoJson(QJsonObject* infoJson) const /* override */ { InfoT::fillInfoJson(infoJson); infoJson->insert("thumbnail_url", thumbnail.url.toString()); infoJson->insert("thumbnail_info", thumbnail.toInfoJson()); } ImageInfo<> thumbnail; }; /** * One more facility base class for content types that have a URL and * additional info * * The assumed layout for types enabled by a combination of UrlInfo and * UrlWith<> is the following: "url" and, optionally, "filename" in the * top-level JSON and the rest of information inside the "info" subobject. * * \tparam InfoT base info class; should derive from \p UrlInfo or * provide a constructor with a compatible signature */ template // InfoT : public FileInfo class UrlWith : public InfoT { public: using InfoT::InfoT; explicit UrlWith(const QJsonObject& json) : InfoT(json["url"].toString(), json["info"].toObject(), json["filename"].toString()) { } }; /** * Content class for m.image * * Available fields: * - corresponding to the top-level JSON: * - url * - filename (extension to the spec) * - corresponding to the "info" subobject: * - payloadSize ("size" in JSON) * - mimeType ("mimetype" in JSON) * - imageSize (QSize for a combination of "h" and "w" in JSON) * - thumbnail.url ("thumbnail_url" in JSON) * - corresponding to the "info/thumbnail_info" subobject: contents of * thumbnail field, in the same vein as for the main image: * - payloadSize * - mimeType * - imageSize */ using ImageContent = UrlWith>>; /** * Content class for m.file * * Available fields: * - corresponding to the top-level JSON: * - url * - filename * - corresponding to the "info" subobject: * - payloadSize ("size" in JSON) * - mimeType ("mimetype" in JSON) * - thumbnail.url ("thumbnail_url" in JSON) * - corresponding to the "info/thumbnail_info" subobject: * - thumbnail.payloadSize * - thumbnail.mimeType * - thumbnail.imageSize (QSize for "h" and "w" in JSON) */ using FileContent = UrlWith>; /** * Content class for m.location * * Available fields: * - corresponding to the top-level JSON: * - geoUri ("geo_uri" in JSON) * - corresponding to the "info" subobject: * - thumbnail.url ("thumbnail_url" in JSON) * - corresponding to the "info/thumbnail_info" subobject: * - thumbnail.payloadSize * - thumbnail.mimeType * - thumbnail.imageSize */ class LocationContent: public Thumbnailed<> { public: LocationContent(const QString& geoUri, const ImageInfo<>& thumbnail); explicit LocationContent(const QJsonObject& json); void fillJson(QJsonObject* o) const override; QString geoUri; }; /** * A base class for "playable" info types: audio and video */ class PlayableInfo : public FileInfo { public: explicit PlayableInfo(const QUrl& u, int fileSize, const QMimeType& mimeType, int duration, const QString& originalFilename = {}); PlayableInfo(const QUrl& u, const QJsonObject& infoJson, const QString& originalFilename = {}); void fillInfoJson(QJsonObject* infoJson) const override; int duration; }; /** * Content class for m.video * * Available fields: * - corresponding to the top-level JSON: * - url * - filename (extension to the CS API spec) * - corresponding to the "info" subobject: * - payloadSize ("size" in JSON) * - mimeType ("mimetype" in JSON) * - duration * - imageSize (QSize for a combination of "h" and "w" in JSON) * - thumbnail.url ("thumbnail_url" in JSON) * - corresponding to the "info/thumbnail_info" subobject: contents of * thumbnail field, in the same vein as for "info": * - payloadSize * - mimeType * - imageSize */ using VideoContent = UrlWith>>; /** * Content class for m.audio * * Available fields: * - corresponding to the top-level JSON: * - url * - filename (extension to the CS API spec) * - corresponding to the "info" subobject: * - payloadSize ("size" in JSON) * - mimeType ("mimetype" in JSON) * - duration */ using AudioContent = UrlWith; } // namespace MessageEventContent } // namespace QMatrixClient