/******************************************************************************
* Copyright (C) 2018 Kitsune Ral <kitsune-ral@users.sf.net>
*
* 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
*/
#include "converters.h"
#include <QtCore/QVariant>
using namespace Quotient;
QJsonValue JsonConverter<QVariant>::dump(const QVariant& v)
{
return QJsonValue::fromVariant(v);
}
QVariant JsonConverter<QVariant>::load(const QJsonValue& jv)
{
return jv.toVariant();
}
QJsonObject JsonConverter<variant_map_t>::dump(const variant_map_t& map)
{
return
#if (QT_VERSION >= QT_VERSION_CHECK(5, 5, 0))
QJsonObject::fromVariant/******************************************************************************
* Copyright (C) 2017 Kitsune Ral <kitsune-ral@users.sf.net>
*
* 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
// This file contains generic event content definitions, applicable to room
// message events as well as other events (e.g., avatars).
#include "converters.h"
#include <QtCore/QMimeType>
#include <QtCore/QUrl>
#include <QtCore/QSize>
#include <functional>
namespace QMatrixClient
{
namespace EventContent
{
/**
* 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.
*/
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;
};
template <typename T = QString>
class SimpleContent: public Base
{
public:
using value_type = T;
// The constructor is templated to enable perfect forwarding
template <typename TT>
SimpleContent(QString keyName, TT&& value)
: value(std::forward<TT>(value)), key(std::move(keyName))
{ }
SimpleContent(const QJsonObject& json, QString keyName)
: Base(json)
, value(QMatrixClient::fromJson<T>(json[keyName]))
, key(std::move(keyName))
{ }
public:
T value;
protected:
QString key;
private:
void fillJson(QJsonObject* json) const override
{
Q_ASSERT(json);
json->insert(key, QMatrixClient::toJson(value));
}
};
// 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>
/**
* 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:
explicit FileInfo(const QUrl& u, int payloadSize = -1,
const QMimeType& mimeType = {},
const QString& originalFilename = {});
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;
};
template <typename InfoT>
QJsonObject toInfoJson(const InfoT& info)
{
QJsonObject infoJson;
info.fillInfoJson(&infoJson);
return infoJson;
}
/**
* A content info class for image content types: image, thumbnail, video
*/
class ImageInfo : public FileInfo
{
public:
explicit ImageInfo(const QUrl& u, int fileSize = -1,
QMimeType mimeType = {},
const QSize& imageSize = {});
ImageInfo(const QUrl& u, const QJsonObject& infoJson,
const QString& originalFilename = {});
void fillInfoJson(QJsonObject* infoJson) const;
public:
QSize imageSize;
};
/**
* An auxiliary class for an info type that carries a thumbnail
*
* 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.
*/
class Thumbnail : public ImageInfo
{
public:
Thumbnail(const QJsonObject& infoJson);
Thumbnail(const ImageInfo& info)
: ImageInfo(info)
{ }
/**
* Writes thumbnail information to "thumbnail_info" subobject
* and thumbnail URL to "thumbnail_url" node inside "info".
*/
void fillInfoJson(QJsonObject* infoJson) const;
};
class TypedBase: public Base
{
public:
explicit TypedBase(const QJsonObject& o = {}) : Base(o) { }
virtual QMimeType type() const = 0;
virtual const FileInfo* fileInfo() const { return nullptr; }
};
/**
* A base class for content types that have a URL and additional info
*
* 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
*/
template <class InfoT>
class UrlBasedContent : public TypedBase, public InfoT
{
public:
UrlBasedContent(QUrl url, InfoT&& info, QString filename = {})
: InfoT(url, std::forward<InfoT>(info), filename)
{ }
explicit UrlBasedContent(const QJsonObject& json)
: TypedBase(json)
, InfoT(json["url"].toString(), json["info"].toObject(),
json["filename"].toString())
{
// A small hack to facilitate links creation in QML.
originalJson.insert("mediaId", InfoT::mediaId());
}
QMimeType type() const override { return InfoT::mimeType; }
const FileInfo* fileInfo() const override { return this; }
protected:
void fillJson(QJsonObject* json) const override
{
Q_ASSERT(json);
json->insert("url", InfoT::url.toString());
if (!InfoT::originalName.isEmpty())
json->insert("filename", InfoT::originalName);
json->insert("info", toInfoJson<InfoT>(*this));
}
};
template <typename InfoT>
class UrlWithThumbnailContent : public UrlBasedContent<InfoT>
{
public:
// TODO: POD constructor
explicit UrlWithThumbnailContent(const QJsonObject& json)
: UrlBasedContent<InfoT>(json)
, thumbnail(InfoT::originalInfoJson)
{
// Another small hack, to simplify making a thumbnail link
UrlBasedContent<InfoT>::originalJson.insert(
"thumbnailMediaId", thumbnail.mediaId());
}
public:
Thumbnail thumbnail;
protected:
void fillJson(QJsonObject* json) const override
{
UrlBasedContent<InfoT>::fillJson(json);
auto infoJson = json->take("info").toObject();
thumbnail.fillInfoJson(&infoJson);
json->insert("info", infoJson);
}
};
/**
* 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 = UrlWithThumbnailContent<ImageInfo>;
/**
* 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 = UrlWithThumbnailContent<FileInfo>;
} // namespace EventContent
} // namespace QMatrixClient