From 87edcd0ce44a28fda2e85a3cb98217e0a98626c0 Mon Sep 17 00:00:00 2001 From: Brian Ferguson Date: Tue, 5 Nov 2013 10:07:42 -0700 Subject: [PATCH] Added new options IfCondition/IfTrueAction/IfFalseAction for all Measures. Multiple conditions/actions are also possible (eg. IfCondition2/IfTrueAction2) --- Library/IfActions.cpp | 261 ++++++++++++++++++++++++++++++++ Library/IfActions.h | 92 +++++++++++ Library/Library.vcxproj | 2 + Library/Library.vcxproj.filters | 6 + Library/Measure.cpp | 104 ++++--------- Library/Measure.h | 12 +- 6 files changed, 390 insertions(+), 87 deletions(-) create mode 100644 Library/IfActions.cpp create mode 100644 Library/IfActions.h diff --git a/Library/IfActions.cpp b/Library/IfActions.cpp new file mode 100644 index 00000000..52aa7ef2 --- /dev/null +++ b/Library/IfActions.cpp @@ -0,0 +1,261 @@ +/* + Copyright (C) 2013 Brian Ferguson + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#include "StdAfx.h" +#include "Measure.h" +#include "IfActions.h" +#include "Rainmeter.h" +#include "../Common/MathParser.h" + +IfActions::IfActions(MeterWindow* meterWindow, Measure* measure) : + m_AboveValue(0.0f), + m_BelowValue(0.0f), + m_EqualValue(0), + m_AboveAction(), + m_BelowAction(), + m_EqualAction(), + m_AboveCommitted(false), + m_BelowCommitted(false), + m_EqualCommitted(false), + m_Conditions(), + m_HasConditions(false), + m_HasDynamicVariables(false), + m_MeterWindow(meterWindow), + m_Measure(measure) +{ +} + +IfActions::~IfActions() +{ +} + +void IfActions::ReadOptions(ConfigParser& parser, const WCHAR* section) +{ + m_Parser = &parser; + m_HasDynamicVariables = m_Measure->HasDynamicVariables(); + + m_AboveAction = parser.ReadString(section, L"IfAboveAction", L"", false); + m_AboveValue = parser.ReadFloat(section, L"IfAboveValue", 0.0f); + + m_BelowAction = parser.ReadString(section, L"IfBelowAction", L"", false); + m_BelowValue = parser.ReadFloat(section, L"IfBelowValue", 0.0f); + + m_EqualAction = parser.ReadString(section, L"IfEqualAction", L"", false); + m_EqualValue = (int64_t)parser.ReadFloat(section, L"IfEqualValue", 0.0f); + + m_HasConditions = false; + bool hasSelf = false; + std::wstring condition; + + if (m_HasDynamicVariables) + { + condition = parser.GetValue(section, L"IfCondition", L""); + if (!condition.empty() && ReplaceSelf(condition, section, L"[]", L"{}")) + { + parser.SetValue(section, L"IfCondition", condition); + hasSelf = true; + } + } + condition = parser.ReadString(section, L"IfCondition", L""); + + std::wstring tAction = parser.ReadString(section, L"IfTrueAction", L"", false); + std::wstring fAction = parser.ReadString(section, L"IfFalseAction", L"", false); + + if (!condition.empty() && (!tAction.empty() || !fAction.empty())) + { + m_HasConditions = true; + int i = 1; + + do + { + if (m_Conditions.size() > (i - 1)) + { + m_Conditions[i - 1].Set(condition, tAction, fAction); + } + else + { + m_Conditions.emplace_back(condition, tAction, fAction, hasSelf); + } + + hasSelf = false; + + // Check for IfCondition2/IfTrueAction2/IfFalseAction2 ... etc. + std::wstring key = L"IfTrueAction" + std::to_wstring(++i); + tAction = parser.ReadString(section, key.c_str(), L"", false); + key = L"IfFalseAction" + std::to_wstring(i); + fAction = parser.ReadString(section, key.c_str(), L"", false); + + key = L"IfCondition" + std::to_wstring(i); + if (m_HasDynamicVariables) + { + condition = parser.GetValue(section, key, L""); + if (!condition.empty() && ReplaceSelf(condition, section, L"[]", L"{}")) + { + parser.SetValue(section, key, condition); + hasSelf = true; + } + } + condition = parser.ReadString(section, key.c_str(), L""); + + } + while (!condition.empty() && (!tAction.empty() || !fAction.empty())); + } +} + +void IfActions::DoIfActions(double& value) +{ + if (!m_EqualAction.empty()) + { + if ((int64_t)value == m_EqualValue) + { + if (!m_EqualCommitted) + { + m_EqualCommitted = true; // To avoid infinite loop from !Update + GetRainmeter().ExecuteCommand(m_EqualAction.c_str(), m_MeterWindow); + } + } + else + { + m_EqualCommitted = false; + } + } + + if (!m_AboveAction.empty()) + { + if (value > m_AboveValue) + { + if (!m_AboveCommitted) + { + m_AboveCommitted = true; // To avoid infinite loop from !Update + GetRainmeter().ExecuteCommand(m_AboveAction.c_str(), m_MeterWindow); + } + } + else + { + m_AboveCommitted = false; + } + } + + if (!m_BelowAction.empty()) + { + if (value < m_BelowValue) + { + if (!m_BelowCommitted) + { + m_BelowCommitted = true; // To avoid infinite loop from !Update + GetRainmeter().ExecuteCommand(m_BelowAction.c_str(), m_MeterWindow); + } + } + else + { + m_BelowCommitted = false; + } + } + + if (m_HasConditions) + { + int i = 0; + for (auto& item : m_Conditions) + { + ++i; + if (!item.condition.empty() && (!item.tAction.empty() || !item.fAction.empty())) + { + //Replace measures (need it here in case the if actions reference themselves) + if (m_HasDynamicVariables && item.containsSelf) + { + ReplaceSelf(item.condition, m_Measure->GetName(), L"{}", L"[]"); + m_Parser->ReplaceMeasures(item.condition); + } + + double result = 0.0f; + const WCHAR* errMsg = MathParser::Parse(item.condition.c_str(), &result, m_Measure->GetCurrentMeasureValue, m_Measure); + if (errMsg != nullptr) + { + if (!item.parseError) + { + if (i == 1) + { + LogErrorF(m_Measure, L"%s: IfCondition=%s", errMsg, item.condition.c_str()); + } + else + { + LogErrorF(m_Measure, L"%s: IfCondition%i=%s", errMsg, i, item.condition.c_str()); + } + item.parseError = true; + } + } + else + { + item.parseError = false; + + if (result == 1.0f) // "True" + { + GetRainmeter().ExecuteCommand(item.tAction.c_str(), m_MeterWindow); + } + else if (result == 0.0f) // "False" + { + GetRainmeter().ExecuteCommand(item.fAction.c_str(), m_MeterWindow); + } + } + } + } + } +} + +void IfActions::SetState(double value) +{ + // Set IfAction committed state to false if condition is not met with value = 0 + if (m_EqualValue != 0) + { + m_EqualCommitted = false; + } + + if (m_AboveValue <= 0.0) + { + m_AboveCommitted = false; + } + + if (m_BelowValue >= 0.0) + { + m_BelowCommitted = false; + } +} + +/* +** Replaces a [MeasureName] with {MeasureName} and vice-versa. +** This is needed to support IfConditions referencing themselves. +*/ +bool IfActions::ReplaceSelf(std::wstring& condition, const WCHAR* section, + const std::wstring sBracket, const std::wstring eBracket) +{ + bool replaced = false; + std::wstring measureName = sBracket; + measureName.replace(1, 1, section); + + size_t pos = 0; + while ((pos = condition.find(measureName, pos)) != std::wstring::npos) + { + condition.replace(pos, 1, eBracket.substr(0, 1)); + pos = condition.find(sBracket[1], pos); + condition.replace(pos, 1, eBracket.substr(1, 1)); + ++pos; + replaced = true; + } + + return replaced; +} diff --git a/Library/IfActions.h b/Library/IfActions.h new file mode 100644 index 00000000..af0c458d --- /dev/null +++ b/Library/IfActions.h @@ -0,0 +1,92 @@ +/* + Copyright (C) 2013 Brian Ferguson + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#ifndef __IFACTIONS_H__ +#define __IFACTIONS_H__ + +#include +#include +#include + +class ConfigParser; +class Measure; +class MeterWindow; + +class IfCondition +{ +public: + IfCondition(std::wstring value, std::wstring trueAction, std::wstring falseAction = L"", bool hasSelf = false) : + condition(), + tAction(), + fAction(), + containsSelf(hasSelf), + parseError(false) + { + Set(value, trueAction, falseAction); + } + + inline void Set(std::wstring value, std::wstring trueAction, std::wstring falseAction) + { + this->condition = value; + this->tAction = trueAction; + this->fAction = falseAction; + } + + std::wstring condition; // IfCondition + std::wstring tAction; // IfTrueAction + std::wstring fAction; // IfFalseAction + bool parseError; + bool containsSelf; +}; + +class IfActions +{ +public: + IfActions(MeterWindow* meterWindow, Measure* measure); + ~IfActions(); + + void ReadOptions(ConfigParser& parser, const WCHAR* section); + void DoIfActions(double& value); + void SetState(double value = 0.0f); + +private: + bool ReplaceSelf(std::wstring& condition, const WCHAR* section, + const std::wstring sBracket, const std::wstring eBracket); + + double m_AboveValue; + double m_BelowValue; + int64_t m_EqualValue; + + std::wstring m_AboveAction; + std::wstring m_BelowAction; + std::wstring m_EqualAction; + + bool m_AboveCommitted; + bool m_BelowCommitted; + bool m_EqualCommitted; + + std::vector m_Conditions; + bool m_HasConditions; + + bool m_HasDynamicVariables; + + ConfigParser* m_Parser; + MeterWindow* m_MeterWindow; + Measure* m_Measure; +}; +#endif diff --git a/Library/Library.vcxproj b/Library/Library.vcxproj index fff4af86..90952c95 100644 --- a/Library/Library.vcxproj +++ b/Library/Library.vcxproj @@ -67,6 +67,7 @@ Use + Use @@ -287,6 +288,7 @@ + diff --git a/Library/Library.vcxproj.filters b/Library/Library.vcxproj.filters index f28be904..4a47f323 100644 --- a/Library/Library.vcxproj.filters +++ b/Library/Library.vcxproj.filters @@ -393,6 +393,9 @@ Source Files + + Source Files + @@ -671,6 +674,9 @@ Header Files + + Header Files + diff --git a/Library/Measure.cpp b/Library/Measure.cpp index 324ce806..1f9cf1fe 100644 --- a/Library/Measure.cpp +++ b/Library/Measure.cpp @@ -77,12 +77,7 @@ Measure::Measure(MeterWindow* meterWindow, const WCHAR* name) : Section(meterWin m_MedianPos(), m_AveragePos(), m_AverageSize(), - m_IfEqualValue(), - m_IfAboveValue(), - m_IfBelowValue(), - m_IfEqualCommitted(false), - m_IfAboveCommitted(false), - m_IfBelowCommitted(false), + m_IfActions(meterWindow, this), m_Disabled(false), m_Paused(false), m_Initialized(false), @@ -134,16 +129,7 @@ void Measure::ReadOptions(ConfigParser& parser, const WCHAR* section) m_MinValue = parser.ReadFloat(section, L"MinValue", m_MinValue); m_MaxValue = parser.ReadFloat(section, L"MaxValue", m_MaxValue); - // The ifabove/ifbelow define actions that are ran when the value goes above/below the given number. - - m_IfAboveValue = parser.ReadFloat(section, L"IfAboveValue", 0.0); - m_IfAboveAction = parser.ReadString(section, L"IfAboveAction", L"", false); - - m_IfBelowValue = parser.ReadFloat(section, L"IfBelowValue", 0.0); - m_IfBelowAction = parser.ReadString(section, L"IfBelowAction", L"", false); - - m_IfEqualValue = (int64_t)parser.ReadFloat(section, L"IfEqualValue", 0.0); - m_IfEqualAction = parser.ReadString(section, L"IfEqualAction", L"", false); + m_IfActions.ReadOptions(parser, section); m_OnChangeAction = parser.ReadString(section, L"OnChangeAction", L"", false); @@ -513,53 +499,7 @@ bool Measure::Update() if (m_MeterWindow) { - if (!m_IfEqualAction.empty()) - { - if ((int64_t)m_Value == m_IfEqualValue) - { - if (!m_IfEqualCommitted) - { - m_IfEqualCommitted = true; // To avoid infinite loop from !Update - GetRainmeter().ExecuteCommand(m_IfEqualAction.c_str(), m_MeterWindow); - } - } - else - { - m_IfEqualCommitted = false; - } - } - - if (!m_IfAboveAction.empty()) - { - if (m_Value > m_IfAboveValue) - { - if (!m_IfAboveCommitted) - { - m_IfAboveCommitted = true; // To avoid infinite loop from !Update - GetRainmeter().ExecuteCommand(m_IfAboveAction.c_str(), m_MeterWindow); - } - } - else - { - m_IfAboveCommitted = false; - } - } - - if (!m_IfBelowAction.empty()) - { - if (m_Value < m_IfBelowValue) - { - if (!m_IfBelowCommitted) - { - m_IfBelowCommitted = true; // To avoid infinite loop from !Update - GetRainmeter().ExecuteCommand(m_IfBelowAction.c_str(), m_MeterWindow); - } - } - else - { - m_IfBelowCommitted = false; - } - } + m_IfActions.DoIfActions(m_Value); } return true; @@ -569,21 +509,7 @@ bool Measure::Update() // Disabled measures have 0 as value m_Value = 0.0; - // Set IfAction committed state to false if condition is not met with value = 0 - if (m_IfEqualValue != 0) - { - m_IfEqualCommitted = false; - } - - if (m_IfAboveValue <= 0.0) - { - m_IfAboveCommitted = false; - } - - if (m_IfBelowValue >= 0.0) - { - m_IfBelowCommitted = false; - } + m_IfActions.SetState(m_Value); return false; } @@ -879,3 +805,25 @@ void Measure::Command(const std::wstring& command) { LogWarningF(this, L"!CommandMeasure: Not supported"); } + +/* +** Returns the number value of a measure, used by IfCondition's. +** +*/ +bool Measure::GetCurrentMeasureValue(const WCHAR* str, int len, double* value, void* context) +{ + auto measure = (Measure*)context; + const std::vector& measures = measure->m_MeterWindow->GetMeasures(); + + for (const auto& iter : measures) + { + if (iter->GetOriginalName().length() == len && + _wcsnicmp(str, iter->GetName(), len) == 0) + { + *value = iter->GetValue(); + return true; + } + } + + return false; +} diff --git a/Library/Measure.h b/Library/Measure.h index de7843c5..b4acf766 100644 --- a/Library/Measure.h +++ b/Library/Measure.h @@ -22,6 +22,7 @@ #include #include #include +#include "IfActions.h" #include "Litestep.h" #include "Section.h" @@ -89,6 +90,7 @@ public: void DoChangeAction(bool execute = true); static Measure* Create(const WCHAR* measure, MeterWindow* meterWindow, const WCHAR* name); + static bool GetCurrentMeasureValue(const WCHAR* str, int len, double* value, void* context); protected: Measure(MeterWindow* meterWindow, const WCHAR* name); @@ -117,15 +119,7 @@ protected: UINT m_AveragePos; UINT m_AverageSize; - int64_t m_IfEqualValue; // The limit for the IfEqual action - double m_IfAboveValue; // The limit for the IfAbove action - double m_IfBelowValue; // The limit for the IfBelow action - std::wstring m_IfEqualAction; // The IfEqual action - std::wstring m_IfAboveAction; // The IfAbove action - std::wstring m_IfBelowAction; // The IfBelow action - bool m_IfEqualCommitted; // True when the IfEqual action is executed - bool m_IfAboveCommitted; // True when the IfAbove action is executed - bool m_IfBelowCommitted; // True when the IfBelow action is executed + IfActions m_IfActions; bool m_Disabled; // Status of the measure bool m_Paused; bool m_Initialized;