From aaa82dce75a9e338201b48dc5eb5717580bb8b1a Mon Sep 17 00:00:00 2001 From: Rasmus Karlsson Date: Sun, 8 Jul 2018 09:42:48 +0000 Subject: [PATCH 01/73] Fix custom highlight sounds not working on Linux --- src/providers/twitch/TwitchChannel.cpp | 1 + src/providers/twitch/TwitchMessageBuilder.cpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/providers/twitch/TwitchChannel.cpp b/src/providers/twitch/TwitchChannel.cpp index 61de45318..4c1285f6f 100644 --- a/src/providers/twitch/TwitchChannel.cpp +++ b/src/providers/twitch/TwitchChannel.cpp @@ -13,6 +13,7 @@ #include "util/PostToThread.hpp" #include +#include #include #include diff --git a/src/providers/twitch/TwitchMessageBuilder.cpp b/src/providers/twitch/TwitchMessageBuilder.cpp index e54d71202..f21426a76 100644 --- a/src/providers/twitch/TwitchMessageBuilder.cpp +++ b/src/providers/twitch/TwitchMessageBuilder.cpp @@ -440,7 +440,7 @@ void TwitchMessageBuilder::parseHighlights() // update the media player url if necessary QUrl highlightSoundUrl; if (app->settings->customHighlightSound) { - highlightSoundUrl = QUrl(app->settings->pathHighlightSound.getValue()); + highlightSoundUrl = QUrl::fromLocalFile(app->settings->pathHighlightSound.getValue()); } else { highlightSoundUrl = QUrl("qrc:/sounds/ping2.wav"); } From da1627e47dc0f5b76e22242a2bf9c1e65d99462f Mon Sep 17 00:00:00 2001 From: Rasmus Karlsson Date: Sun, 8 Jul 2018 09:43:44 +0000 Subject: [PATCH 02/73] Fix historical messages not triggering highlights For now they only trigger the highlighting of the message, but not sound or flash alerts as I think that's weird. Fix #308 --- src/providers/twitch/TwitchMessageBuilder.cpp | 18 +++++++++--------- src/providers/twitch/TwitchMessageBuilder.hpp | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/providers/twitch/TwitchMessageBuilder.cpp b/src/providers/twitch/TwitchMessageBuilder.cpp index f21426a76..bac7baa3a 100644 --- a/src/providers/twitch/TwitchMessageBuilder.cpp +++ b/src/providers/twitch/TwitchMessageBuilder.cpp @@ -141,9 +141,7 @@ MessagePtr TwitchMessageBuilder::build() this->appendUsername(); // highlights - if (/*app->settings->enableHighlights &&*/ !isPastMsg) { - this->parseHighlights(); - } + this->parseHighlights(isPastMsg); QString bits; auto iterator = this->tags.find("bits"); @@ -420,7 +418,7 @@ void TwitchMessageBuilder::appendUsername() } } -void TwitchMessageBuilder::parseHighlights() +void TwitchMessageBuilder::parseHighlights(bool isPastMsg) { static auto player = new QMediaPlayer; static QUrl currentPlayerUrl; @@ -513,12 +511,14 @@ void TwitchMessageBuilder::parseHighlights() this->setHighlight(doHighlight); - if (playSound && (!hasFocus || app->settings->highlightAlwaysPlaySound)) { - player->play(); - } + if (!isPastMsg) { + if (playSound && (!hasFocus || app->settings->highlightAlwaysPlaySound)) { + player->play(); + } - if (doAlert) { - QApplication::alert(getApp()->windows->getMainWindow().window(), 2500); + if (doAlert) { + QApplication::alert(getApp()->windows->getMainWindow().window(), 2500); + } } if (doHighlight) { diff --git a/src/providers/twitch/TwitchMessageBuilder.hpp b/src/providers/twitch/TwitchMessageBuilder.hpp index 80d80fe7b..b27ce8a0a 100644 --- a/src/providers/twitch/TwitchMessageBuilder.hpp +++ b/src/providers/twitch/TwitchMessageBuilder.hpp @@ -48,7 +48,7 @@ private: void appendChannelName(); void parseUsername(); void appendUsername(); - void parseHighlights(); + void parseHighlights(bool isPastMsg); void appendTwitchEmote(const Communi::IrcMessage *ircMessage, const QString &emote, std::vector> &vec); From 91e320edf8df8321410ddf9e0e0369a917e92df1 Mon Sep 17 00:00:00 2001 From: pajlada Date: Sun, 8 Jul 2018 11:18:49 +0000 Subject: [PATCH 03/73] Remove link to clang format automation the description is already in the readme --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index 0eba5a029..8ce67f521 100644 --- a/README.md +++ b/README.md @@ -23,8 +23,6 @@ Before building run `git submodule update --init --recursive` to get required su ## Code style The code is formated using clang format in Qt Creator. [.clang-format](https://github.com/fourtf/chatterino2/blob/master/.clang-format) contains the style file for clang format. -To setup automatic code formating with QT Creator, see [this guide](https://gist.github.com/pajlada/0296454198eb8f8789fd6fe7ea660c5b). - ### Get it automated with QT Creator + Beautifier + Clang Format 1. Download LLVM: http://releases.llvm.org/6.0.1/LLVM-6.0.1-win64.exe 2. During the installation, make sure to add it to your path From d82c647edde185fd4fb0a99ab5c6b1c51cdb41bf Mon Sep 17 00:00:00 2001 From: Lajamerr Mittesdine Date: Tue, 10 Jul 2018 11:47:16 -0400 Subject: [PATCH 04/73] Fix new emotes not loading properly because of cache (#596) Fix #595 --- src/providers/bttv/BttvEmotes.cpp | 2 -- src/providers/ffz/FfzEmotes.cpp | 2 -- 2 files changed, 4 deletions(-) diff --git a/src/providers/bttv/BttvEmotes.cpp b/src/providers/bttv/BttvEmotes.cpp index 1c1f757c0..8252ffe37 100644 --- a/src/providers/bttv/BttvEmotes.cpp +++ b/src/providers/bttv/BttvEmotes.cpp @@ -24,7 +24,6 @@ void BTTVEmotes::loadGlobalEmotes() NetworkRequest request(url); request.setCaller(QThread::currentThread()); request.setTimeout(30000); - request.setUseQuickLoadCache(true); request.onSuccess([this](auto result) { auto root = result.parseJson(); auto emotes = root.value("emotes").toArray(); @@ -68,7 +67,6 @@ void BTTVEmotes::loadChannelEmotes(const QString &channelName, std::weak_ptr Date: Tue, 10 Jul 2018 19:27:42 +0300 Subject: [PATCH 05/73] Added mention of user by right-clicking on his nickname. (#589) * Added mention of user by right-clicking on his nickname. * Removed possibility of choice. Always add @ to username. --- src/widgets/helper/ChannelView.cpp | 10 +++++++++- src/widgets/splits/Split.cpp | 5 +++++ src/widgets/splits/Split.hpp | 2 ++ 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/widgets/helper/ChannelView.cpp b/src/widgets/helper/ChannelView.cpp index ef377300e..bb6ec30a0 100644 --- a/src/widgets/helper/ChannelView.cpp +++ b/src/widgets/helper/ChannelView.cpp @@ -963,7 +963,15 @@ void ChannelView::handleMouseClick(QMouseEvent *event, const MessageLayoutElemen } } break; case Qt::RightButton: { - this->addContextMenuItems(hoveredElement, layout); + auto &link = hoveredElement->getLink(); + if (link.type == Link::UserInfo) { + Split *split = dynamic_cast(this->parentWidget()); + if (split != nullptr) { + split->insertTextToInput("@" + link.value + ", "); + } + } else { + this->addContextMenuItems(hoveredElement, layout); + } } break; default:; } diff --git a/src/widgets/splits/Split.cpp b/src/widgets/splits/Split.cpp index a00cf5085..efa6fbc77 100644 --- a/src/widgets/splits/Split.cpp +++ b/src/widgets/splits/Split.cpp @@ -227,6 +227,11 @@ bool Split::getModerationMode() const return this->moderationMode_; } +void Split::insertTextToInput(const QString &text) +{ + this->input_.insertText(text); +} + void Split::showChangeChannelPopup(const char *dialogTitle, bool empty, std::function callback) { diff --git a/src/widgets/splits/Split.hpp b/src/widgets/splits/Split.hpp index c22b324f4..dc525462a 100644 --- a/src/widgets/splits/Split.hpp +++ b/src/widgets/splits/Split.hpp @@ -60,6 +60,8 @@ public: void setModerationMode(bool value); bool getModerationMode() const; + void insertTextToInput(const QString &text); + void showChangeChannelPopup(const char *dialogTitle, bool empty, std::function callback); void giveFocus(Qt::FocusReason reason); From 77f904fae47810d0e772ffde1296a15b1d0c2d8a Mon Sep 17 00:00:00 2001 From: apa420 <17131426+apa420@users.noreply.github.com> Date: Tue, 10 Jul 2018 18:31:34 +0200 Subject: [PATCH 06/73] Fixes the issue with making the chatterino window smaller with long usernames (#588) --- src/widgets/Window.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/widgets/Window.cpp b/src/widgets/Window.cpp index 2ba2b9a3b..ab083ace1 100644 --- a/src/widgets/Window.cpp +++ b/src/widgets/Window.cpp @@ -177,6 +177,7 @@ void Window::addCustomTitlebarButtons() getApp()->windows->showAccountSelectPopup( this->userLabel_->mapToGlobal(this->userLabel_->rect().bottomLeft())); // }); + this->userLabel_->setMinimumWidth(20 * getScale()); } void Window::addDebugStuff() From 727ccd2ff43053cd699183649807fcc68896dd7e Mon Sep 17 00:00:00 2001 From: Lajamerr Mittesdine Date: Wed, 11 Jul 2018 07:50:05 -0400 Subject: [PATCH 07/73] Add support for spotify hyperlinking (#597) * Add support for spotify hyperlinking * Change open link in browser to open link --- src/common/LinkParser.cpp | 13 ++++++++++--- src/messages/MessageBuilder.cpp | 7 +++---- src/widgets/helper/ChannelView.cpp | 2 +- 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/src/common/LinkParser.cpp b/src/common/LinkParser.cpp index 5580a69c0..b6bf12443 100644 --- a/src/common/LinkParser.cpp +++ b/src/common/LinkParser.cpp @@ -20,9 +20,16 @@ LinkParser::LinkParser(const QString &unparsedString) // Read the TLDs in and replace the newlines with pipes QString tldData = t1.readAll().replace(newLineRegex, "|"); - const QString urlRegExp = + const QString hyperlinkRegExp = "^" - // protocol identifier + // Identifier for spotify + "(?x-mi:(spotify:(?:" + "(?:artist|album|track|user:[^:]+:playlist):" + "[a-zA-Z0-9]+|user:[^:]+|search:" + "(?:[-\\w$\\.+!*'(),]+|%[a-fA-F0-9]{2})+)))" + // If nothing matches then just go on + "|" + // Identifier for http and ftp "(?:(?:https?|ftps?)://)?" // user:pass authentication "(?:\\S+(?::\\S*)?@)?" @@ -53,7 +60,7 @@ LinkParser::LinkParser(const QString &unparsedString) "(?:[/?#]\\S*)?" "$"; - return QRegularExpression(urlRegExp, QRegularExpression::CaseInsensitiveOption); + return QRegularExpression(hyperlinkRegExp, QRegularExpression::CaseInsensitiveOption); }(); this->match_ = linkRegex.match(unparsedString); diff --git a/src/messages/MessageBuilder.cpp b/src/messages/MessageBuilder.cpp index 0054b887d..01a6ea629 100644 --- a/src/messages/MessageBuilder.cpp +++ b/src/messages/MessageBuilder.cpp @@ -49,6 +49,7 @@ QString MessageBuilder::matchLink(const QString &string) static QRegularExpression httpRegex("\\bhttps?://", QRegularExpression::CaseInsensitiveOption); static QRegularExpression ftpRegex("\\bftps?://", QRegularExpression::CaseInsensitiveOption); + static QRegularExpression spotifyRegex("\\bspotify:", QRegularExpression::CaseInsensitiveOption); if (!linkParser.hasMatch()) { return QString(); @@ -56,10 +57,8 @@ QString MessageBuilder::matchLink(const QString &string) QString captured = linkParser.getCaptured(); - if (!captured.contains(httpRegex)) { - if (!captured.contains(ftpRegex)) { - captured.insert(0, "http://"); - } + if (!captured.contains(httpRegex) && !captured.contains(ftpRegex) && !captured.contains(spotifyRegex)) { + captured.insert(0, "http://"); } return captured; diff --git a/src/widgets/helper/ChannelView.cpp b/src/widgets/helper/ChannelView.cpp index bb6ec30a0..926845d2c 100644 --- a/src/widgets/helper/ChannelView.cpp +++ b/src/widgets/helper/ChannelView.cpp @@ -1067,7 +1067,7 @@ void ChannelView::addContextMenuItems(const MessageLayoutElement *hoveredElement if (hoveredElement->getLink().type == Link::Url) { QString url = hoveredElement->getLink().value; - menu->addAction("Open link in browser", [url] { QDesktopServices::openUrl(QUrl(url)); }); + menu->addAction("Open link", [url] { QDesktopServices::openUrl(QUrl(url)); }); menu->addAction("Copy link", [url] { QApplication::clipboard()->setText(url); }); menu->addSeparator(); From db099260ad5e76692e60cf618ed837b668d87c7d Mon Sep 17 00:00:00 2001 From: apa420 <17131426+apa420@users.noreply.github.com> Date: Wed, 11 Jul 2018 14:23:21 +0200 Subject: [PATCH 08/73] Added functionality of reading log-size in the moderation-folder (#600) --- src/widgets/settingspages/ModerationPage.cpp | 83 +++++++++++++++++--- 1 file changed, 71 insertions(+), 12 deletions(-) diff --git a/src/widgets/settingspages/ModerationPage.cpp b/src/widgets/settingspages/ModerationPage.cpp index fd07cf5b9..a706d9037 100644 --- a/src/widgets/settingspages/ModerationPage.cpp +++ b/src/widgets/settingspages/ModerationPage.cpp @@ -21,6 +21,7 @@ #include #include #include +#include namespace chatterino { @@ -34,6 +35,52 @@ inline QString CreateLink(const QString &url, bool file = false) return QString("" + url + ""); } +qint64 dirSize(QString dirPath) +{ + qint64 size = 0; + QDir dir(dirPath); + // calculate total size of current directories' files + QDir::Filters fileFilters = QDir::Files | QDir::System | QDir::Hidden; + for (QString filePath : dir.entryList(fileFilters)) { + QFileInfo fi(dir, filePath); + size += fi.size(); + } + // add size of child directories recursively + QDir::Filters dirFilters = QDir::Dirs | QDir::NoDotAndDotDot | QDir::System | QDir::Hidden; + for (QString childDirPath : dir.entryList(dirFilters)) + size += dirSize(dirPath + QDir::separator() + childDirPath); + return size; +} + +QString formatSize(qint64 size) +{ + QStringList units = {"Bytes", "KB", "MB", "GB", "TB", "PB"}; + int i; + double outputSize = size; + for (i = 0; i < units.size() - 1; i++) { + if (outputSize < 1024) + break; + outputSize = outputSize / 1024; + } + return QString("%0 %1").arg(outputSize, 0, 'f', 2).arg(units[i]); +} + +QString fetchLogDirectorySize() +{ + auto app = getApp(); + QString logPathDirectory; + if (app->settings->logPath == "") { + logPathDirectory = app->paths->messageLogDirectory; + } else { + logPathDirectory = app->settings->logPath; + } + qint64 logsSize = dirSize(logPathDirectory); + QString logsSizeLabel = "Your logs currently take up "; + logsSizeLabel += formatSize(logsSize); + logsSizeLabel += " of space"; + return logsSizeLabel; +} + ModerationPage::ModerationPage() : SettingsPage("Moderation", "") { @@ -44,12 +91,14 @@ ModerationPage::ModerationPage() auto logs = tabs.appendTab(new QVBoxLayout, "Logs"); { - // Logs (copied from LoggingMananger) - auto logsPathLabel = logs.emplace(); - app->settings->logPath.connect([app, logsPathLabel](const QString &logPath, auto) mutable { + // Show how big (size-wise) the logs are + auto logsPathSizeLabel = logs.emplace(); + logsPathSizeLabel->setText(QtConcurrent::run([] { return fetchLogDirectorySize(); })); + // Logs (copied from LoggingMananger) + app->settings->logPath.connect([app, logsPathLabel](const QString &logPath, auto) mutable { QString pathOriginal; if (logPath == "") { @@ -86,19 +135,29 @@ ModerationPage::ModerationPage() auto selectDir = logs.emplace("Set custom logpath"); // Setting custom logpath - QObject::connect(selectDir.getElement(), &QPushButton::clicked, this, [this]() { - auto app = getApp(); - auto dirName = QFileDialog::getExistingDirectory(this); + QObject::connect(selectDir.getElement(), &QPushButton::clicked, this, + [this, logsPathSizeLabel]() mutable { + auto app = getApp(); + auto dirName = QFileDialog::getExistingDirectory(this); - app->settings->logPath = dirName; - }); + app->settings->logPath = dirName; + + // Refresh: Show how big (size-wise) the logs are + logsPathSizeLabel->setText( + QtConcurrent::run([] { return fetchLogDirectorySize(); })); + }); // Reset custom logpath auto resetDir = logs.emplace("Reset logpath"); - QObject::connect(resetDir.getElement(), &QPushButton::clicked, this, []() { - auto app = getApp(); - app->settings->logPath = ""; - }); + QObject::connect(resetDir.getElement(), &QPushButton::clicked, this, + [logsPathSizeLabel]() mutable { + auto app = getApp(); + app->settings->logPath = ""; + + // Refresh: Show how big (size-wise) the logs are + logsPathSizeLabel->setText( + QtConcurrent::run([] { return fetchLogDirectorySize(); })); + }); // Logs end } From f63d9d3e33c1abce24da108fb329060123a42bee Mon Sep 17 00:00:00 2001 From: Nikolai Zimmermann Date: Wed, 11 Jul 2018 16:09:28 +0200 Subject: [PATCH 09/73] Better time display for timeouts (#601) * Better time display for timeouts * remove newline --- src/messages/Message.cpp | 44 ++++++++++++++++++++++++---------------- 1 file changed, 26 insertions(+), 18 deletions(-) diff --git a/src/messages/Message.cpp b/src/messages/Message.cpp index fcf5fbc9e..247dac24d 100644 --- a/src/messages/Message.cpp +++ b/src/messages/Message.cpp @@ -68,27 +68,35 @@ QString makeDuration(int count, const QString &order) return text; } -QString makeDuration(uint32_t timeoutSeconds) +QString makeDuration(int timeoutSeconds) { - int timeoutMinutes = floor(timeoutSeconds / 60); + QString res = ""; - if (timeoutMinutes > 0 && timeoutSeconds % 60 == 0) { - int timeoutHours = floor(timeoutMinutes / 60); - - if (timeoutHours > 0 && timeoutMinutes % 60 == 0) { - int timeoutDays = floor(timeoutHours / 24); - - if (timeoutDays > 0 && timeoutHours % 24 == 0) { - return makeDuration(timeoutDays, "day"); - } - - return makeDuration(timeoutHours, "hour"); - } - - return makeDuration(timeoutMinutes, "minute"); + int seconds = timeoutSeconds % 60; + int timeoutMinutes = timeoutSeconds / 60; + int minutes = timeoutMinutes % 60; + int timeoutHours = timeoutMinutes / 60; + int hours = timeoutHours % 24; + int days = timeoutHours / 24; + if (days > 0) { + res.append(makeDuration(days, "day")); } - - return makeDuration(timeoutSeconds, "second"); + if (hours > 0) { + if (!res.isEmpty() ) + res.append(" "); + res.append(makeDuration(hours, "hour")); + } + if (minutes > 0) { + if (!res.isEmpty() ) + res.append(" "); + res.append(makeDuration(minutes, "minute")); + } + if (seconds > 0) { + if (!res.endsWith(" ")) + res.append(" "); + res.append(makeDuration(seconds, "second")); + } + return res; } } // namespace From 4ea1ed085f64b600e44bbbd2c3d6543a6d5380dd Mon Sep 17 00:00:00 2001 From: Rasmus Karlsson Date: Wed, 11 Jul 2018 16:53:19 +0200 Subject: [PATCH 10/73] Remove unneccesary QString operator= --- src/messages/Message.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/messages/Message.cpp b/src/messages/Message.cpp index 247dac24d..60e9a43e6 100644 --- a/src/messages/Message.cpp +++ b/src/messages/Message.cpp @@ -70,7 +70,7 @@ QString makeDuration(int count, const QString &order) QString makeDuration(int timeoutSeconds) { - QString res = ""; + QString res; int seconds = timeoutSeconds % 60; int timeoutMinutes = timeoutSeconds / 60; From 352da60d07e225777150b90bfafbd60b817963cb Mon Sep 17 00:00:00 2001 From: Rasmus Karlsson Date: Wed, 11 Jul 2018 16:58:57 +0200 Subject: [PATCH 11/73] Fix code formatting (code wasn't clang-formatted) Shorten "minute" to "m" etc Fix space-adding inconsistency when adding "seconds" --- src/messages/Message.cpp | 31 +++++++++++++------------------ 1 file changed, 13 insertions(+), 18 deletions(-) diff --git a/src/messages/Message.cpp b/src/messages/Message.cpp index 60e9a43e6..2af8b8dcb 100644 --- a/src/messages/Message.cpp +++ b/src/messages/Message.cpp @@ -54,18 +54,10 @@ MessagePtr Message::createMessage(const QString &text) namespace { -QString makeDuration(int count, const QString &order) +void appendDuration(int count, QChar &&order, QString &outString) { - QString text; - - text.append(QString::number(count)); - text.append(" " + order); - - if (count > 1) { - text.append("s"); - } - - return text; + outString.append(QString::number(count)); + outString.append(order); } QString makeDuration(int timeoutSeconds) @@ -79,22 +71,25 @@ QString makeDuration(int timeoutSeconds) int hours = timeoutHours % 24; int days = timeoutHours / 24; if (days > 0) { - res.append(makeDuration(days, "day")); + appendDuration(days, 'd', res); } if (hours > 0) { - if (!res.isEmpty() ) + if (!res.isEmpty()) { res.append(" "); - res.append(makeDuration(hours, "hour")); + } + appendDuration(hours, 'h', res); } if (minutes > 0) { - if (!res.isEmpty() ) + if (!res.isEmpty()) { res.append(" "); - res.append(makeDuration(minutes, "minute")); + } + appendDuration(minutes, 'm', res); } if (seconds > 0) { - if (!res.endsWith(" ")) + if (!res.isEmpty()) { res.append(" "); - res.append(makeDuration(seconds, "second")); + } + appendDuration(seconds, 's', res); } return res; } From c26e18c2bf8adc0aa0c636aee25db0f652c545ce Mon Sep 17 00:00:00 2001 From: apa420 Date: Thu, 12 Jul 2018 03:47:37 +0200 Subject: [PATCH 12/73] Added functionality of /follow and /unfollow --- chatterino.pro | 6 ++- .../commands/CommandController.cpp | 51 +++++++++++++++++++ src/providers/twitch/twitchapi.cpp | 37 ++++++++++++++ src/providers/twitch/twitchapi.hpp | 17 +++++++ 4 files changed, 109 insertions(+), 2 deletions(-) create mode 100644 src/providers/twitch/twitchapi.cpp create mode 100644 src/providers/twitch/twitchapi.hpp diff --git a/chatterino.pro b/chatterino.pro index 22554da5f..a7450cc82 100644 --- a/chatterino.pro +++ b/chatterino.pro @@ -231,7 +231,8 @@ SOURCES += \ src/util/InitUpdateButton.cpp \ src/widgets/dialogs/UpdateDialog.cpp \ src/widgets/settingspages/IgnoresPage.cpp \ - src/providers/twitch/PubsubClient.cpp + src/providers/twitch/PubsubClient.cpp \ + src/providers/twitch/TwitchApi.cpp HEADERS += \ src/Application.hpp \ @@ -412,7 +413,8 @@ HEADERS += \ src/util/InitUpdateButton.hpp \ src/widgets/dialogs/UpdateDialog.hpp \ src/widgets/settingspages/IgnoresPage.hpp \ - src/providers/twitch/PubsubClient.hpp + src/providers/twitch/PubsubClient.hpp \ + src/providers/twitch/TwitchApi.hpp RESOURCES += \ resources/resources.qrc \ diff --git a/src/controllers/commands/CommandController.cpp b/src/controllers/commands/CommandController.cpp index 8d33391f9..9e570fd39 100644 --- a/src/controllers/commands/CommandController.cpp +++ b/src/controllers/commands/CommandController.cpp @@ -2,12 +2,15 @@ #include "Application.hpp" #include "common/SignalVector.hpp" +#include "common/UrlFetch.hpp" #include "controllers/accounts/AccountController.hpp" #include "controllers/commands/Command.hpp" #include "controllers/commands/CommandModel.hpp" #include "messages/MessageBuilder.hpp" +#include "providers/twitch/TwitchAccount.hpp" #include "providers/twitch/TwitchChannel.hpp" #include "providers/twitch/TwitchServer.hpp" +#include "providers/twitch/twitchapi.hpp" #include "singletons/Paths.hpp" #include "singletons/Settings.hpp" #include "widgets/dialogs/LogsPopup.hpp" @@ -203,6 +206,54 @@ QString CommandController::execCommand(const QString &text, ChannelPtr channel, channel->addMessage(Message::createSystemMessage(message)); }); + return ""; + } else if (commandName == "/follow" && words.size() >= 2) { + auto app = getApp(); + + auto user = app->accounts->twitch.getCurrent(); + auto target = words.at(1); + if (user->isAnon()) { + channel->addMessage( + Message::createSystemMessage("You must be logged in to follow someone")); + + return ""; + } + TwitchApi::FindUserId(target, [user, channel, target](QString userId) { + if (userId.isEmpty()) { + channel->addMessage( + Message::createSystemMessage("User " + target + " was not found!")); + return; + } + user->followUser(userId, [channel, target]() { + channel->addMessage( + Message::createSystemMessage("You successfully followed " + target)); + }); + }); + + return ""; + } else if (commandName == "/unfollow" && words.size() >= 2) { + auto app = getApp(); + + auto user = app->accounts->twitch.getCurrent(); + auto target = words.at(1); + if (user->isAnon()) { + channel->addMessage( + Message::createSystemMessage("You must be logged in to follow someone")); + return ""; + } + + TwitchApi::FindUserId(target, [user, channel, target](QString userId) { + if (userId.isEmpty()) { + channel->addMessage( + Message::createSystemMessage("User " + target + " was not found!")); + return; + } + user->unfollowUser(userId, [channel, target]() { + channel->addMessage( + Message::createSystemMessage("You successfully unfollowed " + target)); + }); + }); + return ""; } else if (commandName == "/logs") { if (words.size() < 2) { diff --git a/src/providers/twitch/twitchapi.cpp b/src/providers/twitch/twitchapi.cpp new file mode 100644 index 000000000..153778369 --- /dev/null +++ b/src/providers/twitch/twitchapi.cpp @@ -0,0 +1,37 @@ +#include "providers/twitch/twitchapi.hpp" + +#include "Application.hpp" +#include "common/UrlFetch.hpp" +#include "controllers/accounts/AccountController.hpp" +#include "providers/twitch/TwitchCommon.hpp" + +#include +#include + +namespace chatterino { + +void TwitchApi::FindUserId(const QString user, std::function callback) +{ + QString requestUrl("https://api.twitch.tv/kraken/users?login=" + user + + "&api_version=5&client_id=" + getDefaultClientID()); + NetworkRequest request(requestUrl); + request.setCaller(QThread::currentThread()); + request.setTimeout(30000); + request.onSuccess([callback](auto result) mutable { + QString userId; + + auto root = result.parseJson(); + if (root.value("users").toArray().isEmpty()) { + callback(""); + return true; + } + userId = root.value("users").toArray()[0].toObject().value("_id").toString(); + callback(userId); + return true; + }); + + request.execute(); + return; +} + +} // namespace chatterino diff --git a/src/providers/twitch/twitchapi.hpp b/src/providers/twitch/twitchapi.hpp new file mode 100644 index 000000000..9e38bd2cb --- /dev/null +++ b/src/providers/twitch/twitchapi.hpp @@ -0,0 +1,17 @@ +#pragma once + +#include "common/Singleton.hpp" + +#include + +namespace chatterino { + +class TwitchApi +{ +public: + static void FindUserId(const QString user, std::function callback); + +private: +}; + +} // namespace chatterino From 801779b87298560078193e0216cb73cca3de1262 Mon Sep 17 00:00:00 2001 From: apa420 Date: Thu, 12 Jul 2018 10:52:18 +0200 Subject: [PATCH 13/73] fixed stuff for PR --- .../commands/CommandController.cpp | 16 +++--- src/providers/twitch/TwitchApi.cpp | 52 +++++++++++++++++++ .../twitch/{twitchapi.hpp => TwitchApi.hpp} | 4 +- src/providers/twitch/twitchapi.cpp | 37 ------------- 4 files changed, 62 insertions(+), 47 deletions(-) create mode 100644 src/providers/twitch/TwitchApi.cpp rename src/providers/twitch/{twitchapi.hpp => TwitchApi.hpp} (60%) delete mode 100644 src/providers/twitch/twitchapi.cpp diff --git a/src/controllers/commands/CommandController.cpp b/src/controllers/commands/CommandController.cpp index 9e570fd39..d56e8ce6d 100644 --- a/src/controllers/commands/CommandController.cpp +++ b/src/controllers/commands/CommandController.cpp @@ -212,16 +212,17 @@ QString CommandController::execCommand(const QString &text, ChannelPtr channel, auto user = app->accounts->twitch.getCurrent(); auto target = words.at(1); + if (user->isAnon()) { channel->addMessage( Message::createSystemMessage("You must be logged in to follow someone")); - return ""; } - TwitchApi::FindUserId(target, [user, channel, target](QString userId) { + + TwitchApi::findUserId(target, [user, channel, target](QString userId) { if (userId.isEmpty()) { - channel->addMessage( - Message::createSystemMessage("User " + target + " was not found!")); + channel->addMessage(Message::createSystemMessage( + "User " + target + " could not be followed!")); return; } user->followUser(userId, [channel, target]() { @@ -236,16 +237,17 @@ QString CommandController::execCommand(const QString &text, ChannelPtr channel, auto user = app->accounts->twitch.getCurrent(); auto target = words.at(1); + if (user->isAnon()) { channel->addMessage( Message::createSystemMessage("You must be logged in to follow someone")); return ""; } - TwitchApi::FindUserId(target, [user, channel, target](QString userId) { + TwitchApi::findUserId(target, [user, channel, target](QString userId) { if (userId.isEmpty()) { - channel->addMessage( - Message::createSystemMessage("User " + target + " was not found!")); + channel->addMessage(Message::createSystemMessage( + "User " + target + " could not be followed!")); return; } user->unfollowUser(userId, [channel, target]() { diff --git a/src/providers/twitch/TwitchApi.cpp b/src/providers/twitch/TwitchApi.cpp new file mode 100644 index 000000000..06c3380fa --- /dev/null +++ b/src/providers/twitch/TwitchApi.cpp @@ -0,0 +1,52 @@ +#include "providers/twitch/twitchapi.hpp" + +#include "common/NetworkRequest.hpp" +#include "debug/Log.hpp" +#include "providers/twitch/TwitchCommon.hpp" + +#include + +namespace chatterino { + +void TwitchApi::findUserId(const QString user, std::function successCallback) +{ + QString requestUrl("https://api.twitch.tv/kraken/users?login=" + user); + + NetworkRequest request(requestUrl); + request.setCaller(QThread::currentThread()); + request.makeAuthorizedV5(getDefaultClientID()); + request.setTimeout(30000); + request.onSuccess([successCallback](auto result) mutable { + auto root = result.parseJson(); + if (!root.value("users").isArray()) { + Log("API Error while getting user id, users is not an array"); + successCallback(""); + return true; + } + auto users = root.value("users").toArray(); + if (users.size() != 1) { + Log("API Error while getting user id, users array size is not 1"); + successCallback(""); + return true; + } + if (!users[0].isObject()) { + Log("API Error while getting user id, first user is not an object"); + successCallback(""); + return true; + } + auto firstUser = users[0].toObject(); + auto id = firstUser.value("_id"); + if (!id.isString()) { + Log("API Error: while getting user id, first user object `_id` key is not a " + "string"); + successCallback(""); + return true; + } + successCallback(id.toString()); + return true; + }); + + request.execute(); +} + +} // namespace chatterino diff --git a/src/providers/twitch/twitchapi.hpp b/src/providers/twitch/TwitchApi.hpp similarity index 60% rename from src/providers/twitch/twitchapi.hpp rename to src/providers/twitch/TwitchApi.hpp index 9e38bd2cb..00b8ff4d7 100644 --- a/src/providers/twitch/twitchapi.hpp +++ b/src/providers/twitch/TwitchApi.hpp @@ -1,7 +1,5 @@ #pragma once -#include "common/Singleton.hpp" - #include namespace chatterino { @@ -9,7 +7,7 @@ namespace chatterino { class TwitchApi { public: - static void FindUserId(const QString user, std::function callback); + static void findUserId(const QString user, std::function callback); private: }; diff --git a/src/providers/twitch/twitchapi.cpp b/src/providers/twitch/twitchapi.cpp deleted file mode 100644 index 153778369..000000000 --- a/src/providers/twitch/twitchapi.cpp +++ /dev/null @@ -1,37 +0,0 @@ -#include "providers/twitch/twitchapi.hpp" - -#include "Application.hpp" -#include "common/UrlFetch.hpp" -#include "controllers/accounts/AccountController.hpp" -#include "providers/twitch/TwitchCommon.hpp" - -#include -#include - -namespace chatterino { - -void TwitchApi::FindUserId(const QString user, std::function callback) -{ - QString requestUrl("https://api.twitch.tv/kraken/users?login=" + user + - "&api_version=5&client_id=" + getDefaultClientID()); - NetworkRequest request(requestUrl); - request.setCaller(QThread::currentThread()); - request.setTimeout(30000); - request.onSuccess([callback](auto result) mutable { - QString userId; - - auto root = result.parseJson(); - if (root.value("users").toArray().isEmpty()) { - callback(""); - return true; - } - userId = root.value("users").toArray()[0].toObject().value("_id").toString(); - callback(userId); - return true; - }); - - request.execute(); - return; -} - -} // namespace chatterino From 7288c5a0b63863cabcaaed9493b3fc38c247fe4e Mon Sep 17 00:00:00 2001 From: apa420 Date: Thu, 12 Jul 2018 10:58:29 +0200 Subject: [PATCH 14/73] removed unecessary #include --- src/controllers/commands/CommandController.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/controllers/commands/CommandController.cpp b/src/controllers/commands/CommandController.cpp index d56e8ce6d..c85d6e143 100644 --- a/src/controllers/commands/CommandController.cpp +++ b/src/controllers/commands/CommandController.cpp @@ -2,15 +2,14 @@ #include "Application.hpp" #include "common/SignalVector.hpp" -#include "common/UrlFetch.hpp" #include "controllers/accounts/AccountController.hpp" #include "controllers/commands/Command.hpp" #include "controllers/commands/CommandModel.hpp" #include "messages/MessageBuilder.hpp" #include "providers/twitch/TwitchAccount.hpp" +#include "providers/twitch/TwitchApi.hpp" #include "providers/twitch/TwitchChannel.hpp" #include "providers/twitch/TwitchServer.hpp" -#include "providers/twitch/twitchapi.hpp" #include "singletons/Paths.hpp" #include "singletons/Settings.hpp" #include "widgets/dialogs/LogsPopup.hpp" From 1d2e56f19df6992db35b8115a5be3f3333d7378a Mon Sep 17 00:00:00 2001 From: apa420 Date: Thu, 12 Jul 2018 11:11:21 +0200 Subject: [PATCH 15/73] changed return-value of errors to false --- src/providers/twitch/TwitchApi.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/providers/twitch/TwitchApi.cpp b/src/providers/twitch/TwitchApi.cpp index 06c3380fa..a1363a813 100644 --- a/src/providers/twitch/TwitchApi.cpp +++ b/src/providers/twitch/TwitchApi.cpp @@ -21,18 +21,18 @@ void TwitchApi::findUserId(const QString user, std::function succ if (!root.value("users").isArray()) { Log("API Error while getting user id, users is not an array"); successCallback(""); - return true; + return false; } auto users = root.value("users").toArray(); if (users.size() != 1) { Log("API Error while getting user id, users array size is not 1"); successCallback(""); - return true; + return false; } if (!users[0].isObject()) { Log("API Error while getting user id, first user is not an object"); successCallback(""); - return true; + return false; } auto firstUser = users[0].toObject(); auto id = firstUser.value("_id"); @@ -40,7 +40,7 @@ void TwitchApi::findUserId(const QString user, std::function succ Log("API Error: while getting user id, first user object `_id` key is not a " "string"); successCallback(""); - return true; + return false; } successCallback(id.toString()); return true; From 12a21b7b1af0c8f39605ca923e64afb032f7d805 Mon Sep 17 00:00:00 2001 From: Lajamerr Mittesdine Date: Thu, 12 Jul 2018 12:56:08 -0400 Subject: [PATCH 16/73] Fix match linking (#608) Right now it's matching links even if they have garbage text infront of it. For example, `sahttp://www.google.com` This change fixes that. --- src/common/LinkParser.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/common/LinkParser.cpp b/src/common/LinkParser.cpp index b6bf12443..ac4ddf734 100644 --- a/src/common/LinkParser.cpp +++ b/src/common/LinkParser.cpp @@ -29,6 +29,7 @@ LinkParser::LinkParser(const QString &unparsedString) "(?:[-\\w$\\.+!*'(),]+|%[a-fA-F0-9]{2})+)))" // If nothing matches then just go on "|" + "^" // Identifier for http and ftp "(?:(?:https?|ftps?)://)?" // user:pass authentication From 9dda0a05fa310c9172282d9700320f6242b3ad14 Mon Sep 17 00:00:00 2001 From: apa420 <17131426+apa420@users.noreply.github.com> Date: Thu, 12 Jul 2018 18:56:54 +0200 Subject: [PATCH 17/73] @boldUsernames now work dynamically with the setting (#607) --- src/messages/MessageElement.hpp | 4 ++++ src/providers/twitch/TwitchMessageBuilder.cpp | 15 +++++++++++---- src/singletons/Settings.hpp | 3 +-- src/singletons/WindowManager.cpp | 2 ++ src/widgets/settingspages/LookPage.cpp | 2 +- 5 files changed, 19 insertions(+), 7 deletions(-) diff --git a/src/messages/MessageElement.hpp b/src/messages/MessageElement.hpp index d933c8dc3..764101641 100644 --- a/src/messages/MessageElement.hpp +++ b/src/messages/MessageElement.hpp @@ -92,6 +92,10 @@ public: // used in the ChannelView class to make the collapse buttons visible if needed Collapsed = (1 << 26), + // used for dynamic bold usernames + BoldUsername = (1 << 27), + NonBoldUsername = (1 << 28), + Default = Timestamp | Badges | Username | BitsStatic | FfzEmoteImage | BttvEmoteImage | TwitchEmoteImage | BitsAmount | Text | AlwaysShow, }; diff --git a/src/providers/twitch/TwitchMessageBuilder.cpp b/src/providers/twitch/TwitchMessageBuilder.cpp index bac7baa3a..73defdf4a 100644 --- a/src/providers/twitch/TwitchMessageBuilder.cpp +++ b/src/providers/twitch/TwitchMessageBuilder.cpp @@ -218,7 +218,7 @@ MessagePtr TwitchMessageBuilder::build() QString linkString = this->matchLink(string); auto fontStyle = FontStyle::ChatMedium; - if (string.startsWith('@') && app->settings->usernameBold) { + if (string.startsWith('@') && app->settings->enableUsernameBold) { fontStyle = FontStyle::ChatMediumBold; } @@ -246,10 +246,17 @@ MessagePtr TwitchMessageBuilder::build() link = Link(Link::Url, linkString); textColor = MessageColor(MessageColor::Link); } + if (string.startsWith('@')) { + this->emplace(string, TextElement::BoldUsername, textColor, + FontStyle::ChatMediumBold) // + ->setLink(link); + this->emplace(string, TextElement::NonBoldUsername, textColor) // + ->setLink(link); + } else { + this->emplace(string, TextElement::Text, textColor) // + ->setLink(link); + } - this->emplace(string, MessageElement::Text, textColor, - fontStyle) // - ->setLink(link); } else { // is emoji this->emplace(emoteData, EmoteElement::EmojiAll); } diff --git a/src/singletons/Settings.hpp b/src/singletons/Settings.hpp index ac0cbf0b8..1eda0e815 100644 --- a/src/singletons/Settings.hpp +++ b/src/singletons/Settings.hpp @@ -43,6 +43,7 @@ public: BoolSetting enableSmoothScrolling = {"/appearance/smoothScrolling", true}; BoolSetting enableSmoothScrollingNewMessages = {"/appearance/smoothScrollingNewMessages", false}; + BoolSetting enableUsernameBold = {"/appearence/messages/boldUsernames", false}; // BoolSetting useCustomWindowFrame = {"/appearance/useCustomWindowFrame", false}; /// Behaviour @@ -108,8 +109,6 @@ public: BoolSetting inlineWhispers = {"/whispers/enableInlineWhispers", true}; - BoolSetting usernameBold = {"/appearence/messages/boldUsernames", false}; - /// External tools // Streamlink BoolSetting streamlinkUseCustomPath = {"/external/streamlink/useCustomPath", false}; diff --git a/src/singletons/WindowManager.cpp b/src/singletons/WindowManager.cpp index 4077200df..7dc3b7421 100644 --- a/src/singletons/WindowManager.cpp +++ b/src/singletons/WindowManager.cpp @@ -68,6 +68,7 @@ WindowManager::WindowManager() this->wordFlagsListener_.addSetting(settings->enableEmojis); this->wordFlagsListener_.addSetting(settings->enableFfzEmotes); this->wordFlagsListener_.addSetting(settings->enableTwitchEmotes); + this->wordFlagsListener_.addSetting(settings->enableUsernameBold); this->wordFlagsListener_.cb = [this](auto) { this->updateWordTypeMask(); // }; @@ -110,6 +111,7 @@ void WindowManager::updateWordTypeMask() // misc flags |= MEF::AlwaysShow; flags |= MEF::Collapsed; + flags |= settings->enableUsernameBold ? MEF::BoldUsername : MEF::NonBoldUsername; // update flags MessageElement::Flags newFlags = static_cast(flags); diff --git a/src/widgets/settingspages/LookPage.cpp b/src/widgets/settingspages/LookPage.cpp index b0d9baaf2..7769d5b6d 100644 --- a/src/widgets/settingspages/LookPage.cpp +++ b/src/widgets/settingspages/LookPage.cpp @@ -143,7 +143,7 @@ void LookPage::addMessageTab(LayoutCreator layout) // lowercase links layout.append(this->createCheckBox("Lowercase domains", getSettings()->lowercaseLink)); // bold usernames - layout.append(this->createCheckBox("Bold @usernames", getSettings()->usernameBold)); + layout.append(this->createCheckBox("Bold @usernames", getSettings()->enableUsernameBold)); // collapsing { From fb0cdf78cbfda5fb2243318149803effbaf0ab18 Mon Sep 17 00:00:00 2001 From: apa420 <17131426+apa420@users.noreply.github.com> Date: Thu, 12 Jul 2018 18:57:11 +0200 Subject: [PATCH 18/73] Added description when typing /follow (#606) * Added description when typing /follow * syntax fix of 1364e0e707dd6f0989291e1f399460c5a114dbb5 --- src/controllers/commands/CommandController.cpp | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/controllers/commands/CommandController.cpp b/src/controllers/commands/CommandController.cpp index c85d6e143..595643ccd 100644 --- a/src/controllers/commands/CommandController.cpp +++ b/src/controllers/commands/CommandController.cpp @@ -206,7 +206,11 @@ QString CommandController::execCommand(const QString &text, ChannelPtr channel, }); return ""; - } else if (commandName == "/follow" && words.size() >= 2) { + } else if (commandName == "/follow") { + if (words.size() < 2) { + channel->addMessage(Message::createSystemMessage("Usage: /follow [user]")); + return ""; + } auto app = getApp(); auto user = app->accounts->twitch.getCurrent(); @@ -231,7 +235,11 @@ QString CommandController::execCommand(const QString &text, ChannelPtr channel, }); return ""; - } else if (commandName == "/unfollow" && words.size() >= 2) { + } else if (commandName == "/unfollow") { + if (words.size() < 2) { + channel->addMessage(Message::createSystemMessage("Usage: /unfollow [user]")); + return ""; + } auto app = getApp(); auto user = app->accounts->twitch.getCurrent(); From 12093d1bb606c739b70405ef5e5ffb799a15a521 Mon Sep 17 00:00:00 2001 From: apa420 <17131426+apa420@users.noreply.github.com> Date: Thu, 12 Jul 2018 18:58:56 +0200 Subject: [PATCH 19/73] Adding elements to the preview in the look tab (#605) * fixes issue #584 * Added different elements to the preview in the look tab --- resources/images/pajaDank.png | Bin 0 -> 3862 bytes resources/resources.qrc | 1 + src/singletons/Resources.cpp | 2 ++ src/singletons/Resources.hpp | 2 ++ src/widgets/settingspages/LookPage.cpp | 18 +++++++++++++----- 5 files changed, 18 insertions(+), 5 deletions(-) create mode 100644 resources/images/pajaDank.png diff --git a/resources/images/pajaDank.png b/resources/images/pajaDank.png new file mode 100644 index 0000000000000000000000000000000000000000..c8746932b373445fa60c3d2baa388c9edef000fa GIT binary patch literal 3862 zcmV+x59#oUP)KLZ*U+IBfRsybQWXdwQbLP>6pAqfylh#{fb6;Z(vMMVS~$e@S=j*ftg6;Uhf59&ghTmgWD0l;*T zI709Y^p6lP1rIRMx#05C~cW=H_Aw*bJ-5DT&Z2n+x)QHX^p z00esgV8|mQcmRZ%02D^@S3L16t`O%c004NIvOKvYIYoh62rY33S640`D9%Y2D-rV&neh&#Q1i z007~1e$oCcFS8neI|hJl{-P!B1ZZ9hpmq0)X0i`JwE&>$+E?>%_LC6RbVIkUx0b+_+BaR3cnT7Zv!AJxW zizFb)h!jyGOOZ85F;a?DAXP{m@;!0_IfqH8(HlgRxt7s3}k3K`kFu>>-2Q$QMFfPW!La{h336o>X zu_CMttHv6zR;&ZNiS=X8v3CR#fknUxHUxJ0uoBa_M6WNWeqIg~6QE69c9o#eyhGvpiOA@W-aonk<7r1(?fC{oI5N*U!4 zfg=2N-7=cNnjjOr{yriy6mMFgG#l znCF=fnQv8CDz++o6_Lscl}eQ+l^ZHARH>?_s@|##Rr6KLRFA1%Q+=*RRWnoLsR`7U zt5vFIcfW3@?wFpwUVxrVZ>QdQz32KIeJ}k~{cZZE^+ya? z2D1z#2HOnI7(B%_ac?{wFUQ;QQA1tBKtrWrm0_3Rgps+?Jfqb{jYbcQX~taRB;#$y zZN{S}1|}gUOHJxc?wV3fxuz+mJ4`!F$IZ;mqRrNsHJd##*D~ju=bP7?-?v~|cv>vB zsJ6IeNwVZxrdjT`yl#bBIa#GxRa#xMMy;K#CDyyGyQdMSxlWT#tDe?p!?5wT$+oGt z8L;Kp2HUQ-ZMJ=3XJQv;x5ci*?vuTfeY$;({XGW_huIFR9a(?@3)XSs8O^N5RyOM=TTmp(3=8^+zpz2r)C z^>JO{deZfso3oq3?Wo(Y?l$ge?uXo;%ru`Vo>?<<(8I_>;8Eq#KMS9gFl*neeosSB zfoHYnBQIkwkyowPu(zdms`p{<7e4kra-ZWq<2*OsGTvEV%s0Td$hXT+!*8Bnh2KMe zBmZRodjHV?r+_5^X9J0WL4jKW`}lf%A-|44I@@LTvf1rHjG(ze6+w@Jt%Bvjts!X0 z?2xS?_ve_-kiKB_KiJlZ$9G`c^=E@oNG)mWWaNo-3TIW8)$Hg0Ub-~8?KhvJ>$ z3*&nim@mj(aCxE5!t{lw7O5^0EIO7zOo&c6l<+|iDySBWCGrz@C5{St!X3hAA}`T4 z(TLbXTq+(;@<=L8dXnssyft|w#WSTW<++3>sgS%(4NTpeI-VAqb|7ssJvzNHgOZVu zaYCvgO_R1~>SyL=cFU|~g|hy|Zi}}s9+d~lYqOB71z9Z$wnC=pR9Yz4DhIM>Wmjgu z&56o6maCpC&F##y%G;1PobR9i?GnNg;gYtchD%p19a!eQtZF&3JaKv33gZ<8D~47E ztUS1iwkmDaPpj=$m#%)jCVEY4fnLGNg2A-`YwHVD3gv};>)hAvT~AmqS>Lr``i7kw zJ{5_It`yrBmlc25DBO7E8;5VoznR>Ww5hAaxn$2~(q`%A-YuS64wkBy=9dm`4cXeX z4c}I@?e+FW+b@^RDBHV(wnMq2zdX3SWv9u`%{xC-q*U}&`cyXV(%rRT*Z6MH?i+i& z_B8C(+grT%{XWUQ+f@NoP1R=AW&26{v-dx)iK^-Nmiuj8txj!m?Z*Ss1N{dh4z}01 z)YTo*JycSU)+_5r4#yw9{+;i4Ee$peRgIj+;v;ZGdF1K$3E%e~4LaI(jC-u%2h$&R z9cLXcYC@Xwnns&bn)_Q~Te?roKGD|d-g^8;+aC{{G(1^(O7m37Y1-+6)01cN&y1aw zoqc{T`P^XJqPBbIW6s}d4{z_f5Om?vMgNQEJG?v2T=KYd^0M3I6IZxbny)%vZR&LD zJpPl@Psh8QyPB@KTx+@RdcC!KX7}kEo;S|j^u2lU7XQ}Oo;f|;z4Ll+_r>@1-xl3| zawq-H%e&ckC+@AhPrP6BKT#_XdT7&;F71j}Joy zkC~6lh7E@6o;W@^IpRNZ{ptLtL(gQ-CY~4mqW;US7Zxvm_|@yz&e53Bp_lTPlfP|z zrTyx_>lv@x#=^!PzR7qqF<$gm`|ZJZ+;<)Cqu&ot2z=0000WV@Og>004R=004l4008;_004mL004C`008P>0026e000+nl3&F} z000C*NkldxA_bUOP+1tQxJGHT zQYx&jvLFI67-%Fx6SbfcNE9?2+F)TWk}FU&F=*mgs0n}eJ=nRqKQr_F%H8Zj=H2)0 z_hx5z1Em!GCkO)Cf+BMS1dOAUqFA~97K&RMVq4U=ZII7gN`)S9zV9o0vMZ;DL$@c6 z^svVaO*Wwl0t4N7aeobl$PopW5=mtP-Fd|v18dy}P#_ezH#1}P!rnZp00p9e02azK zHhi1iH_~?_Qn*3DfdbuA;M813?QGnohP^xi2jK<^^re7bzHeeAd-fl#0=0msd70|lahpppbH-`!F~L<=7tY}?`5 zT z3bYrSSH>a4xDaB1#TL4}z8KmEax)h`B~}~m{6m2VG5&=c-GEbya1-OE(a_vm6k8M0{ugNnk&VGo1F348?+I&D=^*AP7AO~yDgNv4g%Bmni6_P z)?P(xt(jh##fZBYVx(1EzO)3-ALq2r$Gt4KWQ{zqSb@nz9j&fy?D`c}+e;6Cp*~QW zf#8ZR9iwYgW(l7;na=cTo$bQC@$y;d>e>c=#x5$5U(%fy)I^bw$8J_U3USB3fQ@!wz-$yfhKI{cs!q&8LE3p->5xn&|6R)7z(SEi0SXAB6V*OZ zKqwHPjc||`cdmwRpBuUk1p*Wh1q3J{Fhr1_A64G}isQ9L&&S=x;KB_N(T{K8EBd4r zZb~VviGhUz^nt#>*(4MQ3k9%Hi_y6eEv@F(eWn5mgoOfFsD(70ylnJ*Iu@$);0qK8 z3k4`3aPt~y`nn_`O)~4CGvm@VnB{f_P#`Q6RIP<>zW)@u9p9F(}SNnHc6yEs{#eWLV@#*yv|#Ve6wAyDd&>%1 zwV66vn~hkdN!tZbAPNX9PN6!<(KV}-#$aV;777F?p!Wo;5AUidx06giidD^Tcam&U zxPwkZlTD%9`PW#>HR5(CrJz7qD5$K3o=f$rr@3*AL_|27B;vpPrt>)?$#(ICt^@oT Y00ymLjPQ&xv;Y7A07*qoM6N<$f`I`Y_W%F@ literal 0 HcmV?d00001 diff --git a/resources/resources.qrc b/resources/resources.qrc index cf41c3469..129713804 100644 --- a/resources/resources.qrc +++ b/resources/resources.qrc @@ -74,6 +74,7 @@ avatars/pajlada.png images/download_update.png images/download_update_error.png + images/pajaDank.png qt.conf diff --git a/src/singletons/Resources.cpp b/src/singletons/Resources.cpp index 0180c581b..fdbbf9bbf 100644 --- a/src/singletons/Resources.cpp +++ b/src/singletons/Resources.cpp @@ -287,6 +287,8 @@ Resources::Resources() , splitHeaderContext(lli(":/images/tool_moreCollapser_off16.png")) , buttonBan(lli(":/images/button_ban.png", 0.25)) , buttonTimeout(lli(":/images/button_timeout.png", 0.25)) + , pajaDank(lli(":/images/pajaDank.png", 0.25)) + , ppHop(new Image("https://fourtf.com/ppHop.gif", 0.25)) { this->split.left = QIcon(":/images/split/splitleft.png"); this->split.right = QIcon(":/images/split/splitright.png"); diff --git a/src/singletons/Resources.hpp b/src/singletons/Resources.hpp index 7327c4da0..86db4d84a 100644 --- a/src/singletons/Resources.hpp +++ b/src/singletons/Resources.hpp @@ -86,6 +86,8 @@ public: Image *buttonBan; Image *buttonTimeout; + Image *pajaDank; + Image *ppHop; struct JSONCheermoteSet { QString prefix; diff --git a/src/widgets/settingspages/LookPage.cpp b/src/widgets/settingspages/LookPage.cpp index 7769d5b6d..6f556f025 100644 --- a/src/widgets/settingspages/LookPage.cpp +++ b/src/widgets/settingspages/LookPage.cpp @@ -62,7 +62,7 @@ void LookPage::initializeUi() auto channelView = layout.emplace(); auto channel = this->createPreviewChannel(); channelView->setChannel(channel); - channelView->setScaleIndependantHeight(64); + channelView->setScaleIndependantHeight(74); } void LookPage::addInterfaceTab(LayoutCreator layout) @@ -276,24 +276,32 @@ ChannelPtr LookPage::createPreviewChannel() { auto message = MessagePtr(new Message()); + message->addElement(new TimestampElement(QTime(8, 13, 42))); message->addElement(new ImageElement(getApp()->resources->badgeModerator, MessageElement::BadgeChannelAuthority)); message->addElement(new ImageElement(getApp()->resources->badgeSubscriber, MessageElement::BadgeSubscription)); - message->addElement(new TimestampElement(QTime(8, 13, 42))); message->addElement(new TextElement("username1:", MessageElement::Username, QColor("#0094FF"), FontStyle::ChatMediumBold)); - message->addElement(new TextElement("This is a preview message :)", MessageElement::Text)); + message->addElement(new TextElement("This is a preview message", MessageElement::Text)); + message->addElement(new EmoteElement(EmoteData(getApp()->resources->pajaDank), + MessageElement::Flags::AlwaysShow)); + // message->addElement(new) channel->addMessage(message); } { auto message = MessagePtr(new Message()); + message->addElement(new TimestampElement(QTime(8, 15, 21))); message->addElement(new ImageElement(getApp()->resources->badgePremium, MessageElement::BadgeChannelAuthority)); - message->addElement(new TimestampElement(QTime(8, 15, 21))); message->addElement(new TextElement("username2:", MessageElement::Username, QColor("#FF6A00"), FontStyle::ChatMediumBold)); - message->addElement(new TextElement("This is another one :)", MessageElement::Text)); + message->addElement(new TextElement("This is another one", MessageElement::Text)); + message->addElement( + new EmoteElement(EmoteData(getApp()->resources->ppHop), MessageElement::BttvEmote)); + message->addElement( + (new TextElement("www.fourtf.com", MessageElement::Text, MessageColor::Link)) + ->setLink(Link(Link::Url, "https://www.fourtf.com"))); channel->addMessage(message); } From 5d2be3ae2a93548ace75ef09520c0b6f82a18f58 Mon Sep 17 00:00:00 2001 From: apa420 Date: Fri, 13 Jul 2018 02:06:35 +0200 Subject: [PATCH 20/73] fixed issue with timeoutbuttons #613 --- src/controllers/moderationactions/ModerationAction.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/controllers/moderationactions/ModerationAction.cpp b/src/controllers/moderationactions/ModerationAction.cpp index a7312147f..eb477602f 100644 --- a/src/controllers/moderationactions/ModerationAction.cpp +++ b/src/controllers/moderationactions/ModerationAction.cpp @@ -32,8 +32,8 @@ ModerationAction::ModerationAction(const QString &action) if (timeoutMatch.hasMatch()) { // if (multipleTimeouts > 1) { - QString line1; - QString line2; + // QString line1; + // QString line2; int amount = timeoutMatch.captured(1).toInt(); @@ -51,8 +51,8 @@ ModerationAction::ModerationAction(const QString &action) this->line2_ = "d"; } - this->line1_ = line1; - this->line2_ = line2; + // line1 = this->line1_; + // line2 = this->line2_; // } else { // this->_moderationActions.emplace_back(app->resources->buttonTimeout, str); // } From 588aad751b36178c3f5297b1badeac57562532d7 Mon Sep 17 00:00:00 2001 From: LajamerrMittesdine Date: Fri, 13 Jul 2018 05:56:46 -0400 Subject: [PATCH 21/73] Fix QMenus from going out of screen boundaries --- src/widgets/helper/ChannelView.cpp | 3 +-- src/widgets/splits/SplitHeader.cpp | 8 ++------ 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/src/widgets/helper/ChannelView.cpp b/src/widgets/helper/ChannelView.cpp index 926845d2c..e3b8fb847 100644 --- a/src/widgets/helper/ChannelView.cpp +++ b/src/widgets/helper/ChannelView.cpp @@ -1093,8 +1093,7 @@ void ChannelView::addContextMenuItems(const MessageLayoutElement *hoveredElement // // insert into input // }); - menu->move(QCursor::pos()); - menu->show(); + menu->popup(QCursor::pos()); menu->raise(); return; diff --git a/src/widgets/splits/SplitHeader.cpp b/src/widgets/splits/SplitHeader.cpp index 23bb78fa4..e37938302 100644 --- a/src/widgets/splits/SplitHeader.cpp +++ b/src/widgets/splits/SplitHeader.cpp @@ -56,9 +56,7 @@ SplitHeader::SplitHeader(Split *_split) QTimer::singleShot(80, this, [&, this] { ChannelPtr _channel = this->split_->getChannel(); if (_channel->hasModRights()) { - this->modeMenu_.move( - this->modeButton_->mapToGlobal(QPoint(0, this->modeButton_->height()))); - this->modeMenu_.show(); + this->modeMenu_.popup(QCursor::pos()); } }); }); @@ -83,9 +81,7 @@ SplitHeader::SplitHeader(Split *_split) this->addDropdownItems(dropdown.getElement()); QObject::connect(dropdown.getElement(), &RippleEffectButton::leftMousePress, this, [this] { QTimer::singleShot(80, [&, this] { - this->dropdownMenu_.move( - this->dropdownButton_->mapToGlobal(QPoint(0, this->dropdownButton_->height()))); - this->dropdownMenu_.show(); + this->dropdownMenu_.popup(QCursor::pos()); }); }); } From ba99d6213993ae166642a18231c8ed4a54cf5a52 Mon Sep 17 00:00:00 2001 From: apa420 <17131426+apa420@users.noreply.github.com> Date: Fri, 13 Jul 2018 22:23:03 +0200 Subject: [PATCH 22/73] Fixed /ignore and /unignore as well as give description for the command (#611) * Fixed /ignore and /unignore as well as give description for the command --- src/controllers/commands/CommandController.cpp | 13 ++++++++++--- src/providers/twitch/TwitchAccount.cpp | 5 ++--- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/controllers/commands/CommandController.cpp b/src/controllers/commands/CommandController.cpp index 595643ccd..9d43a0d96 100644 --- a/src/controllers/commands/CommandController.cpp +++ b/src/controllers/commands/CommandController.cpp @@ -6,7 +6,6 @@ #include "controllers/commands/Command.hpp" #include "controllers/commands/CommandModel.hpp" #include "messages/MessageBuilder.hpp" -#include "providers/twitch/TwitchAccount.hpp" #include "providers/twitch/TwitchApi.hpp" #include "providers/twitch/TwitchChannel.hpp" #include "providers/twitch/TwitchServer.hpp" @@ -172,7 +171,11 @@ QString CommandController::execCommand(const QString &text, ChannelPtr channel, channel->addMessage(Message::createSystemMessage(messageText)); return ""; - } else if (commandName == "/ignore" && words.size() >= 2) { + } else if (commandName == "/ignore") { + if (words.size() < 2) { + channel->addMessage(Message::createSystemMessage("Usage: /ignore [user]")); + return ""; + } auto app = getApp(); auto user = app->accounts->twitch.getCurrent(); @@ -189,7 +192,11 @@ QString CommandController::execCommand(const QString &text, ChannelPtr channel, }); return ""; - } else if (commandName == "/unignore" && words.size() >= 2) { + } else if (commandName == "/unignore") { + if (words.size() < 2) { + channel->addMessage(Message::createSystemMessage("Usage: /unignore [user]")); + return ""; + } auto app = getApp(); auto user = app->accounts->twitch.getCurrent(); diff --git a/src/providers/twitch/TwitchAccount.cpp b/src/providers/twitch/TwitchAccount.cpp index 785f159e5..695bc369f 100644 --- a/src/providers/twitch/TwitchAccount.cpp +++ b/src/providers/twitch/TwitchAccount.cpp @@ -130,7 +130,7 @@ void TwitchAccount::ignore(const QString &targetName, this->ignoreByID(targetUserId, targetName, onFinished); // }; - PartialTwitchUser::byName(this->userName_).getId(onIdFetched); + PartialTwitchUser::byName(targetName).getId(onIdFetched); } void TwitchAccount::ignoreByID(const QString &targetUserID, const QString &targetName, @@ -138,7 +138,6 @@ void TwitchAccount::ignoreByID(const QString &targetUserID, const QString &targe { QString url("https://api.twitch.tv/kraken/users/" + this->getUserId() + "/blocks/" + targetUserID); - NetworkRequest req(url, NetworkRequestType::Put); req.setCaller(QThread::currentThread()); req.makeAuthorizedV5(this->getOAuthClient(), this->getOAuthToken()); @@ -197,7 +196,7 @@ void TwitchAccount::unignore(const QString &targetName, this->unignoreByID(targetUserId, targetName, onFinished); // }; - PartialTwitchUser::byName(this->userName_).getId(onIdFetched); + PartialTwitchUser::byName(targetName).getId(onIdFetched); } void TwitchAccount::unignoreByID( From a720c76dc0b37d03af72bad67b6108bb218ec08b Mon Sep 17 00:00:00 2001 From: apa420 <17131426+apa420@users.noreply.github.com> Date: Sat, 14 Jul 2018 08:53:28 +0200 Subject: [PATCH 23/73] changed an #include (#622) --- src/providers/twitch/TwitchApi.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/providers/twitch/TwitchApi.cpp b/src/providers/twitch/TwitchApi.cpp index a1363a813..82d9dc3f3 100644 --- a/src/providers/twitch/TwitchApi.cpp +++ b/src/providers/twitch/TwitchApi.cpp @@ -1,4 +1,4 @@ -#include "providers/twitch/twitchapi.hpp" +#include "providers/twitch/TwitchApi.hpp" #include "common/NetworkRequest.hpp" #include "debug/Log.hpp" From 111853c574beb29e29d664c55714ec72ce0c3e6a Mon Sep 17 00:00:00 2001 From: fourtf Date: Sat, 14 Jul 2018 14:24:18 +0200 Subject: [PATCH 24/73] improving TwitchChannel --- .clang-format | 2 +- chatterino.pro | 958 +++++++++--------- src/common/Channel.cpp | 5 + src/common/Channel.hpp | 3 + src/common/Common.hpp | 12 +- src/common/NetworkRequest.cpp | 4 +- src/common/NetworkResult.cpp | 2 +- src/common/uniqueaccess.cpp | 1 + src/common/uniqueaccess.hpp | 81 ++ src/providers/twitch/IrcMessageHandler.cpp | 9 +- src/providers/twitch/PubsubClient.cpp | 2 +- src/providers/twitch/TwitchChannel.cpp | 273 +++-- src/providers/twitch/TwitchChannel.hpp | 56 +- src/providers/twitch/TwitchMessageBuilder.cpp | 4 +- src/providers/twitch/TwitchServer.cpp | 10 +- src/widgets/splits/SplitHeader.cpp | 4 +- 16 files changed, 752 insertions(+), 674 deletions(-) create mode 100644 src/common/uniqueaccess.cpp create mode 100644 src/common/uniqueaccess.hpp diff --git a/.clang-format b/.clang-format index 6e663cb67..e141fff60 100644 --- a/.clang-format +++ b/.clang-format @@ -15,7 +15,7 @@ BreakBeforeBraces: Custom AccessModifierOffset: -4 ConstructorInitializerAllOnOneLineOrOnePerLine: false AllowShortFunctionsOnASingleLine: false -AllowShortIfStatementsOnASingleLine: false +AllowShortIfStatementsOnASingleLine: true AllowShortLoopsOnASingleLine: false DerivePointerBinding: false BraceWrapping: { diff --git a/chatterino.pro b/chatterino.pro index a7450cc82..f180f0715 100644 --- a/chatterino.pro +++ b/chatterino.pro @@ -1,478 +1,480 @@ -#------------------------------------------------- -# -# Project created by QtCreator 2016-12-28T18:23:35 -# -#------------------------------------------------- - -message(----) - -QT += widgets core gui network multimedia svg -CONFIG += communi -COMMUNI += core model util -CONFIG += c++14 -INCLUDEPATH += src/ -TARGET = chatterino -TEMPLATE = app -DEFINES += QT_DEPRECATED_WARNINGS -PRECOMPILED_HEADER = src/PrecompiledHeader.hpp -CONFIG += precompile_header - -useBreakpad { - LIBS += -L$$PWD/lib/qBreakpad/handler/build - include(lib/qBreakpad/qBreakpad.pri) - DEFINES += C_USE_BREAKPAD -} - -# https://bugreports.qt.io/browse/QTBUG-27018 -equals(QMAKE_CXX, "clang++")|equals(QMAKE_CXX, "g++") { - TARGET = bin/chatterino -} - -# Icons -macx:ICON = resources/images/chatterino2.icns -win32:RC_FILE = resources/windows.rc - - -macx { - LIBS += -L/usr/local/lib -} - -# Submodules -include(dependencies/rapidjson.pri) -include(dependencies/settings.pri) -include(dependencies/signals.pri) -include(dependencies/humanize.pri) -include(dependencies/fmt.pri) -DEFINES += IRC_NAMESPACE=Communi -include(dependencies/libcommuni.pri) -include(dependencies/websocketpp.pri) -include(dependencies/openssl.pri) -include(dependencies/boost.pri) - -# Optional feature: QtWebEngine -#exists ($(QTDIR)/include/QtWebEngine/QtWebEngine) { -# message(Using QWebEngine) -# QT += webenginewidgets -# DEFINES += "USEWEBENGINE" -#} - -linux { - LIBS += -lrt -} - -win32 { - LIBS += -luser32 -} - -# OSX include directory -macx { - INCLUDEPATH += /usr/local/include -} - -# Optional dependency on Windows SDK 7 -!contains(QMAKE_TARGET.arch, x86_64) { - win32:exists(C:\Program Files\Microsoft SDKs\Windows\v7.1\Include\Windows.h) { - LIBS += -L"C:\Program Files\Microsoft SDKs\Windows\v7.1\Lib" \ - -ldwmapi - - DEFINES += "USEWINSDK" - message(Using Windows SDK 7) - } -} - -# Optional dependency on Windows SDK 10 -contains(QMAKE_TARGET.arch, x86_64) { - WIN_SDK_VERSION = $$(WindowsSDKVersion) - !isEmpty(WIN_SDK_VERSION) { - !equals(WIN_SDK_VERSION, "\\") { - DEFINES += "USEWINSDK" - message(Using Windows SDK 10) - } - } -} - -werr { - QMAKE_CXXFLAGS += -Werror - - message("Enabling error on warning") -} - -SOURCES += \ - src/Application.cpp \ - src/common/Channel.cpp \ - src/common/CompletionModel.cpp \ - src/common/Emotemap.cpp \ - src/common/NetworkData.cpp \ - src/common/NetworkManager.cpp \ - src/common/NetworkRequest.cpp \ - src/common/NetworkResult.cpp \ - src/common/NetworkTimer.cpp \ - src/controllers/accounts/Account.cpp \ - src/controllers/accounts/AccountController.cpp \ - src/controllers/accounts/AccountModel.cpp \ - src/controllers/commands/Command.cpp \ - src/controllers/commands/CommandController.cpp \ - src/controllers/commands/CommandModel.cpp \ - src/controllers/highlights/HighlightController.cpp \ - src/controllers/highlights/HighlightModel.cpp \ - src/controllers/highlights/HighlightBlacklistModel.cpp \ - src/controllers/highlights/UserHighlightModel.cpp \ - src/controllers/ignores/IgnoreController.cpp \ - src/controllers/ignores/IgnoreModel.cpp \ - src/controllers/taggedusers/TaggedUser.cpp \ - src/controllers/taggedusers/TaggedUsersController.cpp \ - src/controllers/taggedusers/TaggedUsersModel.cpp \ - src/main.cpp \ - src/messages/Image.cpp \ - src/messages/layouts/MessageLayout.cpp \ - src/messages/layouts/MessageLayoutContainer.cpp \ - src/messages/layouts/MessageLayoutElement.cpp \ - src/messages/Link.cpp \ - src/messages/Message.cpp \ - src/messages/MessageBuilder.cpp \ - src/messages/MessageColor.cpp \ - src/messages/MessageElement.cpp \ - src/providers/bttv/BttvEmotes.cpp \ - src/providers/emoji/Emojis.cpp \ - src/providers/ffz/FfzEmotes.cpp \ - src/providers/irc/AbstractIrcServer.cpp \ - src/providers/irc/IrcAccount.cpp \ - src/providers/irc/IrcChannel2.cpp \ - src/providers/irc/IrcConnection2.cpp \ - src/providers/irc/IrcServer.cpp \ - src/providers/twitch/IrcMessageHandler.cpp \ - src/providers/twitch/PartialTwitchUser.cpp \ - src/providers/twitch/PubsubActions.cpp \ - src/providers/twitch/PubsubHelpers.cpp \ - src/providers/twitch/TwitchAccount.cpp \ - src/providers/twitch/TwitchAccountManager.cpp \ - src/providers/twitch/TwitchChannel.cpp \ - src/providers/twitch/TwitchEmotes.cpp \ - src/providers/twitch/TwitchHelpers.cpp \ - src/providers/twitch/TwitchMessageBuilder.cpp \ - src/providers/twitch/TwitchServer.cpp \ - src/providers/twitch/TwitchUser.cpp \ - src/common/ChatterinoSetting.cpp \ - src/singletons/helper/GifTimer.cpp \ - src/singletons/helper/LoggingChannel.cpp \ - src/controllers/moderationactions/ModerationAction.cpp \ - src/singletons/WindowManager.cpp \ - src/util/DebugCount.cpp \ - src/util/RapidjsonHelpers.cpp \ - src/util/StreamLink.cpp \ - src/util/WindowsHelper.cpp \ - src/widgets/AccountSwitchPopupWidget.cpp \ - src/widgets/AccountSwitchWidget.cpp \ - src/widgets/AttachedWindow.cpp \ - src/widgets/BaseWidget.cpp \ - src/widgets/BaseWindow.cpp \ - src/widgets/dialogs/EmotePopup.cpp \ - src/widgets/dialogs/LastRunCrashDialog.cpp \ - src/widgets/dialogs/LoginDialog.cpp \ - src/widgets/dialogs/LogsPopup.cpp \ - src/widgets/dialogs/NotificationPopup.cpp \ - src/widgets/dialogs/QualityPopup.cpp \ - src/widgets/dialogs/SelectChannelDialog.cpp \ - src/widgets/dialogs/SettingsDialog.cpp \ - src/widgets/dialogs/TextInputDialog.cpp \ - src/widgets/dialogs/UserInfoPopup.cpp \ - src/widgets/dialogs/WelcomeDialog.cpp \ - src/widgets/helper/ChannelView.cpp \ - src/widgets/helper/ComboBoxItemDelegate.cpp \ - src/widgets/helper/DebugPopup.cpp \ - src/widgets/helper/EditableModelView.cpp \ - src/widgets/helper/NotebookButton.cpp \ - src/widgets/helper/NotebookTab.cpp \ - src/widgets/helper/ResizingTextEdit.cpp \ - src/widgets/helper/RippleEffectButton.cpp \ - src/widgets/helper/RippleEffectLabel.cpp \ - src/widgets/helper/ScrollbarHighlight.cpp \ - src/widgets/helper/SearchPopup.cpp \ - src/widgets/helper/SettingsDialogTab.cpp \ - src/widgets/helper/SignalLabel.cpp \ - src/widgets/helper/TitlebarButton.cpp \ - src/widgets/Label.cpp \ - src/widgets/Notebook.cpp \ - src/widgets/Scrollbar.cpp \ - src/widgets/settingspages/AboutPage.cpp \ - src/widgets/settingspages/AccountsPage.cpp \ - src/widgets/settingspages/BrowserExtensionPage.cpp \ - src/widgets/settingspages/CommandPage.cpp \ - src/widgets/settingspages/EmotesPage.cpp \ - src/widgets/settingspages/ExternalToolsPage.cpp \ - src/widgets/settingspages/HighlightingPage.cpp \ - src/widgets/settingspages/KeyboardSettingsPage.cpp \ - src/widgets/settingspages/LogsPage.cpp \ - src/widgets/settingspages/ModerationPage.cpp \ - src/widgets/settingspages/SettingsPage.cpp \ - src/widgets/settingspages/SpecialChannelsPage.cpp \ - src/widgets/splits/Split.cpp \ - src/widgets/splits/SplitContainer.cpp \ - src/widgets/splits/SplitHeader.cpp \ - src/widgets/splits/SplitInput.cpp \ - src/widgets/splits/SplitOverlay.cpp \ - src/widgets/StreamView.cpp \ - src/widgets/TooltipWidget.cpp \ - src/widgets/Window.cpp \ - src/common/LinkParser.cpp \ - src/controllers/moderationactions/ModerationActions.cpp \ - src/singletons/NativeMessaging.cpp \ - src/singletons/Emotes.cpp \ - src/singletons/Fonts.cpp \ - src/singletons/Logging.cpp \ - src/singletons/Paths.cpp \ - src/singletons/Resources.cpp \ - src/singletons/Settings.cpp \ - src/singletons/Updates.cpp \ - src/singletons/Theme.cpp \ - src/controllers/moderationactions/ModerationActionModel.cpp \ - src/widgets/settingspages/LookPage.cpp \ - src/widgets/settingspages/FeelPage.cpp \ - src/util/InitUpdateButton.cpp \ - src/widgets/dialogs/UpdateDialog.cpp \ - src/widgets/settingspages/IgnoresPage.cpp \ - src/providers/twitch/PubsubClient.cpp \ - src/providers/twitch/TwitchApi.cpp - -HEADERS += \ - src/Application.hpp \ - src/common/Channel.hpp \ - src/common/Common.hpp \ - src/common/CompletionModel.hpp \ - src/common/Emotemap.hpp \ - src/common/FlagsEnum.hpp \ - src/common/LockedObject.hpp \ - src/common/MutexValue.hpp \ - src/common/NetworkCommon.hpp \ - src/common/NetworkData.hpp \ - src/common/NetworkManager.hpp \ - src/common/NetworkRequest.hpp \ - src/common/NetworkRequester.hpp \ - src/common/NetworkResult.hpp \ - src/common/NetworkTimer.hpp \ - src/common/NetworkWorker.hpp \ - src/common/NullablePtr.hpp \ - src/common/Property.hpp \ - src/common/ProviderId.hpp \ - src/common/SerializeCustom.hpp \ - src/common/SignalVectorModel.hpp \ - src/common/UrlFetch.hpp \ - src/common/Version.hpp \ - src/controllers/accounts/Account.hpp \ - src/controllers/accounts/AccountController.hpp \ - src/controllers/accounts/AccountModel.hpp \ - src/controllers/commands/Command.hpp \ - src/controllers/commands/CommandController.hpp \ - src/controllers/commands/CommandModel.hpp \ - src/controllers/highlights/HighlightController.hpp \ - src/controllers/highlights/HighlightModel.hpp \ - src/controllers/highlights/HighlightBlacklistModel.hpp \ - src/controllers/highlights/HighlightPhrase.hpp \ - src/controllers/highlights/HighlightBlacklistUser.hpp \ - src/controllers/highlights/UserHighlightModel.hpp \ - src/controllers/ignores/IgnoreController.hpp \ - src/controllers/ignores/IgnoreModel.hpp \ - src/controllers/ignores/IgnorePhrase.hpp \ - src/controllers/taggedusers/TaggedUser.hpp \ - src/controllers/taggedusers/TaggedUsersController.hpp \ - src/controllers/taggedusers/TaggedUsersModel.hpp \ - src/debug/AssertInGuiThread.hpp \ - src/debug/Benchmark.hpp \ - src/debug/Log.hpp \ - src/messages/Image.hpp \ - src/messages/layouts/MessageLayout.hpp \ - src/messages/layouts/MessageLayoutContainer.hpp \ - src/messages/layouts/MessageLayoutElement.hpp \ - src/messages/LimitedQueue.hpp \ - src/messages/LimitedQueueSnapshot.hpp \ - src/messages/Link.hpp \ - src/messages/Message.hpp \ - src/messages/MessageBuilder.hpp \ - src/messages/MessageColor.hpp \ - src/messages/MessageElement.hpp \ - src/messages/MessageParseArgs.hpp \ - src/messages/Selection.hpp \ - src/PrecompiledHeader.hpp \ - src/providers/bttv/BttvEmotes.hpp \ - src/providers/emoji/Emojis.hpp \ - src/providers/ffz/FfzEmotes.hpp \ - src/providers/irc/AbstractIrcServer.hpp \ - src/providers/irc/IrcAccount.hpp \ - src/providers/irc/IrcChannel2.hpp \ - src/providers/irc/IrcConnection2.hpp \ - src/providers/irc/IrcServer.hpp \ - src/providers/twitch/EmoteValue.hpp \ - src/providers/twitch/IrcMessageHandler.hpp \ - src/providers/twitch/PartialTwitchUser.hpp \ - src/providers/twitch/PubsubActions.hpp \ - src/providers/twitch/PubsubHelpers.hpp \ - src/providers/twitch/TwitchAccount.hpp \ - src/providers/twitch/TwitchAccountManager.hpp \ - src/providers/twitch/TwitchChannel.hpp \ - src/providers/twitch/TwitchEmotes.hpp \ - src/providers/twitch/TwitchHelpers.hpp \ - src/providers/twitch/TwitchMessageBuilder.hpp \ - src/providers/twitch/TwitchServer.hpp \ - src/providers/twitch/TwitchUser.hpp \ - src/common/ChatterinoSetting.hpp \ - src/singletons/helper/GifTimer.hpp \ - src/singletons/helper/LoggingChannel.hpp \ - src/controllers/moderationactions/ModerationAction.hpp \ - src/singletons/WindowManager.hpp \ - src/util/Clamp.hpp \ - src/util/CombinePath.hpp \ - src/util/ConcurrentMap.hpp \ - src/util/DebugCount.hpp \ - src/util/DistanceBetweenPoints.hpp \ - src/util/Helpers.hpp \ - src/util/IrcHelpers.hpp \ - src/util/LayoutCreator.hpp \ - src/util/PostToThread.hpp \ - src/util/QStringHash.hpp \ - src/util/RapidjsonHelpers.hpp \ - src/util/RemoveScrollAreaBackground.hpp \ - src/util/SharedPtrElementLess.hpp \ - src/util/StandardItemHelper.hpp \ - src/util/StreamLink.hpp \ - src/util/WindowsHelper.hpp \ - src/widgets/AccountSwitchPopupWidget.hpp \ - src/widgets/AccountSwitchWidget.hpp \ - src/widgets/AttachedWindow.hpp \ - src/widgets/BaseWidget.hpp \ - src/widgets/BaseWindow.hpp \ - src/widgets/dialogs/EmotePopup.hpp \ - src/widgets/dialogs/LastRunCrashDialog.hpp \ - src/widgets/dialogs/LoginDialog.hpp \ - src/widgets/dialogs/LogsPopup.hpp \ - src/widgets/dialogs/NotificationPopup.hpp \ - src/widgets/dialogs/QualityPopup.hpp \ - src/widgets/dialogs/SelectChannelDialog.hpp \ - src/widgets/dialogs/SettingsDialog.hpp \ - src/widgets/dialogs/TextInputDialog.hpp \ - src/widgets/dialogs/UserInfoPopup.hpp \ - src/widgets/dialogs/WelcomeDialog.hpp \ - src/widgets/helper/ChannelView.hpp \ - src/widgets/helper/ComboBoxItemDelegate.hpp \ - src/widgets/helper/DebugPopup.hpp \ - src/widgets/helper/EditableModelView.hpp \ - src/widgets/helper/Line.hpp \ - src/widgets/helper/NotebookButton.hpp \ - src/widgets/helper/NotebookTab.hpp \ - src/widgets/helper/ResizingTextEdit.hpp \ - src/widgets/helper/RippleEffectButton.hpp \ - src/widgets/helper/RippleEffectLabel.hpp \ - src/widgets/helper/ScrollbarHighlight.hpp \ - src/widgets/helper/SearchPopup.hpp \ - src/widgets/helper/SettingsDialogTab.hpp \ - src/widgets/helper/Shortcut.hpp \ - src/widgets/helper/SignalLabel.hpp \ - src/widgets/helper/TitlebarButton.hpp \ - src/widgets/Label.hpp \ - src/widgets/Notebook.hpp \ - src/widgets/Scrollbar.hpp \ - src/widgets/settingspages/AboutPage.hpp \ - src/widgets/settingspages/AccountsPage.hpp \ - src/widgets/settingspages/BrowserExtensionPage.hpp \ - src/widgets/settingspages/CommandPage.hpp \ - src/widgets/settingspages/EmotesPage.hpp \ - src/widgets/settingspages/ExternalToolsPage.hpp \ - src/widgets/settingspages/HighlightingPage.hpp \ - src/widgets/settingspages/KeyboardSettingsPage.hpp \ - src/widgets/settingspages/LogsPage.hpp \ - src/widgets/settingspages/ModerationPage.hpp \ - src/widgets/settingspages/SettingsPage.hpp \ - src/widgets/settingspages/SpecialChannelsPage.hpp \ - src/widgets/splits/Split.hpp \ - src/widgets/splits/SplitContainer.hpp \ - src/widgets/splits/SplitHeader.hpp \ - src/widgets/splits/SplitInput.hpp \ - src/widgets/splits/SplitOverlay.hpp \ - src/widgets/StreamView.hpp \ - src/widgets/TooltipWidget.hpp \ - src/widgets/Window.hpp \ - src/providers/twitch/TwitchCommon.hpp \ - src/util/IsBigEndian.hpp \ - src/common/LinkParser.hpp \ - src/controllers/moderationactions/ModerationActions.hpp \ - src/singletons/Emotes.hpp \ - src/singletons/Fonts.hpp \ - src/singletons/Logging.hpp \ - src/singletons/Paths.hpp \ - src/singletons/Resources.hpp \ - src/singletons/Settings.hpp \ - src/singletons/Updates.hpp \ - src/singletons/NativeMessaging.hpp \ - src/singletons/Theme.hpp \ - src/common/SimpleSignalVector.hpp \ - src/common/SignalVector.hpp \ - src/widgets/dialogs/LogsPopup.hpp \ - src/common/Singleton.hpp \ - src/controllers/moderationactions/ModerationActionModel.hpp \ - src/widgets/settingspages/LookPage.hpp \ - src/widgets/settingspages/FeelPage.hpp \ - src/util/InitUpdateButton.hpp \ - src/widgets/dialogs/UpdateDialog.hpp \ - src/widgets/settingspages/IgnoresPage.hpp \ - src/providers/twitch/PubsubClient.hpp \ - src/providers/twitch/TwitchApi.hpp - -RESOURCES += \ - resources/resources.qrc \ - -DISTFILES += - -FORMS += - -# Define warning flags for Chatterino -win32-msvc* { - QMAKE_CXXFLAGS_WARN_ON = /W4 - # 4714 - function marked as __forceinline not inlined - # 4996 - occurs when the compiler encounters a function or variable that is marked as deprecated. - # These functions may have a different preferred name, may be insecure or have - # a more secure variant, or may be obsolete. - # 4505 - unreferenced local version has been removed - # 4127 - conditional expression is constant - # 4503 - decorated name length exceeded, name was truncated - # 4100 - unreferences formal parameter - # 4305 - possible truncation of data - # 4267 - possible loss of data in return - QMAKE_CXXFLAGS_WARN_ON += /wd4714 - QMAKE_CXXFLAGS_WARN_ON += /wd4996 - QMAKE_CXXFLAGS_WARN_ON += /wd4505 - QMAKE_CXXFLAGS_WARN_ON += /wd4127 - QMAKE_CXXFLAGS_WARN_ON += /wd4503 - QMAKE_CXXFLAGS_WARN_ON += /wd4100 - QMAKE_CXXFLAGS_WARN_ON += /wd4305 - QMAKE_CXXFLAGS_WARN_ON += /wd4267 - -} else { - QMAKE_CXXFLAGS_WARN_ON = -Wall - QMAKE_CXXFLAGS_WARN_ON += -Wno-unused-function - QMAKE_CXXFLAGS_WARN_ON += -Wno-switch - QMAKE_CXXFLAGS_WARN_ON += -Wno-deprecated-declarations - QMAKE_CXXFLAGS_WARN_ON += -Wno-sign-compare - QMAKE_CXXFLAGS_WARN_ON += -Wno-unused-variable - QMAKE_CXXFLAGS_WARN_ON += -Wno-unused-private-field - - # Disabling strict-aliasing warnings for now, although we probably want to re-enable this in the future - QMAKE_CXXFLAGS_WARN_ON += -Wno-strict-aliasing - - QMAKE_CXXFLAGS_WARN_ON += -Werror=return-type - - equals(QMAKE_CXX, "clang++") { - QMAKE_CXXFLAGS_WARN_ON += -Wno-unused-local-typedef - } else { - QMAKE_CXXFLAGS_WARN_ON += -Wno-class-memaccess - } -} - -# do not use windows min/max macros -#win32 { -# DEFINES += NOMINMAX -#} - -#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 - -linux { - QMAKE_LFLAGS += -lrt -} +#------------------------------------------------- +# +# Project created by QtCreator 2016-12-28T18:23:35 +# +#------------------------------------------------- + +message(----) + +QT += widgets core gui network multimedia svg +CONFIG += communi +COMMUNI += core model util +CONFIG += c++14 +INCLUDEPATH += src/ +TARGET = chatterino +TEMPLATE = app +DEFINES += QT_DEPRECATED_WARNINGS +PRECOMPILED_HEADER = src/PrecompiledHeader.hpp +CONFIG += precompile_header + +useBreakpad { + LIBS += -L$$PWD/lib/qBreakpad/handler/build + include(lib/qBreakpad/qBreakpad.pri) + DEFINES += C_USE_BREAKPAD +} + +# https://bugreports.qt.io/browse/QTBUG-27018 +equals(QMAKE_CXX, "clang++")|equals(QMAKE_CXX, "g++") { + TARGET = bin/chatterino +} + +# Icons +macx:ICON = resources/images/chatterino2.icns +win32:RC_FILE = resources/windows.rc + + +macx { + LIBS += -L/usr/local/lib +} + +# Submodules +include(dependencies/rapidjson.pri) +include(dependencies/settings.pri) +include(dependencies/signals.pri) +include(dependencies/humanize.pri) +include(dependencies/fmt.pri) +DEFINES += IRC_NAMESPACE=Communi +include(dependencies/libcommuni.pri) +include(dependencies/websocketpp.pri) +include(dependencies/openssl.pri) +include(dependencies/boost.pri) + +# Optional feature: QtWebEngine +#exists ($(QTDIR)/include/QtWebEngine/QtWebEngine) { +# message(Using QWebEngine) +# QT += webenginewidgets +# DEFINES += "USEWEBENGINE" +#} + +linux { + LIBS += -lrt +} + +win32 { + LIBS += -luser32 +} + +# OSX include directory +macx { + INCLUDEPATH += /usr/local/include +} + +# Optional dependency on Windows SDK 7 +!contains(QMAKE_TARGET.arch, x86_64) { + win32:exists(C:\Program Files\Microsoft SDKs\Windows\v7.1\Include\Windows.h) { + LIBS += -L"C:\Program Files\Microsoft SDKs\Windows\v7.1\Lib" \ + -ldwmapi + + DEFINES += "USEWINSDK" + message(Using Windows SDK 7) + } +} + +# Optional dependency on Windows SDK 10 +contains(QMAKE_TARGET.arch, x86_64) { + WIN_SDK_VERSION = $$(WindowsSDKVersion) + !isEmpty(WIN_SDK_VERSION) { + !equals(WIN_SDK_VERSION, "\\") { + DEFINES += "USEWINSDK" + message(Using Windows SDK 10) + } + } +} + +werr { + QMAKE_CXXFLAGS += -Werror + + message("Enabling error on warning") +} + +SOURCES += \ + src/Application.cpp \ + src/common/Channel.cpp \ + src/common/CompletionModel.cpp \ + src/common/Emotemap.cpp \ + src/common/NetworkData.cpp \ + src/common/NetworkManager.cpp \ + src/common/NetworkRequest.cpp \ + src/common/NetworkResult.cpp \ + src/common/NetworkTimer.cpp \ + src/controllers/accounts/Account.cpp \ + src/controllers/accounts/AccountController.cpp \ + src/controllers/accounts/AccountModel.cpp \ + src/controllers/commands/Command.cpp \ + src/controllers/commands/CommandController.cpp \ + src/controllers/commands/CommandModel.cpp \ + src/controllers/highlights/HighlightController.cpp \ + src/controllers/highlights/HighlightModel.cpp \ + src/controllers/highlights/HighlightBlacklistModel.cpp \ + src/controllers/highlights/UserHighlightModel.cpp \ + src/controllers/ignores/IgnoreController.cpp \ + src/controllers/ignores/IgnoreModel.cpp \ + src/controllers/taggedusers/TaggedUser.cpp \ + src/controllers/taggedusers/TaggedUsersController.cpp \ + src/controllers/taggedusers/TaggedUsersModel.cpp \ + src/main.cpp \ + src/messages/Image.cpp \ + src/messages/layouts/MessageLayout.cpp \ + src/messages/layouts/MessageLayoutContainer.cpp \ + src/messages/layouts/MessageLayoutElement.cpp \ + src/messages/Link.cpp \ + src/messages/Message.cpp \ + src/messages/MessageBuilder.cpp \ + src/messages/MessageColor.cpp \ + src/messages/MessageElement.cpp \ + src/providers/bttv/BttvEmotes.cpp \ + src/providers/emoji/Emojis.cpp \ + src/providers/ffz/FfzEmotes.cpp \ + src/providers/irc/AbstractIrcServer.cpp \ + src/providers/irc/IrcAccount.cpp \ + src/providers/irc/IrcChannel2.cpp \ + src/providers/irc/IrcConnection2.cpp \ + src/providers/irc/IrcServer.cpp \ + src/providers/twitch/IrcMessageHandler.cpp \ + src/providers/twitch/PartialTwitchUser.cpp \ + src/providers/twitch/PubsubActions.cpp \ + src/providers/twitch/PubsubHelpers.cpp \ + src/providers/twitch/TwitchAccount.cpp \ + src/providers/twitch/TwitchAccountManager.cpp \ + src/providers/twitch/TwitchChannel.cpp \ + src/providers/twitch/TwitchEmotes.cpp \ + src/providers/twitch/TwitchHelpers.cpp \ + src/providers/twitch/TwitchMessageBuilder.cpp \ + src/providers/twitch/TwitchServer.cpp \ + src/providers/twitch/TwitchUser.cpp \ + src/common/ChatterinoSetting.cpp \ + src/singletons/helper/GifTimer.cpp \ + src/singletons/helper/LoggingChannel.cpp \ + src/controllers/moderationactions/ModerationAction.cpp \ + src/singletons/WindowManager.cpp \ + src/util/DebugCount.cpp \ + src/util/RapidjsonHelpers.cpp \ + src/util/StreamLink.cpp \ + src/util/WindowsHelper.cpp \ + src/widgets/AccountSwitchPopupWidget.cpp \ + src/widgets/AccountSwitchWidget.cpp \ + src/widgets/AttachedWindow.cpp \ + src/widgets/BaseWidget.cpp \ + src/widgets/BaseWindow.cpp \ + src/widgets/dialogs/EmotePopup.cpp \ + src/widgets/dialogs/LastRunCrashDialog.cpp \ + src/widgets/dialogs/LoginDialog.cpp \ + src/widgets/dialogs/LogsPopup.cpp \ + src/widgets/dialogs/NotificationPopup.cpp \ + src/widgets/dialogs/QualityPopup.cpp \ + src/widgets/dialogs/SelectChannelDialog.cpp \ + src/widgets/dialogs/SettingsDialog.cpp \ + src/widgets/dialogs/TextInputDialog.cpp \ + src/widgets/dialogs/UserInfoPopup.cpp \ + src/widgets/dialogs/WelcomeDialog.cpp \ + src/widgets/helper/ChannelView.cpp \ + src/widgets/helper/ComboBoxItemDelegate.cpp \ + src/widgets/helper/DebugPopup.cpp \ + src/widgets/helper/EditableModelView.cpp \ + src/widgets/helper/NotebookButton.cpp \ + src/widgets/helper/NotebookTab.cpp \ + src/widgets/helper/ResizingTextEdit.cpp \ + src/widgets/helper/RippleEffectButton.cpp \ + src/widgets/helper/RippleEffectLabel.cpp \ + src/widgets/helper/ScrollbarHighlight.cpp \ + src/widgets/helper/SearchPopup.cpp \ + src/widgets/helper/SettingsDialogTab.cpp \ + src/widgets/helper/SignalLabel.cpp \ + src/widgets/helper/TitlebarButton.cpp \ + src/widgets/Label.cpp \ + src/widgets/Notebook.cpp \ + src/widgets/Scrollbar.cpp \ + src/widgets/settingspages/AboutPage.cpp \ + src/widgets/settingspages/AccountsPage.cpp \ + src/widgets/settingspages/BrowserExtensionPage.cpp \ + src/widgets/settingspages/CommandPage.cpp \ + src/widgets/settingspages/EmotesPage.cpp \ + src/widgets/settingspages/ExternalToolsPage.cpp \ + src/widgets/settingspages/HighlightingPage.cpp \ + src/widgets/settingspages/KeyboardSettingsPage.cpp \ + src/widgets/settingspages/LogsPage.cpp \ + src/widgets/settingspages/ModerationPage.cpp \ + src/widgets/settingspages/SettingsPage.cpp \ + src/widgets/settingspages/SpecialChannelsPage.cpp \ + src/widgets/splits/Split.cpp \ + src/widgets/splits/SplitContainer.cpp \ + src/widgets/splits/SplitHeader.cpp \ + src/widgets/splits/SplitInput.cpp \ + src/widgets/splits/SplitOverlay.cpp \ + src/widgets/StreamView.cpp \ + src/widgets/TooltipWidget.cpp \ + src/widgets/Window.cpp \ + src/common/LinkParser.cpp \ + src/controllers/moderationactions/ModerationActions.cpp \ + src/singletons/NativeMessaging.cpp \ + src/singletons/Emotes.cpp \ + src/singletons/Fonts.cpp \ + src/singletons/Logging.cpp \ + src/singletons/Paths.cpp \ + src/singletons/Resources.cpp \ + src/singletons/Settings.cpp \ + src/singletons/Updates.cpp \ + src/singletons/Theme.cpp \ + src/controllers/moderationactions/ModerationActionModel.cpp \ + src/widgets/settingspages/LookPage.cpp \ + src/widgets/settingspages/FeelPage.cpp \ + src/util/InitUpdateButton.cpp \ + src/widgets/dialogs/UpdateDialog.cpp \ + src/widgets/settingspages/IgnoresPage.cpp \ + src/providers/twitch/PubsubClient.cpp \ + src/providers/twitch/TwitchApi.cpp \ + src/common/uniqueaccess.cpp + +HEADERS += \ + src/Application.hpp \ + src/common/Channel.hpp \ + src/common/Common.hpp \ + src/common/CompletionModel.hpp \ + src/common/Emotemap.hpp \ + src/common/FlagsEnum.hpp \ + src/common/LockedObject.hpp \ + src/common/MutexValue.hpp \ + src/common/NetworkCommon.hpp \ + src/common/NetworkData.hpp \ + src/common/NetworkManager.hpp \ + src/common/NetworkRequest.hpp \ + src/common/NetworkRequester.hpp \ + src/common/NetworkResult.hpp \ + src/common/NetworkTimer.hpp \ + src/common/NetworkWorker.hpp \ + src/common/NullablePtr.hpp \ + src/common/Property.hpp \ + src/common/ProviderId.hpp \ + src/common/SerializeCustom.hpp \ + src/common/SignalVectorModel.hpp \ + src/common/UrlFetch.hpp \ + src/common/Version.hpp \ + src/controllers/accounts/Account.hpp \ + src/controllers/accounts/AccountController.hpp \ + src/controllers/accounts/AccountModel.hpp \ + src/controllers/commands/Command.hpp \ + src/controllers/commands/CommandController.hpp \ + src/controllers/commands/CommandModel.hpp \ + src/controllers/highlights/HighlightController.hpp \ + src/controllers/highlights/HighlightModel.hpp \ + src/controllers/highlights/HighlightBlacklistModel.hpp \ + src/controllers/highlights/HighlightPhrase.hpp \ + src/controllers/highlights/HighlightBlacklistUser.hpp \ + src/controllers/highlights/UserHighlightModel.hpp \ + src/controllers/ignores/IgnoreController.hpp \ + src/controllers/ignores/IgnoreModel.hpp \ + src/controllers/ignores/IgnorePhrase.hpp \ + src/controllers/taggedusers/TaggedUser.hpp \ + src/controllers/taggedusers/TaggedUsersController.hpp \ + src/controllers/taggedusers/TaggedUsersModel.hpp \ + src/debug/AssertInGuiThread.hpp \ + src/debug/Benchmark.hpp \ + src/debug/Log.hpp \ + src/messages/Image.hpp \ + src/messages/layouts/MessageLayout.hpp \ + src/messages/layouts/MessageLayoutContainer.hpp \ + src/messages/layouts/MessageLayoutElement.hpp \ + src/messages/LimitedQueue.hpp \ + src/messages/LimitedQueueSnapshot.hpp \ + src/messages/Link.hpp \ + src/messages/Message.hpp \ + src/messages/MessageBuilder.hpp \ + src/messages/MessageColor.hpp \ + src/messages/MessageElement.hpp \ + src/messages/MessageParseArgs.hpp \ + src/messages/Selection.hpp \ + src/PrecompiledHeader.hpp \ + src/providers/bttv/BttvEmotes.hpp \ + src/providers/emoji/Emojis.hpp \ + src/providers/ffz/FfzEmotes.hpp \ + src/providers/irc/AbstractIrcServer.hpp \ + src/providers/irc/IrcAccount.hpp \ + src/providers/irc/IrcChannel2.hpp \ + src/providers/irc/IrcConnection2.hpp \ + src/providers/irc/IrcServer.hpp \ + src/providers/twitch/EmoteValue.hpp \ + src/providers/twitch/IrcMessageHandler.hpp \ + src/providers/twitch/PartialTwitchUser.hpp \ + src/providers/twitch/PubsubActions.hpp \ + src/providers/twitch/PubsubHelpers.hpp \ + src/providers/twitch/TwitchAccount.hpp \ + src/providers/twitch/TwitchAccountManager.hpp \ + src/providers/twitch/TwitchChannel.hpp \ + src/providers/twitch/TwitchEmotes.hpp \ + src/providers/twitch/TwitchHelpers.hpp \ + src/providers/twitch/TwitchMessageBuilder.hpp \ + src/providers/twitch/TwitchServer.hpp \ + src/providers/twitch/TwitchUser.hpp \ + src/common/ChatterinoSetting.hpp \ + src/singletons/helper/GifTimer.hpp \ + src/singletons/helper/LoggingChannel.hpp \ + src/controllers/moderationactions/ModerationAction.hpp \ + src/singletons/WindowManager.hpp \ + src/util/Clamp.hpp \ + src/util/CombinePath.hpp \ + src/util/ConcurrentMap.hpp \ + src/util/DebugCount.hpp \ + src/util/DistanceBetweenPoints.hpp \ + src/util/Helpers.hpp \ + src/util/IrcHelpers.hpp \ + src/util/LayoutCreator.hpp \ + src/util/PostToThread.hpp \ + src/util/QStringHash.hpp \ + src/util/RapidjsonHelpers.hpp \ + src/util/RemoveScrollAreaBackground.hpp \ + src/util/SharedPtrElementLess.hpp \ + src/util/StandardItemHelper.hpp \ + src/util/StreamLink.hpp \ + src/util/WindowsHelper.hpp \ + src/widgets/AccountSwitchPopupWidget.hpp \ + src/widgets/AccountSwitchWidget.hpp \ + src/widgets/AttachedWindow.hpp \ + src/widgets/BaseWidget.hpp \ + src/widgets/BaseWindow.hpp \ + src/widgets/dialogs/EmotePopup.hpp \ + src/widgets/dialogs/LastRunCrashDialog.hpp \ + src/widgets/dialogs/LoginDialog.hpp \ + src/widgets/dialogs/LogsPopup.hpp \ + src/widgets/dialogs/NotificationPopup.hpp \ + src/widgets/dialogs/QualityPopup.hpp \ + src/widgets/dialogs/SelectChannelDialog.hpp \ + src/widgets/dialogs/SettingsDialog.hpp \ + src/widgets/dialogs/TextInputDialog.hpp \ + src/widgets/dialogs/UserInfoPopup.hpp \ + src/widgets/dialogs/WelcomeDialog.hpp \ + src/widgets/helper/ChannelView.hpp \ + src/widgets/helper/ComboBoxItemDelegate.hpp \ + src/widgets/helper/DebugPopup.hpp \ + src/widgets/helper/EditableModelView.hpp \ + src/widgets/helper/Line.hpp \ + src/widgets/helper/NotebookButton.hpp \ + src/widgets/helper/NotebookTab.hpp \ + src/widgets/helper/ResizingTextEdit.hpp \ + src/widgets/helper/RippleEffectButton.hpp \ + src/widgets/helper/RippleEffectLabel.hpp \ + src/widgets/helper/ScrollbarHighlight.hpp \ + src/widgets/helper/SearchPopup.hpp \ + src/widgets/helper/SettingsDialogTab.hpp \ + src/widgets/helper/Shortcut.hpp \ + src/widgets/helper/SignalLabel.hpp \ + src/widgets/helper/TitlebarButton.hpp \ + src/widgets/Label.hpp \ + src/widgets/Notebook.hpp \ + src/widgets/Scrollbar.hpp \ + src/widgets/settingspages/AboutPage.hpp \ + src/widgets/settingspages/AccountsPage.hpp \ + src/widgets/settingspages/BrowserExtensionPage.hpp \ + src/widgets/settingspages/CommandPage.hpp \ + src/widgets/settingspages/EmotesPage.hpp \ + src/widgets/settingspages/ExternalToolsPage.hpp \ + src/widgets/settingspages/HighlightingPage.hpp \ + src/widgets/settingspages/KeyboardSettingsPage.hpp \ + src/widgets/settingspages/LogsPage.hpp \ + src/widgets/settingspages/ModerationPage.hpp \ + src/widgets/settingspages/SettingsPage.hpp \ + src/widgets/settingspages/SpecialChannelsPage.hpp \ + src/widgets/splits/Split.hpp \ + src/widgets/splits/SplitContainer.hpp \ + src/widgets/splits/SplitHeader.hpp \ + src/widgets/splits/SplitInput.hpp \ + src/widgets/splits/SplitOverlay.hpp \ + src/widgets/StreamView.hpp \ + src/widgets/TooltipWidget.hpp \ + src/widgets/Window.hpp \ + src/providers/twitch/TwitchCommon.hpp \ + src/util/IsBigEndian.hpp \ + src/common/LinkParser.hpp \ + src/controllers/moderationactions/ModerationActions.hpp \ + src/singletons/Emotes.hpp \ + src/singletons/Fonts.hpp \ + src/singletons/Logging.hpp \ + src/singletons/Paths.hpp \ + src/singletons/Resources.hpp \ + src/singletons/Settings.hpp \ + src/singletons/Updates.hpp \ + src/singletons/NativeMessaging.hpp \ + src/singletons/Theme.hpp \ + src/common/SimpleSignalVector.hpp \ + src/common/SignalVector.hpp \ + src/widgets/dialogs/LogsPopup.hpp \ + src/common/Singleton.hpp \ + src/controllers/moderationactions/ModerationActionModel.hpp \ + src/widgets/settingspages/LookPage.hpp \ + src/widgets/settingspages/FeelPage.hpp \ + src/util/InitUpdateButton.hpp \ + src/widgets/dialogs/UpdateDialog.hpp \ + src/widgets/settingspages/IgnoresPage.hpp \ + src/providers/twitch/PubsubClient.hpp \ + src/providers/twitch/TwitchApi.hpp \ + src/common/uniqueaccess.hpp + +RESOURCES += \ + resources/resources.qrc \ + +DISTFILES += + +FORMS += + +# Define warning flags for Chatterino +win32-msvc* { + QMAKE_CXXFLAGS_WARN_ON = /W4 + # 4714 - function marked as __forceinline not inlined + # 4996 - occurs when the compiler encounters a function or variable that is marked as deprecated. + # These functions may have a different preferred name, may be insecure or have + # a more secure variant, or may be obsolete. + # 4505 - unreferenced local version has been removed + # 4127 - conditional expression is constant + # 4503 - decorated name length exceeded, name was truncated + # 4100 - unreferences formal parameter + # 4305 - possible truncation of data + # 4267 - possible loss of data in return + QMAKE_CXXFLAGS_WARN_ON += /wd4714 + QMAKE_CXXFLAGS_WARN_ON += /wd4996 + QMAKE_CXXFLAGS_WARN_ON += /wd4505 + QMAKE_CXXFLAGS_WARN_ON += /wd4127 + QMAKE_CXXFLAGS_WARN_ON += /wd4503 + QMAKE_CXXFLAGS_WARN_ON += /wd4100 + QMAKE_CXXFLAGS_WARN_ON += /wd4305 + QMAKE_CXXFLAGS_WARN_ON += /wd4267 + +} else { + QMAKE_CXXFLAGS_WARN_ON = -Wall + QMAKE_CXXFLAGS_WARN_ON += -Wno-unused-function + QMAKE_CXXFLAGS_WARN_ON += -Wno-switch + QMAKE_CXXFLAGS_WARN_ON += -Wno-deprecated-declarations + QMAKE_CXXFLAGS_WARN_ON += -Wno-sign-compare + QMAKE_CXXFLAGS_WARN_ON += -Wno-unused-variable + QMAKE_CXXFLAGS_WARN_ON += -Wno-unused-private-field + + # Disabling strict-aliasing warnings for now, although we probably want to re-enable this in the future + QMAKE_CXXFLAGS_WARN_ON += -Wno-strict-aliasing + + QMAKE_CXXFLAGS_WARN_ON += -Werror=return-type + + equals(QMAKE_CXX, "clang++") { + QMAKE_CXXFLAGS_WARN_ON += -Wno-unused-local-typedef + } else { + QMAKE_CXXFLAGS_WARN_ON += -Wno-class-memaccess + } +} + +# do not use windows min/max macros +#win32 { +# DEFINES += NOMINMAX +#} + +#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 + +linux { + QMAKE_LFLAGS += -lrt +} diff --git a/src/common/Channel.cpp b/src/common/Channel.cpp index 26d9e536e..a0ffc511f 100644 --- a/src/common/Channel.cpp +++ b/src/common/Channel.cpp @@ -215,4 +215,9 @@ void Channel::onConnected() { } +std::weak_ptr Channel::weak_from_this() +{ + return std::weak_ptr(this->shared_from_this()); +} + } // namespace chatterino diff --git a/src/common/Channel.hpp b/src/common/Channel.hpp index 552349419..99172c108 100644 --- a/src/common/Channel.hpp +++ b/src/common/Channel.hpp @@ -65,6 +65,9 @@ public: CompletionModel completionModel; + // pre c++17 polyfill + std::weak_ptr weak_from_this(); + protected: virtual void onConnected(); diff --git a/src/common/Common.hpp b/src/common/Common.hpp index f73ea030b..3a573163d 100644 --- a/src/common/Common.hpp +++ b/src/common/Common.hpp @@ -27,14 +27,14 @@ const Qt::KeyboardModifiers showResizeHandlesModifiers = Qt::ControlModifier; static const char *ANONYMOUS_USERNAME_LABEL ATTR_UNUSED = " - anonymous - "; -#define return_if(x) \ - if ((x)) { \ - return; \ +#define return_if(condition) \ + if ((condition)) { \ + return; \ } -#define return_unless(x) \ - if (!(x)) { \ - return; \ +#define return_unless(condition) \ + if (!(condition)) { \ + return; \ } } // namespace chatterino diff --git a/src/common/NetworkRequest.cpp b/src/common/NetworkRequest.cpp index 0311c09e9..62929fe2a 100644 --- a/src/common/NetworkRequest.cpp +++ b/src/common/NetworkRequest.cpp @@ -212,9 +212,7 @@ void NetworkRequest::doRequest() return; } - QByteArray readBytes = reply->readAll(); - QByteArray bytes; - bytes.setRawData(readBytes.data(), readBytes.size()); + QByteArray bytes = reply->readAll(); data->writeToCache(bytes); NetworkResult result(bytes); diff --git a/src/common/NetworkResult.cpp b/src/common/NetworkResult.cpp index 80eb582ff..6d8b70067 100644 --- a/src/common/NetworkResult.cpp +++ b/src/common/NetworkResult.cpp @@ -25,7 +25,7 @@ QJsonObject NetworkResult::parseJson() const rapidjson::Document NetworkResult::parseRapidJson() const { - rapidjson::Document ret(rapidjson::kNullType); + rapidjson::Document ret(rapidjson::kObjectType); rapidjson::ParseResult result = ret.Parse(this->data_.data(), this->data_.length()); diff --git a/src/common/uniqueaccess.cpp b/src/common/uniqueaccess.cpp new file mode 100644 index 000000000..12e1c8846 --- /dev/null +++ b/src/common/uniqueaccess.cpp @@ -0,0 +1 @@ +#include "uniqueaccess.hpp" diff --git a/src/common/uniqueaccess.hpp b/src/common/uniqueaccess.hpp new file mode 100644 index 000000000..2f2a2be36 --- /dev/null +++ b/src/common/uniqueaccess.hpp @@ -0,0 +1,81 @@ +#pragma once + +#include +#include + +namespace chatterino { + +template +class AccessGuard +{ +public: + AccessGuard(T &element, std::mutex &mutex) + : element_(element) + , mutex_(mutex) + { + this->mutex_.lock(); + } + + ~AccessGuard() + { + this->mutex_.unlock(); + } + + T *operator->() const + { + return &this->element_; + } + + T &operator*() const + { + return this->element_; + } + +private: + T &element_; + std::mutex &mutex_; +}; + +template +class UniqueAccess +{ +public: + template ::value>::type * = 0> + UniqueAccess() + : element_(T()) + { + } + + UniqueAccess(const T &element) + : element_(element) + { + } + + UniqueAccess(T &&element) + : element_(element) + { + } + + UniqueAccess &operator=(const T &element) + { + this->element_ = element; + return *this; + } + + UniqueAccess &operator=(T &&element) + { + this->element_ = element; + return *this; + } + + AccessGuard access() + { + return AccessGuard(this->element_, this->mutex_); + } + +private: + T element_; + std::mutex mutex_; +}; + +} // namespace chatterino diff --git a/src/providers/twitch/IrcMessageHandler.cpp b/src/providers/twitch/IrcMessageHandler.cpp index 904c772c8..de6ab30a9 100644 --- a/src/providers/twitch/IrcMessageHandler.cpp +++ b/src/providers/twitch/IrcMessageHandler.cpp @@ -85,18 +85,17 @@ void IrcMessageHandler::handleRoomStateMessage(Communi::IrcMessage *message) return; } auto chan = app->twitch.server->getChannelOrEmpty(chanName); - TwitchChannel *twitchChannel = dynamic_cast(chan.get()); - if (twitchChannel) { + if (auto *twitchChannel = dynamic_cast(chan.get())) { // room-id decltype(tags.find("xD")) it; if ((it = tags.find("room-id")) != tags.end()) { - auto roomID = it.value().toString(); + auto roomId = it.value().toString(); - twitchChannel->setRoomID(roomID); + twitchChannel->setRoomID(roomId); - app->resources->loadChannelData(roomID); + app->resources->loadChannelData(roomId); } // Room modes diff --git a/src/providers/twitch/PubsubClient.cpp b/src/providers/twitch/PubsubClient.cpp index 726f54915..0ea37481a 100644 --- a/src/providers/twitch/PubsubClient.cpp +++ b/src/providers/twitch/PubsubClient.cpp @@ -513,7 +513,7 @@ void PubSub::listenToChannelModerationActions(const QString &channelID, assert(!channelID.isEmpty()); assert(account != nullptr); QString userID = account->getUserId(); - assert(!userID.isEmpty()); + if (userID.isEmpty()) return; std::string topic(fS("chat_moderator_actions.{}.{}", userID, channelID)); diff --git a/src/providers/twitch/TwitchChannel.cpp b/src/providers/twitch/TwitchChannel.cpp index 4c1285f6f..2fa2a6c8e 100644 --- a/src/providers/twitch/TwitchChannel.cpp +++ b/src/providers/twitch/TwitchChannel.cpp @@ -31,78 +31,35 @@ TwitchChannel::TwitchChannel(const QString &channelName, Communi::IrcConnection { Log("[TwitchChannel:{}] Opened", this->name); - this->startRefreshLiveStatusTimer(60 * 1000); + this->refreshChannelEmotes(); + this->refreshViewerList(); - auto app = getApp(); - this->reloadChannelEmotes(); + this->managedConnect(getApp()->accounts->twitch.currentUserChanged, + [=] { this->setMod(false); }); - this->managedConnect(app->accounts->twitch.currentUserChanged, - [this]() { this->setMod(false); }); + // pubsub + this->userStateChanged.connect([=] { this->refreshPubsub(); }); + this->roomIDChanged.connect([=] { this->refreshPubsub(); }); + this->managedConnect(getApp()->accounts->twitch.currentUserChanged, + [=] { this->refreshPubsub(); }); + this->refreshPubsub(); - auto refreshPubSubState = [=]() { - if (!this->hasModRights()) { - return; - } + // room id loaded -> refresh live status + this->roomIDChanged.connect([this]() { this->refreshLiveStatus(); }); - if (this->roomID.isEmpty()) { - return; - } + // timers + QObject::connect(&this->chattersListTimer_, &QTimer::timeout, + [=] { this->refreshViewerList(); }); + this->chattersListTimer_.start(5 * 60 * 1000); - auto account = app->accounts->twitch.getCurrent(); - if (account && !account->getUserId().isEmpty()) { - app->twitch.pubsub->listenToChannelModerationActions(this->roomID, account); - } - }; - - this->userStateChanged.connect(refreshPubSubState); - this->roomIDchanged.connect(refreshPubSubState); - this->managedConnect(app->accounts->twitch.currentUserChanged, refreshPubSubState); - refreshPubSubState(); - - this->fetchMessages.connect([this] { - this->fetchRecentMessages(); // - }); + QObject::connect(&this->liveStatusTimer_, &QTimer::timeout, [=] { this->refreshLiveStatus(); }); + this->liveStatusTimer_.start(60 * 1000); + // -- this->messageSuffix_.append(' '); this->messageSuffix_.append(QChar(0x206D)); - static QStringList jsonLabels = {"moderators", "staff", "admins", "global_mods", "viewers"}; - auto refreshChatters = [=](QJsonObject obj) { - QJsonObject chattersObj = obj.value("chatters").toObject(); - for (int i = 0; i < jsonLabels.size(); i++) { - foreach (const QJsonValue &v, chattersObj.value(jsonLabels.at(i)).toArray()) { - this->completionModel.addUser(v.toString()); - } - } - }; - - auto doRefreshChatters = [=]() { - const auto streamStatus = this->getStreamStatus(); - - if (app->settings->onlyFetchChattersForSmallerStreamers) { - if (streamStatus.live && streamStatus.viewerCount > app->settings->smallStreamerLimit) { - return; - } - } - - NetworkRequest request("https://tmi.twitch.tv/group/user/" + this->name + "/chatters"); - - request.setCaller(QThread::currentThread()); - request.onSuccess([refreshChatters](auto result) { - refreshChatters(result.parseJson()); // - - return true; - }); - - request.execute(); - }; - - doRefreshChatters(); - - this->chattersListTimer = new QTimer; - QObject::connect(this->chattersListTimer, &QTimer::timeout, doRefreshChatters); - this->chattersListTimer->start(5 * 60 * 1000); - + // debugging #if 0 for (int i = 0; i < 1000; i++) { this->addMessage(Message::createSystemMessage("asdf")); @@ -110,15 +67,6 @@ TwitchChannel::TwitchChannel(const QString &channelName, Communi::IrcConnection #endif } -TwitchChannel::~TwitchChannel() -{ - this->liveStatusTimer->stop(); - this->liveStatusTimer->deleteLater(); - - this->chattersListTimer->stop(); - this->chattersListTimer->deleteLater(); -} - bool TwitchChannel::isEmpty() const { return this->name.isEmpty(); @@ -129,14 +77,7 @@ bool TwitchChannel::canSendMessage() const return !this->isEmpty(); } -void TwitchChannel::setRoomID(const QString &_roomID) -{ - this->roomID = _roomID; - this->roomIDchanged.invoke(); - this->fetchMessages.invoke(); -} - -void TwitchChannel::reloadChannelEmotes() +void TwitchChannel::refreshChannelEmotes() { auto app = getApp(); @@ -249,16 +190,16 @@ void TwitchChannel::addJoinedUser(const QString &user) void TwitchChannel::addPartedUser(const QString &user) { - auto *app = getApp(); + auto app = getApp(); if (user == app->accounts->twitch.getCurrent()->getUserName() || - !app->settings->showJoins.getValue()) { + !getSettings()->showJoins.getValue()) { return; } std::lock_guard guard(this->partedUserMutex_); - partedUsers_ << user; + this->partedUsers_ << user; if (!this->partedUsersMergeQueued_) { this->partedUsersMergeQueued_ = true; @@ -277,6 +218,18 @@ void TwitchChannel::addPartedUser(const QString &user) } } +QString TwitchChannel::getRoomID() const +{ + return this->roomID_.get(); +} + +void TwitchChannel::setRoomID(const QString &id) +{ + this->roomID_.set(id); + this->roomIDChanged.invoke(); + this->loadRecentMessages(); +} + TwitchChannel::RoomModes TwitchChannel::getRoomModes() { std::lock_guard lock(this->roomModeMutex_); @@ -318,13 +271,15 @@ void TwitchChannel::setLive(bool newLiveStatus) } if (gotNewLiveStatus) { - this->updateLiveInfo.invoke(); + this->liveStatusChanged.invoke(); } } void TwitchChannel::refreshLiveStatus() { - if (this->roomID.isEmpty()) { + auto roomID = this->getRoomID(); + + if (roomID.isEmpty()) { Log("[TwitchChannel:{}] Refreshing live status (Missing ID)", this->name); this->setLive(false); return; @@ -332,13 +287,11 @@ void TwitchChannel::refreshLiveStatus() Log("[TwitchChannel:{}] Refreshing live status", this->name); - QString url("https://api.twitch.tv/kraken/streams/" + this->roomID); + QString url("https://api.twitch.tv/kraken/streams/" + roomID); - std::weak_ptr weak = this->shared_from_this(); + auto request = makeGetStreamRequest(roomID, QThread::currentThread()); - auto request = makeGetStreamRequest(this->roomID, QThread::currentThread()); - - request.onSuccess([weak](auto result) { + request.onSuccess([weak = this->weak_from_this()](auto result) { auto d = result.parseRapidJson(); ChannelPtr shared = weak.lock(); @@ -384,6 +337,7 @@ void TwitchChannel::refreshLiveStatus() { std::lock_guard lock(channel->streamStatusMutex_); + StreamStatus status; channel->streamStatus_.live = true; channel->streamStatus_.viewerCount = stream["viewers"].GetUint(); channel->streamStatus_.game = stream["game"].GetString(); @@ -413,7 +367,7 @@ void TwitchChannel::refreshLiveStatus() } // Signal all listeners that the stream status has been updated - channel->updateLiveInfo.invoke(); + channel->liveStatusChanged.invoke(); return true; }); @@ -421,70 +375,109 @@ void TwitchChannel::refreshLiveStatus() request.execute(); } -void TwitchChannel::startRefreshLiveStatusTimer(int intervalMS) +void TwitchChannel::initializeLiveStatusTimer(int intervalMS) { - this->liveStatusTimer = new QTimer; - QObject::connect(this->liveStatusTimer, &QTimer::timeout, [this]() { - this->refreshLiveStatus(); // - }); - - // When the Room ID of a twitch channel has been set, refresh the live status an extra time - this->roomIDchanged.connect([this]() { - this->refreshLiveStatus(); // - }); - - this->liveStatusTimer->start(intervalMS); } -void TwitchChannel::fetchRecentMessages() +void TwitchChannel::loadRecentMessages() { static QString genericURL = "https://tmi.twitch.tv/api/rooms/%1/recent_messages?client_id=" + getDefaultClientID(); - NetworkRequest request(genericURL.arg(this->roomID)); + NetworkRequest request(genericURL.arg(this->getRoomID())); request.makeAuthorizedV5(getDefaultClientID()); request.setCaller(QThread::currentThread()); - std::weak_ptr weak = this->shared_from_this(); - - request.onSuccess([weak](auto result) { - auto obj = result.parseJson(); + request.onSuccess([this, weak = this->weak_from_this()](auto result) { + // channel still exists? ChannelPtr shared = weak.lock(); + if (!shared) return false; - if (!shared) { - return false; - } - - auto channel = dynamic_cast(shared.get()); - assert(channel != nullptr); - - static auto readConnection = channel->readConnection_; - - QJsonArray msgArray = obj.value("messages").toArray(); - if (msgArray.empty()) { - return false; - } - - std::vector messages; - - for (const QJsonValueRef _msg : msgArray) { - QByteArray content = _msg.toString().toUtf8(); - auto msg = Communi::IrcMessage::fromData(content, readConnection); - auto privMsg = static_cast(msg); - - MessageParseArgs args; - TwitchMessageBuilder builder(channel, privMsg, args); - if (!builder.isIgnored()) { - messages.push_back(builder.build()); - } - } - - channel->addMessagesAtStart(messages); - - return true; + // parse json + return this->parseRecentMessages(result.parseJson()); }); request.execute(); } +bool TwitchChannel::parseRecentMessages(const QJsonObject &jsonRoot) +{ + QJsonArray jsonMessages = jsonRoot.value("messages").toArray(); + if (jsonMessages.empty()) { + return false; + } + + std::vector messages; + + for (const auto jsonMessage : jsonMessages) { + auto content = jsonMessage.toString().toUtf8(); + auto message = Communi::IrcMessage::fromData(content, this->readConnection_); + auto privMsg = dynamic_cast(message); + assert(privMsg); + + MessageParseArgs args; + TwitchMessageBuilder builder(this, privMsg, args); + if (!builder.isIgnored()) { + messages.push_back(builder.build()); + } + } + + this->addMessagesAtStart(messages); + + return true; +} + +void TwitchChannel::refreshPubsub() +{ + // listen to moderation actions + if (!this->hasModRights()) return; + auto roomId = this->getRoomID(); + if (roomId.isEmpty()) return; + + auto account = getApp()->accounts->twitch.getCurrent(); + getApp()->twitch2->pubsub->listenToChannelModerationActions(roomId, account); +} + +void TwitchChannel::refreshViewerList() +{ + // setting? + const auto streamStatus = this->getStreamStatus(); + + if (getSettings()->onlyFetchChattersForSmallerStreamers) { + if (streamStatus.live && streamStatus.viewerCount > getSettings()->smallStreamerLimit) { + return; + } + } + + // get viewer list + NetworkRequest request("https://tmi.twitch.tv/group/user/" + this->name + "/chatters"); + + request.setCaller(QThread::currentThread()); + request.onSuccess([this, weak = this->weak_from_this()](auto result) { + // channel still exists? + auto shared = weak.lock(); + if (!shared) return false; + + return this->parseViewerList(result.parseJson()); + }); + + request.execute(); +} + +bool TwitchChannel::parseViewerList(const QJsonObject &jsonRoot) +{ + static QStringList categories = {"moderators", "staff", "admins", "global_mods", "viewers"}; + + // parse json + QJsonObject jsonCategories = jsonRoot.value("chatters").toObject(); + + for (const auto &category : categories) { + for (const auto jsonCategory : jsonCategories.value(category).toArray()) { + this->completionModel.addUser(jsonCategory.toString()); + } + } + + return true; +} + } // namespace chatterino diff --git a/src/providers/twitch/TwitchChannel.hpp b/src/providers/twitch/TwitchChannel.hpp index 7b4e8a638..950c9f9d1 100644 --- a/src/providers/twitch/TwitchChannel.hpp +++ b/src/providers/twitch/TwitchChannel.hpp @@ -18,9 +18,6 @@ class TwitchServer; class TwitchChannel final : public Channel, pajlada::Signals::SignalHolder { - QTimer *liveStatusTimer; - QTimer *chattersListTimer; - public: struct StreamStatus { bool live = false; @@ -46,22 +43,32 @@ public: QString broadcasterLang; }; - ~TwitchChannel() final; - - void reloadChannelEmotes(); - bool isEmpty() const override; bool canSendMessage() const override; void sendMessage(const QString &message) override; + bool isLive() const; virtual bool isMod() const override; void setMod(bool value); virtual bool isBroadcaster() const override; + QString getRoomID() const; + void setRoomID(const QString &id); + RoomModes getRoomModes(); + void setRoomModes(const RoomModes &roomModes_); + StreamStatus getStreamStatus() const; + void addRecentChatter(const std::shared_ptr &message) final; void addJoinedUser(const QString &user); void addPartedUser(const QString &user); + struct NameOptions { + QString displayName; + QString localizedName; + }; + + void refreshChannelEmotes(); + const std::shared_ptr bttvChannelEmotes; const std::shared_ptr ffzChannelEmotes; @@ -69,35 +76,24 @@ public: const QString channelURL; const QString popoutPlayerURL; - void setRoomID(const QString &_roomID); - pajlada::Signals::NoArgSignal roomIDchanged; - pajlada::Signals::NoArgSignal updateLiveInfo; + pajlada::Signals::NoArgSignal roomIDChanged; + pajlada::Signals::NoArgSignal liveStatusChanged; - pajlada::Signals::NoArgBoltSignal fetchMessages; pajlada::Signals::NoArgSignal userStateChanged; pajlada::Signals::NoArgSignal roomModesChanged; - QString roomID; - - RoomModes getRoomModes(); - void setRoomModes(const RoomModes &roomModes_); - - StreamStatus getStreamStatus() const; - - struct NameOptions { - QString displayName; - QString localizedName; - }; - - bool isLive() const; - private: explicit TwitchChannel(const QString &channelName, Communi::IrcConnection *readConnection); - void setLive(bool newLiveStatus); + void initializeLiveStatusTimer(int intervalMS); void refreshLiveStatus(); - void startRefreshLiveStatusTimer(int intervalMS); - void fetchRecentMessages(); + void refreshPubsub(); + void refreshViewerList(); + bool parseViewerList(const QJsonObject &jsonRoot); + void loadRecentMessages(); + bool parseRecentMessages(const QJsonObject &jsonRoot); + + void setLive(bool newLiveStatus); mutable std::mutex streamStatusMutex_; StreamStatus streamStatus_; @@ -110,6 +106,7 @@ private: QString lastSentMessage_; RoomModes roomModes_; std::mutex roomModeMutex_; + MutexValue roomID_; QObject object_; std::mutex joinedUserMutex_; @@ -125,6 +122,9 @@ private: std::map recentChatters_; std::mutex recentChattersMutex_; + QTimer liveStatusTimer_; + QTimer chattersListTimer_; + friend class TwitchServer; }; diff --git a/src/providers/twitch/TwitchMessageBuilder.cpp b/src/providers/twitch/TwitchMessageBuilder.cpp index 73defdf4a..659111220 100644 --- a/src/providers/twitch/TwitchMessageBuilder.cpp +++ b/src/providers/twitch/TwitchMessageBuilder.cpp @@ -298,8 +298,8 @@ void TwitchMessageBuilder::parseRoomID() if (iterator != std::end(this->tags)) { this->roomID_ = iterator.value().toString(); - if (this->twitchChannel->roomID.isEmpty()) { - this->twitchChannel->roomID = this->roomID_; + if (this->twitchChannel->getRoomID().isEmpty()) { + this->twitchChannel->setRoomID(this->roomID_); } } } diff --git a/src/providers/twitch/TwitchServer.cpp b/src/providers/twitch/TwitchServer.cpp index 247d28fe1..8e5ad173c 100644 --- a/src/providers/twitch/TwitchServer.cpp +++ b/src/providers/twitch/TwitchServer.cpp @@ -161,16 +161,12 @@ std::shared_ptr TwitchServer::getChannelOrEmptyByID(const QString &chan for (const auto &weakChannel : this->channels) { auto channel = weakChannel.lock(); - if (!channel) { - continue; - } + if (!channel) continue; auto twitchChannel = std::dynamic_pointer_cast(channel); - if (!twitchChannel) { - continue; - } + if (!twitchChannel) continue; - if (twitchChannel->roomID == channelID) { + if (twitchChannel->getRoomID() == channelID) { return twitchChannel; } } diff --git a/src/widgets/splits/SplitHeader.cpp b/src/widgets/splits/SplitHeader.cpp index e37938302..4f007012c 100644 --- a/src/widgets/splits/SplitHeader.cpp +++ b/src/widgets/splits/SplitHeader.cpp @@ -284,7 +284,7 @@ void SplitHeader::initializeChannelSignals() TwitchChannel *twitchChannel = dynamic_cast(channel.get()); if (twitchChannel) { - this->managedConnections_.emplace_back(twitchChannel->updateLiveInfo.connect([this]() { + this->managedConnections_.emplace_back(twitchChannel->liveStatusChanged.connect([this]() { this->updateChannelText(); // })); } @@ -494,7 +494,7 @@ void SplitHeader::menuReloadChannelEmotes() TwitchChannel *twitchChannel = dynamic_cast(channel.get()); if (twitchChannel) { - twitchChannel->reloadChannelEmotes(); + twitchChannel->refreshChannelEmotes(); } } From 22bf008bdfb494a79931b25fb3cfbf79da090130 Mon Sep 17 00:00:00 2001 From: Rasmus Karlsson Date: Sun, 15 Jul 2018 11:03:25 +0000 Subject: [PATCH 25/73] Update new settings library version --- lib/settings | 2 +- src/common/ChatterinoSetting.hpp | 2 +- src/main.cpp | 2 +- src/singletons/Settings.cpp | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/settings b/lib/settings index 29accdf9d..0be7cf24b 160000 --- a/lib/settings +++ b/lib/settings @@ -1 +1 @@ -Subproject commit 29accdf9dea05947d687112594ad06bf6001ee0a +Subproject commit 0be7cf24b80cb9b7c1063fde2798f05d735278dd diff --git a/src/common/ChatterinoSetting.hpp b/src/common/ChatterinoSetting.hpp index 226610381..370f58414 100644 --- a/src/common/ChatterinoSetting.hpp +++ b/src/common/ChatterinoSetting.hpp @@ -41,7 +41,7 @@ public: using pajlada::Settings::Setting::operator==; using pajlada::Settings::Setting::operator!=; - using pajlada::Settings::Setting::operator const Type; + using pajlada::Settings::Setting::operator Type; }; using BoolSetting = ChatterinoSetting; diff --git a/src/main.cpp b/src/main.cpp index aa7e53aca..9be014123 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -128,7 +128,7 @@ int runGui(QApplication &a, int argc, char *argv[]) QFile::remove(runningPath); // Save settings - pajlada::Settings::SettingManager::save(); + pajlada::Settings::SettingManager::gSave(); // Deinitialize NetworkManager (stop thread and wait for finish, should be instant) chatterino::NetworkManager::deinit(); diff --git a/src/singletons/Settings.cpp b/src/singletons/Settings.cpp index d2c1718cd..79398197f 100644 --- a/src/singletons/Settings.cpp +++ b/src/singletons/Settings.cpp @@ -35,7 +35,7 @@ void Settings::load() { QString settingsPath = getPaths()->settingsDirectory + "/settings.json"; - pajlada::Settings::SettingManager::load(qPrintable(settingsPath)); + pajlada::Settings::SettingManager::gLoad(qPrintable(settingsPath)); } void Settings::saveSnapshot() From fc5186a4993712e911c2b79edee8b60cd712affe Mon Sep 17 00:00:00 2001 From: Rasmus Karlsson Date: Sun, 15 Jul 2018 11:03:41 +0000 Subject: [PATCH 26/73] Remove invalid warning for gcc --- chatterino.pro | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chatterino.pro b/chatterino.pro index a7450cc82..1a3cd6d49 100644 --- a/chatterino.pro +++ b/chatterino.pro @@ -452,7 +452,6 @@ win32-msvc* { QMAKE_CXXFLAGS_WARN_ON += -Wno-deprecated-declarations QMAKE_CXXFLAGS_WARN_ON += -Wno-sign-compare QMAKE_CXXFLAGS_WARN_ON += -Wno-unused-variable - QMAKE_CXXFLAGS_WARN_ON += -Wno-unused-private-field # Disabling strict-aliasing warnings for now, although we probably want to re-enable this in the future QMAKE_CXXFLAGS_WARN_ON += -Wno-strict-aliasing @@ -461,6 +460,7 @@ win32-msvc* { equals(QMAKE_CXX, "clang++") { QMAKE_CXXFLAGS_WARN_ON += -Wno-unused-local-typedef + QMAKE_CXXFLAGS_WARN_ON += -Wno-unused-private-field } else { QMAKE_CXXFLAGS_WARN_ON += -Wno-class-memaccess } From c995e2bf0de6512356f28f4f60074706ad6686e1 Mon Sep 17 00:00:00 2001 From: Rasmus Karlsson Date: Sun, 15 Jul 2018 11:10:23 +0000 Subject: [PATCH 27/73] Remove unused code --- src/providers/twitch/TwitchMessageBuilder.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/providers/twitch/TwitchMessageBuilder.cpp b/src/providers/twitch/TwitchMessageBuilder.cpp index 73defdf4a..de5c40030 100644 --- a/src/providers/twitch/TwitchMessageBuilder.cpp +++ b/src/providers/twitch/TwitchMessageBuilder.cpp @@ -216,11 +216,6 @@ MessagePtr TwitchMessageBuilder::build() // Actually just text QString linkString = this->matchLink(string); - auto fontStyle = FontStyle::ChatMedium; - - if (string.startsWith('@') && app->settings->enableUsernameBold) { - fontStyle = FontStyle::ChatMediumBold; - } Link link; From bfe6b90877c01dcdb94c8933c60c2b2d88092f0b Mon Sep 17 00:00:00 2001 From: Rasmus Karlsson Date: Sun, 15 Jul 2018 11:56:04 +0000 Subject: [PATCH 28/73] update settings and signals library versions --- lib/settings | 2 +- lib/signals | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/settings b/lib/settings index 0be7cf24b..7fdd1db22 160000 --- a/lib/settings +++ b/lib/settings @@ -1 +1 @@ -Subproject commit 0be7cf24b80cb9b7c1063fde2798f05d735278dd +Subproject commit 7fdd1db22ff18cb0cb0d82e70ea1e26a596beef2 diff --git a/lib/signals b/lib/signals index 3f6645c61..e03c868ec 160000 --- a/lib/signals +++ b/lib/signals @@ -1 +1 @@ -Subproject commit 3f6645c615ff7bf412c05fe322e589cbdd34ff9b +Subproject commit e03c868ec922027a0e672b64388808beb1297816 From f1af162dabee0617ef4e549429013aac0e47dbaa Mon Sep 17 00:00:00 2001 From: Rasmus Karlsson Date: Sun, 15 Jul 2018 12:03:41 +0000 Subject: [PATCH 29/73] clean up some includes --- src/singletons/Fonts.cpp | 8 ++++---- src/singletons/NativeMessaging.cpp | 2 +- src/singletons/Resources.cpp | 8 ++++++-- src/singletons/Theme.cpp | 2 +- src/singletons/WindowManager.cpp | 5 ++--- src/singletons/WindowManager.hpp | 3 +-- 6 files changed, 15 insertions(+), 13 deletions(-) diff --git a/src/singletons/Fonts.cpp b/src/singletons/Fonts.cpp index 2760723e0..70aaa93b6 100644 --- a/src/singletons/Fonts.cpp +++ b/src/singletons/Fonts.cpp @@ -1,12 +1,12 @@ #include "singletons/Fonts.hpp" +#include "Application.hpp" +#include "debug/AssertInGuiThread.hpp" +#include "singletons/WindowManager.hpp" + #include #include -#include "Application.hpp" -#include "WindowManager.hpp" -#include "debug/AssertInGuiThread.hpp" - #ifdef Q_OS_WIN32 #define DEFAULT_FONT_FAMILY "Segoe UI" #define DEFAULT_FONT_SIZE 10 diff --git a/src/singletons/NativeMessaging.cpp b/src/singletons/NativeMessaging.cpp index 60226433b..805f0f384 100644 --- a/src/singletons/NativeMessaging.cpp +++ b/src/singletons/NativeMessaging.cpp @@ -1,4 +1,4 @@ -#include "NativeMessaging.hpp" +#include "singletons/NativeMessaging.hpp" #include "Application.hpp" #include "providers/twitch/TwitchServer.hpp" diff --git a/src/singletons/Resources.cpp b/src/singletons/Resources.cpp index fdbbf9bbf..4b05b718b 100644 --- a/src/singletons/Resources.cpp +++ b/src/singletons/Resources.cpp @@ -1,8 +1,12 @@ -#include "Resources.hpp" -#include "common/UrlFetch.hpp" +#include "singletons/Resources.hpp" +#include "common/NetworkRequest.hpp" + +#include #include +#include #include +#include namespace chatterino { diff --git a/src/singletons/Theme.cpp b/src/singletons/Theme.cpp index 38c864c36..b20a79875 100644 --- a/src/singletons/Theme.cpp +++ b/src/singletons/Theme.cpp @@ -1,6 +1,6 @@ #define LOOKUP_COLOR_COUNT 360 -#include "Theme.hpp" +#include "singletons/Theme.hpp" #include diff --git a/src/singletons/WindowManager.cpp b/src/singletons/WindowManager.cpp index 7dc3b7421..2e43f7d15 100644 --- a/src/singletons/WindowManager.cpp +++ b/src/singletons/WindowManager.cpp @@ -1,4 +1,4 @@ -#include "WindowManager.hpp" +#include "singletons/WindowManager.hpp" #include "Application.hpp" #include "debug/AssertInGuiThread.hpp" @@ -11,12 +11,11 @@ #include "widgets/AccountSwitchPopupWidget.hpp" #include "widgets/dialogs/SettingsDialog.hpp" +#include #include #include #include -#include - #define SETTINGS_FILENAME "/window-layout.json" namespace chatterino { diff --git a/src/singletons/WindowManager.hpp b/src/singletons/WindowManager.hpp index 217bf737d..93a586079 100644 --- a/src/singletons/WindowManager.hpp +++ b/src/singletons/WindowManager.hpp @@ -1,7 +1,6 @@ #pragma once -#include - +#include "common/Singleton.hpp" #include "widgets/Window.hpp" #include "widgets/splits/SplitContainer.hpp" From f60d649f5eba89c2856cd07fda7a0f56e0369361 Mon Sep 17 00:00:00 2001 From: Rasmus Karlsson Date: Sun, 15 Jul 2018 12:11:46 +0000 Subject: [PATCH 30/73] Remove UrlFetch file --- chatterino.pro | 1 - src/common/UrlFetch.hpp | 36 -------------------------- src/messages/Image.cpp | 3 +-- src/providers/bttv/BttvEmotes.cpp | 2 +- src/providers/ffz/FfzEmotes.cpp | 2 +- src/providers/twitch/TwitchAccount.cpp | 1 - src/providers/twitch/TwitchChannel.cpp | 5 ++-- src/providers/twitch/TwitchEmotes.cpp | 2 +- src/widgets/dialogs/LoginDialog.cpp | 2 +- src/widgets/dialogs/UserInfoPopup.cpp | 7 +++-- src/widgets/splits/Split.cpp | 3 ++- src/widgets/splits/SplitHeader.cpp | 5 +--- src/widgets/splits/SplitInput.cpp | 1 - 13 files changed, 16 insertions(+), 54 deletions(-) delete mode 100644 src/common/UrlFetch.hpp diff --git a/chatterino.pro b/chatterino.pro index 1a3cd6d49..82c4f5db9 100644 --- a/chatterino.pro +++ b/chatterino.pro @@ -256,7 +256,6 @@ HEADERS += \ src/common/ProviderId.hpp \ src/common/SerializeCustom.hpp \ src/common/SignalVectorModel.hpp \ - src/common/UrlFetch.hpp \ src/common/Version.hpp \ src/controllers/accounts/Account.hpp \ src/controllers/accounts/AccountController.hpp \ diff --git a/src/common/UrlFetch.hpp b/src/common/UrlFetch.hpp deleted file mode 100644 index 5e27fd3d7..000000000 --- a/src/common/UrlFetch.hpp +++ /dev/null @@ -1,36 +0,0 @@ -#pragma once - -#include "common/NetworkRequest.hpp" - -#include -#include - -namespace chatterino { - -// Not sure if I like these, but I'm trying them out - -static NetworkRequest makeGetChannelRequest(const QString &channelId, - const QObject *caller = nullptr) -{ - QString url("https://api.twitch.tv/kraken/channels/" + channelId); - - auto request = NetworkRequest::twitchRequest(url); - - request.setCaller(caller); - - return request; -} - -static NetworkRequest makeGetStreamRequest(const QString &channelId, - const QObject *caller = nullptr) -{ - QString url("https://api.twitch.tv/kraken/streams/" + channelId); - - auto request = NetworkRequest::twitchRequest(url); - - request.setCaller(caller); - - return request; -} - -} // namespace chatterino diff --git a/src/messages/Image.cpp b/src/messages/Image.cpp index 44b204329..11cd4525c 100644 --- a/src/messages/Image.cpp +++ b/src/messages/Image.cpp @@ -1,8 +1,7 @@ #include "messages/Image.hpp" #include "Application.hpp" -#include "common/NetworkManager.hpp" -#include "common/UrlFetch.hpp" +#include "common/NetworkRequest.hpp" #include "debug/Log.hpp" #include "singletons/Emotes.hpp" #include "singletons/WindowManager.hpp" diff --git a/src/providers/bttv/BttvEmotes.cpp b/src/providers/bttv/BttvEmotes.cpp index 8252ffe37..391be6e6f 100644 --- a/src/providers/bttv/BttvEmotes.cpp +++ b/src/providers/bttv/BttvEmotes.cpp @@ -1,6 +1,6 @@ #include "providers/bttv/BttvEmotes.hpp" -#include "common/UrlFetch.hpp" +#include "common/NetworkRequest.hpp" #include "debug/Log.hpp" #include "messages/Image.hpp" diff --git a/src/providers/ffz/FfzEmotes.cpp b/src/providers/ffz/FfzEmotes.cpp index 25fef15ea..9fe7ac5ce 100644 --- a/src/providers/ffz/FfzEmotes.cpp +++ b/src/providers/ffz/FfzEmotes.cpp @@ -1,6 +1,6 @@ #include "providers/ffz/FfzEmotes.hpp" -#include "common/UrlFetch.hpp" +#include "common/NetworkRequest.hpp" #include "debug/Log.hpp" #include "messages/Image.hpp" diff --git a/src/providers/twitch/TwitchAccount.cpp b/src/providers/twitch/TwitchAccount.cpp index 695bc369f..d5908166c 100644 --- a/src/providers/twitch/TwitchAccount.cpp +++ b/src/providers/twitch/TwitchAccount.cpp @@ -1,7 +1,6 @@ #include "providers/twitch/TwitchAccount.hpp" #include "common/NetworkRequest.hpp" -#include "common/UrlFetch.hpp" #include "debug/Log.hpp" #include "providers/twitch/PartialTwitchUser.hpp" #include "providers/twitch/TwitchCommon.hpp" diff --git a/src/providers/twitch/TwitchChannel.cpp b/src/providers/twitch/TwitchChannel.cpp index 4c1285f6f..5092caf0f 100644 --- a/src/providers/twitch/TwitchChannel.cpp +++ b/src/providers/twitch/TwitchChannel.cpp @@ -1,7 +1,7 @@ #include "providers/twitch/TwitchChannel.hpp" #include "common/Common.hpp" -#include "common/UrlFetch.hpp" +#include "common/NetworkRequest.hpp" #include "controllers/accounts/AccountController.hpp" #include "debug/Log.hpp" #include "messages/Message.hpp" @@ -336,7 +336,8 @@ void TwitchChannel::refreshLiveStatus() std::weak_ptr weak = this->shared_from_this(); - auto request = makeGetStreamRequest(this->roomID, QThread::currentThread()); + auto request = NetworkRequest::twitchRequest(url); + request.setCaller(QThread::currentThread()); request.onSuccess([weak](auto result) { auto d = result.parseRapidJson(); diff --git a/src/providers/twitch/TwitchEmotes.cpp b/src/providers/twitch/TwitchEmotes.cpp index d067f320e..3f94a80b3 100644 --- a/src/providers/twitch/TwitchEmotes.cpp +++ b/src/providers/twitch/TwitchEmotes.cpp @@ -1,6 +1,6 @@ #include "providers/twitch/TwitchEmotes.hpp" -#include "common/UrlFetch.hpp" +#include "common/NetworkRequest.hpp" #include "debug/Benchmark.hpp" #include "debug/Log.hpp" #include "messages/Image.hpp" diff --git a/src/widgets/dialogs/LoginDialog.cpp b/src/widgets/dialogs/LoginDialog.cpp index 14fa58dcb..bee0e87b0 100644 --- a/src/widgets/dialogs/LoginDialog.cpp +++ b/src/widgets/dialogs/LoginDialog.cpp @@ -1,7 +1,7 @@ #include "widgets/dialogs/LoginDialog.hpp" #include "common/Common.hpp" -#include "common/UrlFetch.hpp" +#include "common/NetworkRequest.hpp" #include "controllers/accounts/AccountController.hpp" #include "providers/twitch/PartialTwitchUser.hpp" diff --git a/src/widgets/dialogs/UserInfoPopup.cpp b/src/widgets/dialogs/UserInfoPopup.cpp index 02ae86703..edddede5e 100644 --- a/src/widgets/dialogs/UserInfoPopup.cpp +++ b/src/widgets/dialogs/UserInfoPopup.cpp @@ -1,7 +1,7 @@ #include "UserInfoPopup.hpp" #include "Application.hpp" -#include "common/UrlFetch.hpp" +#include "common/NetworkRequest.hpp" #include "controllers/accounts/AccountController.hpp" #include "providers/twitch/PartialTwitchUser.hpp" #include "providers/twitch/TwitchChannel.hpp" @@ -250,7 +250,10 @@ void UserInfoPopup::updateUserData() this->userId_ = id; - auto request = makeGetChannelRequest(id, this); + QString url("https://api.twitch.tv/kraken/channels/" + id); + + auto request = NetworkRequest::twitchRequest(url); + request.setCaller(this); request.onSuccess([this](auto result) { auto obj = result.parseJson(); diff --git a/src/widgets/splits/Split.cpp b/src/widgets/splits/Split.cpp index efa6fbc77..1ccdf63ed 100644 --- a/src/widgets/splits/Split.cpp +++ b/src/widgets/splits/Split.cpp @@ -2,7 +2,7 @@ #include "Application.hpp" #include "common/Common.hpp" -#include "common/UrlFetch.hpp" +#include "common/NetworkRequest.hpp" #include "providers/twitch/EmoteValue.hpp" #include "providers/twitch/TwitchChannel.hpp" #include "providers/twitch/TwitchMessageBuilder.hpp" @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include diff --git a/src/widgets/splits/SplitHeader.cpp b/src/widgets/splits/SplitHeader.cpp index e37938302..78251a303 100644 --- a/src/widgets/splits/SplitHeader.cpp +++ b/src/widgets/splits/SplitHeader.cpp @@ -1,7 +1,6 @@ #include "widgets/splits/SplitHeader.hpp" #include "Application.hpp" -#include "common/UrlFetch.hpp" #include "controllers/accounts/AccountController.hpp" #include "providers/twitch/TwitchChannel.hpp" #include "providers/twitch/TwitchServer.hpp" @@ -80,9 +79,7 @@ SplitHeader::SplitHeader(Split *_split) // dropdown->setScaleIndependantSize(23, 23); this->addDropdownItems(dropdown.getElement()); QObject::connect(dropdown.getElement(), &RippleEffectButton::leftMousePress, this, [this] { - QTimer::singleShot(80, [&, this] { - this->dropdownMenu_.popup(QCursor::pos()); - }); + QTimer::singleShot(80, [&, this] { this->dropdownMenu_.popup(QCursor::pos()); }); }); } diff --git a/src/widgets/splits/SplitInput.cpp b/src/widgets/splits/SplitInput.cpp index 70efdd77b..113a05d7a 100644 --- a/src/widgets/splits/SplitInput.cpp +++ b/src/widgets/splits/SplitInput.cpp @@ -1,7 +1,6 @@ #include "widgets/splits/SplitInput.hpp" #include "Application.hpp" -#include "common/UrlFetch.hpp" #include "controllers/commands/CommandController.hpp" #include "providers/twitch/TwitchChannel.hpp" #include "providers/twitch/TwitchServer.hpp" From 9bfbdefd2f0972a738230d5b95a009f73b1dd933 Mon Sep 17 00:00:00 2001 From: Rasmus Karlsson Date: Sun, 15 Jul 2018 14:30:04 +0000 Subject: [PATCH 31/73] Update settings library version --- lib/settings | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/settings b/lib/settings index 7fdd1db22..7f0db95f2 160000 --- a/lib/settings +++ b/lib/settings @@ -1 +1 @@ -Subproject commit 7fdd1db22ff18cb0cb0d82e70ea1e26a596beef2 +Subproject commit 7f0db95f245fb726e756ecde15a800c0928b054b From 1614b11e4222467824bf967211b55a6a87701cc7 Mon Sep 17 00:00:00 2001 From: fourtf Date: Sun, 15 Jul 2018 20:28:54 +0200 Subject: [PATCH 32/73] Refactored TwitchChannel --- chatterino.pro | 5 +- .../{uniqueaccess.hpp => UniqueAccess.hpp} | 30 +- src/common/uniqueaccess.cpp | 1 - .../commands/CommandController.cpp | 6 +- src/providers/twitch/IrcMessageHandler.cpp | 36 +-- src/providers/twitch/TwitchChannel.cpp | 274 +++++++++--------- src/providers/twitch/TwitchChannel.hpp | 93 +++--- src/providers/twitch/TwitchMessageBuilder.cpp | 8 +- src/providers/twitch/TwitchServer.cpp | 4 +- src/widgets/dialogs/EmotePopup.cpp | 8 +- src/widgets/splits/SplitHeader.cpp | 50 ++-- 11 files changed, 268 insertions(+), 247 deletions(-) rename src/common/{uniqueaccess.hpp => UniqueAccess.hpp} (70%) delete mode 100644 src/common/uniqueaccess.cpp diff --git a/chatterino.pro b/chatterino.pro index f180f0715..723a878a0 100644 --- a/chatterino.pro +++ b/chatterino.pro @@ -232,8 +232,7 @@ SOURCES += \ src/widgets/dialogs/UpdateDialog.cpp \ src/widgets/settingspages/IgnoresPage.cpp \ src/providers/twitch/PubsubClient.cpp \ - src/providers/twitch/TwitchApi.cpp \ - src/common/uniqueaccess.cpp + src/providers/twitch/TwitchApi.cpp HEADERS += \ src/Application.hpp \ @@ -416,7 +415,7 @@ HEADERS += \ src/widgets/settingspages/IgnoresPage.hpp \ src/providers/twitch/PubsubClient.hpp \ src/providers/twitch/TwitchApi.hpp \ - src/common/uniqueaccess.hpp + src/common/UniqueAccess.hpp RESOURCES += \ resources/resources.qrc \ diff --git a/src/common/uniqueaccess.hpp b/src/common/UniqueAccess.hpp similarity index 70% rename from src/common/uniqueaccess.hpp rename to src/common/UniqueAccess.hpp index 2f2a2be36..c4013af3e 100644 --- a/src/common/uniqueaccess.hpp +++ b/src/common/UniqueAccess.hpp @@ -21,16 +21,31 @@ public: this->mutex_.unlock(); } - T *operator->() const + const T *operator->() const { return &this->element_; } - T &operator*() const + T *operator->() + { + return &this->element_; + } + + const T &operator*() const { return this->element_; } + T &operator*() + { + return this->element_; + } + + T clone() const + { + return T(this->element_); + } + private: T &element_; std::mutex &mutex_; @@ -40,7 +55,7 @@ template class UniqueAccess { public: - template ::value>::type * = 0> + template UniqueAccess() : element_(T()) { @@ -73,9 +88,14 @@ public: return AccessGuard(this->element_, this->mutex_); } + const AccessGuard access() const + { + return AccessGuard(this->element_, this->mutex_); + } + private: - T element_; - std::mutex mutex_; + mutable T element_; + mutable std::mutex mutex_; }; } // namespace chatterino diff --git a/src/common/uniqueaccess.cpp b/src/common/uniqueaccess.cpp deleted file mode 100644 index 12e1c8846..000000000 --- a/src/common/uniqueaccess.cpp +++ /dev/null @@ -1 +0,0 @@ -#include "uniqueaccess.hpp" diff --git a/src/controllers/commands/CommandController.cpp b/src/controllers/commands/CommandController.cpp index 9d43a0d96..c558ec376 100644 --- a/src/controllers/commands/CommandController.cpp +++ b/src/controllers/commands/CommandController.cpp @@ -142,7 +142,7 @@ QString CommandController::execCommand(const QString &text, ChannelPtr channel, app->twitch.server->getWriteConnection()->sendRaw("PRIVMSG #jtv :" + text + "\r\n"); - if (app->settings->inlineWhispers) { + if (getSettings()->inlineWhispers) { app->twitch.server->forEachChannel( [&b](ChannelPtr _channel) { _channel->addMessage(b.getMessage()); }); } @@ -163,10 +163,10 @@ QString CommandController::execCommand(const QString &text, ChannelPtr channel, return ""; } else if (commandName == "/uptime") { - const auto &streamStatus = twitchChannel->getStreamStatus(); + const auto &streamStatus = twitchChannel->accessStreamStatus(); QString messageText = - streamStatus.live ? streamStatus.uptime : "Channel is not live."; + streamStatus->live ? streamStatus->uptime : "Channel is not live."; channel->addMessage(Message::createSystemMessage(messageText)); diff --git a/src/providers/twitch/IrcMessageHandler.cpp b/src/providers/twitch/IrcMessageHandler.cpp index de6ab30a9..29d64d397 100644 --- a/src/providers/twitch/IrcMessageHandler.cpp +++ b/src/providers/twitch/IrcMessageHandler.cpp @@ -93,31 +93,33 @@ void IrcMessageHandler::handleRoomStateMessage(Communi::IrcMessage *message) if ((it = tags.find("room-id")) != tags.end()) { auto roomId = it.value().toString(); - twitchChannel->setRoomID(roomId); + twitchChannel->setRoomId(roomId); app->resources->loadChannelData(roomId); } // Room modes - TwitchChannel::RoomModes roomModes = twitchChannel->getRoomModes(); + { + auto roomModes = twitchChannel->accessRoomModes(); - if ((it = tags.find("emote-only")) != tags.end()) { - roomModes.emoteOnly = it.value() == "1"; - } - if ((it = tags.find("subs-only")) != tags.end()) { - roomModes.submode = it.value() == "1"; - } - if ((it = tags.find("slow")) != tags.end()) { - roomModes.slowMode = it.value().toInt(); - } - if ((it = tags.find("r9k")) != tags.end()) { - roomModes.r9k = it.value() == "1"; - } - if ((it = tags.find("broadcaster-lang")) != tags.end()) { - roomModes.broadcasterLang = it.value().toString(); + if ((it = tags.find("emote-only")) != tags.end()) { + roomModes->emoteOnly = it.value() == "1"; + } + if ((it = tags.find("subs-only")) != tags.end()) { + roomModes->submode = it.value() == "1"; + } + if ((it = tags.find("slow")) != tags.end()) { + roomModes->slowMode = it.value().toInt(); + } + if ((it = tags.find("r9k")) != tags.end()) { + roomModes->r9k = it.value() == "1"; + } + if ((it = tags.find("broadcaster-lang")) != tags.end()) { + roomModes->broadcasterLang = it.value().toString(); + } } - twitchChannel->setRoomModes(roomModes); + twitchChannel->roomModesChanged.invoke(); } } diff --git a/src/providers/twitch/TwitchChannel.cpp b/src/providers/twitch/TwitchChannel.cpp index 2fa2a6c8e..8be92b4b5 100644 --- a/src/providers/twitch/TwitchChannel.cpp +++ b/src/providers/twitch/TwitchChannel.cpp @@ -21,31 +21,31 @@ namespace chatterino { TwitchChannel::TwitchChannel(const QString &channelName, Communi::IrcConnection *readConnection) : Channel(channelName, Channel::Type::Twitch) - , bttvChannelEmotes(new EmoteMap) - , ffzChannelEmotes(new EmoteMap) - , subscriptionURL("https://www.twitch.tv/subs/" + name) - , channelURL("https://twitch.tv/" + name) - , popoutPlayerURL("https://player.twitch.tv/?channel=" + name) + , bttvEmotes_(new EmoteMap) + , ffzEmotes_(new EmoteMap) + , subscriptionUrl_("https://www.twitch.tv/subs/" + name) + , channelUrl_("https://twitch.tv/" + name) + , popoutPlayerUrl_("https://player.twitch.tv/?channel=" + name) , mod_(false) , readConnection_(readConnection) { Log("[TwitchChannel:{}] Opened", this->name); this->refreshChannelEmotes(); - this->refreshViewerList(); + // this->refreshViewerList(); this->managedConnect(getApp()->accounts->twitch.currentUserChanged, [=] { this->setMod(false); }); // pubsub this->userStateChanged.connect([=] { this->refreshPubsub(); }); - this->roomIDChanged.connect([=] { this->refreshPubsub(); }); + this->roomIdChanged.connect([=] { this->refreshPubsub(); }); this->managedConnect(getApp()->accounts->twitch.currentUserChanged, [=] { this->refreshPubsub(); }); this->refreshPubsub(); // room id loaded -> refresh live status - this->roomIDChanged.connect([this]() { this->refreshLiveStatus(); }); + this->roomIdChanged.connect([this]() { this->refreshLiveStatus(); }); // timers QObject::connect(&this->chattersListTimer_, &QTimer::timeout, @@ -83,8 +83,8 @@ void TwitchChannel::refreshChannelEmotes() Log("[TwitchChannel:{}] Reloading channel emotes", this->name); - app->emotes->bttv.loadChannelEmotes(this->name, this->bttvChannelEmotes); - app->emotes->ffz.loadChannelEmotes(this->name, this->ffzChannelEmotes); + app->emotes->bttv.loadChannelEmotes(this->name, this->bttvEmotes_); + app->emotes->ffz.loadChannelEmotes(this->name, this->ffzEmotes_); } void TwitchChannel::sendMessage(const QString &message) @@ -112,7 +112,7 @@ void TwitchChannel::sendMessage(const QString &message) } if (!this->hasModRights()) { - if (app->settings->allowDuplicateMessages) { + if (getSettings()->allowDuplicateMessages) { if (parsedMessage == this->lastSentMessage_) { parsedMessage.append(this->messageSuffix_); } @@ -153,36 +153,30 @@ void TwitchChannel::addRecentChatter(const std::shared_ptr &message) { assert(!message->loginName.isEmpty()); - std::lock_guard lock(this->recentChattersMutex_); - - this->recentChatters_[message->loginName] = {message->displayName, message->localizedName}; - this->completionModel.addUser(message->displayName); } void TwitchChannel::addJoinedUser(const QString &user) { - auto *app = getApp(); + auto app = getApp(); if (user == app->accounts->twitch.getCurrent()->getUserName() || - !app->settings->showJoins.getValue()) { + !getSettings()->showJoins.getValue()) { return; } - std::lock_guard guard(this->joinedUserMutex_); - - joinedUsers_ << user; + auto joinedUsers = this->joinedUsers_.access(); + joinedUsers->append(user); if (!this->joinedUsersMergeQueued_) { this->joinedUsersMergeQueued_ = true; - QTimer::singleShot(500, &this->object_, [this] { - std::lock_guard guard(this->joinedUserMutex_); + QTimer::singleShot(500, &this->lifetimeGuard_, [this] { + auto joinedUsers = this->joinedUsers_.access(); - auto message = - Message::createSystemMessage("Users joined: " + this->joinedUsers_.join(", ")); + auto message = Message::createSystemMessage("Users joined: " + joinedUsers->join(", ")); message->flags |= Message::Collapsed; + joinedUsers->clear(); this->addMessage(message); - this->joinedUsers_.clear(); this->joinedUsersMergeQueued_ = false; }); } @@ -197,76 +191,92 @@ void TwitchChannel::addPartedUser(const QString &user) return; } - std::lock_guard guard(this->partedUserMutex_); - - this->partedUsers_ << user; + auto partedUsers = this->partedUsers_.access(); + partedUsers->append(user); if (!this->partedUsersMergeQueued_) { this->partedUsersMergeQueued_ = true; - QTimer::singleShot(500, &this->object_, [this] { - std::lock_guard guard(this->partedUserMutex_); + QTimer::singleShot(500, &this->lifetimeGuard_, [this] { + auto partedUsers = this->partedUsers_.access(); - auto message = - Message::createSystemMessage("Users parted: " + this->partedUsers_.join(", ")); + auto message = Message::createSystemMessage("Users parted: " + partedUsers->join(", ")); message->flags |= Message::Collapsed; this->addMessage(message); - this->partedUsers_.clear(); + partedUsers->clear(); this->partedUsersMergeQueued_ = false; }); } } -QString TwitchChannel::getRoomID() const +QString TwitchChannel::getRoomId() const { return this->roomID_.get(); } -void TwitchChannel::setRoomID(const QString &id) +void TwitchChannel::setRoomId(const QString &id) { this->roomID_.set(id); - this->roomIDChanged.invoke(); + this->roomIdChanged.invoke(); this->loadRecentMessages(); } -TwitchChannel::RoomModes TwitchChannel::getRoomModes() +const AccessGuard TwitchChannel::accessRoomModes() const { - std::lock_guard lock(this->roomModeMutex_); - - return this->roomModes_; + return this->roomModes_.access(); } void TwitchChannel::setRoomModes(const RoomModes &_roomModes) { - { - std::lock_guard lock(this->roomModeMutex_); - this->roomModes_ = _roomModes; - } + this->roomModes_ = _roomModes; this->roomModesChanged.invoke(); } bool TwitchChannel::isLive() const { - std::lock_guard lock(this->streamStatusMutex_); - return this->streamStatus_.live; + return this->streamStatus_.access()->live; } -TwitchChannel::StreamStatus TwitchChannel::getStreamStatus() const +const AccessGuard TwitchChannel::accessStreamStatus() const { - std::lock_guard lock(this->streamStatusMutex_); - return this->streamStatus_; + return this->streamStatus_.access(); +} + +const EmoteMap &TwitchChannel::getFfzEmotes() const +{ + return *this->ffzEmotes_; +} + +const EmoteMap &TwitchChannel::getBttvEmotes() const +{ + return *this->bttvEmotes_; +} + +const QString &TwitchChannel::getSubscriptionUrl() +{ + return this->subscriptionUrl_; +} + +const QString &TwitchChannel::getChannelUrl() +{ + return this->channelUrl_; +} + +const QString &TwitchChannel::getPopoutPlayerUrl() +{ + return this->popoutPlayerUrl_; } void TwitchChannel::setLive(bool newLiveStatus) { bool gotNewLiveStatus = false; { - std::lock_guard lock(this->streamStatusMutex_); - if (this->streamStatus_.live != newLiveStatus) { + auto guard = this->streamStatus_.access(); + if (guard->live != newLiveStatus) { gotNewLiveStatus = true; - this->streamStatus_.live = newLiveStatus; + guard->live = newLiveStatus; } } @@ -277,7 +287,7 @@ void TwitchChannel::setLive(bool newLiveStatus) void TwitchChannel::refreshLiveStatus() { - auto roomID = this->getRoomID(); + auto roomID = this->getRoomId(); if (roomID.isEmpty()) { Log("[TwitchChannel:{}] Refreshing live status (Missing ID)", this->name); @@ -291,92 +301,86 @@ void TwitchChannel::refreshLiveStatus() auto request = makeGetStreamRequest(roomID, QThread::currentThread()); - request.onSuccess([weak = this->weak_from_this()](auto result) { - auto d = result.parseRapidJson(); + request.onSuccess([this, weak = this->weak_from_this()](auto result) { ChannelPtr shared = weak.lock(); + if (!shared) return false; - if (!shared) { - return false; - } - - TwitchChannel *channel = dynamic_cast(shared.get()); - - if (!d.IsObject()) { - Log("[TwitchChannel:refreshLiveStatus] root is not an object"); - return false; - } - - if (!d.HasMember("stream")) { - Log("[TwitchChannel:refreshLiveStatus] Missing stream in root"); - return false; - } - - const auto &stream = d["stream"]; - - if (!stream.IsObject()) { - // Stream is offline (stream is most likely null) - channel->setLive(false); - return false; - } - - if (!stream.HasMember("viewers") || !stream.HasMember("game") || - !stream.HasMember("channel") || !stream.HasMember("created_at")) { - Log("[TwitchChannel:refreshLiveStatus] Missing members in stream"); - channel->setLive(false); - return false; - } - - const rapidjson::Value &streamChannel = stream["channel"]; - - if (!streamChannel.IsObject() || !streamChannel.HasMember("status")) { - Log("[TwitchChannel:refreshLiveStatus] Missing member \"status\" in channel"); - return false; - } - - // Stream is live - - { - std::lock_guard lock(channel->streamStatusMutex_); - StreamStatus status; - channel->streamStatus_.live = true; - channel->streamStatus_.viewerCount = stream["viewers"].GetUint(); - channel->streamStatus_.game = stream["game"].GetString(); - channel->streamStatus_.title = streamChannel["status"].GetString(); - QDateTime since = QDateTime::fromString(stream["created_at"].GetString(), Qt::ISODate); - auto diff = since.secsTo(QDateTime::currentDateTime()); - channel->streamStatus_.uptime = - QString::number(diff / 3600) + "h " + QString::number(diff % 3600 / 60) + "m"; - - channel->streamStatus_.rerun = false; - if (stream.HasMember("stream_type")) { - channel->streamStatus_.streamType = stream["stream_type"].GetString(); - } else { - channel->streamStatus_.streamType = QString(); - } - - if (stream.HasMember("broadcast_platform")) { - const auto &broadcastPlatformValue = stream["broadcast_platform"]; - - if (broadcastPlatformValue.IsString()) { - const char *broadcastPlatform = stream["broadcast_platform"].GetString(); - if (strcmp(broadcastPlatform, "rerun") == 0) { - channel->streamStatus_.rerun = true; - } - } - } - } - - // Signal all listeners that the stream status has been updated - channel->liveStatusChanged.invoke(); - - return true; + return this->parseLiveStatus(result.parseRapidJson()); }); request.execute(); } -void TwitchChannel::initializeLiveStatusTimer(int intervalMS) +bool TwitchChannel::parseLiveStatus(const rapidjson::Document &document) { + if (!document.IsObject()) { + Log("[TwitchChannel:refreshLiveStatus] root is not an object"); + return false; + } + + if (!document.HasMember("stream")) { + Log("[TwitchChannel:refreshLiveStatus] Missing stream in root"); + return false; + } + + const auto &stream = document["stream"]; + + if (!stream.IsObject()) { + // Stream is offline (stream is most likely null) + this->setLive(false); + return false; + } + + if (!stream.HasMember("viewers") || !stream.HasMember("game") || !stream.HasMember("channel") || + !stream.HasMember("created_at")) { + Log("[TwitchChannel:refreshLiveStatus] Missing members in stream"); + this->setLive(false); + return false; + } + + const rapidjson::Value &streamChannel = stream["channel"]; + + if (!streamChannel.IsObject() || !streamChannel.HasMember("status")) { + Log("[TwitchChannel:refreshLiveStatus] Missing member \"status\" in channel"); + return false; + } + + // Stream is live + + { + auto status = this->streamStatus_.access(); + status->live = true; + status->viewerCount = stream["viewers"].GetUint(); + status->game = stream["game"].GetString(); + status->title = streamChannel["status"].GetString(); + QDateTime since = QDateTime::fromString(stream["created_at"].GetString(), Qt::ISODate); + auto diff = since.secsTo(QDateTime::currentDateTime()); + status->uptime = + QString::number(diff / 3600) + "h " + QString::number(diff % 3600 / 60) + "m"; + + status->rerun = false; + if (stream.HasMember("stream_type")) { + status->streamType = stream["stream_type"].GetString(); + } else { + status->streamType = QString(); + } + + if (stream.HasMember("broadcast_platform")) { + const auto &broadcastPlatformValue = stream["broadcast_platform"]; + + if (broadcastPlatformValue.IsString()) { + const char *broadcastPlatform = stream["broadcast_platform"].GetString(); + if (strcmp(broadcastPlatform, "rerun") == 0) { + status->rerun = true; + } + } + } + } + + // Signal all listeners that the stream status has been updated + this->liveStatusChanged.invoke(); + + return true; } void TwitchChannel::loadRecentMessages() @@ -384,7 +388,7 @@ void TwitchChannel::loadRecentMessages() static QString genericURL = "https://tmi.twitch.tv/api/rooms/%1/recent_messages?client_id=" + getDefaultClientID(); - NetworkRequest request(genericURL.arg(this->getRoomID())); + NetworkRequest request(genericURL.arg(this->getRoomId())); request.makeAuthorizedV5(getDefaultClientID()); request.setCaller(QThread::currentThread()); @@ -431,7 +435,7 @@ void TwitchChannel::refreshPubsub() { // listen to moderation actions if (!this->hasModRights()) return; - auto roomId = this->getRoomID(); + auto roomId = this->getRoomId(); if (roomId.isEmpty()) return; auto account = getApp()->accounts->twitch.getCurrent(); @@ -441,10 +445,10 @@ void TwitchChannel::refreshPubsub() void TwitchChannel::refreshViewerList() { // setting? - const auto streamStatus = this->getStreamStatus(); + const auto streamStatus = this->accessStreamStatus(); if (getSettings()->onlyFetchChattersForSmallerStreamers) { - if (streamStatus.live && streamStatus.viewerCount > getSettings()->smallStreamerLimit) { + if (streamStatus->live && streamStatus->viewerCount > getSettings()->smallStreamerLimit) { return; } } diff --git a/src/providers/twitch/TwitchChannel.hpp b/src/providers/twitch/TwitchChannel.hpp index 950c9f9d1..b46b59f9f 100644 --- a/src/providers/twitch/TwitchChannel.hpp +++ b/src/providers/twitch/TwitchChannel.hpp @@ -5,6 +5,7 @@ #include "common/Channel.hpp" #include "common/Common.hpp" #include "common/MutexValue.hpp" +#include "common/UniqueAccess.hpp" #include "singletons/Emotes.hpp" #include "util/ConcurrentMap.hpp" @@ -43,50 +44,53 @@ public: QString broadcasterLang; }; - bool isEmpty() const override; - bool canSendMessage() const override; - void sendMessage(const QString &message) override; + void refreshChannelEmotes(); + // Channel methods + virtual bool isEmpty() const override; + virtual bool canSendMessage() const override; + virtual void sendMessage(const QString &message) override; + + // Auto completion + void addRecentChatter(const std::shared_ptr &message) final; + void addJoinedUser(const QString &user); + void addPartedUser(const QString &user); + + // Twitch data bool isLive() const; virtual bool isMod() const override; void setMod(bool value); virtual bool isBroadcaster() const override; - QString getRoomID() const; - void setRoomID(const QString &id); - RoomModes getRoomModes(); + QString getRoomId() const; + void setRoomId(const QString &id); + const AccessGuard accessRoomModes() const; void setRoomModes(const RoomModes &roomModes_); - StreamStatus getStreamStatus() const; + const AccessGuard accessStreamStatus() const; - void addRecentChatter(const std::shared_ptr &message) final; - void addJoinedUser(const QString &user); - void addPartedUser(const QString &user); + const EmoteMap &getFfzEmotes() const; + const EmoteMap &getBttvEmotes() const; + const QString &getSubscriptionUrl(); + const QString &getChannelUrl(); + const QString &getPopoutPlayerUrl(); + // Signals + pajlada::Signals::NoArgSignal roomIdChanged; + pajlada::Signals::NoArgSignal liveStatusChanged; + pajlada::Signals::NoArgSignal userStateChanged; + pajlada::Signals::NoArgSignal roomModesChanged; + +private: struct NameOptions { QString displayName; QString localizedName; }; - void refreshChannelEmotes(); - - const std::shared_ptr bttvChannelEmotes; - const std::shared_ptr ffzChannelEmotes; - - const QString subscriptionURL; - const QString channelURL; - const QString popoutPlayerURL; - - pajlada::Signals::NoArgSignal roomIDChanged; - pajlada::Signals::NoArgSignal liveStatusChanged; - - pajlada::Signals::NoArgSignal userStateChanged; - pajlada::Signals::NoArgSignal roomModesChanged; - -private: explicit TwitchChannel(const QString &channelName, Communi::IrcConnection *readConnection); - void initializeLiveStatusTimer(int intervalMS); + // Methods void refreshLiveStatus(); + bool parseLiveStatus(const rapidjson::Document &document); void refreshPubsub(); void refreshViewerList(); bool parseViewerList(const QJsonObject &jsonRoot); @@ -95,35 +99,32 @@ private: void setLive(bool newLiveStatus); - mutable std::mutex streamStatusMutex_; - StreamStatus streamStatus_; + // Twitch data + UniqueAccess streamStatus_; + UniqueAccess userState_; + UniqueAccess roomModes_; - mutable std::mutex userStateMutex_; - UserState userState_; + const std::shared_ptr bttvEmotes_; + const std::shared_ptr ffzEmotes_; + const QString subscriptionUrl_; + const QString channelUrl_; + const QString popoutPlayerUrl_; bool mod_ = false; - QByteArray messageSuffix_; - QString lastSentMessage_; - RoomModes roomModes_; - std::mutex roomModeMutex_; MutexValue roomID_; - QObject object_; - std::mutex joinedUserMutex_; - QStringList joinedUsers_; + UniqueAccess joinedUsers_; bool joinedUsersMergeQueued_ = false; - std::mutex partedUserMutex_; - QStringList partedUsers_; + UniqueAccess partedUsers_; bool partedUsersMergeQueued_ = false; - Communi::IrcConnection *readConnection_ = nullptr; - - // Key = login name - std::map recentChatters_; - std::mutex recentChattersMutex_; - + // -- + QByteArray messageSuffix_; + QString lastSentMessage_; + QObject lifetimeGuard_; QTimer liveStatusTimer_; QTimer chattersListTimer_; + Communi::IrcConnection *readConnection_ = nullptr; friend class TwitchServer; }; diff --git a/src/providers/twitch/TwitchMessageBuilder.cpp b/src/providers/twitch/TwitchMessageBuilder.cpp index 659111220..a80d14f56 100644 --- a/src/providers/twitch/TwitchMessageBuilder.cpp +++ b/src/providers/twitch/TwitchMessageBuilder.cpp @@ -298,8 +298,8 @@ void TwitchMessageBuilder::parseRoomID() if (iterator != std::end(this->tags)) { this->roomID_ = iterator.value().toString(); - if (this->twitchChannel->getRoomID().isEmpty()) { - this->twitchChannel->setRoomID(this->roomID_); + if (this->twitchChannel->getRoomId().isEmpty()) { + this->twitchChannel->setRoomId(this->roomID_); } } } @@ -588,14 +588,14 @@ bool TwitchMessageBuilder::tryAppendEmote(QString &emoteString) // BTTV Global Emote return appendEmote(MessageElement::BttvEmote); } else if (this->twitchChannel != nullptr && - this->twitchChannel->bttvChannelEmotes->tryGet(emoteString, emoteData)) { + this->twitchChannel->getBttvEmotes().tryGet(emoteString, emoteData)) { // BTTV Channel Emote return appendEmote(MessageElement::BttvEmote); } else if (app->emotes->ffz.globalEmotes.tryGet(emoteString, emoteData)) { // FFZ Global Emote return appendEmote(MessageElement::FfzEmote); } else if (this->twitchChannel != nullptr && - this->twitchChannel->ffzChannelEmotes->tryGet(emoteString, emoteData)) { + this->twitchChannel->getFfzEmotes().tryGet(emoteString, emoteData)) { // FFZ Channel Emote return appendEmote(MessageElement::FfzEmote); } diff --git a/src/providers/twitch/TwitchServer.cpp b/src/providers/twitch/TwitchServer.cpp index 8e5ad173c..39df1cc64 100644 --- a/src/providers/twitch/TwitchServer.cpp +++ b/src/providers/twitch/TwitchServer.cpp @@ -155,7 +155,7 @@ void TwitchServer::forEachChannelAndSpecialChannels(std::functionmentionsChannel); } -std::shared_ptr TwitchServer::getChannelOrEmptyByID(const QString &channelID) +std::shared_ptr TwitchServer::getChannelOrEmptyByID(const QString &channelId) { std::lock_guard lock(this->channelMutex); @@ -166,7 +166,7 @@ std::shared_ptr TwitchServer::getChannelOrEmptyByID(const QString &chan auto twitchChannel = std::dynamic_pointer_cast(channel); if (!twitchChannel) continue; - if (twitchChannel->getRoomID() == channelID) { + if (twitchChannel->getRoomId() == channelId) { return twitchChannel; } } diff --git a/src/widgets/dialogs/EmotePopup.cpp b/src/widgets/dialogs/EmotePopup.cpp index 91a1a2eb4..24e4e8dcf 100644 --- a/src/widgets/dialogs/EmotePopup.cpp +++ b/src/widgets/dialogs/EmotePopup.cpp @@ -56,7 +56,7 @@ void EmotePopup::loadChannel(ChannelPtr _channel) ChannelPtr emoteChannel(new Channel("", Channel::Type::None)); - auto addEmotes = [&](EmoteMap &map, const QString &title, const QString &emoteDesc) { + auto addEmotes = [&](const EmoteMap &map, const QString &title, const QString &emoteDesc) { // TITLE MessageBuilder builder1; @@ -120,12 +120,10 @@ void EmotePopup::loadChannel(ChannelPtr _channel) } addEmotes(app->emotes->bttv.globalEmotes, "BetterTTV Global Emotes", "BetterTTV Global Emote"); - addEmotes(*channel->bttvChannelEmotes.get(), "BetterTTV Channel Emotes", - "BetterTTV Channel Emote"); + addEmotes(channel->getBttvEmotes(), "BetterTTV Channel Emotes", "BetterTTV Channel Emote"); addEmotes(app->emotes->ffz.globalEmotes, "FrankerFaceZ Global Emotes", "FrankerFaceZ Global Emote"); - addEmotes(*channel->ffzChannelEmotes.get(), "FrankerFaceZ Channel Emotes", - "FrankerFaceZ Channel Emote"); + addEmotes(channel->getFfzEmotes(), "FrankerFaceZ Channel Emotes", "FrankerFaceZ Channel Emote"); this->viewEmotes_->setChannel(emoteChannel); } diff --git a/src/widgets/splits/SplitHeader.cpp b/src/widgets/splits/SplitHeader.cpp index 4f007012c..99887ef2b 100644 --- a/src/widgets/splits/SplitHeader.cpp +++ b/src/widgets/splits/SplitHeader.cpp @@ -80,9 +80,7 @@ SplitHeader::SplitHeader(Split *_split) // dropdown->setScaleIndependantSize(23, 23); this->addDropdownItems(dropdown.getElement()); QObject::connect(dropdown.getElement(), &RippleEffectButton::leftMousePress, this, [this] { - QTimer::singleShot(80, [&, this] { - this->dropdownMenu_.popup(QCursor::pos()); - }); + QTimer::singleShot(80, [&, this] { this->dropdownMenu_.popup(QCursor::pos()); }); }); } @@ -168,17 +166,17 @@ void SplitHeader::setupModeLabel(RippleEffectLabel &label) label.setEnable(twitchChannel->hasModRights()); // set the label text - auto roomModes = twitchChannel->getRoomModes(); QString text; - if (roomModes.r9k) - text += "r9k, "; - if (roomModes.slowMode) - text += QString("slow(%1), ").arg(QString::number(roomModes.slowMode)); - if (roomModes.emoteOnly) - text += "emote, "; - if (roomModes.submode) - text += "sub, "; + { + auto roomModes = twitchChannel->accessRoomModes(); + + if (roomModes->r9k) text += "r9k, "; + if (roomModes->slowMode) + text += QString("slow(%1), ").arg(QString::number(roomModes->slowMode)); + if (roomModes->emoteOnly) text += "emote, "; + if (roomModes->submode) text += "sub, "; + } if (text.length() > 2) { text = text.mid(0, text.size() - 2); @@ -229,12 +227,12 @@ void SplitHeader::addModeActions(QMenu &menu) return; } - auto roomModes = twitchChannel->getRoomModes(); + auto roomModes = twitchChannel->accessRoomModes(); - setR9k->setChecked(roomModes.r9k); - setSlow->setChecked(roomModes.slowMode); - setEmote->setChecked(roomModes.emoteOnly); - setSub->setChecked(roomModes.submode); + setR9k->setChecked(roomModes->r9k); + setSlow->setChecked(roomModes->slowMode); + setEmote->setChecked(roomModes->emoteOnly); + setSub->setChecked(roomModes->submode); })); auto toggle = [this](const QString &_command, QAction *action) mutable { @@ -315,22 +313,22 @@ void SplitHeader::updateChannelText() TwitchChannel *twitchChannel = dynamic_cast(channel.get()); if (twitchChannel != nullptr) { - const auto streamStatus = twitchChannel->getStreamStatus(); + const auto streamStatus = twitchChannel->accessStreamStatus(); - if (streamStatus.live) { + if (streamStatus->live) { this->isLive_ = true; this->tooltip_ = "" "

" + - streamStatus.title + "

" + streamStatus.game + "
" + - (streamStatus.rerun ? "Vod-casting" : "Live") + " for " + - streamStatus.uptime + " with " + - QString::number(streamStatus.viewerCount) + + streamStatus->title + "

" + streamStatus->game + "
" + + (streamStatus->rerun ? "Vod-casting" : "Live") + " for " + + streamStatus->uptime + " with " + + QString::number(streamStatus->viewerCount) + " viewers" "

"; - if (streamStatus.rerun) { + if (streamStatus->rerun) { title += " (rerun)"; - } else if (streamStatus.streamType.isEmpty()) { - title += " (" + streamStatus.streamType + ")"; + } else if (streamStatus->streamType.isEmpty()) { + title += " (" + streamStatus->streamType + ")"; } else { title += " (live)"; } From f33cc884b2761dd9d1337a77c6ab858cd81f361a Mon Sep 17 00:00:00 2001 From: apa420 <17131426+apa420@users.noreply.github.com> Date: Mon, 16 Jul 2018 17:09:29 +0200 Subject: [PATCH 33/73] Fixed issue with Excluded users not saving from the highlights tab (#624) --- src/controllers/highlights/HighlightController.cpp | 7 +++++++ src/controllers/highlights/HighlightController.hpp | 3 ++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/controllers/highlights/HighlightController.cpp b/src/controllers/highlights/HighlightController.cpp index 02c420c79..04aac6603 100644 --- a/src/controllers/highlights/HighlightController.cpp +++ b/src/controllers/highlights/HighlightController.cpp @@ -24,6 +24,13 @@ void HighlightController::initialize(Application &app) this->phrases.delayedItemsChanged.connect([this] { // this->highlightsSetting_.setValue(this->phrases.getVector()); }); + + for (const HighlightBlacklistUser &blacklistedUser : this->blacklistSetting_.getValue()) { + this->blacklistedUsers.appendItem(blacklistedUser); + } + + this->blacklistedUsers.delayedItemsChanged.connect( + [this] { this->blacklistSetting_.setValue(this->blacklistedUsers.getVector()); }); } HighlightModel *HighlightController::createModel(QObject *parent) diff --git a/src/controllers/highlights/HighlightController.hpp b/src/controllers/highlights/HighlightController.hpp index a881499cf..80a1f7564 100644 --- a/src/controllers/highlights/HighlightController.hpp +++ b/src/controllers/highlights/HighlightController.hpp @@ -39,7 +39,8 @@ private: ChatterinoSetting> highlightsSetting_ = { "/highlighting/highlights"}; - ChatterinoSetting> blacklistSetting_ = {"/highlighting/blacklist"}; + ChatterinoSetting> blacklistSetting_ = { + "/highlighting/blacklist"}; }; } // namespace chatterino From 3b3c5d8d7513637b3bc459c736e73d390fbb1ff7 Mon Sep 17 00:00:00 2001 From: fourtf Date: Mon, 16 Jul 2018 17:23:41 +0200 Subject: [PATCH 34/73] added code to handle a single connection --- src/Application.hpp | 4 +- src/common/NetworkData.cpp | 11 ++++++ src/common/NetworkData.hpp | 3 ++ src/common/NetworkRequest.cpp | 32 ++++++++-------- .../commands/CommandController.cpp | 2 +- src/providers/irc/AbstractIrcServer.cpp | 38 +++++++++---------- src/providers/irc/AbstractIrcServer.hpp | 5 +-- src/providers/irc/IrcConnection2.cpp | 17 ++++++--- src/providers/twitch/TwitchChannel.cpp | 7 ++-- src/providers/twitch/TwitchChannel.hpp | 3 +- src/providers/twitch/TwitchServer.cpp | 19 +++++++--- src/providers/twitch/TwitchServer.hpp | 21 ++++++---- src/singletons/Settings.hpp | 2 + src/util/DebugCount.cpp | 3 +- src/util/DebugCount.hpp | 25 ++++++------ src/widgets/helper/DebugPopup.cpp | 2 +- src/widgets/helper/DebugPopup.hpp | 2 +- src/widgets/settingspages/FeelPage.cpp | 2 + 18 files changed, 114 insertions(+), 84 deletions(-) diff --git a/src/Application.hpp b/src/Application.hpp index 8fda2ebba..558ae7b73 100644 --- a/src/Application.hpp +++ b/src/Application.hpp @@ -46,8 +46,8 @@ public: friend void test(); - [[deprecated("use getSettings() instead")]] Settings *settings = nullptr; - [[deprecated("use getPaths() instead")]] Paths *paths = nullptr; + Settings *settings = nullptr; + Paths *paths = nullptr; Theme *themes = nullptr; WindowManager *windows = nullptr; diff --git a/src/common/NetworkData.cpp b/src/common/NetworkData.cpp index 878bda409..12aec944d 100644 --- a/src/common/NetworkData.cpp +++ b/src/common/NetworkData.cpp @@ -2,12 +2,23 @@ #include "Application.hpp" #include "singletons/Paths.hpp" +#include "util/DebugCount.hpp" #include #include namespace chatterino { +NetworkData::NetworkData() +{ + DebugCount::increase("NetworkData"); +} + +NetworkData::~NetworkData() +{ + DebugCount::decrease("NetworkData"); +} + QString NetworkData::getHash() { if (this->hash_.isEmpty()) { diff --git a/src/common/NetworkData.hpp b/src/common/NetworkData.hpp index d3cecc046..d7fbf84ba 100644 --- a/src/common/NetworkData.hpp +++ b/src/common/NetworkData.hpp @@ -13,6 +13,9 @@ namespace chatterino { class NetworkResult; struct NetworkData { + NetworkData(); + ~NetworkData(); + QNetworkRequest request_; const QObject *caller_ = nullptr; bool useQuickLoadCache_{}; diff --git a/src/common/NetworkRequest.cpp b/src/common/NetworkRequest.cpp index 62929fe2a..69c636128 100644 --- a/src/common/NetworkRequest.cpp +++ b/src/common/NetworkRequest.cpp @@ -167,20 +167,21 @@ void NetworkRequest::doRequest() this->timer->start(); auto onUrlRequested = [data = this->data, timer = this->timer, worker]() mutable { - QNetworkReply *reply = nullptr; - switch (data->requestType_) { - case NetworkRequestType::Get: { - reply = NetworkManager::NaM.get(data->request_); - } break; + auto reply = [&]() -> QNetworkReply * { + switch (data->requestType_) { + case NetworkRequestType::Get: + return NetworkManager::NaM.get(data->request_); - case NetworkRequestType::Put: { - reply = NetworkManager::NaM.put(data->request_, data->payload_); - } break; + case NetworkRequestType::Put: + return NetworkManager::NaM.put(data->request_, data->payload_); - case NetworkRequestType::Delete: { - reply = NetworkManager::NaM.deleteResource(data->request_); - } break; - } + case NetworkRequestType::Delete: + return NetworkManager::NaM.deleteResource(data->request_); + + default: + return nullptr; + } + }(); if (reply == nullptr) { Log("Unhandled request type"); @@ -201,8 +202,6 @@ void NetworkRequest::doRequest() data->onReplyCreated_(reply); } - bool directAction = (data->caller_ == nullptr); - auto handleReply = [data, timer, reply]() mutable { // TODO(pajlada): A reply was received, kill the timeout timer if (reply->error() != QNetworkReply::NetworkError::NoError) { @@ -222,8 +221,7 @@ void NetworkRequest::doRequest() }; if (data->caller_ != nullptr) { - QObject::connect(worker, &NetworkWorker::doneUrl, data->caller_, - std::move(handleReply)); + QObject::connect(worker, &NetworkWorker::doneUrl, data->caller_, handleReply); QObject::connect(reply, &QNetworkReply::finished, worker, [worker]() mutable { emit worker->doneUrl(); @@ -231,7 +229,7 @@ void NetworkRequest::doRequest() }); } else { QObject::connect(reply, &QNetworkReply::finished, worker, - [handleReply = std::move(handleReply), worker]() mutable { + [handleReply, worker]() mutable { handleReply(); delete worker; diff --git a/src/controllers/commands/CommandController.cpp b/src/controllers/commands/CommandController.cpp index c558ec376..47c4e25db 100644 --- a/src/controllers/commands/CommandController.cpp +++ b/src/controllers/commands/CommandController.cpp @@ -140,7 +140,7 @@ QString CommandController::execCommand(const QString &text, ChannelPtr channel, app->twitch.server->whispersChannel->addMessage(b.getMessage()); - app->twitch.server->getWriteConnection()->sendRaw("PRIVMSG #jtv :" + text + "\r\n"); + app->twitch.server->sendMessage("jtv", text); if (getSettings()->inlineWhispers) { app->twitch.server->forEachChannel( diff --git a/src/providers/irc/AbstractIrcServer.cpp b/src/providers/irc/AbstractIrcServer.cpp index 592ce4ff1..fcbd306ae 100644 --- a/src/providers/irc/AbstractIrcServer.cpp +++ b/src/providers/irc/AbstractIrcServer.cpp @@ -35,26 +35,18 @@ AbstractIrcServer::AbstractIrcServer() // this->writeConnection->reconnectRequested.connect([this] { this->connect(); }); } -IrcConnection *AbstractIrcServer::getReadConnection() const -{ - return this->readConnection_.get(); -} - -IrcConnection *AbstractIrcServer::getWriteConnection() const -{ - return this->writeConnection_.get(); -} - void AbstractIrcServer::connect() { this->disconnect(); - // if (this->hasSeparateWriteConnection()) { - this->initializeConnection(this->writeConnection_.get(), false, true); - this->initializeConnection(this->readConnection_.get(), true, false); - // } else { - // this->initializeConnection(this->readConnection.get(), true, true); - // } + bool separateWriteConnection = this->hasSeparateWriteConnection(); + + if (separateWriteConnection) { + this->initializeConnection(this->writeConnection_.get(), false, true); + this->initializeConnection(this->readConnection_.get(), true, false); + } else { + this->initializeConnection(this->readConnection_.get(), true, true); + } // fourtf: this should be asynchronous { @@ -67,7 +59,6 @@ void AbstractIrcServer::connect() continue; } - this->writeConnection_->sendRaw("JOIN #" + chan->name); this->readConnection_->sendRaw("JOIN #" + chan->name); } @@ -88,13 +79,18 @@ void AbstractIrcServer::disconnect() } void AbstractIrcServer::sendMessage(const QString &channelName, const QString &message) +{ + this->sendRawMessage("PRIVMSG #" + channelName + " :" + message); +} + +void AbstractIrcServer::sendRawMessage(const QString &rawMessage) { std::lock_guard locker(this->connectionMutex_); - // fourtf: trim the message if it's sent from twitch chat - - if (this->writeConnection_) { - this->writeConnection_->sendRaw("PRIVMSG #" + channelName + " :" + message); + if (this->hasSeparateWriteConnection()) { + this->writeConnection_->sendRaw(rawMessage); + } else { + this->readConnection_->sendRaw(rawMessage); } } diff --git a/src/providers/irc/AbstractIrcServer.hpp b/src/providers/irc/AbstractIrcServer.hpp index 9e2723c39..734be1d87 100644 --- a/src/providers/irc/AbstractIrcServer.hpp +++ b/src/providers/irc/AbstractIrcServer.hpp @@ -17,13 +17,11 @@ public: virtual ~AbstractIrcServer() = default; // connection - IrcConnection *getReadConnection() const; - IrcConnection *getWriteConnection() const; - void connect(); void disconnect(); void sendMessage(const QString &channelName, const QString &message); + void sendRawMessage(const QString &rawMessage); // channels std::shared_ptr getOrAddChannel(const QString &dirtyChannelName); @@ -54,6 +52,7 @@ protected: virtual std::shared_ptr getCustomChannel(const QString &channelName); + virtual bool hasSeparateWriteConnection() const = 0; virtual QString cleanChannelName(const QString &dirtyChannelName); QMap> channels; diff --git a/src/providers/irc/IrcConnection2.cpp b/src/providers/irc/IrcConnection2.cpp index 82d0d1797..b1d28c20b 100644 --- a/src/providers/irc/IrcConnection2.cpp +++ b/src/providers/irc/IrcConnection2.cpp @@ -9,18 +9,23 @@ IrcConnection::IrcConnection(QObject *parent) this->pingTimer_.setInterval(5000); this->pingTimer_.start(); QObject::connect(&this->pingTimer_, &QTimer::timeout, [this] { - if (!this->recentlyReceivedMessage_.load()) { - this->sendRaw("PING"); - this->reconnectTimer_.start(); + if (this->isConnected()) { + if (!this->recentlyReceivedMessage_.load()) { + this->sendRaw("PING"); + this->reconnectTimer_.start(); + } + this->recentlyReceivedMessage_ = false; } - this->recentlyReceivedMessage_ = false; }); // reconnect after x seconds without receiving a message this->reconnectTimer_.setInterval(5000); this->reconnectTimer_.setSingleShot(true); - QObject::connect(&this->reconnectTimer_, &QTimer::timeout, - [this] { reconnectRequested.invoke(); }); + QObject::connect(&this->reconnectTimer_, &QTimer::timeout, [this] { + if (this->isConnected()) { + reconnectRequested.invoke(); + } + }); QObject::connect(this, &Communi::IrcConnection::messageReceived, [this](Communi::IrcMessage *) { this->recentlyReceivedMessage_ = true; diff --git a/src/providers/twitch/TwitchChannel.cpp b/src/providers/twitch/TwitchChannel.cpp index 97af4d729..6a77a3e89 100644 --- a/src/providers/twitch/TwitchChannel.cpp +++ b/src/providers/twitch/TwitchChannel.cpp @@ -19,7 +19,7 @@ namespace chatterino { -TwitchChannel::TwitchChannel(const QString &channelName, Communi::IrcConnection *readConnection) +TwitchChannel::TwitchChannel(const QString &channelName) : Channel(channelName, Channel::Type::Twitch) , bttvEmotes_(new EmoteMap) , ffzEmotes_(new EmoteMap) @@ -27,7 +27,6 @@ TwitchChannel::TwitchChannel(const QString &channelName, Communi::IrcConnection , channelUrl_("https://twitch.tv/" + name) , popoutPlayerUrl_("https://player.twitch.tv/?channel=" + name) , mod_(false) - , readConnection_(readConnection) { Log("[TwitchChannel:{}] Opened", this->name); @@ -420,7 +419,9 @@ bool TwitchChannel::parseRecentMessages(const QJsonObject &jsonRoot) for (const auto jsonMessage : jsonMessages) { auto content = jsonMessage.toString().toUtf8(); - auto message = Communi::IrcMessage::fromData(content, this->readConnection_); + // passing nullptr as the channel makes the message invalid but we don't check for that + // anyways + auto message = Communi::IrcMessage::fromData(content, nullptr); auto privMsg = dynamic_cast(message); assert(privMsg); diff --git a/src/providers/twitch/TwitchChannel.hpp b/src/providers/twitch/TwitchChannel.hpp index b46b59f9f..c2986029d 100644 --- a/src/providers/twitch/TwitchChannel.hpp +++ b/src/providers/twitch/TwitchChannel.hpp @@ -86,7 +86,7 @@ private: QString localizedName; }; - explicit TwitchChannel(const QString &channelName, Communi::IrcConnection *readConnection); + explicit TwitchChannel(const QString &channelName); // Methods void refreshLiveStatus(); @@ -124,7 +124,6 @@ private: QObject lifetimeGuard_; QTimer liveStatusTimer_; QTimer chattersListTimer_; - Communi::IrcConnection *readConnection_ = nullptr; friend class TwitchServer; }; diff --git a/src/providers/twitch/TwitchServer.cpp b/src/providers/twitch/TwitchServer.cpp index 39df1cc64..8649a955f 100644 --- a/src/providers/twitch/TwitchServer.cpp +++ b/src/providers/twitch/TwitchServer.cpp @@ -28,6 +28,9 @@ TwitchServer::TwitchServer() qDebug() << "init TwitchServer"; this->pubsub = new PubSub; + + getSettings()->twitchSeperateWriteConnection.connect([this](auto, auto) { this->connect(); }, + this->signalHolder_, false); } void TwitchServer::initialize(Application &app) @@ -42,12 +45,13 @@ void TwitchServer::initializeConnection(IrcConnection *connection, bool isRead, { assert(this->app); + this->singleConnection_ = isRead == isWrite; + std::shared_ptr account = getApp()->accounts->twitch.getCurrent(); qDebug() << "logging in as" << account->getUserName(); QString username = account->getUserName(); - // QString oauthClient = account->getOAuthClient(); QString oauthToken = account->getOAuthToken(); if (!oauthToken.startsWith("oauth:")) { @@ -60,9 +64,6 @@ void TwitchServer::initializeConnection(IrcConnection *connection, bool isRead, if (!account->isAnon()) { connection->setPassword(oauthToken); - - // fourtf: ignored users - // this->refreshIgnoredUsers(username, oauthClient, oauthToken); } connection->sendCommand(Communi::IrcCommand::createCapability("REQ", "twitch.tv/membership")); @@ -75,7 +76,7 @@ void TwitchServer::initializeConnection(IrcConnection *connection, bool isRead, std::shared_ptr TwitchServer::createChannel(const QString &channelName) { - TwitchChannel *channel = new TwitchChannel(channelName, this->getReadConnection()); + TwitchChannel *channel = new TwitchChannel(channelName); channel->sendMessageSignal.connect([this, channel](auto &chan, auto &msg, bool &sent) { this->onMessageSendRequested(channel, msg, sent); @@ -91,6 +92,8 @@ void TwitchServer::privateMessageReceived(Communi::IrcPrivateMessage *message) void TwitchServer::messageReceived(Communi::IrcMessage *message) { + qDebug() << message->toData(); + // this->readConnection if (message->type() == Communi::IrcMessage::Type::Private) { // We already have a handler for private messages @@ -179,6 +182,12 @@ QString TwitchServer::cleanChannelName(const QString &dirtyChannelName) return dirtyChannelName.toLower(); } +bool TwitchServer::hasSeparateWriteConnection() const +{ + return true; + // return getSettings()->twitchSeperateWriteConnection; +} + void TwitchServer::onMessageSendRequested(TwitchChannel *channel, const QString &message, bool &sent) { diff --git a/src/providers/twitch/TwitchServer.hpp b/src/providers/twitch/TwitchServer.hpp index 4587d0d87..97f0859fe 100644 --- a/src/providers/twitch/TwitchServer.hpp +++ b/src/providers/twitch/TwitchServer.hpp @@ -22,7 +22,6 @@ public: virtual void initialize(Application &app) override; - // fourtf: ugh void forEachChannelAndSpecialChannels(std::function func); std::shared_ptr getChannelOrEmptyByID(const QString &channelID); @@ -36,16 +35,18 @@ public: PubSub *pubsub; protected: - void initializeConnection(IrcConnection *connection, bool isRead, bool isWrite) override; - std::shared_ptr createChannel(const QString &channelName) override; + virtual void initializeConnection(IrcConnection *connection, bool isRead, + bool isWrite) override; + virtual std::shared_ptr createChannel(const QString &channelName) override; - void privateMessageReceived(Communi::IrcPrivateMessage *message) override; - void messageReceived(Communi::IrcMessage *message) override; - void writeConnectionMessageReceived(Communi::IrcMessage *message) override; + virtual void privateMessageReceived(Communi::IrcPrivateMessage *message) override; + virtual void messageReceived(Communi::IrcMessage *message) override; + virtual void writeConnectionMessageReceived(Communi::IrcMessage *message) override; - std::shared_ptr getCustomChannel(const QString &channelname) override; + virtual std::shared_ptr getCustomChannel(const QString &channelname) override; - QString cleanChannelName(const QString &dirtyChannelName) override; + virtual QString cleanChannelName(const QString &dirtyChannelName) override; + virtual bool hasSeparateWriteConnection() const override; private: void onMessageSendRequested(TwitchChannel *channel, const QString &message, bool &sent); @@ -57,6 +58,10 @@ private: std::queue lastMessageMod_; std::chrono::steady_clock::time_point lastErrorTimeSpeed_; std::chrono::steady_clock::time_point lastErrorTimeAmount_; + + bool singleConnection_ = false; + + pajlada::Signals::SignalHolder signalHolder_; }; } // namespace chatterino diff --git a/src/singletons/Settings.hpp b/src/singletons/Settings.hpp index 1eda0e815..a785df395 100644 --- a/src/singletons/Settings.hpp +++ b/src/singletons/Settings.hpp @@ -52,6 +52,8 @@ public: BoolSetting showJoins = {"/behaviour/showJoins", false}; BoolSetting showParts = {"/behaviour/showParts", false}; FloatSetting mouseScrollMultiplier = {"/behaviour/mouseScrollMultiplier", 1.0}; + // BoolSetting twitchSeperateWriteConnection = {"/behaviour/twitchSeperateWriteConnection", + // false}; // Auto-completion BoolSetting onlyFetchChattersForSmallerStreamers = { diff --git a/src/util/DebugCount.cpp b/src/util/DebugCount.cpp index 1f6d28a74..81be6250b 100644 --- a/src/util/DebugCount.cpp +++ b/src/util/DebugCount.cpp @@ -2,7 +2,6 @@ namespace chatterino { -QMap DebugCount::counts_; -std::mutex DebugCount::mut_; +UniqueAccess> DebugCount::counts_; } // namespace chatterino diff --git a/src/util/DebugCount.hpp b/src/util/DebugCount.hpp index 7e78641d1..46bfb83a1 100644 --- a/src/util/DebugCount.hpp +++ b/src/util/DebugCount.hpp @@ -1,5 +1,7 @@ #pragma once +#include + #include #include @@ -13,11 +15,11 @@ class DebugCount public: static void increase(const QString &name) { - std::lock_guard lock(mut_); + auto counts = counts_.access(); - auto it = counts_.find(name); - if (it == counts_.end()) { - counts_.insert(name, 1); + auto it = counts->find(name); + if (it == counts->end()) { + counts->insert(name, 1); } else { reinterpret_cast(it.value())++; } @@ -25,11 +27,11 @@ public: static void decrease(const QString &name) { - std::lock_guard lock(mut_); + auto counts = counts_.access(); - auto it = counts_.find(name); - if (it == counts_.end()) { - counts_.insert(name, -1); + auto it = counts->find(name); + if (it == counts->end()) { + counts->insert(name, -1); } else { reinterpret_cast(it.value())--; } @@ -37,10 +39,10 @@ public: static QString getDebugText() { - std::lock_guard lock(mut_); + auto counts = counts_.access(); QString text; - for (auto it = counts_.begin(); it != counts_.end(); it++) { + for (auto it = counts->begin(); it != counts->end(); it++) { text += it.key() + ": " + QString::number(it.value()) + "\n"; } return text; @@ -52,8 +54,7 @@ public: } private: - static QMap counts_; - static std::mutex mut_; + static UniqueAccess> counts_; }; } // namespace chatterino diff --git a/src/widgets/helper/DebugPopup.cpp b/src/widgets/helper/DebugPopup.cpp index 31162c2eb..c3bb13801 100644 --- a/src/widgets/helper/DebugPopup.cpp +++ b/src/widgets/helper/DebugPopup.cpp @@ -15,7 +15,7 @@ DebugPopup::DebugPopup() auto *text = new QLabel(this); auto *timer = new QTimer(this); - timer->setInterval(1000); + timer->setInterval(300); QObject::connect(timer, &QTimer::timeout, [text] { text->setText(DebugCount::getDebugText()); }); timer->start(); diff --git a/src/widgets/helper/DebugPopup.hpp b/src/widgets/helper/DebugPopup.hpp index b70c97d57..ec0c2bcdd 100644 --- a/src/widgets/helper/DebugPopup.hpp +++ b/src/widgets/helper/DebugPopup.hpp @@ -1,6 +1,6 @@ #pragma once -#include +#include namespace chatterino { diff --git a/src/widgets/settingspages/FeelPage.cpp b/src/widgets/settingspages/FeelPage.cpp index 455eca9f8..83e86f2f8 100644 --- a/src/widgets/settingspages/FeelPage.cpp +++ b/src/widgets/settingspages/FeelPage.cpp @@ -24,6 +24,8 @@ FeelPage::FeelPage() auto layout = layoutCreator.setLayoutType(); + // layout.append(this->createCheckBox("Use a seperate write connection.", + // getSettings()->twitchSeperateWriteConnection)); layout.append(this->createCheckBox(SCROLL_SMOOTH, getSettings()->enableSmoothScrolling)); layout.append( this->createCheckBox(SCROLL_NEWMSG, getSettings()->enableSmoothScrollingNewMessages)); From f0609076784a33647f30f042837a8d940843993f Mon Sep 17 00:00:00 2001 From: DatGuy1 Date: Mon, 23 Jul 2018 15:50:09 +0300 Subject: [PATCH 35/73] Add channel badges and emotes to LogsPopup (#576) * Add channel badges and emotes to LogsPopup * Move roomID stuff to seperate function * Use onSuccess --- src/widgets/dialogs/LogsPopup.cpp | 39 +++++++++++++++++++++++++++---- src/widgets/dialogs/LogsPopup.hpp | 12 ++++++---- 2 files changed, 41 insertions(+), 10 deletions(-) diff --git a/src/widgets/dialogs/LogsPopup.cpp b/src/widgets/dialogs/LogsPopup.cpp index a2afb9301..288f37acb 100644 --- a/src/widgets/dialogs/LogsPopup.cpp +++ b/src/widgets/dialogs/LogsPopup.cpp @@ -35,7 +35,7 @@ void LogsPopup::setInfo(ChannelPtr channel, QString userName) this->channel_ = channel; this->userName_ = userName; this->setWindowTitle(this->userName_ + "'s logs in #" + this->channel_->name); - this->getLogviewerLogs(); + this->getRoomID(); } void LogsPopup::setMessages(std::vector &messages) @@ -46,6 +46,35 @@ void LogsPopup::setMessages(std::vector &messages) this->channelView_->setChannel(logsChannel); } +void LogsPopup::getRoomID() +{ + TwitchChannel *twitchChannel = dynamic_cast(this->channel_.get()); + if (twitchChannel == nullptr) { + return; + } + + QString channelName = twitchChannel->name; + + QString url = QString("https://cbenni.com/api/channel/%1").arg(channelName); + + NetworkRequest req(url); + req.setCaller(QThread::currentThread()); + + req.onError([this](int errorCode) { + this->getOverrustleLogs(); + return true; + }); + + req.onSuccess([this, channelName](auto result) { + auto data = result.parseJson(); + this->roomID_ = data.value("channel")["id"].toInt(); + this->getLogviewerLogs(); + return true; + }); + + req.execute(); +} + void LogsPopup::getLogviewerLogs() { TwitchChannel *twitchChannel = dynamic_cast(this->channel_.get()); @@ -55,8 +84,8 @@ void LogsPopup::getLogviewerLogs() QString channelName = twitchChannel->name; - QString url = QString("https://cbenni.com/api/logs/%1/?nick=%2&before=500") - .arg(channelName, this->userName_); + auto url = QString("https://cbenni.com/api/logs/%1/?nick=%2&before=500") + .arg(channelName, this->userName_); NetworkRequest req(url); req.setCaller(QThread::currentThread()); @@ -69,7 +98,6 @@ void LogsPopup::getLogviewerLogs() req.onSuccess([this, channelName](auto result) { auto data = result.parseJson(); std::vector messages; - ChannelPtr logsChannel(new Channel("logs", Channel::Type::None)); QJsonValue before = data.value("before"); @@ -80,11 +108,12 @@ void LogsPopup::getLogviewerLogs() // Hacky way to fix the timestamp message.insert(1, "historical=1;"); message.insert(1, QString("tmi-sent-ts=%10000;").arg(messageObject["time"].toInt())); + message.insert(1, QString("room-id=%1;").arg(this->roomID_)); MessageParseArgs args; auto ircMessage = Communi::IrcMessage::fromData(message.toUtf8(), nullptr); auto privMsg = static_cast(ircMessage); - TwitchMessageBuilder builder(logsChannel.get(), privMsg, args); + TwitchMessageBuilder builder(this->channel_.get(), privMsg, args); messages.push_back(builder.build()); }; this->setMessages(messages); diff --git a/src/widgets/dialogs/LogsPopup.hpp b/src/widgets/dialogs/LogsPopup.hpp index 90c7f9b0c..5ec39102e 100644 --- a/src/widgets/dialogs/LogsPopup.hpp +++ b/src/widgets/dialogs/LogsPopup.hpp @@ -16,15 +16,17 @@ public: void setInfo(std::shared_ptr channel, QString userName); private: - void initLayout(); - void setMessages(std::vector &messages); - void getOverrustleLogs(); - void getLogviewerLogs(); - ChannelView *channelView_ = nullptr; ChannelPtr channel_ = Channel::getEmpty(); QString userName_; + int roomID_; + + void initLayout(); + void setMessages(std::vector &messages); + void getRoomID(); + void getOverrustleLogs(); + void getLogviewerLogs(); }; } // namespace chatterino From 133bd6a766ea7b299882b70228f217ae5931dd6b Mon Sep 17 00:00:00 2001 From: apa420 <17131426+apa420@users.noreply.github.com> Date: Mon, 23 Jul 2018 07:12:14 -0600 Subject: [PATCH 36/73] fixed (live) issue #626 (#631) --- src/providers/twitch/TwitchChannel.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/providers/twitch/TwitchChannel.cpp b/src/providers/twitch/TwitchChannel.cpp index 97af4d729..08f0229ec 100644 --- a/src/providers/twitch/TwitchChannel.cpp +++ b/src/providers/twitch/TwitchChannel.cpp @@ -302,18 +302,18 @@ void TwitchChannel::refreshLiveStatus() //<<<<<<< HEAD // auto request = makeGetStreamRequest(roomID, QThread::currentThread()); //======= - // auto request = NetworkRequest::twitchRequest(url); - // request.setCaller(QThread::currentThread()); + auto request = NetworkRequest::twitchRequest(url); + request.setCaller(QThread::currentThread()); //>>>>>>> 9bfbdefd2f0972a738230d5b95a009f73b1dd933 - // request.onSuccess([this, weak = this->weak_from_this()](auto result) { - // ChannelPtr shared = weak.lock(); - // if (!shared) return false; + request.onSuccess([this, weak = this->weak_from_this()](auto result) { + ChannelPtr shared = weak.lock(); + if (!shared) return false; - // return this->parseLiveStatus(result.parseRapidJson()); - // }); + return this->parseLiveStatus(result.parseRapidJson()); + }); - // request.execute(); + request.execute(); } bool TwitchChannel::parseLiveStatus(const rapidjson::Document &document) From 7cdfa173c700fbaed2d513292897c6e588362cd0 Mon Sep 17 00:00:00 2001 From: Rasmus Karlsson Date: Sat, 28 Jul 2018 11:20:18 +0000 Subject: [PATCH 37/73] enable external link opening in attributions Fix #641 --- src/widgets/settingspages/AboutPage.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/widgets/settingspages/AboutPage.cpp b/src/widgets/settingspages/AboutPage.cpp index 8cf9059a3..b0778c371 100644 --- a/src/widgets/settingspages/AboutPage.cpp +++ b/src/widgets/settingspages/AboutPage.cpp @@ -97,13 +97,13 @@ AboutPage::AboutPage() auto l = attributions.emplace(); // clang-format off - l.emplace("EmojiOne 2 and 3 emojis provided by EmojiOne"); - l.emplace("Twemoji emojis provided by Twitter's Twemoji"); - l.emplace("Facebook emojis provided by Facebook"); - l.emplace("Apple emojis provided by Apple"); - l.emplace("Google emojis provided by Google"); - l.emplace("Messenger emojis provided by Facebook"); - l.emplace("Emoji datasource provided by Cal Henderson"); + l.emplace("EmojiOne 2 and 3 emojis provided by EmojiOne")->setOpenExternalLinks(true); + l.emplace("Twemoji emojis provided by Twitter's Twemoji")->setOpenExternalLinks(true); + l.emplace("Facebook emojis provided by Facebook")->setOpenExternalLinks(true); + l.emplace("Apple emojis provided by Apple")->setOpenExternalLinks(true); + l.emplace("Google emojis provided by Google")->setOpenExternalLinks(true); + l.emplace("Messenger emojis provided by Facebook")->setOpenExternalLinks(true); + l.emplace("Emoji datasource provided by Cal Henderson")->setOpenExternalLinks(true); // clang-format on } From b220ac765a16f2efc85f313985705c827c927778 Mon Sep 17 00:00:00 2001 From: pajlada Date: Sat, 28 Jul 2018 20:50:23 +0200 Subject: [PATCH 38/73] Linux GCC and Clang Jenkins build (#643) * 1 * 1 * 1 * 1 * 1 * 1 * 1 * 1 * 1 --- Jenkinsfile | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 Jenkinsfile diff --git a/Jenkinsfile b/Jenkinsfile new file mode 100644 index 000000000..16780a908 --- /dev/null +++ b/Jenkinsfile @@ -0,0 +1,20 @@ +pipeline { + agent any + + stages { + stage('Build') { + parallel { + stage('GCC') { + steps { + sh 'mkdir -p build-linux-gcc && cd build-linux-gcc && qmake .. && make' + } + } + stage('Clang') { + steps { + sh 'mkdir -p build-linux-clang && cd build-linux-clang && qmake -spec linux-clang .. && make' + } + } + } + } + } +} From 5b64e9573b7699bf325b62f1335f757f19dbe5bd Mon Sep 17 00:00:00 2001 From: Daniel Pasch Date: Wed, 1 Aug 2018 21:26:11 +0200 Subject: [PATCH 39/73] mac docs update relating to qt (#651) give a little more detailed installation guide for the qt installation --- BUILDING_ON_MAC.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/BUILDING_ON_MAC.md b/BUILDING_ON_MAC.md index 4c2421015..c09d97d24 100644 --- a/BUILDING_ON_MAC.md +++ b/BUILDING_ON_MAC.md @@ -3,7 +3,9 @@ 1. Install Xcode and Xcode Command Line Utilites 2. Start Xcode, settings -> Locations, activate your Command Line Tools 3. Install brew https://brew.sh/ -4. `brew install boost openssl rapidjson qt` +4. `brew install boost openssl rapidjson` +5. `brew install qt` +6. Step 5 should output some directions to add qt to your path, you will need to do this for qmake 5. Go into project directory 6. Create build folder `mkdir build && cd build` 7. `qmake .. && make` From c2e2dfb577196b74d85d9055f24914ffdfc53b45 Mon Sep 17 00:00:00 2001 From: fourtf Date: Thu, 2 Aug 2018 14:23:27 +0200 Subject: [PATCH 40/73] this commit is too big --- chatterino.pro | 51 +- .../_generate_resources.cpython-36.pyc | Bin 0 -> 622 bytes resources/_generate_resources.py | 38 ++ .../button_ban.png => buttons/ban.png} | Bin .../buttons/ban.png => buttons/banRed.png} | Bin resources/{images => buttons}/emote.svg | 0 .../emote_dark.svg => buttons/emoteDark.svg} | 0 .../menu_black.png => buttons/menuDark.png} | Bin .../menu_white.png => buttons/menuLight.png} | Bin resources/{images => }/buttons/mod.png | Bin .../modModeDisabled.png} | Bin .../modModeDisabled2.png} | Bin .../modModeEnabled.png} | Bin .../modModeEnabled2.png} | Bin .../timeout.png} | Bin resources/{images => }/buttons/unban.png | Bin resources/{images => }/buttons/unmod.png | Bin .../update.png} | Bin .../updateError.png} | Bin resources/{images => }/chatterino2.icns | Bin resources/error.png | Bin 0 -> 685 bytes resources/generate_resources.py | 61 +++ resources/{images => }/icon.png | Bin resources/images/AppearanceEditorPart_16x.png | Bin 347 -> 0 bytes resources/images/BrowserLink_16x.png | Bin 680 -> 0 bytes .../images/CopyLongTextToClipboard_16x.png | Bin 403 -> 0 bytes resources/images/CustomActionEditor_16x.png | Bin 368 -> 0 bytes resources/images/Emoji_Color_1F60A_19 old.png | Bin 1932 -> 0 bytes resources/images/Emoji_Color_1F60A_19.png | Bin 660 -> 0 bytes resources/images/Filter_16x.png | Bin 280 -> 0 bytes resources/images/Message_16xLG.png | Bin 179 -> 0 bytes .../StatusAnnotations_Blocked_16xLG_color.png | Bin 1618 -> 0 bytes resources/images/UserProfile_22x.png | Bin 286 -> 0 bytes resources/images/VSO_Link_blue_16x.png | Bin 480 -> 0 bytes resources/images/cheer100.png | Bin 360 -> 0 bytes resources/images/cheer1000.png | Bin 351 -> 0 bytes resources/images/cheer10000.png | Bin 330 -> 0 bytes resources/images/cheer100000.png | Bin 285 -> 0 bytes resources/images/cheer5000.png | Bin 362 -> 0 bytes resources/images/collapse.png | Bin 236 -> 0 bytes resources/images/format_Bold_16xLG.png | Bin 354 -> 0 bytes resources/images/settings.png | Bin 274 -> 0 bytes resources/images/tool_moreCollapser_off16.png | Bin 279 -> 0 bytes resources/{images => }/pajaDank.png | Bin resources/resources.qrc | 83 +-- resources/resources_autogenerated.qrc | 64 +++ resources/{images => settings}/about.svg | 0 resources/{images => settings}/aboutlogo.png | Bin resources/{images => settings}/accounts.svg | 0 resources/{images => settings}/behave.svg | 0 resources/{images => settings}/commands.svg | 0 .../{images => settings}/notifications.svg | 0 resources/{images => settings}/theme.svg | 0 .../split/splitdown.png => split/down.png} | Bin .../split/splitleft.png => split/left.png} | Bin .../split/splitmove.png => split/move.png} | Bin .../split/splitright.png => split/right.png} | Bin .../split/splitup.png => split/up.png} | Bin .../{images/admin_bg.png => twitch/admin.png} | Bin .../broadcaster.png} | Bin resources/{images => twitch}/cheer1.png | Bin .../globalmod_bg.png => twitch/globalmod.png} | Bin .../moderator_bg.png => twitch/moderator.png} | Bin .../twitchprime_bg.png => twitch/prime.png} | Bin .../{images/staff_bg.png => twitch/staff.png} | Bin resources/{images => twitch}/subscriber.png | Bin .../{images/turbo_bg.png => twitch/turbo.png} | Bin resources/{images => twitch}/verified.png | Bin src/Application.cpp | 128 ++--- src/Application.hpp | 70 +-- src/BrowserExtension.cpp | 88 +++ src/BrowserExtension.hpp | 10 + src/RunGui.cpp | 130 +++++ src/RunGui.hpp | 10 + src/autogenerated/ResourcesAutogen.cpp | 44 ++ src/autogenerated/ResourcesAutogen.hpp | 57 ++ src/common/Aliases.hpp | 34 ++ src/common/Channel.cpp | 15 +- src/common/Channel.hpp | 3 +- src/common/Common.hpp | 24 +- src/common/CompletionModel.cpp | 64 ++- src/common/CompletionModel.hpp | 2 + src/common/Emotemap.cpp | 64 +-- src/common/Emotemap.hpp | 26 +- src/common/NetworkCommon.hpp | 4 +- src/common/NetworkRequest.cpp | 10 +- src/common/NetworkRequest.hpp | 4 +- src/common/NetworkResult.cpp | 2 +- src/common/NetworkResult.hpp | 7 +- src/common/NullablePtr.hpp | 15 +- src/common/Outcome.hpp | 51 ++ src/common/Singleton.hpp | 10 +- src/common/UniqueAccess.hpp | 31 +- .../accounts/AccountController.cpp | 2 +- .../accounts/AccountController.hpp | 7 +- .../commands/CommandController.cpp | 10 +- .../commands/CommandController.hpp | 9 +- .../highlights/HighlightController.cpp | 2 +- .../highlights/HighlightController.hpp | 7 +- src/controllers/ignores/IgnoreController.cpp | 2 +- src/controllers/ignores/IgnoreController.hpp | 7 +- .../moderationactions/ModerationAction.cpp | 7 +- .../moderationactions/ModerationAction.hpp | 8 +- .../moderationactions/ModerationActions.cpp | 2 +- .../moderationactions/ModerationActions.hpp | 5 +- .../taggedusers/TaggedUsersController.hpp | 2 +- src/debug/Log.hpp | 7 + src/main.cpp | 221 +------- src/messages/Emote.cpp | 75 +++ src/messages/Emote.hpp | 65 +++ src/messages/EmoteCache.hpp | 93 ++++ src/messages/EmoteMap.cpp | 44 ++ src/messages/EmoteMap.hpp | 20 + src/messages/Image.cpp | 449 ++++++++------- src/messages/Image.hpp | 103 ++-- src/messages/ImageSet.cpp | 95 ++++ src/messages/ImageSet.hpp | 35 ++ src/messages/MessageElement.cpp | 56 +- src/messages/MessageElement.hpp | 13 +- src/messages/layouts/MessageLayoutElement.cpp | 34 +- src/messages/layouts/MessageLayoutElement.hpp | 6 +- src/providers/bttv/BttvEmotes.cpp | 155 +++--- src/providers/bttv/BttvEmotes.hpp | 29 +- src/providers/bttv/LoadBttvChannelEmote.cpp | 76 +++ src/providers/bttv/LoadBttvChannelEmote.hpp | 14 + src/providers/chatterino/ChatterinoBadges.cpp | 50 ++ src/providers/chatterino/ChatterinoBadges.hpp | 25 + src/providers/emoji/Emojis.cpp | 24 +- src/providers/emoji/Emojis.hpp | 9 +- src/providers/ffz/FfzEmotes.cpp | 206 ++++--- src/providers/ffz/FfzEmotes.hpp | 35 +- src/providers/irc/AbstractIrcServer.cpp | 7 +- src/providers/twitch/IrcMessageHandler.cpp | 2 - src/providers/twitch/PartialTwitchUser.cpp | 13 +- src/providers/twitch/TwitchAccount.cpp | 191 ++++++- src/providers/twitch/TwitchAccount.hpp | 33 +- src/providers/twitch/TwitchApi.cpp | 12 +- src/providers/twitch/TwitchBadges.cpp | 58 ++ src/providers/twitch/TwitchBadges.hpp | 26 + src/providers/twitch/TwitchChannel.cpp | 224 ++++++-- src/providers/twitch/TwitchChannel.hpp | 41 +- src/providers/twitch/TwitchEmotes.cpp | 236 ++------ src/providers/twitch/TwitchEmotes.hpp | 61 +-- src/providers/twitch/TwitchMessageBuilder.cpp | 512 +++++++++--------- src/providers/twitch/TwitchMessageBuilder.hpp | 12 +- .../twitch/TwitchParseCheerEmotes.cpp | 264 +++++++++ .../twitch/TwitchParseCheerEmotes.hpp | 36 ++ src/providers/twitch/TwitchServer.cpp | 14 +- src/providers/twitch/TwitchServer.hpp | 9 +- src/singletons/Badges.cpp | 9 + src/singletons/Badges.hpp | 15 + src/singletons/Emotes.cpp | 15 +- src/singletons/Emotes.hpp | 11 +- src/singletons/Fonts.cpp | 14 +- src/singletons/Fonts.hpp | 5 +- src/singletons/Logging.cpp | 2 +- src/singletons/Logging.hpp | 2 +- src/singletons/NativeMessaging.cpp | 109 ++-- src/singletons/NativeMessaging.hpp | 29 +- src/singletons/Paths.cpp | 18 +- src/singletons/Paths.hpp | 10 +- src/singletons/Resources.cpp | 480 ---------------- src/singletons/Resources.hpp | 159 +----- src/singletons/Settings.cpp | 25 +- src/singletons/Settings.hpp | 11 +- src/singletons/WindowManager.cpp | 24 +- src/singletons/WindowManager.hpp | 7 +- src/util/JsonQuery.cpp | 9 + src/util/JsonQuery.hpp | 12 + src/widgets/Notebook.cpp | 5 +- src/widgets/Window.cpp | 4 +- src/widgets/dialogs/EmotePopup.cpp | 38 +- src/widgets/dialogs/LogsPopup.cpp | 14 +- src/widgets/dialogs/SelectChannelDialog.cpp | 2 +- src/widgets/dialogs/UserInfoPopup.cpp | 6 +- src/widgets/helper/ChannelView.cpp | 125 ++--- src/widgets/helper/SearchPopup.cpp | 2 +- src/widgets/settingspages/AboutPage.cpp | 2 +- src/widgets/settingspages/LookPage.cpp | 24 +- src/widgets/splits/Split.cpp | 67 +-- src/widgets/splits/Split.hpp | 18 +- src/widgets/splits/SplitContainer.cpp | 14 +- src/widgets/splits/SplitHeader.cpp | 20 +- src/widgets/splits/SplitInput.cpp | 4 +- src/widgets/splits/SplitOverlay.cpp | 11 +- weakOf | 0 186 files changed, 3626 insertions(+), 2656 deletions(-) create mode 100644 resources/__pycache__/_generate_resources.cpython-36.pyc create mode 100644 resources/_generate_resources.py rename resources/{images/button_ban.png => buttons/ban.png} (100%) rename resources/{images/buttons/ban.png => buttons/banRed.png} (100%) rename resources/{images => buttons}/emote.svg (100%) rename resources/{images/emote_dark.svg => buttons/emoteDark.svg} (100%) rename resources/{images/menu_black.png => buttons/menuDark.png} (100%) rename resources/{images/menu_white.png => buttons/menuLight.png} (100%) rename resources/{images => }/buttons/mod.png (100%) rename resources/{images/moderatormode_disabled.png => buttons/modModeDisabled.png} (100%) rename resources/{images/moderatormode_disabled2.png => buttons/modModeDisabled2.png} (100%) rename resources/{images/moderatormode_enabled.png => buttons/modModeEnabled.png} (100%) rename resources/{images/moderatormode_enabled2.png => buttons/modModeEnabled2.png} (100%) rename resources/{images/button_timeout.png => buttons/timeout.png} (100%) rename resources/{images => }/buttons/unban.png (100%) rename resources/{images => }/buttons/unmod.png (100%) rename resources/{images/download_update.png => buttons/update.png} (100%) rename resources/{images/download_update_error.png => buttons/updateError.png} (100%) rename resources/{images => }/chatterino2.icns (100%) create mode 100644 resources/error.png create mode 100755 resources/generate_resources.py rename resources/{images => }/icon.png (100%) delete mode 100644 resources/images/AppearanceEditorPart_16x.png delete mode 100644 resources/images/BrowserLink_16x.png delete mode 100644 resources/images/CopyLongTextToClipboard_16x.png delete mode 100644 resources/images/CustomActionEditor_16x.png delete mode 100644 resources/images/Emoji_Color_1F60A_19 old.png delete mode 100644 resources/images/Emoji_Color_1F60A_19.png delete mode 100644 resources/images/Filter_16x.png delete mode 100644 resources/images/Message_16xLG.png delete mode 100644 resources/images/StatusAnnotations_Blocked_16xLG_color.png delete mode 100644 resources/images/UserProfile_22x.png delete mode 100644 resources/images/VSO_Link_blue_16x.png delete mode 100644 resources/images/cheer100.png delete mode 100644 resources/images/cheer1000.png delete mode 100644 resources/images/cheer10000.png delete mode 100644 resources/images/cheer100000.png delete mode 100644 resources/images/cheer5000.png delete mode 100644 resources/images/collapse.png delete mode 100644 resources/images/format_Bold_16xLG.png delete mode 100644 resources/images/settings.png delete mode 100644 resources/images/tool_moreCollapser_off16.png rename resources/{images => }/pajaDank.png (100%) create mode 100644 resources/resources_autogenerated.qrc rename resources/{images => settings}/about.svg (100%) rename resources/{images => settings}/aboutlogo.png (100%) rename resources/{images => settings}/accounts.svg (100%) rename resources/{images => settings}/behave.svg (100%) rename resources/{images => settings}/commands.svg (100%) rename resources/{images => settings}/notifications.svg (100%) rename resources/{images => settings}/theme.svg (100%) rename resources/{images/split/splitdown.png => split/down.png} (100%) rename resources/{images/split/splitleft.png => split/left.png} (100%) rename resources/{images/split/splitmove.png => split/move.png} (100%) rename resources/{images/split/splitright.png => split/right.png} (100%) rename resources/{images/split/splitup.png => split/up.png} (100%) rename resources/{images/admin_bg.png => twitch/admin.png} (100%) rename resources/{images/broadcaster_bg.png => twitch/broadcaster.png} (100%) rename resources/{images => twitch}/cheer1.png (100%) rename resources/{images/globalmod_bg.png => twitch/globalmod.png} (100%) rename resources/{images/moderator_bg.png => twitch/moderator.png} (100%) rename resources/{images/twitchprime_bg.png => twitch/prime.png} (100%) rename resources/{images/staff_bg.png => twitch/staff.png} (100%) rename resources/{images => twitch}/subscriber.png (100%) rename resources/{images/turbo_bg.png => twitch/turbo.png} (100%) rename resources/{images => twitch}/verified.png (100%) create mode 100644 src/BrowserExtension.cpp create mode 100644 src/BrowserExtension.hpp create mode 100644 src/RunGui.cpp create mode 100644 src/RunGui.hpp create mode 100644 src/autogenerated/ResourcesAutogen.cpp create mode 100644 src/autogenerated/ResourcesAutogen.hpp create mode 100644 src/common/Aliases.hpp create mode 100644 src/common/Outcome.hpp create mode 100644 src/messages/Emote.cpp create mode 100644 src/messages/Emote.hpp create mode 100644 src/messages/EmoteCache.hpp create mode 100644 src/messages/EmoteMap.cpp create mode 100644 src/messages/EmoteMap.hpp create mode 100644 src/messages/ImageSet.cpp create mode 100644 src/messages/ImageSet.hpp create mode 100644 src/providers/bttv/LoadBttvChannelEmote.cpp create mode 100644 src/providers/bttv/LoadBttvChannelEmote.hpp create mode 100644 src/providers/chatterino/ChatterinoBadges.cpp create mode 100644 src/providers/chatterino/ChatterinoBadges.hpp create mode 100644 src/providers/twitch/TwitchBadges.cpp create mode 100644 src/providers/twitch/TwitchBadges.hpp create mode 100644 src/providers/twitch/TwitchParseCheerEmotes.cpp create mode 100644 src/providers/twitch/TwitchParseCheerEmotes.hpp create mode 100644 src/singletons/Badges.cpp create mode 100644 src/singletons/Badges.hpp create mode 100644 src/util/JsonQuery.cpp create mode 100644 src/util/JsonQuery.hpp create mode 100644 weakOf diff --git a/chatterino.pro b/chatterino.pro index 4a1d00adc..8d477fc61 100644 --- a/chatterino.pro +++ b/chatterino.pro @@ -9,7 +9,6 @@ message(----) QT += widgets core gui network multimedia svg CONFIG += communi COMMUNI += core model util -CONFIG += c++14 INCLUDEPATH += src/ TARGET = chatterino TEMPLATE = app @@ -17,6 +16,16 @@ DEFINES += QT_DEPRECATED_WARNINGS PRECOMPILED_HEADER = src/PrecompiledHeader.hpp CONFIG += precompile_header +win32-msvc* { + QMAKE_CXXFLAGS = /std=c++17 +} else { + QMAKE_CXXFLAGS = -std=c++17 +} + +debug { + DEFINES += QT_DEBUG +} + useBreakpad { LIBS += -L$$PWD/lib/qBreakpad/handler/build include(lib/qBreakpad/qBreakpad.pri) @@ -132,9 +141,7 @@ SOURCES += \ src/messages/MessageBuilder.cpp \ src/messages/MessageColor.cpp \ src/messages/MessageElement.cpp \ - src/providers/bttv/BttvEmotes.cpp \ src/providers/emoji/Emojis.cpp \ - src/providers/ffz/FfzEmotes.cpp \ src/providers/irc/AbstractIrcServer.cpp \ src/providers/irc/IrcAccount.cpp \ src/providers/irc/IrcChannel2.cpp \ @@ -232,7 +239,21 @@ SOURCES += \ src/widgets/dialogs/UpdateDialog.cpp \ src/widgets/settingspages/IgnoresPage.cpp \ src/providers/twitch/PubsubClient.cpp \ - src/providers/twitch/TwitchApi.cpp + src/providers/twitch/TwitchApi.cpp \ + src/messages/Emote.cpp \ + src/messages/EmoteMap.cpp \ + src/messages/ImageSet.cpp \ + src/providers/bttv/BttvEmotes.cpp \ + src/providers/ffz/FfzEmotes.cpp \ + src/autogenerated/ResourcesAutogen.cpp \ + src/singletons/Badges.cpp \ + src/providers/twitch/TwitchBadges.cpp \ + src/providers/chatterino/ChatterinoBadges.cpp \ + src/providers/twitch/TwitchParseCheerEmotes.cpp \ + src/providers/bttv/LoadBttvChannelEmote.cpp \ + src/util/JsonQuery.cpp \ + src/RunGui.cpp \ + src/BrowserExtension.cpp HEADERS += \ src/Application.hpp \ @@ -292,9 +313,7 @@ HEADERS += \ src/messages/MessageParseArgs.hpp \ src/messages/Selection.hpp \ src/PrecompiledHeader.hpp \ - src/providers/bttv/BttvEmotes.hpp \ src/providers/emoji/Emojis.hpp \ - src/providers/ffz/FfzEmotes.hpp \ src/providers/irc/AbstractIrcServer.hpp \ src/providers/irc/IrcAccount.hpp \ src/providers/irc/IrcChannel2.hpp \ @@ -413,10 +432,28 @@ HEADERS += \ src/widgets/dialogs/UpdateDialog.hpp \ src/widgets/settingspages/IgnoresPage.hpp \ src/providers/twitch/PubsubClient.hpp \ - src/providers/twitch/TwitchApi.hpp + src/providers/twitch/TwitchApi.hpp \ + src/messages/Emote.hpp \ + src/messages/EmoteMap.hpp \ + src/messages/EmoteCache.hpp \ + src/messages/ImageSet.hpp \ + src/common/Outcome.hpp \ + src/providers/bttv/BttvEmotes.hpp \ + src/providers/ffz/FfzEmotes.hpp \ + src/autogenerated/ResourcesAutogen.hpp \ + src/singletons/Badges.hpp \ + src/providers/twitch/TwitchBadges.hpp \ + src/providers/chatterino/ChatterinoBadges.hpp \ + src/common/Aliases.hpp \ + src/providers/twitch/TwitchParseCheerEmotes.hpp \ + src/providers/bttv/LoadBttvChannelEmote.hpp \ + src/util/JsonQuery.hpp \ + src/RunGui.hpp \ + src/BrowserExtension.hpp RESOURCES += \ resources/resources.qrc \ + resources/resources_autogenerated.qrc DISTFILES += diff --git a/resources/__pycache__/_generate_resources.cpython-36.pyc b/resources/__pycache__/_generate_resources.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..dfaf6af51e47da0c049a7c2115f8105a43375046 GIT binary patch literal 622 zcmah{yH3L}6m{FA1&c~7Y*1NYX(UujWh)_~Dl>wXDGZf~ohBlQO&(H7)lc9<_y@kE zD?h-@gi9K?0|HAv^1bKC=U!i*x7+aSojiQj0QdkqOUB?z?(|p!1rDG!r$*~eoi>~X z)f|oLj!v5sV77{mH6D#@h>-Q1u$1SC%aBNz7tAjWY}mz-WU!)aXyLWKcs&hbH_RzR z*4=F|kBG3LN*OMXA|B&O5YIxE@wn#;VL%v@h^2x^2iGTA#*!fBXaS)ck~Br*jeXij z10?dtFmRE&LCVV1K$ePjv|aN8!lJX3xI_qJ^qWa>zpYK#4D*c7*nhPtd4Dk2p1USo zz~W^2m+6h!TpcD=Q&XQ2%97oT$GLoiRl8&=K^0bPITOWh!jgRhNlT9S2fl{b=Mlr6 zoMGnSYnDD`T;T0X_pvGlPi5mQAsL&hTu;2L_N<80JY;rb{d=GUP16oEUEA06ZwVd9 AfdBvi literal 0 HcmV?d00001 diff --git a/resources/_generate_resources.py b/resources/_generate_resources.py new file mode 100644 index 000000000..2ce917fbd --- /dev/null +++ b/resources/_generate_resources.py @@ -0,0 +1,38 @@ +resources_header = \ +''' + ''' + +resources_footer = \ +''' +''' + +header_header = \ +'''#include +#include "common/Singleton.hpp" + +namespace chatterino { + +class Resources2 : public Singleton { +public: + Resources2(); + +''' + +header_footer = \ +'''}; + +} // namespace chatterino''' + +source_header = \ +'''#include "ResourcesAutogen.hpp" + +namespace chatterino { + +Resources2::Resources2() +{ +''' + +source_footer = \ +'''} + +} // namespace chatterino''' diff --git a/resources/images/button_ban.png b/resources/buttons/ban.png similarity index 100% rename from resources/images/button_ban.png rename to resources/buttons/ban.png diff --git a/resources/images/buttons/ban.png b/resources/buttons/banRed.png similarity index 100% rename from resources/images/buttons/ban.png rename to resources/buttons/banRed.png diff --git a/resources/images/emote.svg b/resources/buttons/emote.svg similarity index 100% rename from resources/images/emote.svg rename to resources/buttons/emote.svg diff --git a/resources/images/emote_dark.svg b/resources/buttons/emoteDark.svg similarity index 100% rename from resources/images/emote_dark.svg rename to resources/buttons/emoteDark.svg diff --git a/resources/images/menu_black.png b/resources/buttons/menuDark.png similarity index 100% rename from resources/images/menu_black.png rename to resources/buttons/menuDark.png diff --git a/resources/images/menu_white.png b/resources/buttons/menuLight.png similarity index 100% rename from resources/images/menu_white.png rename to resources/buttons/menuLight.png diff --git a/resources/images/buttons/mod.png b/resources/buttons/mod.png similarity index 100% rename from resources/images/buttons/mod.png rename to resources/buttons/mod.png diff --git a/resources/images/moderatormode_disabled.png b/resources/buttons/modModeDisabled.png similarity index 100% rename from resources/images/moderatormode_disabled.png rename to resources/buttons/modModeDisabled.png diff --git a/resources/images/moderatormode_disabled2.png b/resources/buttons/modModeDisabled2.png similarity index 100% rename from resources/images/moderatormode_disabled2.png rename to resources/buttons/modModeDisabled2.png diff --git a/resources/images/moderatormode_enabled.png b/resources/buttons/modModeEnabled.png similarity index 100% rename from resources/images/moderatormode_enabled.png rename to resources/buttons/modModeEnabled.png diff --git a/resources/images/moderatormode_enabled2.png b/resources/buttons/modModeEnabled2.png similarity index 100% rename from resources/images/moderatormode_enabled2.png rename to resources/buttons/modModeEnabled2.png diff --git a/resources/images/button_timeout.png b/resources/buttons/timeout.png similarity index 100% rename from resources/images/button_timeout.png rename to resources/buttons/timeout.png diff --git a/resources/images/buttons/unban.png b/resources/buttons/unban.png similarity index 100% rename from resources/images/buttons/unban.png rename to resources/buttons/unban.png diff --git a/resources/images/buttons/unmod.png b/resources/buttons/unmod.png similarity index 100% rename from resources/images/buttons/unmod.png rename to resources/buttons/unmod.png diff --git a/resources/images/download_update.png b/resources/buttons/update.png similarity index 100% rename from resources/images/download_update.png rename to resources/buttons/update.png diff --git a/resources/images/download_update_error.png b/resources/buttons/updateError.png similarity index 100% rename from resources/images/download_update_error.png rename to resources/buttons/updateError.png diff --git a/resources/images/chatterino2.icns b/resources/chatterino2.icns similarity index 100% rename from resources/images/chatterino2.icns rename to resources/chatterino2.icns diff --git a/resources/error.png b/resources/error.png new file mode 100644 index 0000000000000000000000000000000000000000..07fba9f7cdf56d924bd46eb0a9ba1632e92f2293 GIT binary patch literal 685 zcmV;e0#f~nP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D02y>eSaefwW^{L9 za%BK;VQFr3E^cLXAT%y8E;2FkAZe8V00JFJL_t(|Ugea*ZiFxlMET_Y|DhB|>qu+{ za2(rl=;5L5Y-r|<)NcE>{T}RJ{Orf`RghEbS-xn(5APY_LY}xAfLMQo8+qbs0Ak${ z9^?th0K|GD2;>Rz!Gm>1=x{ooeJ?bZWuw#O_@sL=B?^b~82JThPQ>Sv+_I7lkMm1jC# z-X+g;IMHGB#W(B7Gr|!h1FR>{2u64sU|o6UVT8K@)|Y2)Mz|W_J@O13VcY)zSH)J= TiRV#)00000NkvXXu0mjfZe|>+ literal 0 HcmV?d00001 diff --git a/resources/generate_resources.py b/resources/generate_resources.py new file mode 100755 index 000000000..4b047219c --- /dev/null +++ b/resources/generate_resources.py @@ -0,0 +1,61 @@ +#!/usr/bin/env python3 +from pathlib import Path + +from _generate_resources import * + +ignored_files = ['qt.conf', 'resources.qrc', 'resources_autogenerated.qrc', 'windows.rc', + 'generate_resources.py', '_generate_resources.py'] +ignored_directories = ['__pycache__'] + +def isNotIgnored(file): + return str(file) not in ignored_files + +all_files = list(filter(isNotIgnored, \ + filter(Path.is_file, Path('.').glob('**/*')))) +image_files = list(filter(isNotIgnored, \ + filter(Path.is_file, Path('.').glob('**/*.png')))) + +with open('./resources_autogenerated.qrc', 'w') as out: + out.write(resources_header) + for file in all_files: + out.write(f" {str(file)}\n") + out.write(resources_footer) + +with open('../src/autogenerated/ResourcesAutogen.cpp', 'w') as out: + out.write(source_header) + for file in sorted(image_files): + var_name = str(file.with_suffix("")).replace("/",".") + out.write(f' this->{var_name}') + out.write(f' = QPixmap(":/{file}");\n') + out.write(source_footer) + +def writeHeader(out, name, element, indent): + if isinstance(element, dict): + if name != "": + out.write(f"{indent}struct {{\n") + for (key, value) in element.items(): + writeHeader(out, key, value, indent + ' ') + if name != "": + out.write(f"{indent}}} {name};\n"); + else: + out.write(f"{indent}QPixmap {element};\n") + +with open('../src/autogenerated/ResourcesAutogen.hpp', 'w') as out: + out.write(header_header) + + elements = {} + for file in sorted(image_files): + elements_ref = elements + directories = str(file).split('/')[:-1] + filename = file.stem + for directory in directories: + if directory not in elements_ref: + if directory not in ignored_directories: + elements_ref[directory] = {} + elements_ref = elements_ref[directory] + elements_ref[filename] = filename + + writeHeader(out, "", elements, '') + + out.write(header_footer) + diff --git a/resources/images/icon.png b/resources/icon.png similarity index 100% rename from resources/images/icon.png rename to resources/icon.png diff --git a/resources/images/AppearanceEditorPart_16x.png b/resources/images/AppearanceEditorPart_16x.png deleted file mode 100644 index 86c59f7ff0455b522519a95a7814c96608c0a84c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 347 zcmV-h0i^zkP)!Po0^`Cv+&V#FaXY2*zEZ+KvV zXTk1n2p*3YlarIsYP6VtG>^Htd+>U_B1RnIl16MmHS-KBsZhd5R^(KMxzmAkgry&K^XiQ z@a&~)^RLmo$=27`+2Z0Nvsf&&9y5o-!IqbwkT32A*H)>7DsRYr+=Bi6S2#QSibyz$ z>FH_2_hQiLbQC(F4IU&C37Rcrm`o-ZjYg!?Y4MFlqi{N$LVR)Y4drqfE|-g_87hI9 z&1RO%P)z@Q!Y)7o_C)WxNr z!=*`M>ms!pNKn&A!UyQk598oMn6$_duZNcw^hs`d`MvMMdq;|*0EG|&MxN)V0B!(u zX$im@z_p?%@2Eku5)QN8Znp!^^HkS$mCa`30eCAL6A17Id^%M+pH8Qk-_EgE++jAG z(P?J@c4hqpH9A7D!m_L%ML;4zmSwPQ8&MQNRn?Eu1SNzU@L+Wqy}l2>=Y#FZaU50` z4LC2)uV~`3d(qs0ay97V5D&{0L^^FBiX=($cLT#PFdB^z1OehWhH07@kH^2>jC9N( zxL>w~i$;Sm4EJG5nx^y!zjDOjarMMB+Yk--{cD<5DO0G0uIrdgCjYyIug@KZ!{NS( xEX(yh5N6QyP}uTVBlapq1HPCR*!0SV@(rXni`{+Y%fSEu002ovPDHLkV1j%tuvY*8 diff --git a/resources/images/CustomActionEditor_16x.png b/resources/images/CustomActionEditor_16x.png deleted file mode 100644 index b7b68a21eb6c80e613b18977843f44834284e5e1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 368 zcmV-$0gwKPP)kKCawJa|+ zL2Wb`LVf@&Wzi7JwLleXf<8gC;&+Y>;+Vi&@ij&#=?qmuzfc{Mp*vRV;bJYEna9gY zvn){t0R*ASV$ongRG6q0PvA1>J-$rfSRzDivEt?Nf>?vI9;=jOh#9qd4M$QGM$=l# zV5Dhk6{aI89YLZWt)UEzk!ExzY~sR8voMRydl{FlYN8mjthirQLkvMgA`xvwua#sU zL7C0wIEYTCK@l3IO;p*aMpTO85J?OhR5)1(se&Y8aZI*J3aeHeM}BMqJRnbDi^@c^ zP}_)r;Pb1pgx)AENlee+xv7j$SPmri!gB-+z#tS=1;tY{;!)?O5@wW$vY{uyQLxkb z^zdMC2E3x$ThW;&`tzR!6i-_S5-zAnVVQ&WRFUJ9CX;MI6}}00?+8pmHNsG6Ro$IOm;m*l~B5J z8)eqns|;p4sWVMWw@F+$-l@rS{{N-d$pRuS8+b05I5RLyKxX^`lv$bc3mhA8hnt!q zrzU(!-c+!@q?I%SLMe*D>Sc)!bLyL(DmPXq1_TjWh&Zc|J3*+VHJNK6o>IobbByu~ zWuLXwzu+th8Zp&^qDpexw1c_frkMvzY#hT9Bw|=D{-s!Pg>8j=lJb)-;dy6{`ke%i z_#uLRI=`lZ70IhcSQ(c45K$&2r9u{XAH*u%G`3=mAo5Z~DW17Y&d-WIh{TMDi&C7` zI5B-TlgGCZPc$ewylH}g@{(&vh>!vjQxb@bDG`MtDkdX|+)UIMk-TPxs@Ez2%tews zH$MgJ?plZzF|OL>1R%ByfVbZTVC*q^-UJ{_0`U7r0GOQsyeNIR;Y1YxDLWmua!>Sf z@5lW$spYxv4R*eexo}Q{J-4rB@uiWUGjd%A9e<@!4?wJL@5RfXf6|?IzI|ROqd)7w z<&`Og3-jim?{$t2A6vIAC@sX{o_UCk`9`0ClwLzP%rffL6qh|PA zEU%}sEv4&WVP4(Qb7QWZIX8?yt=s?AOV_G2sXun>n)@#G^yI*<^n`RB&`%|Cqy|w)8w}iT4 z_0X67v(LQ>&i=8W);tK@*9)5eP9NI0YP^JBL3bQl+%@O>l3Md;V_S>ek3O9x-*ap~ zc6)v2nsIOLai#5kMNProjI@(6?8x1ZI_91lY~R;d{K^CROx^ukJ*FR)jQzUBwW;Wr zH_8gt#i!fbZH;>>I=zPNk2^{uXTVKV-cm4s*N_YnV diff --git a/resources/images/Emoji_Color_1F60A_19.png b/resources/images/Emoji_Color_1F60A_19.png deleted file mode 100644 index 15e15bd4ac351cab80279e7b7c3094b98c4d15e0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 660 zcmV;F0&D$=P)004R= z004l4008;_004mL004C`008P>0026e000+nl3&F}00009a7bBm000XT000XT0n*)m z`~Uy|8FWQhbW?9;ba!ELWdLwtX>N2bZe?^JG%heMF*(%MvSa`N0rg2lK~y+Tom9I@ z13?tsY$h)^4>b~F+?b#!5no{N0TD@&N)*xx7AYj9_(VI?*ohVvqK#Izf_{KMAou|m z7J`Lf8!;;5oL%oSYc@o^a9Hl0bI;y6v$G24+z}#}KtW(xpz{693os`>;1n#K@b39M zu39|G-i!HGzPZ%N=Vv0kE#&?{9qZtkbTgd9GQHj2r@~~2KU@uSew(es{pFA$oa*vE zf^W9fi)C8;>f7PE?kMsTM_WlA3A(H3L$fT#Fwt*?2xq$~n$Ymkh}@WVjI9yr?qbl^ z^b_(rwhRwRjKNg$sI(A8m0#}nSq(B!$2xe(;h`2#t0Y!Pba+0A3?fh|Cu|}F)QLki zoU+JurL&!_;P`3b=48OY!}W+&lYu&MsD`Zyv7(aAw4Cph;rkvapyuP|7$UHZj=hhtSFC9iEwoT1p(a%a}zR1v7&8C^vG1 z?b__~Kz*&+%(=69^`fqergykJS@^5}{fp$OVbAZC-c6mQR^t>X67c^_>~xirOb^y) zN-fFk68LiAwCvoYju$6{&wE}Qd!56PfA5`(jfWkhr#(GWweQuJob<`c2j)Cl>9?NK zV0p!=<{1ptUCVztvT(aRS@mhnDy|e6_fxs`wpSQ-8=f}eGux36CM b{d4o%p$=SnbeZ}DE>gTe~DWM4f1jcTo diff --git a/resources/images/Message_16xLG.png b/resources/images/Message_16xLG.png deleted file mode 100644 index 7d06b1995d0f7c0610effdaee04a93782ae5c4b6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 179 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`oCO|{#S9F5M?jcysy3fAP%zcg z#WAEJ?(HN)t_A}h*Wj05@>OGtXWr9g|ES^jQo@>p&2dqg@ck)IZ8`p=q?tG^^UFSa zkG*Wh&y6}tCKn~{bNp$1mdKI;Vst06l6v;s5{u diff --git a/resources/images/StatusAnnotations_Blocked_16xLG_color.png b/resources/images/StatusAnnotations_Blocked_16xLG_color.png deleted file mode 100644 index b58166e3064edc117c8b97942e585dc84d36a27b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1618 zcmbVMPi)&%7!QmNQE6HaYzMeJxsw2?j_v%h8_VuU>y$>C)2NBGawzJH{hGYhvCZ>K zlXgf86B2MiLP7#@=m`l92yNnsrd~K8AwWnwY=>QtI8GhA0f+H9PNIaNYRs}@zxVyV z-}k=n`{nzvT6yEl>`Sw$RO(E5&DwzXT>LzD3V!e3`wcc`d|Kb+oAxE$3EFAw1`U$# zwL`F{QpUwzh@D-+QG+z8ZwklX9SMlKrf@lL%XYXzT6FDzk=j9}?i}no1y{JZG;8#9 zAZQbgQLpX!k=`?f0k00<@wOzO0fg_GLNN}AHti}}2^c~7bXIg^MM0{XR&se&RTfZ2 zRx*+dw<;<*J+JB+4Gk}0X%-pG-O)F!)nPDLnL>;6P?w}`x0~)}(*bKrN}*7QNirD` z5Ms3NbKDdCXg;P$I4lx5jE0;BK8iVUBj|8b5WtRxol$qhYxItZkBN~StU!4E&&ErS>SdY{!V?h zhSq(L`oxf8Re^aRsAM(rHKY^57_Os7PZ0J=L;Z`+7|2LpqY)3-{zPIm^7eFMgM%Y9 z5Rs0Z_@8155#A$iBKeVtK5fsT-U)fPMS%KbeT~6Uu*18Ukzx~sG8!q%jJi#NR%$AG z^)mI{pc}n5eM+Z={U(87CdET3R<$5a_GXm6Aw5>0q6T_qVFQznFS(H3ZACw{3jhpnyBgH!8K%Hu~l0 z`0&=sGqAH&wu<%MTcwYFe4#JT&Ak7`%pVJP`gh)aWp{4<(0l!B{L|Kr_imiqJpA>C zoyw`d_s@P+f4;wW?(Ws_+Y3LRzIKc8+YfKbJ9j^L^I4@OoWJ?8_-SS%rD%& ZV)P$s#V`L_d>I0z%B70+&GPp3e*mip`8EIm diff --git a/resources/images/UserProfile_22x.png b/resources/images/UserProfile_22x.png deleted file mode 100644 index c47f61243ff1731e5b9d01a5bb04a7de5e2efddb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 286 zcmeAS@N?(olHy`uVBq!ia0vp^Vj#@H1|*Mc$*~4foCO|{#S9F5M?jcysy3fAQ1F7M zi(^Pd+})`gxeghKxaO;Rzr1(z+}Ge;D-!;2Sl?dCx9f_%VbiWJ>aTL@7ge8(s60H| zh-H$<5$49zvV8d!o{KVP8L&4sU9(zY+xOU`AW($W>B2VM4%TM}_53EWO#jS!{&+WE z>Z?CTuWodjxBfuDvUA~Qf);yUzM@o?BEe=~c)wm}?&2Lg&2*A9OE)McB+9s7SuVx4 zE-6aa?XKvQU5yip_;*fDFuT3=_7RrOd$GrFrYB8yjx6HQJ~wUVn&mOjZw#KUelF{r5}E+X)^#`l diff --git a/resources/images/VSO_Link_blue_16x.png b/resources/images/VSO_Link_blue_16x.png deleted file mode 100644 index eb3882929648613f91bccc6c7ac9758abf566c63..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 480 zcmV<60U!Q}P))+3xEnKP~`I&NJwkN!5li+b=IKAIc8_rGBZ%J zC6y}{`scSv9o-;|+&d83qnxHw?Ch3oW51~`5TTXLG%-Fou<&UyDK4m<2vfsUMA-Nx zDk85xM``^aL;mkaVt)5EPE14&B-}GGvitu~UNcD34f>hPP>I!@8xbA)b|_}=Qj%Uy zhKWhButEUE5HTRD=~&y(f*%L|ZWo3-T!?w!!dY&?MYf3DQ~@@t3MR*z0YuwhIhyA- zF3}m>!mp2R^fbP}_>U7zubo3xfOKAm$@@1>FbR1d%m#XE;AyRbW)S|K=CJH(g0IVk zGK&I6+(9MVR`0-cUoDs*``|NvVB_>}uGm2^(mN2FDFlFp8MFbgo3pg;8WPXoXSxGN W(LnN591B8F2MazHx%6|XKfBz{VLMb6ZPB(2S zAVDi4M8bOarfS!UTC~P|{5>sNyLIn+P@yd(N63EvEh9$Be*ZZtRWc?_rD@itY1V2- zj!iXeGAB)ALx(vlRboPgw{z-}UcQoDzP57dX-AKAw1aN|005XtL_t(|+DuRf62mYE zV>Ej--FyB2lR-4b!RG_Zk`X4d5L-)lA!3OW#J-t@S0r8&a07{Dwt_!z!?M(%@9p>^ z$^xQ&p;}DQ7_4!lsv{2R>88?BwQBI}AE|p)(E2)bcW{6{B~8$7OGkEX_>}Vltiy@3 z4;swGbufwBWx*ib@6s*yDKb1ISHL8BlpuIx`$=5P0t^5mcLQqA{Jq8i0000QaG}WQl7;NpOBzNqJ&XDuZK6ep0G} zXKrG8YEWuoN@d~6R2!h8gaDrqSGyaV?Qd+6GBlPpG?6wmw!6Mr+Q`Ig-DJBPTddEn zGF>~_bj?JS=n$2-Fy-iAyQ`bDDl%=aZZumrMb6e%!OKlKDp_W_n97{BwWmV<>|DXE6!ux{c zbXGBz1%@sMcwRU6nLktfb9CzM_j1u|c2@1`%y=b`>{&No!=T=tAu`#5Yh~;62fqaK z!_C@S^uCu)vkXw!eMC9I#7uWxXwa!w`iY8R(x)qISI)@jcK&;Njb~I%+pps(PC~!h vq8h#%6}g^E-n{CCqKbP0l+XkKR0M%i diff --git a/resources/images/cheer10000.png b/resources/images/cheer10000.png deleted file mode 100644 index 3e3eb4284a7da6b09effd73a91d672c06de74418..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 330 zcmeAS@N?(olHy`uVBq!ia0vp^LLkh+3?vf;>QaG}WQl7;NpOBzNqJ&XDuZK6ep0G} zXKrG8YEWuoN@d~6R2!h8*Z`jp*Z=Br|J7vUCD`I5*y1JG|EmMp>{}FscPI+)R22TC zDxDz7K2?r)lcLZ@MWHjwVp|o2awR#Y$noY%aZHutJFO(1EyfCXKYrxi zdp<;edwU(@znDA91zFrdJDj-%9PWw;FBdGhqP5`J-XAAp!`4rm+99vQaG}WQl7;NpOBzNqJ&XDuZK6ep0G} zXKrG8YEWuoN@d~6R2!fozW|>Q*Z-%5TR0fjda%7-EpRZC^UE&5Yu&v4l1zuPxGuNy zeAy}Zb&t@iRRZh0*;_andc_#~B$%#s@vik^yVl9uE6#W{k87G-;{JvN(24H;+K^6+2hRK04aV`t|c?8bP0l+XkKg9K)+ diff --git a/resources/images/cheer5000.png b/resources/images/cheer5000.png deleted file mode 100644 index 89a7a4015e3b125791e8b4a05f623703c9ca4d72..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 362 zcmV-w0hRuVP)-U04z@r zNp}D*Q%I@(LZI+CklHzr+Crf5D|oppdAcllx*T7Z9AB0IE>Z?LU=B%l1vp?Tc)A8S zU`MF^8()?NIbleu{v2SJL7(t9kJ;KCq$mIY0HaAnK~#9!L{J9~12GK3_;&Z+{r@v- z>^n7zB0~tlh7qK}42VsOnu(w@Zy0rGqA+U1ii6;Ozsfk>1zQ(QqzrIxy;i!%cPyy4nNmMAzK}>9F*kit$8F|SWPEROJ5ABv z+#&Jl+|Q$>CRBMd93BJz9p(s@swR>SR`bYQMTNrB2lxpv0B?f>pwtt;%K!iX07*qo IM6N<$f>;2N-2eap diff --git a/resources/images/collapse.png b/resources/images/collapse.png deleted file mode 100644 index b181ccbf931e797f8cc0703dc9d0a79300c6ce40..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 236 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|SkfJR9T^xl z_H+M9WCij$3p^r=85sBugD~Uq{1qucLCF%=h?3y^w370~qEv>0#LT=By}Z;C1rt33 zJwwYY*EWF^#dx|nhFJI?J!8mqz<|g3qRd_M@{=E24Y^!OUVLyU%vt;H-w{4xuF$K} z@1waymbh|lX81E-W5;9jZ)&XNPd4=LVE8Y(q5m}#h`Z%u{h|qSPKvfIvTAQW#I0xl YW`FtkGFEq#y`LMqSqkPh2>u3>cPW zvG5-Z-S!!_Z6nNw9bjId?|YCtp)AXgzH1xEh@yxb$3gn396@E8rYX`d@sww!LDMuK z6-7aw=MlgobzKMXMXLkXbt%ttxx_np)E;4)CQf;o2!qxVmt~>0ZDl<#@_J-&b0pvQ zaR(o|Bk%#?IanMpx_Zt z7srr_xV6E%c@HZHeASrQI!`vpK+HCINhkNJ(;PVumN0ltYX73s6BDs3!s?~nd%5!S z8_vY9{W`C>Zc5Otj1vpp?nFL*%PwNJWXdcg&#L-MgKi$F6A3JB2q+2REoY z=7?1u^lRPgg&ebxsLQ0Hr@|D*ylh diff --git a/resources/images/tool_moreCollapser_off16.png b/resources/images/tool_moreCollapser_off16.png deleted file mode 100644 index 717fc75e2b9191726c30875cc8f75d33527bea67..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 279 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Ea{HEjtmSN`?>!lvI6;>1s;*b z3=DjSK$uZf!>a)(C|TkfQ4*Y=R#Ki=l*&+$n3-3imzP?iV4`QBXK1jNi|YVT(G*V? z#}EtuqZ2RkHX8^yJ5HU^F1abp?5vRN!yVI)1xYzc{aC5memGrviB7$%*UQjjhIe*M z`^dd6ztUjypE->8Cf}c3_WZVGj#9_+itjo`3P&V(lP-iuZCYJ3Ux+VGz?oyZYDVw! z|KfXvo}J#%aA1*cqy1qfZl2NvE1fN>+YdR#7Fp}8coG<)q562kx>uUJAKBzuG8Ayu VbXBTb{RFy?!PC{xWt~$(697pmVmbf- diff --git a/resources/images/pajaDank.png b/resources/pajaDank.png similarity index 100% rename from resources/images/pajaDank.png rename to resources/pajaDank.png diff --git a/resources/resources.qrc b/resources/resources.qrc index 129713804..8568b5032 100644 --- a/resources/resources.qrc +++ b/resources/resources.qrc @@ -1,82 +1,5 @@ - - images/AppearanceEditorPart_16x.png - images/BrowserLink_16x.png - images/cheer1.png - images/cheer100.png - images/cheer1000.png - images/cheer10000.png - images/cheer100000.png - images/cheer5000.png - images/verified.png - images/CopyLongTextToClipboard_16x.png - images/CustomActionEditor_16x.png - images/Emoji_Color_1F60A_19.png - images/Filter_16x.png - images/format_Bold_16xLG.png - images/Message_16xLG.png - images/settings.png - images/tool_moreCollapser_off16.png - images/twitchprime_bg.png - qss/settings.qss - images/admin_bg.png - images/broadcaster_bg.png - images/globalmod_bg.png - images/moderator_bg.png - images/staff_bg.png - images/turbo_bg.png - emojidata.txt - images/button_ban.png - images/button_timeout.png - images/StatusAnnotations_Blocked_16xLG_color.png - images/UserProfile_22x.png - images/VSO_Link_blue_16x.png - sounds/ping2.wav - images/subscriber.png - images/collapse.png - images/emote.svg - images/notifications.svg - images/behave.svg - images/theme.svg - images/accounts.svg - images/chatterino2.icns - images/icon.png - images/commands.svg - images/aboutlogo.png - images/about.svg - images/moderatormode_disabled.png - images/moderatormode_enabled.png - images/split/splitdown.png - images/split/splitleft.png - images/split/splitright.png - images/split/splitup.png - images/split/splitmove.png - licenses/boost_boost.txt - licenses/fmt_bsd2.txt - licenses/libcommuni_BSD3.txt - licenses/openssl.txt - licenses/pajlada_settings.txt - licenses/pajlada_signals.txt - licenses/qt_lgpl-3.0.txt - licenses/rapidjson.txt - licenses/websocketpp.txt - emoji.json - images/buttons/ban.png - images/buttons/mod.png - images/buttons/unban.png - images/buttons/unmod.png - images/emote_dark.svg - tlds.txt - images/menu_black.png - images/menu_white.png - contributors.txt - avatars/fourtf.png - avatars/pajlada.png - images/download_update.png - images/download_update_error.png - images/pajaDank.png - - - qt.conf - + + qt.conf + diff --git a/resources/resources_autogenerated.qrc b/resources/resources_autogenerated.qrc new file mode 100644 index 000000000..6fc9e6f68 --- /dev/null +++ b/resources/resources_autogenerated.qrc @@ -0,0 +1,64 @@ + + pajaDank.png + icon.png + emojidata.txt + contributors.txt + error.png + emoji.json + icon.ico + tlds.txt + chatterino2.icns + qss/settings.qss + __pycache__/_generate_resources.cpython-36.pyc + licenses/fmt_bsd2.txt + licenses/openssl.txt + licenses/pajlada_settings.txt + licenses/qt_lgpl-3.0.txt + licenses/pajlada_signals.txt + licenses/rapidjson.txt + licenses/websocketpp.txt + licenses/boost_boost.txt + licenses/libcommuni_BSD3.txt + settings/aboutlogo.png + settings/behave.svg + settings/accounts.svg + settings/about.svg + settings/notifications.svg + settings/commands.svg + settings/theme.svg + split/up.png + split/left.png + split/move.png + split/right.png + split/down.png + buttons/unban.png + buttons/menuDark.png + buttons/mod.png + buttons/emote.svg + buttons/modModeEnabled2.png + buttons/ban.png + buttons/unmod.png + buttons/emoteDark.svg + buttons/updateError.png + buttons/modModeDisabled.png + buttons/modModeDisabled2.png + buttons/modModeEnabled.png + buttons/menuLight.png + buttons/update.png + buttons/timeout.png + buttons/banRed.png + sounds/ping2.wav + twitch/prime.png + twitch/verified.png + twitch/admin.png + twitch/subscriber.png + twitch/turbo.png + twitch/moderator.png + twitch/globalmod.png + twitch/cheer1.png + twitch/broadcaster.png + twitch/staff.png + avatars/fourtf.png + avatars/pajlada.png + + \ No newline at end of file diff --git a/resources/images/about.svg b/resources/settings/about.svg similarity index 100% rename from resources/images/about.svg rename to resources/settings/about.svg diff --git a/resources/images/aboutlogo.png b/resources/settings/aboutlogo.png similarity index 100% rename from resources/images/aboutlogo.png rename to resources/settings/aboutlogo.png diff --git a/resources/images/accounts.svg b/resources/settings/accounts.svg similarity index 100% rename from resources/images/accounts.svg rename to resources/settings/accounts.svg diff --git a/resources/images/behave.svg b/resources/settings/behave.svg similarity index 100% rename from resources/images/behave.svg rename to resources/settings/behave.svg diff --git a/resources/images/commands.svg b/resources/settings/commands.svg similarity index 100% rename from resources/images/commands.svg rename to resources/settings/commands.svg diff --git a/resources/images/notifications.svg b/resources/settings/notifications.svg similarity index 100% rename from resources/images/notifications.svg rename to resources/settings/notifications.svg diff --git a/resources/images/theme.svg b/resources/settings/theme.svg similarity index 100% rename from resources/images/theme.svg rename to resources/settings/theme.svg diff --git a/resources/images/split/splitdown.png b/resources/split/down.png similarity index 100% rename from resources/images/split/splitdown.png rename to resources/split/down.png diff --git a/resources/images/split/splitleft.png b/resources/split/left.png similarity index 100% rename from resources/images/split/splitleft.png rename to resources/split/left.png diff --git a/resources/images/split/splitmove.png b/resources/split/move.png similarity index 100% rename from resources/images/split/splitmove.png rename to resources/split/move.png diff --git a/resources/images/split/splitright.png b/resources/split/right.png similarity index 100% rename from resources/images/split/splitright.png rename to resources/split/right.png diff --git a/resources/images/split/splitup.png b/resources/split/up.png similarity index 100% rename from resources/images/split/splitup.png rename to resources/split/up.png diff --git a/resources/images/admin_bg.png b/resources/twitch/admin.png similarity index 100% rename from resources/images/admin_bg.png rename to resources/twitch/admin.png diff --git a/resources/images/broadcaster_bg.png b/resources/twitch/broadcaster.png similarity index 100% rename from resources/images/broadcaster_bg.png rename to resources/twitch/broadcaster.png diff --git a/resources/images/cheer1.png b/resources/twitch/cheer1.png similarity index 100% rename from resources/images/cheer1.png rename to resources/twitch/cheer1.png diff --git a/resources/images/globalmod_bg.png b/resources/twitch/globalmod.png similarity index 100% rename from resources/images/globalmod_bg.png rename to resources/twitch/globalmod.png diff --git a/resources/images/moderator_bg.png b/resources/twitch/moderator.png similarity index 100% rename from resources/images/moderator_bg.png rename to resources/twitch/moderator.png diff --git a/resources/images/twitchprime_bg.png b/resources/twitch/prime.png similarity index 100% rename from resources/images/twitchprime_bg.png rename to resources/twitch/prime.png diff --git a/resources/images/staff_bg.png b/resources/twitch/staff.png similarity index 100% rename from resources/images/staff_bg.png rename to resources/twitch/staff.png diff --git a/resources/images/subscriber.png b/resources/twitch/subscriber.png similarity index 100% rename from resources/images/subscriber.png rename to resources/twitch/subscriber.png diff --git a/resources/images/turbo_bg.png b/resources/twitch/turbo.png similarity index 100% rename from resources/images/turbo_bg.png rename to resources/twitch/turbo.png diff --git a/resources/images/verified.png b/resources/twitch/verified.png similarity index 100% rename from resources/images/verified.png rename to resources/twitch/verified.png diff --git a/src/Application.cpp b/src/Application.cpp index 9068bf9a8..b7d651aa4 100644 --- a/src/Application.cpp +++ b/src/Application.cpp @@ -6,9 +6,10 @@ #include "controllers/ignores/IgnoreController.hpp" #include "controllers/moderationactions/ModerationActions.hpp" #include "controllers/taggedusers/TaggedUsersController.hpp" +#include "providers/bttv/BttvEmotes.hpp" +#include "providers/ffz/FfzEmotes.hpp" #include "providers/twitch/PubsubClient.hpp" #include "providers/twitch/TwitchServer.hpp" -#include "singletons/Emotes.hpp" #include "singletons/Fonts.hpp" #include "singletons/Logging.hpp" #include "singletons/NativeMessaging.hpp" @@ -24,69 +25,75 @@ namespace chatterino { -static std::atomic isAppConstructed{false}; static std::atomic isAppInitialized{false}; -static Application *staticApp = nullptr; +Application *Application::instance = nullptr; // this class is responsible for handling the workflow of Chatterino // It will create the instances of the major classes, and connect their signals to each other -Application::Application(int _argc, char **_argv) - : argc_(_argc) - , argv_(_argv) +Application::Application(Settings &_settings, Paths &_paths) + : settings(&_settings) + , paths(&_paths) + , resources(&this->emplace()) + + , themes(&this->emplace()) + , fonts(&this->emplace()) + , emotes(&this->emplace()) + , windows(&this->emplace()) + + , accounts(&this->emplace()) + , commands(&this->emplace()) + , highlights(&this->emplace()) + , ignores(&this->emplace()) + , taggedUsers(&this->emplace()) + , moderationActions(&this->emplace()) + , twitch2(&this->emplace()) + , logging(&this->emplace()) { - getSettings()->initialize(); - getSettings()->load(); -} + this->instance = this; -void Application::construct() -{ - assert(isAppConstructed == false); - isAppConstructed = true; + this->fonts->fontChanged.connect([this]() { this->windows->layoutChannelViews(); }); - // 1. Instantiate all classes - this->settings = getSettings(); - this->paths = getPaths(); - - this->addSingleton(this->themes = new Theme); - this->addSingleton(this->windows = new WindowManager); - this->addSingleton(this->logging = new Logging); - this->addSingleton(this->commands = new CommandController); - this->addSingleton(this->highlights = new HighlightController); - this->addSingleton(this->ignores = new IgnoreController); - this->addSingleton(this->taggedUsers = new TaggedUsersController); - this->addSingleton(this->accounts = new AccountController); - this->addSingleton(this->emotes = new Emotes); - this->addSingleton(this->fonts = new Fonts); - this->addSingleton(this->resources = new Resources); - this->addSingleton(this->moderationActions = new ModerationActions); - - this->addSingleton(this->twitch2 = new TwitchServer); this->twitch.server = this->twitch2; this->twitch.pubsub = this->twitch2->pubsub; } -void Application::instantiate(int argc, char **argv) -{ - assert(staticApp == nullptr); - - staticApp = new Application(argc, argv); -} - -void Application::initialize() +void Application::initialize(Settings &settings, Paths &paths) { assert(isAppInitialized == false); isAppInitialized = true; - // 2. Initialize/load classes - for (Singleton *singleton : this->singletons_) { - singleton->initialize(*this); + for (auto &singleton : this->singletons_) { + singleton->initialize(settings, paths); } - // XXX this->windows->updateWordTypeMask(); + this->initNm(); + this->initPubsub(); +} + +int Application::run(QApplication &qtApp) +{ + assert(isAppInitialized); + + this->twitch.server->connect(); + + this->windows->getMainWindow().show(); + + return qtApp.exec(); +} + +void Application::save() +{ + for (auto &singleton : this->singletons_) { + singleton->save(); + } +} + +void Application::initNm() +{ #ifdef Q_OS_WIN #ifdef QT_DEBUG #ifdef C_DEBUG_NM @@ -98,7 +105,10 @@ void Application::initialize() this->nativeMessaging->openGuiMessageQueue(); #endif #endif +} +void Application::initPubsub() +{ this->twitch.pubsub->signals_.whisper.sent.connect([](const auto &msg) { Log("WHISPER SENT LOL"); // }); @@ -197,39 +207,11 @@ void Application::initialize() RequestModerationActions(); } -int Application::run(QApplication &qtApp) -{ - // Start connecting to the IRC Servers (Twitch only for now) - this->twitch.server->connect(); - - // Show main window - this->windows->getMainWindow().show(); - - return qtApp.exec(); -} - -void Application::save() -{ - for (Singleton *singleton : this->singletons_) { - singleton->save(); - } -} - -void Application::addSingleton(Singleton *singleton) -{ - this->singletons_.push_back(singleton); -} - Application *getApp() { - assert(staticApp != nullptr); + assert(Application::instance != nullptr); - return staticApp; -} - -bool appInitialized() -{ - return isAppInitialized; + return Application::instance; } } // namespace chatterino diff --git a/src/Application.hpp b/src/Application.hpp index 558ae7b73..c378715f5 100644 --- a/src/Application.hpp +++ b/src/Application.hpp @@ -1,13 +1,13 @@ #pragma once +#include "common/Singleton.hpp" #include "singletons/Resources.hpp" #include +#include namespace chatterino { -class Singleton; - class TwitchServer; class PubSub; @@ -24,45 +24,47 @@ class Logging; class Paths; class AccountManager; class Emotes; -class NativeMessaging; class Settings; class Fonts; class Resources; class Application { - Application(int _argc, char **_argv); + std::vector> singletons_; + int argc_; + char **argv_; public: - static void instantiate(int argc_, char **argv_); + static Application *instance; - ~Application() = delete; + Application(Settings &settings, Paths &paths); - void construct(); - void initialize(); + void initialize(Settings &settings, Paths &paths); void load(); + void save(); int run(QApplication &qtApp); friend void test(); - Settings *settings = nullptr; - Paths *paths = nullptr; + Settings *const settings = nullptr; + Paths *const paths = nullptr; + Resources2 *const resources; - Theme *themes = nullptr; - WindowManager *windows = nullptr; - Logging *logging = nullptr; - CommandController *commands = nullptr; - HighlightController *highlights = nullptr; - IgnoreController *ignores = nullptr; - TaggedUsersController *taggedUsers = nullptr; - AccountController *accounts = nullptr; - Emotes *emotes = nullptr; - NativeMessaging *nativeMessaging = nullptr; - Fonts *fonts = nullptr; - Resources *resources = nullptr; - ModerationActions *moderationActions = nullptr; - TwitchServer *twitch2 = nullptr; + Theme *const themes = nullptr; + Fonts *const fonts = nullptr; + Emotes *const emotes = nullptr; + WindowManager *const windows = nullptr; + + AccountController *const accounts = nullptr; + CommandController *const commands = nullptr; + HighlightController *const highlights = nullptr; + IgnoreController *const ignores = nullptr; + TaggedUsersController *const taggedUsers = nullptr; + ModerationActions *const moderationActions = nullptr; + TwitchServer *const twitch2 = nullptr; + + [[deprecated]] Logging *const logging = nullptr; /// Provider-specific struct { @@ -70,22 +72,20 @@ public: [[deprecated("use twitch2->pubsub instead")]] PubSub *pubsub = nullptr; } twitch; - void save(); - - // Special application mode that only initializes the native messaging host - static void runNativeMessagingHost(); - private: void addSingleton(Singleton *singleton); + void initPubsub(); + void initNm(); - int argc_; - char **argv_; - - std::vector singletons_; + template ::value>> + T &emplace() + { + auto t = new T; + this->singletons_.push_back(std::unique_ptr(t)); + return *t; + } }; Application *getApp(); -bool appInitialized(); - } // namespace chatterino diff --git a/src/BrowserExtension.cpp b/src/BrowserExtension.cpp new file mode 100644 index 000000000..3440170ab --- /dev/null +++ b/src/BrowserExtension.cpp @@ -0,0 +1,88 @@ +#include "BrowserExtension.hpp" + +#include "singletons/NativeMessaging.hpp" + +#include +#include +#include +#include +#include + +#ifdef Q_OS_WIN +#include +#include +#include +#endif + +namespace chatterino { + +namespace { +void initFileMode() +{ +#ifdef Q_OS_WIN + _setmode(_fileno(stdin), _O_BINARY); + _setmode(_fileno(stdout), _O_BINARY); +#endif +} + +void runLoop(NativeMessagingClient &client) +{ + while (true) { + char size_c[4]; + std::cin.read(size_c, 4); + + if (std::cin.eof()) { + break; + } + + uint32_t size = *reinterpret_cast(size_c); + +#if 0 + bool bigEndian = isBigEndian(); + // To avoid breaking strict-aliasing rules and potentially inducing undefined behaviour, the following code can be run instead + uint32_t size = 0; + if (bigEndian) { + size = size_c[3] | static_cast(size_c[2]) << 8 | + static_cast(size_c[1]) << 16 | static_cast(size_c[0]) << 24; + } else { + size = size_c[0] | static_cast(size_c[1]) << 8 | + static_cast(size_c[2]) << 16 | static_cast(size_c[3]) << 24; + } +#endif + + std::unique_ptr b(new char[size + 1]); + std::cin.read(b.get(), size); + *(b.get() + size) = '\0'; + + client.sendMessage(QByteArray::fromRawData(b.get(), static_cast(size))); + } +} +} // namespace + +bool shouldRunBrowserExtensionHost(const QStringList &args) +{ + return args.size() > 0 && + (args[0].startsWith("chrome-extension://") || args[0].endsWith(".json")); +} + +void runBrowserExtensionHost() +{ + initFileMode(); + + std::atomic ping(false); + + QTimer timer; + QObject::connect(&timer, &QTimer::timeout, [&ping] { + if (!ping.exchange(false)) { + _Exit(0); + } + }); + timer.setInterval(11000); + timer.start(); + + NativeMessagingClient client; + + runLoop(client); +} + +} // namespace chatterino diff --git a/src/BrowserExtension.hpp b/src/BrowserExtension.hpp new file mode 100644 index 000000000..0232ac2b8 --- /dev/null +++ b/src/BrowserExtension.hpp @@ -0,0 +1,10 @@ +#pragma once + +class QStringList; + +namespace chatterino { + +bool shouldRunBrowserExtensionHost(const QStringList &args); +void runBrowserExtensionHost(); + +} // namespace chatterino diff --git a/src/RunGui.cpp b/src/RunGui.cpp new file mode 100644 index 000000000..a15bce6bc --- /dev/null +++ b/src/RunGui.cpp @@ -0,0 +1,130 @@ +#include "RunGui.hpp" + +#include +#include +#include +#include + +#include "Application.hpp" +#include "common/NetworkManager.hpp" +#include "singletons/Paths.hpp" +#include "singletons/Updates.hpp" +#include "widgets/dialogs/LastRunCrashDialog.hpp" + +#ifdef C_USE_BREAKPAD +#include +#endif + +// void initQt(); +// void installCustomPalette(); +// void showLastCrashDialog(); +// void createRunningFile(const QString &path); +// void removeRunningFile(const QString &path); + +namespace chatterino { +namespace { +void installCustomPalette() +{ + // borrowed from + // https://stackoverflow.com/questions/15035767/is-the-qt-5-dark-fusion-theme-available-for-windows + QPalette darkPalette = qApp->palette(); + + darkPalette.setColor(QPalette::Window, QColor(22, 22, 22)); + darkPalette.setColor(QPalette::WindowText, Qt::white); + darkPalette.setColor(QPalette::Text, Qt::white); + darkPalette.setColor(QPalette::Disabled, QPalette::WindowText, QColor(127, 127, 127)); + darkPalette.setColor(QPalette::Base, QColor("#333")); + darkPalette.setColor(QPalette::AlternateBase, QColor("#444")); + darkPalette.setColor(QPalette::ToolTipBase, Qt::white); + darkPalette.setColor(QPalette::ToolTipText, Qt::white); + darkPalette.setColor(QPalette::Disabled, QPalette::Text, QColor(127, 127, 127)); + darkPalette.setColor(QPalette::Dark, QColor(35, 35, 35)); + darkPalette.setColor(QPalette::Shadow, QColor(20, 20, 20)); + darkPalette.setColor(QPalette::Button, QColor(70, 70, 70)); + darkPalette.setColor(QPalette::ButtonText, Qt::white); + darkPalette.setColor(QPalette::Disabled, QPalette::ButtonText, QColor(127, 127, 127)); + darkPalette.setColor(QPalette::BrightText, Qt::red); + darkPalette.setColor(QPalette::Link, QColor(42, 130, 218)); + darkPalette.setColor(QPalette::Highlight, QColor(42, 130, 218)); + darkPalette.setColor(QPalette::Disabled, QPalette::Highlight, QColor(80, 80, 80)); + darkPalette.setColor(QPalette::HighlightedText, Qt::white); + darkPalette.setColor(QPalette::Disabled, QPalette::HighlightedText, QColor(127, 127, 127)); + + qApp->setPalette(darkPalette); +} + +void initQt() +{ + // set up the QApplication flags + QApplication::setAttribute(Qt::AA_Use96Dpi, true); +#ifdef Q_OS_WIN32 + QApplication::setAttribute(Qt::AA_DisableHighDpiScaling, true); +#endif + + QApplication::setStyle(QStyleFactory::create("Fusion")); + + installCustomPalette(); +} + +void showLastCrashDialog() +{ +#ifndef C_DISABLE_CRASH_DIALOG + LastRunCrashDialog dialog; + + switch (dialog.exec()) { + case QDialog::Accepted: { + }; break; + default: { + _exit(0); + } + } +#endif +} + +void createRunningFile(const QString &path) +{ + QFile runningFile(path); + + runningFile.open(QIODevice::WriteOnly | QIODevice::Truncate); + runningFile.flush(); + runningFile.close(); +} + +void removeRunningFile(const QString &path) +{ + QFile::remove(path); +} +} // namespace + +void runGui(QApplication &a, Paths &paths, Settings &settings) +{ + chatterino::NetworkManager::init(); + chatterino::Updates::getInstance().checkForUpdates(); + +#ifdef C_USE_BREAKPAD + QBreakpadInstance.setDumpPath(app->paths->settingsFolderPath + "/Crashes"); +#endif + + // Running file + auto runningPath = paths.miscDirectory + "/running_" + paths.applicationFilePathHash; + + if (QFile::exists(runningPath)) { + showLastCrashDialog(); + } else { + createRunningFile(runningPath); + } + + Application app(settings, paths); + app.initialize(settings, paths); + app.run(a); + app.save(); + + removeRunningFile(runningPath); + + pajlada::Settings::SettingManager::gSave(); + + chatterino::NetworkManager::deinit(); + + _exit(0); +} +} // namespace chatterino diff --git a/src/RunGui.hpp b/src/RunGui.hpp new file mode 100644 index 000000000..338164404 --- /dev/null +++ b/src/RunGui.hpp @@ -0,0 +1,10 @@ +#pragma once + +class QApplication; + +namespace chatterino { +class Paths; +class Settings; + +void runGui(QApplication &a, Paths &paths, Settings &settings); +} // namespace chatterino diff --git a/src/autogenerated/ResourcesAutogen.cpp b/src/autogenerated/ResourcesAutogen.cpp new file mode 100644 index 000000000..c5035f2e6 --- /dev/null +++ b/src/autogenerated/ResourcesAutogen.cpp @@ -0,0 +1,44 @@ +#include "ResourcesAutogen.hpp" + +namespace chatterino { + +Resources2::Resources2() +{ + this->avatars.fourtf = QPixmap(":/avatars/fourtf.png"); + this->avatars.pajlada = QPixmap(":/avatars/pajlada.png"); + this->buttons.ban = QPixmap(":/buttons/ban.png"); + this->buttons.banRed = QPixmap(":/buttons/banRed.png"); + this->buttons.menuDark = QPixmap(":/buttons/menuDark.png"); + this->buttons.menuLight = QPixmap(":/buttons/menuLight.png"); + this->buttons.mod = QPixmap(":/buttons/mod.png"); + this->buttons.modModeDisabled = QPixmap(":/buttons/modModeDisabled.png"); + this->buttons.modModeDisabled2 = QPixmap(":/buttons/modModeDisabled2.png"); + this->buttons.modModeEnabled = QPixmap(":/buttons/modModeEnabled.png"); + this->buttons.modModeEnabled2 = QPixmap(":/buttons/modModeEnabled2.png"); + this->buttons.timeout = QPixmap(":/buttons/timeout.png"); + this->buttons.unban = QPixmap(":/buttons/unban.png"); + this->buttons.unmod = QPixmap(":/buttons/unmod.png"); + this->buttons.update = QPixmap(":/buttons/update.png"); + this->buttons.updateError = QPixmap(":/buttons/updateError.png"); + this->error = QPixmap(":/error.png"); + this->icon = QPixmap(":/icon.png"); + this->pajaDank = QPixmap(":/pajaDank.png"); + this->settings.aboutlogo = QPixmap(":/settings/aboutlogo.png"); + this->split.down = QPixmap(":/split/down.png"); + this->split.left = QPixmap(":/split/left.png"); + this->split.move = QPixmap(":/split/move.png"); + this->split.right = QPixmap(":/split/right.png"); + this->split.up = QPixmap(":/split/up.png"); + this->twitch.admin = QPixmap(":/twitch/admin.png"); + this->twitch.broadcaster = QPixmap(":/twitch/broadcaster.png"); + this->twitch.cheer1 = QPixmap(":/twitch/cheer1.png"); + this->twitch.globalmod = QPixmap(":/twitch/globalmod.png"); + this->twitch.moderator = QPixmap(":/twitch/moderator.png"); + this->twitch.prime = QPixmap(":/twitch/prime.png"); + this->twitch.staff = QPixmap(":/twitch/staff.png"); + this->twitch.subscriber = QPixmap(":/twitch/subscriber.png"); + this->twitch.turbo = QPixmap(":/twitch/turbo.png"); + this->twitch.verified = QPixmap(":/twitch/verified.png"); +} + +} // namespace chatterino \ No newline at end of file diff --git a/src/autogenerated/ResourcesAutogen.hpp b/src/autogenerated/ResourcesAutogen.hpp new file mode 100644 index 000000000..048a1a94b --- /dev/null +++ b/src/autogenerated/ResourcesAutogen.hpp @@ -0,0 +1,57 @@ +#include +#include "common/Singleton.hpp" + +namespace chatterino { + +class Resources2 : public Singleton { +public: + Resources2(); + + struct { + QPixmap fourtf; + QPixmap pajlada; + } avatars; + struct { + QPixmap ban; + QPixmap banRed; + QPixmap menuDark; + QPixmap menuLight; + QPixmap mod; + QPixmap modModeDisabled; + QPixmap modModeDisabled2; + QPixmap modModeEnabled; + QPixmap modModeEnabled2; + QPixmap timeout; + QPixmap unban; + QPixmap unmod; + QPixmap update; + QPixmap updateError; + } buttons; + QPixmap error; + QPixmap icon; + QPixmap pajaDank; + struct { + QPixmap aboutlogo; + } settings; + struct { + QPixmap down; + QPixmap left; + QPixmap move; + QPixmap right; + QPixmap up; + } split; + struct { + QPixmap admin; + QPixmap broadcaster; + QPixmap cheer1; + QPixmap globalmod; + QPixmap moderator; + QPixmap prime; + QPixmap staff; + QPixmap subscriber; + QPixmap turbo; + QPixmap verified; + } twitch; +}; + +} // namespace chatterino \ No newline at end of file diff --git a/src/common/Aliases.hpp b/src/common/Aliases.hpp new file mode 100644 index 000000000..8a61a9639 --- /dev/null +++ b/src/common/Aliases.hpp @@ -0,0 +1,34 @@ +#pragma once + +#include +#include +#include + +#define QStringAlias(name) \ + namespace chatterino { \ + struct name { \ + QString string; \ + bool operator==(const name &other) const \ + { \ + return this->string == other.string; \ + } \ + bool operator!=(const name &other) const \ + { \ + return this->string != other.string; \ + } \ + }; \ + } /* namespace chatterino */ \ + namespace std { \ + template <> \ + struct hash { \ + size_t operator()(const chatterino::name &s) const \ + { \ + return qHash(s.string); \ + } \ + }; \ + } /* namespace std */ + +QStringAlias(UserName); +QStringAlias(UserId); +QStringAlias(Url); +QStringAlias(Tooltip); diff --git a/src/common/Channel.cpp b/src/common/Channel.cpp index a0ffc511f..c03019c43 100644 --- a/src/common/Channel.cpp +++ b/src/common/Channel.cpp @@ -17,9 +17,9 @@ namespace chatterino { -Channel::Channel(const QString &_name, Type type) - : name(_name) - , completionModel(this->name) +Channel::Channel(const QString &name, Type type) + : completionModel(name) + , name_(name) , type_(type) { QObject::connect(&this->clearCompletionModelTimer_, &QTimer::timeout, [this]() { @@ -38,6 +38,11 @@ Channel::Type Channel::getType() const return this->type_; } +const QString &Channel::getName() const +{ + return this->name_; +} + bool Channel::isTwitchChannel() const { return this->type_ >= Type::Twitch && this->type_ < Type::TwitchEnd; @@ -45,7 +50,7 @@ bool Channel::isTwitchChannel() const bool Channel::isEmpty() const { - return this->name.isEmpty(); + return this->name_.isEmpty(); } LimitedQueueSnapshot Channel::getMessageSnapshot() @@ -66,7 +71,7 @@ void Channel::addMessage(MessagePtr message) // FOURTF: change this when adding more providers if (this->isTwitchChannel()) { - app->logging->addMessage(this->name, message); + app->logging->addMessage(this->name_, message); } if (this->messages_.pushBack(message, deleted)) { diff --git a/src/common/Channel.hpp b/src/common/Channel.hpp index 99172c108..e153914c1 100644 --- a/src/common/Channel.hpp +++ b/src/common/Channel.hpp @@ -41,6 +41,7 @@ public: pajlada::Signals::NoArgSignal destroyed; Type getType() const; + const QString &getName() const; bool isTwitchChannel() const; virtual bool isEmpty() const; LimitedQueueSnapshot getMessageSnapshot(); @@ -52,7 +53,6 @@ public: void replaceMessage(MessagePtr message, MessagePtr replacement); virtual void addRecentChatter(const std::shared_ptr &message); - QString name; QStringList modList; virtual bool canSendMessage() const; @@ -72,6 +72,7 @@ protected: virtual void onConnected(); private: + const QString name_; LimitedQueue messages_; Type type_; QTimer clearCompletionModelTimer_; diff --git a/src/common/Common.hpp b/src/common/Common.hpp index 3a573163d..18548cb3b 100644 --- a/src/common/Common.hpp +++ b/src/common/Common.hpp @@ -1,9 +1,13 @@ #pragma once +#include "common/Aliases.hpp" +#include "common/Outcome.hpp" +#include "common/ProviderId.hpp" #include "debug/Log.hpp" #include #include +#include #include #include @@ -27,14 +31,18 @@ const Qt::KeyboardModifiers showResizeHandlesModifiers = Qt::ControlModifier; static const char *ANONYMOUS_USERNAME_LABEL ATTR_UNUSED = " - anonymous - "; -#define return_if(condition) \ - if ((condition)) { \ - return; \ - } +template +std::weak_ptr weakOf(T *element) +{ + return element->shared_from_this(); +} -#define return_unless(condition) \ - if (!(condition)) { \ - return; \ - } +template +struct overloaded : Ts... { + using Ts::operator()...; +}; + +template +overloaded(Ts...)->overloaded; } // namespace chatterino diff --git a/src/common/CompletionModel.cpp b/src/common/CompletionModel.cpp index de5bafce8..f56425d27 100644 --- a/src/common/CompletionModel.cpp +++ b/src/common/CompletionModel.cpp @@ -2,8 +2,10 @@ #include "Application.hpp" #include "common/Common.hpp" +#include "controllers/accounts/AccountController.hpp" #include "controllers/commands/CommandController.hpp" #include "debug/Log.hpp" +#include "providers/twitch/TwitchServer.hpp" #include "singletons/Emotes.hpp" #include @@ -107,41 +109,45 @@ void CompletionModel::refresh() auto app = getApp(); // User-specific: Twitch Emotes - // TODO: Fix this so it properly updates with the proper api. oauth token needs proper scope - for (const auto &m : app->emotes->twitch.emotes) { - for (const auto &emoteName : m.second.emoteCodes) { + if (auto account = app->accounts->twitch.getCurrent()) { + for (const auto &emote : account->accessEmotes()->allEmoteNames) { // XXX: No way to discern between a twitch global emote and sub emote right now - this->addString(emoteName, TaggedString::Type::TwitchGlobalEmote); + this->addString(emote.string, TaggedString::Type::TwitchGlobalEmote); } } - // Global: BTTV Global Emotes - std::vector &bttvGlobalEmoteCodes = app->emotes->bttv.globalEmoteCodes; - for (const auto &m : bttvGlobalEmoteCodes) { - this->addString(m, TaggedString::Type::BTTVGlobalEmote); + // // Global: BTTV Global Emotes + // std::vector &bttvGlobalEmoteCodes = app->emotes->bttv.globalEmoteNames_; + // for (const auto &m : bttvGlobalEmoteCodes) { + // this->addString(m, TaggedString::Type::BTTVGlobalEmote); + // } + + // // Global: FFZ Global Emotes + // std::vector &ffzGlobalEmoteCodes = app->emotes->ffz.globalEmoteCodes; + // for (const auto &m : ffzGlobalEmoteCodes) { + // this->addString(m, TaggedString::Type::FFZGlobalEmote); + // } + + // Channel emotes + if (auto channel = dynamic_cast( + getApp()->twitch2->getChannelOrEmptyByID(this->channelName_).get())) { + auto bttv = channel->accessBttvEmotes(); + // auto it = bttv->begin(); + // for (const auto &emote : *bttv) { + // } + // std::vector &bttvChannelEmoteCodes = + // app->emotes->bttv.channelEmoteName_[this->channelName_]; + // for (const auto &m : bttvChannelEmoteCodes) { + // this->addString(m, TaggedString::Type::BTTVChannelEmote); + // } + + // Channel-specific: FFZ Channel Emotes + for (const auto &emote : *channel->accessFfzEmotes()) { + this->addString(emote.second->name.string, TaggedString::Type::FFZChannelEmote); + } } - // Global: FFZ Global Emotes - std::vector &ffzGlobalEmoteCodes = app->emotes->ffz.globalEmoteCodes; - for (const auto &m : ffzGlobalEmoteCodes) { - this->addString(m, TaggedString::Type::FFZGlobalEmote); - } - - // Channel-specific: BTTV Channel Emotes - std::vector &bttvChannelEmoteCodes = - app->emotes->bttv.channelEmoteCodes[this->channelName_]; - for (const auto &m : bttvChannelEmoteCodes) { - this->addString(m, TaggedString::Type::BTTVChannelEmote); - } - - // Channel-specific: FFZ Channel Emotes - std::vector &ffzChannelEmoteCodes = - app->emotes->ffz.channelEmoteCodes[this->channelName_]; - for (const auto &m : ffzChannelEmoteCodes) { - this->addString(m, TaggedString::Type::FFZChannelEmote); - } - - // Global: Emojis + // Emojis const auto &emojiShortCodes = app->emotes->emojis.shortCodes; for (const auto &m : emojiShortCodes) { this->addString(":" + m + ":", TaggedString::Type::Emoji); diff --git a/src/common/CompletionModel.hpp b/src/common/CompletionModel.hpp index 5bae63be0..d9baac7b3 100644 --- a/src/common/CompletionModel.hpp +++ b/src/common/CompletionModel.hpp @@ -10,6 +10,8 @@ namespace chatterino { +class TwitchChannel; + class CompletionModel : public QAbstractListModel { struct TaggedString { diff --git a/src/common/Emotemap.cpp b/src/common/Emotemap.cpp index b8903afd9..9bfae628b 100644 --- a/src/common/Emotemap.cpp +++ b/src/common/Emotemap.cpp @@ -5,42 +5,42 @@ namespace chatterino { -EmoteData::EmoteData(Image *image) - : image1x(image) -{ -} +// EmoteData::EmoteData(Image *image) +// : image1x(image) +//{ +//} -// Emotes must have a 1x image to be valid -bool EmoteData::isValid() const -{ - return this->image1x != nullptr; -} +//// Emotes must have a 1x image to be valid +// bool EmoteData::isValid() const +//{ +// return this->image1x != nullptr; +//} -Image *EmoteData::getImage(float scale) const -{ - int quality = getApp()->settings->preferredEmoteQuality; +// Image *EmoteData::getImage(float scale) const +//{ +// int quality = getApp()->settings->preferredEmoteQuality; - if (quality == 0) { - scale *= getApp()->settings->emoteScale.getValue(); - quality = [&] { - if (scale <= 1) - return 1; - if (scale <= 2) - return 2; - return 3; - }(); - } +// if (quality == 0) { +// scale *= getApp()->settings->emoteScale.getValue(); +// quality = [&] { +// if (scale <= 1) +// return 1; +// if (scale <= 2) +// return 2; +// return 3; +// }(); +// } - Image *_image; - if (quality == 3 && this->image3x != nullptr) { - _image = this->image3x; - } else if (quality >= 2 && this->image2x != nullptr) { - _image = this->image2x; - } else { - _image = this->image1x; - } +// Image *_image; +// if (quality == 3 && this->image3x != nullptr) { +// _image = this->image3x; +// } else if (quality >= 2 && this->image2x != nullptr) { +// _image = this->image2x; +// } else { +// _image = this->image1x; +// } - return _image; -} +// return _image; +//} } // namespace chatterino diff --git a/src/common/Emotemap.hpp b/src/common/Emotemap.hpp index d4010048d..c57f08e91 100644 --- a/src/common/Emotemap.hpp +++ b/src/common/Emotemap.hpp @@ -5,23 +5,23 @@ namespace chatterino { -struct EmoteData { - EmoteData() = default; +// struct EmoteData { +// EmoteData() = default; - EmoteData(Image *image); +// EmoteData(Image *image); - // Emotes must have a 1x image to be valid - bool isValid() const; - Image *getImage(float scale) const; +// // Emotes must have a 1x image to be valid +// bool isValid() const; +// Image *getImage(float scale) const; - // Link to the emote page i.e. https://www.frankerfacez.com/emoticon/144722-pajaCringe - QString pageLink; +// // Link to the emote page i.e. https://www.frankerfacez.com/emoticon/144722-pajaCringe +// QString pageLink; - Image *image1x = nullptr; - Image *image2x = nullptr; - Image *image3x = nullptr; -}; +// Image *image1x = nullptr; +// Image *image2x = nullptr; +// Image *image3x = nullptr; +//}; -using EmoteMap = ConcurrentMap; +// using EmoteMap = ConcurrentMap; } // namespace chatterino diff --git a/src/common/NetworkCommon.hpp b/src/common/NetworkCommon.hpp index 743eeee04..30d1e9267 100644 --- a/src/common/NetworkCommon.hpp +++ b/src/common/NetworkCommon.hpp @@ -2,13 +2,15 @@ #include +#include "Common.hpp" + class QNetworkReply; namespace chatterino { class NetworkResult; -using NetworkSuccessCallback = std::function; +using NetworkSuccessCallback = std::function; using NetworkErrorCallback = std::function; using NetworkReplyCreatedCallback = std::function; diff --git a/src/common/NetworkRequest.cpp b/src/common/NetworkRequest.cpp index 69c636128..976c0fa14 100644 --- a/src/common/NetworkRequest.cpp +++ b/src/common/NetworkRequest.cpp @@ -129,7 +129,7 @@ void NetworkRequest::execute() } } -bool NetworkRequest::tryLoadCachedFile() +Outcome NetworkRequest::tryLoadCachedFile() { auto app = getApp(); @@ -137,24 +137,24 @@ bool NetworkRequest::tryLoadCachedFile() if (!cachedFile.exists()) { // File didn't exist - return false; + return Failure; } if (!cachedFile.open(QIODevice::ReadOnly)) { // File could not be opened - return false; + return Failure; } QByteArray bytes = cachedFile.readAll(); NetworkResult result(bytes); - bool success = this->data->onSuccess_(result); + auto outcome = this->data->onSuccess_(result); cachedFile.close(); // XXX: If success is false, we should invalidate the cache file somehow/somewhere - return success; + return outcome; } void NetworkRequest::doRequest() diff --git a/src/common/NetworkRequest.hpp b/src/common/NetworkRequest.hpp index e2fd2e7e4..049c2cba1 100644 --- a/src/common/NetworkRequest.hpp +++ b/src/common/NetworkRequest.hpp @@ -33,7 +33,7 @@ public: explicit NetworkRequest(const std::string &url, NetworkRequestType requestType = NetworkRequestType::Get); - NetworkRequest(QUrl url, NetworkRequestType requestType = NetworkRequestType::Get); + explicit NetworkRequest(QUrl url, NetworkRequestType requestType = NetworkRequestType::Get); ~NetworkRequest(); @@ -58,7 +58,7 @@ private: // Returns true if the file was successfully loaded from cache // Returns false if the cache file either didn't exist, or it contained "invalid" data // "invalid" is specified by the onSuccess callback - bool tryLoadCachedFile(); + Outcome tryLoadCachedFile(); void doRequest(); diff --git a/src/common/NetworkResult.cpp b/src/common/NetworkResult.cpp index 6d8b70067..0620bf3c2 100644 --- a/src/common/NetworkResult.cpp +++ b/src/common/NetworkResult.cpp @@ -38,7 +38,7 @@ rapidjson::Document NetworkResult::parseRapidJson() const return ret; } -QByteArray NetworkResult::getData() const +const QByteArray &NetworkResult::getData() const { return this->data_; } diff --git a/src/common/NetworkResult.hpp b/src/common/NetworkResult.hpp index 5631e1fe4..36a23de22 100644 --- a/src/common/NetworkResult.hpp +++ b/src/common/NetworkResult.hpp @@ -7,14 +7,15 @@ namespace chatterino { class NetworkResult { - QByteArray data_; - public: NetworkResult(const QByteArray &data); QJsonObject parseJson() const; rapidjson::Document parseRapidJson() const; - QByteArray getData() const; + const QByteArray &getData() const; + +private: + QByteArray data_; }; } // namespace chatterino diff --git a/src/common/NullablePtr.hpp b/src/common/NullablePtr.hpp index f38ecfa5d..ba08bb7f9 100644 --- a/src/common/NullablePtr.hpp +++ b/src/common/NullablePtr.hpp @@ -1,5 +1,7 @@ #pragma once +#include + namespace chatterino { template @@ -23,7 +25,7 @@ public: return element_; } - T &operator*() const + typename std::add_lvalue_reference::type &operator*() const { assert(this->hasElement()); @@ -52,6 +54,17 @@ public: return this->hasElement(); } + bool operator!() const + { + return !this->hasElement(); + } + + template ::value>> + operator NullablePtr() const + { + return NullablePtr(this->element_); + } + private: T *element_; }; diff --git a/src/common/Outcome.hpp b/src/common/Outcome.hpp new file mode 100644 index 000000000..01be69fd8 --- /dev/null +++ b/src/common/Outcome.hpp @@ -0,0 +1,51 @@ +#pragma once + +namespace chatterino { + +struct SuccessTag { +}; + +struct FailureTag { +}; + +const SuccessTag Success{}; +const FailureTag Failure{}; + +class Outcome +{ +public: + Outcome(SuccessTag) + : success_(true) + { + } + + Outcome(FailureTag) + : success_(false) + { + } + + explicit operator bool() const + { + return this->success_; + } + + bool operator!() const + { + return !this->success_; + } + + bool operator==(const Outcome &other) const + { + return this->success_ == other.success_; + } + + bool operator!=(const Outcome &other) const + { + return !this->operator==(other); + } + +private: + bool success_; +}; + +} // namespace chatterino diff --git a/src/common/Singleton.hpp b/src/common/Singleton.hpp index 474c31f0f..a98dedac2 100644 --- a/src/common/Singleton.hpp +++ b/src/common/Singleton.hpp @@ -4,14 +4,18 @@ namespace chatterino { -class Application; +class Settings; +class Paths; class Singleton : boost::noncopyable { public: - virtual void initialize(Application &app) + virtual ~Singleton() = default; + + virtual void initialize(Settings &settings, Paths &paths) { - (void)(app); + (void)(settings); + (void)(paths); } virtual void save() diff --git a/src/common/UniqueAccess.hpp b/src/common/UniqueAccess.hpp index c4013af3e..ef7f0165f 100644 --- a/src/common/UniqueAccess.hpp +++ b/src/common/UniqueAccess.hpp @@ -1,12 +1,13 @@ #pragma once +#include #include #include namespace chatterino { template -class AccessGuard +class AccessGuard : boost::noncopyable { public: AccessGuard(T &element, std::mutex &mutex) @@ -21,31 +22,16 @@ public: this->mutex_.unlock(); } - const T *operator->() const + T *operator->() const { return &this->element_; } - T *operator->() - { - return &this->element_; - } - - const T &operator*() const + T &operator*() const { return this->element_; } - T &operator*() - { - return this->element_; - } - - T clone() const - { - return T(this->element_); - } - private: T &element_; std::mutex &mutex_; @@ -55,7 +41,7 @@ template class UniqueAccess { public: - template +// template UniqueAccess() : element_(T()) { @@ -83,14 +69,15 @@ public: return *this; } - AccessGuard access() + AccessGuard access() const { return AccessGuard(this->element_, this->mutex_); } - const AccessGuard access() const + template >> + AccessGuard accessConst() const { - return AccessGuard(this->element_, this->mutex_); + return AccessGuard(this->element_, this->mutex_); } private: diff --git a/src/controllers/accounts/AccountController.cpp b/src/controllers/accounts/AccountController.cpp index 0341a06a6..888c21152 100644 --- a/src/controllers/accounts/AccountController.cpp +++ b/src/controllers/accounts/AccountController.cpp @@ -33,7 +33,7 @@ AccountController::AccountController() }); } -void AccountController::initialize(Application &app) +void AccountController::initialize(Settings &settings, Paths &paths) { this->twitch.load(); } diff --git a/src/controllers/accounts/AccountController.hpp b/src/controllers/accounts/AccountController.hpp index 69fc7f092..25f0fee5c 100644 --- a/src/controllers/accounts/AccountController.hpp +++ b/src/controllers/accounts/AccountController.hpp @@ -11,16 +11,19 @@ namespace chatterino { +class Settings; +class Paths; + class AccountModel; -class AccountController : public Singleton +class AccountController final : public Singleton { public: AccountController(); AccountModel *createModel(QObject *parent); - virtual void initialize(Application &app) override; + virtual void initialize(Settings &settings, Paths &paths) override; TwitchAccountManager twitch; diff --git a/src/controllers/commands/CommandController.cpp b/src/controllers/commands/CommandController.cpp index 47c4e25db..1d85052e1 100644 --- a/src/controllers/commands/CommandController.cpp +++ b/src/controllers/commands/CommandController.cpp @@ -11,6 +11,7 @@ #include "providers/twitch/TwitchServer.hpp" #include "singletons/Paths.hpp" #include "singletons/Settings.hpp" +#include "util/CombinePath.hpp" #include "widgets/dialogs/LogsPopup.hpp" #include @@ -44,15 +45,14 @@ CommandController::CommandController() this->items.itemRemoved.connect(addFirstMatchToMap); } -void CommandController::initialize(Application &app) +void CommandController::initialize(Settings &, Paths &paths) { - this->load(); + this->load(paths); } -void CommandController::load() +void CommandController::load(Paths &paths) { - auto app = getApp(); - this->filePath_ = app->paths->settingsDirectory + "/commands.txt"; + this->filePath_ = combinePath(paths.settingsDirectory, "commands.txt"); QFile textFile(this->filePath_); if (!textFile.open(QIODevice::ReadOnly)) { diff --git a/src/controllers/commands/CommandController.hpp b/src/controllers/commands/CommandController.hpp index c1cbfd855..45aba5bcc 100644 --- a/src/controllers/commands/CommandController.hpp +++ b/src/controllers/commands/CommandController.hpp @@ -10,11 +10,14 @@ #include "controllers/commands/Command.hpp" namespace chatterino { + +class Settings; +class Paths; class Channel; class CommandModel; -class CommandController : public Singleton +class CommandController final : public Singleton { public: CommandController(); @@ -22,7 +25,7 @@ public: QString execCommand(const QString &text, std::shared_ptr channel, bool dryRun); QStringList getDefaultTwitchCommandList(); - virtual void initialize(Application &app) override; + virtual void initialize(Settings &settings, Paths &paths) override; virtual void save() override; CommandModel *createModel(QObject *parent); @@ -30,7 +33,7 @@ public: UnsortedSignalVector items; private: - void load(); + void load(Paths &paths); QMap commandsMap_; diff --git a/src/controllers/highlights/HighlightController.cpp b/src/controllers/highlights/HighlightController.cpp index 02c420c79..69cb3d681 100644 --- a/src/controllers/highlights/HighlightController.cpp +++ b/src/controllers/highlights/HighlightController.cpp @@ -12,7 +12,7 @@ HighlightController::HighlightController() { } -void HighlightController::initialize(Application &app) +void HighlightController::initialize(Settings &settings, Paths &paths) { assert(!this->initialized_); this->initialized_ = true; diff --git a/src/controllers/highlights/HighlightController.hpp b/src/controllers/highlights/HighlightController.hpp index a881499cf..8afac7947 100644 --- a/src/controllers/highlights/HighlightController.hpp +++ b/src/controllers/highlights/HighlightController.hpp @@ -10,16 +10,19 @@ namespace chatterino { +class Settings; +class Paths; + class UserHighlightModel; class HighlightModel; class HighlightBlacklistModel; -class HighlightController : public Singleton +class HighlightController final : public Singleton { public: HighlightController(); - virtual void initialize(Application &app) override; + virtual void initialize(Settings &settings, Paths &paths) override; UnsortedSignalVector phrases; UnsortedSignalVector blacklistedUsers; diff --git a/src/controllers/ignores/IgnoreController.cpp b/src/controllers/ignores/IgnoreController.cpp index 5aa271cb9..819779eff 100644 --- a/src/controllers/ignores/IgnoreController.cpp +++ b/src/controllers/ignores/IgnoreController.cpp @@ -7,7 +7,7 @@ namespace chatterino { -void IgnoreController::initialize(Application &) +void IgnoreController::initialize(Settings &, Paths &) { assert(!this->initialized_); this->initialized_ = true; diff --git a/src/controllers/ignores/IgnoreController.hpp b/src/controllers/ignores/IgnoreController.hpp index f03460131..09c109186 100644 --- a/src/controllers/ignores/IgnoreController.hpp +++ b/src/controllers/ignores/IgnoreController.hpp @@ -8,12 +8,15 @@ namespace chatterino { +class Settings; +class Paths; + class IgnoreModel; -class IgnoreController : public Singleton +class IgnoreController final : public Singleton { public: - virtual void initialize(Application &app) override; + virtual void initialize(Settings &settings, Paths &paths) override; UnsortedSignalVector phrases; diff --git a/src/controllers/moderationactions/ModerationAction.cpp b/src/controllers/moderationactions/ModerationAction.cpp index eb477602f..d3f07b172 100644 --- a/src/controllers/moderationactions/ModerationAction.cpp +++ b/src/controllers/moderationactions/ModerationAction.cpp @@ -1,5 +1,6 @@ #include "ModerationAction.hpp" +#include #include "Application.hpp" #include "singletons/Resources.hpp" @@ -57,7 +58,7 @@ ModerationAction::ModerationAction(const QString &action) // this->_moderationActions.emplace_back(app->resources->buttonTimeout, str); // } } else if (action.startsWith("/ban ")) { - this->image_ = getApp()->resources->buttonBan; + this->image_ = Image::fromNonOwningPixmap(&getApp()->resources->buttons.ban); } else { QString xD = action; @@ -75,10 +76,10 @@ bool ModerationAction::operator==(const ModerationAction &other) const bool ModerationAction::isImage() const { - return this->image_ != nullptr; + return bool(this->image_); } -Image *ModerationAction::getImage() const +const boost::optional &ModerationAction::getImage() const { return this->image_; } diff --git a/src/controllers/moderationactions/ModerationAction.hpp b/src/controllers/moderationactions/ModerationAction.hpp index bde1040c9..f509f79c5 100644 --- a/src/controllers/moderationactions/ModerationAction.hpp +++ b/src/controllers/moderationactions/ModerationAction.hpp @@ -1,14 +1,14 @@ #pragma once #include +#include #include +#include "messages/Image.hpp" #include "util/RapidjsonHelpers.hpp" namespace chatterino { -class Image; - class ModerationAction { public: @@ -17,13 +17,13 @@ public: bool operator==(const ModerationAction &other) const; bool isImage() const; - Image *getImage() const; + const boost::optional &getImage() const; const QString &getLine1() const; const QString &getLine2() const; const QString &getAction() const; private: - Image *image_ = nullptr; + boost::optional image_; QString line1_; QString line2_; QString action_; diff --git a/src/controllers/moderationactions/ModerationActions.cpp b/src/controllers/moderationactions/ModerationActions.cpp index c130e65ad..68070f6b2 100644 --- a/src/controllers/moderationactions/ModerationActions.cpp +++ b/src/controllers/moderationactions/ModerationActions.cpp @@ -12,7 +12,7 @@ ModerationActions::ModerationActions() { } -void ModerationActions::initialize(Application &app) +void ModerationActions::initialize(Settings &settings, Paths &paths) { assert(!this->initialized_); this->initialized_ = true; diff --git a/src/controllers/moderationactions/ModerationActions.hpp b/src/controllers/moderationactions/ModerationActions.hpp index 934e2f95e..6ecc3281d 100644 --- a/src/controllers/moderationactions/ModerationActions.hpp +++ b/src/controllers/moderationactions/ModerationActions.hpp @@ -8,6 +8,9 @@ namespace chatterino { +class Settings; +class Paths; + class ModerationActionModel; class ModerationActions final : public Singleton @@ -15,7 +18,7 @@ class ModerationActions final : public Singleton public: ModerationActions(); - virtual void initialize(Application &app) override; + virtual void initialize(Settings &settings, Paths &paths) override; UnsortedSignalVector items; diff --git a/src/controllers/taggedusers/TaggedUsersController.hpp b/src/controllers/taggedusers/TaggedUsersController.hpp index cfd98b620..263ff391c 100644 --- a/src/controllers/taggedusers/TaggedUsersController.hpp +++ b/src/controllers/taggedusers/TaggedUsersController.hpp @@ -9,7 +9,7 @@ namespace chatterino { class TaggedUsersModel; -class TaggedUsersController : public Singleton +class TaggedUsersController final : public Singleton { public: TaggedUsersController(); diff --git a/src/debug/Log.hpp b/src/debug/Log.hpp index 6889139ee..51ef0f053 100644 --- a/src/debug/Log.hpp +++ b/src/debug/Log.hpp @@ -14,4 +14,11 @@ inline void Log(const std::string &formatString, Args &&... args) << fS(formatString, std::forward(args)...).c_str(); } +template +inline void Warn(const std::string &formatString, Args &&... args) +{ + qWarning() << QTime::currentTime().toString("hh:mm:ss.zzz") + << fS(formatString, std::forward(args)...).c_str(); +} + } // namespace chatterino diff --git a/src/main.cpp b/src/main.cpp index 9be014123..4ebb846a3 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,221 +1,28 @@ -#include "Application.hpp" -#include "common/NetworkManager.hpp" -#include "singletons/NativeMessaging.hpp" +#include "BrowserExtension.hpp" +#include "RunGui.hpp" #include "singletons/Paths.hpp" -#include "singletons/Updates.hpp" -#include "util/DebugCount.hpp" -#include "widgets/dialogs/LastRunCrashDialog.hpp" +#include "singletons/Settings.hpp" -#include #include -#include -#include #include -#include -#include -#include -#include +using namespace chatterino; -#ifdef Q_OS_WIN -#include -#include -#include -#endif - -#ifdef C_USE_BREAKPAD -#include -#endif - -int runGui(QApplication &a, int argc, char *argv[]); -void runNativeMessagingHost(); -void installCustomPalette(); - -// -// Main entry point of the application. -// Decides if it should run in gui mode, daemon mode, ... -// Sets up the QApplication -// -int main(int argc, char *argv[]) +int main(int argc, char **argv) { - // set up the QApplication flags - QApplication::setAttribute(Qt::AA_Use96Dpi, true); -#ifdef Q_OS_WIN32 - QApplication::setAttribute(Qt::AA_DisableHighDpiScaling, true); -#endif - // QApplication::setAttribute(Qt::AA_UseSoftwareOpenGL, true); - - // instanciate the QApplication QApplication a(argc, argv); - // FOURTF: might get arguments from the commandline passed in the future - chatterino::Paths::initInstance(); + // convert char[][] to QStringList + auto args = QStringList(); + std::transform(argv + 1, argv + argc, std::back_inserter(args), [&](auto s) { return s; }); - // read args - QStringList args; - - for (int i = 1; i < argc; i++) { - args << argv[i]; - } - - // run native messaging host for the browser extension - if (args.size() > 0 && - (args[0].startsWith("chrome-extension://") || args[0].endsWith(".json"))) { - runNativeMessagingHost(); - return 0; - } - - // run gui - return runGui(a, argc, argv); -} - -int runGui(QApplication &a, int argc, char *argv[]) -{ - QApplication::setStyle(QStyleFactory::create("Fusion")); - - installCustomPalette(); - - // Initialize NetworkManager - chatterino::NetworkManager::init(); - - // Check for upates - chatterino::Updates::getInstance().checkForUpdates(); - - // Initialize application - chatterino::Application::instantiate(argc, argv); - auto app = chatterino::getApp(); - - app->construct(); - -#ifdef C_USE_BREAKPAD - QBreakpadInstance.setDumpPath(app->paths->settingsFolderPath + "/Crashes"); -#endif - - auto &pathMan = *app->paths; - // Running file - auto runningPath = pathMan.miscDirectory + "/running_" + pathMan.applicationFilePathHash; - - if (QFile::exists(runningPath)) { -#ifndef C_DISABLE_CRASH_DIALOG - chatterino::LastRunCrashDialog dialog; - - switch (dialog.exec()) { - case QDialog::Accepted: { - }; break; - default: { - _exit(0); - } - } -#endif + // run in gui mode or browser extension host mode + if (shouldRunBrowserExtensionHost(args)) { + runBrowserExtensionHost(); } else { - QFile runningFile(runningPath); + Paths paths; + Settings settings(paths); - runningFile.open(QIODevice::WriteOnly | QIODevice::Truncate); - runningFile.flush(); - runningFile.close(); - } - - app->initialize(); - - // Start the application - // This is a blocking call - app->run(a); - - // We have finished our application, make sure we save stuff - app->save(); - - // Running file - QFile::remove(runningPath); - - // Save settings - pajlada::Settings::SettingManager::gSave(); - - // Deinitialize NetworkManager (stop thread and wait for finish, should be instant) - chatterino::NetworkManager::deinit(); - - // None of the singletons has a proper destructor - _exit(0); -} - -void runNativeMessagingHost() -{ - auto *nm = new chatterino::NativeMessaging; - -#ifdef Q_OS_WIN - _setmode(_fileno(stdin), _O_BINARY); - _setmode(_fileno(stdout), _O_BINARY); -#endif - -#if 0 - bool bigEndian = isBigEndian(); -#endif - - std::atomic ping(false); - - QTimer timer; - QObject::connect(&timer, &QTimer::timeout, [&ping] { - if (!ping.exchange(false)) { - _exit(0); - } - }); - timer.setInterval(11000); - timer.start(); - - while (true) { - char size_c[4]; - std::cin.read(size_c, 4); - - if (std::cin.eof()) { - break; - } - - uint32_t size = *reinterpret_cast(size_c); -#if 0 - // To avoid breaking strict-aliasing rules and potentially inducing undefined behaviour, the following code can be run instead - uint32_t size = 0; - if (bigEndian) { - size = size_c[3] | static_cast(size_c[2]) << 8 | - static_cast(size_c[1]) << 16 | static_cast(size_c[0]) << 24; - } else { - size = size_c[0] | static_cast(size_c[1]) << 8 | - static_cast(size_c[2]) << 16 | static_cast(size_c[3]) << 24; - } -#endif - - std::unique_ptr b(new char[size + 1]); - std::cin.read(b.get(), size); - *(b.get() + size) = '\0'; - - nm->sendToGuiProcess(QByteArray::fromRawData(b.get(), static_cast(size))); + runGui(a, paths, settings); } } - -void installCustomPalette() -{ - // borrowed from - // https://stackoverflow.com/questions/15035767/is-the-qt-5-dark-fusion-theme-available-for-windows - QPalette darkPalette = qApp->palette(); - - darkPalette.setColor(QPalette::Window, QColor(22, 22, 22)); - darkPalette.setColor(QPalette::WindowText, Qt::white); - darkPalette.setColor(QPalette::Text, Qt::white); - darkPalette.setColor(QPalette::Disabled, QPalette::WindowText, QColor(127, 127, 127)); - darkPalette.setColor(QPalette::Base, QColor("#333")); - darkPalette.setColor(QPalette::AlternateBase, QColor("#444")); - darkPalette.setColor(QPalette::ToolTipBase, Qt::white); - darkPalette.setColor(QPalette::ToolTipText, Qt::white); - darkPalette.setColor(QPalette::Disabled, QPalette::Text, QColor(127, 127, 127)); - darkPalette.setColor(QPalette::Dark, QColor(35, 35, 35)); - darkPalette.setColor(QPalette::Shadow, QColor(20, 20, 20)); - darkPalette.setColor(QPalette::Button, QColor(70, 70, 70)); - darkPalette.setColor(QPalette::ButtonText, Qt::white); - darkPalette.setColor(QPalette::Disabled, QPalette::ButtonText, QColor(127, 127, 127)); - darkPalette.setColor(QPalette::BrightText, Qt::red); - darkPalette.setColor(QPalette::Link, QColor(42, 130, 218)); - darkPalette.setColor(QPalette::Highlight, QColor(42, 130, 218)); - darkPalette.setColor(QPalette::Disabled, QPalette::Highlight, QColor(80, 80, 80)); - darkPalette.setColor(QPalette::HighlightedText, Qt::white); - darkPalette.setColor(QPalette::Disabled, QPalette::HighlightedText, QColor(127, 127, 127)); - - qApp->setPalette(darkPalette); -} diff --git a/src/messages/Emote.cpp b/src/messages/Emote.cpp new file mode 100644 index 000000000..369725426 --- /dev/null +++ b/src/messages/Emote.cpp @@ -0,0 +1,75 @@ +#include "Emote.hpp" + +#include + +namespace chatterino { + +bool operator==(const Emote &a, const Emote &b) +{ + return std::tie(a.homePage, a.name, a.tooltip, a.images) == + std::tie(b.homePage, b.name, b.tooltip, b.images); +} + +bool operator!=(const Emote &a, const Emote &b) +{ + return !(a == b); +} + +// EmotePtr Emote::create(const EmoteData2 &data) +//{ +//} + +// EmotePtr Emote::create(EmoteData2 &&data) +//{ +//} + +// Emote::Emote(EmoteData2 &&data) +// : data_(data) +//{ +//} +// +// Emote::Emote(const EmoteData2 &data) +// : data_(data) +//{ +//} +// +// const Url &Emote::getHomePage() const +//{ +// return this->data_.homePage; +//} +// +// const EmoteName &Emote::getName() const +//{ +// return this->data_.name; +//} +// +// const Tooltip &Emote::getTooltip() const +//{ +// return this->data_.tooltip; +//} +// +// const ImageSet &Emote::getImages() const +//{ +// return this->data_.images; +//} +// +// const QString &Emote::getCopyString() const +//{ +// return this->data_.name.string; +//} +// +// bool Emote::operator==(const Emote &other) const +//{ +// auto &a = this->data_; +// auto &b = other.data_; +// +// return std::tie(a.homePage, a.name, a.tooltip, a.images) == +// std::tie(b.homePage, b.name, b.tooltip, b.images); +//} +// +// bool Emote::operator!=(const Emote &other) const +//{ +// return !this->operator==(other); +//} + +} // namespace chatterino diff --git a/src/messages/Emote.hpp b/src/messages/Emote.hpp new file mode 100644 index 000000000..c5817752e --- /dev/null +++ b/src/messages/Emote.hpp @@ -0,0 +1,65 @@ +#pragma once + +#include "messages/Image.hpp" +#include "messages/ImageSet.hpp" + +#include +#include +#include + +QStringAlias(EmoteId); +QStringAlias(EmoteName); + +namespace chatterino { + +struct Emote { + EmoteName name; + ImageSet images; + Tooltip tooltip; + Url homePage; + + // FOURTF: no solution yet, to be refactored later + const QString &getCopyString() const + { + return name.string; + } +}; + +bool operator==(const Emote &a, const Emote &b); +bool operator!=(const Emote &a, const Emote &b); + +using EmotePtr = std::shared_ptr; + +class EmoteMap : public std::unordered_map +{ +}; +using EmoteIdMap = std::unordered_map; +using WeakEmoteMap = std::unordered_map>; +using WeakEmoteIdMap = std::unordered_map>; + +// struct EmoteData2 { +// EmoteName name; +// ImageSet images; +// Tooltip tooltip; +// Url homePage; +//}; +// +// class Emote +//{ +// public: +// Emote(EmoteData2 &&data); +// Emote(const EmoteData2 &data); +// +// const Url &getHomePage() const; +// const EmoteName &getName() const; +// const Tooltip &getTooltip() const; +// const ImageSet &getImages() const; +// const QString &getCopyString() const; +// bool operator==(const Emote &other) const; +// bool operator!=(const Emote &other) const; +// +// private: +// EmoteData2 data_; +//}; + +} // namespace chatterino diff --git a/src/messages/EmoteCache.hpp b/src/messages/EmoteCache.hpp new file mode 100644 index 000000000..1bbb01fd4 --- /dev/null +++ b/src/messages/EmoteCache.hpp @@ -0,0 +1,93 @@ +#pragma once + +#include +#include +#include +#include + +#include "common/UniqueAccess.hpp" +#include "messages/Emote.hpp" + +namespace chatterino { + +template +class MapReplacement +{ +public: + MapReplacement(std::unordered_map &items) + : oldItems_(items) + { + } + + void add(const TKey &key, const Emote &data) + { + this->add(key, Emote(data)); + } + + void add(const TKey &key, Emote &&data) + { + auto it = this->oldItems_.find(key); + if (it != this->oldItems_.end() && *it->second == data) { + this->newItems_[key] = it->second; + } else { + this->newItems_[key] = std::make_shared(std::move(data)); + } + } + + void apply() + { + this->oldItems_ = std::move(this->newItems_); + } + +private: + std::unordered_map &oldItems_; + std::unordered_map newItems_; +}; + +template +class EmoteCache +{ +public: + using Iterator = typename std::unordered_map::iterator; + using ConstIterator = typename std::unordered_map::iterator; + + Iterator begin() + { + return this->items_.begin(); + } + + ConstIterator begin() const + { + return this->items_.begin(); + } + + Iterator end() + { + return this->items_.end(); + } + + ConstIterator end() const + { + return this->items_.end(); + } + + boost::optional get(const TKey &key) const + { + auto it = this->items_.find(key); + + if (it == this->items_.end()) + return boost::none; + else + return it->second; + } + + MapReplacement makeReplacment() + { + return MapReplacement(this->items_); + } + +private: + std::unordered_map items_; +}; + +} // namespace chatterino diff --git a/src/messages/EmoteMap.cpp b/src/messages/EmoteMap.cpp new file mode 100644 index 000000000..f940de852 --- /dev/null +++ b/src/messages/EmoteMap.cpp @@ -0,0 +1,44 @@ +#include "EmoteMap.hpp" + +#include "Application.hpp" +#include "singletons/Settings.hpp" + +namespace chatterino { + +// EmoteData::EmoteData(Image *image) +// : image1x(image) +//{ +//} + +//// Emotes must have a 1x image to be valid +// bool EmoteData::isValid() const +//{ +// return this->image1x != nullptr; +//} + +// Image *EmoteData::getImage(float scale) const +//{ +// int quality = getApp()->settings->preferredEmoteQuality; + +// if (quality == 0) { +// scale *= getApp()->settings->emoteScale.getValue(); +// quality = [&] { +// if (scale <= 1) return 1; +// if (scale <= 2) return 2; +// return 3; +// }(); +// } + +// Image *_image; +// if (quality == 3 && this->image3x != nullptr) { +// _image = this->image3x; +// } else if (quality >= 2 && this->image2x != nullptr) { +// _image = this->image2x; +// } else { +// _image = this->image1x; +// } + +// return _image; +//} + +} // namespace chatterino diff --git a/src/messages/EmoteMap.hpp b/src/messages/EmoteMap.hpp new file mode 100644 index 000000000..30a0dfdd1 --- /dev/null +++ b/src/messages/EmoteMap.hpp @@ -0,0 +1,20 @@ +#pragma once + +#include "boost/optional.hpp" +#include "messages/Emote.hpp" + +namespace chatterino { + +// class EmoteMap +//{ +// public: +// void add(Emote emote); +// void remove(const Emote &emote); +// void remove(const QString &name); + +// private: +//}; + +// using EmoteMap = ConcurrentMap; + +} // namespace chatterino diff --git a/src/messages/Image.cpp b/src/messages/Image.cpp index 11cd4525c..b1fcf8787 100644 --- a/src/messages/Image.cpp +++ b/src/messages/Image.cpp @@ -2,6 +2,7 @@ #include "Application.hpp" #include "common/NetworkRequest.hpp" +#include "debug/AssertInGuiThread.hpp" #include "debug/Log.hpp" #include "singletons/Emotes.hpp" #include "singletons/WindowManager.hpp" @@ -19,258 +20,308 @@ namespace chatterino { -bool Image::loadedEventQueued = false; +// IMAGE2 +std::atomic Image::loadedEventQueued{false}; -Image::Image(const QString &url, qreal scale, const QString &name, const QString &tooltip, - const QMargins &margin, bool isHat) - : url(url) - , name(name) - , tooltip(tooltip) - , margin(margin) - , ishat(isHat) - , scale(scale) +ImagePtr Image::fromUrl(const Url &url, qreal scale) { - DebugCount::increase("images"); + // herb sutter cache + static std::unordered_map> cache; + static std::mutex mutex; + + std::lock_guard lock(mutex); + + auto shared = cache[url].lock(); + + if (!shared) { + cache[url] = shared = ImagePtr(new Image(url, scale)); + } else { + Warn("same image loaded multiple times: {}", url.string); + } + + return shared; } -Image::Image(QPixmap *image, qreal scale, const QString &name, const QString &tooltip, - const QMargins &margin, bool isHat) - : currentPixmap(image) - , name(name) - , tooltip(tooltip) - , margin(margin) - , ishat(isHat) - , scale(scale) - , isLoading(true) - , isLoaded(true) +ImagePtr Image::fromOwningPixmap(std::unique_ptr pixmap, qreal scale) { - DebugCount::increase("images"); + return ImagePtr(new Image(std::move(pixmap), scale)); } -Image::~Image() +ImagePtr Image::fromNonOwningPixmap(QPixmap *pixmap, qreal scale) { - DebugCount::decrease("images"); + return ImagePtr(new Image(pixmap, scale)); +} - if (this->isAnimated()) { +ImagePtr Image::getEmpty() +{ + static auto empty = ImagePtr(new Image); + return empty; +} + +Image::Image() +{ + this->isLoaded_ = true; + this->isNull_ = true; +} + +Image::Image(const Url &url, qreal scale) +{ + this->url_ = url; + this->scale_ = scale; + + if (url.string.isEmpty()) { + this->isLoaded_ = true; + this->isNull_ = true; + } +} + +Image::Image(std::unique_ptr owning, qreal scale) +{ + this->frames_.push_back(Frame(std::move(owning))); + this->scale_ = scale; + this->isLoaded_ = true; + this->currentFramePixmap_ = this->frames_.front().getPixmap(); +} + +Image::Image(QPixmap *nonOwning, qreal scale) +{ + this->frames_.push_back(Frame(nonOwning)); + this->scale_ = scale; + this->isLoaded_ = true; + this->currentFramePixmap_ = this->frames_.front().getPixmap(); +} + +const Url &Image::getUrl() const +{ + return this->url_; +} + +NullablePtr Image::getPixmap() const +{ + assertInGuiThread(); + + if (!this->isLoaded_) { + const_cast(this)->load(); + } + + return this->currentFramePixmap_; +} + +qreal Image::getScale() const +{ + return this->scale_; +} + +bool Image::isAnimated() const +{ + return this->isAnimated_; +} + +int Image::getWidth() const +{ + if (!this->isLoaded_) return 16; + + return this->frames_.front().getPixmap()->width() * this->scale_; +} + +int Image::getHeight() const +{ + if (!this->isLoaded_) return 16; + + return this->frames_.front().getPixmap()->height() * this->scale_; +} + +bool Image::isLoaded() const +{ + return this->isLoaded_; +} + +bool Image::isValid() const +{ + return !this->isNull_; +} + +bool Image::isNull() const +{ + return this->isNull_; +} + +void Image::load() +{ + // decrease debug count + if (this->isAnimated_) { DebugCount::decrease("animated images"); } - - if (this->isLoaded) { + if (this->isLoaded_) { DebugCount::decrease("loaded images"); } -} -void Image::loadImage() -{ - NetworkRequest req(this->getUrl()); - req.setCaller(this); + this->isLoaded_ = false; + this->isLoading_ = true; + this->frames_.clear(); + + NetworkRequest req(this->getUrl().string); + req.setCaller(&this->object_); req.setUseQuickLoadCache(true); - req.onSuccess([this](auto result) -> bool { - auto bytes = result.getData(); + req.onSuccess([this, weak = weakOf(this)](auto result) -> Outcome { + auto shared = weak.lock(); + if (!shared) return Failure; + + auto &bytes = result.getData(); QByteArray copy = QByteArray::fromRawData(bytes.constData(), bytes.length()); - QBuffer buffer(©); - buffer.open(QIODevice::ReadOnly); - QImage image; - QImageReader reader(&buffer); - - bool first = true; - - // clear stuff before loading the image again - this->allFrames.clear(); - if (this->isAnimated()) { - DebugCount::decrease("animated images"); - } - if (this->isLoaded) { - DebugCount::decrease("loaded images"); - } - - if (reader.imageCount() == -1) { - // An error occured in the reader - Log("An error occured reading the image: '{}'", reader.errorString()); - Log("Image url: {}", this->url); - return false; - } - - if (reader.imageCount() == 0) { - Log("Error: No images read in the buffer"); - // No images read in the buffer. maybe a cache error? - return false; - } - - for (int index = 0; index < reader.imageCount(); ++index) { - if (reader.read(&image)) { - auto pixmap = new QPixmap(QPixmap::fromImage(image)); - - if (first) { - first = false; - this->loadedPixmap = pixmap; - } - - Image::FrameData data; - data.duration = std::max(20, reader.nextImageDelay()); - data.image = pixmap; - - this->allFrames.push_back(data); - } - } - - if (this->allFrames.size() != reader.imageCount()) { - // Log("Error: Wrong amount of images read"); - // One or more images failed to load from the buffer - // return false; - } - - if (this->allFrames.size() > 1) { - if (!this->animated) { - postToThread([this] { - getApp()->emotes->gifTimer.signal.connect([=]() { - this->gifUpdateTimout(); - }); // For some reason when Boost signal is in - // thread scope and thread deletes the signal - // doesn't work, so this is the fix. - }); - } - - this->animated = true; - - DebugCount::increase("animated images"); - } - - this->currentPixmap = this->loadedPixmap; - - this->isLoaded = true; - DebugCount::increase("loaded images"); - - if (!loadedEventQueued) { - loadedEventQueued = true; - - QTimer::singleShot(500, [] { - getApp()->windows->incGeneration(); - - auto app = getApp(); - app->windows->layoutChannelViews(); - loadedEventQueued = false; - }); - } - - return true; + return this->parse(result.getData()); }); req.execute(); } -void Image::gifUpdateTimout() +Outcome Image::parse(const QByteArray &data) { - if (this->animated) { - this->currentFrameOffset += GIF_FRAME_LENGTH; + // const cast since we are only reading from it + QBuffer buffer(const_cast(&data)); + buffer.open(QIODevice::ReadOnly); + QImageReader reader(&buffer); + + return this->setFrames(this->readFrames(reader)); +} + +std::vector Image::readFrames(QImageReader &reader) +{ + std::vector frames; + + if (reader.imageCount() <= 0) { + Log("Error while reading image {}: '{}'", this->url_.string, reader.errorString()); + return frames; + } + + QImage image; + for (int index = 0; index < reader.imageCount(); ++index) { + if (reader.read(&image)) { + auto pixmap = new QPixmap(QPixmap::fromImage(image)); + + int duration = std::max(20, reader.nextImageDelay()); + frames.push_back(Image::Frame(pixmap, duration)); + } + } + + if (frames.size() != 0) { + Log("Error while reading image {}: '{}'", this->url_.string, reader.errorString()); + } + + return frames; +} + +Outcome Image::setFrames(std::vector frames) +{ + std::lock_guard lock(this->framesMutex_); + + if (frames.size() > 0) { + this->currentFramePixmap_ = frames.front().getPixmap(); + + if (frames.size() > 1) { + if (!this->isAnimated_) { + getApp()->emotes->gifTimer.signal.connect([=]() { this->updateAnimation(); }); + } + + this->isAnimated_ = true; + DebugCount::increase("animated images"); + } + + this->isLoaded_ = true; + DebugCount::increase("loaded images"); + + return Success; + } + + this->frames_ = std::move(frames); + this->queueLoadedEvent(); + + return Failure; +} + +void Image::queueLoadedEvent() +{ + if (!loadedEventQueued) { + loadedEventQueued = true; + + QTimer::singleShot(250, [] { + getApp()->windows->incGeneration(); + getApp()->windows->layoutChannelViews(); + loadedEventQueued = false; + }); + } +} + +void Image::updateAnimation() +{ + if (this->isAnimated_) { + std::lock_guard lock(this->framesMutex_); + + this->currentFrameOffset_ += GIF_FRAME_LENGTH; while (true) { - if (this->currentFrameOffset > this->allFrames.at(this->currentFrame).duration) { - this->currentFrameOffset -= this->allFrames.at(this->currentFrame).duration; - this->currentFrame = (this->currentFrame + 1) % this->allFrames.size(); + this->currentFrameIndex_ %= this->frames_.size(); + if (this->currentFrameOffset_ > this->frames_[this->currentFrameIndex_].getDuration()) { + this->currentFrameOffset_ -= this->frames_[this->currentFrameIndex_].getDuration(); + this->currentFrameIndex_ = (this->currentFrameIndex_ + 1) % this->frames_.size(); } else { break; } } - this->currentPixmap = this->allFrames[this->currentFrame].image; + this->currentFramePixmap_ = this->frames_[this->currentFrameIndex_].getPixmap(); } } -const QPixmap *Image::getPixmap() +bool Image::operator==(const Image &other) const { - if (!this->isLoading) { - this->isLoading = true; - - this->loadImage(); - - return nullptr; + if (this->isNull() && other.isNull()) { + return true; } - if (this->isLoaded) { - return this->currentPixmap; - } else { - return nullptr; - } -} - -qreal Image::getScale() const -{ - return this->scale; -} - -const QString &Image::getUrl() const -{ - return this->url; -} - -const QString &Image::getName() const -{ - return this->name; -} - -const QString &Image::getCopyString() const -{ - if (this->copyString.isEmpty()) { - return this->name; + if (!this->url_.string.isEmpty() && this->url_ == other.url_) { + return true; } - return this->copyString; -} + assert(this->frames_.size() == 1); + assert(other.frames_.size() == 1); -const QString &Image::getTooltip() const -{ - return this->tooltip; -} - -const QMargins &Image::getMargin() const -{ - return this->margin; -} - -bool Image::isAnimated() const -{ - return this->animated; -} - -bool Image::isHat() const -{ - return this->ishat; -} - -int Image::getWidth() const -{ - if (this->currentPixmap == nullptr) { - return 16; + if (this->currentFramePixmap_ == other.currentFramePixmap_) { + return true; } - return this->currentPixmap->width(); + return false; } -int Image::getScaledWidth() const +bool Image::operator!=(const Image &other) const { - return static_cast((float)this->getWidth() * this->scale * - getApp()->settings->emoteScale.getValue()); + return !this->operator==(other); } -int Image::getHeight() const +// FRAME +Image::Frame::Frame(QPixmap *nonOwning, int duration) + : nonOwning_(nonOwning) + , duration_(duration) { - if (this->currentPixmap == nullptr) { - return 16; - } - return this->currentPixmap->height(); } -int Image::getScaledHeight() const +Image::Frame::Frame(std::unique_ptr nonOwning, int duration) + : owning_(std::move(nonOwning)) + , duration_(duration) { - return static_cast((float)this->getHeight() * this->scale * - getApp()->settings->emoteScale.getValue()); } -void Image::setCopyString(const QString &newCopyString) +int Image::Frame::getDuration() const { - this->copyString = newCopyString; + return this->duration_; +} + +QPixmap *Image::Frame::getPixmap() const +{ + if (this->nonOwning_) return this->nonOwning_; + + return this->owning_.get(); } } // namespace chatterino diff --git a/src/messages/Image.hpp b/src/messages/Image.hpp index c25c4db45..eec7ea9a9 100644 --- a/src/messages/Image.hpp +++ b/src/messages/Image.hpp @@ -1,69 +1,86 @@ #pragma once +#include "common/Common.hpp" + #include #include -#include - #include +#include +#include +#include + +#include "common/NullablePtr.hpp" namespace chatterino { -class Image : public QObject, boost::noncopyable +class Image; +using ImagePtr = std::shared_ptr; + +class Image : public std::enable_shared_from_this, boost::noncopyable { public: - explicit Image(const QString &_url, qreal _scale = 1, const QString &_name = "", - const QString &_tooltip = "", const QMargins &_margin = QMargins(), - bool isHat = false); + static ImagePtr fromUrl(const Url &url, qreal scale = 1); + static ImagePtr fromOwningPixmap(std::unique_ptr pixmap, qreal scale = 1); + static ImagePtr fromNonOwningPixmap(QPixmap *pixmap, qreal scale = 1); + static ImagePtr getEmpty(); - explicit Image(QPixmap *_currentPixmap, qreal _scale = 1, const QString &_name = "", - const QString &_tooltip = "", const QMargins &_margin = QMargins(), - bool isHat = false); - ~Image(); - - const QPixmap *getPixmap(); + const Url &getUrl() const; + NullablePtr getPixmap() const; qreal getScale() const; - const QString &getUrl() const; - const QString &getName() const; - const QString &getCopyString() const; - const QString &getTooltip() const; - const QMargins &getMargin() const; bool isAnimated() const; - bool isHat() const; int getWidth() const; - int getScaledWidth() const; int getHeight() const; - int getScaledHeight() const; + bool isLoaded() const; + bool isError() const; + bool isValid() const; + bool isNull() const; - void setCopyString(const QString &newCopyString); + bool operator==(const Image &image) const; + bool operator!=(const Image &image) const; private: - struct FrameData { - QPixmap *image; - int duration; + class Frame + { + public: + QPixmap *getPixmap() const; + int getDuration() const; + + Frame(QPixmap *nonOwning, int duration = 1); + Frame(std::unique_ptr nonOwning, int duration = 1); + + private: + QPixmap *nonOwning_; + std::unique_ptr owning_; + int duration_; }; - static bool loadedEventQueued; + Image(); + Image(const Url &url, qreal scale); + Image(std::unique_ptr owning, qreal scale); + Image(QPixmap *nonOwning, qreal scale); - QPixmap *currentPixmap = nullptr; - QPixmap *loadedPixmap = nullptr; - std::vector allFrames; - int currentFrame = 0; - int currentFrameOffset = 0; + void load(); + Outcome parse(const QByteArray &data); + std::vector readFrames(QImageReader &reader); + Outcome setFrames(std::vector frames); + void updateAnimation(); + void queueLoadedEvent(); - QString url; - QString name; - QString copyString; - QString tooltip; - bool animated = false; - QMargins margin; - bool ishat; - qreal scale; + Url url_; + bool isLoaded_{false}; + bool isLoading_{false}; + bool isAnimated_{false}; + bool isError_{false}; + bool isNull_ = false; + qreal scale_ = 1; + QObject object_; - bool isLoading = false; - std::atomic isLoaded{false}; + std::vector frames_; + std::mutex framesMutex_; + NullablePtr currentFramePixmap_; + int currentFrameIndex_ = 0; + int currentFrameOffset_ = 0; - void loadImage(); - void gifUpdateTimout(); + static std::atomic loadedEventQueued; }; - } // namespace chatterino diff --git a/src/messages/ImageSet.cpp b/src/messages/ImageSet.cpp new file mode 100644 index 000000000..0a3e60802 --- /dev/null +++ b/src/messages/ImageSet.cpp @@ -0,0 +1,95 @@ +#include "ImageSet.hpp" + +#include "Application.hpp" +#include "singletons/Resources.hpp" +#include "singletons/Settings.hpp" + +namespace chatterino { + +ImageSet::ImageSet() + : imageX1_(Image::getEmpty()) + , imageX2_(Image::getEmpty()) + , imageX3_(Image::getEmpty()) +{ +} + +ImageSet::ImageSet(const ImagePtr &image1, const ImagePtr &image2, const ImagePtr &image3) + : imageX1_(image1) + , imageX2_(image2) + , imageX3_(image3) +{ +} + +ImageSet::ImageSet(const Url &image1, const Url &image2, const Url &image3) + : imageX1_(Image::fromUrl(image1, 1)) + , imageX2_(Image::fromUrl(image2, 0.5)) + , imageX3_(Image::fromUrl(image3, 0.25)) +{ +} + +void ImageSet::setImage1(const ImagePtr &image) +{ + this->imageX1_ = image; +} + +void ImageSet::setImage2(const ImagePtr &image) +{ + this->imageX2_ = image; +} + +void ImageSet::setImage3(const ImagePtr &image) +{ + this->imageX3_ = image; +} + +const ImagePtr &ImageSet::getImage1() const +{ + return this->imageX1_; +} + +const ImagePtr &ImageSet::getImage2() const +{ + return this->imageX2_; +} + +const ImagePtr &ImageSet::getImage3() const +{ + return this->imageX3_; +} + +const ImagePtr &ImageSet::getImage(float scale) const +{ + int quality = getSettings()->preferredEmoteQuality; + + if (!quality) { + if (scale > 3.999) + quality = 3; + else if (scale > 1.999) + quality = 2; + else + scale = 1; + } + + if (this->imageX3_->isValid() && quality == 3) { + return this->imageX3_; + } + + if (this->imageX2_->isValid() && quality == 2) { + return this->imageX3_; + } + + return this->imageX1_; +} + +bool ImageSet::operator==(const ImageSet &other) const +{ + return std::tie(this->imageX1_, this->imageX2_, this->imageX3_) == + std::tie(other.imageX1_, other.imageX2_, other.imageX3_); +} + +bool ImageSet::operator!=(const ImageSet &other) const +{ + return !this->operator==(other); +} + +} // namespace chatterino diff --git a/src/messages/ImageSet.hpp b/src/messages/ImageSet.hpp new file mode 100644 index 000000000..8f2efab2d --- /dev/null +++ b/src/messages/ImageSet.hpp @@ -0,0 +1,35 @@ +#pragma once + +#include "messages/Image.hpp" + +namespace chatterino { + +class ImageSet +{ +public: + ImageSet(); + ImageSet(const ImagePtr &image1, const ImagePtr &image2 = Image::getEmpty(), + const ImagePtr &image3 = Image::getEmpty()); + ImageSet(const Url &image1, const Url &image2 = {}, const Url &image3 = {}); + + void setImage1(const ImagePtr &image); + void setImage2(const ImagePtr &image); + void setImage3(const ImagePtr &image); + const ImagePtr &getImage1() const; + const ImagePtr &getImage2() const; + const ImagePtr &getImage3() const; + + const ImagePtr &getImage(float scale) const; + + ImagePtr getImage(float scale); + + bool operator==(const ImageSet &other) const; + bool operator!=(const ImageSet &other) const; + +private: + ImagePtr imageX1_; + ImagePtr imageX2_; + ImagePtr imageX3_; +}; + +} // namespace chatterino diff --git a/src/messages/MessageElement.cpp b/src/messages/MessageElement.cpp index 5081f5e47..ed29957b9 100644 --- a/src/messages/MessageElement.cpp +++ b/src/messages/MessageElement.cpp @@ -60,18 +60,18 @@ MessageElement::Flags MessageElement::getFlags() const } // IMAGE -ImageElement::ImageElement(Image *image, MessageElement::Flags flags) +ImageElement::ImageElement(ImagePtr image, MessageElement::Flags flags) : MessageElement(flags) , image_(image) { - this->setTooltip(image->getTooltip()); + // this->setTooltip(image->getTooltip()); } void ImageElement::addToContainer(MessageLayoutContainer &container, MessageElement::Flags flags) { if (flags & this->getFlags()) { - QSize size(this->image_->getScaledWidth() * container.getScale(), - this->image_->getScaledHeight() * container.getScale()); + auto size = QSize(this->image_->getWidth() * container.getScale(), + this->image_->getHeight() * container.getScale()); container.addElement( (new ImageLayoutElement(*this, this->image_, size))->setLink(this->getLink())); @@ -79,29 +79,32 @@ void ImageElement::addToContainer(MessageLayoutContainer &container, MessageElem } // EMOTE -EmoteElement::EmoteElement(const EmoteData &data, MessageElement::Flags flags) +EmoteElement::EmoteElement(const EmotePtr &emote, MessageElement::Flags flags) : MessageElement(flags) - , data(data) + , emote_(emote) { - if (data.isValid()) { - this->setTooltip(data.image1x->getTooltip()); - this->textElement_.reset( - new TextElement(data.image1x->getCopyString(), MessageElement::Misc)); + auto image = emote->images.getImage1(); + if (image->isValid()) { + this->textElement_.reset(new TextElement(emote->getCopyString(), MessageElement::Misc)); } + + this->setTooltip(emote->tooltip.string); +} + +EmotePtr EmoteElement::getEmote() const +{ + return this->emote_; } void EmoteElement::addToContainer(MessageLayoutContainer &container, MessageElement::Flags flags) { if (flags & this->getFlags()) { if (flags & MessageElement::EmoteImages) { - if (!this->data.isValid()) { - return; - } + auto image = this->emote_->images.getImage(container.getScale()); + if (!image->isValid()) return; - Image *image = this->data.getImage(container.getScale()); - - QSize size(int(container.getScale() * image->getScaledWidth()), - int(container.getScale() * image->getScaledHeight())); + QSize size(int(container.getScale() * image->getWidth()), + int(container.getScale() * image->getHeight())); container.addElement( (new ImageLayoutElement(*this, image, size))->setLink(this->getLink())); @@ -173,7 +176,6 @@ void TextElement::addToContainer(MessageLayoutContainer &container, MessageEleme int textLength = text.length(); int wordStart = 0; int width = metrics.width(text[0]); - int lastWidth = 0; for (int i = 1; i < textLength; i++) { int charWidth = metrics.width(text[i]); @@ -184,7 +186,6 @@ void TextElement::addToContainer(MessageLayoutContainer &container, MessageEleme container.breakLine(); wordStart = i; - lastWidth = width; width = 0; if (textLength > i + 2) { width += metrics.width(text[i]); @@ -196,8 +197,6 @@ void TextElement::addToContainer(MessageLayoutContainer &container, MessageEleme width += charWidth; } - UNUSED(lastWidth); // XXX: What should this be used for (if anything)? KKona - container.addElement( getTextLayoutElement(text.mid(wordStart), width, this->hasTrailingSpace())); container.breakLine(); @@ -249,14 +248,15 @@ void TwitchModerationElement::addToContainer(MessageLayoutContainer &container, if (flags & MessageElement::ModeratorTools) { QSize size(int(container.getScale() * 16), int(container.getScale() * 16)); - for (const ModerationAction &m : getApp()->moderationActions->items.getVector()) { - if (m.isImage()) { - container.addElement((new ImageLayoutElement(*this, m.getImage(), size)) - ->setLink(Link(Link::UserAction, m.getAction()))); + for (const auto &action : getApp()->moderationActions->items.getVector()) { + if (auto image = action.getImage()) { + container.addElement((new ImageLayoutElement(*this, image.get(), size)) + ->setLink(Link(Link::UserAction, action.getAction()))); } else { - container.addElement((new TextIconLayoutElement(*this, m.getLine1(), m.getLine2(), - container.getScale(), size)) - ->setLink(Link(Link::UserAction, m.getAction()))); + container.addElement( + (new TextIconLayoutElement(*this, action.getLine1(), action.getLine2(), + container.getScale(), size)) + ->setLink(Link(Link::UserAction, action.getAction()))); } } } diff --git a/src/messages/MessageElement.hpp b/src/messages/MessageElement.hpp index 764101641..51cb5a770 100644 --- a/src/messages/MessageElement.hpp +++ b/src/messages/MessageElement.hpp @@ -1,6 +1,7 @@ #pragma once #include "common/Emotemap.hpp" +#include "messages/Emote.hpp" #include "messages/Image.hpp" #include "messages/Link.hpp" #include "messages/MessageColor.hpp" @@ -16,7 +17,6 @@ namespace chatterino { class Channel; -struct EmoteData; struct MessageLayoutContainer; class MessageElement : boost::noncopyable @@ -133,12 +133,12 @@ private: class ImageElement : public MessageElement { public: - ImageElement(Image *image, MessageElement::Flags flags); + ImageElement(ImagePtr image, MessageElement::Flags flags); void addToContainer(MessageLayoutContainer &container, MessageElement::Flags flags) override; private: - Image *image_; + ImagePtr image_; }; // contains a text, it will split it into words @@ -169,15 +169,14 @@ private: class EmoteElement : public MessageElement { public: - EmoteElement(const EmoteData &data, MessageElement::Flags flags_); - ~EmoteElement() override = default; + EmoteElement(const EmotePtr &data, MessageElement::Flags flags_); void addToContainer(MessageLayoutContainer &container, MessageElement::Flags flags_) override; - - const EmoteData data; + EmotePtr getEmote() const; private: std::unique_ptr textElement_; + EmotePtr emote_; }; // contains a text, formated depending on the preferences diff --git a/src/messages/layouts/MessageLayoutElement.cpp b/src/messages/layouts/MessageLayoutElement.cpp index c71858eec..6d253e7a5 100644 --- a/src/messages/layouts/MessageLayoutElement.cpp +++ b/src/messages/layouts/MessageLayoutElement.cpp @@ -63,16 +63,17 @@ const Link &MessageLayoutElement::getLink() const // IMAGE // -ImageLayoutElement::ImageLayoutElement(MessageElement &_creator, Image *_image, const QSize &_size) - : MessageLayoutElement(_creator, _size) - , image(_image) +ImageLayoutElement::ImageLayoutElement(MessageElement &creator, ImagePtr image, const QSize &size) + : MessageLayoutElement(creator, size) + , image_(image) { - this->trailingSpace = _creator.hasTrailingSpace(); + this->trailingSpace = creator.hasTrailingSpace(); } void ImageLayoutElement::addCopyTextToString(QString &str, int from, int to) const { - str += this->image->getCopyString(); + // str += this->image_->getCopyString(); + str += "not implemented"; if (this->hasTrailingSpace()) { str += " "; @@ -86,13 +87,12 @@ int ImageLayoutElement::getSelectionIndexCount() void ImageLayoutElement::paint(QPainter &painter) { - if (this->image == nullptr) { + if (this->image_ == nullptr) { return; } - const QPixmap *pixmap = this->image->getPixmap(); - - if (pixmap != nullptr && !this->image->isAnimated()) { + auto pixmap = this->image_->getPixmap(); + if (pixmap && !this->image_->isAnimated()) { // fourtf: make it use qreal values painter.drawPixmap(QRectF(this->getRect()), *pixmap, QRectF()); } @@ -100,19 +100,15 @@ void ImageLayoutElement::paint(QPainter &painter) void ImageLayoutElement::paintAnimated(QPainter &painter, int yOffset) { - if (this->image == nullptr) { + if (this->image_ == nullptr) { return; } - if (this->image->isAnimated()) { - // qDebug() << this->image->getUrl(); - auto pixmap = this->image->getPixmap(); - - if (pixmap != nullptr) { - // fourtf: make it use qreal values - QRect _rect = this->getRect(); - _rect.moveTop(_rect.y() + yOffset); - painter.drawPixmap(QRectF(_rect), *pixmap, QRectF()); + if (this->image_->isAnimated()) { + if (auto pixmap = this->image_->getPixmap()) { + auto rect = this->getRect(); + rect.moveTop(rect.y() + yOffset); + painter.drawPixmap(QRectF(rect), *pixmap, QRectF()); } } } diff --git a/src/messages/layouts/MessageLayoutElement.hpp b/src/messages/layouts/MessageLayoutElement.hpp index 3d14b0f32..d8221726f 100644 --- a/src/messages/layouts/MessageLayoutElement.hpp +++ b/src/messages/layouts/MessageLayoutElement.hpp @@ -7,6 +7,7 @@ #include #include +#include "messages/Image.hpp" #include "messages/Link.hpp" #include "messages/MessageColor.hpp" #include "singletons/Fonts.hpp" @@ -15,7 +16,6 @@ class QPainter; namespace chatterino { class MessageElement; -class Image; class MessageLayoutElement : boost::noncopyable { @@ -52,7 +52,7 @@ private: class ImageLayoutElement : public MessageLayoutElement { public: - ImageLayoutElement(MessageElement &creator_, Image *image, const QSize &size); + ImageLayoutElement(MessageElement &creator, ImagePtr image, const QSize &size); protected: void addCopyTextToString(QString &str, int from = 0, int to = INT_MAX) const override; @@ -63,7 +63,7 @@ protected: int getXFromIndex(int index) override; private: - Image *image; + ImagePtr image_; }; // TEXT diff --git a/src/providers/bttv/BttvEmotes.cpp b/src/providers/bttv/BttvEmotes.cpp index 391be6e6f..570879210 100644 --- a/src/providers/bttv/BttvEmotes.cpp +++ b/src/providers/bttv/BttvEmotes.cpp @@ -3,122 +3,107 @@ #include "common/NetworkRequest.hpp" #include "debug/Log.hpp" #include "messages/Image.hpp" +#include "messages/ImageSet.hpp" +#include "providers/twitch/TwitchChannel.hpp" + +#include +#include namespace chatterino { namespace { -QString getEmoteLink(QString urlTemplate, const QString &id, const QString &emoteScale) +Url getEmoteLink(QString urlTemplate, const EmoteId &id, const QString &emoteScale) { urlTemplate.detach(); - return urlTemplate.replace("{{id}}", id).replace("{{image}}", emoteScale); + return {urlTemplate.replace("{{id}}", id.string).replace("{{image}}", emoteScale)}; } } // namespace -void BTTVEmotes::loadGlobalEmotes() +AccessGuard BttvEmotes::accessGlobalEmotes() const { - QString url("https://api.betterttv.net/2/emotes"); + return this->globalEmotes_.accessConst(); +} + +boost::optional BttvEmotes::getGlobalEmote(const EmoteName &name) +{ + auto emotes = this->globalEmotes_.access(); + auto it = emotes->find(name); + + if (it == emotes->end()) return boost::none; + return it->second; +} + +// FOURTF: never returns anything +// boost::optional BttvEmotes::getEmote(const EmoteId &id) +//{ +// auto cache = this->channelEmoteCache_.access(); +// auto it = cache->find(id); +// +// if (it != cache->end()) { +// auto shared = it->second.lock(); +// if (shared) { +// return shared; +// } +// } +// +// return boost::none; +//} + +void BttvEmotes::loadGlobalEmotes() +{ + auto request = NetworkRequest(QString(globalEmoteApiUrl)); - NetworkRequest request(url); request.setCaller(QThread::currentThread()); request.setTimeout(30000); - request.onSuccess([this](auto result) { - auto root = result.parseJson(); - auto emotes = root.value("emotes").toArray(); + request.onSuccess([this](auto result) -> Outcome { + // if (auto shared = weak.lock()) { + auto currentEmotes = this->globalEmotes_.access(); - QString urlTemplate = "https:" + root.value("urlTemplate").toString(); + auto pair = this->parseGlobalEmotes(result.parseJson(), *currentEmotes); - std::vector codes; - for (const QJsonValue &emote : emotes) { - QString id = emote.toObject().value("id").toString(); - QString code = emote.toObject().value("code").toString(); - - EmoteData emoteData; - emoteData.image1x = new Image(getEmoteLink(urlTemplate, id, "1x"), 1, code, - code + "
Global BTTV Emote"); - emoteData.image2x = new Image(getEmoteLink(urlTemplate, id, "2x"), 0.5, code, - code + "
Global BTTV Emote"); - emoteData.image3x = new Image(getEmoteLink(urlTemplate, id, "3x"), 0.25, code, - code + "
Global BTTV Emote"); - emoteData.pageLink = "https://manage.betterttv.net/emotes/" + id; - - this->globalEmotes.insert(code, emoteData); - codes.push_back(code); + if (pair.first) { + *currentEmotes = std::move(pair.second); } - this->globalEmoteCodes = codes; - - return true; + return pair.first; + // } + return Failure; }); request.execute(); } -void BTTVEmotes::loadChannelEmotes(const QString &channelName, std::weak_ptr _map) +std::pair BttvEmotes::parseGlobalEmotes(const QJsonObject &jsonRoot, + const EmoteMap ¤tEmotes) { - printf("[BTTVEmotes] Reload BTTV Channel Emotes for channel %s\n", qPrintable(channelName)); + auto emotes = EmoteMap(); + auto jsonEmotes = jsonRoot.value("emotes").toArray(); + auto urlTemplate = QString("https:" + jsonRoot.value("urlTemplate").toString()); - QString url("https://api.betterttv.net/2/channels/" + channelName); + for (const QJsonValue &jsonEmote : jsonEmotes) { + auto id = EmoteId{jsonEmote.toObject().value("id").toString()}; + auto name = EmoteName{jsonEmote.toObject().value("code").toString()}; - Log("Request bttv channel emotes for {}", channelName); + auto emote = Emote({name, + ImageSet{Image::fromUrl(getEmoteLink(urlTemplate, id, "1x"), 1), + Image::fromUrl(getEmoteLink(urlTemplate, id, "2x"), 0.5), + Image::fromUrl(getEmoteLink(urlTemplate, id, "3x"), 0.25)}, + Tooltip{name.string + "
Global Bttv Emote"}, + Url{"https://manage.betterttv.net/emotes/" + id.string}}); - NetworkRequest request(url); - request.setCaller(QThread::currentThread()); - request.setTimeout(3000); - request.onSuccess([this, channelName, _map](auto result) { - auto rootNode = result.parseJson(); - auto map = _map.lock(); - - if (_map.expired()) { - return false; + auto it = currentEmotes.find(name); + if (it != currentEmotes.end() && *it->second == emote) { + // reuse old shared_ptr if nothing changed + emotes[name] = it->second; + } else { + emotes[name] = std::make_shared(std::move(emote)); } + } - map->clear(); - - auto emotesNode = rootNode.value("emotes").toArray(); - - QString linkTemplate = "https:" + rootNode.value("urlTemplate").toString(); - - std::vector codes; - for (const QJsonValue &emoteNode : emotesNode) { - QJsonObject emoteObject = emoteNode.toObject(); - - QString id = emoteObject.value("id").toString(); - QString code = emoteObject.value("code").toString(); - // emoteObject.value("imageType").toString(); - - auto emote = this->channelEmoteCache_.getOrAdd(id, [&] { - EmoteData emoteData; - QString link = linkTemplate; - link.detach(); - emoteData.image1x = new Image(link.replace("{{id}}", id).replace("{{image}}", "1x"), - 1, code, code + "
Channel BTTV Emote"); - link = linkTemplate; - link.detach(); - emoteData.image2x = new Image(link.replace("{{id}}", id).replace("{{image}}", "2x"), - 0.5, code, code + "
Channel BTTV Emote"); - link = linkTemplate; - link.detach(); - emoteData.image3x = new Image(link.replace("{{id}}", id).replace("{{image}}", "3x"), - 0.25, code, code + "
Channel BTTV Emote"); - emoteData.pageLink = "https://manage.betterttv.net/emotes/" + id; - - return emoteData; - }); - - this->channelEmotes.insert(code, emote); - map->insert(code, emote); - codes.push_back(code); - } - - this->channelEmoteCodes[channelName] = codes; - - return true; - }); - - request.execute(); + return {Success, std::move(emotes)}; } } // namespace chatterino diff --git a/src/providers/bttv/BttvEmotes.hpp b/src/providers/bttv/BttvEmotes.hpp index 134e5a157..f8273afb8 100644 --- a/src/providers/bttv/BttvEmotes.hpp +++ b/src/providers/bttv/BttvEmotes.hpp @@ -1,27 +1,32 @@ #pragma once -#include "common/Emotemap.hpp" -#include "common/SimpleSignalVector.hpp" -#include "util/ConcurrentMap.hpp" +#include -#include +#include "common/UniqueAccess.hpp" +#include "messages/Emote.hpp" +#include "messages/EmoteCache.hpp" namespace chatterino { -class BTTVEmotes +class BttvEmotes final : std::enable_shared_from_this { -public: - EmoteMap globalEmotes; - SimpleSignalVector globalEmoteCodes; + static constexpr const char *globalEmoteApiUrl = "https://api.betterttv.net/2/emotes"; - EmoteMap channelEmotes; - std::map> channelEmoteCodes; +public: + // BttvEmotes(); + + AccessGuard accessGlobalEmotes() const; + boost::optional getGlobalEmote(const EmoteName &name); + boost::optional getEmote(const EmoteId &id); void loadGlobalEmotes(); - void loadChannelEmotes(const QString &channelName, std::weak_ptr channelEmoteMap); private: - EmoteMap channelEmoteCache_; + std::pair parseGlobalEmotes(const QJsonObject &jsonRoot, + const EmoteMap ¤tEmotes); + + UniqueAccess globalEmotes_; + // UniqueAccess channelEmoteCache_; }; } // namespace chatterino diff --git a/src/providers/bttv/LoadBttvChannelEmote.cpp b/src/providers/bttv/LoadBttvChannelEmote.cpp new file mode 100644 index 000000000..9829c6e07 --- /dev/null +++ b/src/providers/bttv/LoadBttvChannelEmote.cpp @@ -0,0 +1,76 @@ +#include "LoadBttvChannelEmote.hpp" + +#include +#include +#include +#include +#include "common/Common.hpp" +#include "common/NetworkRequest.hpp" +#include "common/UniqueAccess.hpp" +#include "messages/Emote.hpp" + +namespace chatterino { + +static Url getEmoteLink(QString urlTemplate, const EmoteId &id, const QString &emoteScale); +static std::pair bttvParseChannelEmotes(const QJsonObject &jsonRoot); + +void loadBttvChannelEmotes(const QString &channelName, std::function callback) +{ + auto request = NetworkRequest(QString(bttvChannelEmoteApiUrl) + channelName); + + request.setCaller(QThread::currentThread()); + request.setTimeout(3000); + request.onSuccess([callback = std::move(callback)](auto result) -> Outcome { + auto pair = bttvParseChannelEmotes(result.parseJson()); + + if (pair.first == Success) callback(std::move(pair.second)); + + return pair.first; + }); + + request.execute(); +} + +static std::pair bttvParseChannelEmotes(const QJsonObject &jsonRoot) +{ + static UniqueAccess>> cache_; + + auto cache = cache_.access(); + auto emotes = EmoteMap(); + auto jsonEmotes = jsonRoot.value("emotes").toArray(); + auto urlTemplate = QString("https:" + jsonRoot.value("urlTemplate").toString()); + + for (auto jsonEmote_ : jsonEmotes) { + auto jsonEmote = jsonEmote_.toObject(); + + auto id = EmoteId{jsonEmote.value("id").toString()}; + auto name = EmoteName{jsonEmote.value("code").toString()}; + // emoteObject.value("imageType").toString(); + + auto emote = Emote({name, + ImageSet{Image::fromUrl(getEmoteLink(urlTemplate, id, "1x"), 1), + Image::fromUrl(getEmoteLink(urlTemplate, id, "2x"), 0.5), + Image::fromUrl(getEmoteLink(urlTemplate, id, "3x"), 0.25)}, + Tooltip{name.string + "
Channel Bttv Emote"}, + Url{"https://manage.betterttv.net/emotes/" + id.string}}); + + auto shared = (*cache)[id].lock(); + if (shared && *shared == emote) { + // reuse old shared_ptr if nothing changed + emotes[name] = shared; + } else { + (*cache)[id] = emotes[name] = std::make_shared(std::move(emote)); + } + } + + return {Success, std::move(emotes)}; +} + +static Url getEmoteLink(QString urlTemplate, const EmoteId &id, const QString &emoteScale) +{ + urlTemplate.detach(); + + return {urlTemplate.replace("{{id}}", id.string).replace("{{image}}", emoteScale)}; +} + +} // namespace chatterino diff --git a/src/providers/bttv/LoadBttvChannelEmote.hpp b/src/providers/bttv/LoadBttvChannelEmote.hpp new file mode 100644 index 000000000..ef9ca160e --- /dev/null +++ b/src/providers/bttv/LoadBttvChannelEmote.hpp @@ -0,0 +1,14 @@ +#pragma once + +#include + +class QString; + +namespace chatterino { + +class EmoteMap; +constexpr const char *bttvChannelEmoteApiUrl = "https://api.betterttv.net/2/channels/"; + +void loadBttvChannelEmotes(const QString &channelName, std::function callback); + +} // namespace chatterino diff --git a/src/providers/chatterino/ChatterinoBadges.cpp b/src/providers/chatterino/ChatterinoBadges.cpp new file mode 100644 index 000000000..01dc82aa4 --- /dev/null +++ b/src/providers/chatterino/ChatterinoBadges.cpp @@ -0,0 +1,50 @@ +#include "ChatterinoBadges.hpp" + +#include +#include +#include +#include +#include "common/NetworkRequest.hpp" + +namespace chatterino { + +ChatterinoBadges::ChatterinoBadges() +{ +} + +boost::optional ChatterinoBadges::getBadge(const UserName &username) +{ + return this->badges.access()->get(username); +} + +void ChatterinoBadges::loadChatterinoBadges() +{ + static QString url("https://fourtf.com/chatterino/badges.json"); + + NetworkRequest req(url); + req.setCaller(QThread::currentThread()); + + req.onSuccess([this](auto result) { + auto jsonRoot = result.parseJson(); + auto badges = this->badges.access(); + auto replacement = badges->makeReplacment(); + + for (auto jsonBadge_ : jsonRoot.value("badges").toArray()) { + auto jsonBadge = jsonBadge_.toObject(); + + auto emote = Emote{EmoteName{}, ImageSet{Url{jsonBadge.value("image").toString()}}, + Tooltip{jsonBadge.value("tooltip").toString()}, Url{}}; + + for (auto jsonUser : jsonBadge.value("users").toArray()) { + replacement.add(UserName{jsonUser.toString()}, std::move(emote)); + } + } + + replacement.apply(); + return Success; + }); + + req.execute(); +} + +} // namespace chatterino diff --git a/src/providers/chatterino/ChatterinoBadges.hpp b/src/providers/chatterino/ChatterinoBadges.hpp new file mode 100644 index 000000000..3162c75b1 --- /dev/null +++ b/src/providers/chatterino/ChatterinoBadges.hpp @@ -0,0 +1,25 @@ +#pragma once + +#include +#include +#include "common/Common.hpp" +#include "common/UniqueAccess.hpp" +#include "messages/Emote.hpp" +#include "messages/EmoteCache.hpp" + +namespace chatterino { + +class ChatterinoBadges +{ +public: + ChatterinoBadges(); + + boost::optional getBadge(const UserName &username); + +private: + void loadChatterinoBadges(); + + UniqueAccess> badges; +}; + +} // namespace chatterino diff --git a/src/providers/emoji/Emojis.cpp b/src/providers/emoji/Emojis.cpp index 10a0f28e6..064e693f1 100644 --- a/src/providers/emoji/Emojis.cpp +++ b/src/providers/emoji/Emojis.cpp @@ -4,7 +4,12 @@ #include "debug/Log.hpp" #include "singletons/Settings.hpp" +#include +#include +#include #include +#include +#include namespace chatterino { @@ -146,7 +151,7 @@ void Emojis::loadEmojis() emojiData->shortCodes[0] + "_" + toneNameIt->second); this->emojiShortCodeToEmoji_.insert(variationEmojiData->shortCodes[0], - variationEmojiData); + variationEmojiData); this->shortCodes.push_back(variationEmojiData->shortCodes[0]); this->emojiFirstByte_[variationEmojiData->value.at(0)].append(variationEmojiData); @@ -260,14 +265,16 @@ void Emojis::loadEmojiSet() urlPrefix = it->second; } QString url = urlPrefix + code + ".png"; - emoji->emoteData.image1x = - new Image(url, 0.35, emoji->value, ":" + emoji->shortCodes[0] + ":
Emoji"); + emoji->emote = std::make_shared( + Emote{EmoteName{emoji->value}, ImageSet{Image::fromUrl({url}, 0.35)}, + Tooltip{":" + emoji->shortCodes[0] + ":
Emoji"}, Url{}}); }); }); } -void Emojis::parse(std::vector> &parsedWords, const QString &text) +std::vector> Emojis::parse(const QString &text) { + auto result = std::vector>(); int lastParsedEmojiEndIndex = 0; for (auto i = 0; i < text.length(); ++i) { @@ -327,12 +334,11 @@ void Emojis::parse(std::vector> &parsedWords, con if (charactersFromLastParsedEmoji > 0) { // Add characters inbetween emojis - parsedWords.emplace_back( - EmoteData(), text.mid(lastParsedEmojiEndIndex, charactersFromLastParsedEmoji)); + result.emplace_back(text.mid(lastParsedEmojiEndIndex, charactersFromLastParsedEmoji)); } // Push the emoji as a word to parsedWords - parsedWords.push_back(std::tuple(matchedEmoji->emoteData, QString())); + result.emplace_back(matchedEmoji->emote); lastParsedEmojiEndIndex = currentParsedEmojiEndIndex; @@ -341,8 +347,10 @@ void Emojis::parse(std::vector> &parsedWords, con if (lastParsedEmojiEndIndex < text.length()) { // Add remaining characters - parsedWords.emplace_back(EmoteData(), text.mid(lastParsedEmojiEndIndex)); + result.emplace_back(text.mid(lastParsedEmojiEndIndex)); } + + return result; } QString Emojis::replaceShortCodes(const QString &text) diff --git a/src/providers/emoji/Emojis.hpp b/src/providers/emoji/Emojis.hpp index dc56c4e5d..59d184d01 100644 --- a/src/providers/emoji/Emojis.hpp +++ b/src/providers/emoji/Emojis.hpp @@ -2,12 +2,15 @@ #include "common/Emotemap.hpp" #include "common/SimpleSignalVector.hpp" +#include "messages/Emote.hpp" #include "util/ConcurrentMap.hpp" #include #include - +#include #include +#include +#include namespace chatterino { @@ -26,7 +29,7 @@ struct EmojiData { std::vector variations; - EmoteData emoteData; + EmotePtr emote; }; using EmojiMap = ConcurrentMap>; @@ -36,7 +39,7 @@ class Emojis public: void initialize(); void load(); - void parse(std::vector> &parsedWords, const QString &text); + std::vector> parse(const QString &text); EmojiMap emojis; std::vector shortCodes; diff --git a/src/providers/ffz/FfzEmotes.cpp b/src/providers/ffz/FfzEmotes.cpp index 9fe7ac5ce..3a9763b22 100644 --- a/src/providers/ffz/FfzEmotes.cpp +++ b/src/providers/ffz/FfzEmotes.cpp @@ -1,142 +1,168 @@ #include "providers/ffz/FfzEmotes.hpp" +#include + #include "common/NetworkRequest.hpp" #include "debug/Log.hpp" #include "messages/Image.hpp" namespace chatterino { - namespace { - -QString getEmoteLink(const QJsonObject &urls, const QString &emoteScale) +Url getEmoteLink(const QJsonObject &urls, const QString &emoteScale) { auto emote = urls.value(emoteScale); if (emote.isUndefined()) { - return ""; + return {""}; } assert(emote.isString()); - return "https:" + emote.toString(); + return {"https:" + emote.toString()}; } -void fillInEmoteData(const QJsonObject &urls, const QString &code, const QString &tooltip, - EmoteData &emoteData) +void fillInEmoteData(const QJsonObject &urls, const EmoteName &name, const QString &tooltip, + Emote &emoteData) { - QString url1x = getEmoteLink(urls, "1"); - QString url2x = getEmoteLink(urls, "2"); - QString url3x = getEmoteLink(urls, "4"); + auto url1x = getEmoteLink(urls, "1"); + auto url2x = getEmoteLink(urls, "2"); + auto url3x = getEmoteLink(urls, "4"); - assert(!url1x.isEmpty()); - - emoteData.image1x = new Image(url1x, 1, code, tooltip); - - if (!url2x.isEmpty()) { - emoteData.image2x = new Image(url2x, 0.5, code, tooltip); - } - - if (!url3x.isEmpty()) { - emoteData.image3x = new Image(url3x, 0.25, code, tooltip); - } + //, code, tooltip + emoteData.name = name; + emoteData.images = + ImageSet{Image::fromUrl(url1x, 1), Image::fromUrl(url2x, 0.5), Image::fromUrl(url3x, 0.25)}; + emoteData.tooltip = {tooltip}; } - } // namespace -void FFZEmotes::loadGlobalEmotes() +AccessGuard> FfzEmotes::accessGlobalEmotes() const +{ + return this->globalEmotes_.accessConst(); +} + +boost::optional FfzEmotes::getEmote(const EmoteId &id) +{ + auto cache = this->channelEmoteCache_.access(); + auto it = cache->find(id); + + if (it != cache->end()) { + auto shared = it->second.lock(); + if (shared) { + return shared; + } + } + + return boost::none; +} + +boost::optional FfzEmotes::getGlobalEmote(const EmoteName &name) +{ + return this->globalEmotes_.access()->get(name); +} + +void FfzEmotes::loadGlobalEmotes() { QString url("https://api.frankerfacez.com/v1/set/global"); NetworkRequest request(url); request.setCaller(QThread::currentThread()); request.setTimeout(30000); - request.onSuccess([this](auto result) { - auto root = result.parseJson(); - auto sets = root.value("sets").toObject(); - - std::vector codes; - for (const QJsonValue &set : sets) { - auto emoticons = set.toObject().value("emoticons").toArray(); - - for (const QJsonValue &emote : emoticons) { - QJsonObject object = emote.toObject(); - - QString code = object.value("name").toString(); - int id = object.value("id").toInt(); - QJsonObject urls = object.value("urls").toObject(); - - EmoteData emoteData; - fillInEmoteData(urls, code, code + "
Global FFZ Emote", emoteData); - emoteData.pageLink = - QString("https://www.frankerfacez.com/emoticon/%1-%2").arg(id).arg(code); - - this->globalEmotes.insert(code, emoteData); - codes.push_back(code); - } - - this->globalEmoteCodes = codes; - } - - return true; - }); + request.onSuccess( + [this](auto result) -> Outcome { return this->parseGlobalEmotes(result.parseJson()); }); request.execute(); } -void FFZEmotes::loadChannelEmotes(const QString &channelName, std::weak_ptr _map) +Outcome FfzEmotes::parseGlobalEmotes(const QJsonObject &jsonRoot) { - printf("[FFZEmotes] Reload FFZ Channel Emotes for channel %s\n", qPrintable(channelName)); + auto jsonSets = jsonRoot.value("sets").toObject(); + auto emotes = this->globalEmotes_.access(); + auto replacement = emotes->makeReplacment(); - QString url("https://api.frankerfacez.com/v1/room/" + channelName); + for (auto jsonSet : jsonSets) { + auto jsonEmotes = jsonSet.toObject().value("emoticons").toArray(); - NetworkRequest request(url); - request.setCaller(QThread::currentThread()); - request.setTimeout(3000); - request.onSuccess([this, channelName, _map](auto result) { - auto rootNode = result.parseJson(); - auto map = _map.lock(); + for (auto jsonEmoteValue : jsonEmotes) { + auto jsonEmote = jsonEmoteValue.toObject(); - if (_map.expired()) { - return false; + auto name = EmoteName{jsonEmote.value("name").toString()}; + auto id = EmoteId{jsonEmote.value("id").toString()}; + auto urls = jsonEmote.value("urls").toObject(); + + auto emote = Emote(); + fillInEmoteData(urls, name, name.string + "
Global FFZ Emote", emote); + emote.homePage = Url{QString("https://www.frankerfacez.com/emoticon/%1-%2") + .arg(id.string) + .arg(name.string)}; + + replacement.add(name, emote); } + } - map->clear(); + return Success; +} - auto setsNode = rootNode.value("sets").toObject(); +void FfzEmotes::loadChannelEmotes(const QString &channelName, + std::function callback) +{ + // printf("[FFZEmotes] Reload FFZ Channel Emotes for channel %s\n", qPrintable(channelName)); - std::vector codes; - for (const QJsonValue &setNode : setsNode) { - auto emotesNode = setNode.toObject().value("emoticons").toArray(); + // QString url("https://api.frankerfacez.com/v1/room/" + channelName); - for (const QJsonValue &emoteNode : emotesNode) { - QJsonObject emoteObject = emoteNode.toObject(); + // NetworkRequest request(url); + // request.setCaller(QThread::currentThread()); + // request.setTimeout(3000); + // request.onSuccess([this, channelName, _map](auto result) -> Outcome { + // return this->parseChannelEmotes(result.parseJson()); + //}); - // margins - int id = emoteObject.value("id").toInt(); - QString code = emoteObject.value("name").toString(); + // request.execute(); +} - QJsonObject urls = emoteObject.value("urls").toObject(); +Outcome parseChannelEmotes(const QJsonObject &jsonRoot) +{ + // auto rootNode = result.parseJson(); + // auto map = _map.lock(); - auto emote = this->channelEmoteCache_.getOrAdd(id, [id, &code, &urls] { - EmoteData emoteData; - fillInEmoteData(urls, code, code + "
Channel FFZ Emote", emoteData); - emoteData.pageLink = - QString("https://www.frankerfacez.com/emoticon/%1-%2").arg(id).arg(code); + // if (_map.expired()) { + // return false; + //} - return emoteData; - }); + // map->clear(); - this->channelEmotes.insert(code, emote); - map->insert(code, emote); - codes.push_back(code); - } + // auto setsNode = rootNode.value("sets").toObject(); - this->channelEmoteCodes[channelName] = codes; - } + // std::vector codes; + // for (const QJsonValue &setNode : setsNode) { + // auto emotesNode = setNode.toObject().value("emoticons").toArray(); - return true; - }); + // for (const QJsonValue &emoteNode : emotesNode) { + // QJsonObject emoteObject = emoteNode.toObject(); - request.execute(); + // // margins + // int id = emoteObject.value("id").toInt(); + // QString code = emoteObject.value("name").toString(); + + // QJsonObject urls = emoteObject.value("urls").toObject(); + + // auto emote = this->channelEmoteCache_.getOrAdd(id, [id, &code, &urls] { + // EmoteData emoteData; + // fillInEmoteData(urls, code, code + "
Channel FFZ Emote", emoteData); + // emoteData.pageLink = + // QString("https://www.frankerfacez.com/emoticon/%1-%2").arg(id).arg(code); + + // return emoteData; + // }); + + // this->channelEmotes.insert(code, emote); + // map->insert(code, emote); + // codes.push_back(code); + // } + + // this->channelEmoteCodes[channelName] = codes; + //} + + return Success; } } // namespace chatterino diff --git a/src/providers/ffz/FfzEmotes.hpp b/src/providers/ffz/FfzEmotes.hpp index b216de0b0..d42885440 100644 --- a/src/providers/ffz/FfzEmotes.hpp +++ b/src/providers/ffz/FfzEmotes.hpp @@ -1,27 +1,36 @@ #pragma once -#include "common/Emotemap.hpp" -#include "common/SimpleSignalVector.hpp" -#include "util/ConcurrentMap.hpp" +#include -#include +#include "common/UniqueAccess.hpp" +#include "messages/Emote.hpp" +#include "messages/EmoteCache.hpp" namespace chatterino { -class FFZEmotes +class FfzEmotes final : std::enable_shared_from_this { -public: - EmoteMap globalEmotes; - SimpleSignalVector globalEmoteCodes; + static constexpr const char *globalEmoteApiUrl = "https://api.frankerfacez.com/v1/set/global"; + static constexpr const char *channelEmoteApiUrl = "https://api.betterttv.net/2/channels/"; - EmoteMap channelEmotes; - std::map> channelEmoteCodes; +public: + // FfzEmotes(); + + static std::shared_ptr create(); + + AccessGuard> accessGlobalEmotes() const; + boost::optional getGlobalEmote(const EmoteName &name); + boost::optional getEmote(const EmoteId &id); void loadGlobalEmotes(); - void loadChannelEmotes(const QString &channelName, std::weak_ptr channelEmoteMap); + void loadChannelEmotes(const QString &channelName, std::function callback); -private: - ConcurrentMap channelEmoteCache_; +protected: + Outcome parseGlobalEmotes(const QJsonObject &jsonRoot); + Outcome parseChannelEmotes(const QJsonObject &jsonRoot); + + UniqueAccess> globalEmotes_; + UniqueAccess channelEmoteCache_; }; } // namespace chatterino diff --git a/src/providers/irc/AbstractIrcServer.cpp b/src/providers/irc/AbstractIrcServer.cpp index fcbd306ae..532b78710 100644 --- a/src/providers/irc/AbstractIrcServer.cpp +++ b/src/providers/irc/AbstractIrcServer.cpp @@ -54,12 +54,9 @@ void AbstractIrcServer::connect() std::lock_guard lock2(this->channelMutex); for (std::weak_ptr &weak : this->channels.values()) { - std::shared_ptr chan = weak.lock(); - if (!chan) { - continue; + if (auto channel = std::shared_ptr(weak.lock())) { + this->readConnection_->sendRaw("JOIN #" + channel->getName()); } - - this->readConnection_->sendRaw("JOIN #" + chan->name); } this->writeConnection_->open(); diff --git a/src/providers/twitch/IrcMessageHandler.cpp b/src/providers/twitch/IrcMessageHandler.cpp index 29d64d397..cd02c94ef 100644 --- a/src/providers/twitch/IrcMessageHandler.cpp +++ b/src/providers/twitch/IrcMessageHandler.cpp @@ -94,8 +94,6 @@ void IrcMessageHandler::handleRoomStateMessage(Communi::IrcMessage *message) auto roomId = it.value().toString(); twitchChannel->setRoomId(roomId); - - app->resources->loadChannelData(roomId); } // Room modes diff --git a/src/providers/twitch/PartialTwitchUser.cpp b/src/providers/twitch/PartialTwitchUser.cpp index 2033e977d..cc8c06f51 100644 --- a/src/providers/twitch/PartialTwitchUser.cpp +++ b/src/providers/twitch/PartialTwitchUser.cpp @@ -4,6 +4,7 @@ #include "debug/Log.hpp" #include "providers/twitch/TwitchCommon.hpp" +#include #include namespace chatterino { @@ -36,32 +37,32 @@ void PartialTwitchUser::getId(std::function successCallback, cons request.setCaller(caller); request.makeAuthorizedV5(getDefaultClientID()); - request.onSuccess([successCallback](auto result) { + request.onSuccess([successCallback](auto result) -> Outcome { auto root = result.parseJson(); if (!root.value("users").isArray()) { Log("API Error while getting user id, users is not an array"); - return false; + return Failure; } auto users = root.value("users").toArray(); if (users.size() != 1) { Log("API Error while getting user id, users array size is not 1"); - return false; + return Failure; } if (!users[0].isObject()) { Log("API Error while getting user id, first user is not an object"); - return false; + return Failure; } auto firstUser = users[0].toObject(); auto id = firstUser.value("_id"); if (!id.isString()) { Log("API Error: while getting user id, first user object `_id` key is not a " "string"); - return false; + return Failure; } successCallback(id.toString()); - return true; + return Success; }); request.execute(); diff --git a/src/providers/twitch/TwitchAccount.cpp b/src/providers/twitch/TwitchAccount.cpp index d5908166c..bb8510231 100644 --- a/src/providers/twitch/TwitchAccount.cpp +++ b/src/providers/twitch/TwitchAccount.cpp @@ -1,13 +1,45 @@ #include "providers/twitch/TwitchAccount.hpp" +#include + +#include "Application.hpp" #include "common/NetworkRequest.hpp" #include "debug/Log.hpp" #include "providers/twitch/PartialTwitchUser.hpp" #include "providers/twitch/TwitchCommon.hpp" +#include "singletons/Emotes.hpp" #include "util/RapidjsonHelpers.hpp" namespace chatterino { +namespace { + +EmoteName cleanUpCode(const EmoteName &dirtyEmoteCode) +{ + auto cleanCode = dirtyEmoteCode.string; + cleanCode.detach(); + + static QMap emoteNameReplacements{ + {"[oO](_|\\.)[oO]", "O_o"}, {"\\>\\;\\(", ">("}, {"\\<\\;3", "<3"}, + {"\\:-?(o|O)", ":O"}, {"\\:-?(p|P)", ":P"}, {"\\:-?[\\\\/]", ":/"}, + {"\\:-?[z|Z|\\|]", ":Z"}, {"\\:-?\\(", ":("}, {"\\:-?\\)", ":)"}, + {"\\:-?D", ":D"}, {"\\;-?(p|P)", ";P"}, {"\\;-?\\)", ";)"}, + {"R-?\\)", "R)"}, {"B-?\\)", "B)"}, + }; + + auto it = emoteNameReplacements.find(dirtyEmoteCode.string); + if (it != emoteNameReplacements.end()) { + cleanCode = it.value(); + } + + cleanCode.replace("<", "<"); + cleanCode.replace(">", ">"); + + return {cleanCode}; +} + +} // namespace + TwitchAccount::TwitchAccount(const QString &username, const QString &oauthToken, const QString &oauthClient, const QString &userID) : Account(ProviderId::Twitch) @@ -78,20 +110,20 @@ void TwitchAccount::loadIgnores() NetworkRequest req(url); req.setCaller(QThread::currentThread()); req.makeAuthorizedV5(this->getOAuthClient(), this->getOAuthToken()); - req.onSuccess([=](auto result) { + req.onSuccess([=](auto result) -> Outcome { auto document = result.parseRapidJson(); if (!document.IsObject()) { - return false; + return Failure; } auto blocksIt = document.FindMember("blocks"); if (blocksIt == document.MemberEnd()) { - return false; + return Failure; } const auto &blocks = blocksIt->value; if (!blocks.IsArray()) { - return false; + return Failure; } { @@ -116,7 +148,7 @@ void TwitchAccount::loadIgnores() } } - return true; + return Success; }); req.execute(); @@ -148,25 +180,25 @@ void TwitchAccount::ignoreByID(const QString &targetUserID, const QString &targe return true; }); - req.onSuccess([=](auto result) { + req.onSuccess([=](auto result) -> Outcome { auto document = result.parseRapidJson(); if (!document.IsObject()) { onFinished(IgnoreResult_Failed, "Bad JSON data while ignoring user " + targetName); - return false; + return Failure; } auto userIt = document.FindMember("user"); if (userIt == document.MemberEnd()) { onFinished(IgnoreResult_Failed, "Bad JSON data while ignoring user (missing user) " + targetName); - return false; + return Failure; } TwitchUser ignoredUser; if (!rj::getSafe(userIt->value, ignoredUser)) { onFinished(IgnoreResult_Failed, "Bad JSON data while ignoring user (invalid user) " + targetName); - return false; + return Failure; } { std::lock_guard lock(this->ignoresMutex_); @@ -177,12 +209,12 @@ void TwitchAccount::ignoreByID(const QString &targetUserID, const QString &targe existingUser.update(ignoredUser); onFinished(IgnoreResult_AlreadyIgnored, "User " + targetName + " is already ignored"); - return false; + return Failure; } } onFinished(IgnoreResult_Success, "Successfully ignored user " + targetName); - return true; + return Success; }); req.execute(); @@ -217,7 +249,7 @@ void TwitchAccount::unignoreByID( return true; }); - req.onSuccess([=](auto result) { + req.onSuccess([=](auto result) -> Outcome { auto document = result.parseRapidJson(); TwitchUser ignoredUser; ignoredUser.id = targetUserID; @@ -228,7 +260,7 @@ void TwitchAccount::unignoreByID( } onFinished(UnignoreResult_Success, "Successfully unignored user " + targetName); - return true; + return Success; }); req.execute(); @@ -254,10 +286,10 @@ void TwitchAccount::checkFollow(const QString targetUserID, return true; }); - req.onSuccess([=](auto result) { + req.onSuccess([=](auto result) -> Outcome { auto document = result.parseRapidJson(); onFinished(FollowResult_Following); - return true; + return Success; }); req.execute(); @@ -274,10 +306,10 @@ void TwitchAccount::followUser(const QString userID, std::function succe request.makeAuthorizedV5(this->getOAuthClient(), this->getOAuthToken()); // TODO: Properly check result of follow request - request.onSuccess([successCallback](auto result) { + request.onSuccess([successCallback](auto result) -> Outcome { successCallback(); - return true; + return Success; }); request.execute(); @@ -301,10 +333,10 @@ void TwitchAccount::unfollowUser(const QString userID, std::function suc return true; }); - request.onSuccess([successCallback](const auto &document) { + request.onSuccess([successCallback](const auto &document) -> Outcome { successCallback(); - return true; + return Success; }); request.execute(); @@ -317,7 +349,7 @@ std::set TwitchAccount::getIgnores() const return this->ignores_; } -void TwitchAccount::loadEmotes(std::function cb) +void TwitchAccount::loadEmotes() { Log("Loading Twitch emotes for user {}", this->getUserName()); @@ -346,11 +378,126 @@ void TwitchAccount::loadEmotes(std::function return true; }); - req.onSuccess([=](auto result) { - cb(result.parseRapidJson()); + req.onSuccess([=](auto result) -> Outcome { + this->parseEmotes(result.parseRapidJson()); + + return Success; + }); + + req.execute(); +} + +AccessGuard TwitchAccount::accessEmotes() const +{ + return this->emotes_.accessConst(); +} + +void TwitchAccount::parseEmotes(const rapidjson::Document &root) +{ + auto emoteData = this->emotes_.access(); + + emoteData->emoteSets.clear(); + emoteData->allEmoteNames.clear(); + + auto emoticonSets = root.FindMember("emoticon_sets"); + if (emoticonSets == root.MemberEnd() || !emoticonSets->value.IsObject()) { + Log("No emoticon_sets in load emotes response"); + return; + } + + for (const auto &emoteSetJSON : emoticonSets->value.GetObject()) { + auto emoteSet = std::make_shared(); + + emoteSet->key = emoteSetJSON.name.GetString(); + + this->loadEmoteSetData(emoteSet); + + for (const rapidjson::Value &emoteJSON : emoteSetJSON.value.GetArray()) { + if (!emoteJSON.IsObject()) { + Log("Emote value was invalid"); + return; + } + + uint64_t idNumber; + if (!rj::getSafe(emoteJSON, "id", idNumber)) { + Log("No ID key found in Emote value"); + return; + } + + EmoteName code; + if (!rj::getSafe(emoteJSON, "code", code)) { + Log("No code key found in Emote value"); + return; + } + + auto id = EmoteId{QString::number(idNumber)}; + + auto cleanCode = cleanUpCode(code); + emoteSet->emotes.emplace_back(TwitchEmote{id, cleanCode}); + emoteData->allEmoteNames.push_back(cleanCode); + + auto emote = getApp()->emotes->twitch.getOrCreateEmote(id, code); + emoteData->emotes.emplace(code, emote); + } + + emoteData->emoteSets.emplace_back(emoteSet); + } +}; + +void TwitchAccount::loadEmoteSetData(std::shared_ptr emoteSet) +{ + if (!emoteSet) { + Log("null emote set sent"); + return; + } + + auto staticSetIt = this->staticEmoteSets.find(emoteSet->key); + if (staticSetIt != this->staticEmoteSets.end()) { + const auto &staticSet = staticSetIt->second; + emoteSet->channelName = staticSet.channelName; + emoteSet->text = staticSet.text; + return; + } + + NetworkRequest req("https://braize.pajlada.com/chatterino/twitchemotes/set/" + emoteSet->key + + "/"); + req.setUseQuickLoadCache(true); + + req.onError([](int errorCode) -> bool { + Log("Error code {} while loading emote set data", errorCode); return true; }); + req.onSuccess([emoteSet](auto result) -> Outcome { + auto root = result.parseRapidJson(); + if (!root.IsObject()) { + return Failure; + } + + std::string emoteSetID; + QString channelName; + QString type; + if (!rj::getSafe(root, "channel_name", channelName)) { + return Failure; + } + + if (!rj::getSafe(root, "type", type)) { + return Failure; + } + + Log("Loaded twitch emote set data for {}!", emoteSet->key); + + if (type == "sub") { + emoteSet->text = QString("Twitch Subscriber Emote (%1)").arg(channelName); + } else { + emoteSet->text = QString("Twitch Account Emote (%1)").arg(channelName); + } + + emoteSet->channelName = channelName; + + return Success; + }); + req.execute(); } diff --git a/src/providers/twitch/TwitchAccount.hpp b/src/providers/twitch/TwitchAccount.hpp index ee215209a..be97a0653 100644 --- a/src/providers/twitch/TwitchAccount.hpp +++ b/src/providers/twitch/TwitchAccount.hpp @@ -1,6 +1,8 @@ #pragma once +#include "common/UniqueAccess.hpp" #include "controllers/accounts/Account.hpp" +#include "messages/Emote.hpp" #include "providers/twitch/TwitchUser.hpp" #include @@ -33,6 +35,28 @@ enum FollowResult { class TwitchAccount : public Account { public: + struct TwitchEmote { + EmoteId id; + EmoteName name; + }; + + struct EmoteSet { + QString key; + QString channelName; + QString text; + std::vector emotes; + }; + + std::map staticEmoteSets; + + struct TwitchAccountEmoteData { + std::vector> emoteSets; + + std::vector allEmoteNames; + + EmoteMap emotes; + }; + TwitchAccount(const QString &username, const QString &oauthToken_, const QString &oauthClient_, const QString &_userID); @@ -70,11 +94,15 @@ public: std::set getIgnores() const; - void loadEmotes(std::function cb); + void loadEmotes(); + AccessGuard accessEmotes() const; QColor color; private: + void parseEmotes(const rapidjson::Document &document); + void loadEmoteSetData(std::shared_ptr emoteSet); + QString oauthClient_; QString oauthToken_; QString userName_; @@ -83,6 +111,9 @@ private: mutable std::mutex ignoresMutex_; std::set ignores_; + + // std::map emotes; + UniqueAccess emotes_; }; } // namespace chatterino diff --git a/src/providers/twitch/TwitchApi.cpp b/src/providers/twitch/TwitchApi.cpp index 82d9dc3f3..58ab03af1 100644 --- a/src/providers/twitch/TwitchApi.cpp +++ b/src/providers/twitch/TwitchApi.cpp @@ -16,23 +16,23 @@ void TwitchApi::findUserId(const QString user, std::function succ request.setCaller(QThread::currentThread()); request.makeAuthorizedV5(getDefaultClientID()); request.setTimeout(30000); - request.onSuccess([successCallback](auto result) mutable { + request.onSuccess([successCallback](auto result) mutable -> Outcome { auto root = result.parseJson(); if (!root.value("users").isArray()) { Log("API Error while getting user id, users is not an array"); successCallback(""); - return false; + return Failure; } auto users = root.value("users").toArray(); if (users.size() != 1) { Log("API Error while getting user id, users array size is not 1"); successCallback(""); - return false; + return Failure; } if (!users[0].isObject()) { Log("API Error while getting user id, first user is not an object"); successCallback(""); - return false; + return Failure; } auto firstUser = users[0].toObject(); auto id = firstUser.value("_id"); @@ -40,10 +40,10 @@ void TwitchApi::findUserId(const QString user, std::function succ Log("API Error: while getting user id, first user object `_id` key is not a " "string"); successCallback(""); - return false; + return Failure; } successCallback(id.toString()); - return true; + return Success; }); request.execute(); diff --git a/src/providers/twitch/TwitchBadges.cpp b/src/providers/twitch/TwitchBadges.cpp new file mode 100644 index 000000000..db329d306 --- /dev/null +++ b/src/providers/twitch/TwitchBadges.cpp @@ -0,0 +1,58 @@ +#include "TwitchBadges.hpp" + +#include +#include +#include +#include +#include "common/NetworkRequest.hpp" + +namespace chatterino { + +TwitchBadges::TwitchBadges() +{ +} + +void TwitchBadges::initialize(Settings &settings, Paths &paths) +{ + this->loadTwitchBadges(); +} + +void TwitchBadges::loadTwitchBadges() +{ + static QString url("https://badges.twitch.tv/v1/badges/global/display?language=en"); + + NetworkRequest req(url); + req.setCaller(QThread::currentThread()); + req.onSuccess([this](auto result) -> Outcome { + auto root = result.parseJson(); + QJsonObject sets = root.value("badge_sets").toObject(); + + for (QJsonObject::iterator it = sets.begin(); it != sets.end(); ++it) { + QJsonObject versions = it.value().toObject().value("versions").toObject(); + + for (auto versionIt = std::begin(versions); versionIt != std::end(versions); + ++versionIt) { + auto emote = + Emote{{""}, + ImageSet{ + Image::fromUrl({root.value("image_url_1x").toString()}, 1), + Image::fromUrl({root.value("image_url_2x").toString()}, 0.5), + Image::fromUrl({root.value("image_url_4x").toString()}, 0.25), + }, + Tooltip{root.value("description").toString()}, + Url{root.value("clickURL").toString()}}; + // "title" + // "clickAction" + + QJsonObject versionObj = versionIt.value().toObject(); + this->badges.emplace(versionIt.key(), std::make_shared(emote)); + } + } + + return Success; + }); + + req.execute(); +} + +} // namespace chatterino diff --git a/src/providers/twitch/TwitchBadges.hpp b/src/providers/twitch/TwitchBadges.hpp new file mode 100644 index 000000000..a228365da --- /dev/null +++ b/src/providers/twitch/TwitchBadges.hpp @@ -0,0 +1,26 @@ +#pragma once + +#include +#include +#include +#include "util/QStringHash.hpp" + +namespace chatterino { + +class Settings; +class Paths; + +class TwitchBadges +{ +public: + TwitchBadges(); + + void initialize(Settings &settings, Paths &paths); + +private: + void loadTwitchBadges(); + + std::unordered_map badges; +}; + +} // namespace chatterino diff --git a/src/providers/twitch/TwitchChannel.cpp b/src/providers/twitch/TwitchChannel.cpp index 6a77a3e89..ea1d0e3ae 100644 --- a/src/providers/twitch/TwitchChannel.cpp +++ b/src/providers/twitch/TwitchChannel.cpp @@ -5,32 +5,34 @@ #include "controllers/accounts/AccountController.hpp" #include "debug/Log.hpp" #include "messages/Message.hpp" +#include "providers/bttv/LoadBttvChannelEmote.hpp" #include "providers/twitch/PubsubClient.hpp" #include "providers/twitch/TwitchCommon.hpp" #include "providers/twitch/TwitchMessageBuilder.hpp" +#include "providers/twitch/TwitchParseCheerEmotes.hpp" #include "singletons/Emotes.hpp" #include "singletons/Settings.hpp" #include "util/PostToThread.hpp" #include #include +#include +#include #include #include namespace chatterino { -TwitchChannel::TwitchChannel(const QString &channelName) - : Channel(channelName, Channel::Type::Twitch) - , bttvEmotes_(new EmoteMap) - , ffzEmotes_(new EmoteMap) +TwitchChannel::TwitchChannel(const QString &name) + : Channel(name, Channel::Type::Twitch) , subscriptionUrl_("https://www.twitch.tv/subs/" + name) , channelUrl_("https://twitch.tv/" + name) , popoutPlayerUrl_("https://player.twitch.tv/?channel=" + name) , mod_(false) { - Log("[TwitchChannel:{}] Opened", this->name); + Log("[TwitchChannel:{}] Opened", name); - this->refreshChannelEmotes(); + // this->refreshChannelEmotes(); // this->refreshViewerList(); this->managedConnect(getApp()->accounts->twitch.currentUserChanged, @@ -38,13 +40,17 @@ TwitchChannel::TwitchChannel(const QString &channelName) // pubsub this->userStateChanged.connect([=] { this->refreshPubsub(); }); - this->roomIdChanged.connect([=] { this->refreshPubsub(); }); this->managedConnect(getApp()->accounts->twitch.currentUserChanged, [=] { this->refreshPubsub(); }); this->refreshPubsub(); // room id loaded -> refresh live status - this->roomIdChanged.connect([this]() { this->refreshLiveStatus(); }); + this->roomIdChanged.connect([this]() { + this->refreshPubsub(); + this->refreshLiveStatus(); + this->loadBadges(); + this->loadCheerEmotes(); + }); // timers QObject::connect(&this->chattersListTimer_, &QTimer::timeout, @@ -68,7 +74,7 @@ TwitchChannel::TwitchChannel(const QString &channelName) bool TwitchChannel::isEmpty() const { - return this->name.isEmpty(); + return this->getName().isEmpty(); } bool TwitchChannel::canSendMessage() const @@ -78,12 +84,15 @@ bool TwitchChannel::canSendMessage() const void TwitchChannel::refreshChannelEmotes() { - auto app = getApp(); - - Log("[TwitchChannel:{}] Reloading channel emotes", this->name); - - app->emotes->bttv.loadChannelEmotes(this->name, this->bttvEmotes_); - app->emotes->ffz.loadChannelEmotes(this->name, this->ffzEmotes_); + loadBttvChannelEmotes(this->getName(), [this, weak = weakOf(this)](auto &&emoteMap) { + if (auto shared = weak.lock()) // + *this->bttvEmotes_.access() = emoteMap; + }); + getApp()->emotes->ffz.loadChannelEmotes(this->getName(), + [this, weak = weakOf(this)](auto &&emoteMap) { + if (auto shared = weak.lock()) + *this->ffzEmotes_.access() = emoteMap; + }); } void TwitchChannel::sendMessage(const QString &message) @@ -99,7 +108,7 @@ void TwitchChannel::sendMessage(const QString &message) return; } - Log("[TwitchChannel:{}] Send message: {}", this->name, message); + Log("[TwitchChannel:{}] Send message: {}", this->getName(), message); // Do last message processing QString parsedMessage = app->emotes->emojis.replaceShortCodes(message); @@ -119,7 +128,7 @@ void TwitchChannel::sendMessage(const QString &message) } bool messageSent = false; - this->sendMessageSignal.invoke(this->name, parsedMessage, messageSent); + this->sendMessageSignal.invoke(this->getName(), parsedMessage, messageSent); if (messageSent) { qDebug() << "sent"; @@ -145,7 +154,7 @@ bool TwitchChannel::isBroadcaster() const { auto app = getApp(); - return this->name == app->accounts->twitch.getCurrent()->getUserName(); + return this->getName() == app->accounts->twitch.getCurrent()->getUserName(); } void TwitchChannel::addRecentChatter(const std::shared_ptr &message) @@ -243,14 +252,32 @@ const AccessGuard TwitchChannel::accessStreamStatus return this->streamStatus_.access(); } -const EmoteMap &TwitchChannel::getFfzEmotes() const +boost::optional TwitchChannel::getBttvEmote(const EmoteName &name) const { - return *this->ffzEmotes_; + auto emotes = this->bttvEmotes_.access(); + auto it = emotes->find(name); + + if (it == emotes->end()) return boost::none; + return it->second; } -const EmoteMap &TwitchChannel::getBttvEmotes() const +boost::optional TwitchChannel::getFfzEmote(const EmoteName &name) const { - return *this->bttvEmotes_; + auto emotes = this->bttvEmotes_.access(); + auto it = emotes->find(name); + + if (it == emotes->end()) return boost::none; + return it->second; +} + +AccessGuard TwitchChannel::accessBttvEmotes() const +{ + return this->bttvEmotes_.accessConst(); +} + +AccessGuard TwitchChannel::accessFfzEmotes() const +{ + return this->ffzEmotes_.accessConst(); } const QString &TwitchChannel::getSubscriptionUrl() @@ -289,12 +316,12 @@ void TwitchChannel::refreshLiveStatus() auto roomID = this->getRoomId(); if (roomID.isEmpty()) { - Log("[TwitchChannel:{}] Refreshing live status (Missing ID)", this->name); + Log("[TwitchChannel:{}] Refreshing live status (Missing ID)", this->getName()); this->setLive(false); return; } - Log("[TwitchChannel:{}] Refreshing live status", this->name); + Log("[TwitchChannel:{}] Refreshing live status", this->getName()); QString url("https://api.twitch.tv/kraken/streams/" + roomID); @@ -315,16 +342,16 @@ void TwitchChannel::refreshLiveStatus() // request.execute(); } -bool TwitchChannel::parseLiveStatus(const rapidjson::Document &document) +Outcome TwitchChannel::parseLiveStatus(const rapidjson::Document &document) { if (!document.IsObject()) { Log("[TwitchChannel:refreshLiveStatus] root is not an object"); - return false; + return Failure; } if (!document.HasMember("stream")) { Log("[TwitchChannel:refreshLiveStatus] Missing stream in root"); - return false; + return Failure; } const auto &stream = document["stream"]; @@ -332,21 +359,21 @@ bool TwitchChannel::parseLiveStatus(const rapidjson::Document &document) if (!stream.IsObject()) { // Stream is offline (stream is most likely null) this->setLive(false); - return false; + return Failure; } if (!stream.HasMember("viewers") || !stream.HasMember("game") || !stream.HasMember("channel") || !stream.HasMember("created_at")) { Log("[TwitchChannel:refreshLiveStatus] Missing members in stream"); this->setLive(false); - return false; + return Failure; } const rapidjson::Value &streamChannel = stream["channel"]; if (!streamChannel.IsObject() || !streamChannel.HasMember("status")) { Log("[TwitchChannel:refreshLiveStatus] Missing member \"status\" in channel"); - return false; + return Failure; } // Stream is live @@ -384,7 +411,7 @@ bool TwitchChannel::parseLiveStatus(const rapidjson::Document &document) // Signal all listeners that the stream status has been updated this->liveStatusChanged.invoke(); - return true; + return Success; } void TwitchChannel::loadRecentMessages() @@ -396,24 +423,20 @@ void TwitchChannel::loadRecentMessages() request.makeAuthorizedV5(getDefaultClientID()); request.setCaller(QThread::currentThread()); - request.onSuccess([this, weak = this->weak_from_this()](auto result) { - // channel still exists? + request.onSuccess([this, weak = weakOf(this)](auto result) -> Outcome { ChannelPtr shared = weak.lock(); - if (!shared) return false; + if (!shared) return Failure; - // parse json return this->parseRecentMessages(result.parseJson()); }); request.execute(); } -bool TwitchChannel::parseRecentMessages(const QJsonObject &jsonRoot) +Outcome TwitchChannel::parseRecentMessages(const QJsonObject &jsonRoot) { QJsonArray jsonMessages = jsonRoot.value("messages").toArray(); - if (jsonMessages.empty()) { - return false; - } + if (jsonMessages.empty()) return Failure; std::vector messages; @@ -434,7 +457,7 @@ bool TwitchChannel::parseRecentMessages(const QJsonObject &jsonRoot) this->addMessagesAtStart(messages); - return true; + return Success; } void TwitchChannel::refreshPubsub() @@ -460,13 +483,13 @@ void TwitchChannel::refreshViewerList() } // get viewer list - NetworkRequest request("https://tmi.twitch.tv/group/user/" + this->name + "/chatters"); + NetworkRequest request("https://tmi.twitch.tv/group/user/" + this->getName() + "/chatters"); request.setCaller(QThread::currentThread()); - request.onSuccess([this, weak = this->weak_from_this()](auto result) { + request.onSuccess([this, weak = this->weak_from_this()](auto result) -> Outcome { // channel still exists? auto shared = weak.lock(); - if (!shared) return false; + if (!shared) return Failure; return this->parseViewerList(result.parseJson()); }); @@ -474,7 +497,7 @@ void TwitchChannel::refreshViewerList() request.execute(); } -bool TwitchChannel::parseViewerList(const QJsonObject &jsonRoot) +Outcome TwitchChannel::parseViewerList(const QJsonObject &jsonRoot) { static QStringList categories = {"moderators", "staff", "admins", "global_mods", "viewers"}; @@ -487,7 +510,118 @@ bool TwitchChannel::parseViewerList(const QJsonObject &jsonRoot) } } - return true; + return Success; +} + +void TwitchChannel::loadBadges() +{ + auto url = Url{"https://badges.twitch.tv/v1/badges/channels/" + this->getRoomId() + + "/display?language=en"}; + NetworkRequest req(url.string); + req.setCaller(QThread::currentThread()); + + req.onSuccess([this, weak = weakOf(this)](auto result) -> Outcome { + auto shared = weak.lock(); + if (!shared) return Failure; + + auto badgeSets = this->badgeSets_.access(); + + auto jsonRoot = result.parseJson(); + + auto _ = jsonRoot["badge_sets"].toObject(); + for (auto jsonBadgeSet = _.begin(); jsonBadgeSet != _.end(); jsonBadgeSet++) { + auto &versions = (*badgeSets)[jsonBadgeSet.key()]; + + auto _ = jsonBadgeSet->toObject()["versions"].toObject(); + for (auto jsonVersion_ = _.begin(); jsonVersion_ != _.end(); jsonVersion_++) { + auto jsonVersion = jsonVersion_->toObject(); + auto emote = std::make_shared( + Emote{EmoteName{}, + ImageSet{Image::fromUrl({jsonVersion["image_url_1x"].toString()}), + Image::fromUrl({jsonVersion["image_url_2x"].toString()}), + Image::fromUrl({jsonVersion["image_url_4x"].toString()})}, + Tooltip{jsonRoot["description"].toString()}, + Url{jsonVersion["clickURL"].toString()}}); + + versions.emplace(jsonVersion_.key(), emote); + }; + } + + return Success; + }); + + req.execute(); +} + +void TwitchChannel::loadCheerEmotes() +{ + auto url = Url{"https://api.twitch.tv/kraken/bits/actions?channel_id=" + this->getRoomId()}; + auto request = NetworkRequest::twitchRequest(url.string); + request.setCaller(QThread::currentThread()); + + request.onSuccess([this, weak = weakOf(this)](auto result) -> Outcome { + auto cheerEmoteSets = ParseCheermoteSets(result.parseRapidJson()); + + for (auto &set : cheerEmoteSets) { + auto cheerEmoteSet = CheerEmoteSet(); + cheerEmoteSet.regex = QRegularExpression("^" + set.prefix.toLower() + "([1-9][0-9]*)$"); + + for (auto &tier : set.tiers) { + CheerEmote cheerEmote; + + cheerEmote.color = QColor(tier.color); + cheerEmote.minBits = tier.minBits; + + // TODO(pajlada): We currently hardcode dark here :| + // We will continue to do so for now since we haven't had to + // solve that anywhere else + + cheerEmote.animatedEmote = + std::make_shared(Emote{EmoteName{"cheer emote"}, + ImageSet{ + tier.images["dark"]["animated"]["1"], + tier.images["dark"]["animated"]["2"], + tier.images["dark"]["animated"]["4"], + }, + Tooltip{}, Url{}}); + cheerEmote.staticEmote = + std::make_shared(Emote{EmoteName{"cheer emote"}, + ImageSet{ + tier.images["dark"]["static"]["1"], + tier.images["dark"]["static"]["2"], + tier.images["dark"]["static"]["4"], + }, + Tooltip{}, Url{}}); + + cheerEmoteSet.cheerEmotes.emplace_back(cheerEmote); + } + + std::sort(cheerEmoteSet.cheerEmotes.begin(), cheerEmoteSet.cheerEmotes.end(), + [](const auto &lhs, const auto &rhs) { + return lhs.minBits < rhs.minBits; // + }); + + this->cheerEmoteSets_.emplace_back(cheerEmoteSet); + } + + return Success; + }); + + request.execute(); +} + +boost::optional TwitchChannel::getTwitchBadge(const QString &set, + const QString &version) const +{ + auto badgeSets = this->badgeSets_.access(); + auto it = badgeSets->find(set); + if (it != badgeSets->end()) { + auto it2 = it->second.find(version); + if (it2 != it->second.end()) { + return it2->second; + } + } + return boost::none; } } // namespace chatterino diff --git a/src/providers/twitch/TwitchChannel.hpp b/src/providers/twitch/TwitchChannel.hpp index c2986029d..480b3d637 100644 --- a/src/providers/twitch/TwitchChannel.hpp +++ b/src/providers/twitch/TwitchChannel.hpp @@ -6,12 +6,14 @@ #include "common/Common.hpp" #include "common/MutexValue.hpp" #include "common/UniqueAccess.hpp" +#include "messages/Emote.hpp" #include "singletons/Emotes.hpp" #include "util/ConcurrentMap.hpp" #include #include +#include namespace chatterino { @@ -68,12 +70,16 @@ public: void setRoomModes(const RoomModes &roomModes_); const AccessGuard accessStreamStatus() const; - const EmoteMap &getFfzEmotes() const; - const EmoteMap &getBttvEmotes() const; + boost::optional getBttvEmote(const EmoteName &name) const; + boost::optional getFfzEmote(const EmoteName &name) const; + AccessGuard accessBttvEmotes() const; + AccessGuard accessFfzEmotes() const; const QString &getSubscriptionUrl(); const QString &getChannelUrl(); const QString &getPopoutPlayerUrl(); + boost::optional getTwitchBadge(const QString &set, const QString &version) const; + // Signals pajlada::Signals::NoArgSignal roomIdChanged; pajlada::Signals::NoArgSignal liveStatusChanged; @@ -86,26 +92,43 @@ private: QString localizedName; }; + struct CheerEmote { + // a Cheermote indicates one tier + QColor color; + int minBits; + + EmotePtr animatedEmote; + EmotePtr staticEmote; + }; + + struct CheerEmoteSet { + QRegularExpression regex; + std::vector cheerEmotes; + }; + explicit TwitchChannel(const QString &channelName); // Methods void refreshLiveStatus(); - bool parseLiveStatus(const rapidjson::Document &document); + Outcome parseLiveStatus(const rapidjson::Document &document); void refreshPubsub(); void refreshViewerList(); - bool parseViewerList(const QJsonObject &jsonRoot); + Outcome parseViewerList(const QJsonObject &jsonRoot); void loadRecentMessages(); - bool parseRecentMessages(const QJsonObject &jsonRoot); + Outcome parseRecentMessages(const QJsonObject &jsonRoot); void setLive(bool newLiveStatus); + void loadBadges(); + void loadCheerEmotes(); + // Twitch data UniqueAccess streamStatus_; UniqueAccess userState_; UniqueAccess roomModes_; - const std::shared_ptr bttvEmotes_; - const std::shared_ptr ffzEmotes_; + UniqueAccess bttvEmotes_; + UniqueAccess ffzEmotes_; const QString subscriptionUrl_; const QString channelUrl_; const QString popoutPlayerUrl_; @@ -118,6 +141,10 @@ private: UniqueAccess partedUsers_; bool partedUsersMergeQueued_ = false; + // "subscribers": { "0": ... "3": ... "6": ... + UniqueAccess>> badgeSets_; + std::vector cheerEmoteSets_; + // -- QByteArray messageSuffix_; QString lastSentMessage_; diff --git a/src/providers/twitch/TwitchEmotes.cpp b/src/providers/twitch/TwitchEmotes.cpp index 3f94a80b3..80337912e 100644 --- a/src/providers/twitch/TwitchEmotes.cpp +++ b/src/providers/twitch/TwitchEmotes.cpp @@ -6,228 +6,64 @@ #include "messages/Image.hpp" #include "util/RapidjsonHelpers.hpp" -#define TWITCH_EMOTE_TEMPLATE "https://static-cdn.jtvnw.net/emoticons/v1/{id}/{scale}" - namespace chatterino { -namespace { - -QString getEmoteLink(const QString &id, const QString &emoteScale) -{ - QString value = TWITCH_EMOTE_TEMPLATE; - - value.detach(); - - return value.replace("{id}", id).replace("{scale}", emoteScale); -} - -QString cleanUpCode(const QString &dirtyEmoteCode) -{ - QString cleanCode = dirtyEmoteCode; - // clang-format off - static QMap emoteNameReplacements{ - {"[oO](_|\\.)[oO]", "O_o"}, {"\\>\\;\\(", ">("}, {"\\<\\;3", "<3"}, - {"\\:-?(o|O)", ":O"}, {"\\:-?(p|P)", ":P"}, {"\\:-?[\\\\/]", ":/"}, - {"\\:-?[z|Z|\\|]", ":Z"}, {"\\:-?\\(", ":("}, {"\\:-?\\)", ":)"}, - {"\\:-?D", ":D"}, {"\\;-?(p|P)", ";P"}, {"\\;-?\\)", ";)"}, - {"R-?\\)", "R)"}, {"B-?\\)", "B)"}, - }; - // clang-format on - - auto it = emoteNameReplacements.find(dirtyEmoteCode); - if (it != emoteNameReplacements.end()) { - cleanCode = it.value(); - } - - cleanCode.replace("<", "<"); - cleanCode.replace(">", ">"); - - return cleanCode; -} - -} // namespace - TwitchEmotes::TwitchEmotes() { - { - EmoteSet emoteSet; - emoteSet.key = "19194"; - emoteSet.text = "Twitch Prime Emotes"; - this->staticEmoteSets[emoteSet.key] = std::move(emoteSet); - } - - { - EmoteSet emoteSet; - emoteSet.key = "0"; - emoteSet.text = "Twitch Global Emotes"; - this->staticEmoteSets[emoteSet.key] = std::move(emoteSet); - } } // id is used for lookup // emoteName is used for giving a name to the emote in case it doesn't exist -EmoteData TwitchEmotes::getEmoteById(const QString &id, const QString &emoteName) +EmotePtr TwitchEmotes::getOrCreateEmote(const EmoteId &id, const EmoteName &name_) { - QString _emoteName = emoteName; - _emoteName.replace("<", "<"); - _emoteName.replace(">", ">"); - - // clang-format off - static QMap emoteNameReplacements{ - {"[oO](_|\\.)[oO]", "O_o"}, {"\\>\\;\\(", ">("}, {"\\<\\;3", "<3"}, - {"\\:-?(o|O)", ":O"}, {"\\:-?(p|P)", ":P"}, {"\\:-?[\\\\/]", ":/"}, - {"\\:-?[z|Z|\\|]", ":Z"}, {"\\:-?\\(", ":("}, {"\\:-?\\)", ":)"}, - {"\\:-?D", ":D"}, {"\\;-?(p|P)", ";P"}, {"\\;-?\\)", ";)"}, + static QMap replacements{ + {"[oO](_|\\.)[oO]", "O_o"}, {"\\>\\;\\(", ">("}, {"\\<\\;3", "<3"}, + {"\\:-?(o|O)", ":O"}, {"\\:-?(p|P)", ":P"}, {"\\:-?[\\\\/]", ":/"}, + {"\\:-?[z|Z|\\|]", ":Z"}, {"\\:-?\\(", ":("}, {"\\:-?\\)", ":)"}, + {"\\:-?D", ":D"}, {"\\;-?(p|P)", ";P"}, {"\\;-?\\)", ";)"}, {"R-?\\)", "R)"}, {"B-?\\)", "B)"}, }; - // clang-format on - auto it = emoteNameReplacements.find(_emoteName); - if (it != emoteNameReplacements.end()) { - _emoteName = it.value(); + auto name = name_.string; + name.detach(); + + // replace < > + name.replace("<", "<"); + name.replace(">", ">"); + + // replace regexes + auto it = replacements.find(name); + if (it != replacements.end()) { + name = it.value(); } - return twitchEmoteFromCache_.getOrAdd(id, [&emoteName, &_emoteName, &id] { - EmoteData newEmoteData; - auto cleanCode = cleanUpCode(emoteName); - newEmoteData.image1x = - new Image(getEmoteLink(id, "1.0"), 1, emoteName, _emoteName + "
Twitch Emote"); - newEmoteData.image1x->setCopyString(cleanCode); + // search in cache or create new emote + auto cache = this->twitchEmotesCache_.access(); + auto shared = (*cache)[id].lock(); - newEmoteData.image2x = - new Image(getEmoteLink(id, "2.0"), .5, emoteName, _emoteName + "
Twitch Emote"); - newEmoteData.image2x->setCopyString(cleanCode); + if (!shared) { + (*cache)[id] = shared = + std::make_shared(Emote{EmoteName{name}, + ImageSet{ + Image::fromUrl(getEmoteLink(id, "1.0"), 1), + Image::fromUrl(getEmoteLink(id, "2.0"), 0.5), + Image::fromUrl(getEmoteLink(id, "3.0"), 0.25), + }, + Tooltip{name}, Url{}}); + } - newEmoteData.image3x = - new Image(getEmoteLink(id, "3.0"), .25, emoteName, _emoteName + "
Twitch Emote"); - - newEmoteData.image3x->setCopyString(cleanCode); - - return newEmoteData; - }); + return shared; } -void TwitchEmotes::refresh(const std::shared_ptr &user) +Url TwitchEmotes::getEmoteLink(const EmoteId &id, const QString &emoteScale) { - const auto &roomID = user->getUserId(); - TwitchAccountEmoteData &emoteData = this->emotes[roomID]; - - if (emoteData.filled) { - Log("Emotes are already loaded for room id {}", roomID); - return; - } - - auto loadEmotes = [=, &emoteData](const rapidjson::Document &root) { - emoteData.emoteSets.clear(); - emoteData.emoteCodes.clear(); - - auto emoticonSets = root.FindMember("emoticon_sets"); - if (emoticonSets == root.MemberEnd() || !emoticonSets->value.IsObject()) { - Log("No emoticon_sets in load emotes response"); - return; - } - - for (const auto &emoteSetJSON : emoticonSets->value.GetObject()) { - auto emoteSet = std::make_shared(); - - emoteSet->key = emoteSetJSON.name.GetString(); - - this->loadSetData(emoteSet); - - for (const rapidjson::Value &emoteJSON : emoteSetJSON.value.GetArray()) { - if (!emoteJSON.IsObject()) { - Log("Emote value was invalid"); - return; - } - - QString id, code; - - uint64_t idNumber; - - if (!rj::getSafe(emoteJSON, "id", idNumber)) { - Log("No ID key found in Emote value"); - return; - } - - if (!rj::getSafe(emoteJSON, "code", code)) { - Log("No code key found in Emote value"); - return; - } - - id = QString::number(idNumber); - - auto cleanCode = cleanUpCode(code); - emoteSet->emotes.emplace_back(id, cleanCode); - emoteData.emoteCodes.push_back(cleanCode); - - EmoteData emote = this->getEmoteById(id, code); - emoteData.emotes.insert(code, emote); - } - - emoteData.emoteSets.emplace_back(emoteSet); - } - - emoteData.filled = true; - }; - - user->loadEmotes(loadEmotes); + return { + QString(TWITCH_EMOTE_TEMPLATE).replace("{id}", id.string).replace("{scale}", emoteScale)}; } -void TwitchEmotes::loadSetData(std::shared_ptr emoteSet) +AccessGuard> TwitchEmotes::accessAll() { - if (!emoteSet) { - Log("null emote set sent"); - return; - } - - auto staticSetIt = this->staticEmoteSets.find(emoteSet->key); - if (staticSetIt != this->staticEmoteSets.end()) { - const auto &staticSet = staticSetIt->second; - emoteSet->channelName = staticSet.channelName; - emoteSet->text = staticSet.text; - return; - } - - NetworkRequest req("https://braize.pajlada.com/chatterino/twitchemotes/set/" + emoteSet->key + - "/"); - req.setUseQuickLoadCache(true); - - req.onError([](int errorCode) -> bool { - Log("Error code {} while loading emote set data", errorCode); - return true; - }); - - req.onSuccess([emoteSet](auto result) -> bool { - auto root = result.parseRapidJson(); - if (!root.IsObject()) { - return false; - } - - std::string emoteSetID; - QString channelName; - QString type; - if (!rj::getSafe(root, "channel_name", channelName)) { - return false; - } - - if (!rj::getSafe(root, "type", type)) { - return false; - } - - Log("Loaded twitch emote set data for {}!", emoteSet->key); - - if (type == "sub") { - emoteSet->text = QString("Twitch Subscriber Emote (%1)").arg(channelName); - } else { - emoteSet->text = QString("Twitch Account Emote (%1)").arg(channelName); - } - - emoteSet->channelName = channelName; - - return true; - }); - - req.execute(); + return this->twitchEmotes_.access(); } } // namespace chatterino diff --git a/src/providers/twitch/TwitchEmotes.hpp b/src/providers/twitch/TwitchEmotes.hpp index 295fca634..e403ebb43 100644 --- a/src/providers/twitch/TwitchEmotes.hpp +++ b/src/providers/twitch/TwitchEmotes.hpp @@ -1,14 +1,17 @@ #pragma once +#include +#include + #include "common/Emotemap.hpp" +#include "common/UniqueAccess.hpp" +#include "messages/Emote.hpp" #include "providers/twitch/EmoteValue.hpp" #include "providers/twitch/TwitchAccount.hpp" #include "providers/twitch/TwitchEmotes.hpp" #include "util/ConcurrentMap.hpp" -#include - -#include +#define TWITCH_EMOTE_TEMPLATE "https://static-cdn.jtvnw.net/emoticons/v1/{id}/{scale}" namespace chatterino { @@ -17,55 +20,13 @@ class TwitchEmotes public: TwitchEmotes(); - EmoteData getEmoteById(const QString &id, const QString &emoteName); - - /// Twitch emotes - void refresh(const std::shared_ptr &user); - - struct TwitchEmote { - TwitchEmote(const QString &_id, const QString &_code) - : id(_id) - , code(_code) - { - } - - // i.e. "403921" - QString id; - - // i.e. "forsenE" - QString code; - }; - - struct EmoteSet { - QString key; - QString channelName; - QString text; - std::vector emotes; - }; - - std::map staticEmoteSets; - - struct TwitchAccountEmoteData { - std::vector> emoteSets; - - std::vector emoteCodes; - - EmoteMap emotes; - - bool filled = false; - }; - - // Key is the user ID - std::map emotes; + EmotePtr getOrCreateEmote(const EmoteId &id, const EmoteName &name); + Url getEmoteLink(const EmoteId &id, const QString &emoteScale); + AccessGuard> accessAll(); private: - void loadSetData(std::shared_ptr emoteSet); - - // emote code - ConcurrentMap twitchEmotes_; - - // emote id - ConcurrentMap twitchEmoteFromCache_; + UniqueAccess> twitchEmotes_; + UniqueAccess>> twitchEmotesCache_; }; } // namespace chatterino diff --git a/src/providers/twitch/TwitchMessageBuilder.cpp b/src/providers/twitch/TwitchMessageBuilder.cpp index 4b75ecd5d..f2a4aa7a5 100644 --- a/src/providers/twitch/TwitchMessageBuilder.cpp +++ b/src/providers/twitch/TwitchMessageBuilder.cpp @@ -16,6 +16,7 @@ #include #include #include +#include namespace chatterino { @@ -83,7 +84,7 @@ MessagePtr TwitchMessageBuilder::build() // PARSING this->parseUsername(); - if (this->userName == this->channel->name) { + if (this->userName == this->channel->getName()) { this->senderIsBroadcaster = true; } @@ -143,14 +144,15 @@ MessagePtr TwitchMessageBuilder::build() // highlights this->parseHighlights(isPastMsg); - QString bits; + // QString bits; auto iterator = this->tags.find("bits"); if (iterator != this->tags.end()) { - bits = iterator.value().toString(); + this->hasBits_ = true; + // bits = iterator.value().toString(); } // twitch emotes - std::vector> twitchEmotes; + std::vector> twitchEmotes; iterator = this->tags.find("emotes"); if (iterator != this->tags.end()) { @@ -164,113 +166,117 @@ MessagePtr TwitchMessageBuilder::build() [](const auto &a, const auto &b) { return a.first < b.first; }); } - auto currentTwitchEmote = twitchEmotes.begin(); - // words - QStringList splits = this->originalMessage_.split(' '); - long int i = 0; + this->addWords(splits, twitchEmotes); - for (QString split : splits) { - MessageColor textColor = - this->action_ ? MessageColor(this->usernameColor_) : MessageColor(MessageColor::Text); + this->message_->searchText = this->userName + ": " + this->originalMessage_; - // twitch emote + return this->getMessage(); +} + +void TwitchMessageBuilder::addWords(const QStringList &words, + const std::vector> &twitchEmotes) +{ + auto i = int(); + auto currentTwitchEmote = twitchEmotes.begin(); + + for (const auto &word : words) { + // check if it's a twitch emote twitch emote if (currentTwitchEmote != twitchEmotes.end() && currentTwitchEmote->first == i) { auto emoteImage = currentTwitchEmote->second; this->emplace(emoteImage, MessageElement::TwitchEmote); - i += split.length() + 1; - currentTwitchEmote = std::next(currentTwitchEmote); + i += word.length() + 1; + currentTwitchEmote++; continue; } // split words - std::vector> parsed; - - // Parse emojis and take all non-emojis and put them in parsed as full text-words - app->emotes->emojis.parse(parsed, split); - - for (const auto &tuple : parsed) { - const EmoteData &emoteData = std::get<0>(tuple); - - if (!emoteData.isValid()) { // is text - QString string = std::get<1>(tuple); - - if (!bits.isEmpty() && this->tryParseCheermote(string)) { - // This string was parsed as a cheermote - continue; - } - - // TODO: Implement ignored emotes - // Format of ignored emotes: - // Emote name: "forsenPuke" - if string in ignoredEmotes - // Will match emote regardless of source (i.e. bttv, ffz) - // Emote source + name: "bttv:nyanPls" - if (this->tryAppendEmote(string)) { - // Successfully appended an emote - continue; - } - - // Actually just text - QString linkString = this->matchLink(string); - - Link link; - - if (linkString.isEmpty()) { - link = Link(); - } else { - if (app->settings->lowercaseLink) { - QRegularExpression httpRegex("\\bhttps?://", - QRegularExpression::CaseInsensitiveOption); - QRegularExpression ftpRegex("\\bftps?://", - QRegularExpression::CaseInsensitiveOption); - QRegularExpression getDomain("\\/\\/([^\\/]*)"); - QString tempString = string; - - if (!string.contains(httpRegex)) { - if (!string.contains(ftpRegex)) { - tempString.insert(0, "http://"); - } - } - QString domain = getDomain.match(tempString).captured(1); - string.replace(domain, domain.toLower()); - } - link = Link(Link::Url, linkString); - textColor = MessageColor(MessageColor::Link); - } - if (string.startsWith('@')) { - this->emplace(string, TextElement::BoldUsername, textColor, - FontStyle::ChatMediumBold) // - ->setLink(link); - this->emplace(string, TextElement::NonBoldUsername, textColor) // - ->setLink(link); - } else { - this->emplace(string, TextElement::Text, textColor) // - ->setLink(link); - } - - } else { // is emoji - this->emplace(emoteData, EmoteElement::EmojiAll); - } + for (auto &variant : getApp()->emotes->emojis.parse(word)) { + // if (std::holds_alternative(variant)) { + // this->addTextOrEmoji(std::get<1>(variant)); + // } else { + // // this->addTextOrEmoji(std::get(variant)); + // } + boost::apply_visitor(/*overloaded{[&](EmotePtr arg) { this->addTextOrEmoji(arg); }, + [&](const QString &arg) { this->addTextOrEmoji(arg); }}*/ + [&](auto &&arg) { this->addTextOrEmoji(arg); }, variant); } - for (int j = 0; j < split.size(); j++) { + for (int j = 0; j < word.size(); j++) { i++; - if (split.at(j).isHighSurrogate()) { + if (word.at(j).isHighSurrogate()) { j++; } } i++; } +} - this->message_->searchText = this->userName + ": " + this->originalMessage_; +void TwitchMessageBuilder::addTextOrEmoji(EmotePtr emote) +{ + this->emplace(emote, EmoteElement::EmojiAll); +} - return this->getMessage(); +void TwitchMessageBuilder::addTextOrEmoji(const QString &string_) +{ + auto string = QString(string_); + + if (this->hasBits_ && this->tryParseCheermote(string)) { + // This string was parsed as a cheermote + return; + } + + // TODO: Implement ignored emotes + // Format of ignored emotes: + // Emote name: "forsenPuke" - if string in ignoredEmotes + // Will match emote regardless of source (i.e. bttv, ffz) + // Emote source + name: "bttv:nyanPls" + if (this->tryAppendEmote({string})) { + // Successfully appended an emote + return; + } + + // Actually just text + auto linkString = this->matchLink(string); + auto link = Link(); + auto textColor = + this->action_ ? MessageColor(this->usernameColor_) : MessageColor(MessageColor::Text); + + if (!linkString.isEmpty()) { + if (getApp()->settings->lowercaseLink) { + QRegularExpression httpRegex("\\bhttps?://", QRegularExpression::CaseInsensitiveOption); + QRegularExpression ftpRegex("\\bftps?://", QRegularExpression::CaseInsensitiveOption); + QRegularExpression getDomain("\\/\\/([^\\/]*)"); + QString tempString = string; + + if (!string.contains(httpRegex)) { + if (!string.contains(ftpRegex)) { + tempString.insert(0, "http://"); + } + } + QString domain = getDomain.match(tempString).captured(1); + string.replace(domain, domain.toLower()); + } + link = Link(Link::Url, linkString); + textColor = MessageColor(MessageColor::Link); + } + if (string.startsWith('@')) { + this->emplace(string, TextElement::BoldUsername, textColor, + FontStyle::ChatMediumBold) // + ->setLink(link); + this->emplace(string, TextElement::NonBoldUsername, + textColor) // + ->setLink(link); + } else { + this->emplace(string, TextElement::Text, textColor) // + ->setLink(link); + } } void TwitchMessageBuilder::parseMessageID() @@ -301,8 +307,8 @@ void TwitchMessageBuilder::parseRoomID() void TwitchMessageBuilder::appendChannelName() { - QString channelName("#" + this->channel->name); - Link link(Link::Url, this->channel->name + "\n" + this->messageID); + QString channelName("#" + this->channel->getName()); + Link link(Link::Url, this->channel->getName() + "\n" + this->messageID); this->emplace(channelName, MessageElement::ChannelName, MessageColor::System) // ->setLink(link); @@ -531,71 +537,64 @@ void TwitchMessageBuilder::parseHighlights(bool isPastMsg) void TwitchMessageBuilder::appendTwitchEmote(const Communi::IrcMessage *ircMessage, const QString &emote, - std::vector> &vec) + std::vector> &vec) { auto app = getApp(); if (!emote.contains(':')) { return; } - QStringList parameters = emote.split(':'); + auto parameters = emote.split(':'); if (parameters.length() < 2) { return; } - const auto &id = parameters.at(0); + auto id = EmoteId{parameters.at(0)}; - QStringList occurences = parameters.at(1).split(','); + auto occurences = parameters.at(1).split(','); for (QString occurence : occurences) { - QStringList coords = occurence.split('-'); + auto coords = occurence.split('-'); if (coords.length() < 2) { return; } - int start = coords.at(0).toInt(); - int end = coords.at(1).toInt(); + auto start = coords.at(0).toInt(); + auto end = coords.at(1).toInt(); if (start >= end || start < 0 || end > this->originalMessage_.length()) { return; } - QString name = this->originalMessage_.mid(start, end - start + 1); + auto name = EmoteName{this->originalMessage_.mid(start, end - start + 1)}; - vec.push_back( - std::pair(start, app->emotes->twitch.getEmoteById(id, name))); + vec.push_back(std::make_pair(start, app->emotes->twitch.getOrCreateEmote(id, name))); } } -bool TwitchMessageBuilder::tryAppendEmote(QString &emoteString) +Outcome TwitchMessageBuilder::tryAppendEmote(const EmoteName &name) { - auto app = getApp(); - EmoteData emoteData; + auto flags = MessageElement::Flags::None; + auto emote = boost::optional{}; - auto appendEmote = [&](MessageElement::Flags flags) { - this->emplace(emoteData, flags); - return true; - }; - - if (app->emotes->bttv.globalEmotes.tryGet(emoteString, emoteData)) { - // BTTV Global Emote - return appendEmote(MessageElement::BttvEmote); - } else if (this->twitchChannel != nullptr && - this->twitchChannel->getBttvEmotes().tryGet(emoteString, emoteData)) { - // BTTV Channel Emote - return appendEmote(MessageElement::BttvEmote); - } else if (app->emotes->ffz.globalEmotes.tryGet(emoteString, emoteData)) { - // FFZ Global Emote - return appendEmote(MessageElement::FfzEmote); - } else if (this->twitchChannel != nullptr && - this->twitchChannel->getFfzEmotes().tryGet(emoteString, emoteData)) { - // FFZ Channel Emote - return appendEmote(MessageElement::FfzEmote); + if ((emote = getApp()->emotes->bttv.getGlobalEmote(name))) { + flags = MessageElement::BttvEmote; + } else if (twitchChannel && (emote = this->twitchChannel->getBttvEmote(name))) { + flags = MessageElement::BttvEmote; + } else if ((emote = getApp()->emotes->ffz.getGlobalEmote(name))) { + flags = MessageElement::FfzEmote; + } else if (twitchChannel && (emote = this->twitchChannel->getFfzEmote(name))) { + flags = MessageElement::FfzEmote; } - return false; + if (emote) { + this->emplace(emote.get(), flags); + return Success; + } + + return Failure; } // fourtf: this is ugly @@ -604,8 +603,6 @@ void TwitchMessageBuilder::appendTwitchBadges() { auto app = getApp(); - const auto &channelResources = app->resources->channels[this->roomID_]; - auto iterator = this->tags.find("badges"); if (iterator == this->tags.end()) { @@ -621,68 +618,75 @@ void TwitchMessageBuilder::appendTwitchBadges() } if (badge.startsWith("bits/")) { - if (!app->resources->dynamicBadgesLoaded) { - // Do nothing - continue; - } + // if (!app->resources->dynamicBadgesLoaded) { + // // Do nothing + // continue; + // } - QString cheerAmountQS = badge.mid(5); - std::string versionKey = cheerAmountQS.toStdString(); - QString tooltip = QString("Twitch cheer ") + cheerAmountQS; + QString cheerAmount = badge.mid(5); + QString tooltip = QString("Twitch cheer ") + cheerAmount; // Try to fetch channel-specific bit badge try { - const auto &badge = channelResources.badgeSets.at("bits").versions.at(versionKey); - this->emplace(badge.badgeImage1x, MessageElement::BadgeVanity) - ->setTooltip(tooltip); - continue; + if (twitchChannel) + if (const auto &badge = + this->twitchChannel->getTwitchBadge("bits", cheerAmount)) { + this->emplace(badge.get(), MessageElement::BadgeVanity) + ->setTooltip(tooltip); + continue; + } } catch (const std::out_of_range &) { // Channel does not contain a special bit badge for this version } // Use default bit badge - try { - const auto &badge = app->resources->badgeSets.at("bits").versions.at(versionKey); - this->emplace(badge.badgeImage1x, MessageElement::BadgeVanity) - ->setTooltip(tooltip); - } catch (const std::out_of_range &) { - Log("No default bit badge for version {} found", versionKey); - continue; - } + // try { + // const auto &badge = app->resources->badgeSets.at("bits").versions.at(cheerAmount); + // this->emplace(badge.badgeImage1x, MessageElement::BadgeVanity) + // ->setTooltip(tooltip); + //} catch (const std::out_of_range &) { + // Log("No default bit badge for version {} found", cheerAmount); + // continue; + //} } else if (badge == "staff/1") { - this->emplace(app->resources->badgeStaff, + this->emplace(Image::fromNonOwningPixmap(&app->resources->twitch.staff), MessageElement::BadgeGlobalAuthority) ->setTooltip("Twitch Staff"); } else if (badge == "admin/1") { - this->emplace(app->resources->badgeAdmin, + this->emplace(Image::fromNonOwningPixmap(&app->resources->twitch.admin), MessageElement::BadgeGlobalAuthority) ->setTooltip("Twitch Admin"); } else if (badge == "global_mod/1") { - this->emplace(app->resources->badgeGlobalModerator, - MessageElement::BadgeGlobalAuthority) + this->emplace( + Image::fromNonOwningPixmap(&app->resources->twitch.globalmod), + MessageElement::BadgeGlobalAuthority) ->setTooltip("Twitch Global Moderator"); } else if (badge == "moderator/1") { // TODO: Implement custom FFZ moderator badge - this->emplace(app->resources->badgeModerator, - MessageElement::BadgeChannelAuthority) + this->emplace( + Image::fromNonOwningPixmap(&app->resources->twitch.moderator), + MessageElement::BadgeChannelAuthority) ->setTooltip("Twitch Channel Moderator"); } else if (badge == "turbo/1") { - this->emplace(app->resources->badgeTurbo, + this->emplace(Image::fromNonOwningPixmap(&app->resources->twitch.turbo), MessageElement::BadgeGlobalAuthority) ->setTooltip("Twitch Turbo Subscriber"); } else if (badge == "broadcaster/1") { - this->emplace(app->resources->badgeBroadcaster, - MessageElement::BadgeChannelAuthority) + this->emplace( + Image::fromNonOwningPixmap(&app->resources->twitch.broadcaster), + MessageElement::BadgeChannelAuthority) ->setTooltip("Twitch Broadcaster"); } else if (badge == "premium/1") { - this->emplace(app->resources->badgePremium, MessageElement::BadgeVanity) + this->emplace(Image::fromNonOwningPixmap(&app->resources->twitch.prime), + MessageElement::BadgeVanity) ->setTooltip("Twitch Prime Subscriber"); } else if (badge.startsWith("partner/")) { int index = badge.midRef(8).toInt(); switch (index) { case 1: { - this->emplace(app->resources->badgeVerified, - MessageElement::BadgeVanity) + this->emplace( + Image::fromNonOwningPixmap(&app->resources->twitch.verified), + MessageElement::BadgeVanity) ->setTooltip("Twitch Verified"); } break; default: { @@ -690,140 +694,142 @@ void TwitchMessageBuilder::appendTwitchBadges() } break; } } else if (badge.startsWith("subscriber/")) { - if (channelResources.loaded == false) { - // qDebug() << "Channel resources are not loaded, can't add the subscriber badge"; - continue; - } + // if (channelResources.loaded == false) { + // // qDebug() << "Channel resources are not loaded, can't add the + // subscriber + // // badge"; + // continue; + // } - auto badgeSetIt = channelResources.badgeSets.find("subscriber"); - if (badgeSetIt == channelResources.badgeSets.end()) { - // Fall back to default badge - this->emplace(app->resources->badgeSubscriber, - MessageElement::BadgeSubscription) - ->setTooltip("Twitch Subscriber"); - continue; - } + // auto badgeSetIt = channelResources.badgeSets.find("subscriber"); + // if (badgeSetIt == channelResources.badgeSets.end()) { + // // Fall back to default badge + // this->emplace(app->resources->badgeSubscriber, + // MessageElement::BadgeSubscription) + // ->setTooltip("Twitch Subscriber"); + // continue; + //} - const auto &badgeSet = badgeSetIt->second; + // const auto &badgeSet = badgeSetIt->second; - std::string versionKey = badge.mid(11).toStdString(); + // std::string versionKey = badge.mid(11).toStdString(); - auto badgeVersionIt = badgeSet.versions.find(versionKey); + // auto badgeVersionIt = badgeSet.versions.find(versionKey); - if (badgeVersionIt == badgeSet.versions.end()) { - // Fall back to default badge - this->emplace(app->resources->badgeSubscriber, - MessageElement::BadgeSubscription) - ->setTooltip("Twitch Subscriber"); - continue; - } + // if (badgeVersionIt == badgeSet.versions.end()) { + // // Fall back to default badge + // this->emplace(app->resources->badgeSubscriber, + // MessageElement::BadgeSubscription) + // ->setTooltip("Twitch Subscriber"); + // continue; + //} - auto &badgeVersion = badgeVersionIt->second; + // auto &badgeVersion = badgeVersionIt->second; - this->emplace(badgeVersion.badgeImage1x, - MessageElement::BadgeSubscription) - ->setTooltip("Twitch " + QString::fromStdString(badgeVersion.title)); + // this->emplace(badgeVersion.badgeImage1x, + // MessageElement::BadgeSubscription) + // ->setTooltip("Twitch " + QString::fromStdString(badgeVersion.title)); } else { - if (!app->resources->dynamicBadgesLoaded) { - // Do nothing - continue; - } + // if (!app->resources->dynamicBadgesLoaded) { + // // Do nothing + // continue; + //} - QStringList parts = badge.split('/'); + // QStringList parts = badge.split('/'); - if (parts.length() != 2) { - qDebug() << "Bad number of parts: " << parts.length() << " in " << parts; - continue; - } + // if (parts.length() != 2) { + // qDebug() << "Bad number of parts: " << parts.length() << " in " << parts; + // continue; + //} - MessageElement::Flags badgeType = MessageElement::Flags::BadgeVanity; + // MessageElement::Flags badgeType = MessageElement::Flags::BadgeVanity; - std::string badgeSetKey = parts[0].toStdString(); - std::string versionKey = parts[1].toStdString(); + // std::string badgeSetKey = parts[0].toStdString(); + // std::string versionKey = parts[1].toStdString(); - try { - auto &badgeSet = app->resources->badgeSets.at(badgeSetKey); + // try { + // auto &badgeSet = app->resources->badgeSets.at(badgeSetKey); - try { - auto &badgeVersion = badgeSet.versions.at(versionKey); + // try { + // auto &badgeVersion = badgeSet.versions.at(versionKey); - this->emplace(badgeVersion.badgeImage1x, badgeType) - ->setTooltip("Twitch " + QString::fromStdString(badgeVersion.title)); - } catch (const std::exception &e) { - qDebug() << "Exception caught:" << e.what() - << "when trying to fetch badge version " << versionKey.c_str(); - } - } catch (const std::exception &e) { - qDebug() << "No badge set with key" << badgeSetKey.c_str() - << ". Exception: " << e.what(); - } + // this->emplace(badgeVersion.badgeImage1x, badgeType) + // ->setTooltip("Twitch " + QString::fromStdString(badgeVersion.title)); + // } catch (const std::exception &e) { + // qDebug() << "Exception caught:" << e.what() + // << "when trying to fetch badge version " << versionKey.c_str(); + // } + //} catch (const std::exception &e) { + // qDebug() << "No badge set with key" << badgeSetKey.c_str() + // << ". Exception: " << e.what(); + //} } } } void TwitchMessageBuilder::appendChatterinoBadges() { - auto app = getApp(); + // auto app = getApp(); - auto &badges = app->resources->chatterinoBadges; - auto it = badges.find(this->userName.toStdString()); + // auto &badges = app->resources->chatterinoBadges; + // auto it = badges.find(this->userName.toStdString()); - if (it == badges.end()) { - return; - } + // if (it == badges.end()) { + // return; + // } - const auto badge = it->second; + // const auto badge = it->second; - this->emplace(badge->image, MessageElement::BadgeChatterino) - ->setTooltip(QString::fromStdString(badge->tooltip)); + // this->emplace(badge->image, MessageElement::BadgeChatterino) + // ->setTooltip(QString::fromStdString(badge->tooltip)); } -bool TwitchMessageBuilder::tryParseCheermote(const QString &string) +Outcome TwitchMessageBuilder::tryParseCheermote(const QString &string) { - auto app = getApp(); - // Try to parse custom cheermotes - const auto &channelResources = app->resources->channels[this->roomID_]; - if (channelResources.loaded) { - for (const auto &cheermoteSet : channelResources.cheermoteSets) { - auto match = cheermoteSet.regex.match(string); - if (!match.hasMatch()) { - continue; - } - QString amount = match.captured(1); - bool ok = false; - int numBits = amount.toInt(&ok); - if (!ok) { - Log("Error parsing bit amount in tryParseCheermote"); - return false; - } + // auto app = getApp(); + //// Try to parse custom cheermotes + // const auto &channelResources = app->resources->channels[this->roomID_]; + // if (channelResources.loaded) { + // for (const auto &cheermoteSet : channelResources.cheermoteSets) { + // auto match = cheermoteSet.regex.match(string); + // if (!match.hasMatch()) { + // continue; + // } + // QString amount = match.captured(1); + // bool ok = false; + // int numBits = amount.toInt(&ok); + // if (!ok) { + // Log("Error parsing bit amount in tryParseCheermote"); + // return Failure; + // } - auto savedIt = cheermoteSet.cheermotes.end(); + // auto savedIt = cheermoteSet.cheermotes.end(); - // Fetch cheermote that matches our numBits - for (auto it = cheermoteSet.cheermotes.begin(); it != cheermoteSet.cheermotes.end(); - ++it) { - if (numBits >= it->minBits) { - savedIt = it; - } else { - break; - } - } + // // Fetch cheermote that matches our numBits + // for (auto it = cheermoteSet.cheermotes.begin(); it != cheermoteSet.cheermotes.end(); + // ++it) { + // if (numBits >= it->minBits) { + // savedIt = it; + // } else { + // break; + // } + // } - if (savedIt == cheermoteSet.cheermotes.end()) { - Log("Error getting a cheermote from a cheermote set for the bit amount {}", - numBits); - return false; - } + // if (savedIt == cheermoteSet.cheermotes.end()) { + // Log("Error getting a cheermote from a cheermote set for the bit amount {}", + // numBits); + // return Failure; + // } - const auto &cheermote = *savedIt; + // const auto &cheermote = *savedIt; - this->emplace(cheermote.emoteDataAnimated, EmoteElement::BitsAnimated); - this->emplace(amount, EmoteElement::Text, cheermote.color); + // this->emplace(cheermote.animatedEmote, EmoteElement::BitsAnimated); + // this->emplace(amount, EmoteElement::Text, cheermote.color); - return true; - } - } + // return Success; + // } + //} - return false; + return Failure; } } // namespace chatterino diff --git a/src/providers/twitch/TwitchMessageBuilder.hpp b/src/providers/twitch/TwitchMessageBuilder.hpp index b27ce8a0a..1bf538f35 100644 --- a/src/providers/twitch/TwitchMessageBuilder.hpp +++ b/src/providers/twitch/TwitchMessageBuilder.hpp @@ -51,14 +51,20 @@ private: void parseHighlights(bool isPastMsg); void appendTwitchEmote(const Communi::IrcMessage *ircMessage, const QString &emote, - std::vector> &vec); - bool tryAppendEmote(QString &emoteString); + std::vector> &vec); + Outcome tryAppendEmote(const EmoteName &name); + + void addWords(const QStringList &words, + const std::vector> &twitchEmotes); + void addTextOrEmoji(EmotePtr emote); + void addTextOrEmoji(const QString &value); void appendTwitchBadges(); void appendChatterinoBadges(); - bool tryParseCheermote(const QString &string); + Outcome tryParseCheermote(const QString &string); QString roomID_; + bool hasBits_ = false; QColor usernameColor_; const QString originalMessage_; diff --git a/src/providers/twitch/TwitchParseCheerEmotes.cpp b/src/providers/twitch/TwitchParseCheerEmotes.cpp new file mode 100644 index 000000000..a7193d2f6 --- /dev/null +++ b/src/providers/twitch/TwitchParseCheerEmotes.cpp @@ -0,0 +1,264 @@ +#include "TwitchParseCheerEmotes.hpp" + +#include +#include +#include + +namespace chatterino { + +namespace { + +template +inline bool ReadValue(const rapidjson::Value &object, const char *key, Type &out) +{ + if (!object.HasMember(key)) { + return false; + } + + const auto &value = object[key]; + + if (!value.Is()) { + return false; + } + + out = value.Get(); + + return true; +} + +template <> +inline bool ReadValue(const rapidjson::Value &object, const char *key, QString &out) +{ + if (!object.HasMember(key)) { + return false; + } + + const auto &value = object[key]; + + if (!value.IsString()) { + return false; + } + + out = value.GetString(); + + return true; +} + +template <> +inline bool ReadValue>(const rapidjson::Value &object, const char *key, + std::vector &out) +{ + if (!object.HasMember(key)) { + return false; + } + + const auto &value = object[key]; + + if (!value.IsArray()) { + return false; + } + + for (const rapidjson::Value &innerValue : value.GetArray()) { + if (!innerValue.IsString()) { + return false; + } + + out.emplace_back(innerValue.GetString()); + } + + return true; +} + +// Parse a single cheermote set (or "action") from the twitch api +inline bool ParseSingleCheermoteSet(JSONCheermoteSet &set, const rapidjson::Value &action) +{ + if (!action.IsObject()) { + return false; + } + + if (!ReadValue(action, "prefix", set.prefix)) { + return false; + } + + if (!ReadValue(action, "scales", set.scales)) { + return false; + } + + if (!ReadValue(action, "backgrounds", set.backgrounds)) { + return false; + } + + if (!ReadValue(action, "states", set.states)) { + return false; + } + + if (!ReadValue(action, "type", set.type)) { + return false; + } + + if (!ReadValue(action, "updated_at", set.updatedAt)) { + return false; + } + + if (!ReadValue(action, "priority", set.priority)) { + return false; + } + + // Tiers + if (!action.HasMember("tiers")) { + return false; + } + + const auto &tiersValue = action["tiers"]; + + if (!tiersValue.IsArray()) { + return false; + } + + for (const rapidjson::Value &tierValue : tiersValue.GetArray()) { + JSONCheermoteSet::CheermoteTier tier; + + if (!tierValue.IsObject()) { + return false; + } + + if (!ReadValue(tierValue, "min_bits", tier.minBits)) { + return false; + } + + if (!ReadValue(tierValue, "id", tier.id)) { + return false; + } + + if (!ReadValue(tierValue, "color", tier.color)) { + return false; + } + + // Images + if (!tierValue.HasMember("images")) { + return false; + } + + const auto &imagesValue = tierValue["images"]; + + if (!imagesValue.IsObject()) { + return false; + } + + // Read images object + for (const auto &imageBackgroundValue : imagesValue.GetObject()) { + QString background = imageBackgroundValue.name.GetString(); + bool backgroundExists = false; + for (const auto &bg : set.backgrounds) { + if (background == bg) { + backgroundExists = true; + break; + } + } + + if (!backgroundExists) { + continue; + } + + const rapidjson::Value &imageBackgroundStates = imageBackgroundValue.value; + if (!imageBackgroundStates.IsObject()) { + continue; + } + + // Read each key which represents a background + for (const auto &imageBackgroundState : imageBackgroundStates.GetObject()) { + QString state = imageBackgroundState.name.GetString(); + bool stateExists = false; + for (const auto &_state : set.states) { + if (state == _state) { + stateExists = true; + break; + } + } + + if (!stateExists) { + continue; + } + + const rapidjson::Value &imageScalesValue = imageBackgroundState.value; + if (!imageScalesValue.IsObject()) { + continue; + } + + // Read each key which represents a scale + for (const auto &imageScaleValue : imageScalesValue.GetObject()) { + QString scale = imageScaleValue.name.GetString(); + bool scaleExists = false; + for (const auto &_scale : set.scales) { + if (scale == _scale) { + scaleExists = true; + break; + } + } + + if (!scaleExists) { + continue; + } + + const rapidjson::Value &imageScaleURLValue = imageScaleValue.value; + if (!imageScaleURLValue.IsString()) { + continue; + } + + QString url = imageScaleURLValue.GetString(); + + bool ok = false; + qreal scaleNumber = scale.toFloat(&ok); + if (!ok) { + continue; + } + + qreal chatterinoScale = 1 / scaleNumber; + + auto image = Image::fromUrl({url}, chatterinoScale); + + // TODO(pajlada): Fill in name and tooltip + tier.images[background][state][scale] = image; + } + } + } + + set.tiers.emplace_back(tier); + } + + return true; +} +} // namespace + +// Look through the results of https://api.twitch.tv/kraken/bits/actions?channel_id=11148817 for +// cheermote sets or "Actions" as they are called in the API +std::vector ParseCheermoteSets(const rapidjson::Document &d) +{ + std::vector sets; + + if (!d.IsObject()) { + return sets; + } + + if (!d.HasMember("actions")) { + return sets; + } + + const auto &actionsValue = d["actions"]; + + if (!actionsValue.IsArray()) { + return sets; + } + + for (const auto &action : actionsValue.GetArray()) { + JSONCheermoteSet set; + bool res = ParseSingleCheermoteSet(set, action); + + if (res) { + sets.emplace_back(set); + } + } + + return sets; +} +} // namespace chatterino diff --git a/src/providers/twitch/TwitchParseCheerEmotes.hpp b/src/providers/twitch/TwitchParseCheerEmotes.hpp new file mode 100644 index 000000000..150c8b447 --- /dev/null +++ b/src/providers/twitch/TwitchParseCheerEmotes.hpp @@ -0,0 +1,36 @@ +#pragma once + +#include +#include +#include +#include +#include "messages/Image.hpp" + +namespace chatterino { + +struct JSONCheermoteSet { + QString prefix; + std::vector scales; + + std::vector backgrounds; + std::vector states; + + QString type; + QString updatedAt; + int priority; + + struct CheermoteTier { + int minBits; + QString id; + QString color; + + // Background State Scale + std::map>> images; + }; + + std::vector tiers; +}; + +std::vector ParseCheermoteSets(const rapidjson::Document &d); + +} // namespace chatterino diff --git a/src/providers/twitch/TwitchServer.cpp b/src/providers/twitch/TwitchServer.cpp index 8649a955f..14542afcf 100644 --- a/src/providers/twitch/TwitchServer.cpp +++ b/src/providers/twitch/TwitchServer.cpp @@ -29,22 +29,18 @@ TwitchServer::TwitchServer() this->pubsub = new PubSub; - getSettings()->twitchSeperateWriteConnection.connect([this](auto, auto) { this->connect(); }, - this->signalHolder_, false); + // getSettings()->twitchSeperateWriteConnection.connect([this](auto, auto) { this->connect(); }, + // this->signalHolder_, false); } -void TwitchServer::initialize(Application &app) +void TwitchServer::initialize(Settings &settings, Paths &paths) { - this->app = &app; - - app.accounts->twitch.currentUserChanged.connect( + getApp()->accounts->twitch.currentUserChanged.connect( [this]() { postToThread([this] { this->connect(); }); }); } void TwitchServer::initializeConnection(IrcConnection *connection, bool isRead, bool isWrite) { - assert(this->app); - this->singleConnection_ = isRead == isWrite; std::shared_ptr account = getApp()->accounts->twitch.getCurrent(); @@ -236,7 +232,7 @@ void TwitchServer::onMessageSendRequested(TwitchChannel *channel, const QString lastMessage.push(now); } - this->sendMessage(channel->name, message); + this->sendMessage(channel->getName(), message); sent = true; } diff --git a/src/providers/twitch/TwitchServer.hpp b/src/providers/twitch/TwitchServer.hpp index 97f0859fe..769f65fd4 100644 --- a/src/providers/twitch/TwitchServer.hpp +++ b/src/providers/twitch/TwitchServer.hpp @@ -12,15 +12,18 @@ namespace chatterino { +class Settings; +class Paths; + class PubSub; -class TwitchServer : public AbstractIrcServer, public Singleton +class TwitchServer final : public AbstractIrcServer, public Singleton { public: TwitchServer(); virtual ~TwitchServer() override = default; - virtual void initialize(Application &app) override; + virtual void initialize(Settings &settings, Paths &paths) override; void forEachChannelAndSpecialChannels(std::function func); @@ -51,8 +54,6 @@ protected: private: void onMessageSendRequested(TwitchChannel *channel, const QString &message, bool &sent); - Application *app = nullptr; - std::mutex lastMessageMutex_; std::queue lastMessagePleb_; std::queue lastMessageMod_; diff --git a/src/singletons/Badges.cpp b/src/singletons/Badges.cpp new file mode 100644 index 000000000..da38def4f --- /dev/null +++ b/src/singletons/Badges.cpp @@ -0,0 +1,9 @@ +#include "Badges.hpp" + +namespace chatterino { + +Badges::Badges() +{ +} + +} // namespace chatterino diff --git a/src/singletons/Badges.hpp b/src/singletons/Badges.hpp new file mode 100644 index 000000000..d049529eb --- /dev/null +++ b/src/singletons/Badges.hpp @@ -0,0 +1,15 @@ +#pragma once + +#include +#include "common/Singleton.hpp" +#include "messages/Emote.hpp" + +namespace chatterino { + +class Badges : public Singleton +{ +public: + Badges(); +}; + +} // namespace chatterino diff --git a/src/singletons/Emotes.cpp b/src/singletons/Emotes.cpp index ca4e91677..0be870a5e 100644 --- a/src/singletons/Emotes.cpp +++ b/src/singletons/Emotes.cpp @@ -5,15 +5,14 @@ namespace chatterino { -void Emotes::initialize(Application &app) +Emotes::Emotes() { - const auto refreshTwitchEmotes = [this, &app] { - auto currentUser = app.accounts->twitch.getCurrent(); - assert(currentUser); - this->twitch.refresh(currentUser); - }; - app.accounts->twitch.currentUserChanged.connect(refreshTwitchEmotes); - refreshTwitchEmotes(); +} + +void Emotes::initialize(Settings &settings, Paths &paths) +{ + getApp()->accounts->twitch.currentUserChanged.connect( + [] { getApp()->accounts->twitch.getCurrent()->loadEmotes(); }); this->emojis.load(); this->bttv.loadGlobalEmotes(); diff --git a/src/singletons/Emotes.hpp b/src/singletons/Emotes.hpp index 279992435..e991fe674 100644 --- a/src/singletons/Emotes.hpp +++ b/src/singletons/Emotes.hpp @@ -12,16 +12,21 @@ namespace chatterino { +class Settings; +class Paths; + class Emotes final : public Singleton { public: - virtual void initialize(Application &app) override; + Emotes(); + + virtual void initialize(Settings &settings, Paths &paths) override; bool isIgnoredEmote(const QString &emote); TwitchEmotes twitch; - BTTVEmotes bttv; - FFZEmotes ffz; + BttvEmotes bttv; + FfzEmotes ffz; Emojis emojis; GIFTimer gifTimer; diff --git a/src/singletons/Fonts.cpp b/src/singletons/Fonts.cpp index 70aaa93b6..eb4af2877 100644 --- a/src/singletons/Fonts.cpp +++ b/src/singletons/Fonts.cpp @@ -29,28 +29,20 @@ Fonts::Fonts() this->fontsByType_.resize(size_t(EndType)); } -void Fonts::initialize(Application &app) +void Fonts::initialize(Settings &, Paths &) { - this->chatFontFamily.connect([this, &app](const std::string &, auto) { + this->chatFontFamily.connect([this](const std::string &, auto) { assertInGuiThread(); - if (app.windows) { - app.windows->incGeneration(); - } - for (auto &map : this->fontsByType_) { map.clear(); } this->fontChanged.invoke(); }); - this->chatFontSize.connect([this, &app](const int &, auto) { + this->chatFontSize.connect([this](const int &, auto) { assertInGuiThread(); - if (app.windows) { - app.windows->incGeneration(); - } - for (auto &map : this->fontsByType_) { map.clear(); } diff --git a/src/singletons/Fonts.hpp b/src/singletons/Fonts.hpp index 5a7192292..59c6550c3 100644 --- a/src/singletons/Fonts.hpp +++ b/src/singletons/Fonts.hpp @@ -13,12 +13,15 @@ namespace chatterino { +class Settings; +class Paths; + class Fonts final : public Singleton { public: Fonts(); - virtual void initialize(Application &app) override; + virtual void initialize(Settings &settings, Paths &paths) override; // font data gets set in createFontData(...) enum Type : uint8_t { diff --git a/src/singletons/Logging.cpp b/src/singletons/Logging.cpp index 711e64a9e..a8a6495c7 100644 --- a/src/singletons/Logging.cpp +++ b/src/singletons/Logging.cpp @@ -12,7 +12,7 @@ namespace chatterino { -void Logging::initialize(Application &app) +void Logging::initialize(Settings &settings, Paths &paths) { } diff --git a/src/singletons/Logging.hpp b/src/singletons/Logging.hpp index f23d8c0e6..98c6c8353 100644 --- a/src/singletons/Logging.hpp +++ b/src/singletons/Logging.hpp @@ -18,7 +18,7 @@ class Logging : public Singleton public: Logging() = default; - virtual void initialize(Application &app) override; + virtual void initialize(Settings &settings, Paths &paths) override; void addMessage(const QString &channelName, MessagePtr message); diff --git a/src/singletons/NativeMessaging.cpp b/src/singletons/NativeMessaging.cpp index 805f0f384..3bf5b0ff3 100644 --- a/src/singletons/NativeMessaging.cpp +++ b/src/singletons/NativeMessaging.cpp @@ -31,29 +31,12 @@ namespace ipc = boost::interprocess; namespace chatterino { -// fourtf: don't add this class to the application class -NativeMessaging::NativeMessaging() -{ - qDebug() << "init NativeMessagingManager"; -} +void registerNmManifest(Paths &paths, const QString &manifestFilename, + const QString ®istryKeyName, const QJsonDocument &document); -void NativeMessaging::writeByteArray(QByteArray a) +void registerNmHost(Paths &paths) { - char *data = a.data(); - uint32_t size; - size = a.size(); - std::cout.write(reinterpret_cast(&size), 4); - std::cout.write(data, a.size()); - std::cout.flush(); -} - -void NativeMessaging::registerHost() -{ - auto app = getApp(); - - if (app->paths->isPortable()) { - return; - } + if (paths.isPortable()) return; auto getBaseDocument = [&] { QJsonObject obj; @@ -65,22 +48,6 @@ void NativeMessaging::registerHost() return obj; }; - auto registerManifest = [&](const QString &manifestFilename, const QString ®istryKeyName, - const QJsonDocument &document) { - // save the manifest - QString manifestPath = app->paths->miscDirectory + manifestFilename; - QFile file(manifestPath); - file.open(QIODevice::WriteOnly | QIODevice::Truncate); - file.write(document.toJson()); - file.flush(); - -#ifdef Q_OS_WIN - // clang-format off - QProcess::execute("REG ADD \"" + registryKeyName + "\" /ve /t REG_SZ /d \"" + manifestPath + "\" /f"); -// clang-format on -#endif - }; - // chrome { QJsonDocument document; @@ -90,8 +57,8 @@ void NativeMessaging::registerHost() obj.insert("allowed_origins", allowed_origins_arr); document.setObject(obj); - registerManifest( - "/native-messaging-manifest-chrome.json", + registerNmManifest( + paths, "/native-messaging-manifest-chrome.json", "HKCU\\Software\\Google\\Chrome\\NativeMessagingHosts\\com.chatterino.chatterino", document); } @@ -105,24 +72,40 @@ void NativeMessaging::registerHost() obj.insert("allowed_extensions", allowed_extensions); document.setObject(obj); - registerManifest("/native-messaging-manifest-firefox.json", - "HKCU\\Software\\Mozilla\\NativeMessagingHosts\\com.chatterino.chatterino", - document); + registerNmManifest( + paths, "/native-messaging-manifest-firefox.json", + "HKCU\\Software\\Mozilla\\NativeMessagingHosts\\com.chatterino.chatterino", document); } } -void NativeMessaging::openGuiMessageQueue() +void registerNmManifest(Paths &paths, const QString &manifestFilename, + const QString ®istryKeyName, const QJsonDocument &document) { - static ReceiverThread thread; + (void)registryKeyName; - if (thread.isRunning()) { - thread.exit(); - } + // save the manifest + QString manifestPath = paths.miscDirectory + manifestFilename; + QFile file(manifestPath); + file.open(QIODevice::WriteOnly | QIODevice::Truncate); + file.write(document.toJson()); + file.flush(); - thread.start(); +#ifdef Q_OS_WIN + // clang-format off + QProcess::execute("REG ADD \"" + registryKeyName + "\" /ve /t REG_SZ /d \"" + manifestPath + "\" /f"); +// clang-format on +#endif } -void NativeMessaging::sendToGuiProcess(const QByteArray &array) +std::string &getNmQueueName(Paths &paths) +{ + static std::string name = "chatterino_gui" + paths.applicationFilePathHash.toStdString(); + return name; +} + +// CLIENT + +void NativeMessagingClient::sendMessage(const QByteArray &array) { try { ipc::message_queue messageQueue(ipc::open_only, "chatterino_gui"); @@ -133,7 +116,24 @@ void NativeMessaging::sendToGuiProcess(const QByteArray &array) } } -void NativeMessaging::ReceiverThread::run() +void NativeMessagingClient::writeToCout(const QByteArray &array) +{ + auto *data = array.data(); + auto size = uint32_t(array.size()); + + std::cout.write(reinterpret_cast(&size), 4); + std::cout.write(data, size); + std::cout.flush(); +} + +// SERVER + +void NativeMessagingServer::start() +{ + this->thread.start(); +} + +void NativeMessagingServer::ReceiverThread::run() { ipc::message_queue::remove("chatterino_gui"); @@ -157,7 +157,7 @@ void NativeMessaging::ReceiverThread::run() } } -void NativeMessaging::ReceiverThread::handleMessage(const QJsonObject &root) +void NativeMessagingServer::ReceiverThread::handleMessage(const QJsonObject &root) { auto app = getApp(); @@ -231,11 +231,4 @@ void NativeMessaging::ReceiverThread::handleMessage(const QJsonObject &root) } } -std::string &NativeMessaging::getGuiMessageQueueName() -{ - static std::string name = - "chatterino_gui" + Paths::getInstance()->applicationFilePathHash.toStdString(); - return name; -} - } // namespace chatterino diff --git a/src/singletons/NativeMessaging.hpp b/src/singletons/NativeMessaging.hpp index b9fa7ce6b..79c90ebd2 100644 --- a/src/singletons/NativeMessaging.hpp +++ b/src/singletons/NativeMessaging.hpp @@ -1,16 +1,28 @@ #pragma once -#include "common/Singleton.hpp" - #include +class Application; +class Paths; + namespace chatterino { -class NativeMessaging final + +void registerNmHost(Application &app); +std::string &getNmQueueName(Paths &paths); + +class NativeMessagingClient final { public: - // fourtf: don't add this class to the application class - NativeMessaging(); + void sendMessage(const QByteArray &array); + void writeToCout(const QByteArray &array); +}; +class NativeMessagingServer final +{ +public: + void start(); + +private: class ReceiverThread : public QThread { public: @@ -20,12 +32,7 @@ public: void handleMessage(const QJsonObject &root); }; - void writeByteArray(QByteArray a); - void registerHost(); - void openGuiMessageQueue(); - void sendToGuiProcess(const QByteArray &array); - - static std::string &getGuiMessageQueueName(); + ReceiverThread thread; }; } // namespace chatterino diff --git a/src/singletons/Paths.cpp b/src/singletons/Paths.cpp index c695e0468..40ce32524 100644 --- a/src/singletons/Paths.cpp +++ b/src/singletons/Paths.cpp @@ -14,6 +14,8 @@ Paths *Paths::instance = nullptr; Paths::Paths() { + this->instance = this; + this->initAppFilePathHash(); this->initCheckPortable(); @@ -21,20 +23,6 @@ Paths::Paths() this->initSubDirectories(); } -void Paths::initInstance() -{ - assert(!instance); - - instance = new Paths(); -} - -Paths *Paths::getInstance() -{ - assert(instance); - - return instance; -} - bool Paths::createFolder(const QString &folderPath) { return QDir().mkpath(folderPath); @@ -116,7 +104,7 @@ void Paths::initSubDirectories() Paths *getPaths() { - return Paths::getInstance(); + return Paths::instance; } } // namespace chatterino diff --git a/src/singletons/Paths.hpp b/src/singletons/Paths.hpp index e1df46767..b1b59da61 100644 --- a/src/singletons/Paths.hpp +++ b/src/singletons/Paths.hpp @@ -7,11 +7,10 @@ namespace chatterino { class Paths { - Paths(); - public: - static void initInstance(); - static Paths *getInstance(); + static Paths *instance; + + Paths(); // Root directory for the configuration files. %APPDATA%/chatterino or ExecutablePath for // portable mode @@ -41,10 +40,9 @@ private: void initAppDataDirectory(); void initSubDirectories(); - static Paths *instance; boost::optional portable_; }; -Paths *getPaths(); +[[deprecated]] Paths *getPaths(); } // namespace chatterino diff --git a/src/singletons/Resources.cpp b/src/singletons/Resources.cpp index 4b05b718b..daec74a5f 100644 --- a/src/singletons/Resources.cpp +++ b/src/singletons/Resources.cpp @@ -1,481 +1 @@ #include "singletons/Resources.hpp" - -#include "common/NetworkRequest.hpp" - -#include -#include -#include -#include -#include - -namespace chatterino { - -namespace { - -inline Image *lli(const char *pixmapPath, qreal scale = 1) -{ - return new Image(new QPixmap(pixmapPath), scale); -} - -template -inline bool ReadValue(const rapidjson::Value &object, const char *key, Type &out) -{ - if (!object.HasMember(key)) { - return false; - } - - const auto &value = object[key]; - - if (!value.Is()) { - return false; - } - - out = value.Get(); - - return true; -} - -template <> -inline bool ReadValue(const rapidjson::Value &object, const char *key, QString &out) -{ - if (!object.HasMember(key)) { - return false; - } - - const auto &value = object[key]; - - if (!value.IsString()) { - return false; - } - - out = value.GetString(); - - return true; -} - -template <> -inline bool ReadValue>(const rapidjson::Value &object, const char *key, - std::vector &out) -{ - if (!object.HasMember(key)) { - return false; - } - - const auto &value = object[key]; - - if (!value.IsArray()) { - return false; - } - - for (const rapidjson::Value &innerValue : value.GetArray()) { - if (!innerValue.IsString()) { - return false; - } - - out.emplace_back(innerValue.GetString()); - } - - return true; -} - -// Parse a single cheermote set (or "action") from the twitch api -inline bool ParseSingleCheermoteSet(Resources::JSONCheermoteSet &set, - const rapidjson::Value &action) -{ - if (!action.IsObject()) { - return false; - } - - if (!ReadValue(action, "prefix", set.prefix)) { - return false; - } - - if (!ReadValue(action, "scales", set.scales)) { - return false; - } - - if (!ReadValue(action, "backgrounds", set.backgrounds)) { - return false; - } - - if (!ReadValue(action, "states", set.states)) { - return false; - } - - if (!ReadValue(action, "type", set.type)) { - return false; - } - - if (!ReadValue(action, "updated_at", set.updatedAt)) { - return false; - } - - if (!ReadValue(action, "priority", set.priority)) { - return false; - } - - // Tiers - if (!action.HasMember("tiers")) { - return false; - } - - const auto &tiersValue = action["tiers"]; - - if (!tiersValue.IsArray()) { - return false; - } - - for (const rapidjson::Value &tierValue : tiersValue.GetArray()) { - Resources::JSONCheermoteSet::CheermoteTier tier; - - if (!tierValue.IsObject()) { - return false; - } - - if (!ReadValue(tierValue, "min_bits", tier.minBits)) { - return false; - } - - if (!ReadValue(tierValue, "id", tier.id)) { - return false; - } - - if (!ReadValue(tierValue, "color", tier.color)) { - return false; - } - - // Images - if (!tierValue.HasMember("images")) { - return false; - } - - const auto &imagesValue = tierValue["images"]; - - if (!imagesValue.IsObject()) { - return false; - } - - // Read images object - for (const auto &imageBackgroundValue : imagesValue.GetObject()) { - QString background = imageBackgroundValue.name.GetString(); - bool backgroundExists = false; - for (const auto &bg : set.backgrounds) { - if (background == bg) { - backgroundExists = true; - break; - } - } - - if (!backgroundExists) { - continue; - } - - const rapidjson::Value &imageBackgroundStates = imageBackgroundValue.value; - if (!imageBackgroundStates.IsObject()) { - continue; - } - - // Read each key which represents a background - for (const auto &imageBackgroundState : imageBackgroundStates.GetObject()) { - QString state = imageBackgroundState.name.GetString(); - bool stateExists = false; - for (const auto &_state : set.states) { - if (state == _state) { - stateExists = true; - break; - } - } - - if (!stateExists) { - continue; - } - - const rapidjson::Value &imageScalesValue = imageBackgroundState.value; - if (!imageScalesValue.IsObject()) { - continue; - } - - // Read each key which represents a scale - for (const auto &imageScaleValue : imageScalesValue.GetObject()) { - QString scale = imageScaleValue.name.GetString(); - bool scaleExists = false; - for (const auto &_scale : set.scales) { - if (scale == _scale) { - scaleExists = true; - break; - } - } - - if (!scaleExists) { - continue; - } - - const rapidjson::Value &imageScaleURLValue = imageScaleValue.value; - if (!imageScaleURLValue.IsString()) { - continue; - } - - QString url = imageScaleURLValue.GetString(); - - bool ok = false; - qreal scaleNumber = scale.toFloat(&ok); - if (!ok) { - continue; - } - - qreal chatterinoScale = 1 / scaleNumber; - - auto image = new Image(url, chatterinoScale); - - // TODO(pajlada): Fill in name and tooltip - tier.images[background][state][scale] = image; - } - } - } - - set.tiers.emplace_back(tier); - } - - return true; -} - -// Look through the results of https://api.twitch.tv/kraken/bits/actions?channel_id=11148817 for -// cheermote sets or "Actions" as they are called in the API -inline void ParseCheermoteSets(std::vector &sets, - const rapidjson::Document &d) -{ - if (!d.IsObject()) { - return; - } - - if (!d.HasMember("actions")) { - return; - } - - const auto &actionsValue = d["actions"]; - - if (!actionsValue.IsArray()) { - return; - } - - for (const auto &action : actionsValue.GetArray()) { - Resources::JSONCheermoteSet set; - bool res = ParseSingleCheermoteSet(set, action); - - if (res) { - sets.emplace_back(set); - } - } -} - -} // namespace -Resources::Resources() - : badgeStaff(lli(":/images/staff_bg.png")) - , badgeAdmin(lli(":/images/admin_bg.png")) - , badgeGlobalModerator(lli(":/images/globalmod_bg.png")) - , badgeModerator(lli(":/images/moderator_bg.png")) - , badgeTurbo(lli(":/images/turbo_bg.png")) - , badgeBroadcaster(lli(":/images/broadcaster_bg.png")) - , badgePremium(lli(":/images/twitchprime_bg.png")) - , badgeVerified(lli(":/images/verified.png", 0.25)) - , badgeSubscriber(lli(":/images/subscriber.png", 0.25)) - , badgeCollapsed(lli(":/images/collapse.png")) - , cheerBadge100000(lli(":/images/cheer100000")) - , cheerBadge10000(lli(":/images/cheer10000")) - , cheerBadge5000(lli(":/images/cheer5000")) - , cheerBadge1000(lli(":/images/cheer1000")) - , cheerBadge100(lli(":/images/cheer100")) - , cheerBadge1(lli(":/images/cheer1")) - , moderationmode_enabled(lli(":/images/moderatormode_enabled")) - , moderationmode_disabled(lli(":/images/moderatormode_disabled")) - , splitHeaderContext(lli(":/images/tool_moreCollapser_off16.png")) - , buttonBan(lli(":/images/button_ban.png", 0.25)) - , buttonTimeout(lli(":/images/button_timeout.png", 0.25)) - , pajaDank(lli(":/images/pajaDank.png", 0.25)) - , ppHop(new Image("https://fourtf.com/ppHop.gif", 0.25)) -{ - this->split.left = QIcon(":/images/split/splitleft.png"); - this->split.right = QIcon(":/images/split/splitright.png"); - this->split.up = QIcon(":/images/split/splitup.png"); - this->split.down = QIcon(":/images/split/splitdown.png"); - this->split.move = QIcon(":/images/split/splitmove.png"); - - this->buttons.ban = QPixmap(":/images/buttons/ban.png"); - this->buttons.unban = QPixmap(":/images/buttons/unban.png"); - this->buttons.mod = QPixmap(":/images/buttons/mod.png"); - this->buttons.unmod = QPixmap(":/images/buttons/unmod.png"); - - qDebug() << "init ResourceManager"; -} - -void Resources::initialize(Application &app) -{ - this->loadDynamicTwitchBadges(); - - this->loadChatterinoBadges(); -} - -Resources::BadgeVersion::BadgeVersion(QJsonObject &&root) - : badgeImage1x(new Image(root.value("image_url_1x").toString())) - , badgeImage2x(new Image(root.value("image_url_2x").toString())) - , badgeImage4x(new Image(root.value("image_url_4x").toString())) - , description(root.value("description").toString().toStdString()) - , title(root.value("title").toString().toStdString()) - , clickAction(root.value("clickAction").toString().toStdString()) - , clickURL(root.value("clickURL").toString().toStdString()) -{ -} - -void Resources::loadChannelData(const QString &roomID, bool bypassCache) -{ - QString url = "https://badges.twitch.tv/v1/badges/channels/" + roomID + "/display?language=en"; - - NetworkRequest req(url); - req.setCaller(QThread::currentThread()); - - req.onSuccess([this, roomID](auto result) { - auto root = result.parseJson(); - QJsonObject sets = root.value("badge_sets").toObject(); - - Resources::Channel &ch = this->channels[roomID]; - - for (QJsonObject::iterator it = sets.begin(); it != sets.end(); ++it) { - QJsonObject versions = it.value().toObject().value("versions").toObject(); - - auto &badgeSet = ch.badgeSets[it.key().toStdString()]; - auto &versionsMap = badgeSet.versions; - - for (auto versionIt = std::begin(versions); versionIt != std::end(versions); - ++versionIt) { - std::string kkey = versionIt.key().toStdString(); - QJsonObject versionObj = versionIt.value().toObject(); - BadgeVersion v(std::move(versionObj)); - versionsMap.emplace(kkey, v); - } - } - - ch.loaded = true; - - return true; - }); - - req.execute(); - - QString cheermoteUrl = "https://api.twitch.tv/kraken/bits/actions?channel_id=" + roomID; - auto request = NetworkRequest::twitchRequest(cheermoteUrl); - request.setCaller(QThread::currentThread()); - - request.onSuccess([this, roomID](auto result) { - auto d = result.parseRapidJson(); - Resources::Channel &ch = this->channels[roomID]; - - ParseCheermoteSets(ch.jsonCheermoteSets, d); - - for (auto &set : ch.jsonCheermoteSets) { - CheermoteSet cheermoteSet; - cheermoteSet.regex = QRegularExpression("^" + set.prefix.toLower() + "([1-9][0-9]*)$"); - - for (auto &tier : set.tiers) { - Cheermote cheermote; - - cheermote.color = QColor(tier.color); - cheermote.minBits = tier.minBits; - - // TODO(pajlada): We currently hardcode dark here :| - // We will continue to do so for now since we haven't had to - // solve that anywhere else - cheermote.emoteDataAnimated.image1x = tier.images["dark"]["animated"]["1"]; - cheermote.emoteDataAnimated.image2x = tier.images["dark"]["animated"]["2"]; - cheermote.emoteDataAnimated.image3x = tier.images["dark"]["animated"]["4"]; - - cheermote.emoteDataStatic.image1x = tier.images["dark"]["static"]["1"]; - cheermote.emoteDataStatic.image2x = tier.images["dark"]["static"]["2"]; - cheermote.emoteDataStatic.image3x = tier.images["dark"]["static"]["4"]; - - cheermoteSet.cheermotes.emplace_back(cheermote); - } - - std::sort(cheermoteSet.cheermotes.begin(), cheermoteSet.cheermotes.end(), - [](const auto &lhs, const auto &rhs) { - return lhs.minBits < rhs.minBits; // - }); - - ch.cheermoteSets.emplace_back(cheermoteSet); - } - - return true; - }); - - request.execute(); -} - -void Resources::loadDynamicTwitchBadges() -{ - static QString url("https://badges.twitch.tv/v1/badges/global/display?language=en"); - - NetworkRequest req(url); - req.setCaller(QThread::currentThread()); - req.onSuccess([this](auto result) { - auto root = result.parseJson(); - QJsonObject sets = root.value("badge_sets").toObject(); - for (QJsonObject::iterator it = sets.begin(); it != sets.end(); ++it) { - QJsonObject versions = it.value().toObject().value("versions").toObject(); - - auto &badgeSet = this->badgeSets[it.key().toStdString()]; - auto &versionsMap = badgeSet.versions; - - for (auto versionIt = std::begin(versions); versionIt != std::end(versions); - ++versionIt) { - std::string kkey = versionIt.key().toStdString(); - QJsonObject versionObj = versionIt.value().toObject(); - BadgeVersion v(std::move(versionObj)); - versionsMap.emplace(kkey, v); - } - } - - this->dynamicBadgesLoaded = true; - - return true; - }); - - req.execute(); -} - -void Resources::loadChatterinoBadges() -{ - this->chatterinoBadges.clear(); - - static QString url("https://fourtf.com/chatterino/badges.json"); - - NetworkRequest req(url); - req.setCaller(QThread::currentThread()); - - req.onSuccess([this](auto result) { - auto root = result.parseJson(); - QJsonArray badgeVariants = root.value("badges").toArray(); - for (QJsonArray::iterator it = badgeVariants.begin(); it != badgeVariants.end(); ++it) { - QJsonObject badgeVariant = it->toObject(); - const std::string badgeVariantTooltip = - badgeVariant.value("tooltip").toString().toStdString(); - const QString &badgeVariantImageURL = badgeVariant.value("image").toString(); - - auto badgeVariantPtr = std::make_shared( - badgeVariantTooltip, new Image(badgeVariantImageURL)); - - QJsonArray badgeVariantUsers = badgeVariant.value("users").toArray(); - - for (QJsonArray::iterator it = badgeVariantUsers.begin(); it != badgeVariantUsers.end(); - ++it) { - const std::string username = it->toString().toStdString(); - this->chatterinoBadges[username] = - std::shared_ptr(badgeVariantPtr); - } - } - - return true; - }); - - req.execute(); -} - -} // namespace chatterino diff --git a/src/singletons/Resources.hpp b/src/singletons/Resources.hpp index 86db4d84a..57a1b79ad 100644 --- a/src/singletons/Resources.hpp +++ b/src/singletons/Resources.hpp @@ -1,160 +1,3 @@ #pragma once -#include "common/Singleton.hpp" - -#include "common/Emotemap.hpp" - -#include -#include - -#include -#include -#include - -namespace chatterino { - -class Resources : public Singleton -{ -public: - Resources(); - - ~Resources() = delete; - - virtual void initialize(Application &app) override; - - struct { - QIcon left; - QIcon right; - QIcon up; - QIcon down; - QIcon move; - } split; - - struct { - QPixmap ban; - QPixmap unban; - QPixmap mod; - QPixmap unmod; - } buttons; - - Image *badgeStaff; - Image *badgeAdmin; - Image *badgeGlobalModerator; - Image *badgeModerator; - Image *badgeTurbo; - Image *badgeBroadcaster; - Image *badgePremium; - Image *badgeVerified; - Image *badgeSubscriber; - Image *badgeCollapsed; - - Image *cheerBadge100000; - Image *cheerBadge10000; - Image *cheerBadge5000; - Image *cheerBadge1000; - Image *cheerBadge100; - Image *cheerBadge1; - - Image *moderationmode_enabled; - Image *moderationmode_disabled; - - Image *splitHeaderContext; - - std::map cheerBadges; - - struct BadgeVersion { - BadgeVersion() = delete; - - explicit BadgeVersion(QJsonObject &&root); - - Image *badgeImage1x; - Image *badgeImage2x; - Image *badgeImage4x; - std::string description; - std::string title; - std::string clickAction; - std::string clickURL; - }; - - struct BadgeSet { - std::map versions; - }; - - std::map badgeSets; - - bool dynamicBadgesLoaded = false; - - Image *buttonBan; - Image *buttonTimeout; - Image *pajaDank; - Image *ppHop; - - struct JSONCheermoteSet { - QString prefix; - std::vector scales; - - std::vector backgrounds; - std::vector states; - - QString type; - QString updatedAt; - int priority; - - struct CheermoteTier { - int minBits; - QString id; - QString color; - - // Background State Scale - std::map>> images; - }; - - std::vector tiers; - }; - - struct Cheermote { - // a Cheermote indicates one tier - QColor color; - int minBits; - - EmoteData emoteDataAnimated; - EmoteData emoteDataStatic; - }; - - struct CheermoteSet { - QRegularExpression regex; - std::vector cheermotes; - }; - - struct Channel { - std::map badgeSets; - std::vector jsonCheermoteSets; - std::vector cheermoteSets; - - bool loaded = false; - }; - - // channelId - std::map channels; - - // Chatterino badges - struct ChatterinoBadge { - ChatterinoBadge(const std::string &_tooltip, Image *_image) - : tooltip(_tooltip) - , image(_image) - { - } - - std::string tooltip; - Image *image; - }; - - // username - std::map> chatterinoBadges; - - void loadChannelData(const QString &roomID, bool bypassCache = false); - void loadDynamicTwitchBadges(); - void loadChatterinoBadges(); -}; - -} // namespace chatterino +#include "autogenerated/ResourcesAutogen.hpp" diff --git a/src/singletons/Settings.cpp b/src/singletons/Settings.cpp index 79398197f..10be19100 100644 --- a/src/singletons/Settings.cpp +++ b/src/singletons/Settings.cpp @@ -10,32 +10,25 @@ namespace chatterino { std::vector> _settings; +Settings *Settings::instance = nullptr; + void _actuallyRegisterSetting(std::weak_ptr setting) { _settings.push_back(setting); } -Settings::Settings() +Settings::Settings(Paths &paths) { - qDebug() << "init SettingManager"; + instance = this; + + QString settingsPath = paths.settingsDirectory + "/settings.json"; + + pajlada::Settings::SettingManager::gLoad(qPrintable(settingsPath)); } Settings &Settings::getInstance() { - static Settings instance; - - return instance; -} - -void Settings::initialize() -{ -} - -void Settings::load() -{ - QString settingsPath = getPaths()->settingsDirectory + "/settings.json"; - - pajlada::Settings::SettingManager::gLoad(qPrintable(settingsPath)); + return *instance; } void Settings::saveSnapshot() diff --git a/src/singletons/Settings.hpp b/src/singletons/Settings.hpp index a785df395..9058210f1 100644 --- a/src/singletons/Settings.hpp +++ b/src/singletons/Settings.hpp @@ -1,5 +1,7 @@ #pragma once +#include "Paths.hpp" + #include "common/ChatterinoSetting.hpp" #include "controllers/highlights/HighlightPhrase.hpp" #include "controllers/moderationactions/ModerationAction.hpp" @@ -14,13 +16,12 @@ void _actuallyRegisterSetting(std::weak_ptr set class Settings { - Settings(); + static Settings *instance; public: - static Settings &getInstance(); + Settings(Paths &paths); - void initialize(); - void load(); + static Settings &getInstance(); /// Appearance BoolSetting showTimestamps = {"/appearance/messages/showTimestamps", true}; @@ -131,6 +132,6 @@ private: std::unique_ptr snapshot_; }; -Settings *getSettings(); +[[deprecated]] Settings *getSettings(); } // namespace chatterino diff --git a/src/singletons/WindowManager.cpp b/src/singletons/WindowManager.cpp index 2e43f7d15..61c5ec2f5 100644 --- a/src/singletons/WindowManager.cpp +++ b/src/singletons/WindowManager.cpp @@ -207,11 +207,12 @@ Window *WindowManager::windowAt(int index) return this->windows_.at(index); } -void WindowManager::initialize(Application &app) +void WindowManager::initialize(Settings &settings, Paths &paths) { assertInGuiThread(); - app.themes->repaintVisibleChatWidgets_.connect([this] { this->repaintVisibleChatWidgets(); }); + getApp()->themes->repaintVisibleChatWidgets_.connect( + [this] { this->repaintVisibleChatWidgets(); }); assert(!this->initialized_); @@ -301,20 +302,15 @@ void WindowManager::initialize(Application &app) mainWindow_->getNotebook().addPage(true); } - auto settings = getSettings(); + settings.timestampFormat.connect([this](auto, auto) { this->layoutChannelViews(); }); - settings->timestampFormat.connect([this](auto, auto) { - auto app = getApp(); - this->layoutChannelViews(); - }); + settings.emoteScale.connect([this](auto, auto) { this->forceLayoutChannelViews(); }); - settings->emoteScale.connect([this](auto, auto) { this->forceLayoutChannelViews(); }); - - settings->timestampFormat.connect([this](auto, auto) { this->forceLayoutChannelViews(); }); - settings->alternateMessageBackground.connect( + settings.timestampFormat.connect([this](auto, auto) { this->forceLayoutChannelViews(); }); + settings.alternateMessageBackground.connect( [this](auto, auto) { this->forceLayoutChannelViews(); }); - settings->separateMessages.connect([this](auto, auto) { this->forceLayoutChannelViews(); }); - settings->collpseMessagesMinLines.connect( + settings.separateMessages.connect([this](auto, auto) { this->forceLayoutChannelViews(); }); + settings.collpseMessagesMinLines.connect( [this](auto, auto) { this->forceLayoutChannelViews(); }); this->initialized_ = true; @@ -438,7 +434,7 @@ void WindowManager::encodeChannel(IndirectChannel channel, QJsonObject &obj) switch (channel.getType()) { case Channel::Type::Twitch: { obj.insert("type", "twitch"); - obj.insert("name", channel.get()->name); + obj.insert("name", channel.get()->getName()); } break; case Channel::Type::TwitchMentions: { obj.insert("type", "mentions"); diff --git a/src/singletons/WindowManager.hpp b/src/singletons/WindowManager.hpp index 93a586079..68a0dc7d5 100644 --- a/src/singletons/WindowManager.hpp +++ b/src/singletons/WindowManager.hpp @@ -6,7 +6,10 @@ namespace chatterino { -class WindowManager : public Singleton +class Settings; +class Paths; + +class WindowManager final : public Singleton { public: WindowManager(); @@ -36,7 +39,7 @@ public: int windowCount(); Window *windowAt(int index); - virtual void initialize(Application &app) override; + virtual void initialize(Settings &settings, Paths &paths) override; virtual void save() override; void closeAll(); diff --git a/src/util/JsonQuery.cpp b/src/util/JsonQuery.cpp new file mode 100644 index 000000000..1c7f18a3c --- /dev/null +++ b/src/util/JsonQuery.cpp @@ -0,0 +1,9 @@ +#include "JsonQuery.hpp" + +namespace chatterino { + +JsonQuery::JsonQuery() +{ +} + +} // namespace chatterino diff --git a/src/util/JsonQuery.hpp b/src/util/JsonQuery.hpp new file mode 100644 index 000000000..7c8f4c11f --- /dev/null +++ b/src/util/JsonQuery.hpp @@ -0,0 +1,12 @@ +#pragma once + +class QJsonObject; + +namespace chatterino { +class JsonQuery +{ +public: + JsonQuery(); +}; + +} // namespace chatterino diff --git a/src/widgets/Notebook.cpp b/src/widgets/Notebook.cpp index b076cabae..5068732b7 100644 --- a/src/widgets/Notebook.cpp +++ b/src/widgets/Notebook.cpp @@ -29,6 +29,8 @@ Notebook::Notebook(QWidget *parent) : BaseWidget(parent) , addButton_(this) { + this->addButton_.setIcon(NotebookButton::Icon::Plus); + this->addButton_.setHidden(true); auto *shortcut_next = new QShortcut(QKeySequence("Ctrl+Tab"), this); @@ -425,8 +427,7 @@ void SplitNotebook::addCustomButtons() settingsBtn->setVisible(!getApp()->settings->hidePreferencesButton.getValue()); getApp()->settings->hidePreferencesButton.connect( - [settingsBtn](bool hide, auto) { settingsBtn->setVisible(!hide); }, - this->connections_); + [settingsBtn](bool hide, auto) { settingsBtn->setVisible(!hide); }, this->connections_); settingsBtn->setIcon(NotebookButton::Settings); diff --git a/src/widgets/Window.cpp b/src/widgets/Window.cpp index ab083ace1..37d808c2f 100644 --- a/src/widgets/Window.cpp +++ b/src/widgets/Window.cpp @@ -159,8 +159,8 @@ void Window::addLayout() void Window::addCustomTitlebarButtons() { - return_unless(this->hasCustomWindowFrame()); - return_unless(this->type_ == Type::Main); + if (!this->hasCustomWindowFrame()) return; + if (this->type_ != Type::Main) return; // settings this->addTitleBarButton(TitleBarButton::Settings, [] { diff --git a/src/widgets/dialogs/EmotePopup.cpp b/src/widgets/dialogs/EmotePopup.cpp index 24e4e8dcf..aa33925e3 100644 --- a/src/widgets/dialogs/EmotePopup.cpp +++ b/src/widgets/dialogs/EmotePopup.cpp @@ -46,7 +46,7 @@ EmotePopup::EmotePopup() void EmotePopup::loadChannel(ChannelPtr _channel) { - this->setWindowTitle("Emotes from " + _channel->name); + this->setWindowTitle("Emotes from " + _channel->getName()); TwitchChannel *channel = dynamic_cast(_channel.get()); @@ -70,21 +70,19 @@ void EmotePopup::loadChannel(ChannelPtr _channel) builder2.getMessage()->flags |= Message::Centered; builder2.getMessage()->flags |= Message::DisableCompactEmotes; - map.each([&](const QString &key, const EmoteData &value) { - builder2.append((new EmoteElement(value, MessageElement::Flags::AlwaysShow)) - ->setLink(Link(Link::InsertText, key))); - }); + for (auto emote : map) { + builder2.append((new EmoteElement(emote.second, MessageElement::Flags::AlwaysShow)) + ->setLink(Link(Link::InsertText, emote.first.string))); + } emoteChannel->addMessage(builder2.getMessage()); }; auto app = getApp(); - QString userID = app->accounts->twitch.getCurrent()->getUserId(); - // fourtf: the entire emote manager needs to be refactored so there's no point in trying to // fix this pile of garbage - for (const auto &set : app->emotes->twitch.emotes[userID].emoteSets) { + for (const auto &set : app->accounts->twitch.getCurrent()->accessEmotes()->emoteSets) { // TITLE MessageBuilder builder1; @@ -110,20 +108,22 @@ void EmotePopup::loadChannel(ChannelPtr _channel) builder2.getMessage()->flags |= Message::DisableCompactEmotes; for (const auto &emote : set->emotes) { - [&](const QString &key, const EmoteData &value) { - builder2.append((new EmoteElement(value, MessageElement::Flags::AlwaysShow)) - ->setLink(Link(Link::InsertText, key))); - }(emote.code, app->emotes->twitch.getEmoteById(emote.id, emote.code)); + builder2.append( + (new EmoteElement(app->emotes->twitch.getOrCreateEmote(emote.id, emote.name), + MessageElement::Flags::AlwaysShow)) + ->setLink(Link(Link::InsertText, emote.name.string))); } emoteChannel->addMessage(builder2.getMessage()); } - addEmotes(app->emotes->bttv.globalEmotes, "BetterTTV Global Emotes", "BetterTTV Global Emote"); - addEmotes(channel->getBttvEmotes(), "BetterTTV Channel Emotes", "BetterTTV Channel Emote"); - addEmotes(app->emotes->ffz.globalEmotes, "FrankerFaceZ Global Emotes", - "FrankerFaceZ Global Emote"); - addEmotes(channel->getFfzEmotes(), "FrankerFaceZ Channel Emotes", "FrankerFaceZ Channel Emote"); + addEmotes(*app->emotes->bttv.accessGlobalEmotes(), "BetterTTV Global Emotes", + "BetterTTV Global Emote"); + addEmotes(*channel->accessBttvEmotes(), "BetterTTV Channel Emotes", "BetterTTV Channel Emote"); + // addEmotes(*app->emotes->ffz.accessGlobalEmotes(), "FrankerFaceZ Global Emotes", + // "FrankerFaceZ Global Emote"); + addEmotes(*channel->accessFfzEmotes(), "FrankerFaceZ Channel Emotes", + "FrankerFaceZ Channel Emote"); this->viewEmotes_->setChannel(emoteChannel); } @@ -146,9 +146,9 @@ void EmotePopup::loadEmojis() builder.getMessage()->flags |= Message::Centered; builder.getMessage()->flags |= Message::DisableCompactEmotes; - emojis.each([&builder](const QString &key, const auto &value) { + emojis.each([&builder](const auto &key, const auto &value) { builder.append( - (new EmoteElement(value->emoteData, MessageElement::Flags::AlwaysShow)) + (new EmoteElement(value->emote, MessageElement::Flags::AlwaysShow)) ->setLink(Link(Link::Type::InsertText, ":" + value->shortCodes[0] + ":"))); }); emojiChannel->addMessage(builder.getMessage()); diff --git a/src/widgets/dialogs/LogsPopup.cpp b/src/widgets/dialogs/LogsPopup.cpp index a2afb9301..4ad3f962e 100644 --- a/src/widgets/dialogs/LogsPopup.cpp +++ b/src/widgets/dialogs/LogsPopup.cpp @@ -34,7 +34,7 @@ void LogsPopup::setInfo(ChannelPtr channel, QString userName) { this->channel_ = channel; this->userName_ = userName; - this->setWindowTitle(this->userName_ + "'s logs in #" + this->channel_->name); + this->setWindowTitle(this->userName_ + "'s logs in #" + this->channel_->getName()); this->getLogviewerLogs(); } @@ -53,7 +53,7 @@ void LogsPopup::getLogviewerLogs() return; } - QString channelName = twitchChannel->name; + QString channelName = twitchChannel->getName(); QString url = QString("https://cbenni.com/api/logs/%1/?nick=%2&before=500") .arg(channelName, this->userName_); @@ -66,7 +66,7 @@ void LogsPopup::getLogviewerLogs() return true; }); - req.onSuccess([this, channelName](auto result) { + req.onSuccess([this, channelName](auto result) -> Outcome { auto data = result.parseJson(); std::vector messages; ChannelPtr logsChannel(new Channel("logs", Channel::Type::None)); @@ -89,7 +89,7 @@ void LogsPopup::getLogviewerLogs() }; this->setMessages(messages); - return true; + return Success; }); req.execute(); @@ -102,7 +102,7 @@ void LogsPopup::getOverrustleLogs() return; } - QString channelName = twitchChannel->name; + QString channelName = twitchChannel->getName(); QString url = QString("https://overrustlelogs.net/api/v1/stalk/%1/%2.json?limit=500") .arg(channelName, this->userName_); @@ -120,7 +120,7 @@ void LogsPopup::getOverrustleLogs() return true; }); - req.onSuccess([this, channelName](auto result) { + req.onSuccess([this, channelName](auto result) -> Outcome { auto data = result.parseJson(); std::vector messages; if (data.contains("lines")) { @@ -141,7 +141,7 @@ void LogsPopup::getOverrustleLogs() } this->setMessages(messages); - return true; + return Success; }); req.execute(); diff --git a/src/widgets/dialogs/SelectChannelDialog.cpp b/src/widgets/dialogs/SelectChannelDialog.cpp index 9a9fb99df..4f026112b 100644 --- a/src/widgets/dialogs/SelectChannelDialog.cpp +++ b/src/widgets/dialogs/SelectChannelDialog.cpp @@ -154,7 +154,7 @@ void SelectChannelDialog::setSelectedChannel(IndirectChannel _channel) case Channel::Type::Twitch: { this->ui_.notebook->selectIndex(TAB_TWITCH); this->ui_.twitch.channel->setFocus(); - this->ui_.twitch.channelName->setText(channel->name); + this->ui_.twitch.channelName->setText(channel->getName()); } break; case Channel::Type::TwitchWatching: { this->ui_.notebook->selectIndex(TAB_TWITCH); diff --git a/src/widgets/dialogs/UserInfoPopup.cpp b/src/widgets/dialogs/UserInfoPopup.cpp index edddede5e..77c30ba77 100644 --- a/src/widgets/dialogs/UserInfoPopup.cpp +++ b/src/widgets/dialogs/UserInfoPopup.cpp @@ -16,6 +16,8 @@ #include #include #include +#include +#include #define TEXT_FOLLOWERS "Followers: " #define TEXT_VIEWS "Views: " @@ -255,7 +257,7 @@ void UserInfoPopup::updateUserData() auto request = NetworkRequest::twitchRequest(url); request.setCaller(this); - request.onSuccess([this](auto result) { + request.onSuccess([this](auto result) -> Outcome { auto obj = result.parseJson(); this->ui_.followerCountLabel->setText(TEXT_FOLLOWERS + QString::number(obj.value("followers").toInt())); @@ -266,7 +268,7 @@ void UserInfoPopup::updateUserData() this->loadAvatar(QUrl(obj.value("logo").toString())); - return true; + return Success; }); request.execute(); diff --git a/src/widgets/helper/ChannelView.cpp b/src/widgets/helper/ChannelView.cpp index e3b8fb847..47aa5b7f6 100644 --- a/src/widgets/helper/ChannelView.cpp +++ b/src/widgets/helper/ChannelView.cpp @@ -32,6 +32,58 @@ #define CHAT_HOVER_PAUSE_DURATION 1000 namespace chatterino { +namespace { +void addEmoteContextMenuItems(const Emote &emote, MessageElement::Flags creatorFlags, QMenu &menu) +{ + auto openAction = menu.addAction("Open"); + auto openMenu = new QMenu; + openAction->setMenu(openMenu); + + auto copyAction = menu.addAction("Copy"); + auto copyMenu = new QMenu; + copyAction->setMenu(copyMenu); + + // see if the QMenu actually gets destroyed + QObject::connect(openMenu, &QMenu::destroyed, [] { + QMessageBox(QMessageBox::Information, "xD", "the menu got deleted").exec(); + }); + + // Add copy and open links for 1x, 2x, 3x + auto addImageLink = [&](const ImagePtr &image, char scale) { + if (image->isValid()) { + copyMenu->addAction(QString(scale) + "x link", [url = image->getUrl()] { + QApplication::clipboard()->setText(url.string); + }); + openMenu->addAction(QString(scale) + "x link", [url = image->getUrl()] { + QDesktopServices::openUrl(QUrl(url.string)); + }); + } + }; + + addImageLink(emote.images.getImage1(), '1'); + addImageLink(emote.images.getImage2(), '2'); + addImageLink(emote.images.getImage3(), '3'); + + // Copy and open emote page link + auto addPageLink = [&](const QString &name) { + copyMenu->addSeparator(); + openMenu->addSeparator(); + + copyMenu->addAction("Copy " + name + " emote link", [url = emote.homePage] { + QApplication::clipboard()->setText(url.string); // + }); + openMenu->addAction("Open " + name + " emote link", [url = emote.homePage] { + QDesktopServices::openUrl(QUrl(url.string)); // + }); + }; + + if (creatorFlags & MessageElement::Flags::BttvEmote) { + addPageLink("BTTV"); + } else if (creatorFlags & MessageElement::Flags::FfzEmote) { + addPageLink("FFZ"); + } +} +} // namespace ChannelView::ChannelView(BaseWidget *parent) : BaseWidget(parent) @@ -988,74 +1040,8 @@ void ChannelView::addContextMenuItems(const MessageLayoutElement *hoveredElement // Emote actions if (creatorFlags & (MessageElement::Flags::EmoteImages | MessageElement::Flags::EmojiImage)) { - const auto &emoteElement = static_cast(creator); - - // TODO: We might want to add direct "Open image" variants alongside the Copy - // actions - if (emoteElement.data.image1x != nullptr) { - QAction *addEntry = menu->addAction("Copy emote link..."); - - QMenu *procmenu = new QMenu; - addEntry->setMenu(procmenu); - procmenu->addAction("Copy 1x link", [url = emoteElement.data.image1x->getUrl()] { - QApplication::clipboard()->setText(url); // - }); - if (emoteElement.data.image2x != nullptr) { - procmenu->addAction("Copy 2x link", [url = emoteElement.data.image2x->getUrl()] { - QApplication::clipboard()->setText(url); // - }); - } - if (emoteElement.data.image3x != nullptr) { - procmenu->addAction("Copy 3x link", [url = emoteElement.data.image3x->getUrl()] { - QApplication::clipboard()->setText(url); // - }); - } - if ((creatorFlags & MessageElement::Flags::BttvEmote) != 0) { - procmenu->addSeparator(); - QString emotePageLink = emoteElement.data.pageLink; - procmenu->addAction("Copy BTTV emote link", [emotePageLink] { - QApplication::clipboard()->setText(emotePageLink); // - }); - } else if ((creatorFlags & MessageElement::Flags::FfzEmote) != 0) { - procmenu->addSeparator(); - QString emotePageLink = emoteElement.data.pageLink; - procmenu->addAction("Copy FFZ emote link", [emotePageLink] { - QApplication::clipboard()->setText(emotePageLink); // - }); - } - } - if (emoteElement.data.image1x != nullptr) { - QAction *addEntry = menu->addAction("Open emote link..."); - - QMenu *procmenu = new QMenu; - addEntry->setMenu(procmenu); - procmenu->addAction("Open 1x link", [url = emoteElement.data.image1x->getUrl()] { - QDesktopServices::openUrl(QUrl(url)); // - }); - if (emoteElement.data.image2x != nullptr) { - procmenu->addAction("Open 2x link", [url = emoteElement.data.image2x->getUrl()] { - QDesktopServices::openUrl(QUrl(url)); // - }); - } - if (emoteElement.data.image3x != nullptr) { - procmenu->addAction("Open 3x link", [url = emoteElement.data.image3x->getUrl()] { - QDesktopServices::openUrl(QUrl(url)); // - }); - } - if ((creatorFlags & MessageElement::Flags::BttvEmote) != 0) { - procmenu->addSeparator(); - QString emotePageLink = emoteElement.data.pageLink; - procmenu->addAction("Open BTTV emote link", [emotePageLink] { - QDesktopServices::openUrl(QUrl(emotePageLink)); // - }); - } else if ((creatorFlags & MessageElement::Flags::FfzEmote) != 0) { - procmenu->addSeparator(); - QString emotePageLink = emoteElement.data.pageLink; - procmenu->addAction("Open FFZ emote link", [emotePageLink] { - QDesktopServices::openUrl(QUrl(emotePageLink)); // - }); - } - } + const auto emoteElement = dynamic_cast(&creator); + if (emoteElement) addEmoteContextMenuItems(*emoteElement->getEmote(), creatorFlags, *menu); } // add seperator @@ -1204,8 +1190,7 @@ bool ChannelView::tryGetMessageAt(QPoint p, std::shared_ptr &_mes int ChannelView::getLayoutWidth() const { - if (this->scrollBar_.isVisible()) - return int(this->width() - 8 * this->getScale()); + if (this->scrollBar_.isVisible()) return int(this->width() - 8 * this->getScale()); return this->width(); } diff --git a/src/widgets/helper/SearchPopup.cpp b/src/widgets/helper/SearchPopup.cpp index 52d20b337..1d311eaa3 100644 --- a/src/widgets/helper/SearchPopup.cpp +++ b/src/widgets/helper/SearchPopup.cpp @@ -64,7 +64,7 @@ void SearchPopup::setChannel(ChannelPtr channel) this->snapshot_ = channel->getMessageSnapshot(); this->performSearch(); - this->setWindowTitle("Searching in " + channel->name + "s history"); + this->setWindowTitle("Searching in " + channel->getName() + "s history"); } void SearchPopup::performSearch() diff --git a/src/widgets/settingspages/AboutPage.cpp b/src/widgets/settingspages/AboutPage.cpp index 8cf9059a3..f5a699eba 100644 --- a/src/widgets/settingspages/AboutPage.cpp +++ b/src/widgets/settingspages/AboutPage.cpp @@ -28,7 +28,7 @@ AboutPage::AboutPage() auto layout = widget.setLayoutType(); { QPixmap pixmap; - pixmap.load(":/images/aboutlogo.png"); + pixmap.load(":/settings/aboutlogo.png"); auto logo = layout.emplace().assign(&this->logo_); logo->setPixmap(pixmap); diff --git a/src/widgets/settingspages/LookPage.cpp b/src/widgets/settingspages/LookPage.cpp index 6f556f025..25943243d 100644 --- a/src/widgets/settingspages/LookPage.cpp +++ b/src/widgets/settingspages/LookPage.cpp @@ -277,28 +277,32 @@ ChannelPtr LookPage::createPreviewChannel() { auto message = MessagePtr(new Message()); message->addElement(new TimestampElement(QTime(8, 13, 42))); - message->addElement(new ImageElement(getApp()->resources->badgeModerator, - MessageElement::BadgeChannelAuthority)); - message->addElement(new ImageElement(getApp()->resources->badgeSubscriber, - MessageElement::BadgeSubscription)); + message->addElement( + new ImageElement(Image::fromNonOwningPixmap(&getApp()->resources->twitch.moderator), + MessageElement::BadgeChannelAuthority)); + message->addElement( + new ImageElement(Image::fromNonOwningPixmap(&getApp()->resources->twitch.subscriber), + MessageElement::BadgeSubscription)); message->addElement(new TextElement("username1:", MessageElement::Username, QColor("#0094FF"), FontStyle::ChatMediumBold)); message->addElement(new TextElement("This is a preview message", MessageElement::Text)); - message->addElement(new EmoteElement(EmoteData(getApp()->resources->pajaDank), - MessageElement::Flags::AlwaysShow)); + message->addElement( + new ImageElement(Image::fromNonOwningPixmap(&getApp()->resources->pajaDank), + MessageElement::Flags::AlwaysShow)); // message->addElement(new) channel->addMessage(message); } { auto message = MessagePtr(new Message()); message->addElement(new TimestampElement(QTime(8, 15, 21))); - message->addElement(new ImageElement(getApp()->resources->badgePremium, - MessageElement::BadgeChannelAuthority)); + message->addElement( + new ImageElement(Image::fromNonOwningPixmap(&getApp()->resources->twitch.broadcaster), + MessageElement::BadgeChannelAuthority)); message->addElement(new TextElement("username2:", MessageElement::Username, QColor("#FF6A00"), FontStyle::ChatMediumBold)); message->addElement(new TextElement("This is another one", MessageElement::Text)); - message->addElement( - new EmoteElement(EmoteData(getApp()->resources->ppHop), MessageElement::BttvEmote)); + // message->addElement(new ImageElement( + // Image::fromNonOwningPixmap(&getApp()->resources->ppHop), MessageElement::BttvEmote)); message->addElement( (new TextElement("www.fourtf.com", MessageElement::Text, MessageColor::Link)) ->setLink(Link(Link::Url, "https://www.fourtf.com"))); diff --git a/src/widgets/splits/Split.cpp b/src/widgets/splits/Split.cpp index 1ccdf63ed..65ab3b45e 100644 --- a/src/widgets/splits/Split.cpp +++ b/src/widgets/splits/Split.cpp @@ -76,7 +76,7 @@ Split::Split(QWidget *parent) createShortcut(this, "CTRL+R", &Split::doChangeChannel); // CTRL+F: Search - createShortcut(this, "CTRL+F", &Split::doSearch); + createShortcut(this, "CTRL+F", &Split::showSearchPopup); // F12 createShortcut(this, "F10", [] { @@ -378,7 +378,7 @@ void Split::doChangeChannel() auto popup = this->findChildren(); if (popup.size() && popup.at(0)->isVisible() && !popup.at(0)->isFloating()) { popup.at(0)->hide(); - doOpenViewerList(); + showViewerList(); } } @@ -401,36 +401,33 @@ void Split::doClearChat() this->view_.clearMessages(); } -void Split::doOpenChannel() +void Split::openInBrowser() { - ChannelPtr _channel = this->getChannel(); - TwitchChannel *tc = dynamic_cast(_channel.get()); + auto channel = this->getChannel(); - if (tc != nullptr) { - QDesktopServices::openUrl("https://twitch.tv/" + tc->name); + if (auto twitchChannel = dynamic_cast(channel.get())) { + QDesktopServices::openUrl("https://twitch.tv/" + twitchChannel->getName()); } } -void Split::doOpenPopupPlayer() +void Split::openInPopupPlayer() { - ChannelPtr _channel = this->getChannel(); - TwitchChannel *tc = dynamic_cast(_channel.get()); - - if (tc != nullptr) { - QDesktopServices::openUrl("https://player.twitch.tv/?channel=" + tc->name); + ChannelPtr channel = this->getChannel(); + if (auto twitchChannel = dynamic_cast(channel.get())) { + QDesktopServices::openUrl("https://player.twitch.tv/?channel=" + twitchChannel->getName()); } } -void Split::doOpenStreamlink() +void Split::openInStreamlink() { try { - openStreamlinkForChannel(this->getChannel()->name); + openStreamlinkForChannel(this->getChannel()->getName()); } catch (const Exception &ex) { Log("Error in doOpenStreamlink: {}", ex.what()); } } -void Split::doOpenViewerList() +void Split::showViewerList() { auto viewerDock = new QDockWidget("Viewer List", this); viewerDock->setAllowedAreas(Qt::LeftDockWidgetArea); @@ -458,10 +455,10 @@ void Split::doOpenViewerList() auto loadingLabel = new QLabel("Loading..."); auto request = NetworkRequest::twitchRequest("https://tmi.twitch.tv/group/user/" + - this->getChannel()->name + "/chatters"); + this->getChannel()->getName() + "/chatters"); request.setCaller(this); - request.onSuccess([=](auto result) { + request.onSuccess([=](auto result) -> Outcome { auto obj = result.parseJson(); QJsonObject chattersObj = obj.value("chatters").toObject(); @@ -472,7 +469,7 @@ void Split::doOpenViewerList() chattersList->addItem(v.toString()); } - return true; + return Success; }); request.execute(); @@ -485,8 +482,7 @@ void Split::doOpenViewerList() chattersList->hide(); resultList->clear(); for (auto &item : results) { - if (!labels.contains(item->text())) - resultList->addItem(item->text()); + if (!labels.contains(item->text())) resultList->addItem(item->text()); } resultList->show(); } else { @@ -500,13 +496,13 @@ void Split::doOpenViewerList() QObject::connect(chattersList, &QListWidget::doubleClicked, this, [=]() { if (!labels.contains(chattersList->currentItem()->text())) { - doOpenUserInfoPopup(chattersList->currentItem()->text()); + showUserInfoPopup(chattersList->currentItem()->text()); } }); QObject::connect(resultList, &QListWidget::doubleClicked, this, [=]() { if (!labels.contains(resultList->currentItem()->text())) { - doOpenUserInfoPopup(resultList->currentItem()->text()); + showUserInfoPopup(resultList->currentItem()->text()); } }); @@ -522,22 +518,22 @@ void Split::doOpenViewerList() viewerDock->show(); } -void Split::doOpenUserInfoPopup(const QString &user) +void Split::showUserInfoPopup(const UserName &user) { auto *userPopup = new UserInfoPopup; - userPopup->setData(user, this->getChannel()); + userPopup->setData(user.string, this->getChannel()); userPopup->setAttribute(Qt::WA_DeleteOnClose); userPopup->move(QCursor::pos() - QPoint(int(150 * this->getScale()), int(70 * this->getScale()))); userPopup->show(); } -void Split::doCopy() +void Split::copyToClipboard() { QApplication::clipboard()->setText(this->view_.getSelectedText()); } -void Split::doSearch() +void Split::showSearchPopup() { SearchPopup *popup = new SearchPopup(); @@ -563,26 +559,19 @@ static Iter select_randomly(Iter start, Iter end) void Split::drag() { - auto container = dynamic_cast(this->parentWidget()); - - if (container != nullptr) { + if (auto container = dynamic_cast(this->parentWidget())) { SplitContainer::isDraggingSplit = true; SplitContainer::draggingSplit = this; auto originalLocation = container->releaseSplit(this); - - QDrag *drag = new QDrag(this); - QMimeData *mimeData = new QMimeData; + auto drag = new QDrag(this); + auto mimeData = new QMimeData; mimeData->setData("chatterino/split", "xD"); - drag->setMimeData(mimeData); - Qt::DropAction dropAction = drag->exec(Qt::MoveAction); - - if (dropAction == Qt::IgnoreAction) { - container->insertSplit(this, - originalLocation); // SplitContainer::dragOriginalPosition); + if (drag->exec(Qt::MoveAction) == Qt::IgnoreAction) { + container->insertSplit(this, originalLocation); } SplitContainer::isDraggingSplit = false; diff --git a/src/widgets/splits/Split.hpp b/src/widgets/splits/Split.hpp index dc525462a..0ddfabced 100644 --- a/src/widgets/splits/Split.hpp +++ b/src/widgets/splits/Split.hpp @@ -90,7 +90,11 @@ protected: void focusInEvent(QFocusEvent *event) override; private: - void doOpenUserInfoPopup(const QString &user); + void showUserInfoPopup(const QString &userName) + { + this->showUserInfoPopup(UserName{userName}); + } + void showUserInfoPopup(const UserName &user); void channelNameUpdated(const QString &newChannelName); void handleModifiers(Qt::KeyboardModifiers modifiers); @@ -137,22 +141,22 @@ public slots: void doClearChat(); // Open link to twitch channel in default browser - void doOpenChannel(); + void openInBrowser(); // Open popup player of twitch channel in default browser - void doOpenPopupPlayer(); + void openInPopupPlayer(); // Open twitch channel stream through streamlink - void doOpenStreamlink(); + void openInStreamlink(); // Copy text from chat - void doCopy(); + void copyToClipboard(); // Open a search popup - void doSearch(); + void showSearchPopup(); // Open viewer list of the channel - void doOpenViewerList(); + void showViewerList(); }; } // namespace chatterino diff --git a/src/widgets/splits/SplitContainer.cpp b/src/widgets/splits/SplitContainer.cpp index 4a8e6da7c..e7dc50952 100644 --- a/src/widgets/splits/SplitContainer.cpp +++ b/src/widgets/splits/SplitContainer.cpp @@ -464,11 +464,9 @@ void SplitContainer::paintEvent(QPaintEvent *) void SplitContainer::dragEnterEvent(QDragEnterEvent *event) { - if (!event->mimeData()->hasFormat("chatterino/split")) - return; + if (!event->mimeData()->hasFormat("chatterino/split")) return; - if (!SplitContainer::isDraggingSplit) - return; + if (!SplitContainer::isDraggingSplit) return; this->isDragging_ = true; this->layout(); @@ -516,7 +514,7 @@ void SplitContainer::refreshTabTitle() bool first = true; for (const auto &chatWidget : this->splits_) { - auto channelName = chatWidget->getChannel()->name; + auto channelName = chatWidget->getChannel()->getName(); if (channelName.isEmpty()) { continue; } @@ -851,10 +849,8 @@ void SplitContainer::Node::layout(bool addSpacing, float _scale, std::vector &resizeRects) { for (std::unique_ptr &node : this->children_) { - if (node->flexH_ <= 0) - node->flexH_ = 0; - if (node->flexV_ <= 0) - node->flexV_ = 0; + if (node->flexH_ <= 0) node->flexH_ = 0; + if (node->flexV_ <= 0) node->flexV_ = 0; } switch (this->type_) { diff --git a/src/widgets/splits/SplitHeader.cpp b/src/widgets/splits/SplitHeader.cpp index 8d6b92676..145c79f9f 100644 --- a/src/widgets/splits/SplitHeader.cpp +++ b/src/widgets/splits/SplitHeader.cpp @@ -115,8 +115,8 @@ void SplitHeader::addDropdownItems(RippleEffectButton *) this->dropdownMenu_.addAction("Close split", this->split_, &Split::doCloseSplit, QKeySequence(tr("Ctrl+W"))); this->dropdownMenu_.addAction("Change channel", this->split_, &Split::doChangeChannel, QKeySequence(tr("Ctrl+R"))); this->dropdownMenu_.addSeparator(); - this->dropdownMenu_.addAction("Viewer list", this->split_, &Split::doOpenViewerList); - this->dropdownMenu_.addAction("Search", this->split_, &Split::doSearch, QKeySequence(tr("Ctrl+F"))); + this->dropdownMenu_.addAction("Viewer list", this->split_, &Split::showViewerList); + this->dropdownMenu_.addAction("Search", this->split_, &Split::showSearchPopup, QKeySequence(tr("Ctrl+F"))); this->dropdownMenu_.addSeparator(); this->dropdownMenu_.addAction("Popup", this->split_, &Split::doPopup); #ifdef USEWEBENGINE @@ -131,11 +131,11 @@ void SplitHeader::addDropdownItems(RippleEffectButton *) } }); #endif - this->dropdownMenu_.addAction("Open browser", this->split_, &Split::doOpenChannel); + this->dropdownMenu_.addAction("Open browser", this->split_, &Split::openInBrowser); #ifndef USEWEBENGINE - this->dropdownMenu_.addAction("Open browser popup", this->split_, &Split::doOpenPopupPlayer); + this->dropdownMenu_.addAction("Open browser popup", this->split_, &Split::openInPopupPlayer); #endif - this->dropdownMenu_.addAction("Open streamlink", this->split_, &Split::doOpenStreamlink); + this->dropdownMenu_.addAction("Open streamlink", this->split_, &Split::openInStreamlink); this->dropdownMenu_.addSeparator(); this->dropdownMenu_.addAction("Reload channel emotes", this, SLOT(menuReloadChannelEmotes())); this->dropdownMenu_.addAction("Reconnect", this, SLOT(menuManualReconnect())); @@ -303,7 +303,7 @@ void SplitHeader::updateChannelText() auto indirectChannel = this->split_->getIndirectChannel(); auto channel = this->split_->getChannel(); - QString title = channel->name; + QString title = channel->getName(); if (indirectChannel.getType() == Channel::Type::TwitchWatching) { title = "watching: " + (title.isEmpty() ? "none" : title); @@ -349,8 +349,8 @@ void SplitHeader::updateModerationModeIcon() auto app = getApp(); this->moderationButton_->setPixmap(this->split_->getModerationMode() - ? *app->resources->moderationmode_enabled->getPixmap() - : *app->resources->moderationmode_disabled->getPixmap()); + ? app->resources->buttons.modModeEnabled + : app->resources->buttons.modModeDisabled); bool modButtonVisible = false; ChannelPtr channel = this->split_->getChannel(); @@ -473,9 +473,9 @@ void SplitHeader::themeChangedEvent() } if (this->theme->isLightTheme()) { - this->dropdownButton_->setPixmap(QPixmap(":/images/menu_black.png")); + this->dropdownButton_->setPixmap(getApp()->resources->buttons.menuDark); } else { - this->dropdownButton_->setPixmap(QPixmap(":/images/menu_white.png")); + this->dropdownButton_->setPixmap(getApp()->resources->buttons.menuLight); } this->titleLabel->setPalette(palette); diff --git a/src/widgets/splits/SplitInput.cpp b/src/widgets/splits/SplitInput.cpp index 113a05d7a..44204fa18 100644 --- a/src/widgets/splits/SplitInput.cpp +++ b/src/widgets/splits/SplitInput.cpp @@ -130,7 +130,7 @@ void SplitInput::updateEmoteButton() { float scale = this->getScale(); - QString text = ""; + QString text = ""; text.replace("xD", QString::number(int(12 * scale))); if (this->theme->isLightTheme()) { @@ -254,7 +254,7 @@ void SplitInput::installKeyPressedEvent() } } else if (event->key() == Qt::Key_C && event->modifiers() == Qt::ControlModifier) { if (this->split_->view_.hasSelection()) { - this->split_->doCopy(); + this->split_->copyToClipboard(); event->accept(); } } diff --git a/src/widgets/splits/SplitOverlay.cpp b/src/widgets/splits/SplitOverlay.cpp index d6b504c6f..2bf27d4e9 100644 --- a/src/widgets/splits/SplitOverlay.cpp +++ b/src/widgets/splits/SplitOverlay.cpp @@ -29,12 +29,11 @@ SplitOverlay::SplitOverlay(Split *parent) layout->setColumnStretch(1, 1); layout->setColumnStretch(3, 1); - QPushButton *move = new QPushButton(getApp()->resources->split.move, QString()); - QPushButton *left = this->left_ = new QPushButton(getApp()->resources->split.left, QString()); - QPushButton *right = this->right_ = - new QPushButton(getApp()->resources->split.right, QString()); - QPushButton *up = this->up_ = new QPushButton(getApp()->resources->split.up, QString()); - QPushButton *down = this->down_ = new QPushButton(getApp()->resources->split.down, QString()); + auto *move = new QPushButton(getApp()->resources->split.move, QString()); + auto *left = this->left_ = new QPushButton(getApp()->resources->split.left, QString()); + auto *right = this->right_ = new QPushButton(getApp()->resources->split.right, QString()); + auto *up = this->up_ = new QPushButton(getApp()->resources->split.up, QString()); + auto *down = this->down_ = new QPushButton(getApp()->resources->split.down, QString()); move->setGraphicsEffect(new QGraphicsOpacityEffect(this)); left->setGraphicsEffect(new QGraphicsOpacityEffect(this)); diff --git a/weakOf b/weakOf new file mode 100644 index 000000000..e69de29bb From 7e3292e900fd241e6f7a0548341c9fe5dfa48e8c Mon Sep 17 00:00:00 2001 From: apa420 <17131426+apa420@users.noreply.github.com> Date: Mon, 6 Aug 2018 16:40:12 +0200 Subject: [PATCH 41/73] Dynamic lowercase link setting - did request (#612) * Added functionality of dynamic lowercase links * fixed fourtfs request, lajamerr has a better solution though * Fix matching and lowercasing of domain only. * Update TwitchMessageBuilder.cpp Added what fourtf asked in a good way NOTE: I removed a : from the regex since otherwise it would fuck up * fixed stuff and removed debugs * fixed stuff --- src/messages/MessageElement.hpp | 4 ++ src/providers/twitch/TwitchMessageBuilder.cpp | 45 +++++++++---------- src/singletons/Settings.hpp | 2 +- src/singletons/WindowManager.cpp | 2 + src/widgets/settingspages/LookPage.cpp | 11 +++-- 5 files changed, 37 insertions(+), 27 deletions(-) diff --git a/src/messages/MessageElement.hpp b/src/messages/MessageElement.hpp index 764101641..89f2f2f3c 100644 --- a/src/messages/MessageElement.hpp +++ b/src/messages/MessageElement.hpp @@ -96,6 +96,10 @@ public: BoldUsername = (1 << 27), NonBoldUsername = (1 << 28), + // for links + LowercaseLink = (1 << 29), + OriginalLink = (1 << 30), + Default = Timestamp | Badges | Username | BitsStatic | FfzEmoteImage | BttvEmoteImage | TwitchEmoteImage | BitsAmount | Text | AlwaysShow, }; diff --git a/src/providers/twitch/TwitchMessageBuilder.cpp b/src/providers/twitch/TwitchMessageBuilder.cpp index 4b75ecd5d..55d1d175e 100644 --- a/src/providers/twitch/TwitchMessageBuilder.cpp +++ b/src/providers/twitch/TwitchMessageBuilder.cpp @@ -220,35 +220,34 @@ MessagePtr TwitchMessageBuilder::build() Link link; if (linkString.isEmpty()) { - link = Link(); + if (string.startsWith('@')) { + this->emplace(string, TextElement::BoldUsername, textColor, + FontStyle::ChatMediumBold); + this->emplace(string, TextElement::NonBoldUsername, textColor); + } else { + this->emplace(string, TextElement::Text, textColor); + } } else { - if (app->settings->lowercaseLink) { - QRegularExpression httpRegex("\\bhttps?://", - QRegularExpression::CaseInsensitiveOption); - QRegularExpression ftpRegex("\\bftps?://", - QRegularExpression::CaseInsensitiveOption); - QRegularExpression getDomain("\\/\\/([^\\/]*)"); - QString tempString = string; + static QRegularExpression domainRegex( + R"(^(?:(?:ftp|http)s?:\/\/)?([^\/:]+)(?:\/.*)?$)", + QRegularExpression::CaseInsensitiveOption); - if (!string.contains(httpRegex)) { - if (!string.contains(ftpRegex)) { - tempString.insert(0, "http://"); - } - } - QString domain = getDomain.match(tempString).captured(1); - string.replace(domain, domain.toLower()); + QString lowercaseLinkString; + auto match = domainRegex.match(string); + if (match.isValid()) { + lowercaseLinkString = string.mid(0, match.capturedStart(1)) + + match.captured(1).toLower() + + string.mid(match.capturedEnd(1)); + } else { + lowercaseLinkString = string; } link = Link(Link::Url, linkString); + textColor = MessageColor(MessageColor::Link); - } - if (string.startsWith('@')) { - this->emplace(string, TextElement::BoldUsername, textColor, - FontStyle::ChatMediumBold) // + this->emplace(lowercaseLinkString, TextElement::LowercaseLink, + textColor) ->setLink(link); - this->emplace(string, TextElement::NonBoldUsername, textColor) // - ->setLink(link); - } else { - this->emplace(string, TextElement::Text, textColor) // + this->emplace(string, TextElement::OriginalLink, textColor) ->setLink(link); } diff --git a/src/singletons/Settings.hpp b/src/singletons/Settings.hpp index 1eda0e815..946904611 100644 --- a/src/singletons/Settings.hpp +++ b/src/singletons/Settings.hpp @@ -82,7 +82,7 @@ public: /// Links BoolSetting linksDoubleClickOnly = {"/links/doubleClickToOpen", false}; - BoolSetting lowercaseLink = {"/links/linkLowercase", true}; + BoolSetting enableLowercaseLink = {"/links/linkLowercase", true}; /// Ingored Users BoolSetting enableTwitchIgnoredUsers = {"/ignore/enableTwitchIgnoredUsers", true}; diff --git a/src/singletons/WindowManager.cpp b/src/singletons/WindowManager.cpp index 2e43f7d15..4a6483ea7 100644 --- a/src/singletons/WindowManager.cpp +++ b/src/singletons/WindowManager.cpp @@ -68,6 +68,7 @@ WindowManager::WindowManager() this->wordFlagsListener_.addSetting(settings->enableFfzEmotes); this->wordFlagsListener_.addSetting(settings->enableTwitchEmotes); this->wordFlagsListener_.addSetting(settings->enableUsernameBold); + this->wordFlagsListener_.addSetting(settings->enableLowercaseLink); this->wordFlagsListener_.cb = [this](auto) { this->updateWordTypeMask(); // }; @@ -111,6 +112,7 @@ void WindowManager::updateWordTypeMask() flags |= MEF::AlwaysShow; flags |= MEF::Collapsed; flags |= settings->enableUsernameBold ? MEF::BoldUsername : MEF::NonBoldUsername; + flags |= settings->enableLowercaseLink ? MEF::LowercaseLink : MEF::OriginalLink; // update flags MessageElement::Flags newFlags = static_cast(flags); diff --git a/src/widgets/settingspages/LookPage.cpp b/src/widgets/settingspages/LookPage.cpp index 6f556f025..919b726d5 100644 --- a/src/widgets/settingspages/LookPage.cpp +++ b/src/widgets/settingspages/LookPage.cpp @@ -141,7 +141,7 @@ void LookPage::addMessageTab(LayoutCreator layout) layout.emplace(false); // lowercase links - layout.append(this->createCheckBox("Lowercase domains", getSettings()->lowercaseLink)); + layout.append(this->createCheckBox("Lowercase domains", getSettings()->enableLowercaseLink)); // bold usernames layout.append(this->createCheckBox("Bold @usernames", getSettings()->enableUsernameBold)); @@ -286,7 +286,9 @@ ChannelPtr LookPage::createPreviewChannel() message->addElement(new TextElement("This is a preview message", MessageElement::Text)); message->addElement(new EmoteElement(EmoteData(getApp()->resources->pajaDank), MessageElement::Flags::AlwaysShow)); - // message->addElement(new) + message->addElement(new TextElement("@fourtf", TextElement::BoldUsername, + MessageColor::Text, FontStyle::ChatMediumBold)); + message->addElement(new TextElement("@fourtf", TextElement::NonBoldUsername)); channel->addMessage(message); } { @@ -300,7 +302,10 @@ ChannelPtr LookPage::createPreviewChannel() message->addElement( new EmoteElement(EmoteData(getApp()->resources->ppHop), MessageElement::BttvEmote)); message->addElement( - (new TextElement("www.fourtf.com", MessageElement::Text, MessageColor::Link)) + (new TextElement("www.fourtf.com", MessageElement::LowercaseLink, MessageColor::Link)) + ->setLink(Link(Link::Url, "https://www.fourtf.com"))); + message->addElement( + (new TextElement("wWw.FoUrTf.CoM", MessageElement::OriginalLink, MessageColor::Link)) ->setLink(Link(Link::Url, "https://www.fourtf.com"))); channel->addMessage(message); } From 82460557cb7401ae20b6d77e6ccd44705de7e6f4 Mon Sep 17 00:00:00 2001 From: apa420 <17131426+apa420@users.noreply.github.com> Date: Mon, 6 Aug 2018 16:40:46 +0200 Subject: [PATCH 42/73] 'ignore highlights' checkbox in user info popup - did request (#618) * Added 'ignore userhighlights' to userpopup * removed unecesarry include * Fixed the todo * renamed bool * Did request * changed some names --- src/widgets/dialogs/UserInfoPopup.cpp | 41 +++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/src/widgets/dialogs/UserInfoPopup.cpp b/src/widgets/dialogs/UserInfoPopup.cpp index edddede5e..a15c6239a 100644 --- a/src/widgets/dialogs/UserInfoPopup.cpp +++ b/src/widgets/dialogs/UserInfoPopup.cpp @@ -3,6 +3,7 @@ #include "Application.hpp" #include "common/NetworkRequest.hpp" #include "controllers/accounts/AccountController.hpp" +#include "controllers/highlights/HighlightController.hpp" #include "providers/twitch/PartialTwitchUser.hpp" #include "providers/twitch/TwitchChannel.hpp" #include "singletons/Resources.hpp" @@ -227,6 +228,31 @@ void UserInfoPopup::installEvents() }); } }); + + // ignore highlights + QObject::connect(this->ui_.ignoreHighlights, &QCheckBox::clicked, [this](bool checked) mutable { + this->ui_.ignoreHighlights->setEnabled(false); + + if (checked) { + getApp()->highlights->blacklistedUsers.insertItem( + HighlightBlacklistUser{this->userName_, false}); + this->ui_.ignoreHighlights->setEnabled(true); + } else { + const auto &vector = getApp()->highlights->blacklistedUsers.getVector(); + + for (int i = 0; i < vector.size(); i++) { + if (this->userName_ == vector[i].getPattern()) { + getApp()->highlights->blacklistedUsers.removeItem(i); + i--; + } + } + if (getApp()->highlights->blacklistContains(this->userName_)) { + this->ui_.ignoreHighlights->setToolTip("Name matched by regex"); + } else { + this->ui_.ignoreHighlights->setEnabled(true); + } + } + }); } void UserInfoPopup::setData(const QString &name, const ChannelPtr &channel) @@ -290,8 +316,23 @@ void UserInfoPopup::updateUserData() } } + // get ignoreHighlights state + bool isIgnoringHighlights = false; + const auto &vector = getApp()->highlights->blacklistedUsers.getVector(); + for (int i = 0; i < vector.size(); i++) { + if (this->userName_ == vector[i].getPattern()) { + isIgnoringHighlights = true; + break; + } + } + if (getApp()->highlights->blacklistContains(this->userName_) && !isIgnoringHighlights) { + this->ui_.ignoreHighlights->setToolTip("Name matched by regex"); + } else { + this->ui_.ignoreHighlights->setEnabled(true); + } this->ui_.ignore->setEnabled(true); this->ui_.ignore->setChecked(isIgnoring); + this->ui_.ignoreHighlights->setChecked(isIgnoringHighlights); }; PartialTwitchUser::byName(this->userName_).getId(onIdFetched, this); From c6cfb548f5e08fdb589335aee7afc6b8a20b67c7 Mon Sep 17 00:00:00 2001 From: apa420 <17131426+apa420@users.noreply.github.com> Date: Mon, 6 Aug 2018 16:41:27 +0200 Subject: [PATCH 43/73] added functionality of 'boldness-slider' #647 (#650) --- src/singletons/Fonts.cpp | 18 +++++++++-- src/singletons/Settings.hpp | 1 + src/widgets/settingspages/LookPage.cpp | 42 ++++++++++++++++++++++++++ src/widgets/settingspages/LookPage.hpp | 1 + 4 files changed, 60 insertions(+), 2 deletions(-) diff --git a/src/singletons/Fonts.cpp b/src/singletons/Fonts.cpp index 70aaa93b6..272a2b1b0 100644 --- a/src/singletons/Fonts.cpp +++ b/src/singletons/Fonts.cpp @@ -56,6 +56,19 @@ void Fonts::initialize(Application &app) } this->fontChanged.invoke(); }); + + getSettings()->boldScale.connect([this, &app](const int &, auto) { + assertInGuiThread(); + + if (app.windows) { + app.windows->incGeneration(); + } + + for (auto &map : this->fontsByType_) { + map.clear(); + } + this->fontChanged.invoke(); + }); } QFont Fonts::getFont(Fonts::Type type, float scale) @@ -99,12 +112,13 @@ Fonts::FontData Fonts::createFontData(Type type, float scale) {ChatSmall, {0.6f, false, QFont::Normal}}, {ChatMediumSmall, {0.8f, false, QFont::Normal}}, {ChatMedium, {1, false, QFont::Normal}}, - {ChatMediumBold, {1, false, QFont::Medium}}, + {ChatMediumBold, {1, false, QFont::Weight(getApp()->settings->boldScale.getValue())}}, {ChatMediumItalic, {1, true, QFont::Normal}}, {ChatLarge, {1.2f, false, QFont::Normal}}, {ChatVeryLarge, {1.4f, false, QFont::Normal}}, }; - + sizeScale[ChatMediumBold] = {1, false, + QFont::Weight(getApp()->settings->boldScale.getValue())}; auto data = sizeScale[type]; return FontData(QFont(QString::fromStdString(this->chatFontFamily.getValue()), int(this->chatFontSize.getValue() * data.scale * scale), data.weight, diff --git a/src/singletons/Settings.hpp b/src/singletons/Settings.hpp index 946904611..0bb92b966 100644 --- a/src/singletons/Settings.hpp +++ b/src/singletons/Settings.hpp @@ -36,6 +36,7 @@ public: BoolSetting alternateMessageBackground = {"/appearance/messages/alternateMessageBackground", false}; IntSetting uiScale = {"/appearance/uiScale", 0}; + IntSetting boldScale = {"/appearance/boldScale", 57}; BoolSetting windowTopMost = {"/appearance/windowAlwaysOnTop", false}; BoolSetting showTabCloseButton = {"/appearance/showTabCloseButton", true}; BoolSetting hidePreferencesButton = {"/appearance/hidePreferencesButton", false}; diff --git a/src/widgets/settingspages/LookPage.cpp b/src/widgets/settingspages/LookPage.cpp index 919b726d5..ad1649740 100644 --- a/src/widgets/settingspages/LookPage.cpp +++ b/src/widgets/settingspages/LookPage.cpp @@ -113,6 +113,12 @@ void LookPage::addMessageTab(LayoutCreator layout) // font layout.append(this->createFontChanger()); + // bold-slider + { + auto box = layout.emplace().withoutMargin(); + box.emplace("Boldness: "); + box.append(this->createBoldScaleSlider()); + } // -- layout.emplace(false); @@ -419,4 +425,40 @@ QLayout *LookPage::createUiScaleSlider() return layout; } +QLayout *LookPage::createBoldScaleSlider() +{ + auto layout = new QHBoxLayout(); + auto slider = new QSlider(Qt::Horizontal); + auto label = new QLabel(); + + layout->addWidget(slider); + layout->addWidget(label); + + slider->setMinimum(50); + slider->setMaximum(100); + slider->setValue(getSettings()->boldScale.getValue()); + + label->setMinimumWidth(100); + + QObject::connect(slider, &QSlider::valueChanged, + [](auto value) { getSettings()->boldScale.setValue(value); }); + // show value + // getSettings()->boldScale.connect( + // [label](auto, auto) { + // label->setText(QString::number(getSettings()->boldScale.getValue())); + // }, + // this->connections_); + + QPushButton *button = new QPushButton("Reset"); + layout->addWidget(button); + button->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Policy::Fixed); + + QObject::connect(button, &QPushButton::clicked, [=]() { + getSettings()->boldScale.setValue(57); + slider->setValue(57); + }); + + return layout; +} + } // namespace chatterino diff --git a/src/widgets/settingspages/LookPage.hpp b/src/widgets/settingspages/LookPage.hpp index 0c8bd4a6e..972271f6f 100644 --- a/src/widgets/settingspages/LookPage.hpp +++ b/src/widgets/settingspages/LookPage.hpp @@ -28,6 +28,7 @@ private: QLayout *createThemeColorChanger(); QLayout *createFontChanger(); QLayout *createUiScaleSlider(); + QLayout *createBoldScaleSlider(); ChannelPtr createPreviewChannel(); From 43ba4085684340dbb6351382fafd89063b4c1070 Mon Sep 17 00:00:00 2001 From: apa420 <17131426+apa420@users.noreply.github.com> Date: Mon, 6 Aug 2018 16:42:23 +0200 Subject: [PATCH 44/73] Added functionality of customizable split headers #637 (#648) * Added functionality of customizable split headers * removed empty line --- src/singletons/Settings.hpp | 6 ++++++ src/widgets/settingspages/LookPage.cpp | 12 +++++++++++- src/widgets/settingspages/LookPage.hpp | 1 + src/widgets/splits/SplitHeader.cpp | 26 ++++++++++++++++++++++++++ 4 files changed, 44 insertions(+), 1 deletion(-) diff --git a/src/singletons/Settings.hpp b/src/singletons/Settings.hpp index 0bb92b966..5816d5ee7 100644 --- a/src/singletons/Settings.hpp +++ b/src/singletons/Settings.hpp @@ -45,6 +45,12 @@ public: BoolSetting enableSmoothScrollingNewMessages = {"/appearance/smoothScrollingNewMessages", false}; BoolSetting enableUsernameBold = {"/appearence/messages/boldUsernames", false}; + // BoolSetting customizable splitheader + BoolSetting showViewerCount = {"/appearance/splitheader/showViewerCount", false}; + BoolSetting showTitle = {"/appearance/splitheader/showTitle", false}; + BoolSetting showGame = {"/appearance/splitheader/showGame", false}; + BoolSetting showUptime = {"/appearance/splitheader/showUptime", false}; + // BoolSetting useCustomWindowFrame = {"/appearance/useCustomWindowFrame", false}; /// Behaviour diff --git a/src/widgets/settingspages/LookPage.cpp b/src/widgets/settingspages/LookPage.cpp index ad1649740..56d59beb6 100644 --- a/src/widgets/settingspages/LookPage.cpp +++ b/src/widgets/settingspages/LookPage.cpp @@ -53,6 +53,7 @@ void LookPage::initializeUi() this->addInterfaceTab(tabs.appendTab(new QVBoxLayout, "Interface")); this->addMessageTab(tabs.appendTab(new QVBoxLayout, "Messages")); this->addEmoteTab(tabs.appendTab(new QVBoxLayout, "Emotes")); + this->addSplitHeaderTab(tabs.appendTab(new QVBoxLayout, "Split header")); layout->addStretch(1); @@ -104,7 +105,6 @@ void LookPage::addInterfaceTab(LayoutCreator layout) layout.append(this->createCheckBox(INPUT_EMPTY, getSettings()->showEmptyInput)); layout.append( this->createCheckBox("Show message length while typing", getSettings()->showMessageLength)); - layout->addStretch(1); } @@ -239,6 +239,16 @@ void LookPage::addEmoteTab(LayoutCreator layout) layout->addStretch(1); } +void LookPage::addSplitHeaderTab(LayoutCreator layout) +{ + layout.append(this->createCheckBox("Show viewer count", getSettings()->showViewerCount)); + layout.append(this->createCheckBox("Show title", getSettings()->showTitle)); + layout.append(this->createCheckBox("Show game", getSettings()->showGame)); + layout.append(this->createCheckBox("Show uptime", getSettings()->showUptime)); + + layout->addStretch(1); +} + void LookPage::addLastReadMessageIndicatorPatternSelector(LayoutCreator layout) { // combo diff --git a/src/widgets/settingspages/LookPage.hpp b/src/widgets/settingspages/LookPage.hpp index 972271f6f..3b3a4f264 100644 --- a/src/widgets/settingspages/LookPage.hpp +++ b/src/widgets/settingspages/LookPage.hpp @@ -22,6 +22,7 @@ private: void addInterfaceTab(LayoutCreator layout); void addMessageTab(LayoutCreator layout); void addEmoteTab(LayoutCreator layout); + void addSplitHeaderTab(LayoutCreator layout); void addLastReadMessageIndicatorPatternSelector(LayoutCreator layout); diff --git a/src/widgets/splits/SplitHeader.cpp b/src/widgets/splits/SplitHeader.cpp index 8d6b92676..9c45383e4 100644 --- a/src/widgets/splits/SplitHeader.cpp +++ b/src/widgets/splits/SplitHeader.cpp @@ -101,6 +101,20 @@ SplitHeader::SplitHeader(Split *_split) this->addModeActions(this->modeMenu_); this->setMouseTracking(true); + + // Update title on title-settings-change + getSettings()->showViewerCount.connect( + [this](const auto &, const auto &) { this->updateChannelText(); }, + this->managedConnections_); + getSettings()->showTitle.connect( + [this](const auto &, const auto &) { this->updateChannelText(); }, + this->managedConnections_); + getSettings()->showGame.connect( + [this](const auto &, const auto &) { this->updateChannelText(); }, + this->managedConnections_); + getSettings()->showUptime.connect( + [this](const auto &, const auto &) { this->updateChannelText(); }, + this->managedConnections_); } SplitHeader::~SplitHeader() @@ -331,6 +345,18 @@ void SplitHeader::updateChannelText() } else { title += " (live)"; } + if (getSettings()->showViewerCount) { + title += " - " + QString::number(streamStatus->viewerCount) + " viewers"; + } + if (getSettings()->showTitle) { + title += " - " + streamStatus->title; + } + if (getSettings()->showGame) { + title += " - " + streamStatus->game; + } + if (getSettings()->showUptime) { + title += " - uptime: " + streamStatus->uptime; + } } else { this->tooltip_ = QString(); } From 35d462d1f1b992c9b852a4bd57f658bd116dbdf2 Mon Sep 17 00:00:00 2001 From: fourtf Date: Mon, 6 Aug 2018 18:25:47 +0200 Subject: [PATCH 45/73] worked on Image --- chatterino.pro | 7 +- src/common/Common.hpp | 8 - src/common/NullablePtr.hpp | 2 +- src/common/UniqueAccess.hpp | 36 +- src/messages/Image.cpp | 368 ++++++++---------- src/messages/Image.hpp | 92 ++--- src/messages/ImageSet.cpp | 4 +- src/messages/Link.hpp | 1 + src/messages/MessageElement.cpp | 17 +- src/messages/layouts/MessageLayoutElement.cpp | 8 +- src/providers/twitch/IrcMessageHandler.cpp | 13 +- src/providers/twitch/TwitchChannel.cpp | 8 +- src/providers/twitch/TwitchChannel.hpp | 4 +- src/singletons/Updates.cpp | 12 +- src/widgets/helper/ChannelView.cpp | 6 +- 15 files changed, 284 insertions(+), 302 deletions(-) diff --git a/chatterino.pro b/chatterino.pro index 8d477fc61..61f797d0b 100644 --- a/chatterino.pro +++ b/chatterino.pro @@ -9,6 +9,7 @@ message(----) QT += widgets core gui network multimedia svg CONFIG += communi COMMUNI += core model util +CONFIG += c++14 INCLUDEPATH += src/ TARGET = chatterino TEMPLATE = app @@ -16,12 +17,6 @@ DEFINES += QT_DEPRECATED_WARNINGS PRECOMPILED_HEADER = src/PrecompiledHeader.hpp CONFIG += precompile_header -win32-msvc* { - QMAKE_CXXFLAGS = /std=c++17 -} else { - QMAKE_CXXFLAGS = -std=c++17 -} - debug { DEFINES += QT_DEBUG } diff --git a/src/common/Common.hpp b/src/common/Common.hpp index 18548cb3b..c4abe043f 100644 --- a/src/common/Common.hpp +++ b/src/common/Common.hpp @@ -37,12 +37,4 @@ std::weak_ptr weakOf(T *element) return element->shared_from_this(); } -template -struct overloaded : Ts... { - using Ts::operator()...; -}; - -template -overloaded(Ts...)->overloaded; - } // namespace chatterino diff --git a/src/common/NullablePtr.hpp b/src/common/NullablePtr.hpp index ba08bb7f9..fb60af0d8 100644 --- a/src/common/NullablePtr.hpp +++ b/src/common/NullablePtr.hpp @@ -25,7 +25,7 @@ public: return element_; } - typename std::add_lvalue_reference::type &operator*() const + typename std::add_lvalue_reference::type operator*() const { assert(this->hasElement()); diff --git a/src/common/UniqueAccess.hpp b/src/common/UniqueAccess.hpp index ef7f0165f..529ac0623 100644 --- a/src/common/UniqueAccess.hpp +++ b/src/common/UniqueAccess.hpp @@ -1,47 +1,61 @@ #pragma once -#include #include #include namespace chatterino { template -class AccessGuard : boost::noncopyable +class AccessGuard { public: AccessGuard(T &element, std::mutex &mutex) - : element_(element) - , mutex_(mutex) + : element_(&element) + , mutex_(&mutex) { - this->mutex_.lock(); + this->mutex_->lock(); + } + + AccessGuard(AccessGuard &&other) + : element_(other.element_) + , mutex_(other.mutex_) + { + other.isValid_ = false; + } + + AccessGuard &operator=(AccessGuard &&other) + { + other.isValid_ = false; + this->element_ = other.element_; + this->mutex_ = other.element_; } ~AccessGuard() { - this->mutex_.unlock(); + if (this->isValid_) this->mutex_->unlock(); } T *operator->() const { - return &this->element_; + return this->element_; } T &operator*() const { - return this->element_; + return *this->element_; } private: - T &element_; - std::mutex &mutex_; + T *element_; + std::mutex *mutex_; + bool isValid_ = true; }; template class UniqueAccess { public: -// template + // template UniqueAccess() : element_(T()) { diff --git a/src/messages/Image.cpp b/src/messages/Image.cpp index b1fcf8787..309d2f0a0 100644 --- a/src/messages/Image.cpp +++ b/src/messages/Image.cpp @@ -19,13 +19,130 @@ #include namespace chatterino { +namespace { +// Frame +Frame::Frame(const QPixmap *nonOwning, int duration) + : nonOwning_(nonOwning) + , duration_(duration) +{ +} + +Frame::Frame(std::unique_ptr owning, int duration) + : owning_(std::move(owning)) + , duration_(duration) +{ +} + +int Frame::duration() const +{ + return this->duration_; +} + +const QPixmap *Frame::pixmap() const +{ + if (this->nonOwning_) return this->nonOwning_; + return this->owning_.get(); +} + +// Frames +Frames::Frames() +{ + DebugCount::increase("images"); +} + +Frames::Frames(std::vector &&frames) + : items_(std::move(frames)) +{ + DebugCount::increase("images"); + if (this->animated()) DebugCount::increase("animated images"); +} + +Frames::~Frames() +{ + DebugCount::decrease("images"); + if (this->animated()) DebugCount::decrease("animated images"); +} + +void Frames::advance() +{ + this->timeOffset_ += GIF_FRAME_LENGTH; + + while (true) { + this->index_ %= this->items_.size(); + if (this->timeOffset_ > this->items_[this->index_].duration()) { + this->timeOffset_ -= this->items_[this->index_].duration(); + this->index_ = (this->index_ + 1) % this->items_.size(); + } else { + break; + } + } +} + +bool Frames::animated() const +{ + return this->items_.size() > 1; +} + +const QPixmap *Frames::current() const +{ + if (this->items_.size() == 0) return nullptr; + return this->items_[this->index_].pixmap(); +} + +const QPixmap *Frames::first() const +{ + if (this->items_.size() == 0) return nullptr; + return this->items_.front().pixmap(); +} + +// functions +std::vector readFrames(QImageReader &reader, const Url &url) +{ + std::vector frames; + + if (reader.imageCount() <= 0) { + Log("Error while reading image {}: '{}'", url.string, reader.errorString()); + return frames; + } + + QImage image; + for (int index = 0; index < reader.imageCount(); ++index) { + if (reader.read(&image)) { + auto pixmap = std::make_unique(QPixmap::fromImage(image)); + + int duration = std::max(20, reader.nextImageDelay()); + frames.push_back(Frame(std::move(pixmap), duration)); + } + } + + if (frames.size() != 0) { + Log("Error while reading image {}: '{}'", url.string, reader.errorString()); + } + + return frames; +} + +void queueLoadedEvent() +{ + static auto eventQueued = false; + + if (!eventQueued) { + eventQueued = true; + + QTimer::singleShot(250, [] { + getApp()->windows->incGeneration(); + getApp()->windows->layoutChannelViews(); + eventQueued = false; + }); + } +} +} // namespace // IMAGE2 std::atomic Image::loadedEventQueued{false}; ImagePtr Image::fromUrl(const Url &url, qreal scale) { - // herb sutter cache static std::unordered_map> cache; static std::mutex mutex; @@ -59,237 +176,123 @@ ImagePtr Image::getEmpty() } Image::Image() + : empty_(true) { - this->isLoaded_ = true; - this->isNull_ = true; } Image::Image(const Url &url, qreal scale) + : url_(url) + , scale_(scale) + , shouldLoad_(true) { - this->url_ = url; - this->scale_ = scale; - - if (url.string.isEmpty()) { - this->isLoaded_ = true; - this->isNull_ = true; - } } Image::Image(std::unique_ptr owning, qreal scale) + : scale_(scale) { - this->frames_.push_back(Frame(std::move(owning))); - this->scale_ = scale; - this->isLoaded_ = true; - this->currentFramePixmap_ = this->frames_.front().getPixmap(); + std::vector vec; + vec.push_back(Frame(std::move(owning))); + this->frames_ = std::move(vec); } Image::Image(QPixmap *nonOwning, qreal scale) + : scale_(scale) { - this->frames_.push_back(Frame(nonOwning)); - this->scale_ = scale; - this->isLoaded_ = true; - this->currentFramePixmap_ = this->frames_.front().getPixmap(); + std::vector vec; + vec.push_back(Frame(nonOwning)); + this->frames_ = std::move(vec); } -const Url &Image::getUrl() const +const Url &Image::url() const { return this->url_; } -NullablePtr Image::getPixmap() const +const QPixmap *Image::pixmap() const { assertInGuiThread(); - if (!this->isLoaded_) { + if (this->shouldLoad_) { + const_cast(this)->shouldLoad_ = false; const_cast(this)->load(); } - return this->currentFramePixmap_; + return this->frames_.current(); } -qreal Image::getScale() const +qreal Image::scale() const { return this->scale_; } -bool Image::isAnimated() const +bool Image::empty() const { - return this->isAnimated_; + return this->empty_; } -int Image::getWidth() const +bool Image::animated() const { - if (!this->isLoaded_) return 16; + assertInGuiThread(); - return this->frames_.front().getPixmap()->width() * this->scale_; + return this->frames_.animated(); } -int Image::getHeight() const +int Image::width() const { - if (!this->isLoaded_) return 16; + assertInGuiThread(); - return this->frames_.front().getPixmap()->height() * this->scale_; + if (auto pixmap = this->frames_.first()) + return pixmap->width() * this->scale_; + else + return 16; } -bool Image::isLoaded() const +int Image::height() const { - return this->isLoaded_; -} + assertInGuiThread(); -bool Image::isValid() const -{ - return !this->isNull_; -} - -bool Image::isNull() const -{ - return this->isNull_; + if (auto pixmap = this->frames_.first()) + return pixmap->height() * this->scale_; + else + return 16; } void Image::load() { - // decrease debug count - if (this->isAnimated_) { - DebugCount::decrease("animated images"); - } - if (this->isLoaded_) { - DebugCount::decrease("loaded images"); - } - - this->isLoaded_ = false; - this->isLoading_ = true; - this->frames_.clear(); - - NetworkRequest req(this->getUrl().string); + NetworkRequest req(this->url().string); req.setCaller(&this->object_); req.setUseQuickLoadCache(true); req.onSuccess([this, weak = weakOf(this)](auto result) -> Outcome { + assertInGuiThread(); + auto shared = weak.lock(); if (!shared) return Failure; - auto &bytes = result.getData(); - QByteArray copy = QByteArray::fromRawData(bytes.constData(), bytes.length()); + // const cast since we are only reading from it + QBuffer buffer(const_cast(&result.getData())); + buffer.open(QIODevice::ReadOnly); + QImageReader reader(&buffer); - return this->parse(result.getData()); + this->frames_ = readFrames(reader, this->url()); + return Success; + }); + req.onError([this, weak = weakOf(this)](int) { + auto shared = weak.lock(); + if (!shared) return false; + + this->frames_ = std::vector(); + + return false; }); req.execute(); } -Outcome Image::parse(const QByteArray &data) -{ - // const cast since we are only reading from it - QBuffer buffer(const_cast(&data)); - buffer.open(QIODevice::ReadOnly); - QImageReader reader(&buffer); - - return this->setFrames(this->readFrames(reader)); -} - -std::vector Image::readFrames(QImageReader &reader) -{ - std::vector frames; - - if (reader.imageCount() <= 0) { - Log("Error while reading image {}: '{}'", this->url_.string, reader.errorString()); - return frames; - } - - QImage image; - for (int index = 0; index < reader.imageCount(); ++index) { - if (reader.read(&image)) { - auto pixmap = new QPixmap(QPixmap::fromImage(image)); - - int duration = std::max(20, reader.nextImageDelay()); - frames.push_back(Image::Frame(pixmap, duration)); - } - } - - if (frames.size() != 0) { - Log("Error while reading image {}: '{}'", this->url_.string, reader.errorString()); - } - - return frames; -} - -Outcome Image::setFrames(std::vector frames) -{ - std::lock_guard lock(this->framesMutex_); - - if (frames.size() > 0) { - this->currentFramePixmap_ = frames.front().getPixmap(); - - if (frames.size() > 1) { - if (!this->isAnimated_) { - getApp()->emotes->gifTimer.signal.connect([=]() { this->updateAnimation(); }); - } - - this->isAnimated_ = true; - DebugCount::increase("animated images"); - } - - this->isLoaded_ = true; - DebugCount::increase("loaded images"); - - return Success; - } - - this->frames_ = std::move(frames); - this->queueLoadedEvent(); - - return Failure; -} - -void Image::queueLoadedEvent() -{ - if (!loadedEventQueued) { - loadedEventQueued = true; - - QTimer::singleShot(250, [] { - getApp()->windows->incGeneration(); - getApp()->windows->layoutChannelViews(); - loadedEventQueued = false; - }); - } -} - -void Image::updateAnimation() -{ - if (this->isAnimated_) { - std::lock_guard lock(this->framesMutex_); - - this->currentFrameOffset_ += GIF_FRAME_LENGTH; - - while (true) { - this->currentFrameIndex_ %= this->frames_.size(); - if (this->currentFrameOffset_ > this->frames_[this->currentFrameIndex_].getDuration()) { - this->currentFrameOffset_ -= this->frames_[this->currentFrameIndex_].getDuration(); - this->currentFrameIndex_ = (this->currentFrameIndex_ + 1) % this->frames_.size(); - } else { - break; - } - } - - this->currentFramePixmap_ = this->frames_[this->currentFrameIndex_].getPixmap(); - } -} - bool Image::operator==(const Image &other) const { - if (this->isNull() && other.isNull()) { - return true; - } - - if (!this->url_.string.isEmpty() && this->url_ == other.url_) { - return true; - } - - assert(this->frames_.size() == 1); - assert(other.frames_.size() == 1); - - if (this->currentFramePixmap_ == other.currentFramePixmap_) { - return true; - } + if (this->empty() && other.empty()) return true; + if (!this->url_.string.isEmpty() && this->url_ == other.url_) return true; + if (this->frames_.first() == other.frames_.first()) return true; return false; } @@ -299,29 +302,4 @@ bool Image::operator!=(const Image &other) const return !this->operator==(other); } -// FRAME -Image::Frame::Frame(QPixmap *nonOwning, int duration) - : nonOwning_(nonOwning) - , duration_(duration) -{ -} - -Image::Frame::Frame(std::unique_ptr nonOwning, int duration) - : owning_(std::move(nonOwning)) - , duration_(duration) -{ -} - -int Image::Frame::getDuration() const -{ - return this->duration_; -} - -QPixmap *Image::Frame::getPixmap() const -{ - if (this->nonOwning_) return this->nonOwning_; - - return this->owning_.get(); -} - } // namespace chatterino diff --git a/src/messages/Image.hpp b/src/messages/Image.hpp index eec7ea9a9..7db43580c 100644 --- a/src/messages/Image.hpp +++ b/src/messages/Image.hpp @@ -12,6 +12,41 @@ #include "common/NullablePtr.hpp" namespace chatterino { +namespace { +class Frame +{ +public: + explicit Frame(const QPixmap *nonOwning, int duration = 1); + explicit Frame(std::unique_ptr owning, int duration = 1); + + const QPixmap *pixmap() const; + int duration() const; + +private: + const QPixmap *nonOwning_{nullptr}; + std::unique_ptr owning_{}; + int duration_{}; +}; +class Frames +{ +public: + Frames(); + Frames(std::vector &&frames); + ~Frames(); + Frames(Frames &&other) = default; + Frames &operator=(Frames &&other) = default; + + bool animated() const; + void advance(); + const QPixmap *current() const; + const QPixmap *first() const; + +private: + std::vector items_; + int index_{0}; + int timeOffset_{0}; +}; +} // namespace class Image; using ImagePtr = std::shared_ptr; @@ -24,62 +59,31 @@ public: static ImagePtr fromNonOwningPixmap(QPixmap *pixmap, qreal scale = 1); static ImagePtr getEmpty(); - const Url &getUrl() const; - NullablePtr getPixmap() const; - qreal getScale() const; - bool isAnimated() const; - int getWidth() const; - int getHeight() const; - bool isLoaded() const; - bool isError() const; - bool isValid() const; - bool isNull() const; + const Url &url() const; + const QPixmap *pixmap() const; + qreal scale() const; + bool empty() const; + int width() const; + int height() const; + bool animated() const; bool operator==(const Image &image) const; bool operator!=(const Image &image) const; private: - class Frame - { - public: - QPixmap *getPixmap() const; - int getDuration() const; - - Frame(QPixmap *nonOwning, int duration = 1); - Frame(std::unique_ptr nonOwning, int duration = 1); - - private: - QPixmap *nonOwning_; - std::unique_ptr owning_; - int duration_; - }; - Image(); Image(const Url &url, qreal scale); Image(std::unique_ptr owning, qreal scale); Image(QPixmap *nonOwning, qreal scale); void load(); - Outcome parse(const QByteArray &data); - std::vector readFrames(QImageReader &reader); - Outcome setFrames(std::vector frames); - void updateAnimation(); - void queueLoadedEvent(); - Url url_; - bool isLoaded_{false}; - bool isLoading_{false}; - bool isAnimated_{false}; - bool isError_{false}; - bool isNull_ = false; - qreal scale_ = 1; - QObject object_; - - std::vector frames_; - std::mutex framesMutex_; - NullablePtr currentFramePixmap_; - int currentFrameIndex_ = 0; - int currentFrameOffset_ = 0; + Url url_{}; + qreal scale_{1}; + bool empty_{false}; + bool shouldLoad_{false}; + Frames frames_{}; + QObject object_{}; static std::atomic loadedEventQueued; }; diff --git a/src/messages/ImageSet.cpp b/src/messages/ImageSet.cpp index 0a3e60802..ebb9b77c7 100644 --- a/src/messages/ImageSet.cpp +++ b/src/messages/ImageSet.cpp @@ -70,11 +70,11 @@ const ImagePtr &ImageSet::getImage(float scale) const scale = 1; } - if (this->imageX3_->isValid() && quality == 3) { + if (!this->imageX3_->empty() && quality == 3) { return this->imageX3_; } - if (this->imageX2_->isValid() && quality == 2) { + if (!this->imageX2_->empty() && quality == 2) { return this->imageX3_; } diff --git a/src/messages/Link.hpp b/src/messages/Link.hpp index a07d21cf3..a6c503540 100644 --- a/src/messages/Link.hpp +++ b/src/messages/Link.hpp @@ -1,6 +1,7 @@ #pragma once #include +#include namespace chatterino { diff --git a/src/messages/MessageElement.cpp b/src/messages/MessageElement.cpp index ed29957b9..ff62aca4e 100644 --- a/src/messages/MessageElement.cpp +++ b/src/messages/MessageElement.cpp @@ -70,8 +70,8 @@ ImageElement::ImageElement(ImagePtr image, MessageElement::Flags flags) void ImageElement::addToContainer(MessageLayoutContainer &container, MessageElement::Flags flags) { if (flags & this->getFlags()) { - auto size = QSize(this->image_->getWidth() * container.getScale(), - this->image_->getHeight() * container.getScale()); + auto size = QSize(this->image_->width() * container.getScale(), + this->image_->height() * container.getScale()); container.addElement( (new ImageLayoutElement(*this, this->image_, size))->setLink(this->getLink())); @@ -83,10 +83,7 @@ EmoteElement::EmoteElement(const EmotePtr &emote, MessageElement::Flags flags) : MessageElement(flags) , emote_(emote) { - auto image = emote->images.getImage1(); - if (image->isValid()) { - this->textElement_.reset(new TextElement(emote->getCopyString(), MessageElement::Misc)); - } + this->textElement_.reset(new TextElement(emote->getCopyString(), MessageElement::Misc)); this->setTooltip(emote->tooltip.string); } @@ -101,10 +98,10 @@ void EmoteElement::addToContainer(MessageLayoutContainer &container, MessageElem if (flags & this->getFlags()) { if (flags & MessageElement::EmoteImages) { auto image = this->emote_->images.getImage(container.getScale()); - if (!image->isValid()) return; + if (image->empty()) return; - QSize size(int(container.getScale() * image->getWidth()), - int(container.getScale() * image->getHeight())); + auto size = QSize(int(container.getScale() * image->width()), + int(container.getScale() * image->height())); container.addElement( (new ImageLayoutElement(*this, image, size))->setLink(this->getLink())); @@ -123,7 +120,7 @@ TextElement::TextElement(const QString &text, MessageElement::Flags flags, , color_(color) , style_(style) { - for (QString word : text.split(' ')) { + for (const auto &word : text.split(' ')) { this->words_.push_back({word, -1}); // fourtf: add logic to store multiple spaces after message } diff --git a/src/messages/layouts/MessageLayoutElement.cpp b/src/messages/layouts/MessageLayoutElement.cpp index 6d253e7a5..3e2c649d2 100644 --- a/src/messages/layouts/MessageLayoutElement.cpp +++ b/src/messages/layouts/MessageLayoutElement.cpp @@ -91,8 +91,8 @@ void ImageLayoutElement::paint(QPainter &painter) return; } - auto pixmap = this->image_->getPixmap(); - if (pixmap && !this->image_->isAnimated()) { + auto pixmap = this->image_->pixmap(); + if (pixmap && !this->image_->animated()) { // fourtf: make it use qreal values painter.drawPixmap(QRectF(this->getRect()), *pixmap, QRectF()); } @@ -104,8 +104,8 @@ void ImageLayoutElement::paintAnimated(QPainter &painter, int yOffset) return; } - if (this->image_->isAnimated()) { - if (auto pixmap = this->image_->getPixmap()) { + if (this->image_->animated()) { + if (auto pixmap = this->image_->pixmap()) { auto rect = this->getRect(); rect.moveTop(rect.y() + yOffset); painter.drawPixmap(QRectF(rect), *pixmap, QRectF()); diff --git a/src/providers/twitch/IrcMessageHandler.cpp b/src/providers/twitch/IrcMessageHandler.cpp index cd02c94ef..7460177ab 100644 --- a/src/providers/twitch/IrcMessageHandler.cpp +++ b/src/providers/twitch/IrcMessageHandler.cpp @@ -98,23 +98,24 @@ void IrcMessageHandler::handleRoomStateMessage(Communi::IrcMessage *message) // Room modes { - auto roomModes = twitchChannel->accessRoomModes(); + auto roomModes = *twitchChannel->accessRoomModes(); if ((it = tags.find("emote-only")) != tags.end()) { - roomModes->emoteOnly = it.value() == "1"; + roomModes.emoteOnly = it.value() == "1"; } if ((it = tags.find("subs-only")) != tags.end()) { - roomModes->submode = it.value() == "1"; + roomModes.submode = it.value() == "1"; } if ((it = tags.find("slow")) != tags.end()) { - roomModes->slowMode = it.value().toInt(); + roomModes.slowMode = it.value().toInt(); } if ((it = tags.find("r9k")) != tags.end()) { - roomModes->r9k = it.value() == "1"; + roomModes.r9k = it.value() == "1"; } if ((it = tags.find("broadcaster-lang")) != tags.end()) { - roomModes->broadcasterLang = it.value().toString(); + roomModes.broadcasterLang = it.value().toString(); } + twitchChannel->setRoomModes(roomModes); } twitchChannel->roomModesChanged.invoke(); diff --git a/src/providers/twitch/TwitchChannel.cpp b/src/providers/twitch/TwitchChannel.cpp index ea1d0e3ae..419a9a4a0 100644 --- a/src/providers/twitch/TwitchChannel.cpp +++ b/src/providers/twitch/TwitchChannel.cpp @@ -230,9 +230,9 @@ void TwitchChannel::setRoomId(const QString &id) this->loadRecentMessages(); } -const AccessGuard TwitchChannel::accessRoomModes() const +AccessGuard TwitchChannel::accessRoomModes() const { - return this->roomModes_.access(); + return this->roomModes_.accessConst(); } void TwitchChannel::setRoomModes(const RoomModes &_roomModes) @@ -247,9 +247,9 @@ bool TwitchChannel::isLive() const return this->streamStatus_.access()->live; } -const AccessGuard TwitchChannel::accessStreamStatus() const +AccessGuard TwitchChannel::accessStreamStatus() const { - return this->streamStatus_.access(); + return this->streamStatus_.accessConst(); } boost::optional TwitchChannel::getBttvEmote(const EmoteName &name) const diff --git a/src/providers/twitch/TwitchChannel.hpp b/src/providers/twitch/TwitchChannel.hpp index 480b3d637..752fd9008 100644 --- a/src/providers/twitch/TwitchChannel.hpp +++ b/src/providers/twitch/TwitchChannel.hpp @@ -66,9 +66,9 @@ public: QString getRoomId() const; void setRoomId(const QString &id); - const AccessGuard accessRoomModes() const; + AccessGuard accessRoomModes() const; void setRoomModes(const RoomModes &roomModes_); - const AccessGuard accessStreamStatus() const; + AccessGuard accessStreamStatus() const; boost::optional getBttvEmote(const EmoteName &name) const; boost::optional getFfzEmote(const EmoteName &name) const; diff --git a/src/singletons/Updates.cpp b/src/singletons/Updates.cpp index 2a525106f..a251b39e1 100644 --- a/src/singletons/Updates.cpp +++ b/src/singletons/Updates.cpp @@ -67,7 +67,7 @@ void Updates::installUpdates() return true; }); - req.onSuccess([this](auto result) -> bool { + req.onSuccess([this](auto result) -> Outcome { QByteArray object = result.getData(); auto filename = combinePath(getPaths()->miscDirectory, "update.zip"); @@ -76,7 +76,7 @@ void Updates::installUpdates() if (file.write(object) == -1) { this->setStatus_(WriteFileFailed); - return false; + return Failure; } QProcess::startDetached( @@ -84,7 +84,7 @@ void Updates::installUpdates() {filename, "restart"}); QApplication::exit(0); - return false; + return Success; }); this->setStatus_(Downloading); req.execute(); @@ -98,7 +98,7 @@ void Updates::checkForUpdates() NetworkRequest req(url); req.setTimeout(30000); - req.onSuccess([this](auto result) -> bool { + req.onSuccess([this](auto result) -> Outcome { auto object = result.parseJson(); QJsonValue version_val = object.value("version"); QJsonValue update_val = object.value("update"); @@ -116,7 +116,7 @@ void Updates::checkForUpdates() box->show(); box->raise(); }); - return false; + return Failure; } this->onlineVersion_ = version_val.toString(); @@ -140,7 +140,7 @@ void Updates::checkForUpdates() } else { this->setStatus_(NoUpdateAvailable); } - return false; + return Failure; }); this->setStatus_(Searching); req.execute(); diff --git a/src/widgets/helper/ChannelView.cpp b/src/widgets/helper/ChannelView.cpp index 47aa5b7f6..391a88a4d 100644 --- a/src/widgets/helper/ChannelView.cpp +++ b/src/widgets/helper/ChannelView.cpp @@ -50,11 +50,11 @@ void addEmoteContextMenuItems(const Emote &emote, MessageElement::Flags creatorF // Add copy and open links for 1x, 2x, 3x auto addImageLink = [&](const ImagePtr &image, char scale) { - if (image->isValid()) { - copyMenu->addAction(QString(scale) + "x link", [url = image->getUrl()] { + if (!image->empty()) { + copyMenu->addAction(QString(scale) + "x link", [url = image->url()] { QApplication::clipboard()->setText(url.string); }); - openMenu->addAction(QString(scale) + "x link", [url = image->getUrl()] { + openMenu->addAction(QString(scale) + "x link", [url = image->url()] { QDesktopServices::openUrl(QUrl(url.string)); }); } From defa7e41fa87918a087608eda2b41351535aad1b Mon Sep 17 00:00:00 2001 From: fourtf Date: Mon, 6 Aug 2018 20:00:04 +0200 Subject: [PATCH 46/73] simplified Image a bit --- src/messages/Image.cpp | 42 +++++++++++++----------------------------- src/messages/Image.hpp | 20 ++++++-------------- 2 files changed, 19 insertions(+), 43 deletions(-) diff --git a/src/messages/Image.cpp b/src/messages/Image.cpp index 309d2f0a0..44c855dd7 100644 --- a/src/messages/Image.cpp +++ b/src/messages/Image.cpp @@ -20,28 +20,12 @@ namespace chatterino { namespace { -// Frame -Frame::Frame(const QPixmap *nonOwning, int duration) - : nonOwning_(nonOwning) - , duration_(duration) +const QPixmap *getPixmap(const Pixmap &pixmap) { -} - -Frame::Frame(std::unique_ptr owning, int duration) - : owning_(std::move(owning)) - , duration_(duration) -{ -} - -int Frame::duration() const -{ - return this->duration_; -} - -const QPixmap *Frame::pixmap() const -{ - if (this->nonOwning_) return this->nonOwning_; - return this->owning_.get(); + if (pixmap.which() == 0) + return boost::get(pixmap); + else + return boost::get>(pixmap).get(); } // Frames @@ -65,12 +49,12 @@ Frames::~Frames() void Frames::advance() { - this->timeOffset_ += GIF_FRAME_LENGTH; + this->durationOffset_ += GIF_FRAME_LENGTH; while (true) { this->index_ %= this->items_.size(); - if (this->timeOffset_ > this->items_[this->index_].duration()) { - this->timeOffset_ -= this->items_[this->index_].duration(); + if (this->durationOffset_ > this->items_[this->index_].duration) { + this->durationOffset_ -= this->items_[this->index_].duration; this->index_ = (this->index_ + 1) % this->items_.size(); } else { break; @@ -86,13 +70,13 @@ bool Frames::animated() const const QPixmap *Frames::current() const { if (this->items_.size() == 0) return nullptr; - return this->items_[this->index_].pixmap(); + return getPixmap(this->items_[this->index_].pixmap); } const QPixmap *Frames::first() const { if (this->items_.size() == 0) return nullptr; - return this->items_.front().pixmap(); + return getPixmap(this->items_.front().pixmap); } // functions @@ -111,7 +95,7 @@ std::vector readFrames(QImageReader &reader, const Url &url) auto pixmap = std::make_unique(QPixmap::fromImage(image)); int duration = std::max(20, reader.nextImageDelay()); - frames.push_back(Frame(std::move(pixmap), duration)); + frames.push_back(Frame{std::move(pixmap), duration}); } } @@ -191,7 +175,7 @@ Image::Image(std::unique_ptr owning, qreal scale) : scale_(scale) { std::vector vec; - vec.push_back(Frame(std::move(owning))); + vec.push_back(Frame{std::move(owning)}); this->frames_ = std::move(vec); } @@ -199,7 +183,7 @@ Image::Image(QPixmap *nonOwning, qreal scale) : scale_(scale) { std::vector vec; - vec.push_back(Frame(nonOwning)); + vec.push_back(Frame{nonOwning}); this->frames_ = std::move(vec); } diff --git a/src/messages/Image.hpp b/src/messages/Image.hpp index 7db43580c..01ccd68eb 100644 --- a/src/messages/Image.hpp +++ b/src/messages/Image.hpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include @@ -13,19 +14,10 @@ namespace chatterino { namespace { -class Frame -{ -public: - explicit Frame(const QPixmap *nonOwning, int duration = 1); - explicit Frame(std::unique_ptr owning, int duration = 1); - - const QPixmap *pixmap() const; - int duration() const; - -private: - const QPixmap *nonOwning_{nullptr}; - std::unique_ptr owning_{}; - int duration_{}; +using Pixmap = boost::variant>; +struct Frame { + Pixmap pixmap; + int duration; }; class Frames { @@ -44,7 +36,7 @@ public: private: std::vector items_; int index_{0}; - int timeOffset_{0}; + int durationOffset_{0}; }; } // namespace From f71ff08e686ae76c3dd4084d0f05f27ba9b3fdcb Mon Sep 17 00:00:00 2001 From: fourtf Date: Mon, 6 Aug 2018 21:17:03 +0200 Subject: [PATCH 47/73] changed to 80 max column --- .clang-format | 30 +- src/Application.cpp | 116 ++++---- src/Application.hpp | 3 +- src/BrowserExtension.cpp | 7 +- src/RunGui.cpp | 18 +- src/autogenerated/ResourcesAutogen.hpp | 3 +- src/common/Channel.cpp | 28 +- src/common/Channel.hpp | 6 +- src/common/Common.hpp | 6 +- src/common/CompletionModel.cpp | 32 ++- src/common/ConcurrentMap.hpp | 3 +- src/common/Emotemap.hpp | 4 +- src/common/LinkParser.cpp | 9 +- src/common/NetworkData.cpp | 3 +- src/common/NetworkRequest.cpp | 39 ++- src/common/NetworkRequest.hpp | 24 +- src/common/NetworkResult.cpp | 7 +- src/common/NetworkTimer.cpp | 3 +- src/common/NullablePtr.hpp | 3 +- src/common/SerializeCustom.hpp | 9 +- src/common/SignalVector.hpp | 9 +- src/common/SignalVectorModel.hpp | 50 ++-- .../accounts/AccountController.cpp | 3 +- .../accounts/AccountController.hpp | 3 +- src/controllers/accounts/AccountModel.cpp | 7 +- src/controllers/accounts/AccountModel.hpp | 9 +- .../commands/CommandController.cpp | 130 +++++---- .../commands/CommandController.hpp | 3 +- src/controllers/commands/CommandModel.cpp | 15 +- src/controllers/commands/CommandModel.hpp | 3 +- .../highlights/HighlightBlacklistModel.hpp | 5 +- .../highlights/HighlightBlacklistUser.hpp | 8 +- .../highlights/HighlightController.cpp | 14 +- src/controllers/highlights/HighlightModel.cpp | 37 ++- src/controllers/highlights/HighlightModel.hpp | 10 +- .../highlights/HighlightPhrase.hpp | 12 +- .../highlights/UserHighlightModel.cpp | 11 +- .../highlights/UserHighlightModel.hpp | 5 +- src/controllers/ignores/IgnoreController.hpp | 3 +- src/controllers/ignores/IgnoreModel.cpp | 3 +- src/controllers/ignores/IgnorePhrase.hpp | 6 +- .../moderationactions/ModerationAction.cpp | 9 +- .../ModerationActionModel.cpp | 4 +- .../ModerationActionModel.hpp | 5 +- .../moderationactions/ModerationActions.hpp | 3 +- src/controllers/taggedusers/TaggedUser.cpp | 3 +- .../taggedusers/TaggedUsersModel.cpp | 19 +- .../taggedusers/TaggedUsersModel.hpp | 9 +- src/debug/Benchmark.hpp | 3 +- src/main.cpp | 5 +- src/messages/Image.cpp | 6 +- src/messages/Image.hpp | 3 +- src/messages/ImageSet.cpp | 3 +- src/messages/LimitedQueue.hpp | 29 +- src/messages/LimitedQueueSnapshot.hpp | 5 +- src/messages/Message.cpp | 21 +- src/messages/Message.hpp | 13 +- src/messages/MessageBuilder.cpp | 12 +- src/messages/MessageBuilder.hpp | 3 +- src/messages/MessageElement.cpp | 67 +++-- src/messages/MessageElement.hpp | 37 ++- src/messages/Selection.hpp | 9 +- src/messages/layouts/MessageLayout.cpp | 38 ++- src/messages/layouts/MessageLayout.hpp | 5 +- .../layouts/MessageLayoutContainer.cpp | 100 ++++--- .../layouts/MessageLayoutContainer.hpp | 3 +- src/messages/layouts/MessageLayoutElement.cpp | 40 ++- src/messages/layouts/MessageLayoutElement.hpp | 24 +- src/providers/bttv/BttvEmotes.cpp | 27 +- src/providers/bttv/BttvEmotes.hpp | 7 +- src/providers/bttv/LoadBttvChannelEmote.cpp | 44 +-- src/providers/bttv/LoadBttvChannelEmote.hpp | 6 +- src/providers/chatterino/ChatterinoBadges.cpp | 8 +- src/providers/emoji/Emojis.cpp | 65 +++-- src/providers/emoji/Emojis.hpp | 6 +- src/providers/ffz/FfzEmotes.cpp | 32 ++- src/providers/ffz/FfzEmotes.hpp | 9 +- src/providers/irc/AbstractIrcServer.cpp | 59 ++-- src/providers/irc/AbstractIrcServer.hpp | 12 +- src/providers/irc/IrcAccount.cpp | 3 +- src/providers/irc/IrcAccount.hpp | 3 +- src/providers/irc/IrcConnection2.cpp | 13 +- src/providers/irc/IrcServer.hpp | 3 +- src/providers/twitch/IrcMessageHandler.cpp | 75 +++-- src/providers/twitch/IrcMessageHandler.hpp | 11 +- src/providers/twitch/PartialTwitchUser.cpp | 9 +- src/providers/twitch/PartialTwitchUser.hpp | 3 +- src/providers/twitch/PubsubActions.hpp | 3 +- src/providers/twitch/PubsubClient.cpp | 162 ++++++----- src/providers/twitch/PubsubClient.hpp | 22 +- src/providers/twitch/PubsubHelpers.cpp | 8 +- src/providers/twitch/PubsubHelpers.hpp | 14 +- src/providers/twitch/TwitchAccount.cpp | 99 ++++--- src/providers/twitch/TwitchAccount.hpp | 28 +- src/providers/twitch/TwitchAccountManager.cpp | 46 +-- src/providers/twitch/TwitchAccountManager.hpp | 15 +- src/providers/twitch/TwitchApi.cpp | 6 +- src/providers/twitch/TwitchApi.hpp | 3 +- src/providers/twitch/TwitchBadges.cpp | 34 ++- src/providers/twitch/TwitchChannel.cpp | 242 +++++++++------- src/providers/twitch/TwitchChannel.hpp | 3 +- src/providers/twitch/TwitchEmotes.cpp | 34 ++- src/providers/twitch/TwitchEmotes.hpp | 6 +- src/providers/twitch/TwitchMessageBuilder.cpp | 256 ++++++++++------- src/providers/twitch/TwitchMessageBuilder.hpp | 12 +- .../twitch/TwitchParseCheerEmotes.cpp | 32 ++- .../twitch/TwitchParseCheerEmotes.hpp | 3 +- src/providers/twitch/TwitchServer.cpp | 57 ++-- src/providers/twitch/TwitchServer.hpp | 15 +- src/providers/twitch/TwitchUser.hpp | 6 +- src/singletons/Fonts.cpp | 21 +- src/singletons/Logging.cpp | 4 +- src/singletons/NativeMessaging.cpp | 46 +-- src/singletons/Paths.cpp | 31 +- src/singletons/Paths.hpp | 4 +- src/singletons/Settings.cpp | 3 +- src/singletons/Settings.hpp | 78 ++++-- src/singletons/Theme.cpp | 111 +++++--- src/singletons/Theme.hpp | 4 +- src/singletons/Updates.cpp | 34 ++- src/singletons/WindowManager.cpp | 48 ++-- src/singletons/helper/LoggingChannel.cpp | 6 +- src/singletons/helper/LoggingChannel.hpp | 6 +- src/util/ConcurrentMap.hpp | 3 +- src/util/InitUpdateButton.cpp | 16 +- src/util/InitUpdateButton.hpp | 3 +- src/util/LayoutCreator.hpp | 33 ++- src/util/PostToThread.hpp | 3 +- src/util/RapidjsonHelpers.hpp | 9 +- src/util/RemoveScrollAreaBackground.hpp | 3 +- src/util/SharedPtrElementLess.hpp | 3 +- src/util/StandardItemHelper.hpp | 16 +- src/util/StreamLink.cpp | 77 ++--- src/util/StreamLink.hpp | 7 +- src/util/WindowsHelper.cpp | 9 +- src/widgets/AccountSwitchWidget.cpp | 9 +- src/widgets/AttachedWindow.cpp | 35 ++- src/widgets/AttachedWindow.hpp | 4 +- src/widgets/BaseWidget.cpp | 9 +- src/widgets/BaseWindow.cpp | 121 ++++---- src/widgets/BaseWindow.hpp | 3 +- src/widgets/Label.cpp | 14 +- src/widgets/Label.hpp | 3 +- src/widgets/Notebook.cpp | 60 ++-- src/widgets/Notebook.hpp | 3 +- src/widgets/Scrollbar.cpp | 62 ++-- src/widgets/Scrollbar.hpp | 3 +- src/widgets/StreamView.cpp | 6 +- src/widgets/TooltipWidget.cpp | 9 +- src/widgets/Window.cpp | 71 +++-- src/widgets/dialogs/EmotePopup.cpp | 40 ++- src/widgets/dialogs/LastRunCrashDialog.cpp | 37 ++- src/widgets/dialogs/LoginDialog.cpp | 102 ++++--- src/widgets/dialogs/LogsPopup.cpp | 44 +-- src/widgets/dialogs/NotificationPopup.cpp | 3 +- src/widgets/dialogs/QualityPopup.cpp | 3 +- src/widgets/dialogs/SelectChannelDialog.cpp | 97 ++++--- src/widgets/dialogs/SettingsDialog.cpp | 13 +- src/widgets/dialogs/SettingsDialog.hpp | 3 +- src/widgets/dialogs/TextInputDialog.cpp | 11 +- src/widgets/dialogs/UpdateDialog.cpp | 27 +- src/widgets/dialogs/UserInfoPopup.cpp | 236 +++++++++------- src/widgets/helper/ChannelView.cpp | 262 ++++++++++------- src/widgets/helper/ChannelView.hpp | 17 +- src/widgets/helper/ComboBoxItemDelegate.cpp | 15 +- src/widgets/helper/ComboBoxItemDelegate.hpp | 3 +- src/widgets/helper/EditableModelView.cpp | 9 +- src/widgets/helper/Line.hpp | 6 +- src/widgets/helper/NotebookButton.cpp | 27 +- src/widgets/helper/NotebookTab.cpp | 97 ++++--- src/widgets/helper/ResizingTextEdit.cpp | 37 ++- src/widgets/helper/ResizingTextEdit.hpp | 3 +- src/widgets/helper/RippleEffectButton.cpp | 38 +-- src/widgets/helper/SearchPopup.cpp | 3 +- src/widgets/helper/SettingsDialogTab.cpp | 7 +- src/widgets/helper/SettingsDialogTab.hpp | 3 +- src/widgets/helper/TitlebarButton.cpp | 5 +- src/widgets/settingspages/AboutPage.cpp | 46 +-- src/widgets/settingspages/AboutPage.hpp | 4 +- src/widgets/settingspages/AccountsPage.cpp | 8 +- .../settingspages/BrowserExtensionPage.cpp | 14 +- src/widgets/settingspages/CommandPage.cpp | 11 +- src/widgets/settingspages/EmotesPage.cpp | 11 +- .../settingspages/ExternalToolsPage.cpp | 40 ++- src/widgets/settingspages/FeelPage.cpp | 40 +-- .../settingspages/HighlightingPage.cpp | 69 +++-- src/widgets/settingspages/IgnoresPage.cpp | 24 +- .../settingspages/KeyboardSettingsPage.cpp | 18 +- src/widgets/settingspages/LogsPage.cpp | 12 +- src/widgets/settingspages/LookPage.cpp | 214 ++++++++------ src/widgets/settingspages/LookPage.hpp | 3 +- src/widgets/settingspages/ModerationPage.cpp | 100 ++++--- src/widgets/settingspages/SettingsPage.cpp | 38 +-- src/widgets/settingspages/SettingsPage.hpp | 6 +- .../settingspages/SpecialChannelsPage.cpp | 6 +- src/widgets/splits/Split.cpp | 77 +++-- src/widgets/splits/Split.hpp | 10 +- src/widgets/splits/SplitContainer.cpp | 265 +++++++++++------- src/widgets/splits/SplitContainer.hpp | 10 +- src/widgets/splits/SplitHeader.cpp | 205 ++++++++------ src/widgets/splits/SplitInput.cpp | 102 ++++--- src/widgets/splits/SplitOverlay.cpp | 39 ++- src/widgets/splits/SplitOverlay.hpp | 9 +- 203 files changed, 3792 insertions(+), 2405 deletions(-) diff --git a/.clang-format b/.clang-format index e141fff60..bd3f2c430 100644 --- a/.clang-format +++ b/.clang-format @@ -1,23 +1,14 @@ -IndentCaseLabels: true -BasedOnStyle: Google -IndentWidth: 4 -Standard: Auto -PointerBindsToType: false Language: Cpp -SpacesBeforeTrailingComments: 2 + AccessModifierOffset: -1 -AlignEscapedNewlinesLeft: true -AlwaysBreakAfterDefinitionReturnType: false -AlwaysBreakBeforeMultilineStrings: false -BreakConstructorInitializersBeforeComma: true -# BreakBeforeBraces: Linux -BreakBeforeBraces: Custom AccessModifierOffset: -4 -ConstructorInitializerAllOnOneLineOrOnePerLine: false +AlignEscapedNewlinesLeft: true AllowShortFunctionsOnASingleLine: false AllowShortIfStatementsOnASingleLine: true AllowShortLoopsOnASingleLine: false -DerivePointerBinding: false +AlwaysBreakAfterDefinitionReturnType: false +AlwaysBreakBeforeMultilineStrings: false +BasedOnStyle: Google BraceWrapping: { AfterNamespace: 'false' AfterClass: 'true' @@ -26,5 +17,14 @@ BraceWrapping: { AfterFunction: 'true' BeforeCatch: 'false' } -ColumnLimit: 100 +BreakBeforeBraces: Custom +BreakConstructorInitializersBeforeComma: true +ColumnLimit: 80 +ConstructorInitializerAllOnOneLineOrOnePerLine: false +DerivePointerBinding: false FixNamespaceComments: true +IndentCaseLabels: true +IndentWidth: 4 +PointerBindsToType: false +SpacesBeforeTrailingComments: 2 +Standard: Auto diff --git a/src/Application.cpp b/src/Application.cpp index b7d651aa4..414e7c5cf 100644 --- a/src/Application.cpp +++ b/src/Application.cpp @@ -30,7 +30,8 @@ static std::atomic isAppInitialized{false}; Application *Application::instance = nullptr; // this class is responsible for handling the workflow of Chatterino -// It will create the instances of the major classes, and connect their signals to each other +// It will create the instances of the major classes, and connect their signals +// to each other Application::Application(Settings &_settings, Paths &_paths) : settings(&_settings) @@ -53,7 +54,8 @@ Application::Application(Settings &_settings, Paths &_paths) { this->instance = this; - this->fonts->fontChanged.connect([this]() { this->windows->layoutChannelViews(); }); + this->fonts->fontChanged.connect( + [this]() { this->windows->layoutChannelViews(); }); this->twitch.server = this->twitch2; this->twitch.pubsub = this->twitch2->pubsub; @@ -117,40 +119,49 @@ void Application::initPubsub() Log("WHISPER RECEIVED LOL"); // }); - this->twitch.pubsub->signals_.moderation.chatCleared.connect([this](const auto &action) { - auto chan = this->twitch.server->getChannelOrEmptyByID(action.roomID); - if (chan->isEmpty()) { - return; - } + this->twitch.pubsub->signals_.moderation.chatCleared.connect( + [this](const auto &action) { + auto chan = + this->twitch.server->getChannelOrEmptyByID(action.roomID); + if (chan->isEmpty()) { + return; + } - QString text = QString("%1 cleared the chat").arg(action.source.name); + QString text = + QString("%1 cleared the chat").arg(action.source.name); - auto msg = Message::createSystemMessage(text); - postToThread([chan, msg] { chan->addMessage(msg); }); - }); + auto msg = Message::createSystemMessage(text); + postToThread([chan, msg] { chan->addMessage(msg); }); + }); - this->twitch.pubsub->signals_.moderation.modeChanged.connect([this](const auto &action) { - auto chan = this->twitch.server->getChannelOrEmptyByID(action.roomID); - if (chan->isEmpty()) { - return; - } + this->twitch.pubsub->signals_.moderation.modeChanged.connect( + [this](const auto &action) { + auto chan = + this->twitch.server->getChannelOrEmptyByID(action.roomID); + if (chan->isEmpty()) { + return; + } - QString text = QString("%1 turned %2 %3 mode") // - .arg(action.source.name) - .arg(action.state == ModeChangedAction::State::On ? "on" : "off") - .arg(action.getModeName()); + QString text = + QString("%1 turned %2 %3 mode") // + .arg(action.source.name) + .arg(action.state == ModeChangedAction::State::On ? "on" + : "off") + .arg(action.getModeName()); - if (action.duration > 0) { - text.append(" (" + QString::number(action.duration) + " seconds)"); - } + if (action.duration > 0) { + text.append(" (" + QString::number(action.duration) + + " seconds)"); + } - auto msg = Message::createSystemMessage(text); - postToThread([chan, msg] { chan->addMessage(msg); }); - }); + auto msg = Message::createSystemMessage(text); + postToThread([chan, msg] { chan->addMessage(msg); }); + }); this->twitch.pubsub->signals_.moderation.moderationStateChanged.connect( [this](const auto &action) { - auto chan = this->twitch.server->getChannelOrEmptyByID(action.roomID); + auto chan = + this->twitch.server->getChannelOrEmptyByID(action.roomID); if (chan->isEmpty()) { return; } @@ -158,48 +169,55 @@ void Application::initPubsub() QString text; if (action.modded) { - text = QString("%1 modded %2").arg(action.source.name, action.target.name); + text = QString("%1 modded %2") + .arg(action.source.name, action.target.name); } else { - text = QString("%1 unmodded %2").arg(action.source.name, action.target.name); + text = QString("%1 unmodded %2") + .arg(action.source.name, action.target.name); } auto msg = Message::createSystemMessage(text); postToThread([chan, msg] { chan->addMessage(msg); }); }); - this->twitch.pubsub->signals_.moderation.userBanned.connect([&](const auto &action) { - auto chan = this->twitch.server->getChannelOrEmptyByID(action.roomID); + this->twitch.pubsub->signals_.moderation.userBanned.connect( + [&](const auto &action) { + auto chan = + this->twitch.server->getChannelOrEmptyByID(action.roomID); - if (chan->isEmpty()) { - return; - } + if (chan->isEmpty()) { + return; + } - auto msg = Message::createTimeoutMessage(action); - msg->flags |= Message::PubSub; + auto msg = Message::createTimeoutMessage(action); + msg->flags |= Message::PubSub; - postToThread([chan, msg] { chan->addOrReplaceTimeout(msg); }); - }); + postToThread([chan, msg] { chan->addOrReplaceTimeout(msg); }); + }); - this->twitch.pubsub->signals_.moderation.userUnbanned.connect([&](const auto &action) { - auto chan = this->twitch.server->getChannelOrEmptyByID(action.roomID); + this->twitch.pubsub->signals_.moderation.userUnbanned.connect( + [&](const auto &action) { + auto chan = + this->twitch.server->getChannelOrEmptyByID(action.roomID); - if (chan->isEmpty()) { - return; - } + if (chan->isEmpty()) { + return; + } - auto msg = Message::createUntimeoutMessage(action); + auto msg = Message::createUntimeoutMessage(action); - postToThread([chan, msg] { chan->addMessage(msg); }); - }); + postToThread([chan, msg] { chan->addMessage(msg); }); + }); this->twitch.pubsub->start(); auto RequestModerationActions = [=]() { this->twitch.server->pubsub->unlistenAllModerationActions(); - // TODO(pajlada): Unlisten to all authed topics instead of only moderation topics - // this->twitch.pubsub->UnlistenAllAuthedTopics(); + // TODO(pajlada): Unlisten to all authed topics instead of only + // moderation topics this->twitch.pubsub->UnlistenAllAuthedTopics(); - this->twitch.server->pubsub->listenToWhispers(this->accounts->twitch.getCurrent()); // + this->twitch.server->pubsub->listenToWhispers( + this->accounts->twitch.getCurrent()); // }; this->accounts->twitch.currentUserChanged.connect(RequestModerationActions); diff --git a/src/Application.hpp b/src/Application.hpp index c378715f5..fc7051e43 100644 --- a/src/Application.hpp +++ b/src/Application.hpp @@ -77,7 +77,8 @@ private: void initPubsub(); void initNm(); - template ::value>> + template ::value>> T &emplace() { auto t = new T; diff --git a/src/BrowserExtension.cpp b/src/BrowserExtension.cpp index 3440170ab..75a6ef26f 100644 --- a/src/BrowserExtension.cpp +++ b/src/BrowserExtension.cpp @@ -54,15 +54,16 @@ void runLoop(NativeMessagingClient &client) std::cin.read(b.get(), size); *(b.get() + size) = '\0'; - client.sendMessage(QByteArray::fromRawData(b.get(), static_cast(size))); + client.sendMessage( + QByteArray::fromRawData(b.get(), static_cast(size))); } } } // namespace bool shouldRunBrowserExtensionHost(const QStringList &args) { - return args.size() > 0 && - (args[0].startsWith("chrome-extension://") || args[0].endsWith(".json")); + return args.size() > 0 && (args[0].startsWith("chrome-extension://") || + args[0].endsWith(".json")); } void runBrowserExtensionHost() diff --git a/src/RunGui.cpp b/src/RunGui.cpp index a15bce6bc..6f0d86622 100644 --- a/src/RunGui.cpp +++ b/src/RunGui.cpp @@ -32,23 +32,28 @@ void installCustomPalette() darkPalette.setColor(QPalette::Window, QColor(22, 22, 22)); darkPalette.setColor(QPalette::WindowText, Qt::white); darkPalette.setColor(QPalette::Text, Qt::white); - darkPalette.setColor(QPalette::Disabled, QPalette::WindowText, QColor(127, 127, 127)); + darkPalette.setColor(QPalette::Disabled, QPalette::WindowText, + QColor(127, 127, 127)); darkPalette.setColor(QPalette::Base, QColor("#333")); darkPalette.setColor(QPalette::AlternateBase, QColor("#444")); darkPalette.setColor(QPalette::ToolTipBase, Qt::white); darkPalette.setColor(QPalette::ToolTipText, Qt::white); - darkPalette.setColor(QPalette::Disabled, QPalette::Text, QColor(127, 127, 127)); + darkPalette.setColor(QPalette::Disabled, QPalette::Text, + QColor(127, 127, 127)); darkPalette.setColor(QPalette::Dark, QColor(35, 35, 35)); darkPalette.setColor(QPalette::Shadow, QColor(20, 20, 20)); darkPalette.setColor(QPalette::Button, QColor(70, 70, 70)); darkPalette.setColor(QPalette::ButtonText, Qt::white); - darkPalette.setColor(QPalette::Disabled, QPalette::ButtonText, QColor(127, 127, 127)); + darkPalette.setColor(QPalette::Disabled, QPalette::ButtonText, + QColor(127, 127, 127)); darkPalette.setColor(QPalette::BrightText, Qt::red); darkPalette.setColor(QPalette::Link, QColor(42, 130, 218)); darkPalette.setColor(QPalette::Highlight, QColor(42, 130, 218)); - darkPalette.setColor(QPalette::Disabled, QPalette::Highlight, QColor(80, 80, 80)); + darkPalette.setColor(QPalette::Disabled, QPalette::Highlight, + QColor(80, 80, 80)); darkPalette.setColor(QPalette::HighlightedText, Qt::white); - darkPalette.setColor(QPalette::Disabled, QPalette::HighlightedText, QColor(127, 127, 127)); + darkPalette.setColor(QPalette::Disabled, QPalette::HighlightedText, + QColor(127, 127, 127)); qApp->setPalette(darkPalette); } @@ -106,7 +111,8 @@ void runGui(QApplication &a, Paths &paths, Settings &settings) #endif // Running file - auto runningPath = paths.miscDirectory + "/running_" + paths.applicationFilePathHash; + auto runningPath = + paths.miscDirectory + "/running_" + paths.applicationFilePathHash; if (QFile::exists(runningPath)) { showLastCrashDialog(); diff --git a/src/autogenerated/ResourcesAutogen.hpp b/src/autogenerated/ResourcesAutogen.hpp index 048a1a94b..c6adb3576 100644 --- a/src/autogenerated/ResourcesAutogen.hpp +++ b/src/autogenerated/ResourcesAutogen.hpp @@ -3,7 +3,8 @@ namespace chatterino { -class Resources2 : public Singleton { +class Resources2 : public Singleton +{ public: Resources2(); diff --git a/src/common/Channel.cpp b/src/common/Channel.cpp index c03019c43..364d836d2 100644 --- a/src/common/Channel.cpp +++ b/src/common/Channel.cpp @@ -22,9 +22,10 @@ Channel::Channel(const QString &name, Type type) , name_(name) , type_(type) { - QObject::connect(&this->clearCompletionModelTimer_, &QTimer::timeout, [this]() { - this->completionModel.clearExpiredStrings(); // - }); + QObject::connect(&this->clearCompletionModelTimer_, &QTimer::timeout, + [this]() { + this->completionModel.clearExpiredStrings(); // + }); this->clearCompletionModelTimer_.start(60 * 1000); } @@ -65,7 +66,8 @@ void Channel::addMessage(MessagePtr message) const QString &username = message->loginName; if (!username.isEmpty()) { - // TODO: Add recent chatters display name. This should maybe be a setting + // TODO: Add recent chatters display name. This should maybe be a + // setting this->addRecentChatter(message); } @@ -101,17 +103,21 @@ void Channel::addOrReplaceTimeout(MessagePtr message) break; } - if (s->flags.HasFlag(Message::Untimeout) && s->timeoutUser == message->timeoutUser) { + if (s->flags.HasFlag(Message::Untimeout) && + s->timeoutUser == message->timeoutUser) { break; } - if (s->flags.HasFlag(Message::Timeout) && s->timeoutUser == message->timeoutUser) { - if (message->flags.HasFlag(Message::PubSub) && !s->flags.HasFlag(Message::PubSub)) { + if (s->flags.HasFlag(Message::Timeout) && + s->timeoutUser == message->timeoutUser) { + if (message->flags.HasFlag(Message::PubSub) && + !s->flags.HasFlag(Message::PubSub)) { this->replaceMessage(s, message); addMessage = false; break; } - if (!message->flags.HasFlag(Message::PubSub) && s->flags.HasFlag(Message::PubSub)) { + if (!message->flags.HasFlag(Message::PubSub) && + s->flags.HasFlag(Message::PubSub)) { addMessage = false; break; } @@ -119,7 +125,8 @@ void Channel::addOrReplaceTimeout(MessagePtr message) int count = s->count + 1; MessagePtr replacement(Message::createSystemMessage( - message->searchText + QString(" (") + QString::number(count) + " times)")); + message->searchText + QString(" (") + QString::number(count) + + " times)")); replacement->timeoutUser = message->timeoutUser; replacement->count = count; @@ -164,7 +171,8 @@ void Channel::disableAllMessages() void Channel::addMessagesAtStart(std::vector &_messages) { - std::vector addedMessages = this->messages_.pushFront(_messages); + std::vector addedMessages = + this->messages_.pushFront(_messages); if (addedMessages.size() != 0) { this->messagesAddedAtStart.invoke(addedMessages); diff --git a/src/common/Channel.hpp b/src/common/Channel.hpp index e153914c1..043b33fe8 100644 --- a/src/common/Channel.hpp +++ b/src/common/Channel.hpp @@ -32,7 +32,8 @@ public: explicit Channel(const QString &name, Type type); virtual ~Channel(); - pajlada::Signals::Signal sendMessageSignal; + pajlada::Signals::Signal + sendMessageSignal; pajlada::Signals::Signal messageRemovedFromStart; pajlada::Signals::Signal messageAppended; @@ -96,7 +97,8 @@ class IndirectChannel }; public: - IndirectChannel(ChannelPtr channel, Channel::Type type = Channel::Type::Direct) + IndirectChannel(ChannelPtr channel, + Channel::Type type = Channel::Type::Direct) : data_(new Data(channel, type)) { } diff --git a/src/common/Common.hpp b/src/common/Common.hpp index c4abe043f..251727c24 100644 --- a/src/common/Common.hpp +++ b/src/common/Common.hpp @@ -25,8 +25,10 @@ inline QString qS(const std::string &string) return QString::fromStdString(string); } -const Qt::KeyboardModifiers showSplitOverlayModifiers = Qt::ControlModifier | Qt::AltModifier; -const Qt::KeyboardModifiers showAddSplitRegions = Qt::ControlModifier | Qt::AltModifier; +const Qt::KeyboardModifiers showSplitOverlayModifiers = + Qt::ControlModifier | Qt::AltModifier; +const Qt::KeyboardModifiers showAddSplitRegions = + Qt::ControlModifier | Qt::AltModifier; const Qt::KeyboardModifiers showResizeHandlesModifiers = Qt::ControlModifier; static const char *ANONYMOUS_USERNAME_LABEL ATTR_UNUSED = " - anonymous - "; diff --git a/src/common/CompletionModel.cpp b/src/common/CompletionModel.cpp index f56425d27..bd0038107 100644 --- a/src/common/CompletionModel.cpp +++ b/src/common/CompletionModel.cpp @@ -111,26 +111,32 @@ void CompletionModel::refresh() // User-specific: Twitch Emotes if (auto account = app->accounts->twitch.getCurrent()) { for (const auto &emote : account->accessEmotes()->allEmoteNames) { - // XXX: No way to discern between a twitch global emote and sub emote right now - this->addString(emote.string, TaggedString::Type::TwitchGlobalEmote); + // XXX: No way to discern between a twitch global emote and sub + // emote right now + this->addString(emote.string, + TaggedString::Type::TwitchGlobalEmote); } } // // Global: BTTV Global Emotes - // std::vector &bttvGlobalEmoteCodes = app->emotes->bttv.globalEmoteNames_; - // for (const auto &m : bttvGlobalEmoteCodes) { + // std::vector &bttvGlobalEmoteCodes = + // app->emotes->bttv.globalEmoteNames_; for (const auto &m : + // bttvGlobalEmoteCodes) { // this->addString(m, TaggedString::Type::BTTVGlobalEmote); // } // // Global: FFZ Global Emotes - // std::vector &ffzGlobalEmoteCodes = app->emotes->ffz.globalEmoteCodes; - // for (const auto &m : ffzGlobalEmoteCodes) { + // std::vector &ffzGlobalEmoteCodes = + // app->emotes->ffz.globalEmoteCodes; for (const auto &m : + // ffzGlobalEmoteCodes) { // this->addString(m, TaggedString::Type::FFZGlobalEmote); // } // Channel emotes if (auto channel = dynamic_cast( - getApp()->twitch2->getChannelOrEmptyByID(this->channelName_).get())) { + getApp() + ->twitch2->getChannelOrEmptyByID(this->channelName_) + .get())) { auto bttv = channel->accessBttvEmotes(); // auto it = bttv->begin(); // for (const auto &emote : *bttv) { @@ -143,7 +149,8 @@ void CompletionModel::refresh() // Channel-specific: FFZ Channel Emotes for (const auto &emote : *channel->accessFfzEmotes()) { - this->addString(emote.second->name.string, TaggedString::Type::FFZChannelEmote); + this->addString(emote.second->name.string, + TaggedString::Type::FFZChannelEmote); } } @@ -164,7 +171,8 @@ void CompletionModel::refresh() // Channel-specific: Usernames // fourtf: only works with twitch chat - // auto c = ChannelManager::getInstance().getTwitchChannel(this->channelName); + // auto c = + // ChannelManager::getInstance().getTwitchChannel(this->channelName); // auto usernames = c->getUsernamesForCompletions(); // for (const auto &name : usernames) { // assert(!name.displayName.isEmpty()); @@ -191,9 +199,11 @@ void CompletionModel::addUser(const QString &username) auto add = [this](const QString &str) { auto ts = this->createUser(str + " "); // Always add a space at the end of completions - std::pair::iterator, bool> p = this->emotes_.insert(ts); + std::pair::iterator, bool> p = + this->emotes_.insert(ts); if (!p.second) { - // No inseration was made, figure out if we need to replace the username. + // No inseration was made, figure out if we need to replace the + // username. if (p.first->str > ts.str) { // Replace lowercase version of name with mixed-case version diff --git a/src/common/ConcurrentMap.hpp b/src/common/ConcurrentMap.hpp index 2db2d9f58..0b5cbeb0e 100644 --- a/src/common/ConcurrentMap.hpp +++ b/src/common/ConcurrentMap.hpp @@ -65,7 +65,8 @@ public: this->data.insert(name, value); } - void each(std::function func) const + void each( + std::function func) const { QMutexLocker lock(&this->mutex); diff --git a/src/common/Emotemap.hpp b/src/common/Emotemap.hpp index c57f08e91..8c219bc67 100644 --- a/src/common/Emotemap.hpp +++ b/src/common/Emotemap.hpp @@ -14,8 +14,8 @@ namespace chatterino { // bool isValid() const; // Image *getImage(float scale) const; -// // Link to the emote page i.e. https://www.frankerfacez.com/emoticon/144722-pajaCringe -// QString pageLink; +// // Link to the emote page i.e. +// https://www.frankerfacez.com/emoticon/144722-pajaCringe QString pageLink; // Image *image1x = nullptr; // Image *image2x = nullptr; diff --git a/src/common/LinkParser.cpp b/src/common/LinkParser.cpp index ac4ddf734..b4f63a156 100644 --- a/src/common/LinkParser.cpp +++ b/src/common/LinkParser.cpp @@ -45,9 +45,11 @@ LinkParser::LinkParser(const QString &unparsedString) "(?:\\.(?:[1-9]\\d?|1\\d\\d|2[0-4]\\d|25[0-4]))" "|" // host name - "(?:(?:[_a-z\\x{00a1}-\\x{ffff}0-9]-*)*[a-z\\x{00a1}-\\x{ffff}0-9]+)" + "(?:(?:[_a-z\\x{00a1}-\\x{ffff}0-9]-*)*[a-z\\x{00a1}-\\x{ffff}0-9]+" + ")" // domain name - "(?:\\.(?:[a-z\\x{00a1}-\\x{ffff}0-9]-*)*[a-z\\x{00a1}-\\x{ffff}0-9]+)*" + "(?:\\.(?:[a-z\\x{00a1}-\\x{ffff}0-9]-*)*[a-z\\x{00a1}-\\x{ffff}0-" + "9]+)*" // TLD identifier //"(?:\\.(?:[a-z\\x{00a1}-\\x{ffff}]{2,}))" "(?:[\\.](?:" + @@ -61,7 +63,8 @@ LinkParser::LinkParser(const QString &unparsedString) "(?:[/?#]\\S*)?" "$"; - return QRegularExpression(hyperlinkRegExp, QRegularExpression::CaseInsensitiveOption); + return QRegularExpression(hyperlinkRegExp, + QRegularExpression::CaseInsensitiveOption); }(); this->match_ = linkRegex.match(unparsedString); diff --git a/src/common/NetworkData.cpp b/src/common/NetworkData.cpp index 12aec944d..ad8fe40b2 100644 --- a/src/common/NetworkData.cpp +++ b/src/common/NetworkData.cpp @@ -30,7 +30,8 @@ QString NetworkData::getHash() bytes.append(header); } - QByteArray hashBytes(QCryptographicHash::hash(bytes, QCryptographicHash::Sha256)); + QByteArray hashBytes( + QCryptographicHash::hash(bytes, QCryptographicHash::Sha256)); this->hash_ = hashBytes.toHex(); } diff --git a/src/common/NetworkRequest.cpp b/src/common/NetworkRequest.cpp index 976c0fa14..c6d882551 100644 --- a/src/common/NetworkRequest.cpp +++ b/src/common/NetworkRequest.cpp @@ -11,7 +11,8 @@ namespace chatterino { -NetworkRequest::NetworkRequest(const std::string &url, NetworkRequestType requestType) +NetworkRequest::NetworkRequest(const std::string &url, + NetworkRequestType requestType) : data(new NetworkData) , timer(new NetworkTimer) { @@ -62,7 +63,8 @@ void NetworkRequest::setRawHeader(const char *headerName, const char *value) this->data->request_.setRawHeader(headerName, value); } -void NetworkRequest::setRawHeader(const char *headerName, const QByteArray &value) +void NetworkRequest::setRawHeader(const char *headerName, + const QByteArray &value) { this->data->request_.setRawHeader(headerName, value); } @@ -77,7 +79,8 @@ void NetworkRequest::setTimeout(int ms) this->timer->timeoutMS_ = ms; } -void NetworkRequest::makeAuthorizedV5(const QString &clientID, const QString &oauthToken) +void NetworkRequest::makeAuthorizedV5(const QString &clientID, + const QString &oauthToken) { this->setRawHeader("Client-ID", clientID); this->setRawHeader("Accept", "application/vnd.twitchtv.v5+json"); @@ -114,12 +117,14 @@ void NetworkRequest::execute() } break; case NetworkRequestType::Put: { - // Put requests cannot be cached, therefore the request is called immediately + // Put requests cannot be cached, therefore the request is called + // immediately this->doRequest(); } break; case NetworkRequestType::Delete: { - // Delete requests cannot be cached, therefore the request is called immediately + // Delete requests cannot be cached, therefore the request is called + // immediately this->doRequest(); } break; @@ -152,7 +157,8 @@ Outcome NetworkRequest::tryLoadCachedFile() cachedFile.close(); - // XXX: If success is false, we should invalidate the cache file somehow/somewhere + // XXX: If success is false, we should invalidate the cache file + // somehow/somewhere return outcome; } @@ -166,14 +172,16 @@ void NetworkRequest::doRequest() this->timer->start(); - auto onUrlRequested = [data = this->data, timer = this->timer, worker]() mutable { + auto onUrlRequested = [data = this->data, timer = this->timer, + worker]() mutable { auto reply = [&]() -> QNetworkReply * { switch (data->requestType_) { case NetworkRequestType::Get: return NetworkManager::NaM.get(data->request_); case NetworkRequestType::Put: - return NetworkManager::NaM.put(data->request_, data->payload_); + return NetworkManager::NaM.put(data->request_, + data->payload_); case NetworkRequestType::Delete: return NetworkManager::NaM.deleteResource(data->request_); @@ -221,12 +229,14 @@ void NetworkRequest::doRequest() }; if (data->caller_ != nullptr) { - QObject::connect(worker, &NetworkWorker::doneUrl, data->caller_, handleReply); - QObject::connect(reply, &QNetworkReply::finished, worker, [worker]() mutable { - emit worker->doneUrl(); + QObject::connect(worker, &NetworkWorker::doneUrl, data->caller_, + handleReply); + QObject::connect(reply, &QNetworkReply::finished, worker, + [worker]() mutable { + emit worker->doneUrl(); - delete worker; - }); + delete worker; + }); } else { QObject::connect(reply, &QNetworkReply::finished, worker, [handleReply, worker]() mutable { @@ -237,7 +247,8 @@ void NetworkRequest::doRequest() } }; - QObject::connect(&requester, &NetworkRequester::requestUrl, worker, onUrlRequested); + QObject::connect(&requester, &NetworkRequester::requestUrl, worker, + onUrlRequested); emit requester.requestUrl(); } diff --git a/src/common/NetworkRequest.hpp b/src/common/NetworkRequest.hpp index 049c2cba1..8112a4852 100644 --- a/src/common/NetworkRequest.hpp +++ b/src/common/NetworkRequest.hpp @@ -12,15 +12,18 @@ namespace chatterino { class NetworkRequest { - // Stores all data about the request that needs to be passed around to each part of the request + // Stores all data about the request that needs to be passed around to each + // part of the request std::shared_ptr data; // Timer that tracks the timeout // By default, there's no explicit timeout for the request - // to enable the timer, the "setTimeout" function needs to be called before execute is called + // to enable the timer, the "setTimeout" function needs to be called before + // execute is called std::shared_ptr timer; - // The NetworkRequest destructor will assert if executed_ hasn't been set to true before dying + // The NetworkRequest destructor will assert if executed_ hasn't been set to + // true before dying bool executed_ = false; public: @@ -31,9 +34,11 @@ public: NetworkRequest(NetworkRequest &&other) = default; NetworkRequest &operator=(NetworkRequest &&other) = default; - explicit NetworkRequest(const std::string &url, - NetworkRequestType requestType = NetworkRequestType::Get); - explicit NetworkRequest(QUrl url, NetworkRequestType requestType = NetworkRequestType::Get); + explicit NetworkRequest( + const std::string &url, + NetworkRequestType requestType = NetworkRequestType::Get); + explicit NetworkRequest( + QUrl url, NetworkRequestType requestType = NetworkRequestType::Get); ~NetworkRequest(); @@ -50,14 +55,15 @@ public: void setRawHeader(const char *headerName, const QByteArray &value); void setRawHeader(const char *headerName, const QString &value); void setTimeout(int ms); - void makeAuthorizedV5(const QString &clientID, const QString &oauthToken = QString()); + void makeAuthorizedV5(const QString &clientID, + const QString &oauthToken = QString()); void execute(); private: // Returns true if the file was successfully loaded from cache - // Returns false if the cache file either didn't exist, or it contained "invalid" data - // "invalid" is specified by the onSuccess callback + // Returns false if the cache file either didn't exist, or it contained + // "invalid" data "invalid" is specified by the onSuccess callback Outcome tryLoadCachedFile(); void doRequest(); diff --git a/src/common/NetworkResult.cpp b/src/common/NetworkResult.cpp index 0620bf3c2..443d0aa05 100644 --- a/src/common/NetworkResult.cpp +++ b/src/common/NetworkResult.cpp @@ -27,11 +27,12 @@ rapidjson::Document NetworkResult::parseRapidJson() const { rapidjson::Document ret(rapidjson::kObjectType); - rapidjson::ParseResult result = ret.Parse(this->data_.data(), this->data_.length()); + rapidjson::ParseResult result = + ret.Parse(this->data_.data(), this->data_.length()); if (result.Code() != rapidjson::kParseErrorNone) { - Log("JSON parse error: {} ({})", rapidjson::GetParseError_En(result.Code()), - result.Offset()); + Log("JSON parse error: {} ({})", + rapidjson::GetParseError_En(result.Code()), result.Offset()); return ret; } diff --git a/src/common/NetworkTimer.cpp b/src/common/NetworkTimer.cpp index 522fdd310..858378b17 100644 --- a/src/common/NetworkTimer.cpp +++ b/src/common/NetworkTimer.cpp @@ -28,7 +28,8 @@ bool NetworkTimer::isStarted() const return this->started_; } -void NetworkTimer::onTimeout(NetworkWorker *worker, std::function cb) const +void NetworkTimer::onTimeout(NetworkWorker *worker, + std::function cb) const { assert(this->timer_ != nullptr); assert(worker != nullptr); diff --git a/src/common/NullablePtr.hpp b/src/common/NullablePtr.hpp index fb60af0d8..8bc160fd8 100644 --- a/src/common/NullablePtr.hpp +++ b/src/common/NullablePtr.hpp @@ -59,7 +59,8 @@ public: return !this->hasElement(); } - template ::value>> + template ::value>> operator NullablePtr() const { return NullablePtr(this->element_); diff --git a/src/common/SerializeCustom.hpp b/src/common/SerializeCustom.hpp index 9aa31bcfb..754cbf115 100644 --- a/src/common/SerializeCustom.hpp +++ b/src/common/SerializeCustom.hpp @@ -8,7 +8,8 @@ namespace Settings { template <> struct Serialize { - static rapidjson::Value get(const QString &value, rapidjson::Document::AllocatorType &a) + static rapidjson::Value get(const QString &value, + rapidjson::Document::AllocatorType &a) { return rapidjson::Value(value.toUtf8(), a); } @@ -20,12 +21,14 @@ struct Deserialize { { if (!value.IsString()) { PAJLADA_REPORT_ERROR(error) - PAJLADA_THROW_EXCEPTION("Deserialized rapidjson::Value is not a string"); + PAJLADA_THROW_EXCEPTION( + "Deserialized rapidjson::Value is not a string"); return QString{}; } try { - return QString::fromUtf8(value.GetString(), value.GetStringLength()); + return QString::fromUtf8(value.GetString(), + value.GetStringLength()); } catch (const std::exception &) { // int x = 5; } catch (...) { diff --git a/src/common/SignalVector.hpp b/src/common/SignalVector.hpp index 0594b8847..1ddf2c13a 100644 --- a/src/common/SignalVector.hpp +++ b/src/common/SignalVector.hpp @@ -88,7 +88,8 @@ template class UnsortedSignalVector : public BaseSignalVector { public: - virtual int insertItem(const TVectorItem &item, int index = -1, void *caller = nullptr) override + virtual int insertItem(const TVectorItem &item, int index = -1, + void *caller = nullptr) override { assertInGuiThread(); if (index == -1) { @@ -115,11 +116,13 @@ template class SortedSignalVector : public BaseSignalVector { public: - virtual int insertItem(const TVectorItem &item, int = -1, void *caller = nullptr) override + virtual int insertItem(const TVectorItem &item, int = -1, + void *caller = nullptr) override { assertInGuiThread(); - auto it = std::lower_bound(this->vector_.begin(), this->vector_.end(), item, Compare{}); + auto it = std::lower_bound(this->vector_.begin(), this->vector_.end(), + item, Compare{}); int index = it - this->vector_.begin(); this->vector_.insert(it, item); diff --git a/src/common/SignalVectorModel.hpp b/src/common/SignalVectorModel.hpp index cab31c063..237727d0d 100644 --- a/src/common/SignalVectorModel.hpp +++ b/src/common/SignalVectorModel.hpp @@ -11,7 +11,8 @@ namespace chatterino { template -class SignalVectorModel : public QAbstractTableModel, pajlada::Signals::SignalHolder +class SignalVectorModel : public QAbstractTableModel, + pajlada::Signals::SignalHolder { public: SignalVectorModel(int columnCount, QObject *parent = nullptr) @@ -43,7 +44,8 @@ public: index = this->beforeInsert(args.item, row, index); this->beginInsertRows(QModelIndex(), index, index); - this->rows_.insert(this->rows_.begin() + index, Row(row, args.item)); + this->rows_.insert(this->rows_.begin() + index, + Row(row, args.item)); this->endInsertRows(); }; @@ -65,7 +67,8 @@ public: assert(row >= 0 && row <= this->rows_.size()); // remove row - std::vector items = std::move(this->rows_[row].items); + std::vector items = + std::move(this->rows_[row].items); this->beginRemoveRows(QModelIndex(), row, row); this->rows_.erase(this->rows_.begin() + row); @@ -103,15 +106,18 @@ public: QVariant data(const QModelIndex &index, int role) const override { int row = index.row(), column = index.column(); - assert(row >= 0 && row < this->rows_.size() && column >= 0 && column < this->columnCount_); + assert(row >= 0 && row < this->rows_.size() && column >= 0 && + column < this->columnCount_); return rows_[row].items[column]->data(role); } - bool setData(const QModelIndex &index, const QVariant &value, int role) override + bool setData(const QModelIndex &index, const QVariant &value, + int role) override { int row = index.row(), column = index.column(); - assert(row >= 0 && row < this->rows_.size() && column >= 0 && column < this->columnCount_); + assert(row >= 0 && row < this->rows_.size() && column >= 0 && + column < this->columnCount_); Row &rowItem = this->rows_[row]; @@ -124,15 +130,16 @@ public: this->vector_->removeItem(vecRow, this); assert(this->rows_[row].original); - TVectorItem item = - this->getItemFromRow(this->rows_[row].items, this->rows_[row].original.get()); + TVectorItem item = this->getItemFromRow( + this->rows_[row].items, this->rows_[row].original.get()); this->vector_->insertItem(item, vecRow, this); } return true; } - QVariant headerData(int section, Qt::Orientation orientation, int role) const override + QVariant headerData(int section, Qt::Orientation orientation, + int role) const override { if (orientation != Qt::Horizontal) { return QVariant(); @@ -146,7 +153,8 @@ public: } } - bool setHeaderData(int section, Qt::Orientation orientation, const QVariant &value, + bool setHeaderData(int section, Qt::Orientation orientation, + const QVariant &value, int role = Qt::DisplayRole) override { if (orientation != Qt::Horizontal) { @@ -162,14 +170,16 @@ public: Qt::ItemFlags flags(const QModelIndex &index) const override { int row = index.row(), column = index.column(); - assert(row >= 0 && row < this->rows_.size() && column >= 0 && column < this->columnCount_); + assert(row >= 0 && row < this->rows_.size() && column >= 0 && + column < this->columnCount_); return this->rows_[index.row()].items[index.column()]->flags(); } QStandardItem *getItem(int row, int column) { - assert(row >= 0 && row < this->rows_.size() && column >= 0 && column < this->columnCount_); + assert(row >= 0 && row < this->rows_.size() && column >= 0 && + column < this->columnCount_); return rows_[row].items[column]; } @@ -204,20 +214,23 @@ protected: const TVectorItem &original) = 0; // turns a row in the model into a vector item - virtual void getRowFromItem(const TVectorItem &item, std::vector &row) = 0; + virtual void getRowFromItem(const TVectorItem &item, + std::vector &row) = 0; - virtual int beforeInsert(const TVectorItem &item, std::vector &row, + virtual int beforeInsert(const TVectorItem &item, + std::vector &row, int proposedIndex) { return proposedIndex; } - virtual void afterRemoved(const TVectorItem &item, std::vector &row, int index) + virtual void afterRemoved(const TVectorItem &item, + std::vector &row, int index) { } - virtual void customRowSetData(const std::vector &row, int column, - const QVariant &value, int role) + virtual void customRowSetData(const std::vector &row, + int column, const QVariant &value, int role) { } @@ -226,7 +239,8 @@ protected: assert(index >= 0 && index <= this->rows_.size()); this->beginInsertRows(QModelIndex(), index, index); - this->rows_.insert(this->rows_.begin() + index, Row(std::move(row), true)); + this->rows_.insert(this->rows_.begin() + index, + Row(std::move(row), true)); this->endInsertRows(); } diff --git a/src/controllers/accounts/AccountController.cpp b/src/controllers/accounts/AccountController.cpp index 888c21152..6cd1f2558 100644 --- a/src/controllers/accounts/AccountController.cpp +++ b/src/controllers/accounts/AccountController.cpp @@ -7,7 +7,8 @@ namespace chatterino { AccountController::AccountController() { this->twitch.accounts.itemInserted.connect([this](const auto &args) { - this->accounts_.insertItem(std::dynamic_pointer_cast(args.item)); + this->accounts_.insertItem( + std::dynamic_pointer_cast(args.item)); }); this->twitch.accounts.itemRemoved.connect([this](const auto &args) { diff --git a/src/controllers/accounts/AccountController.hpp b/src/controllers/accounts/AccountController.hpp index 25f0fee5c..023ad9c9e 100644 --- a/src/controllers/accounts/AccountController.hpp +++ b/src/controllers/accounts/AccountController.hpp @@ -28,7 +28,8 @@ public: TwitchAccountManager twitch; private: - SortedSignalVector, SharedPtrElementLess> accounts_; + SortedSignalVector, SharedPtrElementLess> + accounts_; }; } // namespace chatterino diff --git a/src/controllers/accounts/AccountModel.cpp b/src/controllers/accounts/AccountModel.cpp index d18946468..d63f2c288 100644 --- a/src/controllers/accounts/AccountModel.cpp +++ b/src/controllers/accounts/AccountModel.cpp @@ -10,8 +10,8 @@ AccountModel::AccountModel(QObject *parent) } // turn a vector item into a model row -std::shared_ptr AccountModel::getItemFromRow(std::vector &, - const std::shared_ptr &original) +std::shared_ptr AccountModel::getItemFromRow( + std::vector &, const std::shared_ptr &original) { return original; } @@ -25,7 +25,8 @@ void AccountModel::getRowFromItem(const std::shared_ptr &item, } int AccountModel::beforeInsert(const std::shared_ptr &item, - std::vector &row, int proposedIndex) + std::vector &row, + int proposedIndex) { if (this->categoryCount_[item->getCategory()]++ == 0) { auto row = this->createRow(); diff --git a/src/controllers/accounts/AccountModel.hpp b/src/controllers/accounts/AccountModel.hpp index 0ac239a2a..c0b4538d5 100644 --- a/src/controllers/accounts/AccountModel.hpp +++ b/src/controllers/accounts/AccountModel.hpp @@ -18,17 +18,20 @@ public: protected: // turn a vector item into a model row virtual std::shared_ptr getItemFromRow( - std::vector &row, const std::shared_ptr &original) override; + std::vector &row, + const std::shared_ptr &original) override; // turns a row in the model into a vector item virtual void getRowFromItem(const std::shared_ptr &item, std::vector &row) override; virtual int beforeInsert(const std::shared_ptr &item, - std::vector &row, int proposedIndex) override; + std::vector &row, + int proposedIndex) override; virtual void afterRemoved(const std::shared_ptr &item, - std::vector &row, int index) override; + std::vector &row, + int index) override; friend class AccountController; diff --git a/src/controllers/commands/CommandController.cpp b/src/controllers/commands/CommandController.cpp index 1d85052e1..e3026c1be 100644 --- a/src/controllers/commands/CommandController.cpp +++ b/src/controllers/commands/CommandController.cpp @@ -18,12 +18,13 @@ #include #include -#define TWITCH_DEFAULT_COMMANDS \ - { \ - "/help", "/w", "/me", "/disconnect", "/mods", "/color", "/ban", "/unban", "/timeout", \ - "/untimeout", "/slow", "/slowoff", "/r9kbeta", "/r9kbetaoff", "/emoteonly", \ - "/emoteonlyoff", "/clear", "/subscribers", "/subscribersoff", "/followers", \ - "/followersoff" \ +#define TWITCH_DEFAULT_COMMANDS \ + { \ + "/help", "/w", "/me", "/disconnect", "/mods", "/color", "/ban", \ + "/unban", "/timeout", "/untimeout", "/slow", "/slowoff", \ + "/r9kbeta", "/r9kbetaoff", "/emoteonly", "/emoteonlyoff", \ + "/clear", "/subscribers", "/subscribersoff", "/followers", \ + "/followersoff" \ } namespace chatterino { @@ -77,7 +78,8 @@ void CommandController::save() { QFile textFile(this->filePath_); if (!textFile.open(QIODevice::WriteOnly)) { - Log("[CommandController::saveCommands] Unable to open {} for writing", this->filePath_); + Log("[CommandController::saveCommands] Unable to open {} for writing", + this->filePath_); return; } @@ -96,7 +98,8 @@ CommandModel *CommandController::createModel(QObject *parent) return model; } -QString CommandController::execCommand(const QString &text, ChannelPtr channel, bool dryRun) +QString CommandController::execCommand(const QString &text, ChannelPtr channel, + bool dryRun) { QStringList words = text.split(' ', QString::SkipEmptyParts); Command command; @@ -122,11 +125,13 @@ QString CommandController::execCommand(const QString &text, ChannelPtr channel, MessageBuilder b; b.emplace(); - b.emplace(app->accounts->twitch.getCurrent()->getUserName(), - MessageElement::Text, MessageColor::Text, - FontStyle::ChatMediumBold); + b.emplace( + app->accounts->twitch.getCurrent()->getUserName(), + MessageElement::Text, MessageColor::Text, + FontStyle::ChatMediumBold); b.emplace("->", MessageElement::Text); - b.emplace(words[1] + ":", MessageElement::Text, MessageColor::Text, + b.emplace(words[1] + ":", MessageElement::Text, + MessageColor::Text, FontStyle::ChatMediumBold); QString rest = ""; @@ -144,7 +149,9 @@ QString CommandController::execCommand(const QString &text, ChannelPtr channel, if (getSettings()->inlineWhispers) { app->twitch.server->forEachChannel( - [&b](ChannelPtr _channel) { _channel->addMessage(b.getMessage()); }); + [&b](ChannelPtr _channel) { + _channel->addMessage(b.getMessage()); + }); } return ""; @@ -165,15 +172,17 @@ QString CommandController::execCommand(const QString &text, ChannelPtr channel, } else if (commandName == "/uptime") { const auto &streamStatus = twitchChannel->accessStreamStatus(); - QString messageText = - streamStatus->live ? streamStatus->uptime : "Channel is not live."; + QString messageText = streamStatus->live + ? streamStatus->uptime + : "Channel is not live."; channel->addMessage(Message::createSystemMessage(messageText)); return ""; } else if (commandName == "/ignore") { if (words.size() < 2) { - channel->addMessage(Message::createSystemMessage("Usage: /ignore [user]")); + channel->addMessage( + Message::createSystemMessage("Usage: /ignore [user]")); return ""; } auto app = getApp(); @@ -182,19 +191,21 @@ QString CommandController::execCommand(const QString &text, ChannelPtr channel, auto target = words.at(1); if (user->isAnon()) { - channel->addMessage( - Message::createSystemMessage("You must be logged in to ignore someone")); + channel->addMessage(Message::createSystemMessage( + "You must be logged in to ignore someone")); return ""; } - user->ignore(target, [channel](auto resultCode, const QString &message) { + user->ignore(target, [channel](auto resultCode, + const QString &message) { channel->addMessage(Message::createSystemMessage(message)); }); return ""; } else if (commandName == "/unignore") { if (words.size() < 2) { - channel->addMessage(Message::createSystemMessage("Usage: /unignore [user]")); + channel->addMessage(Message::createSystemMessage( + "Usage: /unignore [user]")); return ""; } auto app = getApp(); @@ -203,19 +214,21 @@ QString CommandController::execCommand(const QString &text, ChannelPtr channel, auto target = words.at(1); if (user->isAnon()) { - channel->addMessage( - Message::createSystemMessage("You must be logged in to ignore someone")); + channel->addMessage(Message::createSystemMessage( + "You must be logged in to ignore someone")); return ""; } - user->unignore(target, [channel](auto resultCode, const QString &message) { + user->unignore(target, [channel](auto resultCode, + const QString &message) { channel->addMessage(Message::createSystemMessage(message)); }); return ""; } else if (commandName == "/follow") { if (words.size() < 2) { - channel->addMessage(Message::createSystemMessage("Usage: /follow [user]")); + channel->addMessage( + Message::createSystemMessage("Usage: /follow [user]")); return ""; } auto app = getApp(); @@ -224,27 +237,29 @@ QString CommandController::execCommand(const QString &text, ChannelPtr channel, auto target = words.at(1); if (user->isAnon()) { - channel->addMessage( - Message::createSystemMessage("You must be logged in to follow someone")); + channel->addMessage(Message::createSystemMessage( + "You must be logged in to follow someone")); return ""; } - TwitchApi::findUserId(target, [user, channel, target](QString userId) { - if (userId.isEmpty()) { - channel->addMessage(Message::createSystemMessage( - "User " + target + " could not be followed!")); - return; - } - user->followUser(userId, [channel, target]() { - channel->addMessage( - Message::createSystemMessage("You successfully followed " + target)); + TwitchApi::findUserId( + target, [user, channel, target](QString userId) { + if (userId.isEmpty()) { + channel->addMessage(Message::createSystemMessage( + "User " + target + " could not be followed!")); + return; + } + user->followUser(userId, [channel, target]() { + channel->addMessage(Message::createSystemMessage( + "You successfully followed " + target)); + }); }); - }); return ""; } else if (commandName == "/unfollow") { if (words.size() < 2) { - channel->addMessage(Message::createSystemMessage("Usage: /unfollow [user]")); + channel->addMessage(Message::createSystemMessage( + "Usage: /unfollow [user]")); return ""; } auto app = getApp(); @@ -253,28 +268,29 @@ QString CommandController::execCommand(const QString &text, ChannelPtr channel, auto target = words.at(1); if (user->isAnon()) { - channel->addMessage( - Message::createSystemMessage("You must be logged in to follow someone")); + channel->addMessage(Message::createSystemMessage( + "You must be logged in to follow someone")); return ""; } - TwitchApi::findUserId(target, [user, channel, target](QString userId) { - if (userId.isEmpty()) { - channel->addMessage(Message::createSystemMessage( - "User " + target + " could not be followed!")); - return; - } - user->unfollowUser(userId, [channel, target]() { - channel->addMessage( - Message::createSystemMessage("You successfully unfollowed " + target)); + TwitchApi::findUserId( + target, [user, channel, target](QString userId) { + if (userId.isEmpty()) { + channel->addMessage(Message::createSystemMessage( + "User " + target + " could not be followed!")); + return; + } + user->unfollowUser(userId, [channel, target]() { + channel->addMessage(Message::createSystemMessage( + "You successfully unfollowed " + target)); + }); }); - }); return ""; } else if (commandName == "/logs") { if (words.size() < 2) { - channel->addMessage( - Message::createSystemMessage("Usage: /logs [user] (channel)")); + channel->addMessage(Message::createSystemMessage( + "Usage: /logs [user] (channel)")); return ""; } auto app = getApp(); @@ -293,7 +309,8 @@ QString CommandController::execCommand(const QString &text, ChannelPtr channel, if (words.at(2).at(0) == "#") { channelName = words.at(2).mid(1); } - auto logsChannel = app->twitch.server->getChannelOrEmpty(channelName); + auto logsChannel = + app->twitch.server->getChannelOrEmpty(channelName); if (logsChannel == nullptr) { } else { logs->setInfo(logsChannel, target); @@ -319,7 +336,8 @@ QString CommandController::execCommand(const QString &text, ChannelPtr channel, return this->execCustomCommand(words, command); } -QString CommandController::execCustomCommand(const QStringList &words, const Command &command) +QString CommandController::execCustomCommand(const QStringList &words, + const Command &command) { QString result; @@ -331,13 +349,15 @@ QString CommandController::execCustomCommand(const QStringList &words, const Com int matchOffset = 0; while (true) { - QRegularExpressionMatch match = parseCommand.match(command.func, matchOffset); + QRegularExpressionMatch match = + parseCommand.match(command.func, matchOffset); if (!match.hasMatch()) { break; } - result += command.func.mid(lastCaptureEnd, match.capturedStart() - lastCaptureEnd + 1); + result += command.func.mid(lastCaptureEnd, + match.capturedStart() - lastCaptureEnd + 1); lastCaptureEnd = match.capturedEnd(); matchOffset = lastCaptureEnd - 1; diff --git a/src/controllers/commands/CommandController.hpp b/src/controllers/commands/CommandController.hpp index 45aba5bcc..f7b43b8f6 100644 --- a/src/controllers/commands/CommandController.hpp +++ b/src/controllers/commands/CommandController.hpp @@ -22,7 +22,8 @@ class CommandController final : public Singleton public: CommandController(); - QString execCommand(const QString &text, std::shared_ptr channel, bool dryRun); + QString execCommand(const QString &text, std::shared_ptr channel, + bool dryRun); QStringList getDefaultTwitchCommandList(); virtual void initialize(Settings &settings, Paths &paths) override; diff --git a/src/controllers/commands/CommandModel.cpp b/src/controllers/commands/CommandModel.cpp index 66e088533..e9c7cc529 100644 --- a/src/controllers/commands/CommandModel.cpp +++ b/src/controllers/commands/CommandModel.cpp @@ -9,18 +9,23 @@ CommandModel::CommandModel(QObject *parent) } // turn a vector item into a model row -Command CommandModel::getItemFromRow(std::vector &row, const Command &original) +Command CommandModel::getItemFromRow(std::vector &row, + const Command &original) { - return Command(row[0]->data(Qt::EditRole).toString(), row[1]->data(Qt::EditRole).toString()); + return Command(row[0]->data(Qt::EditRole).toString(), + row[1]->data(Qt::EditRole).toString()); } // turns a row in the model into a vector item -void CommandModel::getRowFromItem(const Command &item, std::vector &row) +void CommandModel::getRowFromItem(const Command &item, + std::vector &row) { row[0]->setData(item.name, Qt::DisplayRole); - row[0]->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable); + row[0]->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable | + Qt::ItemIsEditable); row[1]->setData(item.func, Qt::DisplayRole); - row[1]->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable); + row[1]->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable | + Qt::ItemIsEditable); } } // namespace chatterino diff --git a/src/controllers/commands/CommandModel.hpp b/src/controllers/commands/CommandModel.hpp index 434b8dfd9..648dcf73a 100644 --- a/src/controllers/commands/CommandModel.hpp +++ b/src/controllers/commands/CommandModel.hpp @@ -19,7 +19,8 @@ protected: const Command &command) override; // turns a row in the model into a vector item - virtual void getRowFromItem(const Command &item, std::vector &row) override; + virtual void getRowFromItem(const Command &item, + std::vector &row) override; friend class CommandController; }; diff --git a/src/controllers/highlights/HighlightBlacklistModel.hpp b/src/controllers/highlights/HighlightBlacklistModel.hpp index 494640833..d073425a5 100644 --- a/src/controllers/highlights/HighlightBlacklistModel.hpp +++ b/src/controllers/highlights/HighlightBlacklistModel.hpp @@ -15,8 +15,9 @@ class HighlightBlacklistModel : public SignalVectorModel protected: // turn a vector item into a model row - virtual HighlightBlacklistUser getItemFromRow(std::vector &row, - const HighlightBlacklistUser &original) override; + virtual HighlightBlacklistUser getItemFromRow( + std::vector &row, + const HighlightBlacklistUser &original) override; // turns a row in the model into a vector item virtual void getRowFromItem(const HighlightBlacklistUser &item, diff --git a/src/controllers/highlights/HighlightBlacklistUser.hpp b/src/controllers/highlights/HighlightBlacklistUser.hpp index 122a53e89..5376d9cf5 100644 --- a/src/controllers/highlights/HighlightBlacklistUser.hpp +++ b/src/controllers/highlights/HighlightBlacklistUser.hpp @@ -16,14 +16,16 @@ class HighlightBlacklistUser public: bool operator==(const HighlightBlacklistUser &other) const { - return std::tie(this->pattern_, this->isRegex_) == std::tie(other.pattern_, other.isRegex_); + return std::tie(this->pattern_, this->isRegex_) == + std::tie(other.pattern_, other.isRegex_); } HighlightBlacklistUser(const QString &pattern, bool isRegex = false) : pattern_(pattern) , isRegex_(isRegex) - , regex_(isRegex ? pattern : "", QRegularExpression::CaseInsensitiveOption | - QRegularExpression::UseUnicodePropertiesOption) + , regex_(isRegex ? pattern : "", + QRegularExpression::CaseInsensitiveOption | + QRegularExpression::UseUnicodePropertiesOption) { } diff --git a/src/controllers/highlights/HighlightController.cpp b/src/controllers/highlights/HighlightController.cpp index 4829e981b..bd78b65ac 100644 --- a/src/controllers/highlights/HighlightController.cpp +++ b/src/controllers/highlights/HighlightController.cpp @@ -25,12 +25,14 @@ void HighlightController::initialize(Settings &settings, Paths &paths) this->highlightsSetting_.setValue(this->phrases.getVector()); }); - for (const HighlightBlacklistUser &blacklistedUser : this->blacklistSetting_.getValue()) { + for (const HighlightBlacklistUser &blacklistedUser : + this->blacklistSetting_.getValue()) { this->blacklistedUsers.appendItem(blacklistedUser); } - this->blacklistedUsers.delayedItemsChanged.connect( - [this] { this->blacklistSetting_.setValue(this->blacklistedUsers.getVector()); }); + this->blacklistedUsers.delayedItemsChanged.connect([this] { + this->blacklistSetting_.setValue(this->blacklistedUsers.getVector()); + }); } HighlightModel *HighlightController::createModel(QObject *parent) @@ -61,7 +63,8 @@ bool HighlightController::isHighlightedUser(const QString &username) return false; } -HighlightBlacklistModel *HighlightController::createBlacklistModel(QObject *parent) +HighlightBlacklistModel *HighlightController::createBlacklistModel( + QObject *parent) { auto *model = new HighlightBlacklistModel(parent); model->init(&this->blacklistedUsers); @@ -71,7 +74,8 @@ HighlightBlacklistModel *HighlightController::createBlacklistModel(QObject *pare bool HighlightController::blacklistContains(const QString &username) { - std::vector blacklistItems = this->blacklistedUsers.getVector(); + std::vector blacklistItems = + this->blacklistedUsers.getVector(); for (const auto &blacklistedUser : blacklistItems) { if (blacklistedUser.isMatch(username)) { return true; diff --git a/src/controllers/highlights/HighlightModel.cpp b/src/controllers/highlights/HighlightModel.cpp index fcc36c3b3..cad2fd006 100644 --- a/src/controllers/highlights/HighlightModel.cpp +++ b/src/controllers/highlights/HighlightModel.cpp @@ -13,18 +13,20 @@ HighlightModel::HighlightModel(QObject *parent) } // turn a vector item into a model row -HighlightPhrase HighlightModel::getItemFromRow(std::vector &row, - const HighlightPhrase &original) +HighlightPhrase HighlightModel::getItemFromRow( + std::vector &row, const HighlightPhrase &original) { // key, alert, sound, regex - return HighlightPhrase{ - row[0]->data(Qt::DisplayRole).toString(), row[1]->data(Qt::CheckStateRole).toBool(), - row[2]->data(Qt::CheckStateRole).toBool(), row[3]->data(Qt::CheckStateRole).toBool()}; + return HighlightPhrase{row[0]->data(Qt::DisplayRole).toString(), + row[1]->data(Qt::CheckStateRole).toBool(), + row[2]->data(Qt::CheckStateRole).toBool(), + row[3]->data(Qt::CheckStateRole).toBool()}; } // turns a row in the model into a vector item -void HighlightModel::getRowFromItem(const HighlightPhrase &item, std::vector &row) +void HighlightModel::getRowFromItem(const HighlightPhrase &item, + std::vector &row) { setStringItem(row[0], item.getPattern()); setBoolItem(row[1], item.getAlert()); @@ -35,31 +37,38 @@ void HighlightModel::getRowFromItem(const HighlightPhrase &item, std::vector row = this->createRow(); - setBoolItem(row[0], getApp()->settings->enableHighlightsSelf.getValue(), true, false); + setBoolItem(row[0], getApp()->settings->enableHighlightsSelf.getValue(), + true, false); row[0]->setData("Your username (automatic)", Qt::DisplayRole); - setBoolItem(row[1], getApp()->settings->enableHighlightTaskbar.getValue(), true, false); - setBoolItem(row[2], getApp()->settings->enableHighlightSound.getValue(), true, false); + setBoolItem(row[1], getApp()->settings->enableHighlightTaskbar.getValue(), + true, false); + setBoolItem(row[2], getApp()->settings->enableHighlightSound.getValue(), + true, false); row[3]->setFlags(0); this->insertCustomRow(row, 0); } -void HighlightModel::customRowSetData(const std::vector &row, int column, - const QVariant &value, int role) +void HighlightModel::customRowSetData(const std::vector &row, + int column, const QVariant &value, + int role) { switch (column) { case 0: { if (role == Qt::CheckStateRole) { - getApp()->settings->enableHighlightsSelf.setValue(value.toBool()); + getApp()->settings->enableHighlightsSelf.setValue( + value.toBool()); } } break; case 1: { if (role == Qt::CheckStateRole) { - getApp()->settings->enableHighlightTaskbar.setValue(value.toBool()); + getApp()->settings->enableHighlightTaskbar.setValue( + value.toBool()); } } break; case 2: { if (role == Qt::CheckStateRole) { - getApp()->settings->enableHighlightSound.setValue(value.toBool()); + getApp()->settings->enableHighlightSound.setValue( + value.toBool()); } } break; case 3: { diff --git a/src/controllers/highlights/HighlightModel.hpp b/src/controllers/highlights/HighlightModel.hpp index 7f50287ed..b42246167 100644 --- a/src/controllers/highlights/HighlightModel.hpp +++ b/src/controllers/highlights/HighlightModel.hpp @@ -15,8 +15,9 @@ class HighlightModel : public SignalVectorModel protected: // turn a vector item into a model row - virtual HighlightPhrase getItemFromRow(std::vector &row, - const HighlightPhrase &original) override; + virtual HighlightPhrase getItemFromRow( + std::vector &row, + const HighlightPhrase &original) override; // turns a row in the model into a vector item virtual void getRowFromItem(const HighlightPhrase &item, @@ -24,8 +25,9 @@ protected: virtual void afterInit() override; - virtual void customRowSetData(const std::vector &row, int column, - const QVariant &value, int role) override; + virtual void customRowSetData(const std::vector &row, + int column, const QVariant &value, + int role) override; friend class HighlightController; }; diff --git a/src/controllers/highlights/HighlightPhrase.hpp b/src/controllers/highlights/HighlightPhrase.hpp index fe4ed5cc2..77cd5936b 100644 --- a/src/controllers/highlights/HighlightPhrase.hpp +++ b/src/controllers/highlights/HighlightPhrase.hpp @@ -14,16 +14,20 @@ class HighlightPhrase public: bool operator==(const HighlightPhrase &other) const { - return std::tie(this->pattern_, this->sound_, this->alert_, this->isRegex_) == - std::tie(other.pattern_, other.sound_, other.alert_, other.isRegex_); + return std::tie(this->pattern_, this->sound_, this->alert_, + this->isRegex_) == std::tie(other.pattern_, + other.sound_, other.alert_, + other.isRegex_); } - HighlightPhrase(const QString &pattern, bool alert, bool sound, bool isRegex) + HighlightPhrase(const QString &pattern, bool alert, bool sound, + bool isRegex) : pattern_(pattern) , alert_(alert) , sound_(sound) , isRegex_(isRegex) - , regex_(isRegex_ ? pattern : "\\b" + QRegularExpression::escape(pattern) + "\\b", + , regex_(isRegex_ ? pattern + : "\\b" + QRegularExpression::escape(pattern) + "\\b", QRegularExpression::CaseInsensitiveOption | QRegularExpression::UseUnicodePropertiesOption) { diff --git a/src/controllers/highlights/UserHighlightModel.cpp b/src/controllers/highlights/UserHighlightModel.cpp index 92b862d60..e04c3403e 100644 --- a/src/controllers/highlights/UserHighlightModel.cpp +++ b/src/controllers/highlights/UserHighlightModel.cpp @@ -13,14 +13,15 @@ UserHighlightModel::UserHighlightModel(QObject *parent) } // turn vector item into model row -HighlightPhrase UserHighlightModel::getItemFromRow(std::vector &row, - const HighlightPhrase &original) +HighlightPhrase UserHighlightModel::getItemFromRow( + std::vector &row, const HighlightPhrase &original) { // key, regex - return HighlightPhrase{ - row[0]->data(Qt::DisplayRole).toString(), row[1]->data(Qt::CheckStateRole).toBool(), - row[2]->data(Qt::CheckStateRole).toBool(), row[3]->data(Qt::CheckStateRole).toBool()}; + return HighlightPhrase{row[0]->data(Qt::DisplayRole).toString(), + row[1]->data(Qt::CheckStateRole).toBool(), + row[2]->data(Qt::CheckStateRole).toBool(), + row[3]->data(Qt::CheckStateRole).toBool()}; } // row into vector item diff --git a/src/controllers/highlights/UserHighlightModel.hpp b/src/controllers/highlights/UserHighlightModel.hpp index b6a4023be..dcc42a950 100644 --- a/src/controllers/highlights/UserHighlightModel.hpp +++ b/src/controllers/highlights/UserHighlightModel.hpp @@ -15,8 +15,9 @@ class UserHighlightModel : public SignalVectorModel protected: // vector into model row - virtual HighlightPhrase getItemFromRow(std::vector &row, - const HighlightPhrase &original) override; + virtual HighlightPhrase getItemFromRow( + std::vector &row, + const HighlightPhrase &original) override; virtual void getRowFromItem(const HighlightPhrase &item, std::vector &row) override; diff --git a/src/controllers/ignores/IgnoreController.hpp b/src/controllers/ignores/IgnoreController.hpp index 09c109186..e7a0472e6 100644 --- a/src/controllers/ignores/IgnoreController.hpp +++ b/src/controllers/ignores/IgnoreController.hpp @@ -25,7 +25,8 @@ public: private: bool initialized_ = false; - ChatterinoSetting> ignoresSetting_ = {"/ignore/phrases"}; + ChatterinoSetting> ignoresSetting_ = { + "/ignore/phrases"}; }; } // namespace chatterino diff --git a/src/controllers/ignores/IgnoreModel.cpp b/src/controllers/ignores/IgnoreModel.cpp index 869d2ec4f..84a034525 100644 --- a/src/controllers/ignores/IgnoreModel.cpp +++ b/src/controllers/ignores/IgnoreModel.cpp @@ -23,7 +23,8 @@ IgnorePhrase IgnoreModel::getItemFromRow(std::vector &row, } // turns a row in the model into a vector item -void IgnoreModel::getRowFromItem(const IgnorePhrase &item, std::vector &row) +void IgnoreModel::getRowFromItem(const IgnorePhrase &item, + std::vector &row) { setStringItem(row[0], item.getPattern()); setBoolItem(row[1], item.isRegex()); diff --git a/src/controllers/ignores/IgnorePhrase.hpp b/src/controllers/ignores/IgnorePhrase.hpp index d02bc1902..f42df8442 100644 --- a/src/controllers/ignores/IgnorePhrase.hpp +++ b/src/controllers/ignores/IgnorePhrase.hpp @@ -16,13 +16,15 @@ class IgnorePhrase public: bool operator==(const IgnorePhrase &other) const { - return std::tie(this->pattern_, this->isRegex_) == std::tie(other.pattern_, other.isRegex_); + return std::tie(this->pattern_, this->isRegex_) == + std::tie(other.pattern_, other.isRegex_); } IgnorePhrase(const QString &pattern, bool isRegex) : pattern_(pattern) , isRegex_(isRegex) - , regex_(isRegex_ ? pattern : "\\b" + QRegularExpression::escape(pattern) + "\\b", + , regex_(isRegex_ ? pattern + : "\\b" + QRegularExpression::escape(pattern) + "\\b", QRegularExpression::CaseInsensitiveOption | QRegularExpression::UseUnicodePropertiesOption) { diff --git a/src/controllers/moderationactions/ModerationAction.cpp b/src/controllers/moderationactions/ModerationAction.cpp index d3f07b172..90abe815d 100644 --- a/src/controllers/moderationactions/ModerationAction.cpp +++ b/src/controllers/moderationactions/ModerationAction.cpp @@ -13,7 +13,8 @@ namespace chatterino { //{ //} -// ModerationAction::ModerationAction(const QString &_line1, const QString &_line2, +// ModerationAction::ModerationAction(const QString &_line1, const QString +// &_line2, // const QString &_action) // : _isImage(false) // , image(nullptr) @@ -55,10 +56,12 @@ ModerationAction::ModerationAction(const QString &action) // line1 = this->line1_; // line2 = this->line2_; // } else { - // this->_moderationActions.emplace_back(app->resources->buttonTimeout, str); + // this->_moderationActions.emplace_back(app->resources->buttonTimeout, + // str); // } } else if (action.startsWith("/ban ")) { - this->image_ = Image::fromNonOwningPixmap(&getApp()->resources->buttons.ban); + this->image_ = + Image::fromNonOwningPixmap(&getApp()->resources->buttons.ban); } else { QString xD = action; diff --git a/src/controllers/moderationactions/ModerationActionModel.cpp b/src/controllers/moderationactions/ModerationActionModel.cpp index aa0fe134c..39f093713 100644 --- a/src/controllers/moderationactions/ModerationActionModel.cpp +++ b/src/controllers/moderationactions/ModerationActionModel.cpp @@ -11,8 +11,8 @@ ModerationActionModel ::ModerationActionModel(QObject *parent) } // turn a vector item into a model row -ModerationAction ModerationActionModel::getItemFromRow(std::vector &row, - const ModerationAction &original) +ModerationAction ModerationActionModel::getItemFromRow( + std::vector &row, const ModerationAction &original) { return ModerationAction(row[0]->data(Qt::DisplayRole).toString()); } diff --git a/src/controllers/moderationactions/ModerationActionModel.hpp b/src/controllers/moderationactions/ModerationActionModel.hpp index 3c8a11675..3610561a9 100644 --- a/src/controllers/moderationactions/ModerationActionModel.hpp +++ b/src/controllers/moderationactions/ModerationActionModel.hpp @@ -16,8 +16,9 @@ public: protected: // turn a vector item into a model row - virtual ModerationAction getItemFromRow(std::vector &row, - const ModerationAction &original) override; + virtual ModerationAction getItemFromRow( + std::vector &row, + const ModerationAction &original) override; // turns a row in the model into a vector item virtual void getRowFromItem(const ModerationAction &item, diff --git a/src/controllers/moderationactions/ModerationActions.hpp b/src/controllers/moderationactions/ModerationActions.hpp index 6ecc3281d..d5e510f1c 100644 --- a/src/controllers/moderationactions/ModerationActions.hpp +++ b/src/controllers/moderationactions/ModerationActions.hpp @@ -25,7 +25,8 @@ public: ModerationActionModel *createModel(QObject *parent); private: - ChatterinoSetting> setting_ = {"/moderation/actions"}; + ChatterinoSetting> setting_ = { + "/moderation/actions"}; bool initialized_ = false; }; diff --git a/src/controllers/taggedusers/TaggedUser.cpp b/src/controllers/taggedusers/TaggedUser.cpp index 9694c4fe0..def7de600 100644 --- a/src/controllers/taggedusers/TaggedUser.cpp +++ b/src/controllers/taggedusers/TaggedUser.cpp @@ -4,7 +4,8 @@ namespace chatterino { -TaggedUser::TaggedUser(ProviderId provider, const QString &name, const QString &id) +TaggedUser::TaggedUser(ProviderId provider, const QString &name, + const QString &id) : providerId_(provider) , name_(name) , id_(id) diff --git a/src/controllers/taggedusers/TaggedUsersModel.cpp b/src/controllers/taggedusers/TaggedUsersModel.cpp index b3ef872f9..e3225888c 100644 --- a/src/controllers/taggedusers/TaggedUsersModel.cpp +++ b/src/controllers/taggedusers/TaggedUsersModel.cpp @@ -19,7 +19,8 @@ TaggedUser TaggedUsersModel::getItemFromRow(std::vector &row, } // turns a row in the model into a vector item -void TaggedUsersModel::getRowFromItem(const TaggedUser &item, std::vector &row) +void TaggedUsersModel::getRowFromItem(const TaggedUser &item, + std::vector &row) { setStringItem(row[0], item.getName()); } @@ -27,14 +28,18 @@ void TaggedUsersModel::getRowFromItem(const TaggedUser &item, std::vector row = this->createRow(); - // setBoolItem(row[0], getApp()->settings->enableHighlightsSelf.getValue(), true, - // false); row[0]->setData("Your username (automatic)", Qt::DisplayRole); - // setBoolItem(row[1], getApp()->settings->enableHighlightTaskbar.getValue(), true, - // false); setBoolItem(row[2], getApp()->settings->enableHighlightSound.getValue(), - // true, false); row[3]->setFlags(0); this->insertCustomRow(row, 0); + // setBoolItem(row[0], + // getApp()->settings->enableHighlightsSelf.getValue(), true, false); + // row[0]->setData("Your username (automatic)", Qt::DisplayRole); + // setBoolItem(row[1], + // getApp()->settings->enableHighlightTaskbar.getValue(), true, false); + // setBoolItem(row[2], + // getApp()->settings->enableHighlightSound.getValue(), true, false); + // row[3]->setFlags(0); this->insertCustomRow(row, 0); } -// void TaggedUserModel::customRowSetData(const std::vector &row, int column, +// void TaggedUserModel::customRowSetData(const std::vector +// &row, int column, // const QVariant &value, int role) //{ // switch (column) { diff --git a/src/controllers/taggedusers/TaggedUsersModel.hpp b/src/controllers/taggedusers/TaggedUsersModel.hpp index 44b566e5b..28c89493c 100644 --- a/src/controllers/taggedusers/TaggedUsersModel.hpp +++ b/src/controllers/taggedusers/TaggedUsersModel.hpp @@ -17,12 +17,15 @@ protected: const TaggedUser &original) override; // turns a row in the model into a vector item - virtual void getRowFromItem(const TaggedUser &item, std::vector &row) override; + virtual void getRowFromItem(const TaggedUser &item, + std::vector &row) override; virtual void afterInit() override; - // virtual void customRowSetData(const std::vector &row, int column, - // const QVariant &value, int role) override; + // virtual void customRowSetData(const std::vector &row, + // int column, + // const QVariant &value, int role) + // override; friend class TaggedUsersController; }; diff --git a/src/debug/Benchmark.hpp b/src/debug/Benchmark.hpp index c648382ea..5eeb6cb36 100644 --- a/src/debug/Benchmark.hpp +++ b/src/debug/Benchmark.hpp @@ -29,7 +29,8 @@ public: ~BenchmarkGuard() { - qDebug() << this->name << float(timer.nsecsElapsed()) / 1000000.0f << "ms"; + qDebug() << this->name << float(timer.nsecsElapsed()) / 1000000.0f + << "ms"; } qreal getElapsedMs() diff --git a/src/main.cpp b/src/main.cpp index 4ebb846a3..04a7b934e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -12,9 +12,10 @@ int main(int argc, char **argv) { QApplication a(argc, argv); - // convert char[][] to QStringList + // convert char** to QStringList auto args = QStringList(); - std::transform(argv + 1, argv + argc, std::back_inserter(args), [&](auto s) { return s; }); + std::transform(argv + 1, argv + argc, std::back_inserter(args), + [&](auto s) { return s; }); // run in gui mode or browser extension host mode if (shouldRunBrowserExtensionHost(args)) { diff --git a/src/messages/Image.cpp b/src/messages/Image.cpp index 44c855dd7..e8df96f57 100644 --- a/src/messages/Image.cpp +++ b/src/messages/Image.cpp @@ -85,7 +85,8 @@ std::vector readFrames(QImageReader &reader, const Url &url) std::vector frames; if (reader.imageCount() <= 0) { - Log("Error while reading image {}: '{}'", url.string, reader.errorString()); + Log("Error while reading image {}: '{}'", url.string, + reader.errorString()); return frames; } @@ -100,7 +101,8 @@ std::vector readFrames(QImageReader &reader, const Url &url) } if (frames.size() != 0) { - Log("Error while reading image {}: '{}'", url.string, reader.errorString()); + Log("Error while reading image {}: '{}'", url.string, + reader.errorString()); } return frames; diff --git a/src/messages/Image.hpp b/src/messages/Image.hpp index 01ccd68eb..50af47f4e 100644 --- a/src/messages/Image.hpp +++ b/src/messages/Image.hpp @@ -47,7 +47,8 @@ class Image : public std::enable_shared_from_this, boost::noncopyable { public: static ImagePtr fromUrl(const Url &url, qreal scale = 1); - static ImagePtr fromOwningPixmap(std::unique_ptr pixmap, qreal scale = 1); + static ImagePtr fromOwningPixmap(std::unique_ptr pixmap, + qreal scale = 1); static ImagePtr fromNonOwningPixmap(QPixmap *pixmap, qreal scale = 1); static ImagePtr getEmpty(); diff --git a/src/messages/ImageSet.cpp b/src/messages/ImageSet.cpp index ebb9b77c7..af3c5e6bd 100644 --- a/src/messages/ImageSet.cpp +++ b/src/messages/ImageSet.cpp @@ -13,7 +13,8 @@ ImageSet::ImageSet() { } -ImageSet::ImageSet(const ImagePtr &image1, const ImagePtr &image2, const ImagePtr &image3) +ImageSet::ImageSet(const ImagePtr &image1, const ImagePtr &image2, + const ImagePtr &image3) : imageX1_(image1) , imageX2_(image2) , imageX3_(image3) diff --git a/src/messages/LimitedQueue.hpp b/src/messages/LimitedQueue.hpp index 4870c6809..5165efa94 100644 --- a/src/messages/LimitedQueue.hpp +++ b/src/messages/LimitedQueue.hpp @@ -16,7 +16,8 @@ namespace chatterino { // // Explanation: // - messages can be appended until 'limit' is reached -// - when the limit is reached for every message added one will be removed at the start +// - when the limit is reached for every message added one will be removed at +// the start // - messages can only be added to the start when there is space for them, // trying to add messages to the start when it's full will not add them // - you are able to get a "Snapshot" which captures the state of this object @@ -41,7 +42,8 @@ public: { std::lock_guard lock(this->mutex_); - this->chunks_ = std::make_shared>>>(); + this->chunks_ = + std::make_shared>>>(); Chunk chunk = std::make_shared>(); chunk->resize(this->chunkSize_); this->chunks_->push_back(chunk); @@ -60,8 +62,8 @@ public: // still space in the last chunk if (lastChunk->size() <= this->lastChunkEnd_) { // create new chunk vector - ChunkVector newVector = - std::make_shared>>>(); + ChunkVector newVector = std::make_shared< + std::vector>>>(); // copy chunks for (Chunk &chunk : *this->chunks_) { @@ -93,8 +95,8 @@ public: std::lock_guard lock(this->mutex_); // create new vector to clone chunks into - ChunkVector newChunks = - std::make_shared>>>(); + ChunkVector newChunks = std::make_shared< + std::vector>>>(); newChunks->resize(this->chunks_->size()); @@ -142,7 +144,8 @@ public: Chunk &chunk = this->chunks_->at(i); size_t start = i == 0 ? this->firstChunkOffset_ : 0; - size_t end = i == chunk->size() - 1 ? this->lastChunkEnd_ : chunk->size(); + size_t end = + i == chunk->size() - 1 ? this->lastChunkEnd_ : chunk->size(); for (size_t j = start; j < end; j++) { if (chunk->at(j) == item) { @@ -176,7 +179,8 @@ public: Chunk &chunk = this->chunks_->at(i); size_t start = i == 0 ? this->firstChunkOffset_ : 0; - size_t end = i == chunk->size() - 1 ? this->lastChunkEnd_ : chunk->size(); + size_t end = + i == chunk->size() - 1 ? this->lastChunkEnd_ : chunk->size(); for (size_t j = start; j < end; j++) { if (x == index) { @@ -204,8 +208,9 @@ public: { std::lock_guard lock(this->mutex_); - return LimitedQueueSnapshot(this->chunks_, this->limit_ - this->space(), - this->firstChunkOffset_, this->lastChunkEnd_); + return LimitedQueueSnapshot( + this->chunks_, this->limit_ - this->space(), + this->firstChunkOffset_, this->lastChunkEnd_); } private: @@ -238,8 +243,8 @@ private: // need to delete the first chunk if (this->firstChunkOffset_ == this->chunks_->front()->size() - 1) { // copy the chunk vector - ChunkVector newVector = - std::make_shared>>>(); + ChunkVector newVector = std::make_shared< + std::vector>>>(); // delete first chunk bool first = true; diff --git a/src/messages/LimitedQueueSnapshot.hpp b/src/messages/LimitedQueueSnapshot.hpp index cd735445e..ce78dad94 100644 --- a/src/messages/LimitedQueueSnapshot.hpp +++ b/src/messages/LimitedQueueSnapshot.hpp @@ -12,8 +12,9 @@ class LimitedQueueSnapshot public: LimitedQueueSnapshot() = default; - LimitedQueueSnapshot(std::shared_ptr>>> chunks, - size_t length, size_t firstChunkOffset, size_t lastChunkEnd) + LimitedQueueSnapshot( + std::shared_ptr>>> chunks, + size_t length, size_t firstChunkOffset, size_t lastChunkEnd) : chunks_(chunks) , length_(length) , firstChunkOffset_(firstChunkOffset) diff --git a/src/messages/Message.cpp b/src/messages/Message.cpp index 2af8b8dcb..2104dd673 100644 --- a/src/messages/Message.cpp +++ b/src/messages/Message.cpp @@ -33,7 +33,8 @@ MessagePtr Message::createSystemMessage(const QString &text) MessagePtr message(new Message); message->addElement(new TimestampElement(QTime::currentTime())); - message->addElement(new TextElement(text, MessageElement::Text, MessageColor::System)); + message->addElement( + new TextElement(text, MessageElement::Text, MessageColor::System)); message->flags |= MessageFlags::System; message->flags |= MessageFlags::DoNotTriggerNotification; message->searchText = text; @@ -46,7 +47,8 @@ MessagePtr Message::createMessage(const QString &text) MessagePtr message(new Message); message->addElement(new TimestampElement(QTime::currentTime())); - message->addElement(new TextElement(text, MessageElement::Text, MessageColor::Text)); + message->addElement( + new TextElement(text, MessageElement::Text, MessageColor::Text)); message->searchText = text; return message; @@ -96,8 +98,10 @@ QString makeDuration(int timeoutSeconds) } // namespace -MessagePtr Message::createTimeoutMessage(const QString &username, const QString &durationInSeconds, - const QString &reason, bool multipleTimes) +MessagePtr Message::createTimeoutMessage(const QString &username, + const QString &durationInSeconds, + const QString &reason, + bool multipleTimes) { QString text; @@ -135,7 +139,8 @@ MessagePtr Message::createTimeoutMessage(const QString &username, const QString return message; } -MessagePtr Message::createTimeoutMessage(const BanAction &action, uint32_t count) +MessagePtr Message::createTimeoutMessage(const BanAction &action, + uint32_t count) { MessagePtr msg(new Message); @@ -178,7 +183,8 @@ MessagePtr Message::createTimeoutMessage(const BanAction &action, uint32_t count } } - msg->addElement(new TextElement(text, MessageElement::Text, MessageColor::System)); + msg->addElement( + new TextElement(text, MessageElement::Text, MessageColor::System)); msg->searchText = text; return msg; @@ -206,7 +212,8 @@ MessagePtr Message::createUntimeoutMessage(const UnbanAction &action) .arg(action.target.name); } - msg->addElement(new TextElement(text, MessageElement::Text, MessageColor::System)); + msg->addElement( + new TextElement(text, MessageElement::Text, MessageColor::System)); msg->searchText = text; return msg; diff --git a/src/messages/Message.hpp b/src/messages/Message.hpp index 102a8d32f..5a2a21074 100644 --- a/src/messages/Message.hpp +++ b/src/messages/Message.hpp @@ -68,13 +68,14 @@ public: static std::shared_ptr createSystemMessage(const QString &text); static std::shared_ptr createMessage(const QString &text); - static std::shared_ptr createTimeoutMessage(const QString &username, - const QString &durationInSeconds, - const QString &reason, bool multipleTimes); + static std::shared_ptr createTimeoutMessage( + const QString &username, const QString &durationInSeconds, + const QString &reason, bool multipleTimes); - static std::shared_ptr createTimeoutMessage(const BanAction &action, - uint32_t count = 1); - static std::shared_ptr createUntimeoutMessage(const UnbanAction &action); + static std::shared_ptr createTimeoutMessage( + const BanAction &action, uint32_t count = 1); + static std::shared_ptr createUntimeoutMessage( + const UnbanAction &action); }; using MessagePtr = std::shared_ptr; diff --git a/src/messages/MessageBuilder.cpp b/src/messages/MessageBuilder.cpp index 01a6ea629..0da3a8445 100644 --- a/src/messages/MessageBuilder.cpp +++ b/src/messages/MessageBuilder.cpp @@ -47,9 +47,12 @@ QString MessageBuilder::matchLink(const QString &string) { LinkParser linkParser(string); - static QRegularExpression httpRegex("\\bhttps?://", QRegularExpression::CaseInsensitiveOption); - static QRegularExpression ftpRegex("\\bftps?://", QRegularExpression::CaseInsensitiveOption); - static QRegularExpression spotifyRegex("\\bspotify:", QRegularExpression::CaseInsensitiveOption); + static QRegularExpression httpRegex( + "\\bhttps?://", QRegularExpression::CaseInsensitiveOption); + static QRegularExpression ftpRegex( + "\\bftps?://", QRegularExpression::CaseInsensitiveOption); + static QRegularExpression spotifyRegex( + "\\bspotify:", QRegularExpression::CaseInsensitiveOption); if (!linkParser.hasMatch()) { return QString(); @@ -57,7 +60,8 @@ QString MessageBuilder::matchLink(const QString &string) QString captured = linkParser.getCaptured(); - if (!captured.contains(httpRegex) && !captured.contains(ftpRegex) && !captured.contains(spotifyRegex)) { + if (!captured.contains(httpRegex) && !captured.contains(ftpRegex) && + !captured.contains(spotifyRegex)) { captured.insert(0, "http://"); } diff --git a/src/messages/MessageBuilder.hpp b/src/messages/MessageBuilder.hpp index d2a617ec3..6951f7255 100644 --- a/src/messages/MessageBuilder.hpp +++ b/src/messages/MessageBuilder.hpp @@ -23,7 +23,8 @@ public: template T *emplace(Args &&... args) { - static_assert(std::is_base_of::value, "T must extend MessageElement"); + static_assert(std::is_base_of::value, + "T must extend MessageElement"); T *element = new T(std::forward(args)...); this->append(element); diff --git a/src/messages/MessageElement.cpp b/src/messages/MessageElement.cpp index ff62aca4e..c2bff2f0d 100644 --- a/src/messages/MessageElement.cpp +++ b/src/messages/MessageElement.cpp @@ -67,14 +67,15 @@ ImageElement::ImageElement(ImagePtr image, MessageElement::Flags flags) // this->setTooltip(image->getTooltip()); } -void ImageElement::addToContainer(MessageLayoutContainer &container, MessageElement::Flags flags) +void ImageElement::addToContainer(MessageLayoutContainer &container, + MessageElement::Flags flags) { if (flags & this->getFlags()) { auto size = QSize(this->image_->width() * container.getScale(), this->image_->height() * container.getScale()); - container.addElement( - (new ImageLayoutElement(*this, this->image_, size))->setLink(this->getLink())); + container.addElement((new ImageLayoutElement(*this, this->image_, size)) + ->setLink(this->getLink())); } } @@ -83,7 +84,8 @@ EmoteElement::EmoteElement(const EmotePtr &emote, MessageElement::Flags flags) : MessageElement(flags) , emote_(emote) { - this->textElement_.reset(new TextElement(emote->getCopyString(), MessageElement::Misc)); + this->textElement_.reset( + new TextElement(emote->getCopyString(), MessageElement::Misc)); this->setTooltip(emote->tooltip.string); } @@ -93,7 +95,8 @@ EmotePtr EmoteElement::getEmote() const return this->emote_; } -void EmoteElement::addToContainer(MessageLayoutContainer &container, MessageElement::Flags flags) +void EmoteElement::addToContainer(MessageLayoutContainer &container, + MessageElement::Flags flags) { if (flags & this->getFlags()) { if (flags & MessageElement::EmoteImages) { @@ -103,11 +106,12 @@ void EmoteElement::addToContainer(MessageLayoutContainer &container, MessageElem auto size = QSize(int(container.getScale() * image->width()), int(container.getScale() * image->height())); - container.addElement( - (new ImageLayoutElement(*this, image, size))->setLink(this->getLink())); + container.addElement((new ImageLayoutElement(*this, image, size)) + ->setLink(this->getLink())); } else { if (this->textElement_) { - this->textElement_->addToContainer(container, MessageElement::Misc); + this->textElement_->addToContainer(container, + MessageElement::Misc); } } } @@ -126,20 +130,24 @@ TextElement::TextElement(const QString &text, MessageElement::Flags flags, } } -void TextElement::addToContainer(MessageLayoutContainer &container, MessageElement::Flags flags) +void TextElement::addToContainer(MessageLayoutContainer &container, + MessageElement::Flags flags) { auto app = getApp(); if (flags & this->getFlags()) { - QFontMetrics metrics = app->fonts->getFontMetrics(this->style_, container.getScale()); + QFontMetrics metrics = + app->fonts->getFontMetrics(this->style_, container.getScale()); for (Word &word : this->words_) { - auto getTextLayoutElement = [&](QString text, int width, bool trailingSpace) { + auto getTextLayoutElement = [&](QString text, int width, + bool trailingSpace) { QColor color = this->color_.getColor(*app->themes); app->themes->normalizeColor(color); - auto e = (new TextLayoutElement(*this, text, QSize(width, metrics.height()), color, - this->style_, container.getScale())) + auto e = (new TextLayoutElement( + *this, text, QSize(width, metrics.height()), + color, this->style_, container.getScale())) ->setLink(this->getLink()); e->setTrailingSpace(trailingSpace); return e; @@ -152,8 +160,8 @@ void TextElement::addToContainer(MessageLayoutContainer &container, MessageEleme // see if the text fits in the current line if (container.fitsInLine(word.width)) { - container.addElementNoLineBreak( - getTextLayoutElement(word.text, word.width, this->hasTrailingSpace())); + container.addElementNoLineBreak(getTextLayoutElement( + word.text, word.width, this->hasTrailingSpace())); continue; } @@ -162,8 +170,8 @@ void TextElement::addToContainer(MessageLayoutContainer &container, MessageEleme container.breakLine(); if (container.fitsInLine(word.width)) { - container.addElementNoLineBreak( - getTextLayoutElement(word.text, word.width, this->hasTrailingSpace())); + container.addElementNoLineBreak(getTextLayoutElement( + word.text, word.width, this->hasTrailingSpace())); continue; } } @@ -178,8 +186,8 @@ void TextElement::addToContainer(MessageLayoutContainer &container, MessageEleme int charWidth = metrics.width(text[i]); if (!container.fitsInLine(width + charWidth)) { - container.addElementNoLineBreak( - getTextLayoutElement(text.mid(wordStart, i - wordStart), width, false)); + container.addElementNoLineBreak(getTextLayoutElement( + text.mid(wordStart, i - wordStart), width, false)); container.breakLine(); wordStart = i; @@ -194,8 +202,8 @@ void TextElement::addToContainer(MessageLayoutContainer &container, MessageEleme width += charWidth; } - container.addElement( - getTextLayoutElement(text.mid(wordStart), width, this->hasTrailingSpace())); + container.addElement(getTextLayoutElement( + text.mid(wordStart), width, this->hasTrailingSpace())); container.breakLine(); } } @@ -230,7 +238,8 @@ TextElement *TimestampElement::formatTime(const QTime &time) QString format = locale.toString(time, getApp()->settings->timestampFormat); - return new TextElement(format, Flags::Timestamp, MessageColor::System, FontStyle::ChatMedium); + return new TextElement(format, Flags::Timestamp, MessageColor::System, + FontStyle::ChatMedium); } // TWITCH MODERATION @@ -243,15 +252,19 @@ void TwitchModerationElement::addToContainer(MessageLayoutContainer &container, MessageElement::Flags flags) { if (flags & MessageElement::ModeratorTools) { - QSize size(int(container.getScale() * 16), int(container.getScale() * 16)); + QSize size(int(container.getScale() * 16), + int(container.getScale() * 16)); - for (const auto &action : getApp()->moderationActions->items.getVector()) { + for (const auto &action : + getApp()->moderationActions->items.getVector()) { if (auto image = action.getImage()) { - container.addElement((new ImageLayoutElement(*this, image.get(), size)) - ->setLink(Link(Link::UserAction, action.getAction()))); + container.addElement( + (new ImageLayoutElement(*this, image.get(), size)) + ->setLink(Link(Link::UserAction, action.getAction()))); } else { container.addElement( - (new TextIconLayoutElement(*this, action.getLine1(), action.getLine2(), + (new TextIconLayoutElement(*this, action.getLine1(), + action.getLine2(), container.getScale(), size)) ->setLink(Link(Link::UserAction, action.getAction()))); } diff --git a/src/messages/MessageElement.hpp b/src/messages/MessageElement.hpp index 2aa25b7e7..cb7ffeebd 100644 --- a/src/messages/MessageElement.hpp +++ b/src/messages/MessageElement.hpp @@ -72,10 +72,11 @@ public: // - Chatterino top donator badge BadgeChatterino = (1 << 18), - // Rest of slots: ffz custom badge? bttv custom badge? mywaifu (puke) custom badge? + // Rest of slots: ffz custom badge? bttv custom badge? mywaifu (puke) + // custom badge? - Badges = BadgeGlobalAuthority | BadgeChannelAuthority | BadgeSubscription | BadgeVanity | - BadgeChatterino, + Badges = BadgeGlobalAuthority | BadgeChannelAuthority | + BadgeSubscription | BadgeVanity | BadgeChatterino, ChannelName = (1 << 19), @@ -89,7 +90,8 @@ public: AlwaysShow = (1 << 25), - // used in the ChannelView class to make the collapse buttons visible if needed + // used in the ChannelView class to make the collapse buttons visible if + // needed Collapsed = (1 << 26), // used for dynamic bold usernames @@ -100,8 +102,9 @@ public: LowercaseLink = (1 << 29), OriginalLink = (1 << 30), - Default = Timestamp | Badges | Username | BitsStatic | FfzEmoteImage | BttvEmoteImage | - TwitchEmoteImage | BitsAmount | Text | AlwaysShow, + Default = Timestamp | Badges | Username | BitsStatic | FfzEmoteImage | + BttvEmoteImage | TwitchEmoteImage | BitsAmount | Text | + AlwaysShow, }; enum UpdateFlags : char { @@ -121,7 +124,8 @@ public: bool hasTrailingSpace() const; Flags getFlags() const; - virtual void addToContainer(MessageLayoutContainer &container, MessageElement::Flags flags) = 0; + virtual void addToContainer(MessageLayoutContainer &container, + MessageElement::Flags flags) = 0; protected: MessageElement(Flags flags); @@ -139,7 +143,8 @@ class ImageElement : public MessageElement public: ImageElement(ImagePtr image, MessageElement::Flags flags); - void addToContainer(MessageLayoutContainer &container, MessageElement::Flags flags) override; + void addToContainer(MessageLayoutContainer &container, + MessageElement::Flags flags) override; private: ImagePtr image_; @@ -154,7 +159,8 @@ public: FontStyle style = FontStyle::ChatMedium); ~TextElement() override = default; - void addToContainer(MessageLayoutContainer &container, MessageElement::Flags flags) override; + void addToContainer(MessageLayoutContainer &container, + MessageElement::Flags flags) override; private: MessageColor color_; @@ -175,7 +181,8 @@ class EmoteElement : public MessageElement public: EmoteElement(const EmotePtr &data, MessageElement::Flags flags_); - void addToContainer(MessageLayoutContainer &container, MessageElement::Flags flags_) override; + void addToContainer(MessageLayoutContainer &container, + MessageElement::Flags flags_) override; EmotePtr getEmote() const; private: @@ -190,7 +197,8 @@ public: TimestampElement(QTime time_ = QTime::currentTime()); ~TimestampElement() override = default; - void addToContainer(MessageLayoutContainer &container, MessageElement::Flags flags) override; + void addToContainer(MessageLayoutContainer &container, + MessageElement::Flags flags) override; TextElement *formatTime(const QTime &time); @@ -200,14 +208,15 @@ private: QString format_; }; -// adds all the custom moderation buttons, adds a variable amount of items depending on settings -// fourtf: implement +// adds all the custom moderation buttons, adds a variable amount of items +// depending on settings fourtf: implement class TwitchModerationElement : public MessageElement { public: TwitchModerationElement(); - void addToContainer(MessageLayoutContainer &container, MessageElement::Flags flags) override; + void addToContainer(MessageLayoutContainer &container, + MessageElement::Flags flags) override; }; } // namespace chatterino diff --git a/src/messages/Selection.hpp b/src/messages/Selection.hpp index e5e6f3042..dbed15d30 100644 --- a/src/messages/Selection.hpp +++ b/src/messages/Selection.hpp @@ -26,7 +26,8 @@ struct SelectionItem { if (this->messageIndex < b.messageIndex) { return true; } - if (this->messageIndex == b.messageIndex && this->charIndex < b.charIndex) { + if (this->messageIndex == b.messageIndex && + this->charIndex < b.charIndex) { return true; } return false; @@ -39,7 +40,8 @@ struct SelectionItem { bool operator==(const SelectionItem &b) const { - return this->messageIndex == b.messageIndex && this->charIndex == b.charIndex; + return this->messageIndex == b.messageIndex && + this->charIndex == b.charIndex; } bool operator!=(const SelectionItem &b) const @@ -74,7 +76,8 @@ struct Selection { bool isSingleMessage() const { - return this->selectionMin.messageIndex == this->selectionMax.messageIndex; + return this->selectionMin.messageIndex == + this->selectionMax.messageIndex; } }; diff --git a/src/messages/layouts/MessageLayout.cpp b/src/messages/layouts/MessageLayout.cpp index 259da3439..376423dea 100644 --- a/src/messages/layouts/MessageLayout.cpp +++ b/src/messages/layouts/MessageLayout.cpp @@ -98,12 +98,14 @@ void MessageLayout::actuallyLayout(int width, MessageElement::Flags _flags) if (this->flags & MessageLayout::Expanded || (_flags & MessageElement::ModeratorTools && !(this->message_->flags & Message::MessageFlags::Disabled))) { - messageFlags = Message::MessageFlags(messageFlags & ~Message::MessageFlags::Collapsed); + messageFlags = Message::MessageFlags(messageFlags & + ~Message::MessageFlags::Collapsed); } this->container_.begin(width, this->scale_, messageFlags); - for (const std::unique_ptr &element : this->message_->getElements()) { + for (const std::unique_ptr &element : + this->message_->getElements()) { element->addToContainer(this->container_, _flags); } @@ -123,7 +125,8 @@ void MessageLayout::actuallyLayout(int width, MessageElement::Flags _flags) // Painting void MessageLayout::paint(QPainter &painter, int width, int y, int messageIndex, - Selection &selection, bool isLastReadMessage, bool isWindowFocused) + Selection &selection, bool isLastReadMessage, + bool isWindowFocused) { auto app = getApp(); QPixmap *pixmap = this->buffer_.get(); @@ -132,7 +135,8 @@ void MessageLayout::paint(QPainter &painter, int width, int y, int messageIndex, if (!pixmap) { #ifdef Q_OS_MACOS pixmap = new QPixmap(int(width * painter.device()->devicePixelRatioF()), - int(container_.getHeight() * painter.device()->devicePixelRatioF())); + int(container_.getHeight() * + painter.device()->devicePixelRatioF())); pixmap->setDevicePixelRatio(painter.device()->devicePixelRatioF()); #else pixmap = new QPixmap(width, std::max(16, this->container_.getHeight())); @@ -149,14 +153,16 @@ void MessageLayout::paint(QPainter &painter, int width, int y, int messageIndex, // draw on buffer painter.drawPixmap(0, y, *pixmap); - // painter.drawPixmap(0, y, this->container.width, this->container.getHeight(), *pixmap); + // painter.drawPixmap(0, y, this->container.width, + // this->container.getHeight(), *pixmap); // draw gif emotes this->container_.paintAnimatedElements(painter, y); // draw disabled if (this->message_->flags.HasFlag(Message::Disabled)) { - painter.fillRect(0, y, pixmap->width(), pixmap->height(), app->themes->messages.disabled); + painter.fillRect(0, y, pixmap->width(), pixmap->height(), + app->themes->messages.disabled); } // draw selection @@ -172,19 +178,23 @@ void MessageLayout::paint(QPainter &painter, int width, int y, int messageIndex, // draw last read message line if (isLastReadMessage) { - QColor color = isWindowFocused ? app->themes->tabs.selected.backgrounds.regular.color() - : app->themes->tabs.selected.backgrounds.unfocused.color(); + QColor color = + isWindowFocused + ? app->themes->tabs.selected.backgrounds.regular.color() + : app->themes->tabs.selected.backgrounds.unfocused.color(); - QBrush brush(color, - static_cast(app->settings->lastMessagePattern.getValue())); + QBrush brush(color, static_cast( + app->settings->lastMessagePattern.getValue())); - painter.fillRect(0, y + this->container_.getHeight() - 1, pixmap->width(), 1, brush); + painter.fillRect(0, y + this->container_.getHeight() - 1, + pixmap->width(), 1, brush); } this->bufferValid_ = true; } -void MessageLayout::updateBuffer(QPixmap *buffer, int /*messageIndex*/, Selection & /*selection*/) +void MessageLayout::updateBuffer(QPixmap *buffer, int /*messageIndex*/, + Selection & /*selection*/) { auto app = getApp(); @@ -212,8 +222,8 @@ void MessageLayout::updateBuffer(QPixmap *buffer, int /*messageIndex*/, Selectio #ifdef FOURTF // debug painter.setPen(QColor(255, 0, 0)); - painter.drawRect(buffer->rect().x(), buffer->rect().y(), buffer->rect().width() - 1, - buffer->rect().height() - 1); + painter.drawRect(buffer->rect().x(), buffer->rect().y(), + buffer->rect().width() - 1, buffer->rect().height() - 1); QTextOption option; option.setAlignment(Qt::AlignRight | Qt::AlignTop); diff --git a/src/messages/layouts/MessageLayout.hpp b/src/messages/layouts/MessageLayout.hpp index f7354a3ae..fc61c7d42 100644 --- a/src/messages/layouts/MessageLayout.hpp +++ b/src/messages/layouts/MessageLayout.hpp @@ -40,8 +40,9 @@ public: bool layout(int width, float scale_, MessageElement::Flags flags); // Painting - void paint(QPainter &painter, int width, int y, int messageIndex, Selection &selection, - bool isLastReadMessage, bool isWindowFocused); + void paint(QPainter &painter, int width, int y, int messageIndex, + Selection &selection, bool isLastReadMessage, + bool isWindowFocused); void invalidateBuffer(); void deleteBuffer(); void deleteCache(); diff --git a/src/messages/layouts/MessageLayoutContainer.cpp b/src/messages/layouts/MessageLayoutContainer.cpp index 19ecfae98..8f9fead59 100644 --- a/src/messages/layouts/MessageLayoutContainer.cpp +++ b/src/messages/layouts/MessageLayoutContainer.cpp @@ -9,7 +9,8 @@ #include #define COMPACT_EMOTES_OFFSET 6 -#define MAX_UNCOLLAPSED_LINES (getApp()->settings->collpseMessagesMinLines.getValue()) +#define MAX_UNCOLLAPSED_LINES \ + (getApp()->settings->collpseMessagesMinLines.getValue()) namespace chatterino { @@ -29,13 +30,15 @@ float MessageLayoutContainer::getScale() const } // methods -void MessageLayoutContainer::begin(int width, float scale, Message::MessageFlags flags) +void MessageLayoutContainer::begin(int width, float scale, + Message::MessageFlags flags) { this->clear(); this->width_ = width; this->scale_ = scale; this->flags_ = flags; - auto mediumFontMetrics = getApp()->fonts->getFontMetrics(FontStyle::ChatMedium, scale); + auto mediumFontMetrics = + getApp()->fonts->getFontMetrics(FontStyle::ChatMedium, scale); this->textLineHeight_ = mediumFontMetrics.height(); this->spaceWidth_ = mediumFontMetrics.width(' '); this->dotdotdotWidth_ = mediumFontMetrics.width("..."); @@ -66,7 +69,8 @@ void MessageLayoutContainer::addElement(MessageLayoutElement *element) this->_addElement(element); } -void MessageLayoutContainer::addElementNoLineBreak(MessageLayoutElement *element) +void MessageLayoutContainer::addElementNoLineBreak( + MessageLayoutElement *element) { this->_addElement(element); } @@ -76,7 +80,8 @@ bool MessageLayoutContainer::canAddElements() return this->canAddMessages_; } -void MessageLayoutContainer::_addElement(MessageLayoutElement *element, bool forceAdd) +void MessageLayoutContainer::_addElement(MessageLayoutElement *element, + bool forceAdd) { if (!this->canAddElements() && !forceAdd) { delete element; @@ -91,8 +96,9 @@ void MessageLayoutContainer::_addElement(MessageLayoutElement *element, bool for int newLineHeight = element->getRect().height(); // compact emote offset - bool isCompactEmote = !(this->flags_ & Message::DisableCompactEmotes) && - element->getCreator().getFlags() & MessageElement::EmoteImages; + bool isCompactEmote = + !(this->flags_ & Message::DisableCompactEmotes) && + element->getCreator().getFlags() & MessageElement::EmoteImages; if (isCompactEmote) { newLineHeight -= COMPACT_EMOTES_OFFSET * this->scale_; @@ -102,7 +108,8 @@ void MessageLayoutContainer::_addElement(MessageLayoutElement *element, bool for this->lineHeight_ = std::max(this->lineHeight_, newLineHeight); // set move element - element->setPosition(QPoint(this->currentX_, this->currentY_ - element->getRect().height())); + element->setPosition( + QPoint(this->currentX_, this->currentY_ - element->getRect().height())); // add element this->elements_.push_back(std::unique_ptr(element)); @@ -120,35 +127,42 @@ void MessageLayoutContainer::breakLine() int xOffset = 0; if (this->flags_ & Message::Centered && this->elements_.size() > 0) { - xOffset = (width_ - this->elements_.at(this->elements_.size() - 1)->getRect().right()) / 2; + xOffset = (width_ - this->elements_.at(this->elements_.size() - 1) + ->getRect() + .right()) / + 2; } for (size_t i = lineStart_; i < this->elements_.size(); i++) { MessageLayoutElement *element = this->elements_.at(i).get(); - bool isCompactEmote = !(this->flags_ & Message::DisableCompactEmotes) && - element->getCreator().getFlags() & MessageElement::EmoteImages; + bool isCompactEmote = + !(this->flags_ & Message::DisableCompactEmotes) && + element->getCreator().getFlags() & MessageElement::EmoteImages; int yExtra = 0; if (isCompactEmote) { yExtra = (COMPACT_EMOTES_OFFSET / 2) * this->scale_; } - // if (element->getCreator().getFlags() & MessageElement::Badges) { + // if (element->getCreator().getFlags() & MessageElement::Badges) + // { if (element->getRect().height() < this->textLineHeight_) { yExtra -= (this->textLineHeight_ - element->getRect().height()) / 2; } - element->setPosition(QPoint(element->getRect().x() + xOffset + this->margin.left, - element->getRect().y() + this->lineHeight_ + yExtra)); + element->setPosition( + QPoint(element->getRect().x() + xOffset + this->margin.left, + element->getRect().y() + this->lineHeight_ + yExtra)); } if (this->lines_.size() != 0) { this->lines_.back().endIndex = this->lineStart_; this->lines_.back().endCharIndex = this->charIndex_; } - this->lines_.push_back({(int)lineStart_, 0, this->charIndex_, 0, - QRect(-100000, this->currentY_, 200000, lineHeight_)}); + this->lines_.push_back( + {(int)lineStart_, 0, this->charIndex_, 0, + QRect(-100000, this->currentY_, 200000, lineHeight_)}); for (int i = this->lineStart_; i < this->elements_.size(); i++) { this->charIndex_ += this->elements_[i]->getSelectionIndexCount(); @@ -178,17 +192,20 @@ bool MessageLayoutContainer::fitsInLine(int _width) { return this->currentX_ + _width <= (this->width_ - this->margin.left - this->margin.right - - (this->line_ + 1 == MAX_UNCOLLAPSED_LINES ? this->dotdotdotWidth_ : 0)); + (this->line_ + 1 == MAX_UNCOLLAPSED_LINES ? this->dotdotdotWidth_ + : 0)); } void MessageLayoutContainer::end() { if (!this->canAddElements()) { - static TextElement dotdotdot("...", MessageElement::Collapsed, MessageColor::Link); + static TextElement dotdotdot("...", MessageElement::Collapsed, + MessageColor::Link); static QString dotdotdotText("..."); auto *element = new TextLayoutElement( - dotdotdot, dotdotdotText, QSize(this->dotdotdotWidth_, this->textLineHeight_), + dotdotdot, dotdotdotText, + QSize(this->dotdotdotWidth_, this->textLineHeight_), QColor("#00D80A"), FontStyle::ChatMediumBold, this->scale_); // getApp()->themes->messages.textColors.system @@ -235,7 +252,8 @@ MessageLayoutElement *MessageLayoutContainer::getElementAt(QPoint point) // painting void MessageLayoutContainer::paintElements(QPainter &painter) { - for (const std::unique_ptr &element : this->elements_) { + for (const std::unique_ptr &element : + this->elements_) { #ifdef FOURTF painter.setPen(QColor(0, 255, 0)); painter.drawRect(element->getRect()); @@ -245,9 +263,11 @@ void MessageLayoutContainer::paintElements(QPainter &painter) } } -void MessageLayoutContainer::paintAnimatedElements(QPainter &painter, int yOffset) +void MessageLayoutContainer::paintAnimatedElements(QPainter &painter, + int yOffset) { - for (const std::unique_ptr &element : this->elements_) { + for (const std::unique_ptr &element : + this->elements_) { element->paintAnimated(painter, yOffset); } } @@ -273,7 +293,8 @@ void MessageLayoutContainer::paintSelection(QPainter &painter, int messageIndex, rect.setTop(std::max(0, rect.top()) + yOffset); rect.setBottom(std::min(this->height_, rect.bottom()) + yOffset); rect.setLeft(this->elements_[line.startIndex]->getRect().left()); - rect.setRight(this->elements_[line.endIndex - 1]->getRect().right()); + rect.setRight( + this->elements_[line.endIndex - 1]->getRect().right()); painter.fillRect(rect, selectionColor); } @@ -302,16 +323,19 @@ void MessageLayoutContainer::paintSelection(QPainter &painter, int messageIndex, int c = this->elements_[i]->getSelectionIndexCount(); if (index + c > selection.selectionMin.charIndex) { - x = this->elements_[i]->getXFromIndex(selection.selectionMin.charIndex - index); + x = this->elements_[i]->getXFromIndex( + selection.selectionMin.charIndex - index); // ends in same line if (selection.selectionMax.messageIndex == messageIndex && - line.endCharIndex > /*=*/selection.selectionMax.charIndex) // + line.endCharIndex > + /*=*/selection.selectionMax.charIndex) // { returnAfter = true; index = line.startCharIndex; for (int i = line.startIndex; i < line.endIndex; i++) { - int c = this->elements_[i]->getSelectionIndexCount(); + int c = + this->elements_[i]->getSelectionIndexCount(); if (index + c > selection.selectionMax.charIndex) { r = this->elements_[i]->getXFromIndex( @@ -330,9 +354,15 @@ void MessageLayoutContainer::paintSelection(QPainter &painter, int messageIndex, QRect rect = line.rect; rect.setTop(std::max(0, rect.top()) + yOffset); - rect.setBottom(std::min(this->height_, rect.bottom()) + yOffset); - rect.setLeft(this->elements_[line.startIndex]->getRect().left()); - rect.setRight(this->elements_[line.endIndex - 1]->getRect().right()); + rect.setBottom( + std::min(this->height_, rect.bottom()) + + yOffset); + rect.setLeft(this->elements_[line.startIndex] + ->getRect() + .left()); + rect.setRight(this->elements_[line.endIndex - 1] + ->getRect() + .right()); painter.fillRect(rect, selectionColor); } @@ -378,7 +408,8 @@ void MessageLayoutContainer::paintSelection(QPainter &painter, int messageIndex, rect.setTop(std::max(0, rect.top()) + yOffset); rect.setBottom(std::min(this->height_, rect.bottom()) + yOffset); rect.setLeft(this->elements_[line.startIndex]->getRect().left()); - rect.setRight(this->elements_[line.endIndex - 1]->getRect().right()); + rect.setRight( + this->elements_[line.endIndex - 1]->getRect().right()); painter.fillRect(rect, selectionColor); continue; @@ -390,7 +421,8 @@ void MessageLayoutContainer::paintSelection(QPainter &painter, int messageIndex, int c = this->elements_[i]->getSelectionIndexCount(); if (index + c > selection.selectionMax.charIndex) { - r = this->elements_[i]->getXFromIndex(selection.selectionMax.charIndex - index); + r = this->elements_[i]->getXFromIndex( + selection.selectionMax.charIndex - index); break; } @@ -424,11 +456,13 @@ int MessageLayoutContainer::getSelectionIndex(QPoint point) } } - int lineStart = line == this->lines_.end() ? this->lines_.back().startIndex : line->startIndex; + int lineStart = line == this->lines_.end() ? this->lines_.back().startIndex + : line->startIndex; if (line != this->lines_.end()) { line++; } - int lineEnd = line == this->lines_.end() ? this->elements_.size() : line->startIndex; + int lineEnd = + line == this->lines_.end() ? this->elements_.size() : line->startIndex; int index = 0; diff --git a/src/messages/layouts/MessageLayoutContainer.hpp b/src/messages/layouts/MessageLayoutContainer.hpp index 3e00effce..3b20e8191 100644 --- a/src/messages/layouts/MessageLayoutContainer.hpp +++ b/src/messages/layouts/MessageLayoutContainer.hpp @@ -66,7 +66,8 @@ struct MessageLayoutContainer { // painting void paintElements(QPainter &painter); void paintAnimatedElements(QPainter &painter, int yOffset); - void paintSelection(QPainter &painter, int messageIndex, Selection &selection, int yOffset); + void paintSelection(QPainter &painter, int messageIndex, + Selection &selection, int yOffset); // selection int getSelectionIndex(QPoint point); diff --git a/src/messages/layouts/MessageLayoutElement.cpp b/src/messages/layouts/MessageLayoutElement.cpp index 3e2c649d2..e8d333e2b 100644 --- a/src/messages/layouts/MessageLayoutElement.cpp +++ b/src/messages/layouts/MessageLayoutElement.cpp @@ -14,7 +14,8 @@ const QRect &MessageLayoutElement::getRect() const return this->rect_; } -MessageLayoutElement::MessageLayoutElement(MessageElement &creator, const QSize &size) +MessageLayoutElement::MessageLayoutElement(MessageElement &creator, + const QSize &size) : creator_(creator) { this->rect_.setSize(size); @@ -63,14 +64,16 @@ const Link &MessageLayoutElement::getLink() const // IMAGE // -ImageLayoutElement::ImageLayoutElement(MessageElement &creator, ImagePtr image, const QSize &size) +ImageLayoutElement::ImageLayoutElement(MessageElement &creator, ImagePtr image, + const QSize &size) : MessageLayoutElement(creator, size) , image_(image) { this->trailingSpace = creator.hasTrailingSpace(); } -void ImageLayoutElement::addCopyTextToString(QString &str, int from, int to) const +void ImageLayoutElement::addCopyTextToString(QString &str, int from, + int to) const { // str += this->image_->getCopyString(); str += "not implemented"; @@ -134,8 +137,9 @@ int ImageLayoutElement::getXFromIndex(int index) // TEXT // -TextLayoutElement::TextLayoutElement(MessageElement &_creator, QString &_text, const QSize &_size, - QColor _color, FontStyle _style, float _scale) +TextLayoutElement::TextLayoutElement(MessageElement &_creator, QString &_text, + const QSize &_size, QColor _color, + FontStyle _style, float _scale) : MessageLayoutElement(_creator, _size) , text(_text) , color(_color) @@ -144,7 +148,8 @@ TextLayoutElement::TextLayoutElement(MessageElement &_creator, QString &_text, c { } -void TextLayoutElement::addCopyTextToString(QString &str, int from, int to) const +void TextLayoutElement::addCopyTextToString(QString &str, int from, + int to) const { str += this->text.mid(from, to - from); @@ -166,8 +171,9 @@ void TextLayoutElement::paint(QPainter &painter) painter.setFont(app->fonts->getFont(this->style, this->scale)); - painter.drawText(QRectF(this->getRect().x(), this->getRect().y(), 10000, 10000), this->text, - QTextOption(Qt::AlignLeft | Qt::AlignTop)); + painter.drawText( + QRectF(this->getRect().x(), this->getRect().y(), 10000, 10000), + this->text, QTextOption(Qt::AlignLeft | Qt::AlignTop)); } void TextLayoutElement::paintAnimated(QPainter &, int) @@ -219,8 +225,10 @@ int TextLayoutElement::getXFromIndex(int index) } // TEXT ICON -TextIconLayoutElement::TextIconLayoutElement(MessageElement &creator, const QString &_line1, - const QString &_line2, float _scale, const QSize &size) +TextIconLayoutElement::TextIconLayoutElement(MessageElement &creator, + const QString &_line1, + const QString &_line2, + float _scale, const QSize &size) : MessageLayoutElement(creator, size) , scale(_scale) , line1(_line1) @@ -228,7 +236,8 @@ TextIconLayoutElement::TextIconLayoutElement(MessageElement &creator, const QStr { } -void TextIconLayoutElement::addCopyTextToString(QString &str, int from, int to) const +void TextIconLayoutElement::addCopyTextToString(QString &str, int from, + int to) const { } @@ -254,11 +263,12 @@ void TextIconLayoutElement::paint(QPainter &painter) painter.drawText(_rect, this->line1, option); } else { painter.drawText( - QPoint(this->getRect().x(), this->getRect().y() + this->getRect().height() / 2), + QPoint(this->getRect().x(), + this->getRect().y() + this->getRect().height() / 2), this->line1); - painter.drawText( - QPoint(this->getRect().x(), this->getRect().y() + this->getRect().height()), - this->line2); + painter.drawText(QPoint(this->getRect().x(), + this->getRect().y() + this->getRect().height()), + this->line2); } } diff --git a/src/messages/layouts/MessageLayoutElement.hpp b/src/messages/layouts/MessageLayoutElement.hpp index d8221726f..44b4c9316 100644 --- a/src/messages/layouts/MessageLayoutElement.hpp +++ b/src/messages/layouts/MessageLayoutElement.hpp @@ -31,7 +31,8 @@ public: MessageLayoutElement *setTrailingSpace(bool value); MessageLayoutElement *setLink(const Link &link_); - virtual void addCopyTextToString(QString &str, int from = 0, int to = INT_MAX) const = 0; + virtual void addCopyTextToString(QString &str, int from = 0, + int to = INT_MAX) const = 0; virtual int getSelectionIndexCount() = 0; virtual void paint(QPainter &painter) = 0; virtual void paintAnimated(QPainter &painter, int yOffset) = 0; @@ -52,10 +53,12 @@ private: class ImageLayoutElement : public MessageLayoutElement { public: - ImageLayoutElement(MessageElement &creator, ImagePtr image, const QSize &size); + ImageLayoutElement(MessageElement &creator, ImagePtr image, + const QSize &size); protected: - void addCopyTextToString(QString &str, int from = 0, int to = INT_MAX) const override; + void addCopyTextToString(QString &str, int from = 0, + int to = INT_MAX) const override; int getSelectionIndexCount() override; void paint(QPainter &painter) override; void paintAnimated(QPainter &painter, int yOffset) override; @@ -70,11 +73,13 @@ private: class TextLayoutElement : public MessageLayoutElement { public: - TextLayoutElement(MessageElement &creator_, QString &text, const QSize &size, QColor color, - FontStyle style, float scale); + TextLayoutElement(MessageElement &creator_, QString &text, + const QSize &size, QColor color, FontStyle style, + float scale); protected: - void addCopyTextToString(QString &str, int from = 0, int to = INT_MAX) const override; + void addCopyTextToString(QString &str, int from = 0, + int to = INT_MAX) const override; int getSelectionIndexCount() override; void paint(QPainter &painter) override; void paintAnimated(QPainter &painter, int yOffset) override; @@ -93,11 +98,12 @@ private: class TextIconLayoutElement : public MessageLayoutElement { public: - TextIconLayoutElement(MessageElement &creator_, const QString &line1, const QString &line2, - float scale, const QSize &size); + TextIconLayoutElement(MessageElement &creator_, const QString &line1, + const QString &line2, float scale, const QSize &size); protected: - void addCopyTextToString(QString &str, int from = 0, int to = INT_MAX) const override; + void addCopyTextToString(QString &str, int from = 0, + int to = INT_MAX) const override; int getSelectionIndexCount() override; void paint(QPainter &painter) override; void paintAnimated(QPainter &painter, int yOffset) override; diff --git a/src/providers/bttv/BttvEmotes.cpp b/src/providers/bttv/BttvEmotes.cpp index 570879210..b9ca7ba99 100644 --- a/src/providers/bttv/BttvEmotes.cpp +++ b/src/providers/bttv/BttvEmotes.cpp @@ -13,11 +13,13 @@ namespace chatterino { namespace { -Url getEmoteLink(QString urlTemplate, const EmoteId &id, const QString &emoteScale) +Url getEmoteLink(QString urlTemplate, const EmoteId &id, + const QString &emoteScale) { urlTemplate.detach(); - return {urlTemplate.replace("{{id}}", id.string).replace("{{image}}", emoteScale)}; + return {urlTemplate.replace("{{id}}", id.string) + .replace("{{image}}", emoteScale)}; } } // namespace @@ -76,23 +78,26 @@ void BttvEmotes::loadGlobalEmotes() request.execute(); } -std::pair BttvEmotes::parseGlobalEmotes(const QJsonObject &jsonRoot, - const EmoteMap ¤tEmotes) +std::pair BttvEmotes::parseGlobalEmotes( + const QJsonObject &jsonRoot, const EmoteMap ¤tEmotes) { auto emotes = EmoteMap(); auto jsonEmotes = jsonRoot.value("emotes").toArray(); - auto urlTemplate = QString("https:" + jsonRoot.value("urlTemplate").toString()); + auto urlTemplate = + QString("https:" + jsonRoot.value("urlTemplate").toString()); for (const QJsonValue &jsonEmote : jsonEmotes) { auto id = EmoteId{jsonEmote.toObject().value("id").toString()}; auto name = EmoteName{jsonEmote.toObject().value("code").toString()}; - auto emote = Emote({name, - ImageSet{Image::fromUrl(getEmoteLink(urlTemplate, id, "1x"), 1), - Image::fromUrl(getEmoteLink(urlTemplate, id, "2x"), 0.5), - Image::fromUrl(getEmoteLink(urlTemplate, id, "3x"), 0.25)}, - Tooltip{name.string + "
Global Bttv Emote"}, - Url{"https://manage.betterttv.net/emotes/" + id.string}}); + auto emote = Emote( + {name, + ImageSet{ + Image::fromUrl(getEmoteLink(urlTemplate, id, "1x"), 1), + Image::fromUrl(getEmoteLink(urlTemplate, id, "2x"), 0.5), + Image::fromUrl(getEmoteLink(urlTemplate, id, "3x"), 0.25)}, + Tooltip{name.string + "
Global Bttv Emote"}, + Url{"https://manage.betterttv.net/emotes/" + id.string}}); auto it = currentEmotes.find(name); if (it != currentEmotes.end() && *it->second == emote) { diff --git a/src/providers/bttv/BttvEmotes.hpp b/src/providers/bttv/BttvEmotes.hpp index f8273afb8..f85333df7 100644 --- a/src/providers/bttv/BttvEmotes.hpp +++ b/src/providers/bttv/BttvEmotes.hpp @@ -10,7 +10,8 @@ namespace chatterino { class BttvEmotes final : std::enable_shared_from_this { - static constexpr const char *globalEmoteApiUrl = "https://api.betterttv.net/2/emotes"; + static constexpr const char *globalEmoteApiUrl = + "https://api.betterttv.net/2/emotes"; public: // BttvEmotes(); @@ -22,8 +23,8 @@ public: void loadGlobalEmotes(); private: - std::pair parseGlobalEmotes(const QJsonObject &jsonRoot, - const EmoteMap ¤tEmotes); + std::pair parseGlobalEmotes( + const QJsonObject &jsonRoot, const EmoteMap ¤tEmotes); UniqueAccess globalEmotes_; // UniqueAccess channelEmoteCache_; diff --git a/src/providers/bttv/LoadBttvChannelEmote.cpp b/src/providers/bttv/LoadBttvChannelEmote.cpp index 9829c6e07..ccc52a130 100644 --- a/src/providers/bttv/LoadBttvChannelEmote.cpp +++ b/src/providers/bttv/LoadBttvChannelEmote.cpp @@ -11,12 +11,16 @@ namespace chatterino { -static Url getEmoteLink(QString urlTemplate, const EmoteId &id, const QString &emoteScale); -static std::pair bttvParseChannelEmotes(const QJsonObject &jsonRoot); +static Url getEmoteLink(QString urlTemplate, const EmoteId &id, + const QString &emoteScale); +static std::pair bttvParseChannelEmotes( + const QJsonObject &jsonRoot); -void loadBttvChannelEmotes(const QString &channelName, std::function callback) +void loadBttvChannelEmotes(const QString &channelName, + std::function callback) { - auto request = NetworkRequest(QString(bttvChannelEmoteApiUrl) + channelName); + auto request = + NetworkRequest(QString(bttvChannelEmoteApiUrl) + channelName); request.setCaller(QThread::currentThread()); request.setTimeout(3000); @@ -31,14 +35,17 @@ void loadBttvChannelEmotes(const QString &channelName, std::function bttvParseChannelEmotes(const QJsonObject &jsonRoot) +static std::pair bttvParseChannelEmotes( + const QJsonObject &jsonRoot) { - static UniqueAccess>> cache_; + static UniqueAccess>> + cache_; auto cache = cache_.access(); auto emotes = EmoteMap(); auto jsonEmotes = jsonRoot.value("emotes").toArray(); - auto urlTemplate = QString("https:" + jsonRoot.value("urlTemplate").toString()); + auto urlTemplate = + QString("https:" + jsonRoot.value("urlTemplate").toString()); for (auto jsonEmote_ : jsonEmotes) { auto jsonEmote = jsonEmote_.toObject(); @@ -47,30 +54,35 @@ static std::pair bttvParseChannelEmotes(const QJsonObject &js auto name = EmoteName{jsonEmote.value("code").toString()}; // emoteObject.value("imageType").toString(); - auto emote = Emote({name, - ImageSet{Image::fromUrl(getEmoteLink(urlTemplate, id, "1x"), 1), - Image::fromUrl(getEmoteLink(urlTemplate, id, "2x"), 0.5), - Image::fromUrl(getEmoteLink(urlTemplate, id, "3x"), 0.25)}, - Tooltip{name.string + "
Channel Bttv Emote"}, - Url{"https://manage.betterttv.net/emotes/" + id.string}}); + auto emote = Emote( + {name, + ImageSet{ + Image::fromUrl(getEmoteLink(urlTemplate, id, "1x"), 1), + Image::fromUrl(getEmoteLink(urlTemplate, id, "2x"), 0.5), + Image::fromUrl(getEmoteLink(urlTemplate, id, "3x"), 0.25)}, + Tooltip{name.string + "
Channel Bttv Emote"}, + Url{"https://manage.betterttv.net/emotes/" + id.string}}); auto shared = (*cache)[id].lock(); if (shared && *shared == emote) { // reuse old shared_ptr if nothing changed emotes[name] = shared; } else { - (*cache)[id] = emotes[name] = std::make_shared(std::move(emote)); + (*cache)[id] = emotes[name] = + std::make_shared(std::move(emote)); } } return {Success, std::move(emotes)}; } -static Url getEmoteLink(QString urlTemplate, const EmoteId &id, const QString &emoteScale) +static Url getEmoteLink(QString urlTemplate, const EmoteId &id, + const QString &emoteScale) { urlTemplate.detach(); - return {urlTemplate.replace("{{id}}", id.string).replace("{{image}}", emoteScale)}; + return {urlTemplate.replace("{{id}}", id.string) + .replace("{{image}}", emoteScale)}; } } // namespace chatterino diff --git a/src/providers/bttv/LoadBttvChannelEmote.hpp b/src/providers/bttv/LoadBttvChannelEmote.hpp index ef9ca160e..ffbb50469 100644 --- a/src/providers/bttv/LoadBttvChannelEmote.hpp +++ b/src/providers/bttv/LoadBttvChannelEmote.hpp @@ -7,8 +7,10 @@ class QString; namespace chatterino { class EmoteMap; -constexpr const char *bttvChannelEmoteApiUrl = "https://api.betterttv.net/2/channels/"; +constexpr const char *bttvChannelEmoteApiUrl = + "https://api.betterttv.net/2/channels/"; -void loadBttvChannelEmotes(const QString &channelName, std::function callback); +void loadBttvChannelEmotes(const QString &channelName, + std::function callback); } // namespace chatterino diff --git a/src/providers/chatterino/ChatterinoBadges.cpp b/src/providers/chatterino/ChatterinoBadges.cpp index 01dc82aa4..98b1841d4 100644 --- a/src/providers/chatterino/ChatterinoBadges.cpp +++ b/src/providers/chatterino/ChatterinoBadges.cpp @@ -32,11 +32,13 @@ void ChatterinoBadges::loadChatterinoBadges() for (auto jsonBadge_ : jsonRoot.value("badges").toArray()) { auto jsonBadge = jsonBadge_.toObject(); - auto emote = Emote{EmoteName{}, ImageSet{Url{jsonBadge.value("image").toString()}}, - Tooltip{jsonBadge.value("tooltip").toString()}, Url{}}; + auto emote = Emote{ + EmoteName{}, ImageSet{Url{jsonBadge.value("image").toString()}}, + Tooltip{jsonBadge.value("tooltip").toString()}, Url{}}; for (auto jsonUser : jsonBadge.value("users").toArray()) { - replacement.add(UserName{jsonUser.toString()}, std::move(emote)); + replacement.add(UserName{jsonUser.toString()}, + std::move(emote)); } } diff --git a/src/providers/emoji/Emojis.cpp b/src/providers/emoji/Emojis.cpp index 064e693f1..ec7bac2b8 100644 --- a/src/providers/emoji/Emojis.cpp +++ b/src/providers/emoji/Emojis.cpp @@ -15,7 +15,8 @@ namespace chatterino { namespace { -void parseEmoji(const std::shared_ptr &emojiData, const rapidjson::Value &unparsedEmoji, +void parseEmoji(const std::shared_ptr &emojiData, + const rapidjson::Value &unparsedEmoji, QString shortCode = QString()) { static uint unicodeBytes[4]; @@ -80,7 +81,8 @@ void parseEmoji(const std::shared_ptr &emojiData, const rapidjson::Va int numUnicodeBytes = 0; for (const QString &unicodeCharacter : unicodeCharacters) { - unicodeBytes[numUnicodeBytes++] = QString(unicodeCharacter).toUInt(nullptr, 16); + unicodeBytes[numUnicodeBytes++] = + QString(unicodeCharacter).toUInt(nullptr, 16); } emojiData->value = QString::fromUcs4(unicodeBytes, numUnicodeBytes); @@ -116,8 +118,8 @@ void Emojis::loadEmojis() rapidjson::ParseResult result = root.Parse(data.toUtf8(), data.length()); if (result.Code() != rapidjson::kParseErrorNone) { - Log("JSON parse error: {} ({})", rapidjson::GetParseError_En(result.Code()), - result.Offset()); + Log("JSON parse error: {} ({})", + rapidjson::GetParseError_En(result.Code()), result.Offset()); return; } @@ -135,7 +137,8 @@ void Emojis::loadEmojis() this->emojis.insert(emojiData->unifiedCode, emojiData); if (unparsedEmoji.HasMember("skin_variations")) { - for (const auto &skinVariation : unparsedEmoji["skin_variations"].GetObject()) { + for (const auto &skinVariation : + unparsedEmoji["skin_variations"].GetObject()) { std::string tone = skinVariation.name.GetString(); const auto &variation = skinVariation.value; @@ -143,20 +146,23 @@ void Emojis::loadEmojis() auto toneNameIt = toneNames.find(tone); if (toneNameIt == toneNames.end()) { - Log("Tone with key {} does not exist in tone names map", tone); + Log("Tone with key {} does not exist in tone names map", + tone); continue; } parseEmoji(variationEmojiData, variation, emojiData->shortCodes[0] + "_" + toneNameIt->second); - this->emojiShortCodeToEmoji_.insert(variationEmojiData->shortCodes[0], - variationEmojiData); + this->emojiShortCodeToEmoji_.insert( + variationEmojiData->shortCodes[0], variationEmojiData); this->shortCodes.push_back(variationEmojiData->shortCodes[0]); - this->emojiFirstByte_[variationEmojiData->value.at(0)].append(variationEmojiData); + this->emojiFirstByte_[variationEmojiData->value.at(0)].append( + variationEmojiData); - this->emojis.insert(variationEmojiData->unifiedCode, variationEmojiData); + this->emojis.insert(variationEmojiData->unifiedCode, + variationEmojiData); } } } @@ -196,14 +202,16 @@ void Emojis::loadEmojiOne2Capabilities() void Emojis::sortEmojis() { for (auto &p : this->emojiFirstByte_) { - std::stable_sort(p.begin(), p.end(), [](const auto &lhs, const auto &rhs) { - return lhs->value.length() > rhs->value.length(); - }); + std::stable_sort(p.begin(), p.end(), + [](const auto &lhs, const auto &rhs) { + return lhs->value.length() > rhs->value.length(); + }); } auto &p = this->shortCodes; - std::stable_sort(p.begin(), p.end(), - [](const auto &lhs, const auto &rhs) { return lhs < rhs; }); + std::stable_sort(p.begin(), p.end(), [](const auto &lhs, const auto &rhs) { + return lhs < rhs; + }); } void Emojis::loadEmojiSet() @@ -212,7 +220,8 @@ void Emojis::loadEmojiSet() app->settings->emojiSet.connect([=](const auto &emojiSet, auto) { Log("Using emoji set {}", emojiSet); - this->emojis.each([=](const auto &name, std::shared_ptr &emoji) { + this->emojis.each([=](const auto &name, + std::shared_ptr &emoji) { QString emojiSetToUse = emojiSet; // clang-format off static std::map emojiSets = { @@ -259,20 +268,22 @@ void Emojis::loadEmojiSet() } } code = code.toLower(); - QString urlPrefix = "https://cdnjs.cloudflare.com/ajax/libs/emojione/2.2.6/assets/png/"; + QString urlPrefix = "https://cdnjs.cloudflare.com/ajax/libs/" + "emojione/2.2.6/assets/png/"; auto it = emojiSets.find(emojiSetToUse); if (it != emojiSets.end()) { urlPrefix = it->second; } QString url = urlPrefix + code + ".png"; - emoji->emote = std::make_shared( - Emote{EmoteName{emoji->value}, ImageSet{Image::fromUrl({url}, 0.35)}, - Tooltip{":" + emoji->shortCodes[0] + ":
Emoji"}, Url{}}); + emoji->emote = std::make_shared(Emote{ + EmoteName{emoji->value}, ImageSet{Image::fromUrl({url}, 0.35)}, + Tooltip{":" + emoji->shortCodes[0] + ":
Emoji"}, Url{}}); }); }); } -std::vector> Emojis::parse(const QString &text) +std::vector> Emojis::parse( + const QString &text) { auto result = std::vector>(); int lastParsedEmojiEndIndex = 0; @@ -330,11 +341,13 @@ std::vector> Emojis::parse(const QString &text int currentParsedEmojiFirstIndex = i; int currentParsedEmojiEndIndex = i + (matchedEmojiLength); - int charactersFromLastParsedEmoji = currentParsedEmojiFirstIndex - lastParsedEmojiEndIndex; + int charactersFromLastParsedEmoji = + currentParsedEmojiFirstIndex - lastParsedEmojiEndIndex; if (charactersFromLastParsedEmoji > 0) { // Add characters inbetween emojis - result.emplace_back(text.mid(lastParsedEmojiEndIndex, charactersFromLastParsedEmoji)); + result.emplace_back(text.mid(lastParsedEmojiEndIndex, + charactersFromLastParsedEmoji)); } // Push the emoji as a word to parsedWords @@ -365,7 +378,8 @@ QString Emojis::replaceShortCodes(const QString &text) auto capturedString = match.captured(); - QString matchString = capturedString.toLower().mid(1, capturedString.size() - 2); + QString matchString = + capturedString.toLower().mid(1, capturedString.size() - 2); auto emojiIt = this->emojiShortCodeToEmoji_.constFind(matchString); @@ -375,7 +389,8 @@ QString Emojis::replaceShortCodes(const QString &text) auto emojiData = emojiIt.value(); - ret.replace(offset + match.capturedStart(), match.capturedLength(), emojiData->value); + ret.replace(offset + match.capturedStart(), match.capturedLength(), + emojiData->value); offset += emojiData->value.size() - match.capturedLength(); } diff --git a/src/providers/emoji/Emojis.hpp b/src/providers/emoji/Emojis.hpp index 59d184d01..6a2f4e74e 100644 --- a/src/providers/emoji/Emojis.hpp +++ b/src/providers/emoji/Emojis.hpp @@ -15,7 +15,8 @@ namespace chatterino { struct EmojiData { - // actual byte-representation of the emoji (i.e. \154075\156150 which is :male:) + // actual byte-representation of the emoji (i.e. \154075\156150 which is + // :male:) QString value; // i.e. 204e-50a2 @@ -57,7 +58,8 @@ private: // shortCodeToEmoji maps strings like "sunglasses" to its emoji QMap> emojiShortCodeToEmoji_; - // Maps the first character of the emoji unicode string to a vector of possible emojis + // Maps the first character of the emoji unicode string to a vector of + // possible emojis QMap>> emojiFirstByte_; }; diff --git a/src/providers/ffz/FfzEmotes.cpp b/src/providers/ffz/FfzEmotes.cpp index 3a9763b22..b5fe65aa2 100644 --- a/src/providers/ffz/FfzEmotes.cpp +++ b/src/providers/ffz/FfzEmotes.cpp @@ -20,8 +20,8 @@ Url getEmoteLink(const QJsonObject &urls, const QString &emoteScale) return {"https:" + emote.toString()}; } -void fillInEmoteData(const QJsonObject &urls, const EmoteName &name, const QString &tooltip, - Emote &emoteData) +void fillInEmoteData(const QJsonObject &urls, const EmoteName &name, + const QString &tooltip, Emote &emoteData) { auto url1x = getEmoteLink(urls, "1"); auto url2x = getEmoteLink(urls, "2"); @@ -30,7 +30,8 @@ void fillInEmoteData(const QJsonObject &urls, const EmoteName &name, const QStri //, code, tooltip emoteData.name = name; emoteData.images = - ImageSet{Image::fromUrl(url1x, 1), Image::fromUrl(url2x, 0.5), Image::fromUrl(url3x, 0.25)}; + ImageSet{Image::fromUrl(url1x, 1), Image::fromUrl(url2x, 0.5), + Image::fromUrl(url3x, 0.25)}; emoteData.tooltip = {tooltip}; } } // namespace @@ -67,8 +68,9 @@ void FfzEmotes::loadGlobalEmotes() NetworkRequest request(url); request.setCaller(QThread::currentThread()); request.setTimeout(30000); - request.onSuccess( - [this](auto result) -> Outcome { return this->parseGlobalEmotes(result.parseJson()); }); + request.onSuccess([this](auto result) -> Outcome { + return this->parseGlobalEmotes(result.parseJson()); + }); request.execute(); } @@ -90,10 +92,12 @@ Outcome FfzEmotes::parseGlobalEmotes(const QJsonObject &jsonRoot) auto urls = jsonEmote.value("urls").toObject(); auto emote = Emote(); - fillInEmoteData(urls, name, name.string + "
Global FFZ Emote", emote); - emote.homePage = Url{QString("https://www.frankerfacez.com/emoticon/%1-%2") - .arg(id.string) - .arg(name.string)}; + fillInEmoteData(urls, name, name.string + "
Global FFZ Emote", + emote); + emote.homePage = + Url{QString("https://www.frankerfacez.com/emoticon/%1-%2") + .arg(id.string) + .arg(name.string)}; replacement.add(name, emote); } @@ -105,7 +109,8 @@ Outcome FfzEmotes::parseGlobalEmotes(const QJsonObject &jsonRoot) void FfzEmotes::loadChannelEmotes(const QString &channelName, std::function callback) { - // printf("[FFZEmotes] Reload FFZ Channel Emotes for channel %s\n", qPrintable(channelName)); + // printf("[FFZEmotes] Reload FFZ Channel Emotes for channel %s\n", + // qPrintable(channelName)); // QString url("https://api.frankerfacez.com/v1/room/" + channelName); @@ -145,10 +150,11 @@ Outcome parseChannelEmotes(const QJsonObject &jsonRoot) // QJsonObject urls = emoteObject.value("urls").toObject(); - // auto emote = this->channelEmoteCache_.getOrAdd(id, [id, &code, &urls] { + // auto emote = this->channelEmoteCache_.getOrAdd(id, [id, &code, + // &urls] { // EmoteData emoteData; - // fillInEmoteData(urls, code, code + "
Channel FFZ Emote", emoteData); - // emoteData.pageLink = + // fillInEmoteData(urls, code, code + "
Channel FFZ Emote", + // emoteData); emoteData.pageLink = // QString("https://www.frankerfacez.com/emoticon/%1-%2").arg(id).arg(code); // return emoteData; diff --git a/src/providers/ffz/FfzEmotes.hpp b/src/providers/ffz/FfzEmotes.hpp index d42885440..2dd747e02 100644 --- a/src/providers/ffz/FfzEmotes.hpp +++ b/src/providers/ffz/FfzEmotes.hpp @@ -10,8 +10,10 @@ namespace chatterino { class FfzEmotes final : std::enable_shared_from_this { - static constexpr const char *globalEmoteApiUrl = "https://api.frankerfacez.com/v1/set/global"; - static constexpr const char *channelEmoteApiUrl = "https://api.betterttv.net/2/channels/"; + static constexpr const char *globalEmoteApiUrl = + "https://api.frankerfacez.com/v1/set/global"; + static constexpr const char *channelEmoteApiUrl = + "https://api.betterttv.net/2/channels/"; public: // FfzEmotes(); @@ -23,7 +25,8 @@ public: boost::optional getEmote(const EmoteId &id); void loadGlobalEmotes(); - void loadChannelEmotes(const QString &channelName, std::function callback); + void loadChannelEmotes(const QString &channelName, + std::function callback); protected: Outcome parseGlobalEmotes(const QJsonObject &jsonRoot); diff --git a/src/providers/irc/AbstractIrcServer.cpp b/src/providers/irc/AbstractIrcServer.cpp index 532b78710..840291c5f 100644 --- a/src/providers/irc/AbstractIrcServer.cpp +++ b/src/providers/irc/AbstractIrcServer.cpp @@ -12,27 +12,35 @@ AbstractIrcServer::AbstractIrcServer() { // Initialize the connections this->writeConnection_.reset(new IrcConnection); - this->writeConnection_->moveToThread(QCoreApplication::instance()->thread()); + this->writeConnection_->moveToThread( + QCoreApplication::instance()->thread()); - QObject::connect(this->writeConnection_.get(), &Communi::IrcConnection::messageReceived, - [this](auto msg) { this->writeConnectionMessageReceived(msg); }); + QObject::connect( + this->writeConnection_.get(), &Communi::IrcConnection::messageReceived, + [this](auto msg) { this->writeConnectionMessageReceived(msg); }); // Listen to read connection message signals this->readConnection_.reset(new IrcConnection); this->readConnection_->moveToThread(QCoreApplication::instance()->thread()); - QObject::connect(this->readConnection_.get(), &Communi::IrcConnection::messageReceived, + QObject::connect(this->readConnection_.get(), + &Communi::IrcConnection::messageReceived, [this](auto msg) { this->messageReceived(msg); }); - QObject::connect(this->readConnection_.get(), &Communi::IrcConnection::privateMessageReceived, + QObject::connect(this->readConnection_.get(), + &Communi::IrcConnection::privateMessageReceived, [this](auto msg) { this->privateMessageReceived(msg); }); - QObject::connect(this->readConnection_.get(), &Communi::IrcConnection::connected, + QObject::connect(this->readConnection_.get(), + &Communi::IrcConnection::connected, [this] { this->onConnected(); }); - QObject::connect(this->readConnection_.get(), &Communi::IrcConnection::disconnected, + QObject::connect(this->readConnection_.get(), + &Communi::IrcConnection::disconnected, [this] { this->onDisconnected(); }); // listen to reconnect request - this->readConnection_->reconnectRequested.connect([this] { this->connect(); }); - // this->writeConnection->reconnectRequested.connect([this] { this->connect(); }); + this->readConnection_->reconnectRequested.connect( + [this] { this->connect(); }); + // this->writeConnection->reconnectRequested.connect([this] { + // this->connect(); }); } void AbstractIrcServer::connect() @@ -75,7 +83,8 @@ void AbstractIrcServer::disconnect() this->writeConnection_->close(); } -void AbstractIrcServer::sendMessage(const QString &channelName, const QString &message) +void AbstractIrcServer::sendMessage(const QString &channelName, + const QString &message) { this->sendRawMessage("PRIVMSG #" + channelName + " :" + message); } @@ -91,11 +100,13 @@ void AbstractIrcServer::sendRawMessage(const QString &rawMessage) } } -void AbstractIrcServer::writeConnectionMessageReceived(Communi::IrcMessage *message) +void AbstractIrcServer::writeConnectionMessageReceived( + Communi::IrcMessage *message) { } -std::shared_ptr AbstractIrcServer::getOrAddChannel(const QString &dirtyChannelName) +std::shared_ptr AbstractIrcServer::getOrAddChannel( + const QString &dirtyChannelName) { auto channelName = this->cleanChannelName(dirtyChannelName); @@ -119,7 +130,8 @@ std::shared_ptr AbstractIrcServer::getOrAddChannel(const QString &dirty chan->destroyed.connect([this, clojuresInCppAreShit] { // fourtf: issues when the server itself is destroyed - Log("[AbstractIrcServer::addChannel] {} was destroyed", clojuresInCppAreShit); + Log("[AbstractIrcServer::addChannel] {} was destroyed", + clojuresInCppAreShit); this->channels.remove(clojuresInCppAreShit); if (this->readConnection_) { @@ -147,7 +159,8 @@ std::shared_ptr AbstractIrcServer::getOrAddChannel(const QString &dirty return chan; } -std::shared_ptr AbstractIrcServer::getChannelOrEmpty(const QString &dirtyChannelName) +std::shared_ptr AbstractIrcServer::getChannelOrEmpty( + const QString &dirtyChannelName) { auto channelName = this->cleanChannelName(dirtyChannelName); @@ -187,9 +200,9 @@ void AbstractIrcServer::onConnected() LimitedQueueSnapshot snapshot = chan->getMessageSnapshot(); - bool replaceMessage = - snapshot.getLength() > 0 && - snapshot[snapshot.getLength() - 1]->flags & Message::DisconnectedMessage; + bool replaceMessage = snapshot.getLength() > 0 && + snapshot[snapshot.getLength() - 1]->flags & + Message::DisconnectedMessage; if (replaceMessage) { chan->replaceMessage(snapshot[snapshot.getLength() - 1], reconnMsg); @@ -217,7 +230,8 @@ void AbstractIrcServer::onDisconnected() } } -std::shared_ptr AbstractIrcServer::getCustomChannel(const QString &channelName) +std::shared_ptr AbstractIrcServer::getCustomChannel( + const QString &channelName) { return nullptr; } @@ -229,16 +243,19 @@ QString AbstractIrcServer::cleanChannelName(const QString &dirtyChannelName) void AbstractIrcServer::addFakeMessage(const QString &data) { - auto fakeMessage = Communi::IrcMessage::fromData(data.toUtf8(), this->readConnection_.get()); + auto fakeMessage = Communi::IrcMessage::fromData( + data.toUtf8(), this->readConnection_.get()); if (fakeMessage->command() == "PRIVMSG") { - this->privateMessageReceived(static_cast(fakeMessage)); + this->privateMessageReceived( + static_cast(fakeMessage)); } else { this->messageReceived(fakeMessage); } } -void AbstractIrcServer::privateMessageReceived(Communi::IrcPrivateMessage *message) +void AbstractIrcServer::privateMessageReceived( + Communi::IrcPrivateMessage *message) { } diff --git a/src/providers/irc/AbstractIrcServer.hpp b/src/providers/irc/AbstractIrcServer.hpp index 734be1d87..580cdd0fe 100644 --- a/src/providers/irc/AbstractIrcServer.hpp +++ b/src/providers/irc/AbstractIrcServer.hpp @@ -30,7 +30,8 @@ public: // signals pajlada::Signals::NoArgSignal connected; pajlada::Signals::NoArgSignal disconnected; - // pajlada::Signals::Signal onPrivateMessage; + // pajlada::Signals::Signal + // onPrivateMessage; void addFakeMessage(const QString &data); @@ -40,8 +41,10 @@ public: protected: AbstractIrcServer(); - virtual void initializeConnection(IrcConnection *connection, bool isRead, bool isWrite) = 0; - virtual std::shared_ptr createChannel(const QString &channelName) = 0; + virtual void initializeConnection(IrcConnection *connection, bool isRead, + bool isWrite) = 0; + virtual std::shared_ptr createChannel( + const QString &channelName) = 0; virtual void privateMessageReceived(Communi::IrcPrivateMessage *message); virtual void messageReceived(Communi::IrcMessage *message); @@ -50,7 +53,8 @@ protected: virtual void onConnected(); virtual void onDisconnected(); - virtual std::shared_ptr getCustomChannel(const QString &channelName); + virtual std::shared_ptr getCustomChannel( + const QString &channelName); virtual bool hasSeparateWriteConnection() const = 0; virtual QString cleanChannelName(const QString &dirtyChannelName); diff --git a/src/providers/irc/IrcAccount.cpp b/src/providers/irc/IrcAccount.cpp index 1b7afad50..96486b7ea 100644 --- a/src/providers/irc/IrcAccount.cpp +++ b/src/providers/irc/IrcAccount.cpp @@ -2,7 +2,8 @@ // namespace chatterino { // -// IrcAccount::IrcAccount(const QString &_userName, const QString &_nickName, const QString +// IrcAccount::IrcAccount(const QString &_userName, const QString &_nickName, +// const QString // &_realName, // const QString &_password) // : userName(_userName) diff --git a/src/providers/irc/IrcAccount.hpp b/src/providers/irc/IrcAccount.hpp index 5906026c6..2c4345b10 100644 --- a/src/providers/irc/IrcAccount.hpp +++ b/src/providers/irc/IrcAccount.hpp @@ -7,7 +7,8 @@ // class IrcAccount //{ // public: -// IrcAccount(const QString &userName, const QString &nickName, const QString &realName, +// IrcAccount(const QString &userName, const QString &nickName, const QString +// &realName, // const QString &password); // const QString &getUserName() const; diff --git a/src/providers/irc/IrcConnection2.cpp b/src/providers/irc/IrcConnection2.cpp index b1d28c20b..4739739aa 100644 --- a/src/providers/irc/IrcConnection2.cpp +++ b/src/providers/irc/IrcConnection2.cpp @@ -27,13 +27,14 @@ IrcConnection::IrcConnection(QObject *parent) } }); - QObject::connect(this, &Communi::IrcConnection::messageReceived, [this](Communi::IrcMessage *) { - this->recentlyReceivedMessage_ = true; + QObject::connect(this, &Communi::IrcConnection::messageReceived, + [this](Communi::IrcMessage *) { + this->recentlyReceivedMessage_ = true; - if (this->reconnectTimer_.isActive()) { - this->reconnectTimer_.stop(); - } - }); + if (this->reconnectTimer_.isActive()) { + this->reconnectTimer_.stop(); + } + }); } } // namespace chatterino diff --git a/src/providers/irc/IrcServer.hpp b/src/providers/irc/IrcServer.hpp index 874179b47..c8faf434c 100644 --- a/src/providers/irc/IrcServer.hpp +++ b/src/providers/irc/IrcServer.hpp @@ -14,7 +14,8 @@ namespace chatterino { // std::shared_ptr getAccount() const; // protected: -// virtual void initializeConnection(Communi::IrcConnection *connection, bool isReadConnection); +// virtual void initializeConnection(Communi::IrcConnection *connection, bool +// isReadConnection); // virtual void privateMessageReceived(Communi::IrcPrivateMessage *message); // virtual void messageReceived(Communi::IrcMessage *message); diff --git a/src/providers/twitch/IrcMessageHandler.cpp b/src/providers/twitch/IrcMessageHandler.cpp index 7460177ab..723156bd7 100644 --- a/src/providers/twitch/IrcMessageHandler.cpp +++ b/src/providers/twitch/IrcMessageHandler.cpp @@ -25,15 +25,17 @@ IrcMessageHandler &IrcMessageHandler::getInstance() return instance; } -void IrcMessageHandler::handlePrivMessage(Communi::IrcPrivateMessage *message, TwitchServer &server) +void IrcMessageHandler::handlePrivMessage(Communi::IrcPrivateMessage *message, + TwitchServer &server) { - this->addMessage(message, message->target(), message->content(), server, false, - message->isAction()); + this->addMessage(message, message->target(), message->content(), server, + false, message->isAction()); } -void IrcMessageHandler::addMessage(Communi::IrcMessage *_message, const QString &target, - const QString &content, TwitchServer &server, bool isSub, - bool isAction) +void IrcMessageHandler::addMessage(Communi::IrcMessage *_message, + const QString &target, + const QString &content, TwitchServer &server, + bool isSub, bool isAction) { QString channelName; if (!trimChannelName(target, channelName)) { @@ -140,14 +142,17 @@ void IrcMessageHandler::handleClearChatMessage(Communi::IrcMessage *message) auto chan = app->twitch.server->getChannelOrEmpty(chanName); if (chan->isEmpty()) { - Log("[IrcMessageHandler:handleClearChatMessage] Twitch channel {} not found", chanName); + Log("[IrcMessageHandler:handleClearChatMessage] Twitch channel {} not " + "found", + chanName); return; } // check if the chat has been cleared by a moderator if (message->parameters().length() == 1) { chan->disableAllMessages(); - chan->addMessage(Message::createSystemMessage("Chat has been cleared by a moderator.")); + chan->addMessage(Message::createSystemMessage( + "Chat has been cleared by a moderator.")); return; } @@ -165,7 +170,8 @@ void IrcMessageHandler::handleClearChatMessage(Communi::IrcMessage *message) reason = v.toString(); } - auto timeoutMsg = Message::createTimeoutMessage(username, durationInSeconds, reason, false); + auto timeoutMsg = Message::createTimeoutMessage(username, durationInSeconds, + reason, false); chan->addOrReplaceTimeout(timeoutMsg); // refresh all @@ -206,7 +212,8 @@ void IrcMessageHandler::handleWhisperMessage(Communi::IrcMessage *message) auto c = app->twitch.server->whispersChannel.get(); - TwitchMessageBuilder builder(c, message, args, message->parameter(1), false); + TwitchMessageBuilder builder(c, message, args, message->parameter(1), + false); if (!builder.isIgnored()) { MessagePtr _message = builder.build(); @@ -229,7 +236,8 @@ void IrcMessageHandler::handleWhisperMessage(Communi::IrcMessage *message) } } -void IrcMessageHandler::handleUserNoticeMessage(Communi::IrcMessage *message, TwitchServer &server) +void IrcMessageHandler::handleUserNoticeMessage(Communi::IrcMessage *message, + TwitchServer &server) { auto data = message->toData(); @@ -244,7 +252,8 @@ void IrcMessageHandler::handleUserNoticeMessage(Communi::IrcMessage *message, Tw } if (msgType == "sub" || msgType == "resub" || msgType == "subgift") { - // Sub-specific message. I think it's only allowed for "resub" messages atm + // Sub-specific message. I think it's only allowed for "resub" messages + // atm if (!content.isEmpty()) { this->addMessage(message, target, content, server, true, false); } @@ -253,7 +262,8 @@ void IrcMessageHandler::handleUserNoticeMessage(Communi::IrcMessage *message, Tw auto it = tags.find("system-msg"); if (it != tags.end()) { - auto newMessage = Message::createSystemMessage(parseTagString(it.value().toString())); + auto newMessage = + Message::createSystemMessage(parseTagString(it.value().toString())); newMessage->flags |= Message::Subscription; @@ -279,7 +289,8 @@ void IrcMessageHandler::handleModeMessage(Communi::IrcMessage *message) { auto app = getApp(); - auto channel = app->twitch.server->getChannelOrEmpty(message->parameter(0).remove(0, 1)); + auto channel = app->twitch.server->getChannelOrEmpty( + message->parameter(0).remove(0, 1)); if (channel->isEmpty()) { return; @@ -299,10 +310,12 @@ void IrcMessageHandler::handleNoticeMessage(Communi::IrcNoticeMessage *message) QString channelName; if (!trimChannelName(message->target(), channelName)) { - // Notice wasn't targeted at a single channel, send to all twitch channels - app->twitch.server->forEachChannelAndSpecialChannels([msg](const auto &c) { - c->addMessage(msg); // - }); + // Notice wasn't targeted at a single channel, send to all twitch + // channels + app->twitch.server->forEachChannelAndSpecialChannels( + [msg](const auto &c) { + c->addMessage(msg); // + }); return; } @@ -310,7 +323,8 @@ void IrcMessageHandler::handleNoticeMessage(Communi::IrcNoticeMessage *message) auto channel = app->twitch.server->getChannelOrEmpty(channelName); if (channel->isEmpty()) { - Log("[IrcManager:handleNoticeMessage] Channel {} not found in channel manager ", + Log("[IrcManager:handleNoticeMessage] Channel {} not found in channel " + "manager ", channelName); return; } @@ -318,7 +332,8 @@ void IrcMessageHandler::handleNoticeMessage(Communi::IrcNoticeMessage *message) channel->addMessage(msg); } -void IrcMessageHandler::handleWriteConnectionNoticeMessage(Communi::IrcNoticeMessage *message) +void IrcMessageHandler::handleWriteConnectionNoticeMessage( + Communi::IrcNoticeMessage *message) { static std::unordered_set readConnectionOnlyIDs{ "host_on", @@ -333,8 +348,9 @@ void IrcMessageHandler::handleWriteConnectionNoticeMessage(Communi::IrcNoticeMes "r9k_on", "r9k_off", - // Display for user who times someone out. This implies you're a moderator, at which point - // you will be connected to PubSub and receive a better message from there + // Display for user who times someone out. This implies you're a + // moderator, at which point you will be connected to PubSub and receive + // a better message from there "timeout_success", "ban_success", }; @@ -347,7 +363,8 @@ void IrcMessageHandler::handleWriteConnectionNoticeMessage(Communi::IrcNoticeMes return; } - Log("Showing notice message from write connection with message id '{}'", msgID); + Log("Showing notice message from write connection with message id '{}'", + msgID); } this->handleNoticeMessage(message); @@ -356,9 +373,11 @@ void IrcMessageHandler::handleWriteConnectionNoticeMessage(Communi::IrcNoticeMes void IrcMessageHandler::handleJoinMessage(Communi::IrcMessage *message) { auto app = getApp(); - auto channel = app->twitch.server->getChannelOrEmpty(message->parameter(0).remove(0, 1)); + auto channel = app->twitch.server->getChannelOrEmpty( + message->parameter(0).remove(0, 1)); - if (TwitchChannel *twitchChannel = dynamic_cast(channel.get())) { + if (TwitchChannel *twitchChannel = + dynamic_cast(channel.get())) { twitchChannel->addJoinedUser(message->nick()); } } @@ -366,9 +385,11 @@ void IrcMessageHandler::handleJoinMessage(Communi::IrcMessage *message) void IrcMessageHandler::handlePartMessage(Communi::IrcMessage *message) { auto app = getApp(); - auto channel = app->twitch.server->getChannelOrEmpty(message->parameter(0).remove(0, 1)); + auto channel = app->twitch.server->getChannelOrEmpty( + message->parameter(0).remove(0, 1)); - if (TwitchChannel *twitchChannel = dynamic_cast(channel.get())) { + if (TwitchChannel *twitchChannel = + dynamic_cast(channel.get())) { twitchChannel->addPartedUser(message->nick()); } } diff --git a/src/providers/twitch/IrcMessageHandler.hpp b/src/providers/twitch/IrcMessageHandler.hpp index 084c88bcd..40f1efb12 100644 --- a/src/providers/twitch/IrcMessageHandler.hpp +++ b/src/providers/twitch/IrcMessageHandler.hpp @@ -13,13 +13,15 @@ class IrcMessageHandler public: static IrcMessageHandler &getInstance(); - void handlePrivMessage(Communi::IrcPrivateMessage *message, TwitchServer &server); + void handlePrivMessage(Communi::IrcPrivateMessage *message, + TwitchServer &server); void handleRoomStateMessage(Communi::IrcMessage *message); void handleClearChatMessage(Communi::IrcMessage *message); void handleUserStateMessage(Communi::IrcMessage *message); void handleWhisperMessage(Communi::IrcMessage *message); - void handleUserNoticeMessage(Communi::IrcMessage *message, TwitchServer &server); + void handleUserNoticeMessage(Communi::IrcMessage *message, + TwitchServer &server); void handleModeMessage(Communi::IrcMessage *message); void handleNoticeMessage(Communi::IrcNoticeMessage *message); void handleWriteConnectionNoticeMessage(Communi::IrcNoticeMessage *message); @@ -28,8 +30,9 @@ public: void handlePartMessage(Communi::IrcMessage *message); private: - void addMessage(Communi::IrcMessage *message, const QString &target, const QString &content, - TwitchServer &server, bool isResub, bool isAction); + void addMessage(Communi::IrcMessage *message, const QString &target, + const QString &content, TwitchServer &server, bool isResub, + bool isAction); }; } // namespace chatterino diff --git a/src/providers/twitch/PartialTwitchUser.cpp b/src/providers/twitch/PartialTwitchUser.cpp index cc8c06f51..98fa2ba42 100644 --- a/src/providers/twitch/PartialTwitchUser.cpp +++ b/src/providers/twitch/PartialTwitchUser.cpp @@ -25,7 +25,8 @@ PartialTwitchUser PartialTwitchUser::byId(const QString &id) return user; } -void PartialTwitchUser::getId(std::function successCallback, const QObject *caller) +void PartialTwitchUser::getId(std::function successCallback, + const QObject *caller) { assert(!this->username_.isEmpty()); @@ -33,7 +34,8 @@ void PartialTwitchUser::getId(std::function successCallback, cons caller = QThread::currentThread(); } - NetworkRequest request("https://api.twitch.tv/kraken/users?login=" + this->username_); + NetworkRequest request("https://api.twitch.tv/kraken/users?login=" + + this->username_); request.setCaller(caller); request.makeAuthorizedV5(getDefaultClientID()); @@ -56,7 +58,8 @@ void PartialTwitchUser::getId(std::function successCallback, cons auto firstUser = users[0].toObject(); auto id = firstUser.value("_id"); if (!id.isString()) { - Log("API Error: while getting user id, first user object `_id` key is not a " + Log("API Error: while getting user id, first user object `_id` key " + "is not a " "string"); return Failure; } diff --git a/src/providers/twitch/PartialTwitchUser.hpp b/src/providers/twitch/PartialTwitchUser.hpp index a868f6fea..36876dc26 100644 --- a/src/providers/twitch/PartialTwitchUser.hpp +++ b/src/providers/twitch/PartialTwitchUser.hpp @@ -19,7 +19,8 @@ public: static PartialTwitchUser byName(const QString &username); static PartialTwitchUser byId(const QString &id); - void getId(std::function successCallback, const QObject *caller = nullptr); + void getId(std::function successCallback, + const QObject *caller = nullptr); }; } // namespace chatterino diff --git a/src/providers/twitch/PubsubActions.hpp b/src/providers/twitch/PubsubActions.hpp index 095cbad85..688468f0e 100644 --- a/src/providers/twitch/PubsubActions.hpp +++ b/src/providers/twitch/PubsubActions.hpp @@ -21,7 +21,8 @@ struct PubSubAction { QString roomID; }; -// Used when a chat mode (i.e. slowmode, subscribers only mode) is enabled or disabled +// Used when a chat mode (i.e. slowmode, subscribers only mode) is enabled or +// disabled struct ModeChangedAction : PubSubAction { using PubSubAction::PubSubAction; diff --git a/src/providers/twitch/PubsubClient.cpp b/src/providers/twitch/PubsubClient.cpp index 0ea37481a..7cdd343ce 100644 --- a/src/providers/twitch/PubsubClient.cpp +++ b/src/providers/twitch/PubsubClient.cpp @@ -24,7 +24,8 @@ static std::map sentMessages; namespace detail { -PubSubClient::PubSubClient(WebsocketClient &websocketClient, WebsocketHandle handle) +PubSubClient::PubSubClient(WebsocketClient &websocketClient, + WebsocketHandle handle) : websocketClient_(websocketClient) , handle_(handle) { @@ -58,7 +59,8 @@ bool PubSubClient::listen(rapidjson::Document &message) this->numListens_ += numRequestedListens; for (const auto &topic : message["data"]["topics"].GetArray()) { - this->listeners_.emplace_back(Listener{topic.GetString(), false, false, false}); + this->listeners_.emplace_back( + Listener{topic.GetString(), false, false, false}); } auto uuid = CreateUUID(); @@ -135,34 +137,38 @@ void PubSubClient::ping() auto self = this->shared_from_this(); - runAfter(this->websocketClient_.get_io_service(), std::chrono::seconds(15), [self](auto timer) { - if (!self->started_) { - return; - } + runAfter(this->websocketClient_.get_io_service(), std::chrono::seconds(15), + [self](auto timer) { + if (!self->started_) { + return; + } - if (self->awaitingPong_) { - Log("No pong respnose, disconnect!"); - // TODO(pajlada): Label this connection as "disconnect me" - } - }); + if (self->awaitingPong_) { + Log("No pong respnose, disconnect!"); + // TODO(pajlada): Label this connection as "disconnect me" + } + }); - runAfter(this->websocketClient_.get_io_service(), std::chrono::minutes(5), [self](auto timer) { - if (!self->started_) { - return; - } + runAfter(this->websocketClient_.get_io_service(), std::chrono::minutes(5), + [self](auto timer) { + if (!self->started_) { + return; + } - self->ping(); // - }); + self->ping(); // + }); } bool PubSubClient::send(const char *payload) { WebsocketErrorCode ec; - this->websocketClient_.send(this->handle_, payload, websocketpp::frame::opcode::text, ec); + this->websocketClient_.send(this->handle_, payload, + websocketpp::frame::opcode::text, ec); if (ec) { Log("Error sending message {}: {}", payload, ec.message()); - // TODO(pajlada): Check which error code happened and maybe gracefully handle it + // TODO(pajlada): Check which error code happened and maybe gracefully + // handle it return false; } @@ -176,13 +182,15 @@ PubSub::PubSub() { qDebug() << "init PubSub"; - this->moderationActionHandlers["clear"] = [this](const auto &data, const auto &roomID) { + this->moderationActionHandlers["clear"] = [this](const auto &data, + const auto &roomID) { ClearChatAction action(data, roomID); this->signals_.moderation.chatCleared.invoke(action); }; - this->moderationActionHandlers["slowoff"] = [this](const auto &data, const auto &roomID) { + this->moderationActionHandlers["slowoff"] = [this](const auto &data, + const auto &roomID) { ModeChangedAction action(data, roomID); action.mode = ModeChangedAction::Mode::Slow; @@ -191,7 +199,8 @@ PubSub::PubSub() this->signals_.moderation.modeChanged.invoke(action); }; - this->moderationActionHandlers["slow"] = [this](const auto &data, const auto &roomID) { + this->moderationActionHandlers["slow"] = [this](const auto &data, + const auto &roomID) { ModeChangedAction action(data, roomID); action.mode = ModeChangedAction::Mode::Slow; @@ -228,7 +237,8 @@ PubSub::PubSub() this->signals_.moderation.modeChanged.invoke(action); }; - this->moderationActionHandlers["r9kbetaoff"] = [this](const auto &data, const auto &roomID) { + this->moderationActionHandlers["r9kbetaoff"] = [this](const auto &data, + const auto &roomID) { ModeChangedAction action(data, roomID); action.mode = ModeChangedAction::Mode::R9K; @@ -237,7 +247,8 @@ PubSub::PubSub() this->signals_.moderation.modeChanged.invoke(action); }; - this->moderationActionHandlers["r9kbeta"] = [this](const auto &data, const auto &roomID) { + this->moderationActionHandlers["r9kbeta"] = [this](const auto &data, + const auto &roomID) { ModeChangedAction action(data, roomID); action.mode = ModeChangedAction::Mode::R9K; @@ -246,17 +257,18 @@ PubSub::PubSub() this->signals_.moderation.modeChanged.invoke(action); }; - this->moderationActionHandlers["subscribersoff"] = [this](const auto &data, - const auto &roomID) { - ModeChangedAction action(data, roomID); + this->moderationActionHandlers["subscribersoff"] = + [this](const auto &data, const auto &roomID) { + ModeChangedAction action(data, roomID); - action.mode = ModeChangedAction::Mode::SubscribersOnly; - action.state = ModeChangedAction::State::Off; + action.mode = ModeChangedAction::Mode::SubscribersOnly; + action.state = ModeChangedAction::State::Off; - this->signals_.moderation.modeChanged.invoke(action); - }; + this->signals_.moderation.modeChanged.invoke(action); + }; - this->moderationActionHandlers["subscribers"] = [this](const auto &data, const auto &roomID) { + this->moderationActionHandlers["subscribers"] = [this](const auto &data, + const auto &roomID) { ModeChangedAction action(data, roomID); action.mode = ModeChangedAction::Mode::SubscribersOnly; @@ -265,16 +277,18 @@ PubSub::PubSub() this->signals_.moderation.modeChanged.invoke(action); }; - this->moderationActionHandlers["emoteonlyoff"] = [this](const auto &data, const auto &roomID) { - ModeChangedAction action(data, roomID); + this->moderationActionHandlers["emoteonlyoff"] = + [this](const auto &data, const auto &roomID) { + ModeChangedAction action(data, roomID); - action.mode = ModeChangedAction::Mode::EmoteOnly; - action.state = ModeChangedAction::State::Off; + action.mode = ModeChangedAction::Mode::EmoteOnly; + action.state = ModeChangedAction::State::Off; - this->signals_.moderation.modeChanged.invoke(action); - }; + this->signals_.moderation.modeChanged.invoke(action); + }; - this->moderationActionHandlers["emoteonly"] = [this](const auto &data, const auto &roomID) { + this->moderationActionHandlers["emoteonly"] = [this](const auto &data, + const auto &roomID) { ModeChangedAction action(data, roomID); action.mode = ModeChangedAction::Mode::EmoteOnly; @@ -283,7 +297,8 @@ PubSub::PubSub() this->signals_.moderation.modeChanged.invoke(action); }; - this->moderationActionHandlers["unmod"] = [this](const auto &data, const auto &roomID) { + this->moderationActionHandlers["unmod"] = [this](const auto &data, + const auto &roomID) { ModerationStateAction action(data, roomID); getTargetUser(data, action.target); @@ -307,7 +322,8 @@ PubSub::PubSub() this->signals_.moderation.moderationStateChanged.invoke(action); }; - this->moderationActionHandlers["mod"] = [this](const auto &data, const auto &roomID) { + this->moderationActionHandlers["mod"] = [this](const auto &data, + const auto &roomID) { ModerationStateAction action(data, roomID); getTargetUser(data, action.target); @@ -331,7 +347,8 @@ PubSub::PubSub() this->signals_.moderation.moderationStateChanged.invoke(action); }; - this->moderationActionHandlers["timeout"] = [this](const auto &data, const auto &roomID) { + this->moderationActionHandlers["timeout"] = [this](const auto &data, + const auto &roomID) { BanAction action(data, roomID); getCreatedByUser(data, action.source); @@ -367,7 +384,8 @@ PubSub::PubSub() } }; - this->moderationActionHandlers["ban"] = [this](const auto &data, const auto &roomID) { + this->moderationActionHandlers["ban"] = [this](const auto &data, + const auto &roomID) { BanAction action(data, roomID); getCreatedByUser(data, action.source); @@ -396,7 +414,8 @@ PubSub::PubSub() } }; - this->moderationActionHandlers["unban"] = [this](const auto &data, const auto &roomID) { + this->moderationActionHandlers["unban"] = [this](const auto &data, + const auto &roomID) { UnbanAction action(data, roomID); getCreatedByUser(data, action.source); @@ -421,7 +440,8 @@ PubSub::PubSub() } }; - this->moderationActionHandlers["untimeout"] = [this](const auto &data, const auto &roomID) { + this->moderationActionHandlers["untimeout"] = [this](const auto &data, + const auto &roomID) { UnbanAction action(data, roomID); getCreatedByUser(data, action.source); @@ -447,16 +467,21 @@ PubSub::PubSub() }; this->websocketClient.set_access_channels(websocketpp::log::alevel::all); - this->websocketClient.clear_access_channels(websocketpp::log::alevel::frame_payload); + this->websocketClient.clear_access_channels( + websocketpp::log::alevel::frame_payload); this->websocketClient.init_asio(); // SSL Handshake - this->websocketClient.set_tls_init_handler(bind(&PubSub::onTLSInit, this, ::_1)); + this->websocketClient.set_tls_init_handler( + bind(&PubSub::onTLSInit, this, ::_1)); - this->websocketClient.set_message_handler(bind(&PubSub::onMessage, this, ::_1, ::_2)); - this->websocketClient.set_open_handler(bind(&PubSub::onConnectionOpen, this, ::_1)); - this->websocketClient.set_close_handler(bind(&PubSub::onConnectionClose, this, ::_1)); + this->websocketClient.set_message_handler( + bind(&PubSub::onMessage, this, ::_1, ::_2)); + this->websocketClient.set_open_handler( + bind(&PubSub::onConnectionOpen, this, ::_1)); + this->websocketClient.set_close_handler( + bind(&PubSub::onConnectionClose, this, ::_1)); // Add an initial client this->addClient(); @@ -477,7 +502,8 @@ void PubSub::addClient() void PubSub::start() { - this->mainThread.reset(new std::thread(std::bind(&PubSub::runThread, this))); + this->mainThread.reset( + new std::thread(std::bind(&PubSub::runThread, this))); } void PubSub::listenToWhispers(std::shared_ptr account) @@ -507,8 +533,8 @@ void PubSub::unlistenAllModerationActions() } } -void PubSub::listenToChannelModerationActions(const QString &channelID, - std::shared_ptr account) +void PubSub::listenToChannelModerationActions( + const QString &channelID, std::shared_ptr account) { assert(!channelID.isEmpty()); assert(account != nullptr); @@ -527,7 +553,8 @@ void PubSub::listenToChannelModerationActions(const QString &channelID, this->listenToTopic(topic, account); } -void PubSub::listenToTopic(const std::string &topic, std::shared_ptr account) +void PubSub::listenToTopic(const std::string &topic, + std::shared_ptr account) { auto message = createListenMessage({topic}, account); @@ -542,7 +569,8 @@ void PubSub::listen(rapidjson::Document &&msg) } Log("Added to the back of the queue"); - this->requests.emplace_back(std::make_unique(std::move(msg))); + this->requests.emplace_back( + std::make_unique(std::move(msg))); } bool PubSub::tryListen(rapidjson::Document &msg) @@ -570,7 +598,8 @@ bool PubSub::isListeningToTopic(const std::string &topic) return false; } -void PubSub::onMessage(websocketpp::connection_hdl hdl, WebsocketMessagePtr websocketMessage) +void PubSub::onMessage(websocketpp::connection_hdl hdl, + WebsocketMessagePtr websocketMessage) { const std::string &payload = websocketMessage->get_payload(); @@ -585,7 +614,9 @@ void PubSub::onMessage(websocketpp::connection_hdl hdl, WebsocketMessagePtr webs } if (!msg.IsObject()) { - Log("Error parsing message '{}' from PubSub. Root object is not an object", payload); + Log("Error parsing message '{}' from PubSub. Root object is not an " + "object", + payload); return; } @@ -615,8 +646,8 @@ void PubSub::onMessage(websocketpp::connection_hdl hdl, WebsocketMessagePtr webs } else if (type == "PONG") { auto clientIt = this->clients.find(hdl); - // If this assert goes off, there's something wrong with the connection creation/preserving - // code KKona + // If this assert goes off, there's something wrong with the connection + // creation/preserving code KKona assert(clientIt != this->clients.end()); auto &client = *clientIt; @@ -629,9 +660,11 @@ void PubSub::onMessage(websocketpp::connection_hdl hdl, WebsocketMessagePtr webs void PubSub::onConnectionOpen(WebsocketHandle hdl) { - auto client = std::make_shared(this->websocketClient, hdl); + auto client = + std::make_shared(this->websocketClient, hdl); - // We separate the starting from the constructor because we will want to use shared_from_this + // We separate the starting from the constructor because we will want to use + // shared_from_this client->start(); this->clients.emplace(hdl, client); @@ -643,8 +676,8 @@ void PubSub::onConnectionClose(WebsocketHandle hdl) { auto clientIt = this->clients.find(hdl); - // If this assert goes off, there's something wrong with the connection creation/preserving - // code KKona + // If this assert goes off, there's something wrong with the connection + // creation/preserving code KKona assert(clientIt != this->clients.end()); auto &client = clientIt->second; @@ -658,7 +691,8 @@ void PubSub::onConnectionClose(WebsocketHandle hdl) PubSub::WebsocketContextPtr PubSub::onTLSInit(websocketpp::connection_hdl hdl) { - WebsocketContextPtr ctx(new boost::asio::ssl::context(boost::asio::ssl::context::tlsv1)); + WebsocketContextPtr ctx( + new boost::asio::ssl::context(boost::asio::ssl::context::tlsv1)); try { ctx->set_options(boost::asio::ssl::context::default_workarounds | diff --git a/src/providers/twitch/PubsubClient.hpp b/src/providers/twitch/PubsubClient.hpp index 3d39b7efb..211502176 100644 --- a/src/providers/twitch/PubsubClient.hpp +++ b/src/providers/twitch/PubsubClient.hpp @@ -22,7 +22,8 @@ namespace chatterino { -using WebsocketClient = websocketpp::client; +using WebsocketClient = + websocketpp::client; using WebsocketHandle = websocketpp::connection_hdl; using WebsocketErrorCode = websocketpp::lib::error_code; @@ -71,11 +72,14 @@ private: class PubSub { - using WebsocketMessagePtr = websocketpp::config::asio_tls_client::message_type::ptr; - using WebsocketContextPtr = websocketpp::lib::shared_ptr; + using WebsocketMessagePtr = + websocketpp::config::asio_tls_client::message_type::ptr; + using WebsocketContextPtr = + websocketpp::lib::shared_ptr; template - using Signal = pajlada::Signals::Signal; // type-id is vector> + using Signal = + pajlada::Signals::Signal; // type-id is vector> WebsocketClient websocketClient; std::unique_ptr mainThread; @@ -121,13 +125,14 @@ public: void unlistenAllModerationActions(); - void listenToChannelModerationActions(const QString &channelID, - std::shared_ptr account); + void listenToChannelModerationActions( + const QString &channelID, std::shared_ptr account); std::vector> requests; private: - void listenToTopic(const std::string &topic, std::shared_ptr account); + void listenToTopic(const std::string &topic, + std::shared_ptr account); void listen(rapidjson::Document &&msg); bool tryListen(rapidjson::Document &msg); @@ -142,7 +147,8 @@ private: std::owner_less> clients; - std::unordered_map> + std::unordered_map> moderationActionHandlers; void onMessage(websocketpp::connection_hdl hdl, WebsocketMessagePtr msg); diff --git a/src/providers/twitch/PubsubHelpers.cpp b/src/providers/twitch/PubsubHelpers.cpp index e93340fe4..cf4560f6b 100644 --- a/src/providers/twitch/PubsubHelpers.cpp +++ b/src/providers/twitch/PubsubHelpers.cpp @@ -31,8 +31,9 @@ bool getTargetUser(const rapidjson::Value &data, ActionUser &user) return rj::getSafe(data, "target_user_id", user.id); } -rapidjson::Document createListenMessage(const std::vector &topicsVec, - std::shared_ptr account) +rapidjson::Document createListenMessage( + const std::vector &topicsVec, + std::shared_ptr account) { rapidjson::Document msg(rapidjson::kObjectType); auto &a = msg.GetAllocator(); @@ -57,7 +58,8 @@ rapidjson::Document createListenMessage(const std::vector &topicsVe return msg; } -rapidjson::Document createUnlistenMessage(const std::vector &topicsVec) +rapidjson::Document createUnlistenMessage( + const std::vector &topicsVec) { rapidjson::Document msg(rapidjson::kObjectType); auto &a = msg.GetAllocator(); diff --git a/src/providers/twitch/PubsubHelpers.hpp b/src/providers/twitch/PubsubHelpers.hpp index 07bda24b1..a891edd4f 100644 --- a/src/providers/twitch/PubsubHelpers.hpp +++ b/src/providers/twitch/PubsubHelpers.hpp @@ -19,13 +19,16 @@ bool getCreatedByUser(const rapidjson::Value &data, ActionUser &user); bool getTargetUser(const rapidjson::Value &data, ActionUser &user); -rapidjson::Document createListenMessage(const std::vector &topicsVec, - std::shared_ptr account); -rapidjson::Document createUnlistenMessage(const std::vector &topicsVec); +rapidjson::Document createListenMessage( + const std::vector &topicsVec, + std::shared_ptr account); +rapidjson::Document createUnlistenMessage( + const std::vector &topicsVec); // Create timer using given ioService template -void runAfter(boost::asio::io_service &ioService, Duration duration, Callback cb) +void runAfter(boost::asio::io_service &ioService, Duration duration, + Callback cb) { auto timer = std::make_shared(ioService); timer->expires_from_now(duration); @@ -42,7 +45,8 @@ void runAfter(boost::asio::io_service &ioService, Duration duration, Callback cb // Use provided timer template -void runAfter(std::shared_ptr timer, Duration duration, Callback cb) +void runAfter(std::shared_ptr timer, + Duration duration, Callback cb) { timer->expires_from_now(duration); diff --git a/src/providers/twitch/TwitchAccount.cpp b/src/providers/twitch/TwitchAccount.cpp index bb8510231..8cde777d1 100644 --- a/src/providers/twitch/TwitchAccount.cpp +++ b/src/providers/twitch/TwitchAccount.cpp @@ -20,10 +20,12 @@ EmoteName cleanUpCode(const EmoteName &dirtyEmoteCode) cleanCode.detach(); static QMap emoteNameReplacements{ - {"[oO](_|\\.)[oO]", "O_o"}, {"\\>\\;\\(", ">("}, {"\\<\\;3", "<3"}, - {"\\:-?(o|O)", ":O"}, {"\\:-?(p|P)", ":P"}, {"\\:-?[\\\\/]", ":/"}, - {"\\:-?[z|Z|\\|]", ":Z"}, {"\\:-?\\(", ":("}, {"\\:-?\\)", ":)"}, - {"\\:-?D", ":D"}, {"\\;-?(p|P)", ";P"}, {"\\;-?\\)", ";)"}, + {"[oO](_|\\.)[oO]", "O_o"}, {"\\>\\;\\(", ">("}, + {"\\<\\;3", "<3"}, {"\\:-?(o|O)", ":O"}, + {"\\:-?(p|P)", ":P"}, {"\\:-?[\\\\/]", ":/"}, + {"\\:-?[z|Z|\\|]", ":Z"}, {"\\:-?\\(", ":("}, + {"\\:-?\\)", ":)"}, {"\\:-?D", ":D"}, + {"\\;-?(p|P)", ";P"}, {"\\;-?\\)", ";)"}, {"R-?\\)", "R)"}, {"B-?\\)", "B)"}, }; @@ -105,7 +107,8 @@ bool TwitchAccount::isAnon() const void TwitchAccount::loadIgnores() { - QString url("https://api.twitch.tv/kraken/users/" + this->getUserId() + "/blocks"); + QString url("https://api.twitch.tv/kraken/users/" + this->getUserId() + + "/blocks"); NetworkRequest req(url); req.setCaller(QThread::currentThread()); @@ -140,7 +143,8 @@ void TwitchAccount::loadIgnores() } TwitchUser ignoredUser; if (!rj::getSafe(userIt->value, ignoredUser)) { - Log("Error parsing twitch user JSON {}", rj::stringify(userIt->value)); + Log("Error parsing twitch user JSON {}", + rj::stringify(userIt->value)); continue; } @@ -154,28 +158,32 @@ void TwitchAccount::loadIgnores() req.execute(); } -void TwitchAccount::ignore(const QString &targetName, - std::function onFinished) +void TwitchAccount::ignore( + const QString &targetName, + std::function onFinished) { - const auto onIdFetched = [this, targetName, onFinished](QString targetUserId) { + const auto onIdFetched = [this, targetName, + onFinished](QString targetUserId) { this->ignoreByID(targetUserId, targetName, onFinished); // }; PartialTwitchUser::byName(targetName).getId(onIdFetched); } -void TwitchAccount::ignoreByID(const QString &targetUserID, const QString &targetName, - std::function onFinished) +void TwitchAccount::ignoreByID( + const QString &targetUserID, const QString &targetName, + std::function onFinished) { - QString url("https://api.twitch.tv/kraken/users/" + this->getUserId() + "/blocks/" + - targetUserID); + QString url("https://api.twitch.tv/kraken/users/" + this->getUserId() + + "/blocks/" + targetUserID); NetworkRequest req(url, NetworkRequestType::Put); req.setCaller(QThread::currentThread()); req.makeAuthorizedV5(this->getOAuthClient(), this->getOAuthToken()); req.onError([=](int errorCode) { - onFinished(IgnoreResult_Failed, "An unknown error occured while trying to ignore user " + - targetName + " (" + QString::number(errorCode) + ")"); + onFinished(IgnoreResult_Failed, + "An unknown error occured while trying to ignore user " + + targetName + " (" + QString::number(errorCode) + ")"); return true; }); @@ -183,21 +191,24 @@ void TwitchAccount::ignoreByID(const QString &targetUserID, const QString &targe req.onSuccess([=](auto result) -> Outcome { auto document = result.parseRapidJson(); if (!document.IsObject()) { - onFinished(IgnoreResult_Failed, "Bad JSON data while ignoring user " + targetName); + onFinished(IgnoreResult_Failed, + "Bad JSON data while ignoring user " + targetName); return Failure; } auto userIt = document.FindMember("user"); if (userIt == document.MemberEnd()) { onFinished(IgnoreResult_Failed, - "Bad JSON data while ignoring user (missing user) " + targetName); + "Bad JSON data while ignoring user (missing user) " + + targetName); return Failure; } TwitchUser ignoredUser; if (!rj::getSafe(userIt->value, ignoredUser)) { onFinished(IgnoreResult_Failed, - "Bad JSON data while ignoring user (invalid user) " + targetName); + "Bad JSON data while ignoring user (invalid user) " + + targetName); return Failure; } { @@ -212,7 +223,8 @@ void TwitchAccount::ignoreByID(const QString &targetUserID, const QString &targe return Failure; } } - onFinished(IgnoreResult_Success, "Successfully ignored user " + targetName); + onFinished(IgnoreResult_Success, + "Successfully ignored user " + targetName); return Success; }); @@ -220,10 +232,12 @@ void TwitchAccount::ignoreByID(const QString &targetUserID, const QString &targe req.execute(); } -void TwitchAccount::unignore(const QString &targetName, - std::function onFinished) +void TwitchAccount::unignore( + const QString &targetName, + std::function onFinished) { - const auto onIdFetched = [this, targetName, onFinished](QString targetUserId) { + const auto onIdFetched = [this, targetName, + onFinished](QString targetUserId) { this->unignoreByID(targetUserId, targetName, onFinished); // }; @@ -234,8 +248,8 @@ void TwitchAccount::unignoreByID( const QString &targetUserID, const QString &targetName, std::function onFinished) { - QString url("https://api.twitch.tv/kraken/users/" + this->getUserId() + "/blocks/" + - targetUserID); + QString url("https://api.twitch.tv/kraken/users/" + this->getUserId() + + "/blocks/" + targetUserID); NetworkRequest req(url, NetworkRequestType::Delete); req.setCaller(QThread::currentThread()); @@ -243,8 +257,8 @@ void TwitchAccount::unignoreByID( req.onError([=](int errorCode) { onFinished(UnignoreResult_Failed, - "An unknown error occured while trying to unignore user " + targetName + " (" + - QString::number(errorCode) + ")"); + "An unknown error occured while trying to unignore user " + + targetName + " (" + QString::number(errorCode) + ")"); return true; }); @@ -258,7 +272,8 @@ void TwitchAccount::unignoreByID( this->ignores_.erase(ignoredUser); } - onFinished(UnignoreResult_Success, "Successfully unignored user " + targetName); + onFinished(UnignoreResult_Success, + "Successfully unignored user " + targetName); return Success; }); @@ -269,8 +284,8 @@ void TwitchAccount::unignoreByID( void TwitchAccount::checkFollow(const QString targetUserID, std::function onFinished) { - QString url("https://api.twitch.tv/kraken/users/" + this->getUserId() + "/follows/channels/" + - targetUserID); + QString url("https://api.twitch.tv/kraken/users/" + this->getUserId() + + "/follows/channels/" + targetUserID); NetworkRequest req(url); req.setCaller(QThread::currentThread()); @@ -295,7 +310,8 @@ void TwitchAccount::checkFollow(const QString targetUserID, req.execute(); } -void TwitchAccount::followUser(const QString userID, std::function successCallback) +void TwitchAccount::followUser(const QString userID, + std::function successCallback) { QUrl requestUrl("https://api.twitch.tv/kraken/users/" + this->getUserId() + "/follows/channels/" + userID); @@ -315,7 +331,8 @@ void TwitchAccount::followUser(const QString userID, std::function succe request.execute(); } -void TwitchAccount::unfollowUser(const QString userID, std::function successCallback) +void TwitchAccount::unfollowUser(const QString userID, + std::function successCallback) { QUrl requestUrl("https://api.twitch.tv/kraken/users/" + this->getUserId() + "/follows/channels/" + userID); @@ -361,7 +378,8 @@ void TwitchAccount::loadEmotes() return; } - QString url("https://api.twitch.tv/kraken/users/" + this->getUserId() + "/emotes"); + QString url("https://api.twitch.tv/kraken/users/" + this->getUserId() + + "/emotes"); NetworkRequest req(url); req.setCaller(QThread::currentThread()); @@ -387,7 +405,8 @@ void TwitchAccount::loadEmotes() req.execute(); } -AccessGuard TwitchAccount::accessEmotes() const +AccessGuard +TwitchAccount::accessEmotes() const { return this->emotes_.accessConst(); } @@ -412,7 +431,8 @@ void TwitchAccount::parseEmotes(const rapidjson::Document &root) this->loadEmoteSetData(emoteSet); - for (const rapidjson::Value &emoteJSON : emoteSetJSON.value.GetArray()) { + for (const rapidjson::Value &emoteJSON : + emoteSetJSON.value.GetArray()) { if (!emoteJSON.IsObject()) { Log("Emote value was invalid"); return; @@ -459,8 +479,9 @@ void TwitchAccount::loadEmoteSetData(std::shared_ptr emoteSet) return; } - NetworkRequest req("https://braize.pajlada.com/chatterino/twitchemotes/set/" + emoteSet->key + - "/"); + NetworkRequest req( + "https://braize.pajlada.com/chatterino/twitchemotes/set/" + + emoteSet->key + "/"); req.setUseQuickLoadCache(true); req.onError([](int errorCode) -> bool { @@ -488,9 +509,11 @@ void TwitchAccount::loadEmoteSetData(std::shared_ptr emoteSet) Log("Loaded twitch emote set data for {}!", emoteSet->key); if (type == "sub") { - emoteSet->text = QString("Twitch Subscriber Emote (%1)").arg(channelName); + emoteSet->text = + QString("Twitch Subscriber Emote (%1)").arg(channelName); } else { - emoteSet->text = QString("Twitch Account Emote (%1)").arg(channelName); + emoteSet->text = + QString("Twitch Account Emote (%1)").arg(channelName); } emoteSet->channelName = channelName; diff --git a/src/providers/twitch/TwitchAccount.hpp b/src/providers/twitch/TwitchAccount.hpp index be97a0653..1a180f5e2 100644 --- a/src/providers/twitch/TwitchAccount.hpp +++ b/src/providers/twitch/TwitchAccount.hpp @@ -57,8 +57,8 @@ public: EmoteMap emotes; }; - TwitchAccount(const QString &username, const QString &oauthToken_, const QString &oauthClient_, - const QString &_userID); + TwitchAccount(const QString &username, const QString &oauthToken_, + const QString &oauthClient_, const QString &_userID); virtual QString toString() const override; @@ -81,16 +81,22 @@ public: void loadIgnores(); void ignore(const QString &targetName, std::function onFinished); - void ignoreByID(const QString &targetUserID, const QString &targetName, - std::function onFinished); - void unignore(const QString &targetName, - std::function onFinished); - void unignoreByID(const QString &targetUserID, const QString &targetName, - std::function onFinished); + void ignoreByID( + const QString &targetUserID, const QString &targetName, + std::function onFinished); + void unignore( + const QString &targetName, + std::function onFinished); + void unignoreByID( + const QString &targetUserID, const QString &targetName, + std::function onFinished); - void checkFollow(const QString targetUserID, std::function onFinished); - void followUser(const QString userID, std::function successCallback); - void unfollowUser(const QString userID, std::function successCallback); + void checkFollow(const QString targetUserID, + std::function onFinished); + void followUser(const QString userID, + std::function successCallback); + void unfollowUser(const QString userID, + std::function successCallback); std::set getIgnores() const; diff --git a/src/providers/twitch/TwitchAccountManager.cpp b/src/providers/twitch/TwitchAccountManager.cpp index 2f177a146..7d4f932eb 100644 --- a/src/providers/twitch/TwitchAccountManager.cpp +++ b/src/providers/twitch/TwitchAccountManager.cpp @@ -72,16 +72,17 @@ void TwitchAccountManager::reloadUsers() continue; } - std::string username = - pajlada::Settings::Setting::get("/accounts/" + uid + "/username"); - std::string userID = - pajlada::Settings::Setting::get("/accounts/" + uid + "/userID"); - std::string clientID = - pajlada::Settings::Setting::get("/accounts/" + uid + "/clientID"); - std::string oauthToken = - pajlada::Settings::Setting::get("/accounts/" + uid + "/oauthToken"); + std::string username = pajlada::Settings::Setting::get( + "/accounts/" + uid + "/username"); + std::string userID = pajlada::Settings::Setting::get( + "/accounts/" + uid + "/userID"); + std::string clientID = pajlada::Settings::Setting::get( + "/accounts/" + uid + "/clientID"); + std::string oauthToken = pajlada::Settings::Setting::get( + "/accounts/" + uid + "/oauthToken"); - if (username.empty() || userID.empty() || clientID.empty() || oauthToken.empty()) { + if (username.empty() || userID.empty() || clientID.empty() || + oauthToken.empty()) { continue; } @@ -96,9 +97,11 @@ void TwitchAccountManager::reloadUsers() // Do nothing } break; case AddUserResponse::UserValuesUpdated: { - Log("User {} already exists, and values updated!", userData.username); + Log("User {} already exists, and values updated!", + userData.username); if (userData.username == this->getCurrent()->getUserName()) { - Log("It was the current user, so we need to reconnect stuff!"); + Log("It was the current user, so we need to reconnect " + "stuff!"); this->currentUserChanged.invoke(); } } break; @@ -122,11 +125,13 @@ void TwitchAccountManager::load() QString newUsername(QString::fromStdString(newValue)); auto user = this->findUserByUsername(newUsername); if (user) { - Log("[AccountManager:currentUsernameChanged] User successfully updated to {}", + Log("[AccountManager:currentUsernameChanged] User successfully " + "updated to {}", newUsername); this->currentUser_ = user; } else { - Log("[AccountManager:currentUsernameChanged] User successfully updated to anonymous"); + Log("[AccountManager:currentUsernameChanged] User successfully " + "updated to anonymous"); this->currentUser_ = this->anonymousUser_; } @@ -140,8 +145,8 @@ bool TwitchAccountManager::isLoggedIn() const return false; } - // Once `TwitchAccount` class has a way to check, we should also return false if the credentials - // are incorrect + // Once `TwitchAccount` class has a way to check, we should also return + // false if the credentials are incorrect return !this->currentUser_->isAnon(); } @@ -151,11 +156,13 @@ bool TwitchAccountManager::removeUser(TwitchAccount *account) std::string userID(account->getUserId().toStdString()); if (!userID.empty()) { - pajlada::Settings::SettingManager::removeSetting("/accounts/uid" + userID); + pajlada::Settings::SettingManager::removeSetting("/accounts/uid" + + userID); } if (account->getUserName() == qS(this->currentUsername.getValue())) { - // The user that was removed is the current user, log into the anonymous user + // The user that was removed is the current user, log into the anonymous + // user this->currentUsername = ""; } @@ -186,8 +193,9 @@ TwitchAccountManager::AddUserResponse TwitchAccountManager::addUser( } } - auto newUser = std::make_shared(userData.username, userData.oauthToken, - userData.clientID, userData.userID); + auto newUser = + std::make_shared(userData.username, userData.oauthToken, + userData.clientID, userData.userID); // std::lock_guard lock(this->mutex); diff --git a/src/providers/twitch/TwitchAccountManager.hpp b/src/providers/twitch/TwitchAccountManager.hpp index 1cb1df1c2..32961f092 100644 --- a/src/providers/twitch/TwitchAccountManager.hpp +++ b/src/providers/twitch/TwitchAccountManager.hpp @@ -11,7 +11,8 @@ // // Warning: This class is not supposed to be created directly. -// Get yourself an instance from our friends over at AccountManager.hpp +// Get yourself an instance from our friends over at +// AccountManager.hpp // namespace chatterino { @@ -30,12 +31,14 @@ public: QString oauthToken; }; - // Returns the current twitchUsers, or the anonymous user if we're not currently logged in + // Returns the current twitchUsers, or the anonymous user if we're not + // currently logged in std::shared_ptr getCurrent(); std::vector getUsernames() const; - std::shared_ptr findUserByUsername(const QString &username) const; + std::shared_ptr findUserByUsername( + const QString &username) const; bool userExists(const QString &username) const; void reloadUsers(); @@ -43,11 +46,13 @@ public: bool isLoggedIn() const; - pajlada::Settings::Setting currentUsername = {"/accounts/current", ""}; + pajlada::Settings::Setting currentUsername = { + "/accounts/current", ""}; pajlada::Signals::NoArgSignal currentUserChanged; pajlada::Signals::NoArgSignal userListUpdated; - SortedSignalVector, SharedPtrElementLess> + SortedSignalVector, + SharedPtrElementLess> accounts; private: diff --git a/src/providers/twitch/TwitchApi.cpp b/src/providers/twitch/TwitchApi.cpp index 58ab03af1..6b1e537c3 100644 --- a/src/providers/twitch/TwitchApi.cpp +++ b/src/providers/twitch/TwitchApi.cpp @@ -8,7 +8,8 @@ namespace chatterino { -void TwitchApi::findUserId(const QString user, std::function successCallback) +void TwitchApi::findUserId(const QString user, + std::function successCallback) { QString requestUrl("https://api.twitch.tv/kraken/users?login=" + user); @@ -37,7 +38,8 @@ void TwitchApi::findUserId(const QString user, std::function succ auto firstUser = users[0].toObject(); auto id = firstUser.value("_id"); if (!id.isString()) { - Log("API Error: while getting user id, first user object `_id` key is not a " + Log("API Error: while getting user id, first user object `_id` key " + "is not a " "string"); successCallback(""); return Failure; diff --git a/src/providers/twitch/TwitchApi.hpp b/src/providers/twitch/TwitchApi.hpp index 00b8ff4d7..67b26c1ca 100644 --- a/src/providers/twitch/TwitchApi.hpp +++ b/src/providers/twitch/TwitchApi.hpp @@ -7,7 +7,8 @@ namespace chatterino { class TwitchApi { public: - static void findUserId(const QString user, std::function callback); + static void findUserId(const QString user, + std::function callback); private: }; diff --git a/src/providers/twitch/TwitchBadges.cpp b/src/providers/twitch/TwitchBadges.cpp index db329d306..fd9fe7461 100644 --- a/src/providers/twitch/TwitchBadges.cpp +++ b/src/providers/twitch/TwitchBadges.cpp @@ -19,7 +19,8 @@ void TwitchBadges::initialize(Settings &settings, Paths &paths) void TwitchBadges::loadTwitchBadges() { - static QString url("https://badges.twitch.tv/v1/badges/global/display?language=en"); + static QString url( + "https://badges.twitch.tv/v1/badges/global/display?language=en"); NetworkRequest req(url); req.setCaller(QThread::currentThread()); @@ -28,24 +29,29 @@ void TwitchBadges::loadTwitchBadges() QJsonObject sets = root.value("badge_sets").toObject(); for (QJsonObject::iterator it = sets.begin(); it != sets.end(); ++it) { - QJsonObject versions = it.value().toObject().value("versions").toObject(); + QJsonObject versions = + it.value().toObject().value("versions").toObject(); - for (auto versionIt = std::begin(versions); versionIt != std::end(versions); - ++versionIt) { - auto emote = - Emote{{""}, - ImageSet{ - Image::fromUrl({root.value("image_url_1x").toString()}, 1), - Image::fromUrl({root.value("image_url_2x").toString()}, 0.5), - Image::fromUrl({root.value("image_url_4x").toString()}, 0.25), - }, - Tooltip{root.value("description").toString()}, - Url{root.value("clickURL").toString()}}; + for (auto versionIt = std::begin(versions); + versionIt != std::end(versions); ++versionIt) { + auto emote = Emote{ + {""}, + ImageSet{ + Image::fromUrl({root.value("image_url_1x").toString()}, + 1), + Image::fromUrl({root.value("image_url_2x").toString()}, + 0.5), + Image::fromUrl({root.value("image_url_4x").toString()}, + 0.25), + }, + Tooltip{root.value("description").toString()}, + Url{root.value("clickURL").toString()}}; // "title" // "clickAction" QJsonObject versionObj = versionIt.value().toObject(); - this->badges.emplace(versionIt.key(), std::make_shared(emote)); + this->badges.emplace(versionIt.key(), + std::make_shared(emote)); } } diff --git a/src/providers/twitch/TwitchChannel.cpp b/src/providers/twitch/TwitchChannel.cpp index f5a501731..274dd4264 100644 --- a/src/providers/twitch/TwitchChannel.cpp +++ b/src/providers/twitch/TwitchChannel.cpp @@ -57,7 +57,8 @@ TwitchChannel::TwitchChannel(const QString &name) [=] { this->refreshViewerList(); }); this->chattersListTimer_.start(5 * 60 * 1000); - QObject::connect(&this->liveStatusTimer_, &QTimer::timeout, [=] { this->refreshLiveStatus(); }); + QObject::connect(&this->liveStatusTimer_, &QTimer::timeout, + [=] { this->refreshLiveStatus(); }); this->liveStatusTimer_.start(60 * 1000); // -- @@ -84,15 +85,16 @@ bool TwitchChannel::canSendMessage() const void TwitchChannel::refreshChannelEmotes() { - loadBttvChannelEmotes(this->getName(), [this, weak = weakOf(this)](auto &&emoteMap) { - if (auto shared = weak.lock()) // - *this->bttvEmotes_.access() = emoteMap; - }); - getApp()->emotes->ffz.loadChannelEmotes(this->getName(), - [this, weak = weakOf(this)](auto &&emoteMap) { - if (auto shared = weak.lock()) - *this->ffzEmotes_.access() = emoteMap; - }); + loadBttvChannelEmotes( + this->getName(), [this, weak = weakOf(this)](auto &&emoteMap) { + if (auto shared = weak.lock()) // + *this->bttvEmotes_.access() = emoteMap; + }); + getApp()->emotes->ffz.loadChannelEmotes( + this->getName(), [this, weak = weakOf(this)](auto &&emoteMap) { + if (auto shared = weak.lock()) + *this->ffzEmotes_.access() = emoteMap; + }); } void TwitchChannel::sendMessage(const QString &message) @@ -100,11 +102,11 @@ void TwitchChannel::sendMessage(const QString &message) auto app = getApp(); if (!app->accounts->twitch.isLoggedIn()) { - // XXX: It would be nice if we could add a link here somehow that opened the "account - // manager" dialog - this->addMessage( - Message::createSystemMessage("You need to log in to send messages. You can " - "link your Twitch account in the settings.")); + // XXX: It would be nice if we could add a link here somehow that opened + // the "account manager" dialog + this->addMessage(Message::createSystemMessage( + "You need to log in to send messages. You can " + "link your Twitch account in the settings.")); return; } @@ -181,7 +183,8 @@ void TwitchChannel::addJoinedUser(const QString &user) QTimer::singleShot(500, &this->lifetimeGuard_, [this] { auto joinedUsers = this->joinedUsers_.access(); - auto message = Message::createSystemMessage("Users joined: " + joinedUsers->join(", ")); + auto message = Message::createSystemMessage( + "Users joined: " + joinedUsers->join(", ")); message->flags |= Message::Collapsed; joinedUsers->clear(); this->addMessage(message); @@ -208,7 +211,8 @@ void TwitchChannel::addPartedUser(const QString &user) QTimer::singleShot(500, &this->lifetimeGuard_, [this] { auto partedUsers = this->partedUsers_.access(); - auto message = Message::createSystemMessage("Users parted: " + partedUsers->join(", ")); + auto message = Message::createSystemMessage( + "Users parted: " + partedUsers->join(", ")); message->flags |= Message::Collapsed; this->addMessage(message); partedUsers->clear(); @@ -230,7 +234,8 @@ void TwitchChannel::setRoomId(const QString &id) this->loadRecentMessages(); } -AccessGuard TwitchChannel::accessRoomModes() const +AccessGuard TwitchChannel::accessRoomModes() + const { return this->roomModes_.accessConst(); } @@ -247,12 +252,14 @@ bool TwitchChannel::isLive() const return this->streamStatus_.access()->live; } -AccessGuard TwitchChannel::accessStreamStatus() const +AccessGuard +TwitchChannel::accessStreamStatus() const { return this->streamStatus_.accessConst(); } -boost::optional TwitchChannel::getBttvEmote(const EmoteName &name) const +boost::optional TwitchChannel::getBttvEmote( + const EmoteName &name) const { auto emotes = this->bttvEmotes_.access(); auto it = emotes->find(name); @@ -261,7 +268,8 @@ boost::optional TwitchChannel::getBttvEmote(const EmoteName &name) con return it->second; } -boost::optional TwitchChannel::getFfzEmote(const EmoteName &name) const +boost::optional TwitchChannel::getFfzEmote( + const EmoteName &name) const { auto emotes = this->bttvEmotes_.access(); auto it = emotes->find(name); @@ -316,7 +324,8 @@ void TwitchChannel::refreshLiveStatus() auto roomID = this->getRoomId(); if (roomID.isEmpty()) { - Log("[TwitchChannel:{}] Refreshing live status (Missing ID)", this->getName()); + Log("[TwitchChannel:{}] Refreshing live status (Missing ID)", + this->getName()); this->setLive(false); return; } @@ -332,12 +341,13 @@ void TwitchChannel::refreshLiveStatus() request.setCaller(QThread::currentThread()); //>>>>>>> 9bfbdefd2f0972a738230d5b95a009f73b1dd933 - request.onSuccess([this, weak = this->weak_from_this()](auto result) -> Outcome { - ChannelPtr shared = weak.lock(); - if (!shared) return Failure; + request.onSuccess( + [this, weak = this->weak_from_this()](auto result) -> Outcome { + ChannelPtr shared = weak.lock(); + if (!shared) return Failure; - return this->parseLiveStatus(result.parseRapidJson()); - }); + return this->parseLiveStatus(result.parseRapidJson()); + }); request.execute(); } @@ -362,8 +372,8 @@ Outcome TwitchChannel::parseLiveStatus(const rapidjson::Document &document) return Failure; } - if (!stream.HasMember("viewers") || !stream.HasMember("game") || !stream.HasMember("channel") || - !stream.HasMember("created_at")) { + if (!stream.HasMember("viewers") || !stream.HasMember("game") || + !stream.HasMember("channel") || !stream.HasMember("created_at")) { Log("[TwitchChannel:refreshLiveStatus] Missing members in stream"); this->setLive(false); return Failure; @@ -372,7 +382,8 @@ Outcome TwitchChannel::parseLiveStatus(const rapidjson::Document &document) const rapidjson::Value &streamChannel = stream["channel"]; if (!streamChannel.IsObject() || !streamChannel.HasMember("status")) { - Log("[TwitchChannel:refreshLiveStatus] Missing member \"status\" in channel"); + Log("[TwitchChannel:refreshLiveStatus] Missing member \"status\" in " + "channel"); return Failure; } @@ -384,10 +395,11 @@ Outcome TwitchChannel::parseLiveStatus(const rapidjson::Document &document) status->viewerCount = stream["viewers"].GetUint(); status->game = stream["game"].GetString(); status->title = streamChannel["status"].GetString(); - QDateTime since = QDateTime::fromString(stream["created_at"].GetString(), Qt::ISODate); + QDateTime since = QDateTime::fromString( + stream["created_at"].GetString(), Qt::ISODate); auto diff = since.secsTo(QDateTime::currentDateTime()); - status->uptime = - QString::number(diff / 3600) + "h " + QString::number(diff % 3600 / 60) + "m"; + status->uptime = QString::number(diff / 3600) + "h " + + QString::number(diff % 3600 / 60) + "m"; status->rerun = false; if (stream.HasMember("stream_type")) { @@ -400,7 +412,8 @@ Outcome TwitchChannel::parseLiveStatus(const rapidjson::Document &document) const auto &broadcastPlatformValue = stream["broadcast_platform"]; if (broadcastPlatformValue.IsString()) { - const char *broadcastPlatform = stream["broadcast_platform"].GetString(); + const char *broadcastPlatform = + stream["broadcast_platform"].GetString(); if (strcmp(broadcastPlatform, "rerun") == 0) { status->rerun = true; } @@ -417,18 +430,20 @@ Outcome TwitchChannel::parseLiveStatus(const rapidjson::Document &document) void TwitchChannel::loadRecentMessages() { static QString genericURL = - "https://tmi.twitch.tv/api/rooms/%1/recent_messages?client_id=" + getDefaultClientID(); + "https://tmi.twitch.tv/api/rooms/%1/recent_messages?client_id=" + + getDefaultClientID(); NetworkRequest request(genericURL.arg(this->getRoomId())); request.makeAuthorizedV5(getDefaultClientID()); request.setCaller(QThread::currentThread()); - request.onSuccess([this, weak = weakOf(this)](auto result) -> Outcome { - ChannelPtr shared = weak.lock(); - if (!shared) return Failure; + request.onSuccess( + [this, weak = weakOf(this)](auto result) -> Outcome { + ChannelPtr shared = weak.lock(); + if (!shared) return Failure; - return this->parseRecentMessages(result.parseJson()); - }); + return this->parseRecentMessages(result.parseJson()); + }); request.execute(); } @@ -442,8 +457,8 @@ Outcome TwitchChannel::parseRecentMessages(const QJsonObject &jsonRoot) for (const auto jsonMessage : jsonMessages) { auto content = jsonMessage.toString().toUtf8(); - // passing nullptr as the channel makes the message invalid but we don't check for that - // anyways + // passing nullptr as the channel makes the message invalid but we don't + // check for that anyways auto message = Communi::IrcMessage::fromData(content, nullptr); auto privMsg = dynamic_cast(message); assert(privMsg); @@ -468,7 +483,8 @@ void TwitchChannel::refreshPubsub() if (roomId.isEmpty()) return; auto account = getApp()->accounts->twitch.getCurrent(); - getApp()->twitch2->pubsub->listenToChannelModerationActions(roomId, account); + getApp()->twitch2->pubsub->listenToChannelModerationActions(roomId, + account); } void TwitchChannel::refreshViewerList() @@ -477,35 +493,40 @@ void TwitchChannel::refreshViewerList() const auto streamStatus = this->accessStreamStatus(); if (getSettings()->onlyFetchChattersForSmallerStreamers) { - if (streamStatus->live && streamStatus->viewerCount > getSettings()->smallStreamerLimit) { + if (streamStatus->live && + streamStatus->viewerCount > getSettings()->smallStreamerLimit) { return; } } // get viewer list - NetworkRequest request("https://tmi.twitch.tv/group/user/" + this->getName() + "/chatters"); + NetworkRequest request("https://tmi.twitch.tv/group/user/" + + this->getName() + "/chatters"); request.setCaller(QThread::currentThread()); - request.onSuccess([this, weak = this->weak_from_this()](auto result) -> Outcome { - // channel still exists? - auto shared = weak.lock(); - if (!shared) return Failure; + request.onSuccess( + [this, weak = this->weak_from_this()](auto result) -> Outcome { + // channel still exists? + auto shared = weak.lock(); + if (!shared) return Failure; - return this->parseViewerList(result.parseJson()); - }); + return this->parseViewerList(result.parseJson()); + }); request.execute(); } Outcome TwitchChannel::parseViewerList(const QJsonObject &jsonRoot) { - static QStringList categories = {"moderators", "staff", "admins", "global_mods", "viewers"}; + static QStringList categories = {"moderators", "staff", "admins", + "global_mods", "viewers"}; // parse json QJsonObject jsonCategories = jsonRoot.value("chatters").toObject(); for (const auto &category : categories) { - for (const auto jsonCategory : jsonCategories.value(category).toArray()) { + for (const auto jsonCategory : + jsonCategories.value(category).toArray()) { this->completionModel.addUser(jsonCategory.toString()); } } @@ -515,8 +536,8 @@ Outcome TwitchChannel::parseViewerList(const QJsonObject &jsonRoot) void TwitchChannel::loadBadges() { - auto url = Url{"https://badges.twitch.tv/v1/badges/channels/" + this->getRoomId() + - "/display?language=en"}; + auto url = Url{"https://badges.twitch.tv/v1/badges/channels/" + + this->getRoomId() + "/display?language=en"}; NetworkRequest req(url.string); req.setCaller(QThread::currentThread()); @@ -529,19 +550,24 @@ void TwitchChannel::loadBadges() auto jsonRoot = result.parseJson(); auto _ = jsonRoot["badge_sets"].toObject(); - for (auto jsonBadgeSet = _.begin(); jsonBadgeSet != _.end(); jsonBadgeSet++) { + for (auto jsonBadgeSet = _.begin(); jsonBadgeSet != _.end(); + jsonBadgeSet++) { auto &versions = (*badgeSets)[jsonBadgeSet.key()]; auto _ = jsonBadgeSet->toObject()["versions"].toObject(); - for (auto jsonVersion_ = _.begin(); jsonVersion_ != _.end(); jsonVersion_++) { + for (auto jsonVersion_ = _.begin(); jsonVersion_ != _.end(); + jsonVersion_++) { auto jsonVersion = jsonVersion_->toObject(); - auto emote = std::make_shared( - Emote{EmoteName{}, - ImageSet{Image::fromUrl({jsonVersion["image_url_1x"].toString()}), - Image::fromUrl({jsonVersion["image_url_2x"].toString()}), - Image::fromUrl({jsonVersion["image_url_4x"].toString()})}, - Tooltip{jsonRoot["description"].toString()}, - Url{jsonVersion["clickURL"].toString()}}); + auto emote = std::make_shared(Emote{ + EmoteName{}, + ImageSet{Image::fromUrl( + {jsonVersion["image_url_1x"].toString()}), + Image::fromUrl( + {jsonVersion["image_url_2x"].toString()}), + Image::fromUrl( + {jsonVersion["image_url_4x"].toString()})}, + Tooltip{jsonRoot["description"].toString()}, + Url{jsonVersion["clickURL"].toString()}}); versions.emplace(jsonVersion_.key(), emote); }; @@ -555,63 +581,67 @@ void TwitchChannel::loadBadges() void TwitchChannel::loadCheerEmotes() { - auto url = Url{"https://api.twitch.tv/kraken/bits/actions?channel_id=" + this->getRoomId()}; + auto url = Url{"https://api.twitch.tv/kraken/bits/actions?channel_id=" + + this->getRoomId()}; auto request = NetworkRequest::twitchRequest(url.string); request.setCaller(QThread::currentThread()); - request.onSuccess([this, weak = weakOf(this)](auto result) -> Outcome { - auto cheerEmoteSets = ParseCheermoteSets(result.parseRapidJson()); + request.onSuccess( + [this, weak = weakOf(this)](auto result) -> Outcome { + auto cheerEmoteSets = ParseCheermoteSets(result.parseRapidJson()); - for (auto &set : cheerEmoteSets) { - auto cheerEmoteSet = CheerEmoteSet(); - cheerEmoteSet.regex = QRegularExpression("^" + set.prefix.toLower() + "([1-9][0-9]*)$"); + for (auto &set : cheerEmoteSets) { + auto cheerEmoteSet = CheerEmoteSet(); + cheerEmoteSet.regex = QRegularExpression( + "^" + set.prefix.toLower() + "([1-9][0-9]*)$"); - for (auto &tier : set.tiers) { - CheerEmote cheerEmote; + for (auto &tier : set.tiers) { + CheerEmote cheerEmote; - cheerEmote.color = QColor(tier.color); - cheerEmote.minBits = tier.minBits; + cheerEmote.color = QColor(tier.color); + cheerEmote.minBits = tier.minBits; - // TODO(pajlada): We currently hardcode dark here :| - // We will continue to do so for now since we haven't had to - // solve that anywhere else + // TODO(pajlada): We currently hardcode dark here :| + // We will continue to do so for now since we haven't had to + // solve that anywhere else - cheerEmote.animatedEmote = - std::make_shared(Emote{EmoteName{"cheer emote"}, - ImageSet{ - tier.images["dark"]["animated"]["1"], - tier.images["dark"]["animated"]["2"], - tier.images["dark"]["animated"]["4"], - }, - Tooltip{}, Url{}}); - cheerEmote.staticEmote = - std::make_shared(Emote{EmoteName{"cheer emote"}, - ImageSet{ - tier.images["dark"]["static"]["1"], - tier.images["dark"]["static"]["2"], - tier.images["dark"]["static"]["4"], - }, - Tooltip{}, Url{}}); + cheerEmote.animatedEmote = std::make_shared( + Emote{EmoteName{"cheer emote"}, + ImageSet{ + tier.images["dark"]["animated"]["1"], + tier.images["dark"]["animated"]["2"], + tier.images["dark"]["animated"]["4"], + }, + Tooltip{}, Url{}}); + cheerEmote.staticEmote = std::make_shared( + Emote{EmoteName{"cheer emote"}, + ImageSet{ + tier.images["dark"]["static"]["1"], + tier.images["dark"]["static"]["2"], + tier.images["dark"]["static"]["4"], + }, + Tooltip{}, Url{}}); - cheerEmoteSet.cheerEmotes.emplace_back(cheerEmote); + cheerEmoteSet.cheerEmotes.emplace_back(cheerEmote); + } + + std::sort(cheerEmoteSet.cheerEmotes.begin(), + cheerEmoteSet.cheerEmotes.end(), + [](const auto &lhs, const auto &rhs) { + return lhs.minBits < rhs.minBits; // + }); + + this->cheerEmoteSets_.emplace_back(cheerEmoteSet); } - std::sort(cheerEmoteSet.cheerEmotes.begin(), cheerEmoteSet.cheerEmotes.end(), - [](const auto &lhs, const auto &rhs) { - return lhs.minBits < rhs.minBits; // - }); - - this->cheerEmoteSets_.emplace_back(cheerEmoteSet); - } - - return Success; - }); + return Success; + }); request.execute(); } -boost::optional TwitchChannel::getTwitchBadge(const QString &set, - const QString &version) const +boost::optional TwitchChannel::getTwitchBadge( + const QString &set, const QString &version) const { auto badgeSets = this->badgeSets_.access(); auto it = badgeSets->find(set); diff --git a/src/providers/twitch/TwitchChannel.hpp b/src/providers/twitch/TwitchChannel.hpp index 752fd9008..422b1b68a 100644 --- a/src/providers/twitch/TwitchChannel.hpp +++ b/src/providers/twitch/TwitchChannel.hpp @@ -78,7 +78,8 @@ public: const QString &getChannelUrl(); const QString &getPopoutPlayerUrl(); - boost::optional getTwitchBadge(const QString &set, const QString &version) const; + boost::optional getTwitchBadge(const QString &set, + const QString &version) const; // Signals pajlada::Signals::NoArgSignal roomIdChanged; diff --git a/src/providers/twitch/TwitchEmotes.cpp b/src/providers/twitch/TwitchEmotes.cpp index 80337912e..08c28286f 100644 --- a/src/providers/twitch/TwitchEmotes.cpp +++ b/src/providers/twitch/TwitchEmotes.cpp @@ -14,13 +14,16 @@ TwitchEmotes::TwitchEmotes() // id is used for lookup // emoteName is used for giving a name to the emote in case it doesn't exist -EmotePtr TwitchEmotes::getOrCreateEmote(const EmoteId &id, const EmoteName &name_) +EmotePtr TwitchEmotes::getOrCreateEmote(const EmoteId &id, + const EmoteName &name_) { static QMap replacements{ - {"[oO](_|\\.)[oO]", "O_o"}, {"\\>\\;\\(", ">("}, {"\\<\\;3", "<3"}, - {"\\:-?(o|O)", ":O"}, {"\\:-?(p|P)", ":P"}, {"\\:-?[\\\\/]", ":/"}, - {"\\:-?[z|Z|\\|]", ":Z"}, {"\\:-?\\(", ":("}, {"\\:-?\\)", ":)"}, - {"\\:-?D", ":D"}, {"\\;-?(p|P)", ";P"}, {"\\;-?\\)", ";)"}, + {"[oO](_|\\.)[oO]", "O_o"}, {"\\>\\;\\(", ">("}, + {"\\<\\;3", "<3"}, {"\\:-?(o|O)", ":O"}, + {"\\:-?(p|P)", ":P"}, {"\\:-?[\\\\/]", ":/"}, + {"\\:-?[z|Z|\\|]", ":Z"}, {"\\:-?\\(", ":("}, + {"\\:-?\\)", ":)"}, {"\\:-?D", ":D"}, + {"\\;-?(p|P)", ";P"}, {"\\;-?\\)", ";)"}, {"R-?\\)", "R)"}, {"B-?\\)", "B)"}, }; @@ -42,14 +45,14 @@ EmotePtr TwitchEmotes::getOrCreateEmote(const EmoteId &id, const EmoteName &name auto shared = (*cache)[id].lock(); if (!shared) { - (*cache)[id] = shared = - std::make_shared(Emote{EmoteName{name}, - ImageSet{ - Image::fromUrl(getEmoteLink(id, "1.0"), 1), - Image::fromUrl(getEmoteLink(id, "2.0"), 0.5), - Image::fromUrl(getEmoteLink(id, "3.0"), 0.25), - }, - Tooltip{name}, Url{}}); + (*cache)[id] = shared = std::make_shared( + Emote{EmoteName{name}, + ImageSet{ + Image::fromUrl(getEmoteLink(id, "1.0"), 1), + Image::fromUrl(getEmoteLink(id, "2.0"), 0.5), + Image::fromUrl(getEmoteLink(id, "3.0"), 0.25), + }, + Tooltip{name}, Url{}}); } return shared; @@ -57,8 +60,9 @@ EmotePtr TwitchEmotes::getOrCreateEmote(const EmoteId &id, const EmoteName &name Url TwitchEmotes::getEmoteLink(const EmoteId &id, const QString &emoteScale) { - return { - QString(TWITCH_EMOTE_TEMPLATE).replace("{id}", id.string).replace("{scale}", emoteScale)}; + return {QString(TWITCH_EMOTE_TEMPLATE) + .replace("{id}", id.string) + .replace("{scale}", emoteScale)}; } AccessGuard> TwitchEmotes::accessAll() diff --git a/src/providers/twitch/TwitchEmotes.hpp b/src/providers/twitch/TwitchEmotes.hpp index e403ebb43..665e1e9cd 100644 --- a/src/providers/twitch/TwitchEmotes.hpp +++ b/src/providers/twitch/TwitchEmotes.hpp @@ -11,7 +11,8 @@ #include "providers/twitch/TwitchEmotes.hpp" #include "util/ConcurrentMap.hpp" -#define TWITCH_EMOTE_TEMPLATE "https://static-cdn.jtvnw.net/emoticons/v1/{id}/{scale}" +#define TWITCH_EMOTE_TEMPLATE \ + "https://static-cdn.jtvnw.net/emoticons/v1/{id}/{scale}" namespace chatterino { @@ -26,7 +27,8 @@ public: private: UniqueAccess> twitchEmotes_; - UniqueAccess>> twitchEmotesCache_; + UniqueAccess>> + twitchEmotesCache_; }; } // namespace chatterino diff --git a/src/providers/twitch/TwitchMessageBuilder.cpp b/src/providers/twitch/TwitchMessageBuilder.cpp index d2cd82af7..91c9161fc 100644 --- a/src/providers/twitch/TwitchMessageBuilder.cpp +++ b/src/providers/twitch/TwitchMessageBuilder.cpp @@ -20,9 +20,9 @@ namespace chatterino { -TwitchMessageBuilder::TwitchMessageBuilder(Channel *_channel, - const Communi::IrcPrivateMessage *_ircMessage, - const MessageParseArgs &_args) +TwitchMessageBuilder::TwitchMessageBuilder( + Channel *_channel, const Communi::IrcPrivateMessage *_ircMessage, + const MessageParseArgs &_args) : channel(_channel) , twitchChannel(dynamic_cast(_channel)) , ircMessage(_ircMessage) @@ -35,10 +35,9 @@ TwitchMessageBuilder::TwitchMessageBuilder(Channel *_channel, this->usernameColor_ = app->themes->messages.textColors.system; } -TwitchMessageBuilder::TwitchMessageBuilder(Channel *_channel, - const Communi::IrcMessage *_ircMessage, - const MessageParseArgs &_args, QString content, - bool isAction) +TwitchMessageBuilder::TwitchMessageBuilder( + Channel *_channel, const Communi::IrcMessage *_ircMessage, + const MessageParseArgs &_args, QString content, bool isAction) : channel(_channel) , twitchChannel(dynamic_cast(_channel)) , ircMessage(_ircMessage) @@ -58,17 +57,21 @@ bool TwitchMessageBuilder::isIgnored() const // TODO(pajlada): Do we need to check if the phrase is valid first? for (const auto &phrase : app->ignores->phrases.getVector()) { if (phrase.isMatch(this->originalMessage_)) { - Log("Blocking message because it contains ignored phrase {}", phrase.getPattern()); + Log("Blocking message because it contains ignored phrase {}", + phrase.getPattern()); return true; } } - if (app->settings->enableTwitchIgnoredUsers && this->tags.contains("user-id")) { + if (app->settings->enableTwitchIgnoredUsers && + this->tags.contains("user-id")) { auto sourceUserID = this->tags.value("user-id").toString(); - for (const auto &user : app->accounts->twitch.getCurrent()->getIgnores()) { + for (const auto &user : + app->accounts->twitch.getCurrent()->getIgnores()) { if (sourceUserID == user.id) { - Log("Blocking message because it's from blocked user {}", user.name); + Log("Blocking message because it's from blocked user {}", + user.name); return true; } } @@ -162,8 +165,9 @@ MessagePtr TwitchMessageBuilder::build() this->appendTwitchEmote(ircMessage, emote, twitchEmotes); } - std::sort(twitchEmotes.begin(), twitchEmotes.end(), - [](const auto &a, const auto &b) { return a.first < b.first; }); + std::sort( + twitchEmotes.begin(), twitchEmotes.end(), + [](const auto &a, const auto &b) { return a.first < b.first; }); } // words @@ -176,17 +180,20 @@ MessagePtr TwitchMessageBuilder::build() return this->getMessage(); } -void TwitchMessageBuilder::addWords(const QStringList &words, - const std::vector> &twitchEmotes) +void TwitchMessageBuilder::addWords( + const QStringList &words, + const std::vector> &twitchEmotes) { auto i = int(); auto currentTwitchEmote = twitchEmotes.begin(); for (const auto &word : words) { // check if it's a twitch emote twitch emote - if (currentTwitchEmote != twitchEmotes.end() && currentTwitchEmote->first == i) { + if (currentTwitchEmote != twitchEmotes.end() && + currentTwitchEmote->first == i) { auto emoteImage = currentTwitchEmote->second; - this->emplace(emoteImage, MessageElement::TwitchEmote); + this->emplace(emoteImage, + MessageElement::TwitchEmote); i += word.length() + 1; currentTwitchEmote++; @@ -196,9 +203,12 @@ void TwitchMessageBuilder::addWords(const QStringList &words, // split words for (auto &variant : getApp()->emotes->emojis.parse(word)) { - boost::apply_visitor(/*overloaded{[&](EmotePtr arg) { this->addTextOrEmoji(arg); }, - [&](const QString &arg) { this->addTextOrEmoji(arg); }}*/ - [&](auto &&arg) { this->addTextOrEmoji(arg); }, variant); + boost::apply_visitor(/*overloaded{[&](EmotePtr arg) { + this->addTextOrEmoji(arg); }, + [&](const QString &arg) { + this->addTextOrEmoji(arg); }}*/ + [&](auto &&arg) { this->addTextOrEmoji(arg); }, + variant); } for (int j = 0; j < word.size(); j++) { @@ -240,14 +250,15 @@ void TwitchMessageBuilder::addTextOrEmoji(const QString &string_) // Actually just text auto linkString = this->matchLink(string); auto link = Link(); - auto textColor = - this->action_ ? MessageColor(this->usernameColor_) : MessageColor(MessageColor::Text); + auto textColor = this->action_ ? MessageColor(this->usernameColor_) + : MessageColor(MessageColor::Text); if (linkString.isEmpty()) { if (string.startsWith('@')) { - this->emplace(string, TextElement::BoldUsername, textColor, - FontStyle::ChatMediumBold); - this->emplace(string, TextElement::NonBoldUsername, textColor); + this->emplace(string, TextElement::BoldUsername, + textColor, FontStyle::ChatMediumBold); + this->emplace(string, TextElement::NonBoldUsername, + textColor); } else { this->emplace(string, TextElement::Text, textColor); } @@ -260,22 +271,26 @@ void TwitchMessageBuilder::addTextOrEmoji(const QString &string_) auto match = domainRegex.match(string); if (match.isValid()) { lowercaseLinkString = string.mid(0, match.capturedStart(1)) + - match.captured(1).toLower() + string.mid(match.capturedEnd(1)); + match.captured(1).toLower() + + string.mid(match.capturedEnd(1)); } else { lowercaseLinkString = string; } link = Link(Link::Url, linkString); textColor = MessageColor(MessageColor::Link); - this->emplace(lowercaseLinkString, TextElement::LowercaseLink, textColor) + this->emplace(lowercaseLinkString, + TextElement::LowercaseLink, textColor) + ->setLink(link); + this->emplace(string, TextElement::OriginalLink, textColor) ->setLink(link); - this->emplace(string, TextElement::OriginalLink, textColor)->setLink(link); } // if (!linkString.isEmpty()) { // if (getApp()->settings->lowercaseLink) { // QRegularExpression httpRegex("\\bhttps?://", - // QRegularExpression::CaseInsensitiveOption); QRegularExpression ftpRegex("\\bftps?://", + // QRegularExpression::CaseInsensitiveOption); QRegularExpression + // ftpRegex("\\bftps?://", // QRegularExpression::CaseInsensitiveOption); QRegularExpression // getDomain("\\/\\/([^\\/]*)"); QString tempString = string; @@ -291,7 +306,8 @@ void TwitchMessageBuilder::addTextOrEmoji(const QString &string_) // textColor = MessageColor(MessageColor::Link); //} // if (string.startsWith('@')) { - // this->emplace(string, TextElement::BoldUsername, textColor, + // this->emplace(string, TextElement::BoldUsername, + // textColor, // FontStyle::ChatMediumBold) // // ->setLink(link); // this->emplace(string, TextElement::NonBoldUsername, @@ -334,7 +350,8 @@ void TwitchMessageBuilder::appendChannelName() QString channelName("#" + this->channel->getName()); Link link(Link::Url, this->channel->getName() + "\n" + this->messageID); - this->emplace(channelName, MessageElement::ChannelName, MessageColor::System) // + this->emplace(channelName, MessageElement::ChannelName, + MessageColor::System) // ->setLink(link); } @@ -355,7 +372,8 @@ void TwitchMessageBuilder::parseUsername() // display name // auto displayNameVariant = this->tags.value("display-name"); // if (displayNameVariant.isValid()) { - // this->userName = displayNameVariant.toString() + " (" + this->userName + ")"; + // this->userName = displayNameVariant.toString() + " (" + + // this->userName + ")"; // } this->message_->loginName = this->userName; @@ -371,9 +389,11 @@ void TwitchMessageBuilder::appendUsername() auto iterator = this->tags.find("display-name"); if (iterator != this->tags.end()) { - QString displayName = parseTagString(iterator.value().toString()).trimmed(); + QString displayName = + parseTagString(iterator.value().toString()).trimmed(); - if (QString::compare(displayName, this->userName, Qt::CaseInsensitive) == 0) { + if (QString::compare(displayName, this->userName, + Qt::CaseInsensitive) == 0) { username = displayName; this->message_->displayName = displayName; @@ -391,7 +411,8 @@ void TwitchMessageBuilder::appendUsername() QString usernameText; pajlada::Settings::Setting usernameDisplayMode( - "/appearance/messages/usernameDisplayMode", UsernameDisplayMode::UsernameAndLocalizedName); + "/appearance/messages/usernameDisplayMode", + UsernameDisplayMode::UsernameAndLocalizedName); switch (usernameDisplayMode.getValue()) { case UsernameDisplayMode::Username: { @@ -418,10 +439,12 @@ void TwitchMessageBuilder::appendUsername() if (this->args.isSentWhisper) { // TODO(pajlada): Re-implement - // userDisplayString += IrcManager::getInstance().getUser().getUserName(); + // userDisplayString += + // IrcManager::getInstance().getUser().getUserName(); } else if (this->args.isReceivedWhisper) { // Sender username - this->emplace(usernameText, MessageElement::Text, this->usernameColor_, + this->emplace(usernameText, MessageElement::Text, + this->usernameColor_, FontStyle::ChatMediumBold) ->setLink({Link::UserInfo, this->userName}); @@ -429,7 +452,8 @@ void TwitchMessageBuilder::appendUsername() // Separator this->emplace("->", MessageElement::Text, - app->themes->messages.textColors.system, FontStyle::ChatMedium); + app->themes->messages.textColors.system, + FontStyle::ChatMedium); QColor selfColor = currentUser->color; if (!selfColor.isValid()) { @@ -437,14 +461,16 @@ void TwitchMessageBuilder::appendUsername() } // Your own username - this->emplace(currentUser->getUserName() + ":", MessageElement::Text, - selfColor, FontStyle::ChatMediumBold); + this->emplace(currentUser->getUserName() + ":", + MessageElement::Text, selfColor, + FontStyle::ChatMediumBold); } else { if (!this->action_) { usernameText += ":"; } - this->emplace(usernameText, MessageElement::Text, this->usernameColor_, + this->emplace(usernameText, MessageElement::Text, + this->usernameColor_, FontStyle::ChatMediumBold) ->setLink({Link::UserInfo, this->userName}); } @@ -470,7 +496,8 @@ void TwitchMessageBuilder::parseHighlights(bool isPastMsg) // update the media player url if necessary QUrl highlightSoundUrl; if (app->settings->customHighlightSound) { - highlightSoundUrl = QUrl::fromLocalFile(app->settings->pathHighlightSound.getValue()); + highlightSoundUrl = + QUrl::fromLocalFile(app->settings->pathHighlightSound.getValue()); } else { highlightSoundUrl = QUrl("qrc:/sounds/ping2.wav"); } @@ -483,12 +510,15 @@ void TwitchMessageBuilder::parseHighlights(bool isPastMsg) // TODO: This vector should only be rebuilt upon highlights being changed // fourtf: should be implemented in the HighlightsController - std::vector activeHighlights = app->highlights->phrases.getVector(); - std::vector userHighlights = app->highlights->highlightedUsers.getVector(); + std::vector activeHighlights = + app->highlights->phrases.getVector(); + std::vector userHighlights = + app->highlights->highlightedUsers.getVector(); if (app->settings->enableHighlightsSelf && currentUsername.size() > 0) { - HighlightPhrase selfHighlight(currentUsername, app->settings->enableHighlightTaskbar, - app->settings->enableHighlightSound, false); + HighlightPhrase selfHighlight( + currentUsername, app->settings->enableHighlightTaskbar, + app->settings->enableHighlightSound, false); activeHighlights.emplace_back(std::move(selfHighlight)); } @@ -514,15 +544,17 @@ void TwitchMessageBuilder::parseHighlights(bool isPastMsg) } if (playSound && doAlert) { - // Break if no further action can be taken from other highlights - // This might change if highlights can have custom colors/sounds/actions + // Break if no further action can be taken from other + // highlights This might change if highlights can have + // custom colors/sounds/actions break; } } } for (const HighlightPhrase &userHighlight : userHighlights) { if (userHighlight.isMatch(this->ircMessage->nick())) { - Log("Highlight because user {} sent a message", this->ircMessage->nick()); + Log("Highlight because user {} sent a message", + this->ircMessage->nick()); doHighlight = true; if (userHighlight.getAlert()) { @@ -534,8 +566,8 @@ void TwitchMessageBuilder::parseHighlights(bool isPastMsg) } if (playSound && doAlert) { - // Break if no further action can be taken from other usernames - // Mostly used for regex stuff + // Break if no further action can be taken from other + // usernames Mostly used for regex stuff break; } } @@ -544,12 +576,14 @@ void TwitchMessageBuilder::parseHighlights(bool isPastMsg) this->setHighlight(doHighlight); if (!isPastMsg) { - if (playSound && (!hasFocus || app->settings->highlightAlwaysPlaySound)) { + if (playSound && + (!hasFocus || app->settings->highlightAlwaysPlaySound)) { player->play(); } if (doAlert) { - QApplication::alert(getApp()->windows->getMainWindow().window(), 2500); + QApplication::alert(getApp()->windows->getMainWindow().window(), + 2500); } } @@ -559,9 +593,9 @@ void TwitchMessageBuilder::parseHighlights(bool isPastMsg) } } -void TwitchMessageBuilder::appendTwitchEmote(const Communi::IrcMessage *ircMessage, - const QString &emote, - std::vector> &vec) +void TwitchMessageBuilder::appendTwitchEmote( + const Communi::IrcMessage *ircMessage, const QString &emote, + std::vector> &vec) { auto app = getApp(); if (!emote.contains(':')) { @@ -588,13 +622,16 @@ void TwitchMessageBuilder::appendTwitchEmote(const Communi::IrcMessage *ircMessa auto start = coords.at(0).toInt(); auto end = coords.at(1).toInt(); - if (start >= end || start < 0 || end > this->originalMessage_.length()) { + if (start >= end || start < 0 || + end > this->originalMessage_.length()) { return; } - auto name = EmoteName{this->originalMessage_.mid(start, end - start + 1)}; + auto name = + EmoteName{this->originalMessage_.mid(start, end - start + 1)}; - vec.push_back(std::make_pair(start, app->emotes->twitch.getOrCreateEmote(id, name))); + vec.push_back(std::make_pair( + start, app->emotes->twitch.getOrCreateEmote(id, name))); } } @@ -605,11 +642,13 @@ Outcome TwitchMessageBuilder::tryAppendEmote(const EmoteName &name) if ((emote = getApp()->emotes->bttv.getGlobalEmote(name))) { flags = MessageElement::BttvEmote; - } else if (twitchChannel && (emote = this->twitchChannel->getBttvEmote(name))) { + } else if (twitchChannel && + (emote = this->twitchChannel->getBttvEmote(name))) { flags = MessageElement::BttvEmote; } else if ((emote = getApp()->emotes->ffz.getGlobalEmote(name))) { flags = MessageElement::FfzEmote; - } else if (twitchChannel && (emote = this->twitchChannel->getFfzEmote(name))) { + } else if (twitchChannel && + (emote = this->twitchChannel->getFfzEmote(name))) { flags = MessageElement::FfzEmote; } @@ -622,7 +661,8 @@ Outcome TwitchMessageBuilder::tryAppendEmote(const EmoteName &name) } // fourtf: this is ugly -// maybe put the individual badges into a map instead of this mess +// maybe put the individual badges into a map instead of this +//mess void TwitchMessageBuilder::appendTwitchBadges() { auto app = getApp(); @@ -653,9 +693,10 @@ void TwitchMessageBuilder::appendTwitchBadges() // Try to fetch channel-specific bit badge try { if (twitchChannel) - if (const auto &badge = - this->twitchChannel->getTwitchBadge("bits", cheerAmount)) { - this->emplace(badge.get(), MessageElement::BadgeVanity) + if (const auto &badge = this->twitchChannel->getTwitchBadge( + "bits", cheerAmount)) { + this->emplace(badge.get(), + MessageElement::BadgeVanity) ->setTooltip(tooltip); continue; } @@ -665,62 +706,72 @@ void TwitchMessageBuilder::appendTwitchBadges() // Use default bit badge // try { - // const auto &badge = app->resources->badgeSets.at("bits").versions.at(cheerAmount); - // this->emplace(badge.badgeImage1x, MessageElement::BadgeVanity) + // const auto &badge = + // app->resources->badgeSets.at("bits").versions.at(cheerAmount); + // this->emplace(badge.badgeImage1x, + // MessageElement::BadgeVanity) // ->setTooltip(tooltip); //} catch (const std::out_of_range &) { // Log("No default bit badge for version {} found", cheerAmount); // continue; //} } else if (badge == "staff/1") { - this->emplace(Image::fromNonOwningPixmap(&app->resources->twitch.staff), - MessageElement::BadgeGlobalAuthority) + this->emplace( + Image::fromNonOwningPixmap(&app->resources->twitch.staff), + MessageElement::BadgeGlobalAuthority) ->setTooltip("Twitch Staff"); } else if (badge == "admin/1") { - this->emplace(Image::fromNonOwningPixmap(&app->resources->twitch.admin), - MessageElement::BadgeGlobalAuthority) + this->emplace( + Image::fromNonOwningPixmap(&app->resources->twitch.admin), + MessageElement::BadgeGlobalAuthority) ->setTooltip("Twitch Admin"); } else if (badge == "global_mod/1") { - this->emplace( - Image::fromNonOwningPixmap(&app->resources->twitch.globalmod), - MessageElement::BadgeGlobalAuthority) + this->emplace(Image::fromNonOwningPixmap( + &app->resources->twitch.globalmod), + MessageElement::BadgeGlobalAuthority) ->setTooltip("Twitch Global Moderator"); } else if (badge == "moderator/1") { // TODO: Implement custom FFZ moderator badge - this->emplace( - Image::fromNonOwningPixmap(&app->resources->twitch.moderator), - MessageElement::BadgeChannelAuthority) + this->emplace(Image::fromNonOwningPixmap( + &app->resources->twitch.moderator), + MessageElement::BadgeChannelAuthority) ->setTooltip("Twitch Channel Moderator"); } else if (badge == "turbo/1") { - this->emplace(Image::fromNonOwningPixmap(&app->resources->twitch.turbo), - MessageElement::BadgeGlobalAuthority) + this->emplace( + Image::fromNonOwningPixmap(&app->resources->twitch.turbo), + MessageElement::BadgeGlobalAuthority) ->setTooltip("Twitch Turbo Subscriber"); } else if (badge == "broadcaster/1") { this->emplace( - Image::fromNonOwningPixmap(&app->resources->twitch.broadcaster), + Image::fromNonOwningPixmap( + &app->resources->twitch.broadcaster), MessageElement::BadgeChannelAuthority) ->setTooltip("Twitch Broadcaster"); } else if (badge == "premium/1") { - this->emplace(Image::fromNonOwningPixmap(&app->resources->twitch.prime), - MessageElement::BadgeVanity) + this->emplace( + Image::fromNonOwningPixmap(&app->resources->twitch.prime), + MessageElement::BadgeVanity) ->setTooltip("Twitch Prime Subscriber"); } else if (badge.startsWith("partner/")) { int index = badge.midRef(8).toInt(); switch (index) { case 1: { this->emplace( - Image::fromNonOwningPixmap(&app->resources->twitch.verified), + Image::fromNonOwningPixmap( + &app->resources->twitch.verified), MessageElement::BadgeVanity) ->setTooltip("Twitch Verified"); } break; default: { - printf("[TwitchMessageBuilder] Unhandled partner badge index: %d\n", index); + printf("[TwitchMessageBuilder] Unhandled partner badge " + "index: %d\n", + index); } break; } } else if (badge.startsWith("subscriber/")) { // if (channelResources.loaded == false) { - // // qDebug() << "Channel resources are not loaded, can't add the - // subscriber + // // qDebug() << "Channel resources are not loaded, + // can't add the subscriber // // badge"; // continue; // } @@ -752,7 +803,8 @@ void TwitchMessageBuilder::appendTwitchBadges() // this->emplace(badgeVersion.badgeImage1x, // MessageElement::BadgeSubscription) - // ->setTooltip("Twitch " + QString::fromStdString(badgeVersion.title)); + // ->setTooltip("Twitch " + + // QString::fromStdString(badgeVersion.title)); } else { // if (!app->resources->dynamicBadgesLoaded) { // // Do nothing @@ -762,11 +814,12 @@ void TwitchMessageBuilder::appendTwitchBadges() // QStringList parts = badge.split('/'); // if (parts.length() != 2) { - // qDebug() << "Bad number of parts: " << parts.length() << " in " << parts; - // continue; + // qDebug() << "Bad number of parts: " << parts.length() << " in + // " << parts; continue; //} - // MessageElement::Flags badgeType = MessageElement::Flags::BadgeVanity; + // MessageElement::Flags badgeType = + // MessageElement::Flags::BadgeVanity; // std::string badgeSetKey = parts[0].toStdString(); // std::string versionKey = parts[1].toStdString(); @@ -777,11 +830,14 @@ void TwitchMessageBuilder::appendTwitchBadges() // try { // auto &badgeVersion = badgeSet.versions.at(versionKey); - // this->emplace(badgeVersion.badgeImage1x, badgeType) - // ->setTooltip("Twitch " + QString::fromStdString(badgeVersion.title)); + // this->emplace(badgeVersion.badgeImage1x, + // badgeType) + // ->setTooltip("Twitch " + + // QString::fromStdString(badgeVersion.title)); // } catch (const std::exception &e) { // qDebug() << "Exception caught:" << e.what() - // << "when trying to fetch badge version " << versionKey.c_str(); + // << "when trying to fetch badge version " << + // versionKey.c_str(); // } //} catch (const std::exception &e) { // qDebug() << "No badge set with key" << badgeSetKey.c_str() @@ -804,7 +860,8 @@ void TwitchMessageBuilder::appendChatterinoBadges() // const auto badge = it->second; - // this->emplace(badge->image, MessageElement::BadgeChatterino) + // this->emplace(badge->image, + // MessageElement::BadgeChatterino) // ->setTooltip(QString::fromStdString(badge->tooltip)); } @@ -830,7 +887,8 @@ Outcome TwitchMessageBuilder::tryParseCheermote(const QString &string) // auto savedIt = cheermoteSet.cheermotes.end(); // // Fetch cheermote that matches our numBits - // for (auto it = cheermoteSet.cheermotes.begin(); it != cheermoteSet.cheermotes.end(); + // for (auto it = cheermoteSet.cheermotes.begin(); it != + // cheermoteSet.cheermotes.end(); // ++it) { // if (numBits >= it->minBits) { // savedIt = it; @@ -840,15 +898,17 @@ Outcome TwitchMessageBuilder::tryParseCheermote(const QString &string) // } // if (savedIt == cheermoteSet.cheermotes.end()) { - // Log("Error getting a cheermote from a cheermote set for the bit amount {}", + // Log("Error getting a cheermote from a cheermote set for the + // bit amount {}", // numBits); // return Failure; // } // const auto &cheermote = *savedIt; - // this->emplace(cheermote.animatedEmote, EmoteElement::BitsAnimated); - // this->emplace(amount, EmoteElement::Text, cheermote.color); + // this->emplace(cheermote.animatedEmote, + // EmoteElement::BitsAnimated); this->emplace(amount, + // EmoteElement::Text, cheermote.color); // return Success; // } diff --git a/src/providers/twitch/TwitchMessageBuilder.hpp b/src/providers/twitch/TwitchMessageBuilder.hpp index 1bf538f35..c82364a67 100644 --- a/src/providers/twitch/TwitchMessageBuilder.hpp +++ b/src/providers/twitch/TwitchMessageBuilder.hpp @@ -25,10 +25,13 @@ public: TwitchMessageBuilder() = delete; - explicit TwitchMessageBuilder(Channel *_channel, const Communi::IrcPrivateMessage *_ircMessage, + explicit TwitchMessageBuilder(Channel *_channel, + const Communi::IrcPrivateMessage *_ircMessage, const MessageParseArgs &_args); - explicit TwitchMessageBuilder(Channel *_channel, const Communi::IrcMessage *_ircMessage, - const MessageParseArgs &_args, QString content, bool isAction); + explicit TwitchMessageBuilder(Channel *_channel, + const Communi::IrcMessage *_ircMessage, + const MessageParseArgs &_args, + QString content, bool isAction); Channel *channel; TwitchChannel *twitchChannel; @@ -50,7 +53,8 @@ private: void appendUsername(); void parseHighlights(bool isPastMsg); - void appendTwitchEmote(const Communi::IrcMessage *ircMessage, const QString &emote, + void appendTwitchEmote(const Communi::IrcMessage *ircMessage, + const QString &emote, std::vector> &vec); Outcome tryAppendEmote(const EmoteName &name); diff --git a/src/providers/twitch/TwitchParseCheerEmotes.cpp b/src/providers/twitch/TwitchParseCheerEmotes.cpp index a7193d2f6..47d6427f8 100644 --- a/src/providers/twitch/TwitchParseCheerEmotes.cpp +++ b/src/providers/twitch/TwitchParseCheerEmotes.cpp @@ -9,7 +9,8 @@ namespace chatterino { namespace { template -inline bool ReadValue(const rapidjson::Value &object, const char *key, Type &out) +inline bool ReadValue(const rapidjson::Value &object, const char *key, + Type &out) { if (!object.HasMember(key)) { return false; @@ -27,7 +28,8 @@ inline bool ReadValue(const rapidjson::Value &object, const char *key, Type &out } template <> -inline bool ReadValue(const rapidjson::Value &object, const char *key, QString &out) +inline bool ReadValue(const rapidjson::Value &object, const char *key, + QString &out) { if (!object.HasMember(key)) { return false; @@ -45,7 +47,8 @@ inline bool ReadValue(const rapidjson::Value &object, const char *key, } template <> -inline bool ReadValue>(const rapidjson::Value &object, const char *key, +inline bool ReadValue>(const rapidjson::Value &object, + const char *key, std::vector &out) { if (!object.HasMember(key)) { @@ -70,7 +73,8 @@ inline bool ReadValue>(const rapidjson::Value &object, cons } // Parse a single cheermote set (or "action") from the twitch api -inline bool ParseSingleCheermoteSet(JSONCheermoteSet &set, const rapidjson::Value &action) +inline bool ParseSingleCheermoteSet(JSONCheermoteSet &set, + const rapidjson::Value &action) { if (!action.IsObject()) { return false; @@ -160,13 +164,15 @@ inline bool ParseSingleCheermoteSet(JSONCheermoteSet &set, const rapidjson::Valu continue; } - const rapidjson::Value &imageBackgroundStates = imageBackgroundValue.value; + const rapidjson::Value &imageBackgroundStates = + imageBackgroundValue.value; if (!imageBackgroundStates.IsObject()) { continue; } // Read each key which represents a background - for (const auto &imageBackgroundState : imageBackgroundStates.GetObject()) { + for (const auto &imageBackgroundState : + imageBackgroundStates.GetObject()) { QString state = imageBackgroundState.name.GetString(); bool stateExists = false; for (const auto &_state : set.states) { @@ -180,13 +186,15 @@ inline bool ParseSingleCheermoteSet(JSONCheermoteSet &set, const rapidjson::Valu continue; } - const rapidjson::Value &imageScalesValue = imageBackgroundState.value; + const rapidjson::Value &imageScalesValue = + imageBackgroundState.value; if (!imageScalesValue.IsObject()) { continue; } // Read each key which represents a scale - for (const auto &imageScaleValue : imageScalesValue.GetObject()) { + for (const auto &imageScaleValue : + imageScalesValue.GetObject()) { QString scale = imageScaleValue.name.GetString(); bool scaleExists = false; for (const auto &_scale : set.scales) { @@ -200,7 +208,8 @@ inline bool ParseSingleCheermoteSet(JSONCheermoteSet &set, const rapidjson::Valu continue; } - const rapidjson::Value &imageScaleURLValue = imageScaleValue.value; + const rapidjson::Value &imageScaleURLValue = + imageScaleValue.value; if (!imageScaleURLValue.IsString()) { continue; } @@ -230,8 +239,9 @@ inline bool ParseSingleCheermoteSet(JSONCheermoteSet &set, const rapidjson::Valu } } // namespace -// Look through the results of https://api.twitch.tv/kraken/bits/actions?channel_id=11148817 for -// cheermote sets or "Actions" as they are called in the API +// Look through the results of +// https://api.twitch.tv/kraken/bits/actions?channel_id=11148817 for cheermote +// sets or "Actions" as they are called in the API std::vector ParseCheermoteSets(const rapidjson::Document &d) { std::vector sets; diff --git a/src/providers/twitch/TwitchParseCheerEmotes.hpp b/src/providers/twitch/TwitchParseCheerEmotes.hpp index 150c8b447..284f87bf0 100644 --- a/src/providers/twitch/TwitchParseCheerEmotes.hpp +++ b/src/providers/twitch/TwitchParseCheerEmotes.hpp @@ -25,7 +25,8 @@ struct JSONCheermoteSet { QString color; // Background State Scale - std::map>> images; + std::map>> + images; }; std::vector tiers; diff --git a/src/providers/twitch/TwitchServer.cpp b/src/providers/twitch/TwitchServer.cpp index 14542afcf..49d24c654 100644 --- a/src/providers/twitch/TwitchServer.cpp +++ b/src/providers/twitch/TwitchServer.cpp @@ -29,8 +29,10 @@ TwitchServer::TwitchServer() this->pubsub = new PubSub; - // getSettings()->twitchSeperateWriteConnection.connect([this](auto, auto) { this->connect(); }, - // this->signalHolder_, false); + // getSettings()->twitchSeperateWriteConnection.connect([this](auto, auto) { + // this->connect(); }, + // this->signalHolder_, + // false); } void TwitchServer::initialize(Settings &settings, Paths &paths) @@ -39,11 +41,13 @@ void TwitchServer::initialize(Settings &settings, Paths &paths) [this]() { postToThread([this] { this->connect(); }); }); } -void TwitchServer::initializeConnection(IrcConnection *connection, bool isRead, bool isWrite) +void TwitchServer::initializeConnection(IrcConnection *connection, bool isRead, + bool isWrite) { this->singleConnection_ = isRead == isWrite; - std::shared_ptr account = getApp()->accounts->twitch.getCurrent(); + std::shared_ptr account = + getApp()->accounts->twitch.getCurrent(); qDebug() << "logging in as" << account->getUserName(); @@ -62,9 +66,12 @@ void TwitchServer::initializeConnection(IrcConnection *connection, bool isRead, connection->setPassword(oauthToken); } - connection->sendCommand(Communi::IrcCommand::createCapability("REQ", "twitch.tv/membership")); - connection->sendCommand(Communi::IrcCommand::createCapability("REQ", "twitch.tv/commands")); - connection->sendCommand(Communi::IrcCommand::createCapability("REQ", "twitch.tv/tags")); + connection->sendCommand( + Communi::IrcCommand::createCapability("REQ", "twitch.tv/membership")); + connection->sendCommand( + Communi::IrcCommand::createCapability("REQ", "twitch.tv/commands")); + connection->sendCommand( + Communi::IrcCommand::createCapability("REQ", "twitch.tv/tags")); connection->setHost("irc.chat.twitch.tv"); connection->setPort(6667); @@ -74,9 +81,10 @@ std::shared_ptr TwitchServer::createChannel(const QString &channelName) { TwitchChannel *channel = new TwitchChannel(channelName); - channel->sendMessageSignal.connect([this, channel](auto &chan, auto &msg, bool &sent) { - this->onMessageSendRequested(channel, msg, sent); - }); + channel->sendMessageSignal.connect( + [this, channel](auto &chan, auto &msg, bool &sent) { + this->onMessageSendRequested(channel, msg, sent); + }); return std::shared_ptr(channel); } @@ -113,7 +121,8 @@ void TwitchServer::messageReceived(Communi::IrcMessage *message) } else if (command == "MODE") { handler.handleModeMessage(message); } else if (command == "NOTICE") { - handler.handleNoticeMessage(static_cast(message)); + handler.handleNoticeMessage( + static_cast(message)); } else if (command == "JOIN") { handler.handleJoinMessage(message); } else if (command == "PART") { @@ -133,7 +142,8 @@ void TwitchServer::writeConnectionMessageReceived(Communi::IrcMessage *message) } } -std::shared_ptr TwitchServer::getCustomChannel(const QString &channelName) +std::shared_ptr TwitchServer::getCustomChannel( + const QString &channelName) { if (channelName == "/whispers") { return this->whispersChannel; @@ -146,7 +156,8 @@ std::shared_ptr TwitchServer::getCustomChannel(const QString &channelNa return nullptr; } -void TwitchServer::forEachChannelAndSpecialChannels(std::function func) +void TwitchServer::forEachChannelAndSpecialChannels( + std::function func) { this->forEachChannel(func); @@ -154,7 +165,8 @@ void TwitchServer::forEachChannelAndSpecialChannels(std::functionmentionsChannel); } -std::shared_ptr TwitchServer::getChannelOrEmptyByID(const QString &channelId) +std::shared_ptr TwitchServer::getChannelOrEmptyByID( + const QString &channelId) { std::lock_guard lock(this->channelMutex); @@ -184,8 +196,8 @@ bool TwitchServer::hasSeparateWriteConnection() const // return getSettings()->twitchSeperateWriteConnection; } -void TwitchServer::onMessageSendRequested(TwitchChannel *channel, const QString &message, - bool &sent) +void TwitchServer::onMessageSendRequested(TwitchChannel *channel, + const QString &message, bool &sent) { sent = false; @@ -193,17 +205,19 @@ void TwitchServer::onMessageSendRequested(TwitchChannel *channel, const QString std::lock_guard guard(this->lastMessageMutex_); // std::queue - auto &lastMessage = - channel->hasModRights() ? this->lastMessageMod_ : this->lastMessagePleb_; + auto &lastMessage = channel->hasModRights() ? this->lastMessageMod_ + : this->lastMessagePleb_; size_t maxMessageCount = channel->hasModRights() ? 99 : 19; auto minMessageOffset = (channel->hasModRights() ? 100ms : 1100ms); auto now = std::chrono::steady_clock::now(); // check if you are sending messages too fast - if (!lastMessage.empty() && lastMessage.back() + minMessageOffset > now) { + if (!lastMessage.empty() && + lastMessage.back() + minMessageOffset > now) { if (this->lastErrorTimeSpeed_ + 30s < now) { - auto errorMessage = Message::createSystemMessage("sending messages too fast"); + auto errorMessage = + Message::createSystemMessage("sending messages too fast"); channel->addMessage(errorMessage); @@ -220,7 +234,8 @@ void TwitchServer::onMessageSendRequested(TwitchChannel *channel, const QString // check if you are sending too many messages if (lastMessage.size() >= maxMessageCount) { if (this->lastErrorTimeAmount_ + 30s < now) { - auto errorMessage = Message::createSystemMessage("sending too many messages"); + auto errorMessage = + Message::createSystemMessage("sending too many messages"); channel->addMessage(errorMessage); diff --git a/src/providers/twitch/TwitchServer.hpp b/src/providers/twitch/TwitchServer.hpp index 769f65fd4..edc06ef6a 100644 --- a/src/providers/twitch/TwitchServer.hpp +++ b/src/providers/twitch/TwitchServer.hpp @@ -40,19 +40,24 @@ public: protected: virtual void initializeConnection(IrcConnection *connection, bool isRead, bool isWrite) override; - virtual std::shared_ptr createChannel(const QString &channelName) override; + virtual std::shared_ptr createChannel( + const QString &channelName) override; - virtual void privateMessageReceived(Communi::IrcPrivateMessage *message) override; + virtual void privateMessageReceived( + Communi::IrcPrivateMessage *message) override; virtual void messageReceived(Communi::IrcMessage *message) override; - virtual void writeConnectionMessageReceived(Communi::IrcMessage *message) override; + virtual void writeConnectionMessageReceived( + Communi::IrcMessage *message) override; - virtual std::shared_ptr getCustomChannel(const QString &channelname) override; + virtual std::shared_ptr getCustomChannel( + const QString &channelname) override; virtual QString cleanChannelName(const QString &dirtyChannelName) override; virtual bool hasSeparateWriteConnection() const override; private: - void onMessageSendRequested(TwitchChannel *channel, const QString &message, bool &sent); + void onMessageSendRequested(TwitchChannel *channel, const QString &message, + bool &sent); std::mutex lastMessageMutex_; std::queue lastMessagePleb_; diff --git a/src/providers/twitch/TwitchUser.hpp b/src/providers/twitch/TwitchUser.hpp index d69c05572..74a9c1252 100644 --- a/src/providers/twitch/TwitchUser.hpp +++ b/src/providers/twitch/TwitchUser.hpp @@ -36,7 +36,8 @@ namespace Settings { template <> struct Deserialize { - static chatterino::TwitchUser get(const rapidjson::Value &value, bool *error = nullptr) + static chatterino::TwitchUser get(const rapidjson::Value &value, + bool *error = nullptr) { using namespace chatterino; @@ -44,7 +45,8 @@ struct Deserialize { if (!value.IsObject()) { PAJLADA_REPORT_ERROR(error) - PAJLADA_THROW_EXCEPTION("Deserialized rapidjson::Value is wrong type"); + PAJLADA_THROW_EXCEPTION( + "Deserialized rapidjson::Value is wrong type"); return user; } diff --git a/src/singletons/Fonts.cpp b/src/singletons/Fonts.cpp index faf927e47..69f327285 100644 --- a/src/singletons/Fonts.cpp +++ b/src/singletons/Fonts.cpp @@ -102,17 +102,20 @@ Fonts::FontData Fonts::createFontData(Type type, float scale) {ChatSmall, {0.6f, false, QFont::Normal}}, {ChatMediumSmall, {0.8f, false, QFont::Normal}}, {ChatMedium, {1, false, QFont::Normal}}, - {ChatMediumBold, {1, false, QFont::Weight(getApp()->settings->boldScale.getValue())}}, + {ChatMediumBold, + {1, false, + QFont::Weight(getApp()->settings->boldScale.getValue())}}, {ChatMediumItalic, {1, true, QFont::Normal}}, {ChatLarge, {1.2f, false, QFont::Normal}}, {ChatVeryLarge, {1.4f, false, QFont::Normal}}, }; - sizeScale[ChatMediumBold] = {1, false, - QFont::Weight(getApp()->settings->boldScale.getValue())}; + sizeScale[ChatMediumBold] = { + 1, false, QFont::Weight(getApp()->settings->boldScale.getValue())}; auto data = sizeScale[type]; - return FontData(QFont(QString::fromStdString(this->chatFontFamily.getValue()), - int(this->chatFontSize.getValue() * data.scale * scale), data.weight, - data.italic)); + return FontData( + QFont(QString::fromStdString(this->chatFontFamily.getValue()), + int(this->chatFontSize.getValue() * data.scale * scale), + data.weight, data.italic)); } // normal Ui font (use pt size) @@ -125,8 +128,10 @@ Fonts::FontData Fonts::createFontData(Type type, float scale) static std::unordered_map defaultSize{ {Tiny, {8, "Monospace", false, QFont::Normal}}, - {UiMedium, {int(9 * multiplier), DEFAULT_FONT_FAMILY, false, QFont::Normal}}, - {UiTabs, {int(9 * multiplier), DEFAULT_FONT_FAMILY, false, QFont::Normal}}, + {UiMedium, + {int(9 * multiplier), DEFAULT_FONT_FAMILY, false, QFont::Normal}}, + {UiTabs, + {int(9 * multiplier), DEFAULT_FONT_FAMILY, false, QFont::Normal}}, }; UiFontData &data = defaultSize[type]; diff --git a/src/singletons/Logging.cpp b/src/singletons/Logging.cpp index a8a6495c7..9d5823564 100644 --- a/src/singletons/Logging.cpp +++ b/src/singletons/Logging.cpp @@ -26,8 +26,8 @@ void Logging::addMessage(const QString &channelName, MessagePtr message) if (it == this->loggingChannels_.end()) { auto channel = new LoggingChannel(channelName); channel->addMessage(message); - this->loggingChannels_.emplace(channelName, - std::unique_ptr(std::move(channel))); + this->loggingChannels_.emplace( + channelName, std::unique_ptr(std::move(channel))); } else { it->second->addMessage(message); } diff --git a/src/singletons/NativeMessaging.cpp b/src/singletons/NativeMessaging.cpp index 3bf5b0ff3..4afea4714 100644 --- a/src/singletons/NativeMessaging.cpp +++ b/src/singletons/NativeMessaging.cpp @@ -32,7 +32,8 @@ namespace ipc = boost::interprocess; namespace chatterino { void registerNmManifest(Paths &paths, const QString &manifestFilename, - const QString ®istryKeyName, const QJsonDocument &document); + const QString ®istryKeyName, + const QJsonDocument &document); void registerNmHost(Paths &paths) { @@ -53,14 +54,15 @@ void registerNmHost(Paths &paths) QJsonDocument document; auto obj = getBaseDocument(); - QJsonArray allowed_origins_arr = {"chrome-extension://" EXTENSION_ID "/"}; + QJsonArray allowed_origins_arr = {"chrome-extension://" EXTENSION_ID + "/"}; obj.insert("allowed_origins", allowed_origins_arr); document.setObject(obj); - registerNmManifest( - paths, "/native-messaging-manifest-chrome.json", - "HKCU\\Software\\Google\\Chrome\\NativeMessagingHosts\\com.chatterino.chatterino", - document); + registerNmManifest(paths, "/native-messaging-manifest-chrome.json", + "HKCU\\Software\\Google\\Chrome\\NativeMessagingHost" + "s\\com.chatterino.chatterino", + document); } // firefox @@ -72,14 +74,16 @@ void registerNmHost(Paths &paths) obj.insert("allowed_extensions", allowed_extensions); document.setObject(obj); - registerNmManifest( - paths, "/native-messaging-manifest-firefox.json", - "HKCU\\Software\\Mozilla\\NativeMessagingHosts\\com.chatterino.chatterino", document); + registerNmManifest(paths, "/native-messaging-manifest-firefox.json", + "HKCU\\Software\\Mozilla\\NativeMessagingHosts\\com." + "chatterino.chatterino", + document); } } void registerNmManifest(Paths &paths, const QString &manifestFilename, - const QString ®istryKeyName, const QJsonDocument &document) + const QString ®istryKeyName, + const QJsonDocument &document) { (void)registryKeyName; @@ -99,7 +103,8 @@ void registerNmManifest(Paths &paths, const QString &manifestFilename, std::string &getNmQueueName(Paths &paths) { - static std::string name = "chatterino_gui" + paths.applicationFilePathHash.toStdString(); + static std::string name = + "chatterino_gui" + paths.applicationFilePathHash.toStdString(); return name; } @@ -137,18 +142,20 @@ void NativeMessagingServer::ReceiverThread::run() { ipc::message_queue::remove("chatterino_gui"); - ipc::message_queue messageQueue(ipc::open_or_create, "chatterino_gui", 100, MESSAGE_SIZE); + ipc::message_queue messageQueue(ipc::open_or_create, "chatterino_gui", 100, + MESSAGE_SIZE); while (true) { try { - std::unique_ptr buf(static_cast(malloc(MESSAGE_SIZE))); + std::unique_ptr buf( + static_cast(malloc(MESSAGE_SIZE))); ipc::message_queue::size_type retSize; unsigned int priority; messageQueue.receive(buf.get(), MESSAGE_SIZE, retSize, priority); - QJsonDocument document = - QJsonDocument::fromJson(QByteArray::fromRawData(buf.get(), retSize)); + QJsonDocument document = QJsonDocument::fromJson( + QByteArray::fromRawData(buf.get(), retSize)); this->handleMessage(document.object()); } catch (ipc::interprocess_exception &ex) { @@ -157,7 +164,8 @@ void NativeMessagingServer::ReceiverThread::run() } } -void NativeMessagingServer::ReceiverThread::handleMessage(const QJsonObject &root) +void NativeMessagingServer::ReceiverThread::handleMessage( + const QJsonObject &root) { auto app = getApp(); @@ -199,9 +207,11 @@ void NativeMessagingServer::ReceiverThread::handleMessage(const QJsonObject &roo if (attach) { #ifdef USEWINSDK // if (args.height != -1) { - auto *window = AttachedWindow::get(::GetForegroundWindow(), args); + auto *window = + AttachedWindow::get(::GetForegroundWindow(), args); if (!name.isEmpty()) { - window->setChannel(app->twitch.server->getOrAddChannel(name)); + window->setChannel( + app->twitch.server->getOrAddChannel(name)); } // } // window->show(); diff --git a/src/singletons/Paths.cpp b/src/singletons/Paths.cpp index 40ce32524..21d1f57ad 100644 --- a/src/singletons/Paths.cpp +++ b/src/singletons/Paths.cpp @@ -36,8 +36,9 @@ bool Paths::isPortable() void Paths::initAppFilePathHash() { this->applicationFilePathHash = - QCryptographicHash::hash(QCoreApplication::applicationFilePath().toUtf8(), - QCryptographicHash::Sha224) + QCryptographicHash::hash( + QCoreApplication::applicationFilePath().toUtf8(), + QCryptographicHash::Sha224) .toBase64() .mid(0, 32) .replace("+", "-") @@ -46,15 +47,16 @@ void Paths::initAppFilePathHash() void Paths::initCheckPortable() { - this->portable_ = - QFileInfo::exists(combinePath(QCoreApplication::applicationDirPath(), "portable")); + this->portable_ = QFileInfo::exists( + combinePath(QCoreApplication::applicationDirPath(), "portable")); } void Paths::initAppDataDirectory() { assert(this->portable_.is_initialized()); - // Root path = %APPDATA%/Chatterino or the folder that the executable resides in + // Root path = %APPDATA%/Chatterino or the folder that the executable + // resides in this->rootAppDataDirectory = [&]() -> QString { // portable @@ -63,13 +65,15 @@ void Paths::initAppDataDirectory() } // permanent installation - QString path = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation); + QString path = + QStandardPaths::writableLocation(QStandardPaths::AppDataLocation); if (path.isEmpty()) { - throw std::runtime_error("Error finding writable location for settings"); + throw std::runtime_error( + "Error finding writable location for settings"); } -// create directory Chatterino2 instead of chatterino on windows because the ladder one is takes by -// chatterino 1 already +// create directory Chatterino2 instead of chatterino on windows because the +// ladder one is takes by chatterino 1 already #ifdef Q_OS_WIN path.replace("chatterino", "Chatterino"); @@ -84,12 +88,15 @@ void Paths::initSubDirectories() // required the app data directory to be set first assert(!this->rootAppDataDirectory.isEmpty()); - // create settings subdirectories and validate that they are created properly + // create settings subdirectories and validate that they are created + // properly auto makePath = [&](const std::string &name) -> QString { - auto path = combinePath(this->rootAppDataDirectory, QString::fromStdString(name)); + auto path = combinePath(this->rootAppDataDirectory, + QString::fromStdString(name)); if (!QDir().mkpath(path)) { - throw std::runtime_error("Error creating appdata path %appdata%/chatterino/" + name); + throw std::runtime_error( + "Error creating appdata path %appdata%/chatterino/" + name); } return path; diff --git a/src/singletons/Paths.hpp b/src/singletons/Paths.hpp index b1b59da61..071feb737 100644 --- a/src/singletons/Paths.hpp +++ b/src/singletons/Paths.hpp @@ -12,8 +12,8 @@ public: Paths(); - // Root directory for the configuration files. %APPDATA%/chatterino or ExecutablePath for - // portable mode + // Root directory for the configuration files. %APPDATA%/chatterino or + // ExecutablePath for portable mode QString rootAppDataDirectory; // Directory for settings files. Same as /Settings diff --git a/src/singletons/Settings.cpp b/src/singletons/Settings.cpp index 10be19100..b380a0119 100644 --- a/src/singletons/Settings.cpp +++ b/src/singletons/Settings.cpp @@ -12,7 +12,8 @@ std::vector> _settings; Settings *Settings::instance = nullptr; -void _actuallyRegisterSetting(std::weak_ptr setting) +void _actuallyRegisterSetting( + std::weak_ptr setting) { _settings.push_back(setting); } diff --git a/src/singletons/Settings.hpp b/src/singletons/Settings.hpp index 507868677..3db58f9c2 100644 --- a/src/singletons/Settings.hpp +++ b/src/singletons/Settings.hpp @@ -12,7 +12,8 @@ namespace chatterino { -void _actuallyRegisterSetting(std::weak_ptr setting); +void _actuallyRegisterSetting( + std::weak_ptr setting); class Settings { @@ -25,48 +26,62 @@ public: /// Appearance BoolSetting showTimestamps = {"/appearance/messages/showTimestamps", true}; - QStringSetting timestampFormat = {"/appearance/messages/timestampFormat", "h:mm"}; + QStringSetting timestampFormat = {"/appearance/messages/timestampFormat", + "h:mm"}; BoolSetting showBadges = {"/appearance/messages/showBadges", true}; - BoolSetting showLastMessageIndicator = {"/appearance/messages/showLastMessageIndicator", false}; - IntSetting lastMessagePattern = {"/appearance/messages/lastMessagePattern", Qt::VerPattern}; + BoolSetting showLastMessageIndicator = { + "/appearance/messages/showLastMessageIndicator", false}; + IntSetting lastMessagePattern = {"/appearance/messages/lastMessagePattern", + Qt::VerPattern}; BoolSetting showEmptyInput = {"/appearance/showEmptyInputBox", true}; - BoolSetting showMessageLength = {"/appearance/messages/showMessageLength", false}; - BoolSetting separateMessages = {"/appearance/messages/separateMessages", false}; - // BoolSetting collapseLongMessages = {"/appearance/messages/collapseLongMessages", false}; - IntSetting collpseMessagesMinLines = {"/appearance/messages/collapseMessagesMinLines", 0}; - BoolSetting alternateMessageBackground = {"/appearance/messages/alternateMessageBackground", - false}; + BoolSetting showMessageLength = {"/appearance/messages/showMessageLength", + false}; + BoolSetting separateMessages = {"/appearance/messages/separateMessages", + false}; + // BoolSetting collapseLongMessages = + // {"/appearance/messages/collapseLongMessages", false}; + IntSetting collpseMessagesMinLines = { + "/appearance/messages/collapseMessagesMinLines", 0}; + BoolSetting alternateMessageBackground = { + "/appearance/messages/alternateMessageBackground", false}; IntSetting uiScale = {"/appearance/uiScale", 0}; IntSetting boldScale = {"/appearance/boldScale", 57}; BoolSetting windowTopMost = {"/appearance/windowAlwaysOnTop", false}; BoolSetting showTabCloseButton = {"/appearance/showTabCloseButton", true}; - BoolSetting hidePreferencesButton = {"/appearance/hidePreferencesButton", false}; + BoolSetting hidePreferencesButton = {"/appearance/hidePreferencesButton", + false}; BoolSetting hideUserButton = {"/appearance/hideUserButton", false}; BoolSetting enableSmoothScrolling = {"/appearance/smoothScrolling", true}; - BoolSetting enableSmoothScrollingNewMessages = {"/appearance/smoothScrollingNewMessages", - false}; - BoolSetting enableUsernameBold = {"/appearence/messages/boldUsernames", false}; + BoolSetting enableSmoothScrollingNewMessages = { + "/appearance/smoothScrollingNewMessages", false}; + BoolSetting enableUsernameBold = {"/appearence/messages/boldUsernames", + false}; // BoolSetting customizable splitheader - BoolSetting showViewerCount = {"/appearance/splitheader/showViewerCount", false}; + BoolSetting showViewerCount = {"/appearance/splitheader/showViewerCount", + false}; BoolSetting showTitle = {"/appearance/splitheader/showTitle", false}; BoolSetting showGame = {"/appearance/splitheader/showGame", false}; BoolSetting showUptime = {"/appearance/splitheader/showUptime", false}; - // BoolSetting useCustomWindowFrame = {"/appearance/useCustomWindowFrame", false}; + // BoolSetting useCustomWindowFrame = {"/appearance/useCustomWindowFrame", + // false}; /// Behaviour - BoolSetting allowDuplicateMessages = {"/behaviour/allowDuplicateMessages", true}; + BoolSetting allowDuplicateMessages = {"/behaviour/allowDuplicateMessages", + true}; BoolSetting mentionUsersWithAt = {"/behaviour/mentionUsersWithAt", false}; BoolSetting showJoins = {"/behaviour/showJoins", false}; BoolSetting showParts = {"/behaviour/showParts", false}; - FloatSetting mouseScrollMultiplier = {"/behaviour/mouseScrollMultiplier", 1.0}; - // BoolSetting twitchSeperateWriteConnection = {"/behaviour/twitchSeperateWriteConnection", - // false}; + FloatSetting mouseScrollMultiplier = {"/behaviour/mouseScrollMultiplier", + 1.0}; + // BoolSetting twitchSeperateWriteConnection = + // {"/behaviour/twitchSeperateWriteConnection", false}; // Auto-completion BoolSetting onlyFetchChattersForSmallerStreamers = { "/behaviour/autocompletion/onlyFetchChattersForSmallerStreamers", true}; - IntSetting smallStreamerLimit = {"/behaviour/autocompletion/smallStreamerLimit", 1000}; + IntSetting smallStreamerLimit = { + "/behaviour/autocompletion/smallStreamerLimit", 1000}; BoolSetting pauseChatHover = {"/behaviour/pauseChatHover", false}; @@ -74,7 +89,8 @@ public: BoolSetting allowCommandsAtEnd = {"/commands/allowCommandsAtEnd", false}; /// Emotes - BoolSetting scaleEmotesByLineHeight = {"/emotes/scaleEmotesByLineHeight", false}; + BoolSetting scaleEmotesByLineHeight = {"/emotes/scaleEmotesByLineHeight", + false}; BoolSetting enableTwitchEmotes = {"/emotes/enableTwitchEmotes", true}; BoolSetting enableBttvEmotes = {"/emotes/enableBTTVEmotes", true}; BoolSetting enableFfzEmotes = {"/emotes/enableFFZEmotes", true}; @@ -95,16 +111,19 @@ public: BoolSetting enableLowercaseLink = {"/links/linkLowercase", true}; /// Ingored Users - BoolSetting enableTwitchIgnoredUsers = {"/ignore/enableTwitchIgnoredUsers", true}; + BoolSetting enableTwitchIgnoredUsers = {"/ignore/enableTwitchIgnoredUsers", + true}; /// Moderation QStringSetting timeoutAction = {"/moderation/timeoutAction", "Disable"}; /// Highlighting // BoolSetting enableHighlights = {"/highlighting/enabled", true}; - BoolSetting enableHighlightsSelf = {"/highlighting/nameIsHighlightKeyword", true}; + BoolSetting enableHighlightsSelf = {"/highlighting/nameIsHighlightKeyword", + true}; BoolSetting enableHighlightSound = {"/highlighting/enableSound", true}; - BoolSetting enableHighlightTaskbar = {"/highlighting/enableTaskbarFlashing", true}; + BoolSetting enableHighlightTaskbar = {"/highlighting/enableTaskbarFlashing", + true}; BoolSetting customHighlightSound = {"/highlighting/useCustomSound", false}; /// Logging @@ -115,15 +134,18 @@ public: QStringSetting pathHighlightSound = {"/highlighting/highlightSoundPath", "qrc:/sounds/ping2.wav"}; - BoolSetting highlightAlwaysPlaySound = {"/highlighting/alwaysPlaySound", false}; + BoolSetting highlightAlwaysPlaySound = {"/highlighting/alwaysPlaySound", + false}; BoolSetting inlineWhispers = {"/whispers/enableInlineWhispers", true}; /// External tools // Streamlink - BoolSetting streamlinkUseCustomPath = {"/external/streamlink/useCustomPath", false}; + BoolSetting streamlinkUseCustomPath = {"/external/streamlink/useCustomPath", + false}; QStringSetting streamlinkPath = {"/external/streamlink/customPath", ""}; - QStringSetting preferredQuality = {"/external/streamlink/quality", "Choose"}; + QStringSetting preferredQuality = {"/external/streamlink/quality", + "Choose"}; QStringSetting streamlinkOpts = {"/external/streamlink/options", ""}; /// Misc diff --git a/src/singletons/Theme.cpp b/src/singletons/Theme.cpp index b20a79875..5140db09a 100644 --- a/src/singletons/Theme.cpp +++ b/src/singletons/Theme.cpp @@ -39,7 +39,8 @@ Theme::Theme() void Theme::update() { - this->actuallyUpdate(this->themeHue, detail::getMultiplierByTheme(this->themeName.getValue())); + this->actuallyUpdate(this->themeHue, detail::getMultiplierByTheme( + this->themeName.getValue())); } // hue: theme color (0 - 1) @@ -88,32 +89,40 @@ void Theme::actuallyUpdate(double hue, double multiplier) /// TABS if (lightWin) { - this->tabs.regular = {QColor("#444"), - {QColor("#fff"), QColor("#eee"), QColor("#fff")}, - {QColor("#fff"), QColor("#fff"), QColor("#fff")}}; - this->tabs.newMessage = {QColor("#222"), - {QColor("#fff"), QColor("#eee"), QColor("#fff")}, - {QColor("#bbb"), QColor("#bbb"), QColor("#bbb")}}; - this->tabs.highlighted = {fg, - {QColor("#fff"), QColor("#eee"), QColor("#fff")}, - {highlighted, highlighted, highlighted}}; - this->tabs.selected = {QColor("#000"), - {QColor("#b4d7ff"), QColor("#b4d7ff"), QColor("#b4d7ff")}, - {QColor("#00aeef"), QColor("#00aeef"), QColor("#00aeef")}}; + this->tabs.regular = { + QColor("#444"), + {QColor("#fff"), QColor("#eee"), QColor("#fff")}, + {QColor("#fff"), QColor("#fff"), QColor("#fff")}}; + this->tabs.newMessage = { + QColor("#222"), + {QColor("#fff"), QColor("#eee"), QColor("#fff")}, + {QColor("#bbb"), QColor("#bbb"), QColor("#bbb")}}; + this->tabs.highlighted = { + fg, + {QColor("#fff"), QColor("#eee"), QColor("#fff")}, + {highlighted, highlighted, highlighted}}; + this->tabs.selected = { + QColor("#000"), + {QColor("#b4d7ff"), QColor("#b4d7ff"), QColor("#b4d7ff")}, + {QColor("#00aeef"), QColor("#00aeef"), QColor("#00aeef")}}; } else { - this->tabs.regular = {QColor("#aaa"), - {QColor("#252525"), QColor("#252525"), QColor("#252525")}, - {QColor("#444"), QColor("#444"), QColor("#444")}}; - this->tabs.newMessage = {fg, - {QColor("#252525"), QColor("#252525"), QColor("#252525")}, - {QColor("#888"), QColor("#888"), QColor("#888")}}; - this->tabs.highlighted = {fg, - {QColor("#252525"), QColor("#252525"), QColor("#252525")}, - {highlighted, highlighted, highlighted}}; + this->tabs.regular = { + QColor("#aaa"), + {QColor("#252525"), QColor("#252525"), QColor("#252525")}, + {QColor("#444"), QColor("#444"), QColor("#444")}}; + this->tabs.newMessage = { + fg, + {QColor("#252525"), QColor("#252525"), QColor("#252525")}, + {QColor("#888"), QColor("#888"), QColor("#888")}}; + this->tabs.highlighted = { + fg, + {QColor("#252525"), QColor("#252525"), QColor("#252525")}, + {highlighted, highlighted, highlighted}}; - this->tabs.selected = {QColor("#fff"), - {QColor("#555555"), QColor("#555555"), QColor("#555555")}, - {QColor("#00aeef"), QColor("#00aeef"), QColor("#00aeef")}}; + this->tabs.selected = { + QColor("#fff"), + {QColor("#555555"), QColor("#555555"), QColor("#555555")}, + {QColor("#00aeef"), QColor("#00aeef"), QColor("#00aeef")}}; } this->splits.input.focusedLine = highlighted; @@ -126,16 +135,19 @@ void Theme::actuallyUpdate(double hue, double multiplier) // fg, // {QBrush(blendColors(themeColor, "#ccc", 0.9), Qt::FDiagPattern), // QBrush(blendColors(themeColor, "#ccc", 0.9), Qt::FDiagPattern), - // QBrush(blendColors(themeColorNoSat, "#ccc", 0.9), Qt::FDiagPattern)}}; + // QBrush(blendColors(themeColorNoSat, "#ccc", 0.9), + // Qt::FDiagPattern)}}; // this->tabs.newMessage = { // fg, - // {QBrush(blendColors(themeColor, "#666", 0.7), Qt::FDiagPattern), - // QBrush(blendColors(themeColor, "#666", 0.5), Qt::FDiagPattern), + // {QBrush(blendColors(themeColor, "#666", 0.7), + // Qt::FDiagPattern), + // QBrush(blendColors(themeColor, "#666", 0.5), + // Qt::FDiagPattern), // QBrush(blendColors(themeColorNoSat, "#666", 0.7), // Qt::FDiagPattern)}}; - // this->tabs.highlighted = {fg, {QColor("#777"), QColor("#777"), - // QColor("#666")}}; + // this->tabs.highlighted = {fg, {QColor("#777"), + // QColor("#777"), QColor("#666")}}; this->tabs.bottomLine = this->tabs.selected.backgrounds.regular.color(); } @@ -143,7 +155,8 @@ void Theme::actuallyUpdate(double hue, double multiplier) // Split bool flat = isLight_; - this->splits.messageSeperator = isLight_ ? QColor(127, 127, 127) : QColor(60, 60, 60); + this->splits.messageSeperator = + isLight_ ? QColor(127, 127, 127) : QColor(60, 60, 60); this->splits.background = getColor(0, sat, 1); this->splits.dropPreview = QColor(0, 148, 255, 0x30); this->splits.dropPreviewBorder = QColor(0, 148, 255, 0xff); @@ -165,20 +178,23 @@ void Theme::actuallyUpdate(double hue, double multiplier) this->splits.header.background = getColor(0, sat, flat ? 1 : 0.9); this->splits.header.border = getColor(0, sat, flat ? 1 : 0.85); this->splits.header.text = this->messages.textColors.regular; - this->splits.header.focusedText = isLight_ ? QColor("#198CFF") : QColor("#84C1FF"); + this->splits.header.focusedText = + isLight_ ? QColor("#198CFF") : QColor("#84C1FF"); this->splits.input.background = getColor(0, sat, flat ? 0.95 : 0.95); this->splits.input.border = getColor(0, sat, flat ? 1 : 1); this->splits.input.text = this->messages.textColors.regular; this->splits.input.styleSheet = "background:" + this->splits.input.background.name() + ";" + - "border:" + this->tabs.selected.backgrounds.regular.color().name() + ";" + - "color:" + this->messages.textColors.regular.name() + ";" + // + "border:" + this->tabs.selected.backgrounds.regular.color().name() + + ";" + "color:" + this->messages.textColors.regular.name() + ";" + // "selection-background-color:" + - (isLight_ ? "#68B1FF" : this->tabs.selected.backgrounds.regular.color().name()); + (isLight_ ? "#68B1FF" + : this->tabs.selected.backgrounds.regular.color().name()); // Message - this->messages.textColors.link = isLight_ ? QColor(66, 134, 244) : QColor(66, 134, 244); + this->messages.textColors.link = + isLight_ ? QColor(66, 134, 244) : QColor(66, 134, 244); this->messages.textColors.system = QColor(140, 127, 127); this->messages.backgrounds.regular = splits.background; @@ -212,12 +228,14 @@ void Theme::actuallyUpdate(double hue, double multiplier) this->tooltip.text = QColor(255, 255, 255); // Selection - this->messages.selection = isLightTheme() ? QColor(0, 0, 0, 64) : QColor(255, 255, 255, 64); + this->messages.selection = + isLightTheme() ? QColor(0, 0, 0, 64) : QColor(255, 255, 255, 64); this->updated.invoke(); } -QColor Theme::blendColors(const QColor &color1, const QColor &color2, qreal ratio) +QColor Theme::blendColors(const QColor &color1, const QColor &color2, + qreal ratio) { int r = int(color1.red() * (1 - ratio) + color2.red() * ratio); int g = int(color1.green() * (1 - ratio) + color2.green() * ratio); @@ -233,21 +251,24 @@ void Theme::normalizeColor(QColor &color) color.setHslF(color.hueF(), color.saturationF(), 0.5); } - if (color.lightnessF() > 0.4 && color.hueF() > 0.1 && color.hueF() < 0.33333) { - color.setHslF( - color.hueF(), color.saturationF(), - color.lightnessF() - sin((color.hueF() - 0.1) / (0.3333 - 0.1) * 3.14159) * - color.saturationF() * 0.4); + if (color.lightnessF() > 0.4 && color.hueF() > 0.1 && + color.hueF() < 0.33333) { + color.setHslF(color.hueF(), color.saturationF(), + color.lightnessF() - sin((color.hueF() - 0.1) / + (0.3333 - 0.1) * 3.14159) * + color.saturationF() * 0.4); } } else { if (color.lightnessF() < 0.5) { color.setHslF(color.hueF(), color.saturationF(), 0.5); } - if (color.lightnessF() < 0.6 && color.hueF() > 0.54444 && color.hueF() < 0.83333) { + if (color.lightnessF() < 0.6 && color.hueF() > 0.54444 && + color.hueF() < 0.83333) { color.setHslF( color.hueF(), color.saturationF(), - color.lightnessF() + sin((color.hueF() - 0.54444) / (0.8333 - 0.54444) * 3.14159) * + color.lightnessF() + sin((color.hueF() - 0.54444) / + (0.8333 - 0.54444) * 3.14159) * color.saturationF() * 0.4); } } diff --git a/src/singletons/Theme.hpp b/src/singletons/Theme.hpp index 25a978271..47bb6c3ef 100644 --- a/src/singletons/Theme.hpp +++ b/src/singletons/Theme.hpp @@ -135,8 +135,8 @@ public: private: void actuallyUpdate(double hue, double multiplier); QColor blendColors(const QColor &color1, const QColor &color2, qreal ratio); - void fillLookupTableValues(double (&array)[360], double from, double to, double fromValue, - double toValue); + void fillLookupTableValues(double (&array)[360], double from, double to, + double fromValue, double toValue); double middleLookupTable_[360] = {}; double minLookupTable_[360] = {}; diff --git a/src/singletons/Updates.cpp b/src/singletons/Updates.cpp index a251b39e1..d56a8f510 100644 --- a/src/singletons/Updates.cpp +++ b/src/singletons/Updates.cpp @@ -44,10 +44,11 @@ void Updates::installUpdates() } #ifdef Q_OS_WIN - QMessageBox *box = new QMessageBox(QMessageBox::Information, "Chatterino Update", - "Chatterino is downloading the update " - "in the background and will run the " - "updater once it is finished."); + QMessageBox *box = + new QMessageBox(QMessageBox::Information, "Chatterino Update", + "Chatterino is downloading the update " + "in the background and will run the " + "updater once it is finished."); box->setAttribute(Qt::WA_DeleteOnClose); box->show(); @@ -57,8 +58,9 @@ void Updates::installUpdates() this->setStatus_(DownloadFailed); postToThread([] { - QMessageBox *box = new QMessageBox(QMessageBox::Information, "Chatterino Update", - "Failed while trying to download the update."); + QMessageBox *box = + new QMessageBox(QMessageBox::Information, "Chatterino Update", + "Failed while trying to download the update."); box->setAttribute(Qt::WA_DeleteOnClose); box->show(); box->raise(); @@ -80,7 +82,8 @@ void Updates::installUpdates() } QProcess::startDetached( - combinePath(QCoreApplication::applicationDirPath(), "updater.1/ChatterinoUpdater.exe"), + combinePath(QCoreApplication::applicationDirPath(), + "updater.1/ChatterinoUpdater.exe"), {filename, "restart"}); QApplication::exit(0); @@ -94,7 +97,9 @@ void Updates::installUpdates() void Updates::checkForUpdates() { #ifdef Q_OS_WIN - QString url = "https://notitia.chatterino.com/version/chatterino/" CHATTERINO_OS "/stable"; + QString url = + "https://notitia.chatterino.com/version/chatterino/" CHATTERINO_OS + "/stable"; NetworkRequest req(url); req.setTimeout(30000); @@ -110,7 +115,8 @@ void Updates::checkForUpdates() postToThread([] { QMessageBox *box = new QMessageBox( QMessageBox::Information, "Chatterino Update", - "Error while searching for updates.\n\nEither the service is down " + "Error while searching for updates.\n\nEither the service " + "is down " "temporarily or everything is broken."); box->setAttribute(Qt::WA_DeleteOnClose); box->show(); @@ -125,11 +131,11 @@ void Updates::checkForUpdates() if (this->currentVersion_ != this->onlineVersion_) { this->setStatus_(UpdateAvailable); postToThread([this] { - QMessageBox *box = - new QMessageBox(QMessageBox::Information, "Chatterino Update", - "An update for chatterino is available.\n\nDo you " - "want to download and install it?", - QMessageBox::Yes | QMessageBox::No); + QMessageBox *box = new QMessageBox( + QMessageBox::Information, "Chatterino Update", + "An update for chatterino is available.\n\nDo you " + "want to download and install it?", + QMessageBox::Yes | QMessageBox::No); box->setAttribute(Qt::WA_DeleteOnClose); box->show(); box->raise(); diff --git a/src/singletons/WindowManager.cpp b/src/singletons/WindowManager.cpp index 6825ada39..9f853b20c 100644 --- a/src/singletons/WindowManager.cpp +++ b/src/singletons/WindowManager.cpp @@ -93,14 +93,17 @@ void WindowManager::updateWordTypeMask() } // emotes - flags |= settings->enableTwitchEmotes ? MEF::TwitchEmoteImage : MEF::TwitchEmoteText; + flags |= settings->enableTwitchEmotes ? MEF::TwitchEmoteImage + : MEF::TwitchEmoteText; flags |= settings->enableFfzEmotes ? MEF::FfzEmoteImage : MEF::FfzEmoteText; - flags |= settings->enableBttvEmotes ? MEF::BttvEmoteImage : MEF::BttvEmoteText; + flags |= + settings->enableBttvEmotes ? MEF::BttvEmoteImage : MEF::BttvEmoteText; flags |= settings->enableEmojis ? MEF::EmojiImage : MEF::EmojiText; // bits flags |= MEF::BitsAmount; - flags |= settings->enableGifAnimations ? MEF::BitsAnimated : MEF::BitsStatic; + flags |= + settings->enableGifAnimations ? MEF::BitsAnimated : MEF::BitsStatic; // badges flags |= settings->showBadges ? MEF::Badges : MEF::None; @@ -111,8 +114,10 @@ void WindowManager::updateWordTypeMask() // misc flags |= MEF::AlwaysShow; flags |= MEF::Collapsed; - flags |= settings->enableUsernameBold ? MEF::BoldUsername : MEF::NonBoldUsername; - flags |= settings->enableLowercaseLink ? MEF::LowercaseLink : MEF::OriginalLink; + flags |= + settings->enableUsernameBold ? MEF::BoldUsername : MEF::NonBoldUsername; + flags |= + settings->enableLowercaseLink ? MEF::LowercaseLink : MEF::OriginalLink; // update flags MessageElement::Flags newFlags = static_cast(flags); @@ -180,7 +185,8 @@ Window &WindowManager::createWindow(Window::Type type) window->setAttribute(Qt::WA_DeleteOnClose); QObject::connect(window, &QWidget::destroyed, [this, window] { - for (auto it = this->windows_.begin(); it != this->windows_.end(); it++) { + for (auto it = this->windows_.begin(); it != this->windows_.end(); + it++) { if (*it == window) { this->windows_.erase(it); break; @@ -232,7 +238,8 @@ void WindowManager::initialize(Settings &settings, Paths &paths) // get type QString type_val = window_obj.value("type").toString(); - Window::Type type = type_val == "main" ? Window::Type::Main : Window::Type::Popup; + Window::Type type = + type_val == "main" ? Window::Type::Main : Window::Type::Popup; if (type == Window::Type::Main && mainWindow_ != nullptr) { type = Window::Type::Popup; @@ -304,14 +311,18 @@ void WindowManager::initialize(Settings &settings, Paths &paths) mainWindow_->getNotebook().addPage(true); } - settings.timestampFormat.connect([this](auto, auto) { this->layoutChannelViews(); }); + settings.timestampFormat.connect( + [this](auto, auto) { this->layoutChannelViews(); }); - settings.emoteScale.connect([this](auto, auto) { this->forceLayoutChannelViews(); }); + settings.emoteScale.connect( + [this](auto, auto) { this->forceLayoutChannelViews(); }); - settings.timestampFormat.connect([this](auto, auto) { this->forceLayoutChannelViews(); }); + settings.timestampFormat.connect( + [this](auto, auto) { this->forceLayoutChannelViews(); }); settings.alternateMessageBackground.connect( [this](auto, auto) { this->forceLayoutChannelViews(); }); - settings.separateMessages.connect([this](auto, auto) { this->forceLayoutChannelViews(); }); + settings.separateMessages.connect( + [this](auto, auto) { this->forceLayoutChannelViews(); }); settings.collpseMessagesMinLines.connect( [this](auto, auto) { this->forceLayoutChannelViews(); }); @@ -352,10 +363,11 @@ void WindowManager::save() // window tabs QJsonArray tabs_arr; - for (int tab_i = 0; tab_i < window->getNotebook().getPageCount(); tab_i++) { + for (int tab_i = 0; tab_i < window->getNotebook().getPageCount(); + tab_i++) { QJsonObject tab_obj; - SplitContainer *tab = - dynamic_cast(window->getNotebook().getPageAt(tab_i)); + SplitContainer *tab = dynamic_cast( + window->getNotebook().getPageAt(tab_i)); assert(tab != nullptr); // custom tab title @@ -415,8 +427,9 @@ void WindowManager::encodeNodeRecusively(SplitNode *node, QJsonObject &obj) } break; case SplitNode::HorizontalContainer: case SplitNode::VerticalContainer: { - obj.insert("type", node->getType() == SplitNode::HorizontalContainer ? "horizontal" - : "vertical"); + obj.insert("type", node->getType() == SplitNode::HorizontalContainer + ? "horizontal" + : "vertical"); QJsonArray items_arr; for (const std::unique_ptr &n : node->getChildren()) { @@ -458,7 +471,8 @@ IndirectChannel WindowManager::decodeChannel(const QJsonObject &obj) QString type = obj.value("type").toString(); if (type == "twitch") { - return app->twitch.server->getOrAddChannel(obj.value("name").toString()); + return app->twitch.server->getOrAddChannel( + obj.value("name").toString()); } else if (type == "mentions") { return app->twitch.server->mentionsChannel; } else if (type == "watching") { diff --git a/src/singletons/helper/LoggingChannel.cpp b/src/singletons/helper/LoggingChannel.cpp index cce98a505..0c33fcf0b 100644 --- a/src/singletons/helper/LoggingChannel.cpp +++ b/src/singletons/helper/LoggingChannel.cpp @@ -21,7 +21,8 @@ LoggingChannel::LoggingChannel(const QString &_channelName) } else if (channelName.startsWith("/mentions")) { this->subDirectory = "Mentions"; } else { - this->subDirectory = QStringLiteral("Channels") + QDir::separator() + channelName; + this->subDirectory = + QStringLiteral("Channels") + QDir::separator() + channelName; } // FOURTF: change this when adding more providers @@ -60,7 +61,8 @@ void LoggingChannel::openLogFile() QString baseFileName = this->channelName + "-" + this->dateString + ".log"; - QString directory = this->baseDirectory + QDir::separator() + this->subDirectory; + QString directory = + this->baseDirectory + QDir::separator() + this->subDirectory; if (!QDir().mkpath(directory)) { Log("Unable to create logging path"); diff --git a/src/singletons/helper/LoggingChannel.hpp b/src/singletons/helper/LoggingChannel.hpp index 985ab27b9..3f327538f 100644 --- a/src/singletons/helper/LoggingChannel.hpp +++ b/src/singletons/helper/LoggingChannel.hpp @@ -24,8 +24,10 @@ public: private: void openLogFile(); - QString generateOpeningString(const QDateTime &now = QDateTime::currentDateTime()) const; - QString generateClosingString(const QDateTime &now = QDateTime::currentDateTime()) const; + QString generateOpeningString( + const QDateTime &now = QDateTime::currentDateTime()) const; + QString generateClosingString( + const QDateTime &now = QDateTime::currentDateTime()) const; void appendLine(const QString &line); diff --git a/src/util/ConcurrentMap.hpp b/src/util/ConcurrentMap.hpp index 84cf98365..281f3ce46 100644 --- a/src/util/ConcurrentMap.hpp +++ b/src/util/ConcurrentMap.hpp @@ -65,7 +65,8 @@ public: this->data_.insert(name, value); } - void each(std::function func) const + void each( + std::function func) const { QMutexLocker lock(&this->mutex_); diff --git a/src/util/InitUpdateButton.cpp b/src/util/InitUpdateButton.cpp index acb584175..d10ba2bc1 100644 --- a/src/util/InitUpdateButton.cpp +++ b/src/util/InitUpdateButton.cpp @@ -5,7 +5,8 @@ namespace chatterino { -void initUpdateButton(RippleEffectButton &button, std::unique_ptr &handle, +void initUpdateButton(RippleEffectButton &button, + std::unique_ptr &handle, pajlada::Signals::SignalHolder &signalHolder) { button.hide(); @@ -16,7 +17,8 @@ void initUpdateButton(RippleEffectButton &button, std::unique_ptr auto dialog = new UpdateDialog(); dialog->setActionOnFocusLoss(BaseWindow::Delete); - dialog->move(button.mapToGlobal(QPoint(int(-100 * button.getScale()), button.height()))); + dialog->move(button.mapToGlobal( + QPoint(int(-100 * button.getScale()), button.height()))); dialog->show(); dialog->raise(); @@ -39,15 +41,17 @@ void initUpdateButton(RippleEffectButton &button, std::unique_ptr auto updateChange = [&button](auto) { button.setVisible(Updates::getInstance().shouldShowUpdateButton()); - auto imageUrl = Updates::getInstance().isError() ? ":/images/download_update_error.png" - : ":/images/download_update.png"; + auto imageUrl = Updates::getInstance().isError() + ? ":/images/download_update_error.png" + : ":/images/download_update.png"; button.setPixmap(QPixmap(imageUrl)); }; updateChange(Updates::getInstance().getStatus()); - signalHolder.managedConnect(Updates::getInstance().statusUpdated, - [updateChange](auto status) { updateChange(status); }); + signalHolder.managedConnect( + Updates::getInstance().statusUpdated, + [updateChange](auto status) { updateChange(status); }); } } // namespace chatterino diff --git a/src/util/InitUpdateButton.hpp b/src/util/InitUpdateButton.hpp index 6a0966e54..17237d593 100644 --- a/src/util/InitUpdateButton.hpp +++ b/src/util/InitUpdateButton.hpp @@ -13,7 +13,8 @@ namespace chatterino { class RippleEffectButton; class UpdateDialog; -void initUpdateButton(RippleEffectButton &button, std::unique_ptr &handle, +void initUpdateButton(RippleEffectButton &button, + std::unique_ptr &handle, pajlada::Signals::SignalHolder &signalHolder); } // namespace chatterino diff --git a/src/util/LayoutCreator.hpp b/src/util/LayoutCreator.hpp index af01db373..c519b7659 100644 --- a/src/util/LayoutCreator.hpp +++ b/src/util/LayoutCreator.hpp @@ -53,7 +53,8 @@ public: } template ::value, int>::type = 0> + typename std::enable_if::value, + int>::type = 0> LayoutCreator emplaceScrollAreaWidget() { QWidget *widget = new QWidget; @@ -62,8 +63,10 @@ public: } template ::value, int>::type = 0, - typename std::enable_if::value, int>::type = 0> + typename std::enable_if::value, + int>::type = 0, + typename std::enable_if::value, + int>::type = 0> LayoutCreator setLayoutType() { T2 *layout = new T2; @@ -81,7 +84,8 @@ public: } template ::value, int>::type = 0> + typename std::enable_if::value, + int>::type = 0> LayoutCreator withoutMargin() { this->item_->setContentsMargins(0, 0, 0, 0); @@ -90,7 +94,8 @@ public: } template ::value, int>::type = 0> + typename std::enable_if::value, + int>::type = 0> LayoutCreator hidden() { this->item_->setVisible(false); @@ -99,10 +104,12 @@ public: } template ::value, int>::type = 0> + typename std::enable_if::value, + int>::type = 0> LayoutCreator appendTab(T2 *item, const QString &title) { - static_assert(std::is_base_of::value, "needs to be QLayout"); + static_assert(std::is_base_of::value, + "needs to be QLayout"); QWidget *widget = new QWidget; widget->setLayout(item); @@ -116,14 +123,16 @@ private: T *item_; template ::value, int>::type = 0> + typename std::enable_if::value, + int>::type = 0> void addItem(QLayout *layout, T2 *item) { layout->addWidget(item); } template ::value, int>::type = 0> + typename std::enable_if::value, + int>::type = 0> void addItem(QLayout *layout, T2 *item) { QWidget *widget = new QWidget(); @@ -132,14 +141,16 @@ private: } template ::value, int>::type = 0> + typename std::enable_if::value, + int>::type = 0> QLayout *getOrCreateLayout() { return this->item_; } template ::value, int>::type = 0> + typename std::enable_if::value, + int>::type = 0> QLayout *getOrCreateLayout() { if (!this->item_->layout()) { diff --git a/src/util/PostToThread.hpp b/src/util/PostToThread.hpp index ddf5caa12..ab0fbe1cb 100644 --- a/src/util/PostToThread.hpp +++ b/src/util/PostToThread.hpp @@ -7,7 +7,8 @@ #include -#define async_exec(a) QThreadPool::globalInstance()->start(new LambdaRunnable(a)); +#define async_exec(a) \ + QThreadPool::globalInstance()->start(new LambdaRunnable(a)); namespace chatterino { diff --git a/src/util/RapidjsonHelpers.hpp b/src/util/RapidjsonHelpers.hpp index 25e72ed4b..db6db99f4 100644 --- a/src/util/RapidjsonHelpers.hpp +++ b/src/util/RapidjsonHelpers.hpp @@ -26,7 +26,8 @@ void set(rapidjson::Value &obj, const char *key, const Type &value, } template <> -inline void set(rapidjson::Value &obj, const char *key, const rapidjson::Value &value, +inline void set(rapidjson::Value &obj, const char *key, + const rapidjson::Value &value, rapidjson::Document::AllocatorType &a) { assert(obj.IsObject()); @@ -45,7 +46,8 @@ void set(rapidjson::Document &obj, const char *key, const Type &value) } template <> -inline void set(rapidjson::Document &obj, const char *key, const rapidjson::Value &value) +inline void set(rapidjson::Document &obj, const char *key, + const rapidjson::Value &value) { assert(obj.IsObject()); @@ -55,7 +57,8 @@ inline void set(rapidjson::Document &obj, const char *key, const rapidjson::Valu } template -void add(rapidjson::Value &arr, const Type &value, rapidjson::Document::AllocatorType &a) +void add(rapidjson::Value &arr, const Type &value, + rapidjson::Document::AllocatorType &a) { assert(arr.IsArray()); diff --git a/src/util/RemoveScrollAreaBackground.hpp b/src/util/RemoveScrollAreaBackground.hpp index 66629c4e7..b82bbeea2 100644 --- a/src/util/RemoveScrollAreaBackground.hpp +++ b/src/util/RemoveScrollAreaBackground.hpp @@ -4,7 +4,8 @@ namespace chatterino { -static void removeScrollAreaBackground(QScrollArea *scrollArea, QWidget *childWidget) +static void removeScrollAreaBackground(QScrollArea *scrollArea, + QWidget *childWidget) { scrollArea->setWidgetResizable(true); scrollArea->setFrameStyle(0); diff --git a/src/util/SharedPtrElementLess.hpp b/src/util/SharedPtrElementLess.hpp index 0fa121a1d..b88f20230 100644 --- a/src/util/SharedPtrElementLess.hpp +++ b/src/util/SharedPtrElementLess.hpp @@ -6,7 +6,8 @@ namespace chatterino { template struct SharedPtrElementLess { - bool operator()(const std::shared_ptr &a, const std::shared_ptr &b) const + bool operator()(const std::shared_ptr &a, + const std::shared_ptr &b) const { return a->operator<(*b); } diff --git a/src/util/StandardItemHelper.hpp b/src/util/StandardItemHelper.hpp index 860dddc2a..4638e7277 100644 --- a/src/util/StandardItemHelper.hpp +++ b/src/util/StandardItemHelper.hpp @@ -4,19 +4,21 @@ namespace chatterino { -static void setBoolItem(QStandardItem *item, bool value, bool userCheckable = true, - bool selectable = true) +static void setBoolItem(QStandardItem *item, bool value, + bool userCheckable = true, bool selectable = true) { - item->setFlags((Qt::ItemFlags)(Qt::ItemIsEnabled | (selectable ? Qt::ItemIsSelectable : 0) | - (userCheckable ? Qt::ItemIsUserCheckable : 0))); + item->setFlags((Qt::ItemFlags)( + Qt::ItemIsEnabled | (selectable ? Qt::ItemIsSelectable : 0) | + (userCheckable ? Qt::ItemIsUserCheckable : 0))); item->setCheckState(value ? Qt::Checked : Qt::Unchecked); } -static void setStringItem(QStandardItem *item, const QString &value, bool editable = true, - bool selectable = true) +static void setStringItem(QStandardItem *item, const QString &value, + bool editable = true, bool selectable = true) { item->setData(value, Qt::EditRole); - item->setFlags((Qt::ItemFlags)(Qt::ItemIsEnabled | (selectable ? Qt::ItemIsSelectable : 0) | + item->setFlags((Qt::ItemFlags)(Qt::ItemIsEnabled | + (selectable ? Qt::ItemIsSelectable : 0) | (editable ? (Qt::ItemIsEditable) : 0))); } diff --git a/src/util/StreamLink.cpp b/src/util/StreamLink.cpp index 312f027d2..2fd2ef0a3 100644 --- a/src/util/StreamLink.cpp +++ b/src/util/StreamLink.cpp @@ -50,7 +50,8 @@ bool checkStreamlinkPath(const QString &path) if (!fileinfo.exists()) { return false; - // throw Exception(fS("Streamlink path ({}) is invalid, file does not exist", path)); + // throw Exception(fS("Streamlink path ({}) is invalid, file does not + // exist", path)); } return fileinfo.isExecutable(); @@ -63,11 +64,13 @@ void showStreamlinkNotFoundError() auto app = getApp(); if (app->settings->streamlinkUseCustomPath) { msg->showMessage( - "Unable to find Streamlink executable\nMake sure your custom path is pointing " + "Unable to find Streamlink executable\nMake sure your custom path " + "is pointing " "to the DIRECTORY where the streamlink executable is located"); } else { - msg->showMessage("Unable to find Streamlink executable.\nIf you have Streamlink " - "installed, you might need to enable the custom path option"); + msg->showMessage( + "Unable to find Streamlink executable.\nIf you have Streamlink " + "installed, you might need to enable the custom path option"); } } @@ -86,51 +89,58 @@ QProcess *createStreamlinkProcess() p->deleteLater(); }); - QObject::connect(p, static_cast(&QProcess::finished), [=](int res) { - p->deleteLater(); // - }); + QObject::connect(p, + static_cast(&QProcess::finished), + [=](int res) { + p->deleteLater(); // + }); return p; } } // namespace -void getStreamQualities(const QString &channelURL, std::function cb) +void getStreamQualities(const QString &channelURL, + std::function cb) { auto p = createStreamlinkProcess(); - QObject::connect(p, static_cast(&QProcess::finished), [=](int res) { - if (res != 0) { - qDebug() << "Got error code" << res; - // return; - } - QString lastLine = QString(p->readAllStandardOutput()); - lastLine = lastLine.trimmed().split('\n').last().trimmed(); - if (lastLine.startsWith("Available streams: ")) { - QStringList options; - QStringList split = lastLine.right(lastLine.length() - 19).split(", "); - - for (int i = split.length() - 1; i >= 0; i--) { - QString option = split.at(i); - if (option.endsWith(" (worst)")) { - options << option.left(option.length() - 8); - } else if (option.endsWith(" (best)")) { - options << option.left(option.length() - 7); - } else { - options << option; - } + QObject::connect( + p, static_cast(&QProcess::finished), + [=](int res) { + if (res != 0) { + qDebug() << "Got error code" << res; + // return; } + QString lastLine = QString(p->readAllStandardOutput()); + lastLine = lastLine.trimmed().split('\n').last().trimmed(); + if (lastLine.startsWith("Available streams: ")) { + QStringList options; + QStringList split = + lastLine.right(lastLine.length() - 19).split(", "); - cb(options); - } - }); + for (int i = split.length() - 1; i >= 0; i--) { + QString option = split.at(i); + if (option.endsWith(" (worst)")) { + options << option.left(option.length() - 8); + } else if (option.endsWith(" (best)")) { + options << option.left(option.length() - 7); + } else { + options << option; + } + } + + cb(options); + } + }); p->setArguments({channelURL, "--default-stream=KKona"}); p->start(); } -void openStreamlink(const QString &channelURL, const QString &quality, QStringList extraArguments) +void openStreamlink(const QString &channelURL, const QString &quality, + QStringList extraArguments) { auto app = getApp(); @@ -149,7 +159,8 @@ void openStreamlink(const QString &channelURL, const QString &quality, QStringLi arguments << quality; } - bool res = QProcess::startDetached(getStreamlinkProgram() + " " + QString(arguments.join(' '))); + bool res = QProcess::startDetached(getStreamlinkProgram() + " " + + QString(arguments.join(' '))); if (!res) { showStreamlinkNotFoundError(); diff --git a/src/util/StreamLink.hpp b/src/util/StreamLink.hpp index 2c5eebf4a..99dbb95b9 100644 --- a/src/util/StreamLink.hpp +++ b/src/util/StreamLink.hpp @@ -15,12 +15,13 @@ public: }; // Open streamlink for given channel, quality and extra arguments -// the "Additional arguments" are fetched and added at the beginning of the streamlink call +// the "Additional arguments" are fetched and added at the beginning of the +// streamlink call void openStreamlink(const QString &channelURL, const QString &quality, QStringList extraArguments = QStringList()); -// Start opening streamlink for the given channel, reading settings like quality from settings -// and opening a quality dialog if the quality is "Choose" +// Start opening streamlink for the given channel, reading settings like quality +// from settings and opening a quality dialog if the quality is "Choose" void openStreamlinkForChannel(const QString &channel); } // namespace chatterino diff --git a/src/util/WindowsHelper.cpp b/src/util/WindowsHelper.cpp index 743362ade..38ca74698 100644 --- a/src/util/WindowsHelper.cpp +++ b/src/util/WindowsHelper.cpp @@ -11,14 +11,17 @@ typedef enum MONITOR_DPI_TYPE { MDT_DEFAULT = MDT_EFFECTIVE_DPI } MONITOR_DPI_TYPE; -typedef HRESULT(CALLBACK *GetDpiForMonitor_)(HMONITOR, MONITOR_DPI_TYPE, UINT *, UINT *); +typedef HRESULT(CALLBACK *GetDpiForMonitor_)(HMONITOR, MONITOR_DPI_TYPE, UINT *, + UINT *); boost::optional getWindowDpi(HWND hwnd) { static HINSTANCE shcore = LoadLibrary(L"Shcore.dll"); if (shcore != nullptr) { - if (auto getDpiForMonitor = GetDpiForMonitor_(GetProcAddress(shcore, "GetDpiForMonitor"))) { - HMONITOR monitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST); + if (auto getDpiForMonitor = + GetDpiForMonitor_(GetProcAddress(shcore, "GetDpiForMonitor"))) { + HMONITOR monitor = + MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST); UINT xScale, yScale; diff --git a/src/widgets/AccountSwitchWidget.cpp b/src/widgets/AccountSwitchWidget.cpp index 3b2acc268..9c307ba73 100644 --- a/src/widgets/AccountSwitchWidget.cpp +++ b/src/widgets/AccountSwitchWidget.cpp @@ -39,10 +39,12 @@ AccountSwitchWidget::AccountSwitchWidget(QWidget *parent) QObject::connect(this, &QListWidget::clicked, [=] { if (!this->selectedItems().isEmpty()) { QString newUsername = this->currentItem()->text(); - if (newUsername.compare(ANONYMOUS_USERNAME_LABEL, Qt::CaseInsensitive) == 0) { + if (newUsername.compare(ANONYMOUS_USERNAME_LABEL, + Qt::CaseInsensitive) == 0) { app->accounts->twitch.currentUsername = ""; } else { - app->accounts->twitch.currentUsername = newUsername.toStdString(); + app->accounts->twitch.currentUsername = + newUsername.toStdString(); } } }); @@ -70,7 +72,8 @@ void AccountSwitchWidget::refreshSelection() for (int i = 0; i < this->count(); ++i) { QString itemText = this->item(i)->text(); - if (itemText.compare(currentUsername, Qt::CaseInsensitive) == 0) { + if (itemText.compare(currentUsername, Qt::CaseInsensitive) == + 0) { this->setCurrentRow(i); break; } diff --git a/src/widgets/AttachedWindow.cpp b/src/widgets/AttachedWindow.cpp index 6fdf93680..b247129b6 100644 --- a/src/widgets/AttachedWindow.cpp +++ b/src/widgets/AttachedWindow.cpp @@ -129,14 +129,17 @@ void AttachedWindow::attachToHwnd(void *_attachedPtr) DWORD processId; ::GetWindowThreadProcessId(attached, &processId); - HANDLE process = - ::OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, false, processId); + HANDLE process = ::OpenProcess( + PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, false, processId); std::unique_ptr filename(new TCHAR[512]); - DWORD filenameLength = ::GetModuleFileNameEx(process, nullptr, filename.get(), 512); - QString qfilename = QString::fromWCharArray(filename.get(), filenameLength); + DWORD filenameLength = + ::GetModuleFileNameEx(process, nullptr, filename.get(), 512); + QString qfilename = + QString::fromWCharArray(filename.get(), filenameLength); - if (!qfilename.endsWith("chrome.exe") && !qfilename.endsWith("firefox.exe")) { + if (!qfilename.endsWith("chrome.exe") && + !qfilename.endsWith("firefox.exe")) { qDebug() << "NM Illegal caller" << qfilename; this->timer_.stop(); this->deleteLater(); @@ -157,9 +160,9 @@ void AttachedWindow::updateWindowRect(void *_attachedPtr) auto hwnd = HWND(this->winId()); auto attached = HWND(_attachedPtr); - // We get the window rect first so we can close this window when it returns an error. - // If we query the process first and check the filename then it will return and empty string - // that doens't match. + // We get the window rect first so we can close this window when it returns + // an error. If we query the process first and check the filename then it + // will return and empty string that doens't match. ::SetLastError(0); RECT rect; ::GetWindowRect(attached, &rect); @@ -189,21 +192,25 @@ void AttachedWindow::updateWindowRect(void *_attachedPtr) } if (this->height_ == -1) { - // ::MoveWindow(hwnd, rect.right - this->width_ - 8, rect.top + this->yOffset_ - 8, - // this->width_, rect.bottom - rect.top - this->yOffset_, false); + // ::MoveWindow(hwnd, rect.right - this->width_ - 8, rect.top + + // this->yOffset_ - 8, + // this->width_, rect.bottom - rect.top - this->yOffset_, + // false); } else { ::MoveWindow(hwnd, // int(rect.right - this->width_ * scale - 8), // int(rect.bottom - this->height_ * scale - 8), // - int(this->width_ * scale), int(this->height_ * scale), true); + int(this->width_ * scale), int(this->height_ * scale), + true); } -// ::MoveWindow(hwnd, rect.right - 360, rect.top + 82, 360 - 8, rect.bottom - -// rect.top - 82 - 8, false); +// ::MoveWindow(hwnd, rect.right - 360, rect.top + 82, 360 - 8, +// rect.bottom - rect.top - 82 - 8, false); #endif } -// void AttachedWindow::nativeEvent(const QByteArray &eventType, void *message, long *result) +// void AttachedWindow::nativeEvent(const QByteArray &eventType, void *message, +// long *result) //{ // MSG *msg = reinterpret_cast diff --git a/src/widgets/AttachedWindow.hpp b/src/widgets/AttachedWindow.hpp index b7a9ad617..80b030fca 100644 --- a/src/widgets/AttachedWindow.hpp +++ b/src/widgets/AttachedWindow.hpp @@ -28,8 +28,8 @@ public: protected: virtual void showEvent(QShowEvent *) override; - // virtual void nativeEvent(const QByteArray &eventType, void *message, long *result) - // override; + // virtual void nativeEvent(const QByteArray &eventType, void *message, + // long *result) override; private: struct { diff --git a/src/widgets/BaseWidget.cpp b/src/widgets/BaseWidget.cpp index 818d951a8..278fe9248 100644 --- a/src/widgets/BaseWidget.cpp +++ b/src/widgets/BaseWidget.cpp @@ -97,12 +97,14 @@ void BaseWidget::setScaleIndependantSize(QSize size) void BaseWidget::setScaleIndependantWidth(int value) { - this->setScaleIndependantSize(QSize(value, this->scaleIndependantSize_.height())); + this->setScaleIndependantSize( + QSize(value, this->scaleIndependantSize_.height())); } void BaseWidget::setScaleIndependantHeight(int value) { - this->setScaleIndependantSize(QSize(this->scaleIndependantSize_.height(), value)); + this->setScaleIndependantSize( + QSize(this->scaleIndependantSize_.height(), value)); } void BaseWidget::childEvent(QChildEvent *event) @@ -114,7 +116,8 @@ void BaseWidget::childEvent(QChildEvent *event) this->widgets_.push_back(widget); } } else if (event->removed()) { - for (auto it = this->widgets_.begin(); it != this->widgets_.end(); it++) { + for (auto it = this->widgets_.begin(); it != this->widgets_.end(); + it++) { if (*it == event->child()) { this->widgets_.erase(it); break; diff --git a/src/widgets/BaseWindow.cpp b/src/widgets/BaseWindow.cpp index 00a841684..424b96e6d 100644 --- a/src/widgets/BaseWindow.cpp +++ b/src/widgets/BaseWindow.cpp @@ -41,7 +41,8 @@ namespace chatterino { BaseWindow::BaseWindow(QWidget *parent, Flags _flags) : BaseWidget(parent, - Qt::Window | ((_flags & TopMost) ? Qt::WindowStaysOnTopHint : Qt::WindowFlags())) + Qt::Window | ((_flags & TopMost) ? Qt::WindowStaysOnTopHint + : Qt::WindowFlags())) , enableCustomFrame_(_flags & EnableCustomFrame) , frameless_(_flags & Frameless) , flags_(_flags) @@ -59,7 +60,8 @@ BaseWindow::BaseWindow(QWidget *parent, Flags _flags) this->updateScale(); - createWindowShortcut(this, "CTRL+0", [] { getApp()->settings->uiScale.setValue(0); }); + createWindowShortcut(this, "CTRL+0", + [] { getApp()->settings->uiScale.setValue(0); }); // QTimer::this->scaleChangedEvent(this->getScale()); } @@ -88,16 +90,19 @@ void BaseWindow::init() this->setLayout(layout); { if (!this->frameless_) { - QHBoxLayout *buttonLayout = this->ui_.titlebarBox = new QHBoxLayout(); + QHBoxLayout *buttonLayout = this->ui_.titlebarBox = + new QHBoxLayout(); buttonLayout->setMargin(0); layout->addLayout(buttonLayout); // title Label *title = new Label("Chatterino"); - QObject::connect(this, &QWidget::windowTitleChanged, - [title](const QString &text) { title->setText(text); }); + QObject::connect( + this, &QWidget::windowTitleChanged, + [title](const QString &text) { title->setText(text); }); - QSizePolicy policy(QSizePolicy::Ignored, QSizePolicy::Preferred); + QSizePolicy policy(QSizePolicy::Ignored, + QSizePolicy::Preferred); policy.setHorizontalStretch(1); // title->setBaseSize(0, 0); // title->setScaledContents(true); @@ -113,14 +118,18 @@ void BaseWindow::init() TitleBarButton *_exitButton = new TitleBarButton; _exitButton->setButtonStyle(TitleBarButton::Close); - QObject::connect(_minButton, &TitleBarButton::clicked, this, [this] { - this->setWindowState(Qt::WindowMinimized | this->windowState()); - }); - QObject::connect(_maxButton, &TitleBarButton::clicked, this, [this] { - this->setWindowState(this->windowState() == Qt::WindowMaximized - ? Qt::WindowActive - : Qt::WindowMaximized); - }); + QObject::connect(_minButton, &TitleBarButton::clicked, this, + [this] { + this->setWindowState(Qt::WindowMinimized | + this->windowState()); + }); + QObject::connect( + _maxButton, &TitleBarButton::clicked, this, [this] { + this->setWindowState(this->windowState() == + Qt::WindowMaximized + ? Qt::WindowActive + : Qt::WindowMaximized); + }); QObject::connect(_exitButton, &TitleBarButton::clicked, this, [this] { this->close(); }); @@ -157,8 +166,10 @@ void BaseWindow::init() QTimer::singleShot(1, this, [this] { getApp()->settings->windowTopMost.connect( [this](bool topMost, auto) { - ::SetWindowPos(HWND(this->winId()), topMost ? HWND_TOPMOST : HWND_NOTOPMOST, 0, - 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE); + ::SetWindowPos(HWND(this->winId()), + topMost ? HWND_TOPMOST : HWND_NOTOPMOST, 0, + 0, 0, 0, + SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE); }, this->managedConnections_); }); @@ -222,8 +233,9 @@ void BaseWindow::themeChangedEvent() if (this->ui_.titleLabel) { QPalette palette_title; - palette_title.setColor(QPalette::Foreground, - this->theme->isLightTheme() ? "#333" : "#ccc"); + palette_title.setColor( + QPalette::Foreground, + this->theme->isLightTheme() ? "#333" : "#ccc"); this->ui_.titleLabel->setPalette(palette_title); } @@ -240,7 +252,8 @@ void BaseWindow::themeChangedEvent() bool BaseWindow::event(QEvent *event) { - if (event->type() == QEvent::WindowDeactivate /*|| event->type() == QEvent::FocusOut*/) { + if (event->type() == + QEvent::WindowDeactivate /*|| event->type() == QEvent::FocusOut*/) { this->onFocusLost(); } @@ -255,11 +268,11 @@ void BaseWindow::wheelEvent(QWheelEvent *event) if (event->modifiers() & Qt::ControlModifier) { if (event->delta() > 0) { - getApp()->settings->uiScale.setValue( - WindowManager::clampUiScale(getApp()->settings->uiScale.getValue() + 1)); + getApp()->settings->uiScale.setValue(WindowManager::clampUiScale( + getApp()->settings->uiScale.getValue() + 1)); } else { - getApp()->settings->uiScale.setValue( - WindowManager::clampUiScale(getApp()->settings->uiScale.getValue() - 1)); + getApp()->settings->uiScale.setValue(WindowManager::clampUiScale( + getApp()->settings->uiScale.getValue() - 1)); } } } @@ -288,7 +301,8 @@ void BaseWindow::mousePressEvent(QMouseEvent *event) #ifndef Q_OS_WIN if (this->flags_ & FramelessDraggable) { this->movingRelativePos = event->localPos(); - if (auto widget = this->childAt(event->localPos().x(), event->localPos().y())) { + if (auto widget = + this->childAt(event->localPos().x(), event->localPos().y())) { std::function recursiveCheckMouseTracking; recursiveCheckMouseTracking = [&](QWidget *widget) { if (widget == nullptr) { @@ -341,8 +355,8 @@ void BaseWindow::mouseMoveEvent(QMouseEvent *event) BaseWidget::mouseMoveEvent(event); } -TitleBarButton *BaseWindow::addTitleBarButton(const TitleBarButton::Style &style, - std::function onClicked) +TitleBarButton *BaseWindow::addTitleBarButton( + const TitleBarButton::Style &style, std::function onClicked) { TitleBarButton *button = new TitleBarButton; button->setScaleIndependantSize(30, 30); @@ -351,7 +365,8 @@ TitleBarButton *BaseWindow::addTitleBarButton(const TitleBarButton::Style &style this->ui_.titlebarBox->insertWidget(1, button); button->setButtonStyle(style); - QObject::connect(button, &TitleBarButton::clicked, this, [onClicked] { onClicked(); }); + QObject::connect(button, &TitleBarButton::clicked, this, + [onClicked] { onClicked(); }); return button; } @@ -364,7 +379,8 @@ RippleEffectLabel *BaseWindow::addTitleBarLabel(std::function onClicked) this->ui_.buttons.push_back(button); this->ui_.titlebarBox->insertWidget(1, button); - QObject::connect(button, &RippleEffectLabel::clicked, this, [onClicked] { onClicked(); }); + QObject::connect(button, &RippleEffectLabel::clicked, this, + [onClicked] { onClicked(); }); return button; } @@ -375,7 +391,8 @@ void BaseWindow::changeEvent(QEvent *) #ifdef USEWINSDK if (this->ui_.maxButton) { - this->ui_.maxButton->setButtonStyle(this->windowState() & Qt::WindowMaximized + this->ui_.maxButton->setButtonStyle(this->windowState() & + Qt::WindowMaximized ? TitleBarButton::Unmaximize : TitleBarButton::Maximize); } @@ -416,8 +433,7 @@ void BaseWindow::closeEvent(QCloseEvent *) void BaseWindow::moveIntoDesktopRect(QWidget *parent) { - if (!this->stayInScreenRect_) - return; + if (!this->stayInScreenRect_) return; // move the widget into the screen geometry if it's not already in there QDesktopWidget *desktop = QApplication::desktop(); @@ -438,11 +454,11 @@ void BaseWindow::moveIntoDesktopRect(QWidget *parent) p.setY(s.bottom() - this->height()); } - if (p != this->pos()) - this->move(p); + if (p != this->pos()) this->move(p); } -bool BaseWindow::nativeEvent(const QByteArray &eventType, void *message, long *result) +bool BaseWindow::nativeEvent(const QByteArray &eventType, void *message, + long *result) { #ifdef USEWINSDK MSG *msg = reinterpret_cast(message); @@ -503,8 +519,10 @@ void BaseWindow::paintEvent(QPaintEvent *) void BaseWindow::updateScale() { - auto scale = this->nativeScale_ * - (this->flags_ & DisableCustomScaling ? 1 : getApp()->windows->getUiScaleValue()); + auto scale = + this->nativeScale_ * (this->flags_ & DisableCustomScaling + ? 1 + : getApp()->windows->getUiScaleValue()); this->setScale(scale); for (auto child : this->findChildren()) { @@ -541,9 +559,11 @@ void BaseWindow::drawCustomWindowFrame(QPainter &painter) if (this->hasCustomWindowFrame()) { QPainter painter(this); - QColor bg = this->overrideBackgroundColor_.value_or(this->theme->window.background); + QColor bg = this->overrideBackgroundColor_.value_or( + this->theme->window.background); - painter.fillRect(QRect(0, 1, this->width() - 0, this->height() - 0), bg); + painter.fillRect(QRect(0, 1, this->width() - 0, this->height() - 0), + bg); } #endif } @@ -561,7 +581,8 @@ bool BaseWindow::handleDPICHANGED(MSG *msg) auto *prcNewWindow = reinterpret_cast(msg->lParam); SetWindowPos(msg->hwnd, nullptr, prcNewWindow->left, prcNewWindow->top, prcNewWindow->right - prcNewWindow->left, - prcNewWindow->bottom - prcNewWindow->top, SWP_NOZORDER | SWP_NOACTIVATE); + prcNewWindow->bottom - prcNewWindow->top, + SWP_NOZORDER | SWP_NOACTIVATE); } firstResize = false; @@ -605,7 +626,8 @@ bool BaseWindow::handleNCCALCSIZE(MSG *msg, long *result) // int cy = GetSystemMetrics(SM_CYSIZEFRAME); if (msg->wParam == TRUE) { - NCCALCSIZE_PARAMS *ncp = (reinterpret_cast(msg->lParam)); + NCCALCSIZE_PARAMS *ncp = + (reinterpret_cast(msg->lParam)); ncp->lppos->flags |= SWP_NOREDRAW; RECT *clientRect = &ncp->rgrc[0]; @@ -634,7 +656,8 @@ bool BaseWindow::handleSIZE(MSG *msg) if (msg->wParam == SIZE_MAXIMIZED) { auto offset = int(this->getScale() * 8); - this->ui_.windowLayout->setContentsMargins(offset, offset, offset, offset); + this->ui_.windowLayout->setContentsMargins(offset, offset, + offset, offset); } else { this->ui_.windowLayout->setContentsMargins(0, 1, 0, 0); } @@ -686,23 +709,23 @@ bool BaseWindow::handleNCHITTEST(MSG *msg, long *result) } if (resizeWidth && resizeHeight) { // bottom left corner - if (x >= winrect.left && x < winrect.left + border_width && y < winrect.bottom && - y >= winrect.bottom - border_width) { + if (x >= winrect.left && x < winrect.left + border_width && + y < winrect.bottom && y >= winrect.bottom - border_width) { *result = HTBOTTOMLEFT; } // bottom right corner - if (x < winrect.right && x >= winrect.right - border_width && y < winrect.bottom && - y >= winrect.bottom - border_width) { + if (x < winrect.right && x >= winrect.right - border_width && + y < winrect.bottom && y >= winrect.bottom - border_width) { *result = HTBOTTOMRIGHT; } // top left corner - if (x >= winrect.left && x < winrect.left + border_width && y >= winrect.top && - y < winrect.top + border_width) { + if (x >= winrect.left && x < winrect.left + border_width && + y >= winrect.top && y < winrect.top + border_width) { *result = HTTOPLEFT; } // top right corner - if (x < winrect.right && x >= winrect.right - border_width && y >= winrect.top && - y < winrect.top + border_width) { + if (x < winrect.right && x >= winrect.right - border_width && + y >= winrect.top && y < winrect.top + border_width) { *result = HTTOPRIGHT; } } diff --git a/src/widgets/BaseWindow.hpp b/src/widgets/BaseWindow.hpp index d25a1be2e..dbfb4b46b 100644 --- a/src/widgets/BaseWindow.hpp +++ b/src/widgets/BaseWindow.hpp @@ -55,7 +55,8 @@ public: pajlada::Signals::NoArgSignal closing; protected: - virtual bool nativeEvent(const QByteArray &eventType, void *message, long *result) override; + virtual bool nativeEvent(const QByteArray &eventType, void *message, + long *result) override; virtual void scaleChangedEvent(float) override; virtual void paintEvent(QPaintEvent *) override; diff --git a/src/widgets/Label.cpp b/src/widgets/Label.cpp index ca851d4b5..aefe07d49 100644 --- a/src/widgets/Label.cpp +++ b/src/widgets/Label.cpp @@ -18,7 +18,8 @@ Label::Label(BaseWidget *parent, QString text, FontStyle style) { auto app = getApp(); - this->connections_.managedConnect(app->fonts->fontChanged, [this] { this->updateSize(); }); + this->connections_.managedConnect(app->fonts->fontChanged, + [this] { this->updateSize(); }); } const QString &Label::getText() const @@ -88,11 +89,11 @@ void Label::paintEvent(QPaintEvent *) QPainter painter(this); QFontMetrics metrics = app->fonts->getFontMetrics( - this->getFontStyle(), - this->getScale() * 96.f / this->logicalDpiX() * this->devicePixelRatioF()); + this->getFontStyle(), this->getScale() * 96.f / this->logicalDpiX() * + this->devicePixelRatioF()); painter.setFont(app->fonts->getFont( - this->getFontStyle(), - this->getScale() * 96.f / this->logicalDpiX() * this->devicePixelRatioF())); + this->getFontStyle(), this->getScale() * 96.f / this->logicalDpiX() * + this->devicePixelRatioF())); int offset = this->getOffset(); @@ -118,7 +119,8 @@ void Label::updateSize() { auto app = getApp(); - QFontMetrics metrics = app->fonts->getFontMetrics(this->fontStyle_, this->getScale()); + QFontMetrics metrics = + app->fonts->getFontMetrics(this->fontStyle_, this->getScale()); int width = metrics.width(this->text_) + (2 * this->getOffset()); int height = metrics.height(); diff --git a/src/widgets/Label.hpp b/src/widgets/Label.hpp index 96e726b08..4a10d307c 100644 --- a/src/widgets/Label.hpp +++ b/src/widgets/Label.hpp @@ -10,7 +10,8 @@ namespace chatterino { class Label : public BaseWidget { public: - explicit Label(QString text = QString(), FontStyle style = FontStyle::UiMedium); + explicit Label(QString text = QString(), + FontStyle style = FontStyle::UiMedium); explicit Label(BaseWidget *parent, QString text = QString(), FontStyle style = FontStyle::UiMedium); diff --git a/src/widgets/Notebook.cpp b/src/widgets/Notebook.cpp index 5068732b7..d99d1f021 100644 --- a/src/widgets/Notebook.cpp +++ b/src/widgets/Notebook.cpp @@ -34,10 +34,12 @@ Notebook::Notebook(QWidget *parent) this->addButton_.setHidden(true); auto *shortcut_next = new QShortcut(QKeySequence("Ctrl+Tab"), this); - QObject::connect(shortcut_next, &QShortcut::activated, [this] { this->selectNextTab(); }); + QObject::connect(shortcut_next, &QShortcut::activated, + [this] { this->selectNextTab(); }); auto *shortcut_prev = new QShortcut(QKeySequence("Ctrl+Shift+Tab"), this); - QObject::connect(shortcut_prev, &QShortcut::activated, [this] { this->selectPreviousTab(); }); + QObject::connect(shortcut_prev, &QShortcut::activated, + [this] { this->selectPreviousTab(); }); } NotebookTab *Notebook::addPage(QWidget *page, QString title, bool select) @@ -134,7 +136,8 @@ void Notebook::select(QWidget *page) qDebug() << item.selectedWidget; item.selectedWidget->setFocus(Qt::MouseFocusReason); } else { - qDebug() << "Notebook: selected child of page doesn't exist anymore"; + qDebug() + << "Notebook: selected child of page doesn't exist anymore"; } } } @@ -165,21 +168,23 @@ bool Notebook::containsPage(QWidget *page) Notebook::Item &Notebook::findItem(QWidget *page) { - auto it = std::find_if(this->items_.begin(), this->items_.end(), - [page](const auto &item) { return page == item.page; }); + auto it = + std::find_if(this->items_.begin(), this->items_.end(), + [page](const auto &item) { return page == item.page; }); assert(it != this->items_.end()); return *it; } bool Notebook::containsChild(const QObject *obj, const QObject *child) { - return std::any_of(obj->children().begin(), obj->children().end(), [child](const QObject *o) { - if (o == child) { - return true; - } + return std::any_of(obj->children().begin(), obj->children().end(), + [child](const QObject *o) { + if (o == child) { + return true; + } - return containsChild(o, child); - }); + return containsChild(o, child); + }); } void Notebook::selectIndex(int index) @@ -328,8 +333,10 @@ void Notebook::performLayout(bool animated) for (auto i = this->items_.begin(); i != this->items_.end(); i++) { bool wrap = - !first && (((i + 1 == this->items_.end() && this->showAddButton_) ? tabHeight : 0) + x + - i->tab->width()) > width(); + !first && + (((i + 1 == this->items_.end() && this->showAddButton_) ? tabHeight + : 0) + + x + i->tab->width()) > width(); if (wrap) { y += i->tab->height(); @@ -376,8 +383,8 @@ void Notebook::paintEvent(QPaintEvent *event) BaseWidget::paintEvent(event); QPainter painter(this); - painter.fillRect(0, this->lineY_, this->width(), (int)(3 * this->getScale()), - this->theme->tabs.bottomLine); + painter.fillRect(0, this->lineY_, this->width(), + (int)(3 * this->getScale()), this->theme->tabs.bottomLine); } NotebookButton *Notebook::getAddButton() @@ -409,8 +416,9 @@ NotebookTab *Notebook::getTabFromPage(QWidget *page) SplitNotebook::SplitNotebook(Window *parent) : Notebook(parent) { - this->connect(this->getAddButton(), &NotebookButton::clicked, - [this]() { QTimer::singleShot(80, this, [this] { this->addPage(true); }); }); + this->connect(this->getAddButton(), &NotebookButton::clicked, [this]() { + QTimer::singleShot(80, this, [this] { this->addPage(true); }); + }); bool customFrame = parent->hasCustomWindowFrame(); @@ -424,10 +432,12 @@ void SplitNotebook::addCustomButtons() // settings auto settingsBtn = this->addCustomButton(); - settingsBtn->setVisible(!getApp()->settings->hidePreferencesButton.getValue()); + settingsBtn->setVisible( + !getApp()->settings->hidePreferencesButton.getValue()); getApp()->settings->hidePreferencesButton.connect( - [settingsBtn](bool hide, auto) { settingsBtn->setVisible(!hide); }, this->connections_); + [settingsBtn](bool hide, auto) { settingsBtn->setVisible(!hide); }, + this->connections_); settingsBtn->setIcon(NotebookButton::Settings); @@ -438,17 +448,20 @@ void SplitNotebook::addCustomButtons() auto userBtn = this->addCustomButton(); userBtn->setVisible(!getApp()->settings->hideUserButton.getValue()); getApp()->settings->hideUserButton.connect( - [userBtn](bool hide, auto) { userBtn->setVisible(!hide); }, this->connections_); + [userBtn](bool hide, auto) { userBtn->setVisible(!hide); }, + this->connections_); userBtn->setIcon(NotebookButton::User); QObject::connect(userBtn, &NotebookButton::clicked, [this, userBtn] { - getApp()->windows->showAccountSelectPopup(this->mapToGlobal(userBtn->rect().bottomRight())); + getApp()->windows->showAccountSelectPopup( + this->mapToGlobal(userBtn->rect().bottomRight())); }); // updates auto updateBtn = this->addCustomButton(); - initUpdateButton(*updateBtn, this->updateDialogHandle_, this->signalHolder_); + initUpdateButton(*updateBtn, this->updateDialogHandle_, + this->signalHolder_); } SplitContainer *SplitNotebook::addPage(bool select) @@ -465,7 +478,8 @@ SplitContainer *SplitNotebook::getOrAddSelectedPage() { auto *selectedPage = this->getSelectedPage(); - return selectedPage != nullptr ? (SplitContainer *)selectedPage : this->addPage(); + return selectedPage != nullptr ? (SplitContainer *)selectedPage + : this->addPage(); } } // namespace chatterino diff --git a/src/widgets/Notebook.hpp b/src/widgets/Notebook.hpp index 4741fbac1..749d12594 100644 --- a/src/widgets/Notebook.hpp +++ b/src/widgets/Notebook.hpp @@ -23,7 +23,8 @@ class Notebook : public BaseWidget public: explicit Notebook(QWidget *parent); - NotebookTab *addPage(QWidget *page, QString title = QString(), bool select = false); + NotebookTab *addPage(QWidget *page, QString title = QString(), + bool select = false); void removePage(QWidget *page); void removeCurrentPage(); diff --git a/src/widgets/Scrollbar.cpp b/src/widgets/Scrollbar.cpp index 37efde61e..9c46cff80 100644 --- a/src/widgets/Scrollbar.cpp +++ b/src/widgets/Scrollbar.cpp @@ -21,7 +21,8 @@ Scrollbar::Scrollbar(ChannelView *parent) { resize(int(16 * this->getScale()), 100); this->currentValueAnimation_.setDuration(150); - this->currentValueAnimation_.setEasingCurve(QEasingCurve(QEasingCurve::OutCubic)); + this->currentValueAnimation_.setEasingCurve( + QEasingCurve(QEasingCurve::OutCubic)); setMouseTracking(true); } @@ -32,7 +33,8 @@ void Scrollbar::addHighlight(ScrollbarHighlight highlight) this->highlights_.pushBack(highlight, deleted); } -void Scrollbar::addHighlightsAtStart(const std::vector &_highlights) +void Scrollbar::addHighlightsAtStart( + const std::vector &_highlights) { this->highlights_.pushFront(_highlights); } @@ -103,28 +105,35 @@ void Scrollbar::setDesiredValue(qreal value, bool animated) { auto app = getApp(); animated &= app->settings->enableSmoothScrolling.getValue(); - value = std::max(this->minimum_, std::min(this->maximum_ - this->largeChange_, value)); + value = std::max(this->minimum_, + std::min(this->maximum_ - this->largeChange_, value)); - if (std::abs(this->desiredValue_ + this->smoothScrollingOffset_ - value) > 0.0001) { + if (std::abs(this->desiredValue_ + this->smoothScrollingOffset_ - value) > + 0.0001) { if (animated) { this->currentValueAnimation_.stop(); - this->currentValueAnimation_.setStartValue(this->currentValue_ + - this->smoothScrollingOffset_); + this->currentValueAnimation_.setStartValue( + this->currentValue_ + this->smoothScrollingOffset_); - // if (((this->getMaximum() - this->getLargeChange()) - value) <= 0.01) { + // if (((this->getMaximum() - this->getLargeChange()) - + // value) <= 0.01) { // value += 1; // } this->currentValueAnimation_.setEndValue(value); this->smoothScrollingOffset_ = 0; - this->atBottom_ = ((this->getMaximum() - this->getLargeChange()) - value) <= 0.0001; + this->atBottom_ = ((this->getMaximum() - this->getLargeChange()) - + value) <= 0.0001; this->currentValueAnimation_.start(); } else { - if (this->currentValueAnimation_.state() != QPropertyAnimation::Running) { + if (this->currentValueAnimation_.state() != + QPropertyAnimation::Running) { this->smoothScrollingOffset_ = 0; this->desiredValue_ = value; this->currentValueAnimation_.stop(); - this->atBottom_ = ((this->getMaximum() - this->getLargeChange()) - value) <= 0.0001; + this->atBottom_ = + ((this->getMaximum() - this->getLargeChange()) - value) <= + 0.0001; setCurrentValue(value); } } @@ -187,8 +196,9 @@ pajlada::Signals::NoArgSignal &Scrollbar::getDesiredValueChanged() void Scrollbar::setCurrentValue(qreal value) { - value = std::max(this->minimum_, std::min(this->maximum_ - this->largeChange_, - value + this->smoothScrollingOffset_)); + value = std::max(this->minimum_, + std::min(this->maximum_ - this->largeChange_, + value + this->smoothScrollingOffset_)); if (std::abs(this->currentValue_ - value) > 0.0001) { this->currentValue_ = value; @@ -221,15 +231,16 @@ void Scrollbar::paintEvent(QPaintEvent *) // painter.fillRect(QRect(xOffset, 0, width(), this->buttonHeight), // this->themeManager->ScrollbarArrow); - // painter.fillRect(QRect(xOffset, height() - this->buttonHeight, width(), - // this->buttonHeight), + // painter.fillRect(QRect(xOffset, height() - this->buttonHeight, + // width(), this->buttonHeight), // this->themeManager->ScrollbarArrow); this->thumbRect_.setX(xOffset); // mouse over thumb if (this->mouseDownIndex_ == 2) { - painter.fillRect(this->thumbRect_, this->theme->scrollbars.thumbSelected); + painter.fillRect(this->thumbRect_, + this->theme->scrollbars.thumbSelected); } // mouse not over thumb else { @@ -265,7 +276,8 @@ void Scrollbar::paintEvent(QPaintEvent *) switch (highlight.getStyle()) { case ScrollbarHighlight::Default: { - painter.fillRect(w / 8 * 3, int(y), w / 4, highlightHeight, color); + painter.fillRect(w / 8 * 3, int(y), w / 4, highlightHeight, + color); } break; case ScrollbarHighlight::Line: { @@ -310,7 +322,8 @@ void Scrollbar::mouseMoveEvent(QMouseEvent *event) } else if (this->mouseDownIndex_ == 2) { int delta = event->pos().y() - this->lastMousePosition_.y(); - setDesiredValue(this->desiredValue_ + qreal(delta) / this->trackHeight_ * this->maximum_); + setDesiredValue(this->desiredValue_ + + qreal(delta) / this->trackHeight_ * this->maximum_); } this->lastMousePosition_ = event->pos(); @@ -370,13 +383,16 @@ void Scrollbar::leaveEvent(QEvent *) void Scrollbar::updateScroll() { - this->trackHeight_ = - this->height() - this->buttonHeight_ - this->buttonHeight_ - MIN_THUMB_HEIGHT - 1; + this->trackHeight_ = this->height() - this->buttonHeight_ - + this->buttonHeight_ - MIN_THUMB_HEIGHT - 1; - this->thumbRect_ = QRect( - 0, int(this->currentValue_ / this->maximum_ * this->trackHeight_) + 1 + this->buttonHeight_, - this->width(), - int(this->largeChange_ / this->maximum_ * this->trackHeight_) + MIN_THUMB_HEIGHT); + this->thumbRect_ = + QRect(0, + int(this->currentValue_ / this->maximum_ * this->trackHeight_) + + 1 + this->buttonHeight_, + this->width(), + int(this->largeChange_ / this->maximum_ * this->trackHeight_) + + MIN_THUMB_HEIGHT); this->update(); } diff --git a/src/widgets/Scrollbar.hpp b/src/widgets/Scrollbar.hpp index 8de43d60a..fe72477c3 100644 --- a/src/widgets/Scrollbar.hpp +++ b/src/widgets/Scrollbar.hpp @@ -22,7 +22,8 @@ public: Scrollbar(ChannelView *parent = nullptr); void addHighlight(ScrollbarHighlight highlight); - void addHighlightsAtStart(const std::vector &highlights_); + void addHighlightsAtStart( + const std::vector &highlights_); void replaceHighlight(size_t index, ScrollbarHighlight replacement); void pauseHighlights(); diff --git a/src/widgets/StreamView.cpp b/src/widgets/StreamView.cpp index 05bd0e68e..ad3f1966c 100644 --- a/src/widgets/StreamView.cpp +++ b/src/widgets/StreamView.cpp @@ -16,9 +16,11 @@ StreamView::StreamView(ChannelPtr channel, const QUrl &url) LayoutCreator layoutCreator(this); #ifdef USEWEBENGINE - auto web = layoutCreator.emplace(this).assign(&this->stream); + auto web = + layoutCreator.emplace(this).assign(&this->stream); web->setUrl(url); - web->settings()->setAttribute(QWebEngineSettings::FullScreenSupportEnabled, true); + web->settings()->setAttribute(QWebEngineSettings::FullScreenSupportEnabled, + true); #endif auto chat = layoutCreator.emplace(); diff --git a/src/widgets/TooltipWidget.cpp b/src/widgets/TooltipWidget.cpp index f703a7c47..0b3b83639 100644 --- a/src/widgets/TooltipWidget.cpp +++ b/src/widgets/TooltipWidget.cpp @@ -36,7 +36,8 @@ TooltipWidget::TooltipWidget(BaseWidget *parent) this->setStayInScreenRect(true); this->setAttribute(Qt::WA_ShowWithoutActivating); - this->setWindowFlags(Qt::Tool | Qt::FramelessWindowHint | Qt::X11BypassWindowManagerHint | + this->setWindowFlags(Qt::Tool | Qt::FramelessWindowHint | + Qt::X11BypassWindowManagerHint | Qt::BypassWindowManagerHint); displayText_->setAlignment(Qt::AlignHCenter); @@ -46,7 +47,8 @@ TooltipWidget::TooltipWidget(BaseWidget *parent) layout->addWidget(displayText_); this->setLayout(layout); - this->fontChangedConnection_ = app->fonts->fontChanged.connect([this] { this->updateFont(); }); + this->fontChangedConnection_ = + app->fonts->fontChanged.connect([this] { this->updateFont(); }); } TooltipWidget::~TooltipWidget() @@ -76,7 +78,8 @@ void TooltipWidget::updateFont() { auto app = getApp(); - this->setFont(app->fonts->getFont(Fonts::Type::ChatMediumSmall, this->getScale())); + this->setFont( + app->fonts->getFont(Fonts::Type::ChatMediumSmall, this->getScale())); } void TooltipWidget::setText(QString text) diff --git a/src/widgets/Window.cpp b/src/widgets/Window.cpp index 37d808c2f..7088e5cb6 100644 --- a/src/widgets/Window.cpp +++ b/src/widgets/Window.cpp @@ -38,7 +38,8 @@ Window::Window(Type type) this->addShortcuts(); this->addLayout(); - getApp()->accounts->twitch.currentUserChanged.connect([this] { this->onAccountSelected(); }); + getApp()->accounts->twitch.currentUserChanged.connect( + [this] { this->onAccountSelected(); }); this->onAccountSelected(); if (type == Type::Main) { @@ -86,7 +87,8 @@ bool Window::event(QEvent *event) } } - if (SplitContainer *container = dynamic_cast(page)) { + if (SplitContainer *container = + dynamic_cast(page)) { container->hideResizeHandles(); } } break; @@ -103,10 +105,11 @@ void Window::showEvent(QShowEvent *event) if (getApp()->settings->startUpNotification.getValue() < 1) { getApp()->settings->startUpNotification = 1; - auto box = - new QMessageBox(QMessageBox::Information, "Chatterino 2 Beta", - "Please note that this software is not stable yet. Things are rough " - "around the edges and everything is subject to change."); + auto box = new QMessageBox( + QMessageBox::Information, "Chatterino 2 Beta", + "Please note that this software is not stable yet. Things are " + "rough " + "around the edges and everything is subject to change."); box->setAttribute(Qt::WA_DeleteOnClose); box->show(); } @@ -114,11 +117,13 @@ void Window::showEvent(QShowEvent *event) // Show changelog if (getApp()->settings->currentVersion.getValue() != "" && getApp()->settings->currentVersion.getValue() != CHATTERINO_VERSION) { - auto box = new QMessageBox(QMessageBox::Information, "Chatterino 2 Beta", "Show changelog?", + auto box = new QMessageBox(QMessageBox::Information, + "Chatterino 2 Beta", "Show changelog?", QMessageBox::Yes | QMessageBox::No); box->setAttribute(Qt::WA_DeleteOnClose); if (box->exec() == QMessageBox::Yes) { - QDesktopServices::openUrl(QUrl("https://fourtf.com/chatterino-changelog/")); + QDesktopServices::openUrl( + QUrl("https://fourtf.com/chatterino-changelog/")); } } @@ -174,8 +179,8 @@ void Window::addCustomTitlebarButtons() // account this->userLabel_ = this->addTitleBarLabel([this] { - getApp()->windows->showAccountSelectPopup( - this->userLabel_->mapToGlobal(this->userLabel_->rect().bottomLeft())); // + getApp()->windows->showAccountSelectPopup(this->userLabel_->mapToGlobal( + this->userLabel_->rect().bottomLeft())); // }); this->userLabel_->setMinimumWidth(20 * getScale()); } @@ -246,26 +251,36 @@ void Window::addShortcuts() createWindowShortcut(this, "CTRL+P", [] { SettingsDialog::showDialog(); }); // Switch tab - createWindowShortcut(this, "CTRL+T", - [this] { this->notebook_.getOrAddSelectedPage()->appendNewSplit(true); }); + createWindowShortcut(this, "CTRL+T", [this] { + this->notebook_.getOrAddSelectedPage()->appendNewSplit(true); + }); - createWindowShortcut(this, "CTRL+1", [this] { this->notebook_.selectIndex(0); }); - createWindowShortcut(this, "CTRL+2", [this] { this->notebook_.selectIndex(1); }); - createWindowShortcut(this, "CTRL+3", [this] { this->notebook_.selectIndex(2); }); - createWindowShortcut(this, "CTRL+4", [this] { this->notebook_.selectIndex(3); }); - createWindowShortcut(this, "CTRL+5", [this] { this->notebook_.selectIndex(4); }); - createWindowShortcut(this, "CTRL+6", [this] { this->notebook_.selectIndex(5); }); - createWindowShortcut(this, "CTRL+7", [this] { this->notebook_.selectIndex(6); }); - createWindowShortcut(this, "CTRL+8", [this] { this->notebook_.selectIndex(7); }); - createWindowShortcut(this, "CTRL+9", [this] { this->notebook_.selectIndex(8); }); + createWindowShortcut(this, "CTRL+1", + [this] { this->notebook_.selectIndex(0); }); + createWindowShortcut(this, "CTRL+2", + [this] { this->notebook_.selectIndex(1); }); + createWindowShortcut(this, "CTRL+3", + [this] { this->notebook_.selectIndex(2); }); + createWindowShortcut(this, "CTRL+4", + [this] { this->notebook_.selectIndex(3); }); + createWindowShortcut(this, "CTRL+5", + [this] { this->notebook_.selectIndex(4); }); + createWindowShortcut(this, "CTRL+6", + [this] { this->notebook_.selectIndex(5); }); + createWindowShortcut(this, "CTRL+7", + [this] { this->notebook_.selectIndex(6); }); + createWindowShortcut(this, "CTRL+8", + [this] { this->notebook_.selectIndex(7); }); + createWindowShortcut(this, "CTRL+9", + [this] { this->notebook_.selectIndex(8); }); // Zoom in { auto s = new QShortcut(QKeySequence::ZoomIn, this); s->setContext(Qt::WindowShortcut); QObject::connect(s, &QShortcut::activated, this, [] { - getApp()->settings->uiScale.setValue( - WindowManager::clampUiScale(getApp()->settings->uiScale.getValue() + 1)); + getApp()->settings->uiScale.setValue(WindowManager::clampUiScale( + getApp()->settings->uiScale.getValue() + 1)); }); } @@ -274,16 +289,18 @@ void Window::addShortcuts() auto s = new QShortcut(QKeySequence::ZoomOut, this); s->setContext(Qt::WindowShortcut); QObject::connect(s, &QShortcut::activated, this, [] { - getApp()->settings->uiScale.setValue( - WindowManager::clampUiScale(getApp()->settings->uiScale.getValue() - 1)); + getApp()->settings->uiScale.setValue(WindowManager::clampUiScale( + getApp()->settings->uiScale.getValue() - 1)); }); } // New tab - createWindowShortcut(this, "CTRL+SHIFT+T", [this] { this->notebook_.addPage(true); }); + createWindowShortcut(this, "CTRL+SHIFT+T", + [this] { this->notebook_.addPage(true); }); // Close tab - createWindowShortcut(this, "CTRL+SHIFT+W", [this] { this->notebook_.removeCurrentPage(); }); + createWindowShortcut(this, "CTRL+SHIFT+W", + [this] { this->notebook_.removeCurrentPage(); }); } void Window::onAccountSelected() diff --git a/src/widgets/dialogs/EmotePopup.cpp b/src/widgets/dialogs/EmotePopup.cpp index aa33925e3..74ce5951e 100644 --- a/src/widgets/dialogs/EmotePopup.cpp +++ b/src/widgets/dialogs/EmotePopup.cpp @@ -19,9 +19,11 @@ EmotePopup::EmotePopup() this->viewEmojis_ = new ChannelView(); this->viewEmotes_->setOverrideFlags(MessageElement::Flags( - MessageElement::Default | MessageElement::AlwaysShow | MessageElement::EmoteImages)); + MessageElement::Default | MessageElement::AlwaysShow | + MessageElement::EmoteImages)); this->viewEmojis_->setOverrideFlags(MessageElement::Flags( - MessageElement::Default | MessageElement::AlwaysShow | MessageElement::EmoteImages)); + MessageElement::Default | MessageElement::AlwaysShow | + MessageElement::EmoteImages)); this->viewEmotes_->setEnableScrollingToBottom(false); this->viewEmojis_->setEnableScrollingToBottom(false); @@ -56,7 +58,8 @@ void EmotePopup::loadChannel(ChannelPtr _channel) ChannelPtr emoteChannel(new Channel("", Channel::Type::None)); - auto addEmotes = [&](const EmoteMap &map, const QString &title, const QString &emoteDesc) { + auto addEmotes = [&](const EmoteMap &map, const QString &title, + const QString &emoteDesc) { // TITLE MessageBuilder builder1; @@ -71,8 +74,10 @@ void EmotePopup::loadChannel(ChannelPtr _channel) builder2.getMessage()->flags |= Message::DisableCompactEmotes; for (auto emote : map) { - builder2.append((new EmoteElement(emote.second, MessageElement::Flags::AlwaysShow)) - ->setLink(Link(Link::InsertText, emote.first.string))); + builder2.append( + (new EmoteElement(emote.second, + MessageElement::Flags::AlwaysShow)) + ->setLink(Link(Link::InsertText, emote.first.string))); } emoteChannel->addMessage(builder2.getMessage()); @@ -80,9 +85,10 @@ void EmotePopup::loadChannel(ChannelPtr _channel) auto app = getApp(); - // fourtf: the entire emote manager needs to be refactored so there's no point in trying to - // fix this pile of garbage - for (const auto &set : app->accounts->twitch.getCurrent()->accessEmotes()->emoteSets) { + // fourtf: the entire emote manager needs to be refactored so there's no + // point in trying to fix this pile of garbage + for (const auto &set : + app->accounts->twitch.getCurrent()->accessEmotes()->emoteSets) { // TITLE MessageBuilder builder1; @@ -109,18 +115,21 @@ void EmotePopup::loadChannel(ChannelPtr _channel) for (const auto &emote : set->emotes) { builder2.append( - (new EmoteElement(app->emotes->twitch.getOrCreateEmote(emote.id, emote.name), - MessageElement::Flags::AlwaysShow)) + (new EmoteElement( + app->emotes->twitch.getOrCreateEmote(emote.id, emote.name), + MessageElement::Flags::AlwaysShow)) ->setLink(Link(Link::InsertText, emote.name.string))); } emoteChannel->addMessage(builder2.getMessage()); } - addEmotes(*app->emotes->bttv.accessGlobalEmotes(), "BetterTTV Global Emotes", - "BetterTTV Global Emote"); - addEmotes(*channel->accessBttvEmotes(), "BetterTTV Channel Emotes", "BetterTTV Channel Emote"); - // addEmotes(*app->emotes->ffz.accessGlobalEmotes(), "FrankerFaceZ Global Emotes", + addEmotes(*app->emotes->bttv.accessGlobalEmotes(), + "BetterTTV Global Emotes", "BetterTTV Global Emote"); + addEmotes(*channel->accessBttvEmotes(), "BetterTTV Channel Emotes", + "BetterTTV Channel Emote"); + // addEmotes(*app->emotes->ffz.accessGlobalEmotes(), "FrankerFaceZ Global + // Emotes", // "FrankerFaceZ Global Emote"); addEmotes(*channel->accessFfzEmotes(), "FrankerFaceZ Channel Emotes", "FrankerFaceZ Channel Emote"); @@ -149,7 +158,8 @@ void EmotePopup::loadEmojis() emojis.each([&builder](const auto &key, const auto &value) { builder.append( (new EmoteElement(value->emote, MessageElement::Flags::AlwaysShow)) - ->setLink(Link(Link::Type::InsertText, ":" + value->shortCodes[0] + ":"))); + ->setLink(Link(Link::Type::InsertText, + ":" + value->shortCodes[0] + ":"))); }); emojiChannel->addMessage(builder.getMessage()); diff --git a/src/widgets/dialogs/LastRunCrashDialog.cpp b/src/widgets/dialogs/LastRunCrashDialog.cpp index 0e46702c6..e67483909 100644 --- a/src/widgets/dialogs/LastRunCrashDialog.cpp +++ b/src/widgets/dialogs/LastRunCrashDialog.cpp @@ -16,10 +16,11 @@ LastRunCrashDialog::LastRunCrashDialog() this->setWindowFlag(Qt::WindowContextHelpButtonHint, false); this->setWindowTitle("Chatterino"); - auto layout = LayoutCreator(this).setLayoutType(); + auto layout = + LayoutCreator(this).setLayoutType(); - layout.emplace( - "The application wasn't terminated properly the last time it was executed."); + layout.emplace("The application wasn't terminated properly the " + "last time it was executed."); layout->addSpacing(16); @@ -28,7 +29,8 @@ LastRunCrashDialog::LastRunCrashDialog() // auto *installUpdateButton = buttons->addButton("Install Update", // QDialogButtonBox::NoRole); installUpdateButton->setEnabled(false); - // QObject::connect(installUpdateButton, &QPushButton::clicked, [this, update]() mutable { + // QObject::connect(installUpdateButton, &QPushButton::clicked, [this, + // update]() mutable { // auto &updateManager = UpdateManager::getInstance(); // updateManager.installUpdates(); @@ -36,8 +38,10 @@ LastRunCrashDialog::LastRunCrashDialog() // update->setText("Downloading updates..."); // }); - auto *okButton = buttons->addButton("Ignore", QDialogButtonBox::ButtonRole::NoRole); - QObject::connect(okButton, &QPushButton::clicked, [this] { this->accept(); }); + auto *okButton = + buttons->addButton("Ignore", QDialogButtonBox::ButtonRole::NoRole); + QObject::connect(okButton, &QPushButton::clicked, + [this] { this->accept(); }); // Updates // auto updateUpdateLabel = [update]() mutable { @@ -57,28 +61,31 @@ LastRunCrashDialog::LastRunCrashDialog() // update->setText("No update abailable."); // } break; // case UpdateManager::SearchFailed: { - // update->setText("Error while searching for update.\nEither the update service - // is " - // "temporarily down or there is an issue with your - // installation."); + // update->setText("Error while searching for update.\nEither + // the update service is " + // "temporarily down or there is an issue + // with your installation."); // } break; // case UpdateManager::Downloading: { // update->setText( - // "Downloading the update. Chatterino will close once the download is - // done."); + // "Downloading the update. Chatterino will close once + // the download is done."); // } break; // case UpdateManager::DownloadFailed: { // update->setText("Download failed."); // } break; // case UpdateManager::WriteFileFailed: { - // update->setText("Writing the update file to the hard drive failed."); + // update->setText("Writing the update file to the hard drive + // failed."); // } break; // } // }; // updateUpdateLabel(); - // this->managedConnect(updateManager.statusUpdated, [updateUpdateLabel](auto) mutable { - // postToThread([updateUpdateLabel]() mutable { updateUpdateLabel(); }); + // this->managedConnect(updateManager.statusUpdated, + // [updateUpdateLabel](auto) mutable { + // postToThread([updateUpdateLabel]() mutable { updateUpdateLabel(); + // }); // }); } diff --git a/src/widgets/dialogs/LoginDialog.cpp b/src/widgets/dialogs/LoginDialog.cpp index bee0e87b0..1b73f143f 100644 --- a/src/widgets/dialogs/LoginDialog.cpp +++ b/src/widgets/dialogs/LoginDialog.cpp @@ -20,8 +20,10 @@ namespace chatterino { namespace { -void LogInWithCredentials(const std::string &userID, const std::string &username, - const std::string &clientID, const std::string &oauthToken) +void LogInWithCredentials(const std::string &userID, + const std::string &username, + const std::string &clientID, + const std::string &oauthToken) { QStringList errors; @@ -49,12 +51,16 @@ void LogInWithCredentials(const std::string &userID, const std::string &username // QMessageBox messageBox; // messageBox.setIcon(QMessageBox::Information); - // messageBox.setText("Successfully logged in with user " + qS(username) + "!"); - pajlada::Settings::Setting::set("/accounts/uid" + userID + "/username", username); - pajlada::Settings::Setting::set("/accounts/uid" + userID + "/userID", userID); - pajlada::Settings::Setting::set("/accounts/uid" + userID + "/clientID", clientID); - pajlada::Settings::Setting::set("/accounts/uid" + userID + "/oauthToken", - oauthToken); + // messageBox.setText("Successfully logged in with user " + + // qS(username) + "!"); + pajlada::Settings::Setting::set( + "/accounts/uid" + userID + "/username", username); + pajlada::Settings::Setting::set( + "/accounts/uid" + userID + "/userID", userID); + pajlada::Settings::Setting::set( + "/accounts/uid" + userID + "/clientID", clientID); + pajlada::Settings::Setting::set( + "/accounts/uid" + userID + "/oauthToken", oauthToken); getApp()->accounts->twitch.reloadUsers(); @@ -142,46 +148,59 @@ AdvancedLoginWidget::AdvancedLoginWidget() this->ui_.oauthTokenInput.setEchoMode(QLineEdit::Password); - connect(&this->ui_.userIDInput, &QLineEdit::textChanged, [=]() { this->refreshButtons(); }); - connect(&this->ui_.usernameInput, &QLineEdit::textChanged, [=]() { this->refreshButtons(); }); - connect(&this->ui_.clientIDInput, &QLineEdit::textChanged, [=]() { this->refreshButtons(); }); - connect(&this->ui_.oauthTokenInput, &QLineEdit::textChanged, [=]() { this->refreshButtons(); }); + connect(&this->ui_.userIDInput, &QLineEdit::textChanged, + [=]() { this->refreshButtons(); }); + connect(&this->ui_.usernameInput, &QLineEdit::textChanged, + [=]() { this->refreshButtons(); }); + connect(&this->ui_.clientIDInput, &QLineEdit::textChanged, + [=]() { this->refreshButtons(); }); + connect(&this->ui_.oauthTokenInput, &QLineEdit::textChanged, + [=]() { this->refreshButtons(); }); /// Upper button row this->ui_.buttonUpperRow.addUserButton.setText("Add user"); this->ui_.buttonUpperRow.clearFieldsButton.setText("Clear fields"); - this->ui_.buttonUpperRow.layout.addWidget(&this->ui_.buttonUpperRow.addUserButton); - this->ui_.buttonUpperRow.layout.addWidget(&this->ui_.buttonUpperRow.clearFieldsButton); + this->ui_.buttonUpperRow.layout.addWidget( + &this->ui_.buttonUpperRow.addUserButton); + this->ui_.buttonUpperRow.layout.addWidget( + &this->ui_.buttonUpperRow.clearFieldsButton); - connect(&this->ui_.buttonUpperRow.clearFieldsButton, &QPushButton::clicked, [=]() { - this->ui_.userIDInput.clear(); - this->ui_.usernameInput.clear(); - this->ui_.clientIDInput.clear(); - this->ui_.oauthTokenInput.clear(); - }); + connect(&this->ui_.buttonUpperRow.clearFieldsButton, &QPushButton::clicked, + [=]() { + this->ui_.userIDInput.clear(); + this->ui_.usernameInput.clear(); + this->ui_.clientIDInput.clear(); + this->ui_.oauthTokenInput.clear(); + }); - connect(&this->ui_.buttonUpperRow.addUserButton, &QPushButton::clicked, [=]() { - std::string userID = this->ui_.userIDInput.text().toStdString(); - std::string username = this->ui_.usernameInput.text().toStdString(); - std::string clientID = this->ui_.clientIDInput.text().toStdString(); - std::string oauthToken = this->ui_.oauthTokenInput.text().toStdString(); + connect( + &this->ui_.buttonUpperRow.addUserButton, &QPushButton::clicked, [=]() { + std::string userID = this->ui_.userIDInput.text().toStdString(); + std::string username = this->ui_.usernameInput.text().toStdString(); + std::string clientID = this->ui_.clientIDInput.text().toStdString(); + std::string oauthToken = + this->ui_.oauthTokenInput.text().toStdString(); - LogInWithCredentials(userID, username, clientID, oauthToken); - }); + LogInWithCredentials(userID, username, clientID, oauthToken); + }); /// Lower button row - this->ui_.buttonLowerRow.fillInUserIDButton.setText("Get user ID from username"); + this->ui_.buttonLowerRow.fillInUserIDButton.setText( + "Get user ID from username"); - this->ui_.buttonLowerRow.layout.addWidget(&this->ui_.buttonLowerRow.fillInUserIDButton); + this->ui_.buttonLowerRow.layout.addWidget( + &this->ui_.buttonLowerRow.fillInUserIDButton); - connect(&this->ui_.buttonLowerRow.fillInUserIDButton, &QPushButton::clicked, [=]() { - const auto onIdFetched = [=](const QString &userID) { - this->ui_.userIDInput.setText(userID); // - }; - PartialTwitchUser::byName(this->ui_.usernameInput.text()).getId(onIdFetched, this); - }); + connect(&this->ui_.buttonLowerRow.fillInUserIDButton, &QPushButton::clicked, + [=]() { + const auto onIdFetched = [=](const QString &userID) { + this->ui_.userIDInput.setText(userID); // + }; + PartialTwitchUser::byName(this->ui_.usernameInput.text()) + .getId(onIdFetched, this); + }); } void AdvancedLoginWidget::refreshButtons() @@ -189,8 +208,10 @@ void AdvancedLoginWidget::refreshButtons() this->ui_.buttonLowerRow.fillInUserIDButton.setEnabled( !this->ui_.usernameInput.text().isEmpty()); - if (this->ui_.userIDInput.text().isEmpty() || this->ui_.usernameInput.text().isEmpty() || - this->ui_.clientIDInput.text().isEmpty() || this->ui_.oauthTokenInput.text().isEmpty()) { + if (this->ui_.userIDInput.text().isEmpty() || + this->ui_.usernameInput.text().isEmpty() || + this->ui_.clientIDInput.text().isEmpty() || + this->ui_.oauthTokenInput.text().isEmpty()) { this->ui_.buttonUpperRow.addUserButton.setEnabled(false); } else { this->ui_.buttonUpperRow.addUserButton.setEnabled(true); @@ -214,9 +235,10 @@ LoginWidget::LoginWidget() this->ui_.buttonBox.setStandardButtons(QDialogButtonBox::Close); - QObject::connect(&this->ui_.buttonBox, &QDialogButtonBox::rejected, [this]() { - this->close(); // - }); + QObject::connect(&this->ui_.buttonBox, &QDialogButtonBox::rejected, + [this]() { + this->close(); // + }); this->ui_.mainLayout.addWidget(&this->ui_.buttonBox); } diff --git a/src/widgets/dialogs/LogsPopup.cpp b/src/widgets/dialogs/LogsPopup.cpp index 5caa14fa2..218505a2d 100644 --- a/src/widgets/dialogs/LogsPopup.cpp +++ b/src/widgets/dialogs/LogsPopup.cpp @@ -35,7 +35,8 @@ void LogsPopup::setInfo(ChannelPtr channel, QString userName) this->channel_ = channel; this->userName_ = userName; this->getRoomID(); - this->setWindowTitle(this->userName_ + "'s logs in #" + this->channel_->getName()); + this->setWindowTitle(this->userName_ + "'s logs in #" + + this->channel_->getName()); this->getLogviewerLogs(); } @@ -49,7 +50,8 @@ void LogsPopup::setMessages(std::vector &messages) void LogsPopup::getRoomID() { - TwitchChannel *twitchChannel = dynamic_cast(this->channel_.get()); + TwitchChannel *twitchChannel = + dynamic_cast(this->channel_.get()); if (twitchChannel == nullptr) { return; } @@ -78,7 +80,8 @@ void LogsPopup::getRoomID() void LogsPopup::getLogviewerLogs() { - TwitchChannel *twitchChannel = dynamic_cast(this->channel_.get()); + TwitchChannel *twitchChannel = + dynamic_cast(this->channel_.get()); if (twitchChannel == nullptr) { return; } @@ -108,12 +111,15 @@ void LogsPopup::getLogviewerLogs() // Hacky way to fix the timestamp message.insert(1, "historical=1;"); - message.insert(1, QString("tmi-sent-ts=%10000;").arg(messageObject["time"].toInt())); + message.insert(1, QString("tmi-sent-ts=%10000;") + .arg(messageObject["time"].toInt())); message.insert(1, QString("room-id=%1;").arg(this->roomID_)); MessageParseArgs args; - auto ircMessage = Communi::IrcMessage::fromData(message.toUtf8(), nullptr); - auto privMsg = static_cast(ircMessage); + auto ircMessage = + Communi::IrcMessage::fromData(message.toUtf8(), nullptr); + auto privMsg = + static_cast(ircMessage); TwitchMessageBuilder builder(this->channel_.get(), privMsg, args); messages.push_back(builder.build()); }; @@ -127,22 +133,25 @@ void LogsPopup::getLogviewerLogs() void LogsPopup::getOverrustleLogs() { - TwitchChannel *twitchChannel = dynamic_cast(this->channel_.get()); + TwitchChannel *twitchChannel = + dynamic_cast(this->channel_.get()); if (twitchChannel == nullptr) { return; } QString channelName = twitchChannel->getName(); - QString url = QString("https://overrustlelogs.net/api/v1/stalk/%1/%2.json?limit=500") - .arg(channelName, this->userName_); + QString url = + QString("https://overrustlelogs.net/api/v1/stalk/%1/%2.json?limit=500") + .arg(channelName, this->userName_); NetworkRequest req(url); req.setCaller(QThread::currentThread()); req.onError([this, channelName](int errorCode) { this->close(); - QMessageBox *box = new QMessageBox(QMessageBox::Information, "Error getting logs", - "No logs could be found for channel " + channelName); + QMessageBox *box = new QMessageBox( + QMessageBox::Information, "Error getting logs", + "No logs could be found for channel " + channelName); box->setAttribute(Qt::WA_DeleteOnClose); box->show(); box->raise(); @@ -157,15 +166,18 @@ void LogsPopup::getOverrustleLogs() QJsonArray dataMessages = data.value("lines").toArray(); for (auto i : dataMessages) { QJsonObject singleMessage = i.toObject(); - QTime timeStamp = - QDateTime::fromSecsSinceEpoch(singleMessage.value("timestamp").toInt()).time(); + QTime timeStamp = QDateTime::fromSecsSinceEpoch( + singleMessage.value("timestamp").toInt()) + .time(); MessagePtr message(new Message); message->addElement(new TimestampElement(timeStamp)); - message->addElement(new TextElement(this->userName_, MessageElement::Username, + message->addElement(new TextElement(this->userName_, + MessageElement::Username, MessageColor::System)); - message->addElement(new TextElement(singleMessage.value("text").toString(), - MessageElement::Text, MessageColor::Text)); + message->addElement( + new TextElement(singleMessage.value("text").toString(), + MessageElement::Text, MessageColor::Text)); messages.push_back(message); } } diff --git a/src/widgets/dialogs/NotificationPopup.cpp b/src/widgets/dialogs/NotificationPopup.cpp index cc7a46abf..46ff7e8af 100644 --- a/src/widgets/dialogs/NotificationPopup.cpp +++ b/src/widgets/dialogs/NotificationPopup.cpp @@ -33,7 +33,8 @@ void NotificationPopup::updatePosition() switch (location) { case BottomRight: { - this->move(rect.right() - this->width(), rect.bottom() - this->height()); + this->move(rect.right() - this->width(), + rect.bottom() - this->height()); } break; } } diff --git a/src/widgets/dialogs/QualityPopup.cpp b/src/widgets/dialogs/QualityPopup.cpp index faaf95b3f..c4f9c4ceb 100644 --- a/src/widgets/dialogs/QualityPopup.cpp +++ b/src/widgets/dialogs/QualityPopup.cpp @@ -15,7 +15,8 @@ QualityPopup::QualityPopup(const QString &_channelName, QStringList options) QObject::connect(&this->ui_.cancelButton, &QPushButton::clicked, this, &QualityPopup::cancelButtonClicked); - this->ui_.buttonBox.addButton(&this->ui_.okButton, QDialogButtonBox::ButtonRole::AcceptRole); + this->ui_.buttonBox.addButton(&this->ui_.okButton, + QDialogButtonBox::ButtonRole::AcceptRole); this->ui_.buttonBox.addButton(&this->ui_.cancelButton, QDialogButtonBox::ButtonRole::RejectRole); diff --git a/src/widgets/dialogs/SelectChannelDialog.cpp b/src/widgets/dialogs/SelectChannelDialog.cpp index 4f026112b..367b626b2 100644 --- a/src/widgets/dialogs/SelectChannelDialog.cpp +++ b/src/widgets/dialogs/SelectChannelDialog.cpp @@ -33,17 +33,20 @@ SelectChannelDialog::SelectChannelDialog(QWidget *parent) auto vbox = obj.setLayoutType(); // channel_btn - auto channel_btn = vbox.emplace("Channel").assign(&this->ui_.twitch.channel); - auto channel_lbl = vbox.emplace("Join a twitch channel by its name.").hidden(); + auto channel_btn = vbox.emplace("Channel").assign( + &this->ui_.twitch.channel); + auto channel_lbl = + vbox.emplace("Join a twitch channel by its name.").hidden(); channel_lbl->setWordWrap(true); - auto channel_edit = - vbox.emplace().hidden().assign(&this->ui_.twitch.channelName); + auto channel_edit = vbox.emplace().hidden().assign( + &this->ui_.twitch.channelName); QObject::connect(channel_btn.getElement(), &QRadioButton::toggled, [=](bool enabled) mutable { if (enabled) { channel_edit->setFocus(); - channel_edit->setSelection(0, channel_edit->text().length()); + channel_edit->setSelection( + 0, channel_edit->text().length()); } channel_edit->setVisible(enabled); @@ -54,50 +57,60 @@ SelectChannelDialog::SelectChannelDialog(QWidget *parent) channel_edit->installEventFilter(&this->tabFilter_); // whispers_btn - auto whispers_btn = - vbox.emplace("Whispers").assign(&this->ui_.twitch.whispers); + auto whispers_btn = vbox.emplace("Whispers") + .assign(&this->ui_.twitch.whispers); auto whispers_lbl = - vbox.emplace("Shows the whispers that you receive while chatterino is running.") + vbox.emplace("Shows the whispers that you receive while " + "chatterino is running.") .hidden(); whispers_lbl->setWordWrap(true); whispers_btn->installEventFilter(&this->tabFilter_); - QObject::connect(whispers_btn.getElement(), &QRadioButton::toggled, - [=](bool enabled) mutable { whispers_lbl->setVisible(enabled); }); + QObject::connect( + whispers_btn.getElement(), &QRadioButton::toggled, + [=](bool enabled) mutable { whispers_lbl->setVisible(enabled); }); // mentions_btn - auto mentions_btn = - vbox.emplace("Mentions").assign(&this->ui_.twitch.mentions); + auto mentions_btn = vbox.emplace("Mentions") + .assign(&this->ui_.twitch.mentions); auto mentions_lbl = - vbox.emplace("Shows all the messages that highlight you from any channel.") + vbox.emplace("Shows all the messages that highlight you " + "from any channel.") .hidden(); mentions_lbl->setWordWrap(true); mentions_btn->installEventFilter(&this->tabFilter_); - QObject::connect(mentions_btn.getElement(), &QRadioButton::toggled, - [=](bool enabled) mutable { mentions_lbl->setVisible(enabled); }); + QObject::connect( + mentions_btn.getElement(), &QRadioButton::toggled, + [=](bool enabled) mutable { mentions_lbl->setVisible(enabled); }); // watching_btn - auto watching_btn = - vbox.emplace("Watching").assign(&this->ui_.twitch.watching); + auto watching_btn = vbox.emplace("Watching") + .assign(&this->ui_.twitch.watching); auto watching_lbl = - vbox.emplace("Requires the chatterino browser extension.").hidden(); + vbox.emplace("Requires the chatterino browser extension.") + .hidden(); watching_lbl->setWordWrap(true); watching_btn->installEventFilter(&this->tabFilter_); - QObject::connect(watching_btn.getElement(), &QRadioButton::toggled, - [=](bool enabled) mutable { watching_lbl->setVisible(enabled); }); + QObject::connect( + watching_btn.getElement(), &QRadioButton::toggled, + [=](bool enabled) mutable { watching_lbl->setVisible(enabled); }); vbox->addStretch(1); // tabbing order - QWidget::setTabOrder(watching_btn.getElement(), channel_btn.getElement()); - QWidget::setTabOrder(channel_btn.getElement(), whispers_btn.getElement()); - QWidget::setTabOrder(whispers_btn.getElement(), mentions_btn.getElement()); - QWidget::setTabOrder(mentions_btn.getElement(), watching_btn.getElement()); + QWidget::setTabOrder(watching_btn.getElement(), + channel_btn.getElement()); + QWidget::setTabOrder(channel_btn.getElement(), + whispers_btn.getElement()); + QWidget::setTabOrder(whispers_btn.getElement(), + mentions_btn.getElement()); + QWidget::setTabOrder(mentions_btn.getElement(), + watching_btn.getElement()); // tab NotebookTab *tab = notebook->addPage(obj.getElement()); @@ -117,12 +130,15 @@ SelectChannelDialog::SelectChannelDialog(QWidget *parent) layout->setStretchFactor(notebook.getElement(), 1); - auto buttons = layout.emplace().emplace(this); + auto buttons = + layout.emplace().emplace(this); { auto *button_ok = buttons->addButton(QDialogButtonBox::Ok); - QObject::connect(button_ok, &QPushButton::clicked, [=](bool) { this->ok(); }); + QObject::connect(button_ok, &QPushButton::clicked, + [=](bool) { this->ok(); }); auto *button_cancel = buttons->addButton(QDialogButtonBox::Cancel); - QObject::connect(button_cancel, &QAbstractButton::clicked, [=](bool) { this->close(); }); + QObject::connect(button_cancel, &QAbstractButton::clicked, + [=](bool) { this->close(); }); } this->setScaleIndependantSize(300, 210); @@ -133,7 +149,8 @@ SelectChannelDialog::SelectChannelDialog(QWidget *parent) auto *shortcut_ok = new QShortcut(QKeySequence("Return"), this); QObject::connect(shortcut_ok, &QShortcut::activated, [=] { this->ok(); }); auto *shortcut_cancel = new QShortcut(QKeySequence("Esc"), this); - QObject::connect(shortcut_cancel, &QShortcut::activated, [=] { this->close(); }); + QObject::connect(shortcut_cancel, &QShortcut::activated, + [=] { this->close(); }); } void SelectChannelDialog::ok() @@ -188,7 +205,8 @@ IndirectChannel SelectChannelDialog::getSelectedChannel() const switch (this->ui_.notebook->getSelectedIndex()) { case TAB_TWITCH: { if (this->ui_.twitch.channel->isChecked()) { - return app->twitch.server->getOrAddChannel(this->ui_.twitch.channelName->text()); + return app->twitch.server->getOrAddChannel( + this->ui_.twitch.channelName->text()); } else if (this->ui_.twitch.watching->isChecked()) { return app->twitch.server->watchingChannel; } else if (this->ui_.twitch.mentions->isChecked()) { @@ -207,7 +225,8 @@ bool SelectChannelDialog::hasSeletedChannel() const return this->hasSelectedChannel_; } -bool SelectChannelDialog::EventFilter::eventFilter(QObject *watched, QEvent *event) +bool SelectChannelDialog::EventFilter::eventFilter(QObject *watched, + QEvent *event) { auto *widget = (QWidget *)watched; @@ -225,7 +244,8 @@ bool SelectChannelDialog::EventFilter::eventFilter(QObject *watched, QEvent *eve return false; } else if (event->type() == QEvent::KeyPress) { QKeyEvent *event_key = static_cast(event); - if ((event_key->key() == Qt::Key_Tab || event_key->key() == Qt::Key_Down) && + if ((event_key->key() == Qt::Key_Tab || + event_key->key() == Qt::Key_Down) && event_key->modifiers() == Qt::NoModifier) { if (widget == this->dialog->ui_.twitch.channelName) { this->dialog->ui_.twitch.whispers->setFocus(); @@ -234,9 +254,11 @@ bool SelectChannelDialog::EventFilter::eventFilter(QObject *watched, QEvent *eve widget->nextInFocusChain()->setFocus(); } return true; - } else if (((event_key->key() == Qt::Key_Tab || event_key->key() == Qt::Key_Backtab) && + } else if (((event_key->key() == Qt::Key_Tab || + event_key->key() == Qt::Key_Backtab) && event_key->modifiers() == Qt::ShiftModifier) || - ((event_key->key() == Qt::Key_Up) && event_key->modifiers() == Qt::NoModifier)) { + ((event_key->key() == Qt::Key_Up) && + event_key->modifiers() == Qt::NoModifier)) { if (widget == this->dialog->ui_.twitch.channelName) { this->dialog->ui_.twitch.watching->setFocus(); return true; @@ -253,7 +275,8 @@ bool SelectChannelDialog::EventFilter::eventFilter(QObject *watched, QEvent *eve return true; } else if (event->type() == QEvent::KeyRelease) { QKeyEvent *event_key = static_cast(event); - if ((event_key->key() == Qt::Key_Backtab || event_key->key() == Qt::Key_Down) && + if ((event_key->key() == Qt::Key_Backtab || + event_key->key() == Qt::Key_Down) && event_key->modifiers() == Qt::NoModifier) { return true; } @@ -272,9 +295,11 @@ void SelectChannelDialog::themeChangedEvent() BaseWindow::themeChangedEvent(); if (this->theme->isLightTheme()) { - this->setStyleSheet("QRadioButton { color: #000 } QLabel { color: #000 }"); + this->setStyleSheet( + "QRadioButton { color: #000 } QLabel { color: #000 }"); } else { - this->setStyleSheet("QRadioButton { color: #fff } QLabel { color: #fff }"); + this->setStyleSheet( + "QRadioButton { color: #fff } QLabel { color: #fff }"); } } diff --git a/src/widgets/dialogs/SettingsDialog.cpp b/src/widgets/dialogs/SettingsDialog.cpp index 72100be2a..8f0746ccb 100644 --- a/src/widgets/dialogs/SettingsDialog.cpp +++ b/src/widgets/dialogs/SettingsDialog.cpp @@ -49,12 +49,16 @@ void SettingsDialog::initUi() // right side layout auto right = layoutCreator.emplace().withoutMargin(); { - right.emplace().assign(&this->ui_.pageStack).withoutMargin(); + right.emplace() + .assign(&this->ui_.pageStack) + .withoutMargin(); auto buttons = right.emplace(Qt::Horizontal); { - this->ui_.okButton = buttons->addButton("Ok", QDialogButtonBox::YesRole); - this->ui_.cancelButton = buttons->addButton("Cancel", QDialogButtonBox::NoRole); + this->ui_.okButton = + buttons->addButton("Ok", QDialogButtonBox::YesRole); + this->ui_.cancelButton = + buttons->addButton("Cancel", QDialogButtonBox::NoRole); } } @@ -62,7 +66,8 @@ void SettingsDialog::initUi() this->ui_.tabContainerContainer->setObjectName("tabWidget"); this->ui_.pageStack->setObjectName("pages"); - QObject::connect(this->ui_.okButton, &QPushButton::clicked, this, &SettingsDialog::onOkClicked); + QObject::connect(this->ui_.okButton, &QPushButton::clicked, this, + &SettingsDialog::onOkClicked); QObject::connect(this->ui_.cancelButton, &QPushButton::clicked, this, &SettingsDialog::onCancelClicked); } diff --git a/src/widgets/dialogs/SettingsDialog.hpp b/src/widgets/dialogs/SettingsDialog.hpp index 2b6b960af..03ddc90a5 100644 --- a/src/widgets/dialogs/SettingsDialog.hpp +++ b/src/widgets/dialogs/SettingsDialog.hpp @@ -25,7 +25,8 @@ public: Accounts, }; - static void showDialog(PreferredTab preferredTab = PreferredTab::NoPreference); + static void showDialog( + PreferredTab preferredTab = PreferredTab::NoPreference); protected: virtual void scaleChangedEvent(float newDpi) override; diff --git a/src/widgets/dialogs/TextInputDialog.cpp b/src/widgets/dialogs/TextInputDialog.cpp index 7d13a3c46..f42c8fe93 100644 --- a/src/widgets/dialogs/TextInputDialog.cpp +++ b/src/widgets/dialogs/TextInputDialog.cpp @@ -15,13 +15,16 @@ TextInputDialog::TextInputDialog(QWidget *parent) this->buttonBox_.addWidget(&okButton_); this->buttonBox_.addWidget(&cancelButton_); - QObject::connect(&this->okButton_, SIGNAL(clicked()), this, SLOT(okButtonClicked())); - QObject::connect(&this->cancelButton_, SIGNAL(clicked()), this, SLOT(cancelButtonClicked())); + QObject::connect(&this->okButton_, SIGNAL(clicked()), this, + SLOT(okButtonClicked())); + QObject::connect(&this->cancelButton_, SIGNAL(clicked()), this, + SLOT(cancelButtonClicked())); this->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); - this->setWindowFlags((this->windowFlags() & ~(Qt::WindowContextHelpButtonHint)) | Qt::Dialog | - Qt::MSWindowsFixedSizeDialogHint); + this->setWindowFlags( + (this->windowFlags() & ~(Qt::WindowContextHelpButtonHint)) | + Qt::Dialog | Qt::MSWindowsFixedSizeDialogHint); } QString TextInputDialog::getText() const diff --git a/src/widgets/dialogs/UpdateDialog.cpp b/src/widgets/dialogs/UpdateDialog.cpp index 1b66177e7..de065283f 100644 --- a/src/widgets/dialogs/UpdateDialog.cpp +++ b/src/widgets/dialogs/UpdateDialog.cpp @@ -11,27 +11,32 @@ namespace chatterino { UpdateDialog::UpdateDialog() - : BaseWindow(nullptr, BaseWindow::Flags(BaseWindow::Frameless | BaseWindow::TopMost | - BaseWindow::EnableCustomFrame)) + : BaseWindow(nullptr, + BaseWindow::Flags(BaseWindow::Frameless | BaseWindow::TopMost | + BaseWindow::EnableCustomFrame)) { - auto layout = LayoutCreator(this).setLayoutType(); + auto layout = + LayoutCreator(this).setLayoutType(); - layout.emplace