Newer
Older
Konstantinos Sideris
committed
#include "Utils.h"
#include <QApplication>
#include <QDesktopWidget>
#include <QSettings>
#include <QTextDocument>
#include <QXmlStreamReader>
#include <cmath>
#include <boost/variant.hpp>
#include <maddy/parser.h>
Konstantinos Sideris
committed
Konstantinos Sideris
committed
using TimelineEvent = mtx::events::collections::TimelineEvents;
QString
utils::localUser()
{
QSettings settings;
return settings.value("auth/user_id").toString();
}
void
utils::setScaleFactor(float factor)
{
if (factor < 1 || factor > 3)
return;
QSettings settings;
settings.setValue("settings/scale_factor", factor);
}
float
utils::scaleFactor()
{
QSettings settings("nheko", "nheko");
return settings.value("settings/scale_factor", -1).toFloat();
}
bool
utils::respondsToKeyRequests(const std::string &roomId)
{
return respondsToKeyRequests(QString::fromStdString(roomId));
}
bool
utils::respondsToKeyRequests(const QString &roomId)
{
if (roomId.isEmpty())
return false;
QSettings settings;
return settings.value("rooms/respond_to_key_requests/" + roomId, false).toBool();
}
void
utils::setKeyRequestsPreference(QString roomId, bool value)
{
if (roomId.isEmpty())
return;
QSettings settings;
settings.setValue("rooms/respond_to_key_requests/" + roomId, value);
}
Konstantinos Sideris
committed
QString
utils::descriptiveTime(const QDateTime &then)
{
const auto now = QDateTime::currentDateTime();
const auto days = then.daysTo(now);
if (days == 0)
return then.toString("HH:mm");
else if (days < 2)
return QString("Yesterday");
else if (days < 365)
return then.toString("dd/MM");
return then.toString("dd/MM/yy");
}
DescInfo
utils::getMessageDescription(const TimelineEvent &event,
const QString &localUser,
const QString &room_id)
Konstantinos Sideris
committed
{
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 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>;
Konstantinos Sideris
committed
if (boost::get<Audio>(&event) != nullptr) {
return createDescriptionInfo<Audio>(event, localUser, room_id);
} else if (boost::get<Emote>(&event) != nullptr) {
return createDescriptionInfo<Emote>(event, localUser, room_id);
} else if (boost::get<File>(&event) != nullptr) {
return createDescriptionInfo<File>(event, localUser, room_id);
} else if (boost::get<Image>(&event) != nullptr) {
return createDescriptionInfo<Image>(event, localUser, room_id);
} else if (boost::get<Notice>(&event) != nullptr) {
return createDescriptionInfo<Notice>(event, localUser, room_id);
} else if (boost::get<Text>(&event) != nullptr) {
return createDescriptionInfo<Text>(event, localUser, room_id);
} else if (boost::get<Video>(&event) != nullptr) {
return createDescriptionInfo<Video>(event, localUser, room_id);
} else if (boost::get<mtx::events::Sticker>(&event) != nullptr) {
return createDescriptionInfo<mtx::events::Sticker>(event, localUser, room_id);
} else if (boost::get<Encrypted>(&event) != nullptr) {
const auto msg = boost::get<Encrypted>(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);
DescInfo info;
if (sender == localUser)
info.username = "You";
else
info.username = username;
info.userid = sender;
info.body = QString(" %1").arg(messageDescription<Encrypted>());
info.timestamp = utils::descriptiveTime(ts);
info.datetime = ts;
return info;
Konstantinos Sideris
committed
}
return DescInfo{};
}
QString
utils::firstChar(const QString &input)
{
if (input.isEmpty())
return input;
for (auto const &c : input.toUcs4()) {
if (QString::fromUcs4(&c, 1) != QString("#"))
return QString::fromUcs4(&c, 1).toUpper();
}
return QString::fromUcs4(&input.toUcs4().at(0), 1).toUpper();
{
constexpr static const char *units[] = {"B", "KiB", "MiB", "GiB", "TiB"};
constexpr static const int length = sizeof(units) / sizeof(units[0]);
int u = 0;
double size = static_cast<double>(bytes);
while (size >= 1024.0 && u < length) {
++u;
size /= 1024.0;
}
return QString::number(size, 'g', 4) + ' ' + units[u];
}
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
int
utils::levenshtein_distance(const std::string &s1, const std::string &s2)
{
const int nlen = s1.size();
const int hlen = s2.size();
if (hlen == 0)
return -1;
if (nlen == 1)
return s2.find(s1);
std::vector<int> row1(hlen + 1, 0);
for (int i = 0; i < nlen; ++i) {
std::vector<int> row2(1, i + 1);
for (int j = 0; j < hlen; ++j) {
const int cost = s1[i] != s2[j];
row2.push_back(
std::min(row1[j + 1] + 1, std::min(row2[j] + 1, row1[j] + cost)));
}
row1.swap(row2);
}
return *std::min_element(row1.begin(), row1.end());
}
QString
utils::event_body(const mtx::events::collections::TimelineEvents &event)
{
using namespace mtx::events;
using namespace mtx::events::msg;
if (boost::get<RoomEvent<Audio>>(&event) != nullptr) {
return message_body<RoomEvent<Audio>>(event);
} else if (boost::get<RoomEvent<Emote>>(&event) != nullptr) {
return message_body<RoomEvent<Emote>>(event);
} else if (boost::get<RoomEvent<File>>(&event) != nullptr) {
return message_body<RoomEvent<File>>(event);
} else if (boost::get<RoomEvent<Image>>(&event) != nullptr) {
return message_body<RoomEvent<Image>>(event);
} else if (boost::get<RoomEvent<Notice>>(&event) != nullptr) {
return message_body<RoomEvent<Notice>>(event);
} else if (boost::get<Sticker>(&event) != nullptr) {
return message_body<Sticker>(event);
} else if (boost::get<RoomEvent<Text>>(&event) != nullptr) {
return message_body<RoomEvent<Text>>(event);
} else if (boost::get<RoomEvent<Video>>(&event) != nullptr) {
return message_body<RoomEvent<Video>>(event);
}
return QString();
}
QPixmap
utils::scaleImageToPixmap(const QImage &img, int size)
{
if (img.isNull())
return QPixmap();
const double sz =
std::ceil(QApplication::desktop()->screen()->devicePixelRatioF() * (double)size);
return QPixmap::fromImage(
img.scaled(sz, sz, Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
}
QPixmap
utils::scaleDown(uint64_t maxWidth, uint64_t maxHeight, const QPixmap &source)
{
if (source.isNull())
return QPixmap();
const double widthRatio = (double)maxWidth / (double)source.width();
const double heightRatio = (double)maxHeight / (double)source.height();
const double minAspectRatio = std::min(widthRatio, heightRatio);
// Size of the output image.
int w, h = 0;
if (minAspectRatio > 1) {
w = source.width();
h = source.height();
} else {
w = source.width() * minAspectRatio;
h = source.height() * minAspectRatio;
}
return source.scaled(w, h, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
}
QString
utils::mxcToHttp(const QUrl &url, const QString &server, int port)
{
auto mxcParts = mtx::client::utils::parse_mxc_url(url.toString().toStdString());
return QString("https://%1:%2/_matrix/media/r0/download/%3/%4")
.arg(server)
.arg(port)
.arg(QString::fromStdString(mxcParts.server))
.arg(QString::fromStdString(mxcParts.media_id));
}
QString
utils::humanReadableFingerprint(const std::string &ed25519)
{
return humanReadableFingerprint(QString::fromStdString(ed25519));
}
QString
utils::humanReadableFingerprint(const QString &ed25519)
{
QStringList fingerprintList;
for (int i = 0; i < ed25519.length(); i = i + 4) {
fingerprintList << ed25519.mid(i, 4);
}
return fingerprintList.join(" ");
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
QString
utils::linkifyMessage(const QString &body)
{
QXmlStreamReader xml{"<html>" + body + "</html>"};
QString textString;
while (!xml.atEnd() && !xml.hasError()) {
auto t = xml.readNext();
switch (t) {
case QXmlStreamReader::Characters: {
auto text = xml.text().toString();
text.replace(conf::strings::url_regex, conf::strings::url_html);
textString += text;
break;
}
case QXmlStreamReader::StartDocument:
case QXmlStreamReader::EndDocument:
break;
case QXmlStreamReader::StartElement: {
if (xml.name() == "html")
break;
textString += "<" + xml.name() + ">";
break;
}
case QXmlStreamReader::EndElement: {
if (xml.name() == "html")
break;
textString += "</" + xml.name() + ">";
break;
}
default: {
break;
}
}
}
if (xml.hasError()) {
// qWarning() << "error while parsing xml";
return body;
}
return textString;
}
std::string
utils::markdownToHtml(const std::string &text)
{
std::stringstream markdownInput(text);
auto parser = std::make_shared<maddy::Parser>();
return parser->Parse(markdownInput);
}
std::string
utils::markdownToHtml(const QString &text)
{
return markdownToHtml(text.toStdString());
}
std::string
utils::stripHtml(const std::string &text)
{
return QString::fromStdString(text).remove(QRegExp("<[^>]*>")).toStdString();
}