#include "macro-condition-folder.hpp" #include "macro-helpers.hpp" #include "layout-helpers.hpp" #include "ui-helpers.hpp" #include #include namespace advss { const std::string MacroConditionFolder::id = "folder"; bool MacroConditionFolder::_registered = MacroConditionFactory::Register( MacroConditionFolder::id, {MacroConditionFolder::Create, MacroConditionFolderEdit::Create, "AdvSceneSwitcher.condition.folder"}); static const std::map conditions = { {MacroConditionFolder::Condition::ANY, "AdvSceneSwitcher.condition.folder.condition.any"}, {MacroConditionFolder::Condition::FILE_ADD, "AdvSceneSwitcher.condition.folder.condition.fileAdd"}, {MacroConditionFolder::Condition::FILE_CHANGE, "AdvSceneSwitcher.condition.folder.condition.fileChange"}, {MacroConditionFolder::Condition::FILE_REMOVE, "AdvSceneSwitcher.condition.folder.condition.fileRemove"}, {MacroConditionFolder::Condition::FOLDER_ADD, "AdvSceneSwitcher.condition.folder.condition.folderAdd"}, {MacroConditionFolder::Condition::FOLDER_REMOVE, "AdvSceneSwitcher.condition.folder.condition.folderRemove"}, }; MacroConditionFolder::MacroConditionFolder(Macro *m) : MacroCondition(m, true) { } bool MacroConditionFolder::CheckCondition() { bool ret = _matched; if (_lastWatchedValue != _folder.UnresolvedValue()) { SetupWatcher(); } SetTempVarValues(); _newFiles.clear(); _changedFiles.clear(); _removedFiles.clear(); _newDirs.clear(); _removedDirs.clear(); _matched = false; return ret; } bool MacroConditionFolder::Save(obs_data_t *obj) const { MacroCondition::Save(obj); _folder.Save(obj, "file"); obs_data_set_bool(obj, "enableFilter", _enableFilter); _regex.Save(obj); _filter.Save(obj, "filter"); obs_data_set_int(obj, "condition", static_cast(_condition)); return true; } bool MacroConditionFolder::Load(obs_data_t *obj) { MacroCondition::Load(obj); _folder.Load(obj, "file"); _enableFilter = obs_data_get_bool(obj, "enableFilter"); _regex.Load(obj); _regex.SetEnabled(true); // Already controlled via _enableFilter _filter.Save(obj, "filter"); _condition = static_cast(obs_data_get_int(obj, "condition")); SetupWatcher(); return true; } std::string MacroConditionFolder::GetShortDesc() const { return _folder.UnresolvedValue(); } void MacroConditionFolder::SetFolder(const std::string &folder) { _folder = folder; SetupWatcher(); } static QSet getFilesInDir(const QString &path) { QSet result; for (const auto &file : QDir(path).entryList(QDir::Files)) { result << file; } return result; } static QSet getDirsInDir(const QString &path) { QSet result; for (const auto &dir : QDir(path).entryList(QDir::AllDirs)) { result << dir; } return result; } void MacroConditionFolder::DirectoryChanged(const QString &path) { if (MacroIsPaused(GetMacro())) { return; } if (_enableFilter && !_regex.Matches(path.toStdString(), _filter)) { return; } auto currentFiles = getFilesInDir(path); auto currentDirs = getDirsInDir(path); if (currentFiles.count() > _currentFiles.count()) { _newFiles += currentFiles - _currentFiles; } else { _removedFiles += _currentFiles - currentFiles; } if (currentDirs.count() > _currentDirs.count()) { _newDirs += currentDirs - _currentDirs; } else { _removedDirs += _currentDirs - currentDirs; } switch (_condition) { case Condition::ANY: _matched = true; break; case Condition::FILE_ADD: _matched = _newFiles.count() > 0; break; case Condition::FILE_REMOVE: _matched = _removedFiles.count() > 0; break; case Condition::FOLDER_ADD: _matched = _newDirs.count() > 0; break; case Condition::FOLDER_REMOVE: _matched = _removedDirs.count() > 0; break; default: break; } for (const auto &newFile : _newFiles) { _watcher->addPath(path + "/" + newFile); } _currentFiles = currentFiles; _currentDirs = currentDirs; } void MacroConditionFolder::FileChanged(const QString &path) { QFileInfo fileInfo(path); if (!fileInfo.exists()) { return; } _changedFiles << fileInfo.fileName(); if (_condition == Condition::FILE_CHANGE) { _matched = true; } } static QStringList getFileWatcherList(const QSet &files, const QString &dirName) { QStringList list; for (const auto &value : files) { list << (dirName + "/" + value); } return list; } void MacroConditionFolder::SetupWatcher() { _watcher = std::make_unique(); const auto path = QString::fromStdString(_folder); _currentFiles = getFilesInDir(path); _currentDirs = getDirsInDir(path); _lastWatchedValue = _folder.UnresolvedValue(); connect(_watcher.get(), SIGNAL(directoryChanged(const QString &)), this, SLOT(DirectoryChanged(const QString &))); connect(_watcher.get(), SIGNAL(fileChanged(const QString &)), this, SLOT(FileChanged(const QString &))); _watcher.get()->addPaths(getFileWatcherList(_currentFiles, path)); _watcher->addPath(path); } void MacroConditionFolder::SetTempVarValues() { auto setVarHelper = [this](const QSet &set, const std::string &id) { std::string result; for (const auto &value : set) { result += value.toStdString() + "\n"; } if (result.size() > 0) { result.pop_back(); } SetTempVarValue(id, result); }; setVarHelper(_newFiles, "newFiles"); setVarHelper(_changedFiles, "changedFiles"); setVarHelper(_removedFiles, "removedFiles"); setVarHelper(_newDirs, "newDirs"); setVarHelper(_removedDirs, "removedDirs"); } void MacroConditionFolder::SetupTempVars() { MacroCondition::SetupTempVars(); AddTempvar("newFiles", obs_module_text("AdvSceneSwitcher.tempVar.folder.newFiles")); AddTempvar("changedFiles", obs_module_text( "AdvSceneSwitcher.tempVar.folder.changedFiles")); AddTempvar("removedFiles", obs_module_text( "AdvSceneSwitcher.tempVar.folder.removedFiles")); AddTempvar("newDirs", obs_module_text("AdvSceneSwitcher.tempVar.folder.newDirs")); AddTempvar( "removedDirs", obs_module_text("AdvSceneSwitcher.tempVar.folder.removedDirs")); } static void populateConditions(QComboBox *list) { for (const auto &[value, name] : conditions) { list->addItem(obs_module_text(name.c_str()), static_cast(value)); } } MacroConditionFolderEdit::MacroConditionFolderEdit( QWidget *parent, std::shared_ptr entryData) : QWidget(parent), _conditions(new QComboBox()), _folder(new FileSelection(FileSelection::Type::FOLDER)), _enableFilter(new QCheckBox(obs_module_text( "AdvSceneSwitcher.condition.folder.enableFilter"))), _filterLayout(new QHBoxLayout()), _regex(new RegexConfigWidget(this, false)), _filter(new VariableLineEdit(this)) { QString path = GetThemeTypeName() == "Light" ? ":/res/images/help.svg" : ":/res/images/help_light.svg"; QIcon icon(path); QPixmap pixmap = icon.pixmap(QSize(16, 16)); auto tooltipLabel = new QLabel(this); tooltipLabel->setPixmap(pixmap); tooltipLabel->setToolTip( obs_module_text("AdvSceneSwitcher.condition.folder.tooltip")); populateConditions(_conditions); QWidget::connect(_conditions, SIGNAL(currentIndexChanged(int)), this, SLOT(ConditionChanged(int))); QWidget::connect(_folder, SIGNAL(PathChanged(const QString &)), this, SLOT(PathChanged(const QString &))); QWidget::connect(_enableFilter, SIGNAL(stateChanged(int)), this, SLOT(EnableFilterChanged(int))); QWidget::connect(_regex, SIGNAL(RegexConfigChanged(const RegexConfig &)), this, SLOT(RegexChanged(const RegexConfig &))); QWidget::connect(_filter, SIGNAL(editingFinished()), this, SLOT(FilterChanged())); const std::unordered_map widgetPlaceholders = { {"{{conditions}}", _conditions}, {"{{folder}}", _folder}, {"{{tooltip}}", tooltipLabel}, {"{{regex}}", _regex}, {"{{filter}}", _filter}, }; auto entryLayout = new QHBoxLayout(); entryLayout->setContentsMargins(0, 0, 0, 0); PlaceWidgets(obs_module_text("AdvSceneSwitcher.condition.folder.entry"), entryLayout, widgetPlaceholders, false); _filterLayout->setContentsMargins(0, 0, 0, 0); PlaceWidgets(obs_module_text( "AdvSceneSwitcher.condition.folder.entry.filter"), _filterLayout, widgetPlaceholders, false); auto layout = new QVBoxLayout(); layout->addLayout(entryLayout); layout->addWidget(_enableFilter); layout->addLayout(_filterLayout); setLayout(layout); _entryData = entryData; UpdateEntryData(); _loading = false; } void MacroConditionFolderEdit::UpdateEntryData() { if (!_entryData) { return; } _conditions->setCurrentIndex(_conditions->findData( static_cast(_entryData->_condition))); _folder->SetPath(_entryData->GetFolder()); _enableFilter->setChecked(_entryData->_enableFilter); _regex->SetRegexConfig(_entryData->_regex); _filter->setText(_entryData->_filter); SetWidgetVisibility(); } void MacroConditionFolderEdit::ConditionChanged(int index) { if (_loading || !_entryData) { return; } auto lock = LockContext(); _entryData->_condition = static_cast( _conditions->itemData(index).toInt()); } void MacroConditionFolderEdit::PathChanged(const QString &text) { if (_loading || !_entryData) { return; } auto lock = LockContext(); _entryData->SetFolder(text.toStdString()); emit HeaderInfoChanged( QString::fromStdString(_entryData->GetShortDesc())); } void MacroConditionFolderEdit::RegexChanged(const RegexConfig ®ex) { if (_loading || !_entryData) { return; } auto lock = LockContext(); _entryData->_regex = regex; _entryData->_regex.SetEnabled(true); } void MacroConditionFolderEdit::FilterChanged() { if (_loading || !_entryData) { return; } auto lock = LockContext(); _entryData->_filter = _filter->text().toStdString(); } void MacroConditionFolderEdit::EnableFilterChanged(int value) { if (_loading || !_entryData) { return; } auto lock = LockContext(); _entryData->_enableFilter = value; SetWidgetVisibility(); } void MacroConditionFolderEdit::SetWidgetVisibility() { SetLayoutVisible(_filterLayout, _entryData->_enableFilter); adjustSize(); updateGeometry(); } } // namespace advss