2018-01-05 01:27:32 +03:00
|
|
|
#pragma once
|
|
|
|
|
2018-09-01 13:35:10 +03:00
|
|
|
#include <boost/variant.hpp>
|
|
|
|
|
2018-04-29 15:42:40 +03:00
|
|
|
#include "Cache.h"
|
2018-01-05 01:27:32 +03:00
|
|
|
#include "RoomInfoListItem.h"
|
2018-04-29 15:42:40 +03:00
|
|
|
#include "timeline/widgets/AudioItem.h"
|
|
|
|
#include "timeline/widgets/FileItem.h"
|
|
|
|
#include "timeline/widgets/ImageItem.h"
|
|
|
|
#include "timeline/widgets/VideoItem.h"
|
2018-01-05 01:27:32 +03:00
|
|
|
|
|
|
|
#include <QDateTime>
|
2018-05-11 16:00:14 +03:00
|
|
|
#include <QPixmap>
|
2018-01-05 01:27:32 +03:00
|
|
|
#include <mtx/events/collections.hpp>
|
2018-09-06 22:34:41 +03:00
|
|
|
#include <mtx/events/common.hpp>
|
2018-01-05 01:27:32 +03:00
|
|
|
|
2019-01-18 20:17:25 +03:00
|
|
|
#include <qmath.h>
|
|
|
|
|
2018-10-01 17:56:46 +03:00
|
|
|
class QComboBox;
|
|
|
|
|
2018-01-05 01:27:32 +03:00
|
|
|
namespace utils {
|
|
|
|
|
|
|
|
using TimelineEvent = mtx::events::collections::TimelineEvents;
|
|
|
|
|
2018-07-20 16:15:50 +03:00
|
|
|
QString
|
|
|
|
localUser();
|
|
|
|
|
2018-07-22 19:48:58 +03:00
|
|
|
float
|
|
|
|
scaleFactor();
|
|
|
|
|
|
|
|
void
|
|
|
|
setScaleFactor(float factor);
|
|
|
|
|
2018-07-22 16:36:25 +03:00
|
|
|
//! Whether or not we should respond to key requests for the given room.
|
|
|
|
bool
|
|
|
|
respondsToKeyRequests(const QString &roomId);
|
|
|
|
bool
|
|
|
|
respondsToKeyRequests(const std::string &roomId);
|
|
|
|
|
|
|
|
void
|
|
|
|
setKeyRequestsPreference(QString roomId, bool value);
|
|
|
|
|
2018-01-05 01:27:32 +03:00
|
|
|
//! Human friendly timestamp representation.
|
|
|
|
QString
|
|
|
|
descriptiveTime(const QDateTime &then);
|
|
|
|
|
|
|
|
//! Generate a message description from the event to be displayed
|
|
|
|
//! in the RoomList.
|
|
|
|
DescInfo
|
2018-04-21 16:34:50 +03:00
|
|
|
getMessageDescription(const TimelineEvent &event, const QString &localUser, const QString &room_id);
|
2018-01-12 11:21:53 +03:00
|
|
|
|
|
|
|
//! Get the first character of a string, taking into account that
|
|
|
|
//! surrogate pairs might be in use.
|
|
|
|
QString
|
|
|
|
firstChar(const QString &input);
|
2018-02-18 23:52:31 +03:00
|
|
|
|
|
|
|
//! Get a human readable file size with the appropriate units attached.
|
|
|
|
QString
|
2018-02-19 23:09:21 +03:00
|
|
|
humanReadableFileSize(uint64_t bytes);
|
2018-02-20 00:32:37 +03:00
|
|
|
|
2018-05-05 16:38:41 +03:00
|
|
|
QString
|
|
|
|
event_body(const mtx::events::collections::TimelineEvents &event);
|
|
|
|
|
2018-04-29 15:42:40 +03:00
|
|
|
//! Match widgets/events with a description message.
|
|
|
|
template<class T>
|
|
|
|
QString
|
|
|
|
messageDescription(const QString &username = "", const QString &body = "")
|
|
|
|
{
|
2018-06-28 16:16:43 +03:00
|
|
|
using Audio = mtx::events::RoomEvent<mtx::events::msg::Audio>;
|
|
|
|
using Emote = mtx::events::RoomEvent<mtx::events::msg::Emote>;
|
|
|
|
using File = mtx::events::RoomEvent<mtx::events::msg::File>;
|
|
|
|
using Image = mtx::events::RoomEvent<mtx::events::msg::Image>;
|
|
|
|
using Notice = mtx::events::RoomEvent<mtx::events::msg::Notice>;
|
|
|
|
using Sticker = mtx::events::Sticker;
|
|
|
|
using Text = mtx::events::RoomEvent<mtx::events::msg::Text>;
|
|
|
|
using Video = mtx::events::RoomEvent<mtx::events::msg::Video>;
|
|
|
|
using Encrypted = mtx::events::EncryptedEvent<mtx::events::msg::Encrypted>;
|
2018-04-29 15:42:40 +03:00
|
|
|
|
|
|
|
if (std::is_same<T, AudioItem>::value || std::is_same<T, Audio>::value)
|
|
|
|
return QString("sent an audio clip");
|
|
|
|
else if (std::is_same<T, ImageItem>::value || std::is_same<T, Image>::value)
|
|
|
|
return QString("sent an image");
|
|
|
|
else if (std::is_same<T, FileItem>::value || std::is_same<T, File>::value)
|
|
|
|
return QString("sent a file");
|
|
|
|
else if (std::is_same<T, VideoItem>::value || std::is_same<T, Video>::value)
|
|
|
|
return QString("sent a video clip");
|
|
|
|
else if (std::is_same<T, StickerItem>::value || std::is_same<T, Sticker>::value)
|
|
|
|
return QString("sent a sticker");
|
|
|
|
else if (std::is_same<T, Notice>::value)
|
|
|
|
return QString("sent a notification");
|
|
|
|
else if (std::is_same<T, Text>::value)
|
|
|
|
return QString(": %1").arg(body);
|
|
|
|
else if (std::is_same<T, Emote>::value)
|
|
|
|
return QString("* %1 %2").arg(username).arg(body);
|
2018-06-28 16:16:43 +03:00
|
|
|
else if (std::is_same<T, Encrypted>::value)
|
|
|
|
return QString("sent an encrypted message");
|
2018-04-29 15:42:40 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
template<class T, class Event>
|
|
|
|
DescInfo
|
|
|
|
createDescriptionInfo(const Event &event, const QString &localUser, const QString &room_id)
|
|
|
|
{
|
|
|
|
using Text = mtx::events::RoomEvent<mtx::events::msg::Text>;
|
|
|
|
using Emote = mtx::events::RoomEvent<mtx::events::msg::Emote>;
|
|
|
|
|
2018-09-01 13:35:10 +03:00
|
|
|
const auto msg = boost::get<T>(event);
|
2018-04-29 15:42:40 +03:00
|
|
|
const auto sender = QString::fromStdString(msg.sender);
|
|
|
|
|
|
|
|
const auto username = Cache::displayName(room_id, sender);
|
|
|
|
const auto ts = QDateTime::fromMSecsSinceEpoch(msg.origin_server_ts);
|
|
|
|
|
|
|
|
bool isText = std::is_same<T, Text>::value;
|
|
|
|
bool isEmote = std::is_same<T, Emote>::value;
|
|
|
|
|
|
|
|
return DescInfo{
|
2018-09-13 19:15:58 +03:00
|
|
|
QString::fromStdString(msg.event_id),
|
2018-04-29 15:42:40 +03:00
|
|
|
isEmote ? "" : (sender == localUser ? "You" : username),
|
|
|
|
sender,
|
|
|
|
(isText || isEmote)
|
|
|
|
? messageDescription<T>(username, QString::fromStdString(msg.content.body).trimmed())
|
|
|
|
: QString(" %1").arg(messageDescription<T>()),
|
|
|
|
utils::descriptiveTime(ts),
|
|
|
|
ts};
|
|
|
|
}
|
|
|
|
|
2018-02-20 00:32:37 +03:00
|
|
|
//! Scale down an image to fit to the given width & height limitations.
|
2018-08-01 21:10:03 +03:00
|
|
|
QPixmap
|
|
|
|
scaleDown(uint64_t maxWidth, uint64_t maxHeight, const QPixmap &source);
|
2018-03-25 00:16:15 +03:00
|
|
|
|
2018-04-27 18:19:43 +03:00
|
|
|
//! Delete items in a container based on a predicate.
|
|
|
|
template<typename ContainerT, typename PredicateT>
|
|
|
|
void
|
|
|
|
erase_if(ContainerT &items, const PredicateT &predicate)
|
|
|
|
{
|
|
|
|
for (auto it = items.begin(); it != items.end();) {
|
|
|
|
if (predicate(*it))
|
|
|
|
it = items.erase(it);
|
|
|
|
else
|
|
|
|
++it;
|
|
|
|
}
|
2018-04-28 16:17:36 +03:00
|
|
|
}
|
2018-04-27 18:19:43 +03:00
|
|
|
|
2018-06-28 16:16:43 +03:00
|
|
|
inline uint64_t
|
|
|
|
event_timestamp(const mtx::events::collections::TimelineEvents &event)
|
|
|
|
{
|
2018-09-01 13:35:10 +03:00
|
|
|
return boost::apply_visitor([](auto msg) { return msg.origin_server_ts; }, event);
|
2018-06-28 16:16:43 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
inline nlohmann::json
|
|
|
|
serialize_event(const mtx::events::collections::TimelineEvents &event)
|
|
|
|
{
|
2018-09-01 13:35:10 +03:00
|
|
|
return boost::apply_visitor([](auto msg) { return json(msg); }, event);
|
2018-06-28 16:16:43 +03:00
|
|
|
}
|
|
|
|
|
2018-05-05 16:38:41 +03:00
|
|
|
inline mtx::events::EventType
|
|
|
|
event_type(const mtx::events::collections::TimelineEvents &event)
|
|
|
|
{
|
2018-09-01 13:35:10 +03:00
|
|
|
return boost::apply_visitor([](auto msg) { return msg.type; }, event);
|
2018-05-05 16:38:41 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
inline std::string
|
|
|
|
event_id(const mtx::events::collections::TimelineEvents &event)
|
|
|
|
{
|
2018-09-01 13:35:10 +03:00
|
|
|
return boost::apply_visitor([](auto msg) { return msg.event_id; }, event);
|
2018-05-05 16:38:41 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
inline QString
|
|
|
|
eventId(const mtx::events::collections::TimelineEvents &event)
|
|
|
|
{
|
|
|
|
return QString::fromStdString(event_id(event));
|
|
|
|
}
|
|
|
|
|
|
|
|
inline QString
|
|
|
|
event_sender(const mtx::events::collections::TimelineEvents &event)
|
|
|
|
{
|
2018-09-01 13:35:10 +03:00
|
|
|
return boost::apply_visitor([](auto msg) { return QString::fromStdString(msg.sender); },
|
|
|
|
event);
|
2018-05-05 16:38:41 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
template<class T>
|
|
|
|
QString
|
|
|
|
message_body(const mtx::events::collections::TimelineEvents &event)
|
|
|
|
{
|
2018-09-01 13:35:10 +03:00
|
|
|
return QString::fromStdString(boost::get<T>(event).content.body);
|
2018-05-05 16:38:41 +03:00
|
|
|
}
|
|
|
|
|
2018-03-25 00:16:15 +03:00
|
|
|
//! Calculate the Levenshtein distance between two strings with character skipping.
|
|
|
|
int
|
|
|
|
levenshtein_distance(const std::string &s1, const std::string &s2);
|
2018-05-11 16:00:14 +03:00
|
|
|
|
|
|
|
QPixmap
|
|
|
|
scaleImageToPixmap(const QImage &img, int size);
|
2018-07-15 20:05:31 +03:00
|
|
|
|
|
|
|
//! Convert a Content Matrix URI to an HTTP link.
|
|
|
|
QString
|
|
|
|
mxcToHttp(const QUrl &url, const QString &server, int port);
|
2018-08-21 09:22:51 +03:00
|
|
|
|
|
|
|
//! Convert a ed25519 fingerprint into a human readable form
|
|
|
|
QString
|
|
|
|
humanReadableFingerprint(const std::string &ed25519);
|
|
|
|
|
|
|
|
QString
|
|
|
|
humanReadableFingerprint(const QString &ed25519);
|
2018-09-06 22:34:41 +03:00
|
|
|
|
|
|
|
//! Retrieve the message body taking into account the `formatted_body` field.
|
|
|
|
//! If the `format` of the message is not supported we fallback to `body`.
|
|
|
|
template<typename RoomMessageT>
|
|
|
|
QString
|
2018-09-07 14:52:29 +03:00
|
|
|
getMessageBody(const RoomMessageT &event)
|
2018-09-06 22:34:41 +03:00
|
|
|
{
|
|
|
|
if (event.content.format.empty())
|
2018-09-13 11:02:54 +03:00
|
|
|
return QString::fromStdString(event.content.body).toHtmlEscaped();
|
2018-09-06 22:34:41 +03:00
|
|
|
|
|
|
|
if (event.content.format != common::FORMAT_MSG_TYPE)
|
2018-09-13 11:02:54 +03:00
|
|
|
return QString::fromStdString(event.content.body).toHtmlEscaped();
|
2018-09-06 22:34:41 +03:00
|
|
|
|
|
|
|
return QString::fromStdString(event.content.formatted_body);
|
|
|
|
}
|
2018-09-07 14:52:29 +03:00
|
|
|
|
|
|
|
//! Replace raw URLs in text with HTML link tags.
|
|
|
|
QString
|
|
|
|
linkifyMessage(const QString &body);
|
2018-09-07 20:05:30 +03:00
|
|
|
|
|
|
|
//! Convert the input markdown text to html.
|
2018-09-12 14:20:12 +03:00
|
|
|
QString
|
2018-09-07 20:05:30 +03:00
|
|
|
markdownToHtml(const QString &text);
|
2018-09-19 22:42:26 +03:00
|
|
|
|
2018-09-21 10:55:24 +03:00
|
|
|
//! Retrieve the color of the links based on the current theme.
|
2018-09-19 22:42:26 +03:00
|
|
|
QString
|
|
|
|
linkColor();
|
2018-09-21 10:55:24 +03:00
|
|
|
|
2019-01-18 20:17:25 +03:00
|
|
|
//! Returns the hash code of the input QString
|
|
|
|
int
|
|
|
|
hashQString(const QString &input);
|
|
|
|
|
|
|
|
//! Generate a color (matching #RRGGBB) that has an acceptable contrast to background that is based
|
|
|
|
//! on the input string.
|
|
|
|
QString
|
|
|
|
generateContrastingHexColor(const QString &input, const QString &background);
|
|
|
|
|
|
|
|
//! Given two luminance values, compute the contrast ratio between them.
|
|
|
|
qreal
|
|
|
|
computeContrast(const qreal &one, const qreal &two);
|
|
|
|
|
|
|
|
//! Compute the luminance of a single color. Based on https://stackoverflow.com/a/9733420
|
|
|
|
qreal
|
|
|
|
luminance(const QColor &col);
|
2019-01-18 07:09:42 +03:00
|
|
|
|
2018-09-21 10:55:24 +03:00
|
|
|
//! Center a widget in relation to another widget.
|
|
|
|
void
|
|
|
|
centerWidget(QWidget *widget, QWidget *parent);
|
2018-10-01 17:56:46 +03:00
|
|
|
|
|
|
|
void
|
|
|
|
restoreCombobox(QComboBox *combo, const QString &value);
|
2018-10-07 14:09:47 +03:00
|
|
|
|
|
|
|
struct SideBarSizes
|
|
|
|
{
|
|
|
|
int small;
|
|
|
|
int normal;
|
|
|
|
int groups;
|
2018-10-07 14:18:44 +03:00
|
|
|
int collapsePoint;
|
2018-10-07 14:09:47 +03:00
|
|
|
};
|
|
|
|
|
|
|
|
SideBarSizes
|
|
|
|
calculateSidebarSizes(const QFont &f);
|
2018-09-01 13:35:10 +03:00
|
|
|
}
|