diff --git a/data/locale/de-DE.ini b/data/locale/de-DE.ini index e0a9b69d..16f369f8 100644 --- a/data/locale/de-DE.ini +++ b/data/locale/de-DE.ini @@ -211,8 +211,8 @@ AdvSceneSwitcher.condition.record.state.start="Aufnahme läuft" AdvSceneSwitcher.condition.record.state.pause="Aufnahme pausiert" AdvSceneSwitcher.condition.record.state.stop="Aufnahme gestoppt" AdvSceneSwitcher.condition.process="Prozess" -AdvSceneSwitcher.condition.process.entry="{{processes}}{{regex}}läuft{{focused}}und ist fokusiert" -AdvSceneSwitcher.condition.process.entry.focus="Aktueller Vordergrundprozess: {{focusProcess}}" +AdvSceneSwitcher.condition.process.layout="{{processes}}{{regex}}läuft{{focused}}und ist fokusiert" +AdvSceneSwitcher.condition.process.layout.focus="Aktueller Vordergrundprozess: {{focusProcess}}" AdvSceneSwitcher.condition.idle="Leerlauf" AdvSceneSwitcher.condition.idle.entry="Keine Tastatur- oder Mauseingaben für {{duration}}" AdvSceneSwitcher.condition.pluginState="Plugin-Status" diff --git a/data/locale/en-US.ini b/data/locale/en-US.ini index 857f9990..0b805cbe 100644 --- a/data/locale/en-US.ini +++ b/data/locale/en-US.ini @@ -484,8 +484,9 @@ AdvSceneSwitcher.condition.record.state.stop="Recording stopped" AdvSceneSwitcher.condition.record.state.duration="Recording duration is longer than" AdvSceneSwitcher.condition.record.entry="{{condition}}{{duration}}" AdvSceneSwitcher.condition.process="Process" -AdvSceneSwitcher.condition.process.entry="{{processes}}{{regex}}is running{{focused}}and is focused" -AdvSceneSwitcher.condition.process.entry.focus="Current foreground process:{{focusProcess}}" +AdvSceneSwitcher.condition.process.layout="{{processes}}{{regex}}is running{{focused}}and is focused" +AdvSceneSwitcher.condition.process.layout.focus="Current foreground process:{{focusProcess}}" +AdvSceneSwitcher.condition.process.layout.path="{{checkPath}}Match binary path:{{path}}{{pathRegex}}" AdvSceneSwitcher.condition.idle="Idle" AdvSceneSwitcher.condition.idle.entry="No keyboard or mouse inputs for{{duration}}" AdvSceneSwitcher.condition.pluginState="Plugin state" @@ -2312,6 +2313,7 @@ AdvSceneSwitcher.tempVar.macro.info.secondsSinceLastRun="Seconds since last run" AdvSceneSwitcher.tempVar.macro.info.secondsSinceLastRun.description="The number of seconds elapsed since the macro was last executed. Value is -1 if the macro has never been executed." AdvSceneSwitcher.tempVar.process.name="Process name" +AdvSceneSwitcher.tempVar.process.path="Process path" AdvSceneSwitcher.tempVar.run.process.id="Process ID" AdvSceneSwitcher.tempVar.run.process.id.description="PID of the process assigned by the system." diff --git a/data/locale/es-ES.ini b/data/locale/es-ES.ini index 515eaaff..97e42473 100644 --- a/data/locale/es-ES.ini +++ b/data/locale/es-ES.ini @@ -173,7 +173,7 @@ AdvSceneSwitcher.condition.record.state.start="Grabación en ejecución" AdvSceneSwitcher.condition.record.state.pause="Grabación en pausa" AdvSceneSwitcher.condition.record.state.stop="Grabación detenida" AdvSceneSwitcher.condition.process="Proceso" -AdvSceneSwitcher.condition.process.entry="{{processes}}{{regex}}se está ejecutando{{focused}}y está enfocado" +AdvSceneSwitcher.condition.process.layout="{{processes}}{{regex}}se está ejecutando{{focused}}y está enfocado" AdvSceneSwitcher.condition.idle="Inactivo" AdvSceneSwitcher.condition.idle.entry="No hay entradas de teclado o ratón durante {{duration}}" AdvSceneSwitcher.condition.pluginState="Estado del complemento" diff --git a/data/locale/fr-FR.ini b/data/locale/fr-FR.ini index 6af447bc..3b77a891 100644 --- a/data/locale/fr-FR.ini +++ b/data/locale/fr-FR.ini @@ -279,8 +279,8 @@ AdvSceneSwitcher.condition.record.state.start="Enregistrement en cours" AdvSceneSwitcher.condition.record.state.pause="Enregistrement en pause" AdvSceneSwitcher.condition.record.state.stop="Arrêt de l'enregistrement" AdvSceneSwitcher.condition.process="Processus" -AdvSceneSwitcher.condition.process.entry="{{processes}}{{regex}}en cours d'exécution{{focused}}et est au premier plan" -AdvSceneSwitcher.condition.process.entry.focus="Processus au premier plan actuel :{{focusProcess}}" +AdvSceneSwitcher.condition.process.layout="{{processes}}{{regex}}en cours d'exécution{{focused}}et est au premier plan" +AdvSceneSwitcher.condition.process.layout.focus="Processus au premier plan actuel :{{focusProcess}}" AdvSceneSwitcher.condition.idle="Inactif" AdvSceneSwitcher.condition.idle.entry="Aucune entrée de clavier ou de souris pendant{{duration}}" AdvSceneSwitcher.condition.pluginState="État du plugin" diff --git a/data/locale/ja-JP.ini b/data/locale/ja-JP.ini index f3d5e233..a1c0e57c 100644 --- a/data/locale/ja-JP.ini +++ b/data/locale/ja-JP.ini @@ -445,8 +445,8 @@ AdvSceneSwitcher.condition.stream.service.tooltip="現在のサービス名: %1" AdvSceneSwitcher.condition.record.state.duration="録音時間が長くなっています。" ; AdvSceneSwitcher.condition.record.entry="{{condition}}{{duration}}" AdvSceneSwitcher.condition.process="プロセス" -AdvSceneSwitcher.condition.process.entry="{{processes}}{{regex}}が実行中{{focused}}に集中しています" -AdvSceneSwitcher.condition.process.entry.focus="現在のフォアグラウンドプロセス:{{focusProcess}}" +AdvSceneSwitcher.condition.process.layout="{{processes}}{{regex}}が実行中{{focused}}に集中しています" +AdvSceneSwitcher.condition.process.layout.focus="現在のフォアグラウンドプロセス:{{focusProcess}}" AdvSceneSwitcher.condition.idle="アイドル" AdvSceneSwitcher.condition.idle.entry="{{duration}}の間、キーボードまたはマウスの入力がありません" AdvSceneSwitcher.condition.pluginState="プラグインの状態" diff --git a/data/locale/pt-BR.ini b/data/locale/pt-BR.ini index 92c29e04..331be24f 100644 --- a/data/locale/pt-BR.ini +++ b/data/locale/pt-BR.ini @@ -395,8 +395,8 @@ AdvSceneSwitcher.condition.record.state.stop="Gravação parada" AdvSceneSwitcher.condition.record.state.duration="Duração da gravação é maior que" AdvSceneSwitcher.condition.record.entry="{{condition}}{{duration}}" AdvSceneSwitcher.condition.process="Processo" -AdvSceneSwitcher.condition.process.entry="{{processes}}{{regex}}está em execução{{focused}}e está em foco" -AdvSceneSwitcher.condition.process.entry.focus="Processo atual em primeiro plano:{{focusProcess}}" +AdvSceneSwitcher.condition.process.layout="{{processes}}{{regex}}está em execução{{focused}}e está em foco" +AdvSceneSwitcher.condition.process.layout.focus="Processo atual em primeiro plano:{{focusProcess}}" AdvSceneSwitcher.condition.idle="Inativo" AdvSceneSwitcher.condition.idle.entry="Sem entradas de teclado ou mouse por {{duration}}" AdvSceneSwitcher.condition.pluginState="Estado do plugin" diff --git a/data/locale/ru-RU.ini b/data/locale/ru-RU.ini index e2273057..0589b1ae 100644 --- a/data/locale/ru-RU.ini +++ b/data/locale/ru-RU.ini @@ -103,7 +103,7 @@ AdvSceneSwitcher.condition.record.state.start="Запись запущена" AdvSceneSwitcher.condition.record.state.pause="Запись приостановлена" AdvSceneSwitcher.condition.record.state.stop="Запись остановлена" AdvSceneSwitcher.condition.process="Процесс" -AdvSceneSwitcher.condition.process.entry="{{processes}}{{regex}}запущен{{focused}}и сфокусирован" +AdvSceneSwitcher.condition.process.layout="{{processes}}{{regex}}запущен{{focused}}и сфокусирован" AdvSceneSwitcher.condition.idle="Простой" AdvSceneSwitcher.condition.idle.entry="Не было ни клавиатуры, ни мыши в течении{{duration}}" AdvSceneSwitcher.condition.pluginState="Состояние плагина" diff --git a/data/locale/tr-TR.ini b/data/locale/tr-TR.ini index f1c59095..5466fb0d 100644 --- a/data/locale/tr-TR.ini +++ b/data/locale/tr-TR.ini @@ -150,7 +150,7 @@ AdvSceneSwitcher.condition.record.state.start="Kayıt Çalışıyor" AdvSceneSwitcher.condition.record.state.pause="Kayıt durakladı" AdvSceneSwitcher.condition.record.state.stop="Kayıt durdu" AdvSceneSwitcher.condition.process="İşlem" -AdvSceneSwitcher.condition.process.entry="{{processes}}{{regex}}çalışıyor{{focused}}ve odaklandı" +AdvSceneSwitcher.condition.process.layout="{{processes}}{{regex}}çalışıyor{{focused}}ve odaklandı" AdvSceneSwitcher.condition.idle="Boşta" AdvSceneSwitcher.condition.idle.entry="...için klavye veya fare girişi yok {{duration}}" AdvSceneSwitcher.condition.pluginState="Eklenti durumu" diff --git a/data/locale/zh-CN.ini b/data/locale/zh-CN.ini index 7918c560..6ed2ee25 100644 --- a/data/locale/zh-CN.ini +++ b/data/locale/zh-CN.ini @@ -424,8 +424,8 @@ AdvSceneSwitcher.condition.record.state.stop="录制停止" AdvSceneSwitcher.condition.record.state.duration="录制时间长于" AdvSceneSwitcher.condition.record.entry="{{condition}}{{duration}}" AdvSceneSwitcher.condition.process="进程" -AdvSceneSwitcher.condition.process.entry="{{processes}}{{regex}}为正在运行中{{focused}}且为焦点" -AdvSceneSwitcher.condition.process.entry.focus="当前焦点进程: {{focusProcess}}" +AdvSceneSwitcher.condition.process.layout="{{processes}}{{regex}}为正在运行中{{focused}}且为焦点" +AdvSceneSwitcher.condition.process.layout.focus="当前焦点进程: {{focusProcess}}" AdvSceneSwitcher.condition.idle="闲置检测" AdvSceneSwitcher.condition.idle.entry="{{duration}}内没有键盘或鼠标输入" AdvSceneSwitcher.condition.pluginState="插件状态" diff --git a/lib/linux/advanced-scene-switcher-nix.cpp b/lib/linux/advanced-scene-switcher-nix.cpp index 30493124..5466a474 100644 --- a/lib/linux/advanced-scene-switcher-nix.cpp +++ b/lib/linux/advanced-scene-switcher-nix.cpp @@ -32,6 +32,9 @@ #endif #include #include +#include +#include +#include #include "kwin-helpers.h" namespace advss { @@ -483,6 +486,74 @@ void GetForegroundProcessName(std::string &proc) proc = getProcNameFromPid(pid); } +static std::string getProcessPathFromPid(long pid) +{ + std::string linkPath = "/proc/" + std::to_string(pid) + "/exe"; + char buf[PATH_MAX]; + ssize_t len = readlink(linkPath.c_str(), buf, sizeof(buf) - 1); + if (len <= 0) { + return {}; + } + buf[len] = '\0'; + return buf; +} + +std::string GetForegroundProcessPath() +{ + auto pid = getForegroundProcessPid(); + if (pid <= 0) { + return {}; + } + return getProcessPathFromPid(pid); +} + +QStringList GetProcessPathsFromName(const QString &name) +{ + QStringList paths; + const std::string nameStr = name.toStdString(); + DIR *procDir = opendir("/proc"); + if (!procDir) { + return paths; + } + + struct dirent *entry; + while ((entry = readdir(procDir)) != nullptr) { + bool isPid = (entry->d_name[0] != '\0'); + for (const char *c = entry->d_name; *c; c++) { + if (!isdigit(*c)) { + isPid = false; + break; + } + } + if (!isPid) { + continue; + } + + std::string pid = entry->d_name; + std::string commPath = "/proc/" + pid + "/comm"; + std::ifstream commFile(commPath); + if (!commFile) { + continue; + } + std::string comm; + std::getline(commFile, comm); + if (comm != nameStr) { + continue; + } + + std::string path = getProcessPathFromPid(std::stol(pid)); + if (path.empty()) { + continue; + } + QString qPath = QString::fromStdString(path); + if (!paths.contains(qPath)) { + paths.append(qPath); + } + } + closedir(procDir); + return paths; +} + bool IsInFocus(const QString &executable) { std::string current; diff --git a/lib/osx/advanced-scene-switcher-osx.mm b/lib/osx/advanced-scene-switcher-osx.mm index 81a6169b..1abc7826 100644 --- a/lib/osx/advanced-scene-switcher-osx.mm +++ b/lib/osx/advanced-scene-switcher-osx.mm @@ -321,6 +321,61 @@ void GetForegroundProcessName(QString &proc) proc = QString::fromStdString(temp); } +std::string GetForegroundProcessPath() +{ + @autoreleasepool { + NSWorkspace *ws = [NSWorkspace sharedWorkspace]; + for (NSRunningApplication *app in [ws runningApplications]) { + if (!app.isActive) { + continue; + } + NSURL *url = app.executableURL; + if (!url) { + break; + } + const char *str = url.path.UTF8String; + if (str) { + return str; + } + break; + } + } + return {}; +} + +QStringList GetProcessPathsFromName(const QString &name) +{ + QStringList paths; + @autoreleasepool { + NSWorkspace *ws = [NSWorkspace sharedWorkspace]; + for (NSRunningApplication *app in [ws runningApplications]) { + NSString *appName = app.localizedName; + if (!appName) { + continue; + } + const char *nameStr = appName.UTF8String; + if (!nameStr || + name != QString::fromUtf8(nameStr)) { + continue; + } + NSURL *url = app.executableURL; + if (!url) { + continue; + } + const char *pathStr = url.path.UTF8String; + if (!pathStr) { + continue; + } + QString path = QString::fromUtf8(pathStr); + if (!paths.contains(path)) { + paths.append(path); + } + } + } + + return paths; +} + bool IsInFocus(const QString &executable) { std::string current; diff --git a/lib/platform-funcs.hpp b/lib/platform-funcs.hpp index 0b4956cc..f33abfd3 100644 --- a/lib/platform-funcs.hpp +++ b/lib/platform-funcs.hpp @@ -20,6 +20,8 @@ EXPORT std::optional GetTextInWindow(const std::string &window); EXPORT int SecondsSinceLastInput(); EXPORT void GetProcessList(QStringList &processes); EXPORT void GetForegroundProcessName(std::string &name); +EXPORT std::string GetForegroundProcessPath(); +EXPORT QStringList GetProcessPathsFromName(const QString &name); EXPORT bool IsInFocus(const QString &executable); void PlatformInit(); void PlatformCleanup(); diff --git a/lib/win/advanced-scene-switcher-win.cpp b/lib/win/advanced-scene-switcher-win.cpp index e6b2c487..4d8c0421 100644 --- a/lib/win/advanced-scene-switcher-win.cpp +++ b/lib/win/advanced-scene-switcher-win.cpp @@ -415,6 +415,68 @@ void GetForegroundProcessName(std::string &proc) proc = temp.toStdString(); } +std::string GetForegroundProcessPath() +{ + HWND foregroundWindow = GetForegroundWindow(); + DWORD processId = 0; + GetWindowThreadProcessId(foregroundWindow, &processId); + + HANDLE process = OpenProcess( + PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, processId); + if (process == NULL) { + return {}; + } + + WCHAR executablePath[600]; + GetModuleFileNameEx(process, 0, executablePath, 600); + CloseHandle(process); + + return QString::fromWCharArray(executablePath).toStdString(); +} + +QStringList GetProcessPathsFromName(const QString &name) +{ + QStringList paths; + HANDLE procSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + if (procSnapshot == INVALID_HANDLE_VALUE) { + return paths; + } + + PROCESSENTRY32 procEntry; + procEntry.dwSize = sizeof(PROCESSENTRY32); + + if (!Process32First(procSnapshot, &procEntry)) { + CloseHandle(procSnapshot); + return paths; + } + + do { + QString exeName = QString::fromWCharArray(procEntry.szExeFile); + if (exeName != name) { + continue; + } + + HANDLE process = + OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, + FALSE, procEntry.th32ProcessID); + if (process == NULL) { + continue; + } + + WCHAR executablePath[600]; + if (GetModuleFileNameEx(process, 0, executablePath, 600)) { + QString path = QString::fromWCharArray(executablePath); + if (!paths.contains(path)) { + paths.append(path); + } + } + CloseHandle(process); + } while (Process32Next(procSnapshot, &procEntry)); + + CloseHandle(procSnapshot); + return paths; +} + bool IsInFocus(const QString &executable) { // only checks if the current foreground window is from the same executable, diff --git a/plugins/base/macro-condition-process.cpp b/plugins/base/macro-condition-process.cpp index 720a32d6..e79a7c06 100644 --- a/plugins/base/macro-condition-process.cpp +++ b/plugins/base/macro-condition-process.cpp @@ -16,54 +16,102 @@ bool MacroConditionProcess::_registered = MacroConditionFactory::Register( bool MacroConditionProcess::CheckCondition() { - QStringList runningProcesses; - QString proc = QString::fromStdString(_process); - GetProcessList(runningProcesses); std::string foregroundProcessName; GetForegroundProcessName(foregroundProcessName); - SetVariableValue(foregroundProcessName); - if (!_regex.Enabled()) { - if (runningProcesses.contains(proc) && - (!_checkFocus || IsInFocus(proc))) { - SetTempVarValue("name", proc.toStdString()); - return true; - } - return false; - } + const QString proc = QString::fromStdString(_process); - int matchIndex = -1; - bool foundMatch = false; - for (const auto &process : runningProcesses) { - matchIndex++; - if (_regex.Matches(process, proc)) { - foundMatch = true; - break; + if (_checkFocus) { + // Check name and path against the same foreground process + // instance to avoid false positives when multiple processes + // share the same name + const auto foregroundPath = GetForegroundProcessPath(); + const QString foregroundName = + QString::fromStdString(foregroundProcessName); + + SetTempVarValue("name", foregroundProcessName); + SetTempVarValue("path", foregroundPath); + + bool nameMatches = + _regex.Enabled() ? _regex.Matches(foregroundName, proc) + : (foregroundName == proc); + if (!nameMatches) { + return false; } - } - if (!foundMatch) { - return false; - } - if (!_checkFocus) { - SetTempVarValue("name", - runningProcesses.at(matchIndex).toStdString()); + + if (_checkPath) { + const QString pathPattern = + QString::fromStdString(_processPath); + const QString qForegroundPath = + QString::fromStdString(foregroundPath); + bool pathMatches = + _pathRegex.Enabled() + ? _pathRegex.Matches(qForegroundPath, + pathPattern) + : qForegroundPath == pathPattern; + if (!pathMatches) { + return false; + } + } + return true; } - if (!IsInFocus(proc)) { - return false; + + QStringList runningProcesses; + GetProcessList(runningProcesses); + + for (const auto &process : runningProcesses) { + bool nameMatches = _regex.Enabled() + ? _regex.Matches(process, proc) + : (process == proc); + if (!nameMatches) { + continue; + } + + if (!_checkPath) { + SetTempVarValue("name", process.toStdString()); + return true; + } + + const auto paths = GetProcessPathsFromName(process); + + const QString pathPattern = + QString::fromStdString(_processPath); + bool foundMatchingPath = false; + for (const auto &path : paths) { + bool pathMatches = + _pathRegex.Enabled() + ? _pathRegex.Matches(path, pathPattern) + : path == pathPattern; + if (pathMatches) { + SetTempVarValue("path", path.toStdString()); + foundMatchingPath = true; + break; + } + } + + if (!foundMatchingPath) { + continue; + } + + SetTempVarValue("name", process.toStdString()); + return true; } - SetTempVarValue("name", foregroundProcessName); - return true; + + return false; } bool MacroConditionProcess::Save(obs_data_t *obj) const { MacroCondition::Save(obj); _process.Save(obj, "process"); - obs_data_set_bool(obj, "focus", _checkFocus); _regex.Save(obj); - obs_data_set_int(obj, "version", 1); + obs_data_set_bool(obj, "focus", _checkFocus); + obs_data_set_bool(obj, "checkPath", _checkPath); + _processPath.Save(obj, "processPath"); + _pathRegex.Save(obj, "pathRegex"); + obs_data_set_int(obj, "version", 2); return true; } @@ -78,6 +126,9 @@ bool MacroConditionProcess::Load(obs_data_t *obj) } else { _regex.Load(obj); } + _checkPath = obs_data_get_bool(obj, "checkPath"); + _processPath.Load(obj, "processPath"); + _pathRegex.Load(obj, "pathRegex"); return true; } @@ -91,6 +142,8 @@ void MacroConditionProcess::SetupTempVars() MacroCondition::SetupTempVars(); AddTempvar("name", obs_module_text("AdvSceneSwitcher.tempVar.process.name")); + AddTempvar("path", + obs_module_text("AdvSceneSwitcher.tempVar.process.path")); } MacroConditionProcessEdit::MacroConditionProcessEdit( @@ -100,13 +153,20 @@ MacroConditionProcessEdit::MacroConditionProcessEdit( _regex(new RegexConfigWidget(this)), _focused(new QCheckBox()), _focusProcess(new QLabel()), - _focusLayout(new QHBoxLayout()) + _focusLayout(new QHBoxLayout()), + _checkPath(new QCheckBox()), + _processPath(new VariableLineEdit(this)), + _pathRegex(new RegexConfigWidget(this)), + _pathLayout(new QHBoxLayout()) { _processSelection->setEditable(true); _processSelection->setMaxVisibleItems(20); _processSelection->setToolTip( obs_module_text("AdvSceneSwitcher.tooltip.availableVariables")); + _processPath->setToolTip( + obs_module_text("AdvSceneSwitcher.tooltip.availableVariables")); + QWidget::connect(_processSelection, SIGNAL(currentTextChanged(const QString &)), this, SLOT(ProcessChanged(const QString &))); @@ -115,6 +175,13 @@ MacroConditionProcessEdit::MacroConditionProcessEdit( SLOT(RegexChanged(const RegexConfig &))); QWidget::connect(_focused, SIGNAL(stateChanged(int)), this, SLOT(FocusChanged(int))); + QWidget::connect(_checkPath, SIGNAL(stateChanged(int)), this, + SLOT(CheckPathChanged(int))); + QWidget::connect(_processPath, SIGNAL(textChanged(const QString &)), + this, SLOT(ProcessPathChanged(const QString &))); + QWidget::connect(_pathRegex, + SIGNAL(RegexConfigChanged(const RegexConfig &)), this, + SLOT(PathRegexChanged(const RegexConfig &))); QWidget::connect(&_timer, SIGNAL(timeout()), this, SLOT(UpdateFocusProcess())); @@ -125,18 +192,25 @@ MacroConditionProcessEdit::MacroConditionProcessEdit( {"{{regex}}", _regex}, {"{{focused}}", _focused}, {"{{focusProcess}}", _focusProcess}, + {"{{checkPath}}", _checkPath}, + {"{{path}}", _processPath}, + {"{{pathRegex}}", _pathRegex}, }; auto entryLayout = new QHBoxLayout; PlaceWidgets( - obs_module_text("AdvSceneSwitcher.condition.process.entry"), + obs_module_text("AdvSceneSwitcher.condition.process.layout"), entryLayout, widgetPlaceholders); PlaceWidgets(obs_module_text( - "AdvSceneSwitcher.condition.process.entry.focus"), + "AdvSceneSwitcher.condition.process.layout.focus"), _focusLayout, widgetPlaceholders); + PlaceWidgets(obs_module_text( + "AdvSceneSwitcher.condition.process.layout.path"), + _pathLayout, widgetPlaceholders); auto mainLayout = new QVBoxLayout; mainLayout->addLayout(entryLayout); mainLayout->addLayout(_focusLayout); + mainLayout->addLayout(_pathLayout); setLayout(mainLayout); _entryData = entryData; @@ -178,6 +252,27 @@ void MacroConditionProcessEdit::FocusChanged(int state) SetWidgetVisibility(); } +void MacroConditionProcessEdit::CheckPathChanged(int state) +{ + GUARD_LOADING_AND_LOCK(); + _entryData->_checkPath = state; + SetWidgetVisibility(); +} + +void MacroConditionProcessEdit::ProcessPathChanged(const QString &text) +{ + GUARD_LOADING_AND_LOCK(); + _entryData->_processPath = text.toStdString(); +} + +void MacroConditionProcessEdit::PathRegexChanged(const RegexConfig &conf) +{ + GUARD_LOADING_AND_LOCK(); + _entryData->_pathRegex = conf; + adjustSize(); + updateGeometry(); +} + void MacroConditionProcessEdit::UpdateFocusProcess() { std::string name; @@ -191,6 +286,13 @@ void MacroConditionProcessEdit::SetWidgetVisibility() return; } SetLayoutVisible(_focusLayout, _entryData->_checkFocus); + _processPath->setVisible(_entryData->_checkPath); + _pathRegex->setVisible(_entryData->_checkPath); + if (_entryData->_checkPath) { + RemoveStretchIfPresent(_pathLayout); + } else { + AddStretchIfNecessary(_pathLayout); + } adjustSize(); updateGeometry(); } @@ -205,6 +307,9 @@ void MacroConditionProcessEdit::UpdateEntryData() _entryData->_process.UnresolvedValue().c_str()); _regex->SetRegexConfig(_entryData->_regex); _focused->setChecked(_entryData->_checkFocus); + _checkPath->setChecked(_entryData->_checkPath); + _processPath->setText(_entryData->_processPath); + _pathRegex->SetRegexConfig(_entryData->_pathRegex); SetWidgetVisibility(); } diff --git a/plugins/base/macro-condition-process.hpp b/plugins/base/macro-condition-process.hpp index dcd46ffa..568d89ab 100644 --- a/plugins/base/macro-condition-process.hpp +++ b/plugins/base/macro-condition-process.hpp @@ -1,7 +1,7 @@ #pragma once #include "macro-condition-edit.hpp" #include "regex-config.hpp" -#include "variable-string.hpp" +#include "variable-line-edit.hpp" #include @@ -21,8 +21,11 @@ public: } StringVariable _process; - bool _checkFocus = true; RegexConfig _regex = RegexConfig::PartialMatchRegexConfig(); + bool _checkFocus = true; + bool _checkPath = false; + StringVariable _processPath; + RegexConfig _pathRegex = RegexConfig::PartialMatchRegexConfig(); private: void SetupTempVars(); @@ -54,6 +57,9 @@ private slots: void ProcessChanged(const QString &text); void RegexChanged(const RegexConfig &); void FocusChanged(int state); + void CheckPathChanged(int state); + void ProcessPathChanged(const QString &text); + void PathRegexChanged(const RegexConfig &); void UpdateFocusProcess(); signals: void HeaderInfoChanged(const QString &); @@ -66,6 +72,10 @@ private: QCheckBox *_focused; QLabel *_focusProcess; QHBoxLayout *_focusLayout; + QCheckBox *_checkPath; + VariableLineEdit *_processPath; + RegexConfigWidget *_pathRegex; + QHBoxLayout *_pathLayout; QTimer _timer; std::shared_ptr _entryData; bool _loading = true;