#include "singletons/Settings.hpp" #include "Application.hpp" #include "controllers/filters/FilterRecord.hpp" #include "controllers/highlights/HighlightBadge.hpp" #include "controllers/highlights/HighlightBlacklistUser.hpp" #include "controllers/highlights/HighlightPhrase.hpp" #include "controllers/ignores/IgnorePhrase.hpp" #include "controllers/moderationactions/ModerationAction.hpp" #include "controllers/nicknames/Nickname.hpp" #include "debug/Benchmark.hpp" #include "pajlada/settings/signalargs.hpp" #include "util/WindowsHelper.hpp" #include namespace { using namespace chatterino; template void initializeSignalVector(pajlada::Signals::SignalHolder &signalHolder, ChatterinoSetting> &setting, SignalVector &vec) { // Fill the SignalVector up with initial values for (auto &&item : setting.getValue()) { vec.append(item); } // Set up a signal to signalHolder.managedConnect(vec.delayedItemsChanged, [&] { setting.setValue(vec.raw()); }); } } // namespace namespace chatterino { std::vector> _settings; void _actuallyRegisterSetting( std::weak_ptr setting) { _settings.push_back(std::move(setting)); } bool Settings::isHighlightedUser(const QString &username) { auto items = this->highlightedUsers.readOnly(); for (const auto &highlightedUser : *items) { if (highlightedUser.isMatch(username)) { return true; } } return false; } bool Settings::isBlacklistedUser(const QString &username) { auto items = this->blacklistedUsers.readOnly(); for (const auto &blacklistedUser : *items) { if (blacklistedUser.isMatch(username)) { return true; } } return false; } bool Settings::isMutedChannel(const QString &channelName) { auto items = this->mutedChannels.readOnly(); for (const auto &channel : *items) { if (channelName.toLower() == channel.toLower()) { return true; } } return false; } std::optional Settings::matchNickname(const QString &usernameText) { auto nicknames = this->nicknames.readOnly(); for (const auto &nickname : *nicknames) { if (auto nicknameText = nickname.match(usernameText)) { return nicknameText; } } return std::nullopt; } void Settings::mute(const QString &channelName) { mutedChannels.append(channelName); } void Settings::unmute(const QString &channelName) { for (std::vector::size_type i = 0; i != mutedChannels.raw().size(); i++) { if (mutedChannels.raw()[i].toLower() == channelName.toLower()) { mutedChannels.removeAt(i); i--; } } } bool Settings::toggleMutedChannel(const QString &channelName) { if (this->isMutedChannel(channelName)) { unmute(channelName); return false; } else { mute(channelName); return true; } } Settings *Settings::instance_ = nullptr; Settings::Settings(const QString &settingsDirectory) : prevInstance_(Settings::instance_) { QString settingsPath = settingsDirectory + "/settings.json"; // get global instance of the settings library auto settingsInstance = pajlada::Settings::SettingManager::getInstance(); settingsInstance->load(qPrintable(settingsPath)); settingsInstance->setBackupEnabled(true); settingsInstance->setBackupSlots(9); settingsInstance->saveMethod = pajlada::Settings::SettingManager::SaveMethod::SaveOnExit; initializeSignalVector(this->signalHolder, this->highlightedMessagesSetting, this->highlightedMessages); initializeSignalVector(this->signalHolder, this->highlightedUsersSetting, this->highlightedUsers); initializeSignalVector(this->signalHolder, this->highlightedBadgesSetting, this->highlightedBadges); initializeSignalVector(this->signalHolder, this->blacklistedUsersSetting, this->blacklistedUsers); initializeSignalVector(this->signalHolder, this->ignoredMessagesSetting, this->ignoredMessages); initializeSignalVector(this->signalHolder, this->mutedChannelsSetting, this->mutedChannels); initializeSignalVector(this->signalHolder, this->filterRecordsSetting, this->filterRecords); initializeSignalVector(this->signalHolder, this->nicknamesSetting, this->nicknames); initializeSignalVector(this->signalHolder, this->moderationActionsSetting, this->moderationActions); initializeSignalVector(this->signalHolder, this->loggedChannelsSetting, this->loggedChannels); instance_ = this; #ifdef USEWINSDK this->autorun = isRegisteredForStartup(); this->autorun.connect( [](bool autorun) { setRegisteredForStartup(autorun); }, false); #endif } Settings::~Settings() { Settings::instance_ = this->prevInstance_; } void Settings::saveSnapshot() { BenchmarkGuard benchmark("Settings::saveSnapshot"); rapidjson::Document *d = new rapidjson::Document(rapidjson::kObjectType); rapidjson::Document::AllocatorType &a = d->GetAllocator(); for (const auto &weakSetting : _settings) { auto setting = weakSetting.lock(); if (!setting) { continue; } rapidjson::Value key(setting->getPath().c_str(), a); auto *curVal = setting->unmarshalJSON(); if (curVal == nullptr) { continue; } rapidjson::Value val; val.CopyFrom(*curVal, a); d->AddMember(key.Move(), val.Move(), a); } // log("Snapshot state: {}", rj::stringify(*d)); this->snapshot_.reset(d); } void Settings::restoreSnapshot() { if (!this->snapshot_) { return; } BenchmarkGuard benchmark("Settings::restoreSnapshot"); const auto &snapshot = *(this->snapshot_.get()); if (!snapshot.IsObject()) { return; } for (const auto &weakSetting : _settings) { auto setting = weakSetting.lock(); if (!setting) { continue; } const char *path = setting->getPath().c_str(); if (!snapshot.HasMember(path)) { continue; } pajlada::Settings::SignalArgs args; args.compareBeforeSet = true; setting->marshalJSON(snapshot[path], std::move(args)); } } float Settings::getClampedUiScale() const { return std::clamp(this->uiScale.getValue(), 0.2F, 10.F); } void Settings::setClampedUiScale(float value) { this->uiScale.setValue(std::clamp(value, 0.2F, 10.F)); } Settings &Settings::instance() { assert(instance_ != nullptr); return *instance_; } Settings *getSettings() { return &Settings::instance(); } } // namespace chatterino