aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--events/eventcontent.cpp51
-rw-r--r--events/eventcontent.h212
-rw-r--r--events/roommessageevent.cpp43
-rw-r--r--events/roommessageevent.h34
4 files changed, 175 insertions, 165 deletions
diff --git a/events/eventcontent.cpp b/events/eventcontent.cpp
index dcbccf08..271669e2 100644
--- a/events/eventcontent.cpp
+++ b/events/eventcontent.cpp
@@ -30,27 +30,21 @@ QJsonObject Base::toJson() const
return o;
}
-QJsonObject InfoBase::toInfoJson() const
-{
- QJsonObject info;
- fillInfoJson(&info);
- return info;
-}
-
-void InfoBase::fillInfoJson(QJsonObject*) const { }
-
FileInfo::FileInfo(const QUrl& u, int payloadSize, const QMimeType& mimeType,
const QString& originalFilename)
- : InfoBase(mimeType), url(u), payloadSize(payloadSize)
+ : mimeType(mimeType), url(u), payloadSize(payloadSize)
, originalName(originalFilename)
{ }
FileInfo::FileInfo(const QUrl& u, const QJsonObject& infoJson,
const QString& originalFilename)
- : FileInfo(u, infoJson["size"].toInt(),
- QMimeDatabase().mimeTypeForName(infoJson["mimetype"].toString()),
- originalFilename)
+ : originalInfoJson(infoJson)
+ , mimeType(QMimeDatabase().mimeTypeForName(infoJson["mimetype"].toString()))
+ , url(u)
+ , payloadSize(infoJson["size"].toInt())
+ , originalName(originalFilename)
{
+ originalInfoJson.insert("mediaId", url.authority() + url.path());
if (!mimeType.isValid())
mimeType = QMimeDatabase().mimeTypeForData(QByteArray());
}
@@ -61,3 +55,34 @@ void FileInfo::fillInfoJson(QJsonObject* infoJson) const
infoJson->insert("size", payloadSize);
infoJson->insert("mimetype", mimeType.name());
}
+
+ImageInfo::ImageInfo(const QUrl& u, int fileSize, QMimeType mimeType,
+ const QSize& imageSize)
+ : FileInfo(u, fileSize, mimeType), imageSize(imageSize)
+{ }
+
+ImageInfo::ImageInfo(const QUrl& u, const QJsonObject& infoJson,
+ const QString& originalFilename)
+ : FileInfo(u, infoJson, originalFilename)
+ , imageSize(infoJson["w"].toInt(), infoJson["h"].toInt())
+{ }
+
+void ImageInfo::fillInfoJson(QJsonObject* infoJson) const
+{
+ FileInfo::fillInfoJson(infoJson);
+ infoJson->insert("w", imageSize.width());
+ infoJson->insert("h", imageSize.height());
+}
+
+WithThumbnail::WithThumbnail(const QJsonObject& infoJson)
+ : thumbnail(infoJson["thumbnail_url"].toString(),
+ infoJson["thumbnail_info"].toObject())
+{ }
+
+void WithThumbnail::fillInfoJson(QJsonObject* infoJson) const
+{
+ infoJson->insert("thumbnail_url", thumbnail.url.toString());
+ QJsonObject thumbnailInfoJson;
+ thumbnail.fillInfoJson(&thumbnailInfoJson);
+ infoJson->insert("thumbnail_info", thumbnailInfoJson);
+}
diff --git a/events/eventcontent.h b/events/eventcontent.h
index 60437995..b37dc923 100644
--- a/events/eventcontent.h
+++ b/events/eventcontent.h
@@ -23,7 +23,6 @@
#include "converters.h"
-#include <QtCore/QJsonObject>
#include <QtCore/QMimeType>
#include <QtCore/QUrl>
#include <QtCore/QSize>
@@ -40,28 +39,23 @@ namespace QMatrixClient
* 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.
+ * from plain data.
*/
class Base
{
public:
+ explicit Base (const QJsonObject& o = {}) : originalJson(o) { }
virtual ~Base() = default;
QJsonObject toJson() const;
+ public:
+ QJsonObject originalJson;
+
protected:
virtual void fillJson(QJsonObject* o) const = 0;
};
- class TypedBase: public Base
- {
- public:
- virtual QMimeType type() const = 0;
- };
-
template <typename T = QString>
class SimpleContent: public Base
{
@@ -74,10 +68,12 @@ namespace QMatrixClient
: value(std::forward<TT>(value)), key(std::move(keyName))
{ }
SimpleContent(const QJsonObject& json, QString keyName)
- : value(QMatrixClient::fromJson<T>(json[keyName]))
+ : Base(json)
+ , value(QMatrixClient::fromJson<T>(json[keyName]))
, key(std::move(keyName))
{ }
+ public:
T value;
protected:
@@ -91,44 +87,36 @@ namespace QMatrixClient
}
};
- /**
- * 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 FileInfo) 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:
- virtual ~InfoBase() = default;
-
- QJsonObject toInfoJson() const;
-
- QMimeType mimeType;
-
- protected:
- InfoBase() = default;
- explicit InfoBase(const QMimeType& type) : mimeType(type) { }
-
- virtual void fillInfoJson(QJsonObject* /*infoJson*/) const = 0;
- };
-
// 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.
+ // A quick classes inheritance structure follows:
+ // FileInfo
+ // FileContent : UrlBasedContent<FileInfo, Thumbnail>
+ // AudioContent : UrlBasedContent<FileInfo, Duration>
+ // ImageInfo : FileInfo + imageSize attribute
+ // ImageContent : UrlBasedContent<ImageInfo, Thumbnail>
+ // VideoContent : UrlBasedContent<ImageInfo, Thumbnail, Duration>
+
/**
- * Base class for content types that consist of a URL along with
- * additional information. Most of message types except textual fall
- * under this category.
+ * A base/mixin class for structures representing an "info" object for
+ * some content types. These include most attachment types currently in
+ * the CS API spec.
+ *
+ * In order to use it in a content class, derive both from TypedBase
+ * (or Base) and from FileInfo (or its derivative, such as \p ImageInfo)
+ * and call fillInfoJson() to fill the "info" subobject. Make sure
+ * to pass an "info" part of JSON to FileInfo constructor, not the whole
+ * JSON content, as well as contents of "url" (or a similar key) and
+ * optionally "filename" node from the main JSON content. Assuming you
+ * don't do unusual things, you should use \p UrlBasedContent<> instead
+ * of doing multiple inheritance and overriding Base::fillJson() by hand.
+ *
+ * This class is not polymorphic.
*/
- class FileInfo: public InfoBase
+ class FileInfo
{
public:
explicit FileInfo(const QUrl& u, int payloadSize = -1,
@@ -137,111 +125,101 @@ namespace QMatrixClient
FileInfo(const QUrl& u, const QJsonObject& infoJson,
const QString& originalFilename = {});
+ void fillInfoJson(QJsonObject* infoJson) const;
+
+ /**
+ * \brief Extract media id from the URL
+ *
+ * This can be used, e.g., to construct a QML-facing image://
+ * URI as follows:
+ * \code "image://provider/" + info.mediaId() \endcode
+ */
+ QString mediaId() const { return url.authority() + url.path(); }
+
+ public:
+ QJsonObject originalInfoJson;
+ QMimeType mimeType;
QUrl url;
int payloadSize;
QString originalName;
-
- protected:
- 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
+ * A content info class for image content types: image, thumbnail, video
*/
- template <class InfoT = FileInfo>
- class ImageInfo : public InfoT
+ class ImageInfo : public FileInfo
{
public:
explicit ImageInfo(const QUrl& u, int fileSize = -1,
QMimeType mimeType = {},
- const QSize& imageSize = {})
- : InfoT(u, fileSize, mimeType), imageSize(imageSize)
- { }
+ const QSize& imageSize = {});
ImageInfo(const QUrl& u, const QJsonObject& infoJson,
- const QString& originalFilename = {})
- : InfoT(u, infoJson, originalFilename)
- , imageSize(infoJson["w"].toInt(), infoJson["h"].toInt())
- { }
+ const QString& originalFilename = {});
- QSize imageSize;
+ void fillInfoJson(QJsonObject* infoJson) const;
- protected:
- void fillInfoJson(QJsonObject* infoJson) const override
- {
- InfoT::fillInfoJson(infoJson);
- infoJson->insert("w", imageSize.width());
- infoJson->insert("h", imageSize.height());
- }
+ public:
+ QSize imageSize;
};
/**
- * A base class for an info type that carries a thumbnail
+ * A mixin class for an info type that carries a thumbnail
*
- * This class decorates the underlying type, adding ability to save/load
- * a thumbnail to/from "info" subobject of the JSON representation of
- * event content; namely, "info/thumbnail_url" and "info/thumbnail_info"
- * fields are used.
- *
- * \tparam InfoT base info class; should derive from \p InfoBase
+ * This class saves/loads a thumbnail to/from "info" subobject of
+ * the JSON representation of event content; namely,
+ * "info/thumbnail_url" and "info/thumbnail_info" fields are used.
*/
- template <class InfoT = InfoBase>
- class Thumbnailed : public InfoT
+ class WithThumbnail
{
public:
- template <typename... ArgTs>
- explicit Thumbnailed(const ImageInfo<>& thumbnail,
- ArgTs&&... infoArgs)
- : InfoT(std::forward<ArgTs>(infoArgs)...)
- , thumbnail(thumbnail)
- { }
-
- explicit Thumbnailed(const QJsonObject& infoJson)
- : thumbnail(infoJson["thumbnail_url"].toString(),
- infoJson["thumbnail_info"].toObject())
+ WithThumbnail(const QJsonObject& infoJson);
+ WithThumbnail(const ImageInfo& info)
+ : thumbnail(info)
{ }
- Thumbnailed(const QUrl& u, const QJsonObject& infoJson,
- const QString& originalFilename = {})
- : InfoT(u, infoJson, originalFilename)
- , thumbnail(infoJson["thumbnail_url"].toString(),
- infoJson["thumbnail_info"].toObject())
- { }
+ /**
+ * Writes thumbnail information to "thumbnail_info" subobject
+ * and thumbnail URL to "thumbnail_url" node inside "info".
+ */
+ void fillInfoJson(QJsonObject* infoJson) const;
- ImageInfo<> thumbnail;
+ public:
+ ImageInfo thumbnail;
+ };
- protected:
- void fillInfoJson(QJsonObject* infoJson) const override
- {
- InfoT::fillInfoJson(infoJson);
- infoJson->insert("thumbnail_url", thumbnail.url.toString());
- infoJson->insert("thumbnail_info", thumbnail.toInfoJson());
- }
+ class TypedBase: public Base
+ {
+ public:
+ explicit TypedBase(const QJsonObject& o = {}) : Base(o) { }
+ virtual QMimeType type() const = 0;
+ virtual const FileInfo* fileInfo() const { return nullptr; }
};
/**
- * One more facility base class for content types that have a URL and
- * additional info
+ * A base class for content types that have a URL and additional info
*
- * Types that derive from UrlWith<InfoT> take "url" and, optionally,
- * "filename" values from the top-level JSON object and the rest of
- * information from the "info" subobject.
+ * Types that derive from this class template take "url" and,
+ * optionally, "filename" values from the top-level JSON object and
+ * the rest of information from the "info" subobject, as defined by
+ * the parameter type.
*
- * \tparam InfoT base info class; should derive from \p FileInfo or
- * provide a constructor with a compatible signature
+ * \tparam InfoT base info class
+ * \tparam InfoMixinTs... additional info mixin classes (e.g. WithThumbnail)
*/
- template <class InfoT> // InfoT : public FileInfo
- class UrlWith : public TypedBase, public InfoT
+ template <class InfoT, class... InfoMixinTs>
+ class UrlBasedContent :
+ public TypedBase, public InfoT, public InfoMixinTs...
{
public:
- using InfoT::InfoT;
- explicit UrlWith(const QJsonObject& json)
- : InfoT(json["url"].toString(), json["info"].toObject(),
+ explicit UrlBasedContent(const QJsonObject& json)
+ : TypedBase(json)
+ , InfoT(json["url"].toString(), json["info"].toObject(),
json["filename"].toString())
+ , InfoMixinTs(InfoT::originalInfoJson)...
{ }
QMimeType type() const override { return InfoT::mimeType; }
+ const FileInfo* fileInfo() const override { return this; }
protected:
void fillJson(QJsonObject* json) const override
@@ -250,7 +228,13 @@ namespace QMatrixClient
json->insert("url", InfoT::url.toString());
if (!InfoT::originalName.isEmpty())
json->insert("filename", InfoT::originalName);
- json->insert("info", InfoT::toInfoJson());
+ QJsonObject infoJson;
+ InfoT::fillInfoJson(&infoJson);
+ // http://en.cppreference.com/w/cpp/language/parameter_pack#Brace-enclosed_initializers
+ // Looking forward to C++17 and its folding awesomeness.
+ int d[] = { (InfoMixinTs::fillInfoJson(&infoJson), 0)... };
+ Q_UNUSED(d);
+ json->insert("info", infoJson);
}
};
@@ -272,7 +256,7 @@ namespace QMatrixClient
* - mimeType
* - imageSize
*/
- using ImageContent = UrlWith<Thumbnailed<ImageInfo<>>>;
+ using ImageContent = UrlBasedContent<ImageInfo, WithThumbnail>;
/**
* Content class for m.file
@@ -290,6 +274,6 @@ namespace QMatrixClient
* - thumbnail.mimeType
* - thumbnail.imageSize (QSize for "h" and "w" in JSON)
*/
- using FileContent = UrlWith<Thumbnailed<FileInfo>>;
+ using FileContent = UrlBasedContent<FileInfo, WithThumbnail>;
} // namespace EventContent
} // namespace QMatrixClient
diff --git a/events/roommessageevent.cpp b/events/roommessageevent.cpp
index bc41abf6..20e81564 100644
--- a/events/roommessageevent.cpp
+++ b/events/roommessageevent.cpp
@@ -116,6 +116,11 @@ QMimeType RoomMessageEvent::mimeType() const
QMimeDatabase().mimeTypeForName("text/plain");
}
+bool RoomMessageEvent::hasFileContent() const
+{
+ return content() && content()->fileInfo();
+}
+
QJsonObject RoomMessageEvent::toJson() const
{
QJsonObject obj = _content ? _content->toJson() : QJsonObject();
@@ -153,43 +158,35 @@ void TextContent::fillJson(QJsonObject* json) const
json->insert("formatted_body", body);
}
-LocationContent::LocationContent(const QString& geoUri,
- const ImageInfo<>& thumbnail)
- : Thumbnailed<>(thumbnail), geoUri(geoUri)
+LocationContent::LocationContent(const QString& geoUri, const ImageInfo& thumbnail)
+ : WithThumbnail(thumbnail), geoUri(geoUri)
{ }
LocationContent::LocationContent(const QJsonObject& json)
- : Thumbnailed<>(json["info"].toObject())
+ : TypedBase(json)
+ , WithThumbnail(json["info"].toObject())
, geoUri(json["geo_uri"].toString())
{ }
-void LocationContent::fillJson(QJsonObject* o) const
-{
- Q_ASSERT(o);
- o->insert("geo_uri", geoUri);
- o->insert("info", Thumbnailed::toInfoJson());
-}
-
QMimeType LocationContent::type() const
{
return QMimeDatabase().mimeTypeForData(geoUri.toLatin1());
}
-PlayableInfo::PlayableInfo(const QUrl& u, int fileSize,
- const QMimeType& mimeType, int duration,
- const QString& originalFilename)
- : FileInfo(u, fileSize, mimeType, originalFilename)
- , duration(duration)
-{ }
+void LocationContent::fillJson(QJsonObject* o) const
+{
+ Q_ASSERT(o);
+ o->insert("geo_uri", geoUri);
+ QJsonObject infoJson;
+ WithThumbnail::fillInfoJson(&infoJson);
+ o->insert("info", infoJson);
+}
-PlayableInfo::PlayableInfo(const QUrl& u, const QJsonObject& infoJson,
- const QString& originalFilename)
- : FileInfo(u, infoJson, originalFilename)
- , duration(infoJson["duration"].toInt())
+WithDuration::WithDuration(const QJsonObject& infoJson)
+ : duration(infoJson["duration"].toInt())
{ }
-void PlayableInfo::fillInfoJson(QJsonObject* infoJson) const
+void WithDuration::fillInfoJson(QJsonObject* infoJson) const
{
- FileInfo::fillInfoJson(infoJson);
infoJson->insert("duration", duration);
}
diff --git a/events/roommessageevent.h b/events/roommessageevent.h
index eef6b657..6b551b76 100644
--- a/events/roommessageevent.h
+++ b/events/roommessageevent.h
@@ -32,6 +32,10 @@ namespace QMatrixClient
class RoomMessageEvent: public RoomEvent
{
Q_GADGET
+ Q_PROPERTY(QString msgType READ rawMsgtype CONSTANT)
+ Q_PROPERTY(QString plainBody READ plainBody CONSTANT)
+ Q_PROPERTY(QMimeType mimeType READ mimeType STORED false CONSTANT)
+ Q_PROPERTY(EventContent::TypedBase* content READ content CONSTANT)
public:
enum class MsgType
{
@@ -52,9 +56,10 @@ namespace QMatrixClient
MsgType msgtype() const;
QString rawMsgtype() const { return _msgtype; }
const QString& plainBody() const { return _plainBody; }
- const EventContent::TypedBase* content() const
+ EventContent::TypedBase* content() const
{ return _content.data(); }
QMimeType mimeType() const;
+ bool hasFileContent() const;
QJsonObject toJson() const;
@@ -107,15 +112,16 @@ namespace QMatrixClient
* - thumbnail.mimeType
* - thumbnail.imageSize
*/
- class LocationContent: public TypedBase, public Thumbnailed<>
+ class LocationContent: public TypedBase, public WithThumbnail
{
public:
LocationContent(const QString& geoUri,
- const ImageInfo<>& thumbnail);
+ const ImageInfo& thumbnail);
explicit LocationContent(const QJsonObject& json);
QMimeType type() const override;
+ public:
QString geoUri;
protected:
@@ -123,21 +129,18 @@ namespace QMatrixClient
};
/**
- * A base class for "playable" info types: audio and video
+ * A mixin class for info types that include duration: audio and video
*/
- class PlayableInfo : public FileInfo
+ class WithDuration
{
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 = {});
+ explicit WithDuration(int duration) : duration(duration) { }
+ WithDuration(const QJsonObject& infoJson);
- int duration;
+ void fillInfoJson(QJsonObject* infoJson) const;
- protected:
- void fillInfoJson(QJsonObject* infoJson) const override;
+ public:
+ int duration;
};
/**
@@ -159,7 +162,8 @@ namespace QMatrixClient
* - mimeType
* - imageSize
*/
- using VideoContent = UrlWith<Thumbnailed<ImageInfo<PlayableInfo>>>;
+ using VideoContent =
+ UrlBasedContent<ImageInfo, WithThumbnail, WithDuration>;
/**
* Content class for m.audio
@@ -173,6 +177,6 @@ namespace QMatrixClient
* - mimeType ("mimetype" in JSON)
* - duration
*/
- using AudioContent = UrlWith<PlayableInfo>;
+ using AudioContent = UrlBasedContent<FileInfo, WithDuration>;
} // namespace EventContent
} // namespace QMatrixClient