aboutsummaryrefslogtreecommitdiff
path: root/events/eventcontent.h
blob: 4afbaff39f63b78dafc8f58b1ae07a70642fe51d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
pre { line-height: 125%; margin: 0; }
td.linenos pre { color: #000000; background-color: #f0f0f0; padding: 0 5px 0 5px; }
span.linenos { color: #000000; background-color: #f0f0f0; padding: 0 5px 0 5px; }
td.linenos pre.special { color: #000000; background-color: #ffffc0; padding: 0 5px 0 5px; }
span.linenos.special { color: #000000; background-color: #ffffc0; padding: 0 5px 0 5px; }
.highlight .hll { background-color: #ffffcc }
.highlight .c { color: #888888 } /* Comment */
.highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */
.highlight .k { color: #008800; font-weight: bold } /* Keyword */
.highlight .ch { color: #888888 } /* Comment.Hashbang */
.highlight .cm { color: #888888 } /* Comment.Multiline */
.highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */
.highlight .cpf { color: #888888 } /* Comment.PreprocFile */
.highlight .c1 { color: #888888 } /* Comment.Single */
.highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */
.highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .gr { color: #aa0000 } /* Generic.Error */
.highlight .gh { color: #333333 } /* Generic.Heading */
.highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */
.highlight .go { color: #888888 } /* Generic.Output */
.highlight .gp { color: #555555 } /* Generic.Prompt */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #666666 } /* Generic.Subheading */
.highlight .gt { color: #aa0000 } /* Generic.Traceback */
.highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */
.highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */
.highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */
.highlight .kp { color: #008800 } /* Keyword.Pseudo */
.highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */
.highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */
.highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */
.highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */
.highlight .na { color: #336699 } /* Name.Attribute */
.highlight .nb { color: #003388 } /* Name.Builtin */
.highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */
.highlight .no { color: #003366; font-weight: bold } /* Name.Constant */
.highlight .nd { color: #555555 } /* Name.Decorator */
.highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */
.highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */
.highlight .nl { color: #336699; font-style: italic } /* Name.Label */
.highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */
.highlight .py { color: #336699; font-weight: bold } /* Name.Property */
.highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */
.highlight .nv { color: #336699 } /* Name.Variable */
.highlight .ow { color: #008800 } /* Operator.Word */
.highlight .w { color: #bbbbbb } /* Text.Whitespace */
.highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */
.highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */
.highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */
.highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */
.highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */
.highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */
.highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */
.highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */
.highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */
.highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */
.highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */
.highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */
.highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */
.highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */
.highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */
.highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */
.highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */
.highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */
.highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */
.highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */
.highlight .vc { color: #336699 } /* Name.Variable.Class */
.highlight .vg { color: #dd7700 } /* Name.Variable.Global */
.highlight .vi { color: #3333bb } /* Name.Variable.Instance */
.highlight .vm { color: #336699 } /* Name.Variable.Magic */
.highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */
/******************************************************************************
 * 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