- 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.
This commit is contained in:
spx 2010-08-03 15:10:42 +00:00
parent d92f4f939d
commit 64888434c9
11 changed files with 802 additions and 437 deletions

View File

@ -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<std::wstring> 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<std::wstring>& 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;
if (CRainmeter::GetDebug()) DebugLog(L"Reading file: %s (Temp: %s)", iniFile.c_str(), iniRead.c_str());
}
else // copy failed
{
DeleteFile(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<std::wstring> CConfigParser::GetKeys(const std::wstring& strSection)
return std::vector<std::wstring>();
}
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;
}

View File

@ -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<std::wstring>& 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<std::wstring> 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<std::wstring, std::vector<std::wstring> > m_Keys;
stdext::hash_map<std::wstring, std::wstring> m_Values;
std::vector<std::wstring> m_IniFileMappings;
static std::map<std::wstring, std::wstring> c_MonitorVariables;
};

View File

@ -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,26 +235,60 @@ void CMeterButton::BindMeasure(std::list<CMeasure*>& 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_State == BUTTON_STATE_DOWN)
if (m_MouseOver &&
px >= x && px < x + m_W &&
py >= y && py < y + m_H)
{
if (m_Clicked &&
pos.x >= x && pos.x < x + m_W &&
pos.y >= y && pos.y < y + m_H)
if (checkAlpha)
{
if (m_SolidColor.GetA() > 0 || m_SolidColor2.GetA() > 0)
{
return true;
}
// Check transparent pixels
if (m_Bitmap)
{
Color color;
m_Bitmap->GetPixel(pos.x - x + m_W * m_State, pos.y - y, &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;
}
if (color.GetA() > 0)
bool CMeterButton::MouseUp(POINT pos, CMeterWindow* window)
{
if (m_State == BUTTON_STATE_DOWN)
{
if (window && m_Clicked && HitTest2(pos.x, pos.y, true))
{
// 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;
return true;
@ -266,39 +300,20 @@ 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)
{
Color color;
m_Bitmap->GetPixel(pos.x - x + m_W * m_State, pos.y - y, &color);
if (color.GetA() > 0)
if (HitTest2(pos.x, pos.y, 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)
{
Color color;
m_Bitmap->GetPixel(pos.x - x + m_W * m_State, pos.y - y, &color);
if (color.GetA() > 0)
if (HitTest2(pos.x, pos.y, true))
{
if (m_State == BUTTON_STATE_NORMAL)
{
@ -306,7 +321,6 @@ bool CMeterButton::MouseMove(POINT pos)
return true;
}
}
}
else
{
// If the left button is not down anymore the cliked state needs to be set false
@ -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)
{

View File

@ -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

View File

@ -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<CMeasure*>::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<CMeter*>::const_iterator j = m_Meters.begin();
for( ; j != m_Meters.end(); ++j)
if (m_MouseOver)
{
// Hidden meters are ignored
if ((*j)->IsHidden()) continue;
POINT pos;
GetCursorPos(&pos);
if (WindowFromPoint(pos) == m_Window)
{
SetMouseLeaveEvent(false);
CMeterButton* button = dynamic_cast<CMeterButton*>(*j);
if (button)
MapWindowPoints(NULL, m_Window, &pos, 1);
DoMoveAction(pos.x, pos.y, MOUSE_OVER);
// Handle buttons
HandleButtons(pos, BUTTONPROC_MOVE, NULL, false);
}
else
{
redraw |= button->MouseMove(pos);
// 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 (WindowFromPoint(pos) == m_Window)
{
MapWindowPoints(NULL, m_Window, &pos, 1);
if(rect.left <= pos.x && rect.right > pos.x &&
rect.top <= pos.y && rect.bottom > pos.y)
{
// 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;
}
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<CMeter*>::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<CMeterButton*>(*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<CMeter*>::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<CMeterButton*>(*j);
if (button)
{
redraw |= button->MouseMove(pos);
}
return 0;
}
if((*j)->HitTest(pos.x, pos.y) && (*j)->HasMouseActionCursor())
/*
** 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
{
drawCursor |= ((*j)->HasMouseAction() || button);
}
}
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<CMeter*>::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<CMeterButton*>(*j);
if (button)
if (!DoAction(pos.x, pos.y, MOUSE_LMB_DOWN, false) && m_WindowDraggable)
{
redraw |= button->MouseDown(pos);
}
// 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<CMeter*>::const_iterator j = m_Meters.begin();
for( ; j != m_Meters.end(); ++j)
{
// Hidden meters are ignored
if ((*j)->IsHidden()) continue;
CMeterButton* button = dynamic_cast<CMeterButton*>(*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<CMeter*>::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<CMeterButton*>(*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 (x == -1 && y == -1) // WM_CONTEXTMENU is generated from the keyboard (Shift+F10/VK_APPS)
{
// 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(x, y, MOUSE_RMB_UP, false) || DoAction(x, y, MOUSE_RMB_DOWN, true) || DoAction(x, y, MOUSE_RMB_DBLCLK, true))
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;
}
POINT pos;
pos.x = xPos;
pos.y = yPos;
// Set menu position to cursor position
pos.x = x;
pos.y = y;
}
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<CMeter*>::const_iterator j = m_Meters.begin();
for( ; j != m_Meters.end(); ++j)
std::list<CMeter*>::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,36 +3986,130 @@ 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<CMeter*>::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())
{
if (!test) m_Rainmeter->ExecuteCommand(m_MouseOverAction.c_str(), this);
m_Rainmeter->ExecuteCommand(m_MouseOverAction.c_str(), this);
}
}
if (!(*j)->IsMouseOver())
{
if (!((*j)->GetMouseOverAction().empty()) ||
!((*j)->GetMouseLeaveAction().empty()) ||
dynamic_cast<CMeterButton*>(*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())
{
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)
{
//DebugLog(L"Leave: %s", m_SkinName.c_str());
m_MouseOver = false;
SetMouseLeaveEvent(true);
if (!m_MouseLeaveAction.empty())
{
if (!test) m_Rainmeter->ExecuteCommand(m_MouseLeaveAction.c_str(), this);
m_Rainmeter->ExecuteCommand(m_MouseLeaveAction.c_str(), this);
return true;
}
}
}
}
return false;
}
@ -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);

View File

@ -54,6 +54,13 @@ enum MOUSE
MOUSE_LEAVE
};
enum BUTTONPROC
{
BUTTONPROC_DOWN,
BUTTONPROC_UP,
BUTTONPROC_MOVE
};
enum ZPOSITION
{
ZPOSITION_ONDESKTOP = -2,
@ -146,6 +153,8 @@ 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);
@ -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();

View File

@ -47,7 +47,7 @@ std::wstring CRainmeter::c_CmdLine;
** Splits the given string into substrings
**
*/
std::vector<std::wstring> ParseString(LPCTSTR str)
std::vector<std::wstring> CRainmeter::ParseString(LPCTSTR str)
{
std::vector<std::wstring> result;
if (str)
@ -198,7 +198,7 @@ void BangWithArgs(BANGCOMMAND bang, const WCHAR* arg, size_t numOfArgs)
{
if(Rainmeter)
{
std::vector<std::wstring> subStrings = ParseString(arg);
std::vector<std::wstring> 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<std::wstring> subStrings = ParseString(arg);
std::vector<std::wstring> subStrings = CRainmeter::ParseString(arg);
if (subStrings.size() > numOfArgs)
{
@ -479,7 +479,7 @@ void RainmeterActivateConfig(HWND, const char* arg)
{
if (Rainmeter)
{
std::vector<std::wstring> subStrings = ParseString(ConvertToWide(arg).c_str());
std::vector<std::wstring> 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<std::wstring> subStrings = ParseString(ConvertToWide(arg).c_str());
std::vector<std::wstring> 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<std::wstring> subStrings = ParseString(ConvertToWide(arg).c_str());
std::vector<std::wstring> 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<std::wstring> subStrings = ParseString(ConvertToWide(arg).c_str());
std::vector<std::wstring> 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<std::wstring> subStrings = ParseString(ConvertToWide(arg).c_str());
std::vector<std::wstring> 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<std::wstring> 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<std::wstring> 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);
}
}
}

View File

@ -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<std::wstring> 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<UINT, RECT> m_DesktopWorkAreas;
std::vector<RECT> m_OldDesktopWorkAreas;
bool m_MenuActive;
bool m_Logging;
std::wstring m_ConfigEditor;

View File

@ -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<std::wstring>& 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<std::wstring>& 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;
}

View File

@ -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<std::wstring>& iniFileMappings);
static std::wstring GetTemporaryFile(const std::vector<std::wstring>& 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);

View File

@ -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;