matrixion/src/Utils.h

344 lines
12 KiB
C
Raw Normal View History

// SPDX-FileCopyrightText: Nheko Contributors
2021-03-05 02:35:15 +03:00
//
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include <CacheStructs.h>
2019-07-29 01:40:23 +03:00
#include <QCoreApplication>
#include <QDateTime>
#include <QPixmap>
#include <QRegularExpression>
#include <mtx/events/collections.hpp>
#include <mtx/events/common.hpp>
#include <qmath.h>
2020-01-31 08:12:02 +03:00
struct DescInfo;
namespace cache {
// Forward declarations to prevent dependency on Cache.h, since this header is included often!
QString
displayName(const QString &room_id, const QString &user_id);
}
class QComboBox;
// Contains information about related events for
// outgoing messages
struct RelatedInfo
{
2021-09-18 01:22:33 +03:00
using MsgType = mtx::events::MessageType;
MsgType type;
QString room;
QString quoted_body, quoted_formatted_body;
std::string related_event;
QString quoted_user;
};
namespace utils {
//! Helper function to remove reply fallback from body
2021-04-22 05:49:27 +03:00
std::string
stripReplyFromBody(const std::string &body);
//! Helper function to remove reply fallback from formatted body
2021-04-22 05:49:27 +03:00
std::string
stripReplyFromFormattedBody(const std::string &formatted_body);
2021-03-17 21:08:17 +03:00
RelatedInfo
2023-04-11 01:11:46 +03:00
stripReplyFallbacks(const mtx::events::collections::TimelineEvents &event,
std::string id,
QString room_id_);
2021-03-17 21:08:17 +03:00
bool
codepointIsEmoji(uint code);
QString
replaceEmoji(const QString &body);
QString
localUser();
float
scaleFactor();
void
setScaleFactor(float factor);
//! Human friendly timestamp representation.
QString
descriptiveTime(const QDateTime &then);
//! Generate a message description from the event to be displayed
//! in the RoomList.
DescInfo
2023-04-11 01:11:46 +03:00
getMessageDescription(const mtx::events::collections::TimelineEvents &event,
2020-11-26 01:43:31 +03:00
const QString &localUser,
const QString &displayName);
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);
//! 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-04-29 15:42:40 +03:00
//! Match widgets/events with a description message.
template<class T>
QString
2021-12-29 02:36:43 +03:00
messageDescription(const QString &username = QString(),
const QString &body = QString(),
2019-07-29 01:40:23 +03:00
const bool isLocal = false)
2018-04-29 15:42:40 +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 Unknown = mtx::events::RoomEvent<mtx::events::msg::Unknown>;
using Video = mtx::events::RoomEvent<mtx::events::msg::Video>;
using ElementEffect = mtx::events::RoomEvent<mtx::events::msg::ElementEffect>;
using CallInvite = mtx::events::RoomEvent<mtx::events::voip::CallInvite>;
using CallAnswer = mtx::events::RoomEvent<mtx::events::voip::CallAnswer>;
using CallHangUp = mtx::events::RoomEvent<mtx::events::voip::CallHangUp>;
using CallReject = mtx::events::RoomEvent<mtx::events::voip::CallReject>;
using Encrypted = mtx::events::EncryptedEvent<mtx::events::msg::Encrypted>;
2021-09-18 01:22:33 +03:00
if (std::is_same<T, Audio>::value) {
if (isLocal)
return QCoreApplication::translate("message-description sent:",
"You sent an audio clip");
else
return QCoreApplication::translate("message-description sent:", "%1 sent an audio clip")
.arg(username);
} else if (std::is_same<T, Image>::value) {
if (isLocal)
return QCoreApplication::translate("message-description sent:", "You sent an image");
else
return QCoreApplication::translate("message-description sent:", "%1 sent an image")
.arg(username);
} else if (std::is_same<T, File>::value) {
if (isLocal)
return QCoreApplication::translate("message-description sent:", "You sent a file");
else
return QCoreApplication::translate("message-description sent:", "%1 sent a file")
.arg(username);
} else if (std::is_same<T, Video>::value) {
if (isLocal)
return QCoreApplication::translate("message-description sent:", "You sent a video");
else
return QCoreApplication::translate("message-description sent:", "%1 sent a video")
.arg(username);
} else if (std::is_same<T, Sticker>::value) {
if (isLocal)
return QCoreApplication::translate("message-description sent:", "You sent a sticker");
else
return QCoreApplication::translate("message-description sent:", "%1 sent a sticker")
.arg(username);
} else if (std::is_same<T, Notice>::value) {
if (isLocal)
return QCoreApplication::translate("message-description sent:",
"You sent a notification");
else
return QCoreApplication::translate("message-description sent:",
"%1 sent a notification")
.arg(username);
} else if (std::is_same<T, Text>::value || std::is_same<T, Unknown>::value) {
2021-09-18 01:22:33 +03:00
if (isLocal)
return QCoreApplication::translate("message-description sent:", "You: %1").arg(body);
else
return QCoreApplication::translate("message-description sent:", "%1: %2")
2021-12-29 00:30:12 +03:00
.arg(username, body);
} else if (std::is_same<T, ElementEffect>::value) {
2022-12-17 03:11:19 +03:00
if (body.isEmpty()) {
// TODO: what is the best way to handle this?
2022-12-17 03:11:19 +03:00
if (isLocal)
return QCoreApplication::translate("message-description sent:",
"You sent a chat effect");
2022-12-17 03:11:19 +03:00
else
return QCoreApplication::translate("message-description sent:",
"%1 sent a chat effect")
2022-12-17 03:11:19 +03:00
.arg(username);
} else {
if (isLocal)
return QCoreApplication::translate("message-description sent:", "You: %1")
.arg(body);
else
return QCoreApplication::translate("message-description sent:", "%1: %2")
.arg(username, body);
}
2021-09-18 01:22:33 +03:00
} else if (std::is_same<T, Emote>::value) {
2021-12-29 02:36:43 +03:00
return QStringLiteral("* %1 %2").arg(username, body);
2021-09-18 01:22:33 +03:00
} else if (std::is_same<T, Encrypted>::value) {
if (isLocal)
return QCoreApplication::translate("message-description sent:",
"You sent an encrypted message");
else
return QCoreApplication::translate("message-description sent:",
"%1 sent an encrypted message")
.arg(username);
} else if (std::is_same<T, CallInvite>::value) {
if (isLocal)
return QCoreApplication::translate("message-description sent:", "You placed a call");
else
return QCoreApplication::translate("message-description sent:", "%1 placed a call")
.arg(username);
} else if (std::is_same<T, CallAnswer>::value) {
if (isLocal)
return QCoreApplication::translate("message-description sent:", "You answered a call");
else
return QCoreApplication::translate("message-description sent:", "%1 answered a call")
.arg(username);
} else if (std::is_same<T, CallHangUp>::value) {
if (isLocal)
return QCoreApplication::translate("message-description sent:", "You ended a call");
else
return QCoreApplication::translate("message-description sent:", "%1 ended a call")
.arg(username);
} else if (std::is_same<T, CallReject>::value) {
if (isLocal)
return QCoreApplication::translate("message-description sent:", "You rejected a call");
else
return QCoreApplication::translate("message-description sent:", "%1 rejected a call")
.arg(username);
2021-09-18 01:22:33 +03:00
} else {
return QCoreApplication::translate("utils", "Unknown Message Type");
}
2018-04-29 15:42:40 +03:00
}
2018-02-20 00:32:37 +03:00
//! Scale down an image to fit to the given width & height limitations.
QPixmap
scaleDown(uint64_t maxWidth, uint64_t maxHeight, const QPixmap &source);
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)
{
2021-09-18 01:22:33 +03:00
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
template<class T>
QString
message_body(const mtx::events::collections::TimelineEvents &event)
{
2021-09-18 01:22:33 +03:00
return QString::fromStdString(std::get<T>(event).content.body);
}
//! Calculate the Levenshtein distance between two strings with character skipping.
int
levenshtein_distance(const std::string &s1, const std::string &s2);
QPixmap
scaleImageToPixmap(const QImage &img, int size);
//! Convert a Content Matrix URI to an HTTP link.
QString
mxcToHttp(const QUrl &url, const QString &server, int port);
//! Convert a ed25519 fingerprint into a human readable form
QString
humanReadableFingerprint(const std::string &ed25519);
QString
humanReadableFingerprint(const QString &ed25519);
//! 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
getMessageBody(const RoomMessageT &event)
{
2021-09-18 01:22:33 +03:00
if (event.content.format.empty())
return QString::fromStdString(event.content.body).toHtmlEscaped();
2021-09-18 01:22:33 +03:00
if (event.content.format != mtx::common::FORMAT_MSG_TYPE)
return QString::fromStdString(event.content.body).toHtmlEscaped();
2021-09-18 01:22:33 +03:00
return QString::fromStdString(event.content.formatted_body);
}
//! Replace raw URLs in text with HTML link tags.
QString
linkifyMessage(const QString &body);
//! Convert the input markdown text to html.
2018-09-12 14:20:12 +03:00
QString
markdownToHtml(const QString &text, bool rainbowify = false, bool noExtensions = false);
2018-09-19 22:42:26 +03:00
2020-01-14 19:47:30 +03:00
//! Escape every html tag, that was not whitelisted
QString
escapeBlacklistedHtml(const QString &data);
//! Generate a Rich Reply quote message
QString
getFormattedQuoteBody(const RelatedInfo &related, const QString &html);
//! Get the body for the quote, depending on the event type.
QString
getQuoteBody(const RelatedInfo &related);
//! Retrieve the color of the links based on the current theme.
2018-09-19 22:42:26 +03:00
QString
linkColor();
//! Returns the hash code of the input QString
uint32_t
hashQString(const QString &input);
//! Generate a color (matching #RRGGBB) that has an acceptable contrast to background that is based
//! on the input string.
QColor
generateContrastingHexColor(const QString &input, const QColor &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);
//! Center a widget in relation to another widget.
void
2022-01-29 21:07:27 +03:00
centerWidget(QWidget *widget, QWindow *parent);
void
restoreCombobox(QComboBox *combo, const QString &value);
2021-04-04 01:15:08 +03:00
//! Read image respecting exif orientation
QImage
readImageFromFile(const QString &filename);
//! Read image respecting exif orientation
QImage
readImage(const QByteArray &data);
bool
isReply(const mtx::events::collections::TimelineEvents &e);
void
removeDirectFromRoom(QString roomid);
void
markRoomAsDirect(QString roomid, std::vector<RoomMember> members);
std::vector<std::string>
roomVias(const std::string &roomid);
void
updateSpaceVias();
2023-07-04 19:01:44 +03:00
void
removeExpiredEvents();
}