#pragma once #include #include "Cache.h" #include "RoomInfoListItem.h" #include "timeline/widgets/AudioItem.h" #include "timeline/widgets/FileItem.h" #include "timeline/widgets/ImageItem.h" #include "timeline/widgets/VideoItem.h" #include #include #include #include #include #include class QComboBox; // Contains information about related events for // outgoing messages struct RelatedInfo { using MsgType = mtx::events::MessageType; MsgType type; QString room; QString quoted_body; std::string related_event; QString quoted_user; }; namespace utils { using TimelineEvent = mtx::events::collections::TimelineEvents; QString replaceEmoji(const QString &body); QString localUser(); float scaleFactor(); void setScaleFactor(float factor); //! 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); //! Human friendly timestamp representation. QString descriptiveTime(const QDateTime &then); //! Generate a message description from the event to be displayed //! in the RoomList. DescInfo getMessageDescription(const TimelineEvent &event, const QString &localUser, const QString &room_id); //! 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 humanReadableFileSize(uint64_t bytes); QString event_body(const mtx::events::collections::TimelineEvents &event); //! Match widgets/events with a description message. template QString messageDescription(const QString &username = "", const QString &body = "", const bool isLocal = false) { using Audio = mtx::events::RoomEvent; using Emote = mtx::events::RoomEvent; using File = mtx::events::RoomEvent; using Image = mtx::events::RoomEvent; using Notice = mtx::events::RoomEvent; using Sticker = mtx::events::Sticker; using Text = mtx::events::RoomEvent; using Video = mtx::events::RoomEvent; using Encrypted = mtx::events::EncryptedEvent; // Sometimes the verb form of sent changes in some languages depending on the actor. auto remoteSent = QCoreApplication::translate( "message-description: ", "sent", "For when you are the sender"); auto localSent = QCoreApplication::translate( "message-description:", "sent", "For when someone else is the sender"); QString sentVerb = isLocal ? localSent : remoteSent; if (std::is_same::value || std::is_same::value) { return QCoreApplication::translate("message-description sent:", "%1 an audio clip") .arg(sentVerb); } else if (std::is_same::value || std::is_same::value) { return QCoreApplication::translate("message-description sent:", "%1 an image") .arg(sentVerb); } else if (std::is_same::value || std::is_same::value) { return QCoreApplication::translate("message-description sent:", "%1 a file") .arg(sentVerb); } else if (std::is_same::value || std::is_same::value) { return QCoreApplication::translate("message-description sent:", "%1 a video clip") .arg(sentVerb); } else if (std::is_same::value || std::is_same::value) { return QCoreApplication::translate("message-description sent:", "%1 a sticker") .arg(sentVerb); } else if (std::is_same::value) { return QCoreApplication::translate("message-description sent:", "%1 a notification") .arg(sentVerb); } else if (std::is_same::value) { return QString(": %1").arg(body); } else if (std::is_same::value) { return QString("* %1 %2").arg(username).arg(body); } else if (std::is_same::value) { return QCoreApplication::translate("message-description sent:", "%1 an encrypted message") .arg(sentVerb); } else { return QCoreApplication::translate("utils", "Unknown Message Type"); } } template DescInfo createDescriptionInfo(const Event &event, const QString &localUser, const QString &room_id) { using Text = mtx::events::RoomEvent; using Emote = mtx::events::RoomEvent; const auto msg = boost::get(event); 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::value; bool isEmote = std::is_same::value; return DescInfo{ QString::fromStdString(msg.event_id), isEmote ? "" : (sender == localUser ? QCoreApplication::translate("utils", "You") : username), sender, (isText || isEmote) ? messageDescription( username, QString::fromStdString(msg.content.body).trimmed(), sender == localUser) : QString(" %1").arg(messageDescription()), utils::descriptiveTime(ts), ts}; } //! Scale down an image to fit to the given width & height limitations. QPixmap scaleDown(uint64_t maxWidth, uint64_t maxHeight, const QPixmap &source); //! Delete items in a container based on a predicate. template 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; } } inline uint64_t event_timestamp(const mtx::events::collections::TimelineEvents &event) { return boost::apply_visitor([](auto msg) { return msg.origin_server_ts; }, event); } inline nlohmann::json serialize_event(const mtx::events::collections::TimelineEvents &event) { return boost::apply_visitor([](auto msg) { return json(msg); }, event); } inline mtx::events::EventType event_type(const mtx::events::collections::TimelineEvents &event) { return boost::apply_visitor([](auto msg) { return msg.type; }, event); } inline std::string event_id(const mtx::events::collections::TimelineEvents &event) { return boost::apply_visitor([](auto msg) { return msg.event_id; }, event); } 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) { return boost::apply_visitor([](auto msg) { return QString::fromStdString(msg.sender); }, event); } template QString message_body(const mtx::events::collections::TimelineEvents &event) { return QString::fromStdString(boost::get(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 QString getMessageBody(const RoomMessageT &event) { if (event.content.format.empty()) return QString::fromStdString(event.content.body).toHtmlEscaped(); if (event.content.format != common::FORMAT_MSG_TYPE) return QString::fromStdString(event.content.body).toHtmlEscaped(); 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. QString markdownToHtml(const QString &text); //! 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. 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. 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); //! Center a widget in relation to another widget. void centerWidget(QWidget *widget, QWidget *parent); void restoreCombobox(QComboBox *combo, const QString &value); struct SideBarSizes { int small; int normal; int groups; int collapsePoint; }; SideBarSizes calculateSidebarSizes(const QFont &f); }