From dc3c767efa504973dfd4b259cc68053e59ed8e5a Mon Sep 17 00:00:00 2001 From: Kimmo Pekkola Date: Wed, 26 Aug 2009 17:37:15 +0000 Subject: [PATCH] It's now possible to use the measures as if they were variables (use [MeasureName] instead #VariableName#). Set DynamicVariables=1 for all meters and measures which refer to other measures. New bang !RainmeterSetVariable can be used to change the value of a variable (DynamicVariables must be 1 in places where the variable is used). --- Library/ConfigParser.cpp | 294 ++++++++++++++++++++++++++++++--------- Library/ConfigParser.h | 14 ++ Library/Measure.cpp | 5 +- Library/Measure.h | 3 + Library/Meter.cpp | 2 + Library/Meter.h | 3 + Library/MeterWindow.cpp | 30 +++- Library/MeterWindow.h | 3 +- Library/Rainmeter.cpp | 33 ++++- Library/Rainmeter.h | 7 +- 10 files changed, 317 insertions(+), 77 deletions(-) diff --git a/Library/ConfigParser.cpp b/Library/ConfigParser.cpp index 7b517dd3..a1ce0384 100644 --- a/Library/ConfigParser.cpp +++ b/Library/ConfigParser.cpp @@ -60,6 +60,9 @@ void CConfigParser::Initialize(LPCTSTR filename, CRainmeter* pRainmeter) m_Filename = filename; m_Variables.clear(); + m_Measures.clear(); + + ReadIniFile(m_Filename); // Set the default variables if (pRainmeter) @@ -99,45 +102,26 @@ void CConfigParser::Initialize(LPCTSTR filename, CRainmeter* pRainmeter) */ void CConfigParser::ReadVariables() { - DWORD size; - TCHAR* buffer; - TCHAR* variable; - int bufferSize = 4096; - bool loop; + std::vector listVariables = GetKeys(L"Variables"); - do + for (size_t i = 0; i < listVariables.size(); i++) { - loop = false; - buffer = new TCHAR[bufferSize]; - - size = GetPrivateProfileString(L"Variables", NULL, L"", buffer, bufferSize, m_Filename.c_str()); - - if (size == bufferSize - 1) - { - // Buffer too small, increase it and retry - delete [] buffer; - bufferSize *= 2; - loop = true; - } - - } while(loop); - - if (size > 0) - { - // Read all variables - WCHAR* pos = buffer; - while(wcslen(pos) > 0) - { - std::wstring variable = ReadString(L"Variables", pos, L""); - if (!variable.empty()) - { - m_Variables[pos] = variable; - } - - pos = pos + wcslen(pos) + 1; - } + m_Variables[listVariables[i]] = GetValue(L"Variables", listVariables[i], L""); } - delete [] buffer; +} + +//============================================================================== +/** +** Sets a new value for the variable. The DynamicVariables must be set to 1 in the +** meter/measure for the changes to be applied. +** +** \param strVariable +** \param strValue +** \return void +*/ +void CConfigParser::SetVariable(const std::wstring& strVariable, const std::wstring& strValue) +{ + m_Variables[strVariable] = strValue; } /* @@ -148,32 +132,12 @@ void CConfigParser::ReadVariables() const std::wstring& CConfigParser::ReadString(LPCTSTR section, LPCTSTR key, LPCTSTR defValue) { static std::wstring result; - DWORD size; - TCHAR* buffer; - int bufferSize = 4096; - bool loop; - do + result = GetValue(section, key, defValue); + if (result == defValue) { - loop = false; - buffer = new TCHAR[bufferSize]; - buffer[0] = 0; - - size = GetPrivateProfileString(section, key, defValue, buffer, bufferSize, m_Filename.c_str()); - - if (size == bufferSize - 1) - { - // Buffer too small, increase it and retry - delete [] buffer; - bufferSize *= 2; - loop = true; - } - - } while(loop); - - result = buffer; - - delete [] buffer; + return result; + } // Check Litestep vars if (Rainmeter && !Rainmeter->GetDummyLitestep()) @@ -188,13 +152,13 @@ const std::wstring& CConfigParser::ReadString(LPCTSTR section, LPCTSTR key, LPCT } } - result = CRainmeter::ExpandEnvironmentVariables(result); + CRainmeter::ExpandEnvironmentVariables(result); // Check for variables (#VAR#) size_t start = 0; size_t end = std::wstring::npos; size_t pos = std::wstring::npos; - loop = true; + bool loop = true; do { @@ -229,9 +193,59 @@ const std::wstring& CConfigParser::ReadString(LPCTSTR section, LPCTSTR key, LPCT } } while(loop); + // Check for measures ([Measure]) + if (!m_Measures.empty()) + { + start = 0; + end = std::wstring::npos; + pos = std::wstring::npos; + loop = true; + do + { + pos = result.find(L'[', start); + if (pos != std::wstring::npos) + { + size_t end = result.find(L']', pos + 1); + if (end != std::wstring::npos) + { + std::wstring var(result.begin() + pos + 1, result.begin() + end); + + std::map::iterator iter = m_Measures.find(var); + if (iter != m_Measures.end()) + { + std::wstring value = (*iter).second->GetStringValue(true, 1, 1, false); + + // Measure found, replace it with the value + result.replace(result.begin() + pos, result.begin() + end + 1, value); + start = pos + value.length(); + } + else + { + start = end; + } + } + else + { + loop = false; + } + } + else + { + loop = false; + } + } while(loop); + } + return result; } +void CConfigParser::AddMeasure(CMeasure* pMeasure) +{ + if (pMeasure) + { + m_Measures[pMeasure->GetName()] = pMeasure; + } +} double CConfigParser::ReadFloat(LPCTSTR section, LPCTSTR key, double defValue) { @@ -405,3 +419,159 @@ Color CConfigParser::ParseColor(LPCTSTR string) return Color(A, R, G, B); } + +//============================================================================== +/** +** Reads the given ini file and fills the m_Values and m_Keys maps. +** +** \param iniFile The ini file to be read. +*/ +void CConfigParser::ReadIniFile(const std::wstring& iniFile) +{ + m_Keys.clear(); + m_Values.clear(); + + // Get all the sections (i.e. different meters) + WCHAR* items = new WCHAR[MAX_LINE_LENGTH]; + int size = MAX_LINE_LENGTH; + + // Get all the sections + while(true) + { + items[0] = 0; + int res = GetPrivateProfileString( NULL, NULL, NULL, items, size, iniFile.c_str()); + if (res == 0) return; // File not found + if (res != size - 2) break; // Fits in the buffer + + delete [] items; + size *= 2; + items = new WCHAR[size]; + }; + + // Read the sections + WCHAR* pos = items; + while(wcslen(pos) > 0) + { + m_Keys[pos] = std::vector(); + pos = pos + wcslen(pos) + 1; + } + + // Read the keys and values + int bufferSize = MAX_LINE_LENGTH; + TCHAR* buffer = new TCHAR[bufferSize]; + + stdext::hash_map >::iterator iter = m_Keys.begin(); + for ( ; iter != m_Keys.end(); iter++) + { + while(true) + { + items[0] = 0; + int res = GetPrivateProfileString((*iter).first.c_str(), NULL, NULL, items, size, iniFile.c_str()); + if (res != size - 2) break; // Fits in the buffer + + delete [] items; + size *= 2; + items = new WCHAR[size]; + }; + + WCHAR* pos = items; + while(wcslen(pos) > 0) + { + std::wstring strKey = pos; + + while(true) + { + buffer[0] = 0; + int res = GetPrivateProfileString((*iter).first.c_str(), strKey.c_str(), L"", buffer, bufferSize, iniFile.c_str()); + if (res != size - 2) break; // Fits in the buffer + + delete [] buffer; + bufferSize *= 2; + buffer = new WCHAR[size]; + }; + + SetValue((*iter).first, strKey, buffer); + + pos = pos + wcslen(pos) + 1; + } + } + delete [] buffer; + delete [] items; +} + +//============================================================================== +/** +** Sets the value for the key under the given section. +** +** \param strSection The name of the section. +** \param strKey The name of the key. +** \param strValue The value for the key. +*/ +void CConfigParser::SetValue(const std::wstring& strSection, const std::wstring& strKey, const std::wstring& strValue) +{ + stdext::hash_map >::iterator iter = m_Keys.find(strSection); + if (iter != m_Keys.end()) + { + std::vector& array = (*iter).second; + array.push_back(strKey); + } + m_Values[strSection + L"::" + strKey] = strValue; +} + +//============================================================================== +/** +** Returns the value for the key under the given section. +** +** \param strSection The name of the section. +** \param strKey The name of the key. +** \param strDefault The default value for the key. +** \return The value for the key. +*/ +const std::wstring& CConfigParser::GetValue(const std::wstring& strSection, const std::wstring& strKey, const std::wstring& strDefault) +{ + stdext::hash_map::iterator iter = m_Values.find(strSection + L"::" + strKey); + if (iter != m_Values.end()) + { + return (*iter).second; + } + + return strDefault; +} + +//============================================================================== +/** +** Returns the list of sections in the ini file. +** +** \return A list of sections in the ini file. +*/ +std::vector CConfigParser::GetSections() +{ + std::vector listSections; + + stdext::hash_map >::iterator iter = m_Keys.begin(); + for ( ; iter != m_Keys.end(); iter++) + { + listSections.push_back((*iter).first); + } + + return listSections; +} + +//============================================================================== +/** +** Returns a list of keys under the given section. +** +** \param strSection The name of the section. +** \return A list of keys under the given section. +*/ +std::vector CConfigParser::GetKeys(const std::wstring& strSection) +{ + stdext::hash_map >::iterator iter = m_Keys.find(strSection); + if (iter != m_Keys.end()) + { + return (*iter).second; + } + + return std::vector(); +} + diff --git a/Library/ConfigParser.h b/Library/ConfigParser.h index 3c5788d5..134d386c 100644 --- a/Library/ConfigParser.h +++ b/Library/ConfigParser.h @@ -23,10 +23,12 @@ #include #include #include +#include #include #include "ccalc-0.5.1/mparser.h" class CRainmeter; +class CMeasure; class CConfigParser { @@ -35,6 +37,8 @@ public: ~CConfigParser(); void Initialize(LPCTSTR filename, CRainmeter* pRainmeter); + void AddMeasure(CMeasure* pMeasure); + void SetVariable(const std::wstring& strVariable, const std::wstring& strValue); const std::wstring& ReadString(LPCTSTR section, LPCTSTR key, LPCTSTR defValue); double ReadFloat(LPCTSTR section, LPCTSTR key, double defValue); @@ -50,10 +54,20 @@ private: Gdiplus::Color ParseColor(LPCTSTR string); std::vector Tokenize(const std::wstring& str, const std::wstring delimiters); + void ReadIniFile(const std::wstring& strFileName); + void SetValue(const std::wstring& strSection, const std::wstring& strKey, const std::wstring& strValue); + const std::wstring& GetValue(const std::wstring& strSection, const std::wstring& strKey, const std::wstring& strDefault); + std::vector GetSections(); + std::vector GetKeys(const std::wstring& strSection); + std::map m_Variables; std::wstring m_Filename; hqMathParser* m_Parser; + std::map m_Measures; + + stdext::hash_map > m_Keys; + stdext::hash_map m_Values; }; #endif diff --git a/Library/Measure.cpp b/Library/Measure.cpp index 47b59f56..17b1587e 100644 --- a/Library/Measure.cpp +++ b/Library/Measure.cpp @@ -67,6 +67,7 @@ CMeasure::CMeasure(CMeterWindow* meterWindow) m_AveragePos = 0; m_AverageSize = 0; m_MeterWindow = meterWindow; + m_DynamicVariables = false; } /* @@ -108,7 +109,9 @@ void CMeasure::ReadConfig(CConfigParser& parser, const WCHAR* section) m_IfEqualAction = parser.ReadString(section, L"IfEqualAction", L""); m_AverageSize = parser.ReadInt(section, L"AverageSize", 0); - + + m_DynamicVariables = 0!=parser.ReadInt(section, L"DynamicVariables", 0); + std::wstring subs; subs = parser.ReadString(section, L"Substitute", L""); if (!ParseSubstitute(subs)) diff --git a/Library/Measure.h b/Library/Measure.h index 7ddcf462..4e99a0e9 100644 --- a/Library/Measure.h +++ b/Library/Measure.h @@ -43,6 +43,8 @@ public: void Enable() { m_Disabled = false; }; bool IsDisabled() { return m_Disabled; }; + bool HasDynamicVariables() { return m_DynamicVariables; } + virtual void ExecuteBang(const WCHAR* args); double GetValue(); @@ -64,6 +66,7 @@ protected: std::wstring ExtractWord(std::wstring& buffer); const WCHAR* CheckSubstitute(const WCHAR* buffer); + bool m_DynamicVariables; // If true, the measure contains dynamic variables bool m_Invert; // If true, the value should be inverted bool m_LogMaxValue; // If true, The maximum & minimum values are logged double m_MinValue; // The minimum value (so far) diff --git a/Library/Meter.cpp b/Library/Meter.cpp index bf3ff6dd..ea51b209 100644 --- a/Library/Meter.cpp +++ b/Library/Meter.cpp @@ -62,6 +62,7 @@ CMeter::CMeter(CMeterWindow* meterWindow) m_SolidAngle = 0.0; m_MeterWindow = meterWindow; m_AntiAlias = false; + m_DynamicVariables = false; } /* @@ -267,6 +268,7 @@ void CMeter::ReadConfig(const WCHAR* section) m_UpdateDivider = parser.ReadInt(section, L"UpdateDivider", 1); m_UpdateCounter = m_UpdateDivider; m_AntiAlias = 0!=parser.ReadInt(section, L"AntiAlias", 0); + m_DynamicVariables = 0!=parser.ReadInt(section, L"DynamicVariables", 0); std::vector matrix = parser.ReadFloats(section, L"TransformationMatrix"); if (matrix.size() == 6) diff --git a/Library/Meter.h b/Library/Meter.h index 533204cf..1f71bc40 100644 --- a/Library/Meter.h +++ b/Library/Meter.h @@ -39,6 +39,8 @@ public: virtual bool Draw(Gdiplus::Graphics& graphics); virtual void BindMeasure(std::list& measures); virtual bool HasActiveTransition() { return false; } + + bool HasDynamicVariables() { return m_DynamicVariables; } virtual int GetH() { return m_Hidden ? 0 : m_H; }; virtual int GetW() { return m_Hidden ? 0 : m_W; }; @@ -100,6 +102,7 @@ protected: int m_H; // Height of the meter bool m_Hidden; // Status of the meter CMeter* m_RelativeMeter; + bool m_DynamicVariables; // If true, the measure contains dynamic variables static int c_OldX; static int c_OldY; diff --git a/Library/MeterWindow.cpp b/Library/MeterWindow.cpp index 5e783cab..02cdce7b 100644 --- a/Library/MeterWindow.cpp +++ b/Library/MeterWindow.cpp @@ -247,8 +247,8 @@ int CMeterWindow::Initialize(CRainmeter& Rainmeter) if (DwmSetWindowAttribute) { DwmSetWindowAttribute(m_Window, DWMWA_EXCLUDED_FROM_PEEK, &bValue, sizeof(bValue)); - } - FreeLibrary(h); + } + FreeLibrary(h); } // Gotta have some kind of buffer during initialization @@ -689,6 +689,20 @@ void CMeterWindow::RunBang(BANGCOMMAND bang, const WCHAR* arg) // Quit needs to be delayed since it crashes if done during Update() PostMessage(m_Window, WM_DELAYED_QUIT, (WPARAM)NULL, (LPARAM)NULL); break; + + case BANG_SETVARIABLE: + pos = wcschr(arg, ' '); + if (pos != NULL) + { + std::wstring strVariable(arg, pos - arg); + std::wstring strValue(pos + 1); + m_Parser.SetVariable(strVariable, strValue); + } + else + { + DebugLog(L"Cannot parse parameters for !RainmeterSetVariable"); + } + break; } } @@ -1338,7 +1352,7 @@ void CMeterWindow::ReadSkin() } else { - wsprintf(buffer, L"%i.%i", appVersion / 1000000, appVersion / 1000); + wsprintf(buffer, L"%i.%i", appVersion / 1000000, (appVersion / 1000) % 1000); } text = L"This skin needs Rainmeter version "; text += buffer; @@ -1428,6 +1442,8 @@ void CMeterWindow::ReadSkin() measure->SetName(pos); measure->ReadConfig(m_Parser, pos); m_Measures.push_back(measure); + + m_Parser.AddMeasure(measure); } catch (CError& error) { @@ -1857,6 +1873,10 @@ void CMeterWindow::Update(bool nodraw) { try { + if ((*i)->HasDynamicVariables()) + { + (*i)->ReadConfig(m_Parser, (*i)->GetName()); + } (*i)->Update(); } catch (CError& error) @@ -1871,6 +1891,10 @@ void CMeterWindow::Update(bool nodraw) { try { + if ((*j)->HasDynamicVariables()) + { + (*j)->ReadConfig((*j)->GetName()); + } (*j)->Update(); } catch (CError& error) diff --git a/Library/MeterWindow.h b/Library/MeterWindow.h index 251fdf74..ac93feee 100644 --- a/Library/MeterWindow.h +++ b/Library/MeterWindow.h @@ -99,7 +99,8 @@ enum BANGCOMMAND BANG_ABOUT, BANG_MOVEMETER, BANG_PLUGIN, - BANG_QUIT + BANG_QUIT, + BANG_SETVARIABLE }; typedef struct diff --git a/Library/Rainmeter.cpp b/Library/Rainmeter.cpp index aa7890f9..5102ae67 100644 --- a/Library/Rainmeter.cpp +++ b/Library/Rainmeter.cpp @@ -580,6 +580,17 @@ void RainmeterQuit(HWND, const char* arg) BangWithArgs(BANG_QUIT, ConvertToWide(arg).c_str(), 0); } +/* +** RainmeterSetVariable +** +** Callback for the !RainmeterSetVariable bang +** +*/ +void RainmeterSetVariable(HWND, const char* arg) +{ + BangWithArgs(BANG_SETVARIABLE, ConvertToWide(arg).c_str(), 2); +} + // ----------------------------------------------------------------------------------------------- // // The class starts here @@ -690,7 +701,8 @@ int CRainmeter::Initialize(HWND Parent, HINSTANCE Instance, LPCSTR szPath) // If the ini file doesn't exist in the program folder store it to the %APPDATA% instead so that things work better in Vista/Win7 if (_waccess(m_IniFile.c_str(), 0) == -1) { - m_IniFile = ExpandEnvironmentVariables(L"%APPDATA%\\Rainmeter\\Rainmeter.ini"); + m_IniFile = L"%APPDATA%\\Rainmeter\\Rainmeter.ini"; + ExpandEnvironmentVariables(m_IniFile); bDefaultIniLocation = true; // If the ini file doesn't exist in the %APPDATA% either, create a default rainmeter.ini file. @@ -709,7 +721,7 @@ int CRainmeter::Initialize(HWND Parent, HINSTANCE Instance, LPCSTR szPath) iniFile = iniFile.substr(1, iniFile.length() - 2); } - iniFile = ExpandEnvironmentVariables(iniFile); + ExpandEnvironmentVariables(iniFile); if (iniFile[iniFile.length() - 1] == L'\\') { @@ -738,7 +750,8 @@ int CRainmeter::Initialize(HWND Parent, HINSTANCE Instance, LPCSTR szPath) WCHAR tmpSz[MAX_LINE_LENGTH]; if (GetPrivateProfileString(L"Rainmeter", L"SkinPath", L"", tmpSz, MAX_LINE_LENGTH, m_IniFile.c_str()) > 0) { - m_SkinPath = ExpandEnvironmentVariables(tmpSz); + m_SkinPath = tmpSz; + ExpandEnvironmentVariables(m_SkinPath); } else if (bDefaultIniLocation) { @@ -892,6 +905,7 @@ int CRainmeter::Initialize(HWND Parent, HINSTANCE Instance, LPCSTR szPath) AddBangCommand("!RainmeterMoveMeter", RainmeterMoveMeter); AddBangCommand("!RainmeterPluginBang", RainmeterPluginBang); AddBangCommand("!RainmeterQuit", RainmeterQuit); + AddBangCommand("!RainmeterSetVariable", RainmeterSetVariable); } // Create meter windows for active configs @@ -1129,6 +1143,7 @@ void CRainmeter::Quit(HINSTANCE dllInst) RemoveBangCommand("!RainmeterMoveMeter"); RemoveBangCommand("!RainmeterPluginBang"); RemoveBangCommand("!RainmeterQuit"); + RemoveBangCommand("!RainmeterSetVariable"); } } @@ -1337,6 +1352,10 @@ BOOL CRainmeter::ExecuteBang(const std::wstring& bang, const std::wstring& arg, { BangWithArgs(BANG_PLUGIN, arg.c_str(), 1); } + else if (wcsicmp(bang.c_str(), L"!RainmeterSetVariable") == 0) + { + BangWithArgs(BANG_SETVARIABLE, arg.c_str(), 2); + } else if (wcsicmp(bang.c_str(), L"!RainmeterLsBoxHook") == 0) { // Nothing to do here (this works only with Litestep) @@ -2064,7 +2083,8 @@ void CRainmeter::TestSettingsFile(bool bDefaultIniLocation) if (!bDefaultIniLocation) { - std::wstring strTarget = ExpandEnvironmentVariables(L"%APPDATA%\\Rainmeter\\"); + std::wstring strTarget = L"%APPDATA%\\Rainmeter\\"; + ExpandEnvironmentVariables(strTarget); error += L"You should quit Rainmeter and move the settings file from\n\n"; error += m_IniFile; @@ -2101,7 +2121,7 @@ std::wstring CRainmeter::ExtractPath(const std::wstring& strFilePath) return L"."; } -std::wstring CRainmeter::ExpandEnvironmentVariables(const std::wstring strPath) +void CRainmeter::ExpandEnvironmentVariables(std::wstring& strPath) { if (strPath.find(L'%') != std::wstring::npos) { @@ -2111,12 +2131,11 @@ std::wstring CRainmeter::ExpandEnvironmentVariables(const std::wstring strPath) DWORD ret = ExpandEnvironmentStrings(strPath.c_str(), buffer, 4096); if (ret != 0 && ret < 4096) { - return buffer; + strPath = buffer; } else { DebugLog(L"Unable to expand the environment strings for string: %s", strPath.c_str()); } } - return strPath; } diff --git a/Library/Rainmeter.h b/Library/Rainmeter.h index cd0e479f..89cc9f7b 100644 --- a/Library/Rainmeter.h +++ b/Library/Rainmeter.h @@ -32,13 +32,13 @@ #define MAKE_VER(major, minor1, minor2) major * 1000000 + minor1 * 1000 + minor2 #define APPNAME L"Rainmeter" -#define APPVERSION L"1.0" +#define APPVERSION L"1.1" #ifdef _WIN64 #define APPBITS L"(64-bit)" #else #define APPBITS L"(32-bit)" #endif -#define RAINMETER_VERSION MAKE_VER(1, 0, 0) +#define RAINMETER_VERSION MAKE_VER(1, 1, 0) enum PLATFORM { @@ -70,6 +70,7 @@ void RainmeterResetStats(HWND, const char* arg); void RainmeterMoveMeter(HWND, const char* arg); void RainmeterPluginBang(HWND, const char* arg); void RainmeterQuit(HWND, const char* arg); +void RainmeterSetVariable(HWND, const char* arg); void BangWithArgs(BANGCOMMAND bang, const WCHAR* arg, size_t numOfArgs); @@ -162,7 +163,7 @@ public: static PLATFORM IsNT(); static std::wstring ExtractPath(const std::wstring& strFilePath); - static std::wstring ExpandEnvironmentVariables(const std::wstring strPath); + static void ExpandEnvironmentVariables(std::wstring& strPath); private: void CreateMeterWindow(std::wstring path, std::wstring config, std::wstring iniFile);