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;