From 64888434c948981acf64487f04095112fb7e2e74 Mon Sep 17 00:00:00 2001 From: spx Date: Tue, 3 Aug 2010 15:10:42 +0000 Subject: [PATCH] - Added !RainmeterWriteKeyValue. (based on MattKing's code) !RainmeterWriteKeyValue "Section" "Key" "Value" ("FileSpec"), where "FileSpec" is an optional parameter. If FileSpec is not present, the change is automatically done in the currently running skin file. If the bang is executed from command line, FileSpec is required. - Some improvements of MouseOver/Leave detection. - Fixed the issue that window dragging brings on MouseLeave if "Show window contents while dragging" is not set. - Fixed the issue that window dragging can't be begun by dragging the Button. - Fixed the issue that a reaction of the upper Button penetrates to the lower Buttons or the other window. --- Library/ConfigParser.cpp | 118 +------- Library/ConfigParser.h | 7 +- Library/MeterButton.cpp | 99 ++++--- Library/MeterButton.h | 2 + Library/MeterWindow.cpp | 605 +++++++++++++++++++++++++-------------- Library/MeterWindow.h | 15 +- Library/Rainmeter.cpp | 237 +++++++++++---- Library/Rainmeter.h | 6 +- Library/System.cpp | 142 +++++++++ Library/System.h | 6 + Library/TrayWindow.cpp | 2 - 11 files changed, 802 insertions(+), 437 deletions(-) diff --git a/Library/ConfigParser.cpp b/Library/ConfigParser.cpp index 17e40134..83408a34 100644 --- a/Library/ConfigParser.cpp +++ b/Library/ConfigParser.cpp @@ -78,15 +78,11 @@ void CConfigParser::Initialize(LPCTSTR filename, CRainmeter* pRainmeter, CMeterW // Set the SCREENAREA/WORKAREA variables for present monitor SetAutoSelectedMonitorVariables(meterWindow); - if (meterWindow) - { - GetIniFileMappingList(); - } + std::vector iniFileMappings; + CSystem::GetIniFileMappingList(iniFileMappings); - ReadIniFile(m_Filename); + ReadIniFile(iniFileMappings, m_Filename); ReadVariables(); - - m_IniFileMappings.clear(); } /* @@ -869,7 +865,7 @@ Color CConfigParser::ParseColor(LPCTSTR string) ** ** \param iniFile The ini file to be read. */ -void CConfigParser::ReadIniFile(const std::wstring& iniFile, int depth) +void CConfigParser::ReadIniFile(const std::vector& iniFileMappings, const std::wstring& iniFile, int depth) { if (depth > 100) // Is 100 enough to assume the include loop never ends? { @@ -885,24 +881,14 @@ void CConfigParser::ReadIniFile(const std::wstring& iniFile, int depth) } // Avoid "IniFileMapping" - std::wstring iniRead = GetAlternateFileName(iniFile); - bool alternate = false; + std::wstring iniRead = CSystem::GetTemporaryFile(iniFileMappings, iniFile); + bool temporary = (!iniRead.empty() && iniRead != L"<>"); - if (!iniRead.empty()) + if (temporary) { - // Copy iniFile to temporary directory - if (CRainmeter::CopyFiles(iniFile, iniRead)) - { - if (CRainmeter::GetDebug()) DebugLog(L"Reading file: %s (Alternate: %s)", iniFile.c_str(), iniRead.c_str()); - alternate = true; - } - else // copy failed - { - DeleteFile(iniRead.c_str()); - } + if (CRainmeter::GetDebug()) DebugLog(L"Reading file: %s (Temp: %s)", iniFile.c_str(), iniRead.c_str()); } - - if (!alternate) + else { if (CRainmeter::GetDebug()) DebugLog(L"Reading file: %s", iniFile.c_str()); iniRead = iniFile; @@ -920,7 +906,7 @@ void CConfigParser::ReadIniFile(const std::wstring& iniFile, int depth) if (res == 0) // File not found { delete [] items; - if (alternate) DeleteFile(iniRead.c_str()); + if (temporary) CSystem::RemoveFile(iniRead); return; } if (res < size - 2) break; // Fits in the buffer @@ -991,7 +977,7 @@ void CConfigParser::ReadIniFile(const std::wstring& iniFile, int depth) // It's a relative path so add the current path as a prefix strIncludeFile = CRainmeter::ExtractPath(iniFile) + strIncludeFile; } - ReadIniFile(strIncludeFile, depth + 1); + ReadIniFile(iniFileMappings, strIncludeFile, depth + 1); } else { @@ -1003,7 +989,7 @@ void CConfigParser::ReadIniFile(const std::wstring& iniFile, int depth) } delete [] buffer; delete [] items; - if (alternate) DeleteFile(iniRead.c_str()); + if (temporary) CSystem::RemoveFile(iniRead); } //============================================================================== @@ -1086,83 +1072,3 @@ std::vector CConfigParser::GetKeys(const std::wstring& strSection) return std::vector(); } - -void CConfigParser::GetIniFileMappingList() -{ - HKEY hKey; - LONG ret; - - ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\IniFileMapping", 0, KEY_ENUMERATE_SUB_KEYS, &hKey); - if (ret == ERROR_SUCCESS) - { - WCHAR buffer[MAX_PATH]; - DWORD index = 0, cch = MAX_PATH; - - while (true) - { - ret = RegEnumKeyEx(hKey, index++, buffer, &cch, NULL, NULL, NULL, NULL); - if (ret == ERROR_NO_MORE_ITEMS) break; - - if (ret == ERROR_SUCCESS) - { - m_IniFileMappings.push_back(buffer); - } - cch = MAX_PATH; - } - RegCloseKey(hKey); - } -} - -std::wstring CConfigParser::GetAlternateFileName(const std::wstring &iniFile) -{ - std::wstring alternate; - - if (!m_IniFileMappings.empty()) - { - std::wstring::size_type pos = iniFile.find_last_of(L'\\'); - std::wstring filename; - - if (pos != std::wstring::npos) - { - filename = iniFile.substr(pos + 1); - } - else - { - filename = iniFile; - } - - for (size_t i = 0; i < m_IniFileMappings.size(); ++i) - { - if (wcsicmp(m_IniFileMappings[i].c_str(), filename.c_str()) == 0) - { - WCHAR buffer[4096]; - - GetTempPath(4096, buffer); - alternate = buffer; - if (GetTempFileName(alternate.c_str(), L"cfg", 0, buffer) != 0) - { - alternate = buffer; - - std::wstring tmp = GetAlternateFileName(alternate); - if (tmp.empty()) - { - return alternate; - } - else // alternate is reserved - { - DeleteFile(alternate.c_str()); - return tmp; - } - } - else // failed - { - DebugLog(L"Unable to create a temporary file to: %s", alternate.c_str()); - alternate.clear(); - break; - } - } - } - } - - return alternate; -} diff --git a/Library/ConfigParser.h b/Library/ConfigParser.h index 91c72550..be059a72 100644 --- a/Library/ConfigParser.h +++ b/Library/ConfigParser.h @@ -71,16 +71,13 @@ private: bool ReplaceVariables(std::wstring& result); bool ReplaceMeasures(std::wstring& result); - void ReadIniFile(const std::wstring& strFileName, int depth = 0); + void ReadIniFile(const std::vector& iniFileMappings, const std::wstring& strFileName, int depth = 0); 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 GetKeys(const std::wstring& strSection); void SetAutoSelectedMonitorVariables(CMeterWindow* meterWindow); - void GetIniFileMappingList(); - std::wstring GetAlternateFileName(const std::wstring& iniFile); - static void SetMultiMonitorVariables(bool reset); static void SetMonitorVariable(const std::wstring& strVariable, const std::wstring& strValue); @@ -96,8 +93,6 @@ private: stdext::hash_map > m_Keys; stdext::hash_map m_Values; - std::vector m_IniFileMappings; - static std::map c_MonitorVariables; }; diff --git a/Library/MeterButton.cpp b/Library/MeterButton.cpp index 0fcd7ac4..c95d90aa 100644 --- a/Library/MeterButton.cpp +++ b/Library/MeterButton.cpp @@ -43,7 +43,7 @@ CMeterButton::CMeterButton(CMeterWindow* meterWindow) : CMeter(meterWindow) { for (int i = 0; i < BUTTON_FRAMES; ++i) { - m_Bitmaps[i] = NULL;; + m_Bitmaps[i] = NULL; } m_Bitmap = NULL; m_State = BUTTON_STATE_NORMAL; @@ -235,25 +235,59 @@ void CMeterButton::BindMeasure(std::list& measures) } } -bool CMeterButton::MouseUp(POINT pos, CMeterWindow* window) +/* +** HitTest2 +** +** Checks if the given point is inside the button. +** +*/ +bool CMeterButton::HitTest2(int px, int py, bool checkAlpha) { int x = GetX(); int y = GetY(); + if (m_MouseOver && + px >= x && px < x + m_W && + py >= y && py < y + m_H) + { + if (checkAlpha) + { + if (m_SolidColor.GetA() > 0 || m_SolidColor2.GetA() > 0) + { + return true; + } + + // Check transparent pixels + if (m_Bitmap) + { + Color color; + Status status = m_Bitmap->GetPixel(px - x + m_W * m_State, py - y, &color); + if (status != Ok || color.GetA() > 0) + { + return true; + } + } + else + { + return true; + } + } + else + { + return true; + } + } + return false; +} + +bool CMeterButton::MouseUp(POINT pos, CMeterWindow* window) +{ if (m_State == BUTTON_STATE_DOWN) { - if (m_Clicked && - pos.x >= x && pos.x < x + m_W && - pos.y >= y && pos.y < y + m_H) + if (window && m_Clicked && HitTest2(pos.x, pos.y, true)) { - Color color; - m_Bitmap->GetPixel(pos.x - x + m_W * m_State, pos.y - y, &color); - - if (color.GetA() > 0) - { - // Do a delayed execute or ortherwise !RainmeterRefresh crashes - PostMessage(window->GetWindow(), WM_DELAYED_EXECUTE, (WPARAM)NULL, (LPARAM)m_Command.c_str()); - } + // Do a delayed execute or ortherwise !RainmeterRefresh crashes + PostMessage(window->GetWindow(), WM_DELAYED_EXECUTE, (WPARAM)NULL, (LPARAM)m_Command.c_str()); } m_State = BUTTON_STATE_NORMAL; m_Clicked = false; @@ -266,45 +300,25 @@ bool CMeterButton::MouseUp(POINT pos, CMeterWindow* window) bool CMeterButton::MouseDown(POINT pos) { - int x = GetX(); - int y = GetY(); - - if (pos.x >= x && pos.x < x + m_W && - pos.y >= y && pos.y < y + m_H) + if (HitTest2(pos.x, pos.y, true)) { - Color color; - m_Bitmap->GetPixel(pos.x - x + m_W * m_State, pos.y - y, &color); - - if (color.GetA() > 0) - { - m_State = BUTTON_STATE_DOWN; - m_Clicked = true; - return true; - } + m_State = BUTTON_STATE_DOWN; + m_Clicked = true; + return true; } return false; } bool CMeterButton::MouseMove(POINT pos) { - int x = GetX(); - int y = GetY(); - if (m_Clicked == true) { - if (pos.x >= x && pos.x < x + m_W && - pos.y >= y && pos.y < y + m_H) + if (HitTest2(pos.x, pos.y, true)) { - Color color; - m_Bitmap->GetPixel(pos.x - x + m_W * m_State, pos.y - y, &color); - - if (color.GetA() > 0) + if (m_State == BUTTON_STATE_NORMAL) { - if (m_State == BUTTON_STATE_NORMAL) - { - m_State = BUTTON_STATE_DOWN; - return true; - } + m_State = BUTTON_STATE_DOWN; + return true; } } else @@ -324,8 +338,7 @@ bool CMeterButton::MouseMove(POINT pos) } else { - if (pos.x >= x && pos.x < x + m_W && - pos.y >= y && pos.y < y + m_H) + if (HitTest2(pos.x, pos.y, false)) { if (m_State == BUTTON_STATE_NORMAL) { diff --git a/Library/MeterButton.h b/Library/MeterButton.h index abccf417..0c43ca40 100644 --- a/Library/MeterButton.h +++ b/Library/MeterButton.h @@ -41,6 +41,8 @@ public: bool MouseDown(POINT pos); private: + bool HitTest2(int px, int py, bool checkAlpha); + Gdiplus::Bitmap* m_Bitmap; // The bitmap Gdiplus::CachedBitmap* m_Bitmaps[BUTTON_FRAMES]; // The cached bitmaps std::wstring m_ImageName; // Name of the image diff --git a/Library/MeterWindow.cpp b/Library/MeterWindow.cpp index aa742c34..af2825e9 100644 --- a/Library/MeterWindow.cpp +++ b/Library/MeterWindow.cpp @@ -323,6 +323,9 @@ void CMeterWindow::Refresh(bool init, bool all) KillTimer(m_Window, FADETIMER); KillTimer(m_Window, TRANSITIONTIMER); + m_MouseOver = false; + SetMouseLeaveEvent(true); + std::list::iterator i = m_Measures.begin(); for( ; i != m_Measures.end(); ++i) { @@ -417,6 +420,52 @@ void CMeterWindow::Refresh(bool init, bool all) } } +void CMeterWindow::SetMouseLeaveEvent(bool cancel) +{ + if (!cancel && !m_MouseOver) return; + + // Check whether the mouse event is set + TRACKMOUSEEVENT tme = {sizeof(TRACKMOUSEEVENT)}; + tme.hwndTrack = m_Window; + tme.dwFlags = TME_QUERY; + + if (TrackMouseEvent(&tme) != 0) + { + if (cancel) + { + if (tme.dwFlags == 0) return; + } + else + { + if (m_WindowDraggable) + { + if (tme.dwFlags == (TME_LEAVE | TME_NONCLIENT)) return; + } + else + { + if (tme.dwFlags == TME_LEAVE) return; + } + } + } + + tme.cbSize = sizeof(TRACKMOUSEEVENT); + tme.hwndTrack = m_Window; + + // Cancel the mouse event set before + tme.dwFlags |= TME_CANCEL; + TrackMouseEvent(&tme); + + if (cancel) return; + + // Set the mouse event + tme.dwFlags = TME_LEAVE; + if (m_WindowDraggable) + { + tme.dwFlags |= TME_NONCLIENT; + } + TrackMouseEvent(&tme); +} + void CMeterWindow::MapCoordsToScreen(int& x, int& y, int w, int h) { // Check that the window is inside the screen area @@ -522,7 +571,7 @@ void CMeterWindow::ChangeZPos(ZPOSITION zPos, bool all) if (CSystem::GetShowDesktop()) { // Insert after the tray window temporarily to keep order - winPos = Rainmeter->GetTrayWindow()->GetWindow(); + winPos = m_Rainmeter->GetTrayWindow()->GetWindow(); } else { @@ -2547,40 +2596,35 @@ LRESULT CMeterWindow::OnTimer(WPARAM wParam, LPARAM lParam) } else if(wParam == MOUSETIMER) { - ShowWindowIfAppropriate(); - - POINT pos; - GetCursorPos(&pos); - MapWindowPoints(NULL, m_Window, &pos, 1); - - if (!m_MouseLeaveAction.empty()) + if (!m_Rainmeter->IsMenuActive() && !m_Dragging) { - // Check mouse leave actions - DoAction(pos.x, pos.y, MOUSE_LEAVE, false); - } + ShowWindowIfAppropriate(); - if (m_WindowZPosition == ZPOSITION_ONTOPMOST) - { - ChangeZPos(ZPOSITION_ONTOPMOST); - } - - // Handle buttons - bool redraw = false; - std::list::const_iterator j = m_Meters.begin(); - for( ; j != m_Meters.end(); ++j) - { - // Hidden meters are ignored - if ((*j)->IsHidden()) continue; - - CMeterButton* button = dynamic_cast(*j); - if (button) + if (m_WindowZPosition == ZPOSITION_ONTOPMOST) { - redraw |= button->MouseMove(pos); + ChangeZPos(ZPOSITION_ONTOPMOST); + } + + if (m_MouseOver) + { + POINT pos; + GetCursorPos(&pos); + if (WindowFromPoint(pos) == m_Window) + { + SetMouseLeaveEvent(false); + + MapWindowPoints(NULL, m_Window, &pos, 1); + DoMoveAction(pos.x, pos.y, MOUSE_OVER); + + // Handle buttons + HandleButtons(pos, BUTTONPROC_MOVE, NULL, false); + } + else + { + // Run all mouse leave actions + OnMouseLeave(0, 0); + } } - } - if (redraw) - { - Redraw(); } } else if(wParam == FADETIMER) @@ -2684,29 +2728,14 @@ void CMeterWindow::ShowWindowIfAppropriate() { bool inside = false; bool keyDown = GetKeyState(VK_CONTROL) & 0x8000 || GetKeyState(VK_SHIFT) & 0x8000 || GetKeyState(VK_MENU) & 0x8000; + POINT pos; - RECT rect; - - GetWindowRect(m_Window, &rect); GetCursorPos(&pos); - - if(rect.left <= pos.x && rect.right > pos.x && - rect.top <= pos.y && rect.bottom > pos.y) + if (WindowFromPoint(pos) == m_Window) { - // Check transparent pixels - if (m_DoubleBuffer) - { - Color color; - m_DoubleBuffer->GetPixel(pos.x - rect.left, pos.y - rect.top, &color); - if (color.GetA() != 0) - { - inside = true; - } - } - else - { - inside = true; - } + MapWindowPoints(NULL, m_Window, &pos, 1); + + inside = HitTest(pos.x, pos.y); } if (m_ClickThrough) @@ -2765,6 +2794,92 @@ void CMeterWindow::ShowWindowIfAppropriate() } } +/* +** HitTest +** +** Checks if the given point is inside the window. +** +*/ +bool CMeterWindow::HitTest(int x, int y) +{ + if (x >= 0 && y >= 0 && x < m_WindowW && y < m_WindowH) + { + // Check transparent pixels + if (m_DoubleBuffer) + { + Color color; + Status status = m_DoubleBuffer->GetPixel(x, y, &color); + if (status != Ok || color.GetA() > 0) + { + return true; + } + } + else + { + return true; + } + } + + return false; +} + +/* +** HandleButtons +** +** Handles all buttons and cursor. +** Note that meterWindow parameter is used if proc is BUTTONPROC_UP. +** +*/ +void CMeterWindow::HandleButtons(POINT pos, BUTTONPROC proc, CMeterWindow* meterWindow, bool changeCursor) +{ + bool redraw = false; + bool drawCursor = false; + + std::list::const_reverse_iterator j = m_Meters.rbegin(); + for( ; j != m_Meters.rend(); ++j) + { + // Hidden meters are ignored + if ((*j)->IsHidden()) continue; + + CMeterButton* button = dynamic_cast(*j); + if (button) + { + switch (proc) + { + case BUTTONPROC_DOWN: + redraw |= button->MouseDown(pos); + break; + + case BUTTONPROC_UP: + redraw |= button->MouseUp(pos, meterWindow); + break; + + case BUTTONPROC_MOVE: + default: + redraw |= button->MouseMove(pos); + break; + } + } + + if (changeCursor && !drawCursor) + { + if ((*j)->HitTest(pos.x, pos.y) && (*j)->HasMouseActionCursor()) + { + drawCursor = ((*j)->HasMouseAction() || button); + } + } + } + + if (redraw) + { + Redraw(); + } + + if (changeCursor) + { + SetCursor(LoadCursor(NULL, drawCursor ? IDC_HAND : IDC_ARROW)); + } +} /* ** OnMouseMove @@ -2823,44 +2938,35 @@ LRESULT CMeterWindow::OnMouseMove(WPARAM wParam, LPARAM lParam) if (m_Message == WM_NCMOUSEMOVE) { // Map to local window - MapWindowPoints(GetDesktopWindow(), m_Window, &pos, 1); + MapWindowPoints(NULL, m_Window, &pos, 1); } - - DoAction(pos.x, pos.y, MOUSE_OVER, false); + + DoMoveAction(pos.x, pos.y, MOUSE_OVER); // Handle buttons - bool redraw = false; - bool drawCursor = false; - std::list::const_iterator j = m_Meters.begin(); - for( ; j != m_Meters.end(); ++j) - { - // Hidden meters are ignored - if ((*j)->IsHidden()) continue; + HandleButtons(pos, BUTTONPROC_MOVE, NULL, true); - CMeterButton* button = dynamic_cast(*j); - if (button) - { - redraw |= button->MouseMove(pos); - } + return 0; +} - if((*j)->HitTest(pos.x, pos.y) && (*j)->HasMouseActionCursor()) - { - drawCursor |= ((*j)->HasMouseAction() || button); - } - } +/* +** OnMouseLeave +** +** When we get WM_MOUSELEAVE messages, run all leave actions. +** +*/ +LRESULT CMeterWindow::OnMouseLeave(WPARAM wParam, LPARAM lParam) +{ + POINT pos; + GetCursorPos(&pos); + HWND hWnd = WindowFromPoint(pos); + if (!hWnd || (hWnd != m_Window && GetParent(hWnd) != m_Window)) // ignore tooltips + { + POINT pos = {SHRT_MIN, SHRT_MIN}; + while (DoMoveAction(pos.x, pos.y, MOUSE_LEAVE)) ; // Leave all forcibly - if(drawCursor) - { - SetCursor(LoadCursor(NULL, IDC_HAND)); - } - else - { - SetCursor(LoadCursor(NULL, IDC_ARROW)); - } - - if (redraw) - { - Redraw(); + // Handle buttons + HandleButtons(pos, BUTTONPROC_MOVE, NULL, true); } return 0; @@ -2889,12 +2995,12 @@ LRESULT CMeterWindow::OnCommand(WPARAM wParam, LPARAM lParam) { if(wParam == ID_CONTEXT_SKINMENU_EDITSKIN) { - std::wstring command = Rainmeter->GetConfigEditor(); + std::wstring command = m_Rainmeter->GetConfigEditor(); command += L" \""; command += m_SkinPath + L"\\" + m_SkinName + L"\\" + m_SkinIniFile + L"\""; // If the skins are in the program folder start the editor as admin - if (Rainmeter->GetPath() + L"Skins\\" == Rainmeter->GetSkinPath()) + if (m_Rainmeter->GetPath() + L"Skins\\" == m_Rainmeter->GetSkinPath()) { LSExecuteAsAdmin(NULL, command.c_str(), SW_SHOWNORMAL); } @@ -3154,9 +3260,6 @@ LRESULT CMeterWindow::OnSysCommand(WPARAM wParam, LPARAM lParam) m_Dragging = true; m_Dragged = false; - std::wstring startWindowX = m_WindowX; - std::wstring startWindowY = m_WindowY; - // Run the DefWindowProc so the dragging works LRESULT result = DefWindowProc(m_Window, m_Message, wParam, lParam); @@ -3169,6 +3272,13 @@ LRESULT CMeterWindow::OnSysCommand(WPARAM wParam, LPARAM lParam) { WriteConfig(); } + + POINT pos; + GetCursorPos(&pos); + MapWindowPoints(NULL, m_Window, &pos, 1); + + // Handle buttons + HandleButtons(pos, BUTTONPROC_UP, NULL, true); // redraw only } else // not dragged { @@ -3197,6 +3307,9 @@ LRESULT CMeterWindow::OnEnterSizeMove(WPARAM wParam, LPARAM lParam) if (m_Dragging) { m_Dragged = true; // Don't post the WM_NCLBUTTONUP message! + + // Set cursor to default + SetCursor(LoadCursor(NULL, IDC_ARROW)); } return 0; @@ -3226,7 +3339,7 @@ LRESULT CMeterWindow::OnNcHitTest(WPARAM wParam, LPARAM lParam) POINT pos; pos.x = (SHORT)LOWORD(lParam); pos.y = (SHORT)HIWORD(lParam); - MapWindowPoints(GetDesktopWindow(), m_Window, &pos, 1); + MapWindowPoints(NULL, m_Window, &pos, 1); int x1 = m_DragMargins.GetLeft(); int x2 = m_WindowW - m_DragMargins.GetRight(); @@ -3406,26 +3519,13 @@ LRESULT CMeterWindow::OnLeftButtonDown(WPARAM wParam, LPARAM lParam) } // Handle buttons - bool redraw = false; - std::list::const_iterator j = m_Meters.begin(); - for( ; j != m_Meters.end(); ++j) - { - // Hidden meters are ignored - if ((*j)->IsHidden()) continue; + HandleButtons(pos, BUTTONPROC_DOWN, NULL, true); - CMeterButton* button = dynamic_cast(*j); - if (button) - { - redraw |= button->MouseDown(pos); - } + if (!DoAction(pos.x, pos.y, MOUSE_LMB_DOWN, false) && m_WindowDraggable) + { + // Cancel the mouse event beforehand + SetMouseLeaveEvent(true); - } - if (redraw) - { - Redraw(); - } - else if(!DoAction(pos.x, pos.y, MOUSE_LMB_DOWN, false) && m_WindowDraggable) - { // Run the DefWindowProc so the dragging works return DefWindowProc(m_Window, m_Message, wParam, lParam); } @@ -3455,23 +3555,7 @@ LRESULT CMeterWindow::OnLeftButtonUp(WPARAM wParam, LPARAM lParam) } // Handle buttons - bool redraw = false; - std::list::const_iterator j = m_Meters.begin(); - for( ; j != m_Meters.end(); ++j) - { - // Hidden meters are ignored - if ((*j)->IsHidden()) continue; - - CMeterButton* button = dynamic_cast(*j); - if (button) - { - redraw |= button->MouseUp(pos, this); - } - } - if (redraw) - { - Redraw(); - } + HandleButtons(pos, BUTTONPROC_UP, this, true); DoAction(pos.x, pos.y, MOUSE_LMB_UP, false); @@ -3500,25 +3584,9 @@ LRESULT CMeterWindow::OnLeftButtonDoubleClick(WPARAM wParam, LPARAM lParam) } // Handle buttons - bool redraw = false; - std::list::iterator j = m_Meters.begin(); - for( ; j != m_Meters.end(); ++j) - { - // Hidden meters are ignored - if ((*j)->IsHidden()) continue; + HandleButtons(pos, BUTTONPROC_DOWN, NULL, true); - CMeterButton* button = dynamic_cast(*j); - if (button) - { - redraw |= button->MouseDown(pos); - } - - } - if (redraw) - { - Redraw(); - } - else if (!DoAction(pos.x, pos.y, MOUSE_LMB_DBLCLK, false)) + if (!DoAction(pos.x, pos.y, MOUSE_LMB_DBLCLK, false)) { DoAction(pos.x, pos.y, MOUSE_LMB_DOWN, false); } @@ -3547,6 +3615,9 @@ LRESULT CMeterWindow::OnRightButtonDown(WPARAM wParam, LPARAM lParam) pos.y = pos.y - rect.top; } + // Handle buttons + HandleButtons(pos, BUTTONPROC_MOVE, NULL, true); + DoAction(pos.x, pos.y, MOUSE_RMB_DOWN, false); return 0; @@ -3560,7 +3631,14 @@ LRESULT CMeterWindow::OnRightButtonDown(WPARAM wParam, LPARAM lParam) */ LRESULT CMeterWindow::OnRightButtonUp(WPARAM wParam, LPARAM lParam) { - if (!DoAction((SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam), MOUSE_RMB_UP, false)) + POINT pos; + pos.x = (SHORT)LOWORD(lParam); + pos.y = (SHORT)HIWORD(lParam); + + // Handle buttons + HandleButtons(pos, BUTTONPROC_MOVE, NULL, true); + + if (!DoAction(pos.x, pos.y, MOUSE_RMB_UP, false)) { // Run the DefWindowProc so the context menu works return DefWindowProc(m_Window, WM_RBUTTONUP, wParam, lParam); @@ -3590,6 +3668,9 @@ LRESULT CMeterWindow::OnRightButtonDoubleClick(WPARAM wParam, LPARAM lParam) pos.y = pos.y - rect.top; } + // Handle buttons + HandleButtons(pos, BUTTONPROC_MOVE, NULL, true); + if (!DoAction(pos.x, pos.y, MOUSE_RMB_DBLCLK, false)) { DoAction(pos.x, pos.y, MOUSE_RMB_DOWN, false); @@ -3619,6 +3700,9 @@ LRESULT CMeterWindow::OnMiddleButtonDown(WPARAM wParam, LPARAM lParam) pos.y = pos.y - rect.top; } + // Handle buttons + HandleButtons(pos, BUTTONPROC_MOVE, NULL, true); + DoAction(pos.x, pos.y, MOUSE_MMB_DOWN, false); return 0; @@ -3645,6 +3729,9 @@ LRESULT CMeterWindow::OnMiddleButtonUp(WPARAM wParam, LPARAM lParam) pos.y = pos.y - rect.top; } + // Handle buttons + HandleButtons(pos, BUTTONPROC_MOVE, NULL, true); + DoAction(pos.x, pos.y, MOUSE_MMB_UP, false); return 0; @@ -3671,6 +3758,9 @@ LRESULT CMeterWindow::OnMiddleButtonDoubleClick(WPARAM wParam, LPARAM lParam) pos.y = pos.y - rect.top; } + // Handle buttons + HandleButtons(pos, BUTTONPROC_MOVE, NULL, true); + if (!DoAction(pos.x, pos.y, MOUSE_MMB_DBLCLK, false)) { DoAction(pos.x, pos.y, MOUSE_MMB_DOWN, false); @@ -3687,26 +3777,39 @@ LRESULT CMeterWindow::OnMiddleButtonDoubleClick(WPARAM wParam, LPARAM lParam) */ LRESULT CMeterWindow::OnContextMenu(WPARAM wParam, LPARAM lParam) { - int xPos = (SHORT)LOWORD(lParam); - int yPos = (SHORT)HIWORD(lParam); + POINT pos; + int x = (SHORT)LOWORD(lParam); + int y = (SHORT)HIWORD(lParam); - // Transform the point to client rect - int x = (INT)(SHORT)LOWORD(lParam); - int y = (INT)(SHORT)HIWORD(lParam); RECT rect; GetWindowRect(m_Window, &rect); - x = x - rect.left; - y = y - rect.top; - // If RMB up or RMB down or double-click cause actions, do not show the menu! - if (DoAction(x, y, MOUSE_RMB_UP, false) || DoAction(x, y, MOUSE_RMB_DOWN, true) || DoAction(x, y, MOUSE_RMB_DBLCLK, true)) + if (x == -1 && y == -1) // WM_CONTEXTMENU is generated from the keyboard (Shift+F10/VK_APPS) { - return 0; + // Set menu position to (0,0) on the window + pos.x = rect.left; + pos.y = rect.top; + } + else + { + // Transform the point to client rect + pos.x = x - rect.left; + pos.y = y - rect.top; + + // Handle buttons + HandleButtons(pos, BUTTONPROC_MOVE, NULL, true); + + // If RMB up or RMB down or double-click cause actions, do not show the menu! + if (DoAction(pos.x, pos.y, MOUSE_RMB_UP, false) || DoAction(pos.x, pos.y, MOUSE_RMB_DOWN, true) || DoAction(pos.x, pos.y, MOUSE_RMB_DBLCLK, true)) + { + return 0; + } + + // Set menu position to cursor position + pos.x = x; + pos.y = y; } - POINT pos; - pos.x = xPos; - pos.y = yPos; m_Rainmeter->ShowContextMenu(pos, this); return 0; @@ -3722,8 +3825,8 @@ LRESULT CMeterWindow::OnContextMenu(WPARAM wParam, LPARAM lParam) bool CMeterWindow::DoAction(int x, int y, MOUSE mouse, bool test) { // Check if the hitpoint was over some meter - std::list::const_iterator j = m_Meters.begin(); - for( ; j != m_Meters.end(); ++j) + std::list::const_reverse_iterator j = m_Meters.rbegin(); + for( ; j != m_Meters.rend(); ++j) { // Hidden meters are ignored if ((*j)->IsHidden()) continue; @@ -3803,60 +3906,11 @@ bool CMeterWindow::DoAction(int x, int y, MOUSE mouse, bool test) return true; } break; - - case MOUSE_OVER: - if (!(*j)->IsMouseOver()) - { - (*j)->SetMouseOver(true); - - if (!((*j)->GetMouseOverAction().empty())) - { - m_MouseOver = true; // If the mouse is over a meter it's also over the main window - if (!test) m_Rainmeter->ExecuteCommand((*j)->GetMouseOverAction().c_str(), this); - return true; - } - } - break; - } - } - else - { - if (mouse == MOUSE_LEAVE || mouse == MOUSE_OVER) - { - if ((*j)->IsMouseOver()) - { - (*j)->SetMouseOver(false); - - if (!((*j)->GetMouseLeaveAction().empty())) - { - if (!test) m_Rainmeter->ExecuteCommand((*j)->GetMouseLeaveAction().c_str(), this); - return true; - } - } } } } - bool inside = false; - if (x >= 0 && y >= 0 && x < m_WindowW && y < m_WindowH) - { - // Check transparent pixels - if (m_DoubleBuffer) - { - Color color; - Status status = m_DoubleBuffer->GetPixel(x, y, &color); - if (status != Ok || color.GetA() > 0) - { - inside = true; - } - } - else - { - inside = true; - } - } - - if (inside) + if (HitTest(x, y)) { // If no meters caused actions, do the default actions switch (mouse) @@ -3932,33 +3986,127 @@ bool CMeterWindow::DoAction(int x, int y, MOUSE mouse, bool test) return true; } break; + } + } - case MOUSE_OVER: + return false; +} + +/* +** DoMoveAction +** +** Executes the action if such are defined. Returns true, if meter/window which should be processed still may exist. +** +*/ +bool CMeterWindow::DoMoveAction(int x, int y, MOUSE mouse, CMeter* upperMeter) +{ + // Check if the hitpoint was over some meter + std::list::const_reverse_iterator j = m_Meters.rbegin(); + if (upperMeter) + { + for ( ; j != m_Meters.rend(); ++j) + { + if ((*j) == upperMeter) + { + ++j; + break; + } + } + } + for( ; j != m_Meters.rend(); ++j) + { + if (!(*j)->IsHidden() && !upperMeter && (*j)->HitTest(x, y)) + { + if (mouse == MOUSE_OVER) + { + if (!m_MouseOver) + { + // If the mouse is over a meter it's also over the main window + //DebugLog(L"@Enter: %s", m_SkinName.c_str()); + m_MouseOver = true; + SetMouseLeaveEvent(false); + + if (!m_MouseOverAction.empty()) + { + m_Rainmeter->ExecuteCommand(m_MouseOverAction.c_str(), this); + } + } + + if (!(*j)->IsMouseOver()) + { + if (!((*j)->GetMouseOverAction().empty()) || + !((*j)->GetMouseLeaveAction().empty()) || + dynamic_cast(*j) != NULL) + { + while (DoMoveAction(x, y, MOUSE_LEAVE, (*j))) ; // Leave all lower meters + + //DebugLog(L"MeterEnter: %s - [%s]", m_SkinName.c_str(), (*j)->GetName()); + (*j)->SetMouseOver(true); + + if (!((*j)->GetMouseOverAction().empty())) + { + m_Rainmeter->ExecuteCommand((*j)->GetMouseOverAction().c_str(), this); + return true; + } + return false; + } + } + else + { + return false; + } + } + } + else + { + if ((*j)->IsMouseOver()) + { + //DebugLog(L"MeterLeave: %s - [%s]", m_SkinName.c_str(), (*j)->GetName()); + (*j)->SetMouseOver(false); + + if (!((*j)->GetMouseLeaveAction().empty())) + { + m_Rainmeter->ExecuteCommand((*j)->GetMouseLeaveAction().c_str(), this); + } + return true; + } + } + } + + if (upperMeter) return false; + + if (HitTest(x, y)) + { + // If no meters caused actions, do the default actions + if (mouse == MOUSE_OVER) + { if (!m_MouseOver) { + //DebugLog(L"Enter: %s", m_SkinName.c_str()); m_MouseOver = true; + SetMouseLeaveEvent(false); + if (!m_MouseOverAction.empty()) { - if (!test) m_Rainmeter->ExecuteCommand(m_MouseOverAction.c_str(), this); + m_Rainmeter->ExecuteCommand(m_MouseOverAction.c_str(), this); return true; } } - break; } } else { // Mouse leave happens when the mouse is outside the window - if (mouse == MOUSE_LEAVE) + if (m_MouseOver) { - if (m_MouseOver) + //DebugLog(L"Leave: %s", m_SkinName.c_str()); + m_MouseOver = false; + SetMouseLeaveEvent(true); + + if (!m_MouseLeaveAction.empty()) { - m_MouseOver = false; - if (!m_MouseLeaveAction.empty()) - { - if (!test) m_Rainmeter->ExecuteCommand(m_MouseLeaveAction.c_str(), this); - return true; - } + m_Rainmeter->ExecuteCommand(m_MouseLeaveAction.c_str(), this); + return true; } } } @@ -4031,6 +4179,8 @@ LRESULT CALLBACK CMeterWindow::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPAR MESSAGE(OnNcHitTest, WM_NCHITTEST) MESSAGE(OnMouseMove, WM_MOUSEMOVE) MESSAGE(OnMouseMove, WM_NCMOUSEMOVE) + MESSAGE(OnMouseLeave, WM_MOUSELEAVE) + MESSAGE(OnMouseLeave, WM_NCMOUSELEAVE) MESSAGE(OnContextMenu, WM_CONTEXTMENU) MESSAGE(OnRightButtonDown, WM_NCRBUTTONDOWN) MESSAGE(OnRightButtonDown, WM_RBUTTONDOWN) @@ -4180,11 +4330,26 @@ LRESULT CMeterWindow::OnCopyData(WPARAM wParam, LPARAM lParam) bang = str; } + if (wcsicmp(bang.c_str(), L"!RainmeterWriteKeyValue") == 0) + { + // !RainmeterWriteKeyValue is a special case. + if (CRainmeter::ParseString(arg.c_str()).size() < 4) + { + // Add the current config filepath to the args + arg += L" \""; + arg += m_SkinPath; + arg += m_SkinName; + arg += L"\\"; + arg += m_SkinIniFile; + arg += L"\""; + } + } + // Add the current config name to the args. If it's not defined already // the bang only affects this config, if there already is a config defined // another one doesn't matter. arg += L" \""; - arg += m_SkinName.c_str(); + arg += m_SkinName; arg += L"\""; return Rainmeter->ExecuteBang(bang, arg, this); diff --git a/Library/MeterWindow.h b/Library/MeterWindow.h index dd941ce7..91788202 100644 --- a/Library/MeterWindow.h +++ b/Library/MeterWindow.h @@ -54,6 +54,13 @@ enum MOUSE MOUSE_LEAVE }; +enum BUTTONPROC +{ + BUTTONPROC_DOWN, + BUTTONPROC_UP, + BUTTONPROC_MOVE +}; + enum ZPOSITION { ZPOSITION_ONDESKTOP = -2, @@ -146,10 +153,12 @@ public: void Refresh(bool init, bool all = false); void Redraw(); + void SetMouseLeaveEvent(bool cancel); + void MoveWindow(int x, int y); void ChangeZPos(ZPOSITION zPos, bool all = false); void FadeWindow(int from, int to); - + Gdiplus::Bitmap* GetDoubleBuffer() { return m_DoubleBuffer; }; HWND GetWindow() { return m_Window; }; @@ -226,6 +235,8 @@ protected: LRESULT OnDisplayChange(WPARAM wParam, LPARAM lParam); private: + bool HitTest(int x, int y); + void CreateRegion(bool clear); void GetSkinFolders(const std::wstring& folder); Gdiplus::Bitmap* GrabDesktop(int x, int y, int w, int h); @@ -241,7 +252,9 @@ private: void InitializeMeasures(); void InitializeMeters(); void ShowWindowIfAppropriate(); + void HandleButtons(POINT pos, BUTTONPROC proc, CMeterWindow* meterWindow, bool changeCursor); bool DoAction(int x, int y, MOUSE mouse, bool test); + bool DoMoveAction(int x, int y, MOUSE mouse, CMeter* upperMeter = NULL); bool ResizeWindow(bool reset); void IgnoreAeroPeek(); diff --git a/Library/Rainmeter.cpp b/Library/Rainmeter.cpp index 719b683c..a87ab1cf 100644 --- a/Library/Rainmeter.cpp +++ b/Library/Rainmeter.cpp @@ -47,7 +47,7 @@ std::wstring CRainmeter::c_CmdLine; ** Splits the given string into substrings ** */ -std::vector ParseString(LPCTSTR str) +std::vector CRainmeter::ParseString(LPCTSTR str) { std::vector result; if (str) @@ -198,7 +198,7 @@ void BangWithArgs(BANGCOMMAND bang, const WCHAR* arg, size_t numOfArgs) { if(Rainmeter) { - std::vector subStrings = ParseString(arg); + std::vector subStrings = CRainmeter::ParseString(arg); std::wstring config; std::wstring argument; @@ -271,7 +271,7 @@ void BangGroupWithArgs(BANGCOMMAND bang, const WCHAR* arg, size_t numOfArgs) { if (Rainmeter) { - std::vector subStrings = ParseString(arg); + std::vector subStrings = CRainmeter::ParseString(arg); if (subStrings.size() > numOfArgs) { @@ -479,7 +479,7 @@ void RainmeterActivateConfig(HWND, const char* arg) { if (Rainmeter) { - std::vector subStrings = ParseString(ConvertToWide(arg).c_str()); + std::vector subStrings = CRainmeter::ParseString(ConvertToWide(arg).c_str()); if (subStrings.size() > 1) { @@ -519,7 +519,7 @@ void RainmeterDeactivateConfig(HWND, const char* arg) { if (Rainmeter) { - std::vector subStrings = ParseString(ConvertToWide(arg).c_str()); + std::vector subStrings = CRainmeter::ParseString(ConvertToWide(arg).c_str()); if (subStrings.size() > 0) { @@ -548,7 +548,7 @@ void RainmeterToggleConfig(HWND, const char* arg) { if (Rainmeter) { - std::vector subStrings = ParseString(ConvertToWide(arg).c_str()); + std::vector subStrings = CRainmeter::ParseString(ConvertToWide(arg).c_str()); if (subStrings.size() >= 2) { @@ -777,7 +777,7 @@ void RainmeterDeactivateConfigGroup(HWND, const char* arg) { if (Rainmeter) { - std::vector subStrings = ParseString(ConvertToWide(arg).c_str()); + std::vector subStrings = CRainmeter::ParseString(ConvertToWide(arg).c_str()); if (subStrings.size() > 0) { @@ -862,7 +862,7 @@ void RainmeterSkinMenu(HWND, const char* arg) { if (Rainmeter) { - std::vector subStrings = ParseString(ConvertToWide(arg).c_str()); + std::vector subStrings = CRainmeter::ParseString(ConvertToWide(arg).c_str()); if (subStrings.size() > 0) { @@ -924,6 +924,140 @@ void RainmeterMoveMeter(HWND, const char* arg) BangWithArgs(BANG_MOVEMETER, ConvertToWide(arg).c_str(), 3); } +/* +** RainmeterWriteKeyValue +** +** Callback for the !RainmeterWriteKeyValue bang +** +*/ +void RainmeterWriteKeyValue(HWND, const char* arg) +{ + if (Rainmeter) + { + std::vector subStrings = CRainmeter::ParseString(ConvertToWide(arg).c_str()); + + if (subStrings.size() > 3) + { + const std::wstring& iniFile = subStrings[3]; + + if (iniFile.find(L"..\\") != std::string::npos || iniFile.find(L"../") != std::string::npos) + { + DebugLog(L"!RainmeterWriteKeyValue: Illegal characters in path - \"..\\\": %s", iniFile.c_str()); + return; + } + + if (wcsnicmp(iniFile.c_str(), Rainmeter->GetSkinPath().c_str(), Rainmeter->GetSkinPath().size()) != 0 && + wcsnicmp(iniFile.c_str(), Rainmeter->GetPath().c_str(), Rainmeter->GetPath().size()) != 0) + { + DebugLog(L"!RainmeterWriteKeyValue: Illegal path outside of Rainmeter directories: %s", iniFile.c_str()); + return; + } + + // Verify whether the file exists + if (_waccess(iniFile.c_str(), 0) == -1) + { + DebugLog(L"!RainmeterWriteKeyValue: File not found: %s", iniFile.c_str()); + return; + } + + // Verify whether the file is read-only + DWORD attr = GetFileAttributes(iniFile.c_str()); + if (attr == -1 || (attr & FILE_ATTRIBUTE_READONLY)) + { + DebugLog(L"!RainmeterWriteKeyValue: File is read-only: %s", iniFile.c_str()); + return; + } + + // Avoid "IniFileMapping" + std::vector iniFileMappings; + CSystem::GetIniFileMappingList(iniFileMappings); + std::wstring iniWrite = CSystem::GetTemporaryFile(iniFileMappings, iniFile); + if (iniWrite == L"<>") // error occurred + { + DebugLog(L"!RainmeterWriteKeyValue: Failed to create a temporary file: %s", iniFile.c_str()); + return; + } + + bool temporary = !iniWrite.empty(); + + if (temporary) + { + if (CRainmeter::GetDebug()) DebugLog(L"!RainmeterWriteKeyValue: Writing file: %s (Temp: %s)", iniFile.c_str(), iniWrite.c_str()); + } + else + { + if (CRainmeter::GetDebug()) DebugLog(L"!RainmeterWriteKeyValue: Writing file: %s", iniFile.c_str()); + iniWrite = iniFile; + } + + const std::wstring& strSection = subStrings[0]; + const std::wstring& strKey = subStrings[1]; + const std::wstring& strValue = subStrings[2]; + + int formula = -1; + BOOL write = 0; + + if (subStrings.size() > 4) + { + CMeterWindow* mw = Rainmeter->GetMeterWindow(subStrings[4]); + if (mw) + { + double value; + formula = mw->GetParser().ReadFormula(strValue, &value); + + // Formula read fine + if (formula != -1) + { + TCHAR buffer[256]; + swprintf(buffer, L"%f", value); + + const std::wstring& resultString = buffer; + + write = WritePrivateProfileString(strSection.c_str(), strKey.c_str(), resultString.c_str(), iniWrite.c_str()); + } + } + } + + if (formula == -1) + { + write = WritePrivateProfileString(strSection.c_str(), strKey.c_str(), strValue.c_str(), iniWrite.c_str()); + } + + if (temporary) + { + if (write != 0) + { + WritePrivateProfileString(NULL, NULL, NULL, iniWrite.c_str()); // FLUSH + + // Copy the file back + if (!CSystem::CopyFiles(iniWrite, iniFile)) + { + DebugLog(L"!RainmeterWriteKeyValue: Failed to copy a temporary file to the original filepath: %s (Temp: %s)", iniFile.c_str(), iniWrite.c_str()); + } + } + else // failed + { + DebugLog(L"!RainmeterWriteKeyValue: Failed to write a value to the file: %s (Temp: %s)", iniFile.c_str(), iniWrite.c_str()); + } + + // Remove a temporary file + CSystem::RemoveFile(iniWrite); + } + else + { + if (write == 0) // failed + { + DebugLog(L"!RainmeterWriteKeyValue: Failed to write a value to the file: %s", iniFile.c_str()); + } + } + } + else + { + DebugLog(L"Unable to parse the arguments for !RainmeterWriteKeyValue"); + } + } +} + /* ** RainmeterPluginBang ** @@ -973,6 +1107,8 @@ CRainmeter::CRainmeter() c_Debug = false; + m_MenuActive = false; + m_Logging = false; m_DesktopWorkAreaChanged = false; @@ -1178,18 +1314,18 @@ int CRainmeter::Initialize(HWND Parent, HINSTANCE Instance, LPCSTR szPath) // Copy the default skin to the Skins folder std::wstring strFrom(m_Path + L"Skins\\" + L"*.*"); std::wstring strTo(m_SkinPath); - CopyFiles(strFrom, strTo); + CSystem::CopyFiles(strFrom, strTo); // This shouldn't be copied std::wstring strNote = strTo + L"Read me before copying skins to here.txt"; - DeleteFile(strNote.c_str()); + CSystem::RemoveFile(strNote); // Copy also the themes to the %APPDATA% strFrom = std::wstring(m_Path + L"Themes\\" + L"*.*"); strTo = std::wstring(GetSettingsPath() + L"Themes"); CreateDirectory(strTo.c_str(), NULL); strTo += L"\\"; - CopyFiles(strFrom, strTo); + CSystem::CopyFiles(strFrom, strTo); } } else @@ -1338,6 +1474,7 @@ int CRainmeter::Initialize(HWND Parent, HINSTANCE Instance, LPCSTR szPath) AddBangCommand("!RainmeterSkinMenu", RainmeterSkinMenu); AddBangCommand("!RainmeterTrayMenu", RainmeterTrayMenu); AddBangCommand("!RainmeterResetStats", RainmeterResetStats); + AddBangCommand("!RainmeterWriteKeyValue", RainmeterWriteKeyValue); AddBangCommand("!RainmeterPluginBang", RainmeterPluginBang); AddBangCommand("!RainmeterQuit", RainmeterQuit); } @@ -1433,17 +1570,17 @@ void CRainmeter::CheckSkinVersions() if (strVersionCurrent.find_first_of(L"\\/\"*:?<>|") == std::wstring::npos) { std::wstring strTarget = m_SkinPath + L"Backup\\" + menu[i].name + L"-" + strVersionCurrent; - if (CopyFiles(m_SkinPath + menu[i].name, strTarget, true)) // Move the folder to "backup" + if (CSystem::CopyFiles(m_SkinPath + menu[i].name, strTarget, true)) // Move the folder to "backup" { // Upgrade the skin - CopyFiles(strMainSkinsPath + menu[i].name, m_SkinPath); + CSystem::CopyFiles(strMainSkinsPath + menu[i].name, m_SkinPath); // TODO: Temporary 'fix': If this was Enigma upgrade the themes too if (menu[i].name == L"Enigma" || menu[i].name == L"Gnometer") { std::wstring strMainThemes = m_Path + L"Themes"; std::wstring strCurrentThemes = GetSettingsPath(); - CopyFiles(strMainThemes, strCurrentThemes); + CSystem::CopyFiles(strMainThemes, strCurrentThemes); } // End of temporary 'fix' } @@ -1466,10 +1603,10 @@ void CRainmeter::CheckSkinVersions() strMessage += L"Do you want to add it to your skin and themes libraries?"; if (IDYES == MessageBox(NULL, strMessage.c_str(), APPNAME, MB_YESNO | MB_ICONQUESTION)) { - CopyFiles(strMainSkinsPath + menu[i].name, m_SkinPath); + CSystem::CopyFiles(strMainSkinsPath + menu[i].name, m_SkinPath); std::wstring strMainThemes = m_Path + L"Themes"; std::wstring strCurrentThemes = GetSettingsPath(); - CopyFiles(strMainThemes, strCurrentThemes); + CSystem::CopyFiles(strMainThemes, strCurrentThemes); } } @@ -1516,37 +1653,6 @@ int CRainmeter::CompareVersions(std::wstring strA, std::wstring strB) return 0; } -/* -** CopyFiles -** -** Copies files and folders from one location to another. -** -*/ -bool CRainmeter::CopyFiles(const std::wstring& strFrom, const std::wstring& strTo, bool bMove) -{ - std::wstring tmpFrom(strFrom), tmpTo(strTo); - - // The strings must end with double nul - tmpFrom.append(L"0"); - tmpFrom[tmpFrom.size() - 1] = L'\0'; - tmpTo.append(L"0"); - tmpTo[tmpTo.size() - 1] = L'\0'; - - SHFILEOPSTRUCT fo = {0}; - fo.wFunc = bMove ? FO_MOVE : FO_COPY; - fo.pFrom = tmpFrom.c_str(); - fo.pTo = tmpTo.c_str(); - fo.fFlags = FOF_NO_UI | FOF_NOCONFIRMATION | FOF_ALLOWUNDO; - - int result = SHFileOperation(&fo); - if (result != 0) - { - DebugLog(L"Unable to copy files from %s to %s (%i)", strFrom.c_str(), strTo.c_str(), result); - return false; - } - return true; -} - /* ** CreateDefaultConfigFile ** @@ -1576,7 +1682,7 @@ void CRainmeter::CreateDefaultConfigFile(std::wstring strFile) } else { - CopyFiles(defaultIni, GetIniFile()); + CSystem::CopyFiles(defaultIni, GetIniFile()); } } @@ -2245,6 +2351,10 @@ BOOL CRainmeter::ExecuteBang(const std::wstring& bang, const std::wstring& arg, { BangWithArgs(BANG_MOVEMETER, arg.c_str(), 3); } + else if (wcsicmp(bang.c_str(), L"!RainmeterWriteKeyValue") == 0) + { + RainmeterWriteKeyValue(NULL, ConvertToAscii(arg.c_str()).c_str()); + } else if (wcsicmp(bang.c_str(), L"!RainmeterPluginBang") == 0) { BangWithArgs(BANG_PLUGIN, arg.c_str(), 1); @@ -2937,11 +3047,9 @@ PLATFORM CRainmeter::IsNT() */ void CRainmeter::ShowContextMenu(POINT pos, CMeterWindow* meterWindow) { - static bool active = false; - - if (!active) + if (!m_MenuActive) { - active = true; + m_MenuActive = true; // Show context menu, if no actions were executed HMENU menu = LoadMenu(m_Instance, MAKEINTRESOURCE(IDR_CONTEXT_MENU)); @@ -2955,7 +3063,7 @@ void CRainmeter::ShowContextMenu(POINT pos, CMeterWindow* meterWindow) { // Disable Quit/Logging if ran as a Litestep plugin EnableMenuItem(subMenu, ID_CONTEXT_QUIT, MF_BYCOMMAND | MF_GRAYED); - EnableMenuItem(subMenu, 6, MF_BYPOSITION | MF_GRAYED); + EnableMenuItem(subMenu, 6, MF_BYPOSITION | MF_GRAYED); // "Logging" menu } else { @@ -3032,8 +3140,19 @@ void CRainmeter::ShowContextMenu(POINT pos, CMeterWindow* meterWindow) } } - // Show context menu - HWND hWnd = meterWindow ? meterWindow->GetWindow() : m_TrayWindow->GetWindow(); + HWND hWnd = WindowFromPoint(pos); + if (hWnd != NULL) + { + CMeterWindow* mw = GetMeterWindow(hWnd); + if (mw) + { + // Cancel the mouse event beforehand + mw->SetMouseLeaveEvent(true); + } + } + + // Set the window to foreground + hWnd = meterWindow ? meterWindow->GetWindow() : m_TrayWindow->GetWindow(); HWND hWndForeground = GetForegroundWindow(); if (hWndForeground != hWnd) { @@ -3043,6 +3162,8 @@ void CRainmeter::ShowContextMenu(POINT pos, CMeterWindow* meterWindow) SetForegroundWindow(hWnd); AttachThreadInput(currentThreadID, foregroundThreadID, FALSE); } + + // Show context menu TrackPopupMenu( subMenu, TPM_RIGHTBUTTON | TPM_LEFTALIGN, @@ -3062,7 +3183,7 @@ void CRainmeter::ShowContextMenu(POINT pos, CMeterWindow* meterWindow) DestroyMenu(menu); } - active = false; + m_MenuActive = false; } } @@ -3183,7 +3304,7 @@ HMENU CRainmeter::CreateSkinMenu(CMeterWindow* meterWindow, int index, HMENU con // Tick the transparency if (!meterWindow->GetNativeTransparency()) { - EnableMenuItem(settingsMenu, 1, MF_BYPOSITION | MF_GRAYED); + EnableMenuItem(settingsMenu, 1, MF_BYPOSITION | MF_GRAYED); // "Transparency" menu EnableMenuItem(settingsMenu, ID_CONTEXT_SKINMENU_CLICKTHROUGH, MF_BYCOMMAND | MF_GRAYED); } else @@ -3451,7 +3572,7 @@ void CRainmeter::DeleteLogFile() SetLogging(false); ResetLoggingFlag(); - DeleteFile(m_LogFile.c_str()); + CSystem::RemoveFile(m_LogFile); } } } diff --git a/Library/Rainmeter.h b/Library/Rainmeter.h index 0a4bb67a..67bd5ce6 100644 --- a/Library/Rainmeter.h +++ b/Library/Rainmeter.h @@ -96,6 +96,7 @@ void RainmeterAbout(HWND, const char* arg); void RainmeterSkinMenu(HWND, const char* arg); void RainmeterTrayMenu(HWND, const char* arg); void RainmeterResetStats(HWND, const char* arg); +void RainmeterWriteKeyValue(HWND, const char* arg); void RainmeterPluginBang(HWND, const char* arg); void RainmeterQuit(HWND, const char* arg); @@ -190,6 +191,7 @@ public: void SetDebug(bool debug); + bool IsMenuActive() { return m_MenuActive; } void ShowContextMenu(POINT pos, CMeterWindow* meterWindow); std::wstring GetTrayExecuteL() { return m_TrayExecuteL; }; @@ -207,10 +209,10 @@ public: void ClearDeleteLaterList(); + static std::vector ParseString(LPCTSTR str); static PLATFORM IsNT(); static std::wstring ExtractPath(const std::wstring& strFilePath); static void ExpandEnvironmentVariables(std::wstring& strPath); - static bool CopyFiles(const std::wstring& strFrom, const std::wstring& strTo, bool bMove = false); private: void CreateMeterWindow(std::wstring path, std::wstring config, std::wstring iniFile); @@ -266,6 +268,8 @@ private: std::map m_DesktopWorkAreas; std::vector m_OldDesktopWorkAreas; + bool m_MenuActive; + bool m_Logging; std::wstring m_ConfigEditor; diff --git a/Library/System.cpp b/Library/System.cpp index 43120918..d0511ce8 100644 --- a/Library/System.cpp +++ b/Library/System.cpp @@ -999,3 +999,145 @@ BOOL CSystem::DwmIsCompositionEnabled() } return fEnabled; } + +/* +** CopyFiles +** +** Copies files and folders from one location to another. +** +*/ +bool CSystem::CopyFiles(const std::wstring& strFrom, const std::wstring& strTo, bool bMove) +{ + std::wstring tmpFrom(strFrom), tmpTo(strTo); + + // The strings must end with double nul + tmpFrom.append(L"0"); + tmpFrom[tmpFrom.size() - 1] = L'\0'; + tmpTo.append(L"0"); + tmpTo[tmpTo.size() - 1] = L'\0'; + + SHFILEOPSTRUCT fo = {0}; + fo.wFunc = bMove ? FO_MOVE : FO_COPY; + fo.pFrom = tmpFrom.c_str(); + fo.pTo = tmpTo.c_str(); + fo.fFlags = FOF_NO_UI | FOF_NOCONFIRMATION | FOF_ALLOWUNDO; + + int result = SHFileOperation(&fo); + if (result != 0) + { + DebugLog(L"Unable to copy files from %s to %s (%i)", strFrom.c_str(), strTo.c_str(), result); + return false; + } + return true; +} + +/* +** RemoveFile +** +** Removes a file even if a file is read-only. +** +*/ +bool CSystem::RemoveFile(const std::wstring& file) +{ + DWORD attr = GetFileAttributes(file.c_str()); + if (attr == -1 || (attr & FILE_ATTRIBUTE_READONLY)) + { + // Unset read-only + SetFileAttributes(file.c_str(), (attr == -1) ? FILE_ATTRIBUTE_NORMAL : attr - FILE_ATTRIBUTE_READONLY); + } + + return (DeleteFile(file.c_str()) != 0); +} + +/* +** GetIniFileMappingList +** +** Retrieves the "IniFileMapping" entries from Registry. +** +*/ +void CSystem::GetIniFileMappingList(std::vector& iniFileMappings) +{ + HKEY hKey; + LONG ret; + + ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\IniFileMapping", 0, KEY_ENUMERATE_SUB_KEYS, &hKey); + if (ret == ERROR_SUCCESS) + { + WCHAR buffer[MAX_PATH]; + DWORD index = 0, cch = MAX_PATH; + + while (true) + { + ret = RegEnumKeyEx(hKey, index++, buffer, &cch, NULL, NULL, NULL, NULL); + if (ret == ERROR_NO_MORE_ITEMS) break; + + if (ret == ERROR_SUCCESS) + { + iniFileMappings.push_back(buffer); + } + cch = MAX_PATH; + } + RegCloseKey(hKey); + } +} + +/* +** GetTemporaryFile +** +** Prepares a temporary file if iniFile is included in the "IniFileMapping" entries. +** If iniFile is not included, returns a empty string. If error occurred, returns "<>". +** Note that a temporary file must be deleted by caller. +** +*/ +std::wstring CSystem::GetTemporaryFile(const std::vector& iniFileMappings, const std::wstring &iniFile) +{ + std::wstring temporary; + + if (!iniFileMappings.empty()) + { + std::wstring::size_type pos = iniFile.find_last_of(L'\\'); + std::wstring filename; + + if (pos != std::wstring::npos) + { + filename = iniFile.substr(pos + 1); + } + else + { + filename = iniFile; + } + + for (size_t i = 0; i < iniFileMappings.size(); ++i) + { + if (wcsicmp(iniFileMappings[i].c_str(), filename.c_str()) == 0) + { + WCHAR buffer[MAX_PATH]; + + GetTempPath(MAX_PATH, buffer); + temporary = buffer; + if (GetTempFileName(temporary.c_str(), L"cfg", 0, buffer) != 0) + { + temporary = buffer; + + std::wstring tmp = GetTemporaryFile(iniFileMappings, temporary); + if (tmp.empty() && CopyFiles(iniFile, temporary)) + { + return temporary; + } + else // alternate is reserved or failed + { + RemoveFile(temporary); + return tmp.empty() ? L"<>" : tmp; + } + } + else // failed + { + DebugLog(L"Unable to create a temporary file to: %s", temporary.c_str()); + return L"<>"; + } + } + } + } + + return temporary; +} diff --git a/Library/System.h b/Library/System.h index be3be1eb..c6c3a1c7 100644 --- a/Library/System.h +++ b/Library/System.h @@ -64,6 +64,12 @@ public: static HWND GetHelperWindow() { return c_HelperWindow; } static void PrepareHelperWindow(HWND WorkerW); + static bool CopyFiles(const std::wstring& strFrom, const std::wstring& strTo, bool bMove = false); + static bool RemoveFile(const std::wstring& file); + + static void GetIniFileMappingList(std::vector& iniFileMappings); + static std::wstring GetTemporaryFile(const std::vector& iniFileMappings, const std::wstring& iniFile); + private: static void CALLBACK MyWinEventProc(HWINEVENTHOOK hWinEventHook, DWORD event, HWND hwnd, LONG idObject, LONG idChild, DWORD dwEventThread, DWORD dwmsEventTime); static LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); diff --git a/Library/TrayWindow.cpp b/Library/TrayWindow.cpp index 199c8839..a4000d48 100644 --- a/Library/TrayWindow.cpp +++ b/Library/TrayWindow.cpp @@ -599,9 +599,7 @@ LRESULT CALLBACK CTrayWindow::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA { POINT point; GetCursorPos(&point); - SetForegroundWindow(tray->m_Window); Rainmeter->ShowContextMenu(point, NULL); - SetForegroundWindow(tray->m_Window); } } break;