rainmeter-studio/Library/Rainmeter.cpp

2527 lines
66 KiB
C++
Raw Normal View History

2009-02-10 18:37:48 +00:00
/*
Copyright (C) 2001 Kimmo Pekkola
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
2009-02-10 18:37:48 +00:00
*/
#include "StdAfx.h"
#include "../Common/MenuTemplate.h"
#include "../Common/PathUtil.h"
2009-02-10 18:37:48 +00:00
#include "Rainmeter.h"
#include "TrayWindow.h"
#include "System.h"
2009-02-10 18:37:48 +00:00
#include "Error.h"
#include "DialogAbout.h"
#include "DialogManage.h"
2009-02-10 18:37:48 +00:00
#include "MeasureNet.h"
2012-11-12 02:10:40 +00:00
#include "MeasureCPU.h"
#include "MeterString.h"
#include "resource.h"
2009-02-10 18:37:48 +00:00
#include "UpdateCheck.h"
#include "../Version.h"
2009-02-10 18:37:48 +00:00
using namespace Gdiplus;
enum TIMER
{
TIMER_NETSTATS = 1
};
enum INTERVAL
{
INTERVAL_NETSTATS = 120000
};
/*
** Initializes Rainmeter.
**
*/
2012-02-16 17:38:45 +00:00
int RainmeterMain(LPWSTR cmdLine)
{
2012-10-06 17:12:40 +00:00
// Avoid loading a dll from current directory
SetDllDirectory(L"");
2013-05-31 14:28:39 +00:00
const WCHAR* layout = nullptr;
2012-10-06 17:12:40 +00:00
if (cmdLine[0] == L'!' || cmdLine[0] == L'[')
{
HWND wnd = FindWindow(RAINMETER_CLASS_NAME, RAINMETER_WINDOW_NAME);
if (wnd)
{
// Deliver bang to existing Rainmeter instance
COPYDATASTRUCT cds;
cds.dwData = 1;
cds.cbData = (DWORD)((wcslen(cmdLine) + 1) * sizeof(WCHAR));
cds.lpData = (PVOID)cmdLine;
2013-05-31 14:28:39 +00:00
SendMessage(wnd, WM_COPYDATA, 0, (LPARAM)&cds);
return 0;
}
2012-05-24 09:19:32 +00:00
2012-10-06 17:12:40 +00:00
// Disallow everything except !LoadLayout.
2012-10-06 17:20:39 +00:00
if (_wcsnicmp(cmdLine, L"!LoadLayout ", 12) == 0)
2012-10-06 17:12:40 +00:00
{
2012-10-06 17:27:19 +00:00
layout = cmdLine + 12; // Skip "!LoadLayout ".
2012-10-06 17:12:40 +00:00
}
else
{
return 1;
}
2012-05-24 09:19:32 +00:00
}
else if (cmdLine[0] == L'"')
{
// Strip quotes
++cmdLine;
WCHAR* pos = wcsrchr(cmdLine, L'"');
if (pos)
{
2012-05-24 09:19:32 +00:00
*pos = L'\0';
}
}
2013-05-31 14:28:39 +00:00
const WCHAR* iniFile = (*cmdLine && !layout) ? cmdLine : nullptr;
2012-05-24 09:19:32 +00:00
auto& rainmeter = GetRainmeter();
int ret = rainmeter.Initialize(iniFile, layout);
2012-07-18 06:22:47 +00:00
if (ret == 0)
{
ret = rainmeter.MessagePump();
}
2013-06-14 13:58:42 +00:00
rainmeter.Finalize();
return ret;
2010-12-11 16:30:49 +00:00
}
2009-02-10 18:37:48 +00:00
2011-03-29 19:21:57 +00:00
/*
2009-02-10 18:37:48 +00:00
** Constructor
**
*/
2013-05-31 14:18:52 +00:00
Rainmeter::Rainmeter() :
m_TrayWindow(),
m_UseD2D(false),
m_Debug(false),
m_DisableVersionCheck(false),
m_NewVersion(false),
m_DesktopWorkAreaChanged(false),
m_DesktopWorkAreaType(false),
m_NormalStayDesktop(true),
m_MenuActive(false),
m_DisableRDP(false),
m_DisableDragging(false),
m_CurrentParser(),
m_Window(),
m_Mutex(),
m_Instance(),
2011-09-23 16:28:38 +00:00
m_ResourceInstance(),
m_ResourceLCID(),
m_GDIplusToken(),
m_GlobalOptions()
2009-02-10 18:37:48 +00:00
{
2013-05-31 14:28:39 +00:00
CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
InitCommonControls();
2009-02-10 18:37:48 +00:00
2011-03-29 19:21:57 +00:00
// Initialize GDI+.
GdiplusStartupInput gdiplusStartupInput;
2013-05-31 14:28:39 +00:00
GdiplusStartup(&m_GDIplusToken, &gdiplusStartupInput, nullptr);
2009-02-10 18:37:48 +00:00
}
2011-03-29 19:21:57 +00:00
/*
2009-02-10 18:37:48 +00:00
** Destructor
**
*/
2013-05-31 14:18:52 +00:00
Rainmeter::~Rainmeter()
2009-02-10 18:37:48 +00:00
{
CoUninitialize();
2009-02-10 18:37:48 +00:00
GdiplusShutdown(m_GDIplusToken);
}
Rainmeter& Rainmeter::GetInstance()
{
static Rainmeter s_Rainmeter;
return s_Rainmeter;
}
2011-03-29 19:21:57 +00:00
/*
2009-02-10 18:37:48 +00:00
** The main initialization function for the module.
**
*/
2013-05-31 14:18:52 +00:00
int Rainmeter::Initialize(LPCWSTR iniPath, LPCWSTR layout)
2009-02-10 18:37:48 +00:00
{
2012-06-11 13:15:03 +00:00
m_Instance = GetModuleHandle(L"Rainmeter");
2012-03-22 21:16:41 +00:00
WCHAR* buffer = new WCHAR[MAX_LINE_LENGTH];
GetModuleFileName(m_Instance, buffer, MAX_LINE_LENGTH);
2009-02-10 18:37:48 +00:00
// Remove the module's name from the path
2012-03-22 21:16:41 +00:00
WCHAR* pos = wcsrchr(buffer, L'\\');
m_Path.assign(buffer, pos ? pos - buffer + 1 : 0);
m_Drive = PathUtil::GetVolume(m_Path);
bool bDefaultIniLocation = false;
2012-10-06 17:12:40 +00:00
if (iniPath)
{
// The command line defines the location of Rainmeter.ini (or whatever it calls it).
2012-05-24 09:19:32 +00:00
std::wstring iniFile = iniPath;
PathUtil::ExpandEnvironmentVariables(iniFile);
if (iniFile.empty() || PathUtil::IsSeparator(iniFile[iniFile.length() - 1]))
{
iniFile += L"Rainmeter.ini";
}
2011-11-10 13:44:19 +00:00
else if (iniFile.length() <= 4 || _wcsicmp(iniFile.c_str() + (iniFile.length() - 4), L".ini") != 0)
{
iniFile += L"\\Rainmeter.ini";
}
if (!PathUtil::IsSeparator(iniFile[0]) && iniFile.find_first_of(L':') == std::wstring::npos)
{
// Make absolute path
iniFile.insert(0, m_Path);
}
m_IniFile = iniFile;
bDefaultIniLocation = true;
}
else
{
m_IniFile = m_Path;
m_IniFile += L"Rainmeter.ini";
// If the ini file doesn't exist in the program folder store it to the %APPDATA% instead so that things work better in Vista/Win7
if (_waccess(m_IniFile.c_str(), 0) == -1)
{
m_IniFile = L"%APPDATA%\\Rainmeter\\Rainmeter.ini";
PathUtil::ExpandEnvironmentVariables(m_IniFile);
bDefaultIniLocation = true;
}
}
if (IsAlreadyRunning())
{
// Instance already running with same .ini file
return 1;
}
WNDCLASS wc = {0};
wc.lpfnWndProc = (WNDPROC)MainWndProc;
wc.hInstance = m_Instance;
wc.lpszClassName = RAINMETER_CLASS_NAME;
2012-06-01 13:21:25 +00:00
ATOM className = RegisterClass(&wc);
m_Window = CreateWindowEx(
WS_EX_TOOLWINDOW,
2012-06-01 13:21:25 +00:00
MAKEINTATOM(className),
RAINMETER_WINDOW_NAME,
WS_POPUP | WS_DISABLED,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
2013-05-31 14:28:39 +00:00
nullptr,
nullptr,
m_Instance,
2013-05-31 14:28:39 +00:00
nullptr);
if (!m_Window) return 1;
2013-06-13 14:22:42 +00:00
Logger& logger = GetLogger();
2012-05-05 11:45:34 +00:00
const WCHAR* iniFile = m_IniFile.c_str();
// Set file locations
{
m_SettingsPath = PathUtil::GetFolderFromFilePath(m_IniFile);
2012-05-05 11:45:34 +00:00
size_t len = m_IniFile.length();
2012-05-29 16:02:20 +00:00
if (len > 4 && _wcsicmp(iniFile + (len - 4), L".ini") == 0)
2012-05-05 11:45:34 +00:00
{
len -= 4;
}
std::wstring logFile(m_IniFile, 0, len);
m_DataFile = m_StatsFile = logFile;
logFile += L".log";
2012-05-05 11:45:34 +00:00
m_StatsFile += L".stats";
m_DataFile += L".data";
logger.SetLogFilePath(logFile);
}
2012-05-05 11:45:34 +00:00
// Create a default Rainmeter.ini file if needed
if (_waccess(iniFile, 0) == -1)
{
CreateOptionsFile();
}
2012-05-05 11:45:34 +00:00
bool dataFileCreated = false;
if (_waccess(m_DataFile.c_str(), 0) == -1)
{
2012-05-05 11:45:34 +00:00
dataFileCreated = true;
CreateDataFile();
}
// Reset log file
2013-05-31 14:18:52 +00:00
System::RemoveFile(logger.GetLogFilePath());
m_Debug = 0!=GetPrivateProfileInt(L"Rainmeter", L"Debug", 0, iniFile);
const bool logging = GetPrivateProfileInt(L"Rainmeter", L"Logging", 0, iniFile) != 0;
logger.SetLogToFile(logging);
if (logging)
{
logger.StartLogFile();
}
// Determine the language resource to load
std::wstring resource = m_Path + L"Languages\\";
2012-05-05 11:45:34 +00:00
if (GetPrivateProfileString(L"Rainmeter", L"Language", L"", buffer, MAX_LINE_LENGTH, iniFile) == 0)
{
// Use whatever the user selected for the installer
DWORD size = MAX_LINE_LENGTH;
HKEY hKey;
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, L"Software\\Rainmeter", 0, KEY_QUERY_VALUE | KEY_WOW64_32KEY, &hKey) == ERROR_SUCCESS)
{
DWORD type = 0;
2013-05-31 14:28:39 +00:00
if (RegQueryValueEx(hKey, L"Language", nullptr, &type, (LPBYTE)buffer, (LPDWORD)&size) != ERROR_SUCCESS ||
type != REG_SZ)
{
2012-03-22 21:16:41 +00:00
buffer[0] = L'\0';
}
RegCloseKey(hKey);
}
}
2012-03-22 21:16:41 +00:00
if (buffer[0] != L'\0')
{
// Try selected language
2013-05-31 14:28:39 +00:00
m_ResourceLCID = wcstoul(buffer, nullptr, 10);
2012-03-22 21:16:41 +00:00
resource += buffer;
resource += L".dll";
2013-05-31 14:28:39 +00:00
m_ResourceInstance = LoadLibraryEx(resource.c_str(), nullptr, DONT_RESOLVE_DLL_REFERENCES | LOAD_LIBRARY_AS_DATAFILE);
}
if (!m_ResourceInstance)
{
// Try English
resource = m_Path;
resource += L"Languages\\1033.dll";
2013-05-31 14:28:39 +00:00
m_ResourceInstance = LoadLibraryEx(resource.c_str(), nullptr, DONT_RESOLVE_DLL_REFERENCES | LOAD_LIBRARY_AS_DATAFILE);
m_ResourceLCID = 1033;
if (!m_ResourceInstance)
{
2013-05-31 14:28:39 +00:00
MessageBox(nullptr, L"Unable to load language library", APPNAME, MB_OK | MB_TOPMOST | MB_ICONERROR);
2012-05-24 09:19:32 +00:00
return 1;
}
}
// Get skin folder path
2012-05-05 11:45:34 +00:00
size_t len = GetPrivateProfileString(L"Rainmeter", L"SkinPath", L"", buffer, MAX_LINE_LENGTH, iniFile);
if (len > 0 &&
_waccess(buffer, 0) != -1) // Temporary fix
{
// Try Rainmeter.ini first
2012-03-22 21:16:41 +00:00
m_SkinPath.assign(buffer, len);
PathUtil::ExpandEnvironmentVariables(m_SkinPath);
PathUtil::AppendBacklashIfMissing(m_SkinPath);
2011-05-01 17:10:49 +00:00
}
else if (bDefaultIniLocation &&
2013-05-31 14:28:39 +00:00
SUCCEEDED(SHGetFolderPath(nullptr, CSIDL_MYDOCUMENTS, nullptr, SHGFP_TYPE_CURRENT, buffer)))
2011-05-01 17:10:49 +00:00
{
// Use My Documents/Rainmeter/Skins
m_SkinPath = buffer;
m_SkinPath += L"\\Rainmeter\\";
2013-05-31 14:28:39 +00:00
CreateDirectory(m_SkinPath.c_str(), nullptr);
m_SkinPath += L"Skins\\";
2012-05-05 11:45:34 +00:00
WritePrivateProfileString(L"Rainmeter", L"SkinPath", m_SkinPath.c_str(), iniFile);
}
else
{
m_SkinPath = m_Path + L"Skins\\";
}
2012-10-02 14:44:47 +00:00
// Create user skins, layouts, addons, and plugins folders if needed
2012-06-10 09:17:47 +00:00
CreateComponentFolders(bDefaultIniLocation);
2012-03-22 21:16:41 +00:00
delete [] buffer;
2013-05-31 14:28:39 +00:00
buffer = nullptr;
2013-05-30 14:19:42 +00:00
LogNoticeF(L"Path: %s", m_Path.c_str());
LogNoticeF(L"IniFile: %s", iniFile);
LogNoticeF(L"SkinPath: %s", m_SkinPath.c_str());
2009-02-10 18:37:48 +00:00
// Test that the Rainmeter.ini file is writable
TestSettingsFile(bDefaultIniLocation);
2013-05-31 14:18:52 +00:00
System::Initialize(m_Instance);
2013-05-31 14:18:52 +00:00
MeasureNet::InitializeStatic();
MeasureCPU::InitializeStatic();
MeterString::InitializeStatic();
// Tray must exist before skins are read
2013-05-31 14:18:52 +00:00
m_TrayWindow = new TrayWindow();
2012-05-05 12:59:35 +00:00
m_TrayWindow->Initialize();
2009-02-10 18:37:48 +00:00
ReloadSettings();
if (m_SkinFolders.empty())
{
2011-09-23 16:28:38 +00:00
std::wstring error = GetFormattedString(ID_STR_NOAVAILABLESKINS, m_SkinPath.c_str());
2013-05-31 14:28:39 +00:00
ShowMessage(nullptr, error.c_str(), MB_OK | MB_ICONERROR);
}
2009-02-10 18:37:48 +00:00
ResetStats();
ReadStats();
// Change the work area if necessary
if (m_DesktopWorkAreaChanged)
{
UpdateDesktopWorkArea(false);
2009-02-10 18:37:48 +00:00
}
2012-10-06 17:12:40 +00:00
bool layoutLoaded = false;
if (layout)
{
2013-05-31 14:18:52 +00:00
std::vector<std::wstring> args = CommandHandler::ParseString(layout);
2012-10-06 17:12:40 +00:00
layoutLoaded = (args.size() == 1 && LoadLayout(args[0]));
}
if (!layoutLoaded)
{
ActivateActiveSkins();
}
2009-02-10 18:37:48 +00:00
2012-05-05 11:45:34 +00:00
if (dataFileCreated)
{
m_TrayWindow->ShowWelcomeNotification();
}
else if (!m_DisableVersionCheck)
{
CheckUpdate();
}
2012-05-24 09:19:32 +00:00
return 0; // All is OK
2009-02-10 18:37:48 +00:00
}
2013-06-14 13:58:42 +00:00
void Rainmeter::Finalize()
{
KillTimer(m_Window, TIMER_NETSTATS);
DeleteAllUnmanagedMeterWindows();
DeleteAllMeterWindows();
delete m_TrayWindow;
System::Finalize();
MeasureNet::UpdateIFTable();
MeasureNet::UpdateStats();
WriteStats(true);
MeasureNet::FinalizeStatic();
MeasureCPU::FinalizeStatic();
MeterString::FinalizeStatic();
// Change the work area back
if (m_DesktopWorkAreaChanged)
{
UpdateDesktopWorkArea(true);
}
if (m_ResourceInstance) FreeLibrary(m_ResourceInstance);
if (m_Mutex) ReleaseMutex(m_Mutex);
}
2013-05-31 14:18:52 +00:00
bool Rainmeter::IsAlreadyRunning()
{
typedef struct
{
ULONG i[2];
ULONG buf[4];
unsigned char in[64];
unsigned char digest[16];
} MD5_CTX;
typedef void (WINAPI * FPMD5INIT)(MD5_CTX* context);
typedef void (WINAPI * FPMD5UPDATE)(MD5_CTX* context, const unsigned char* input, unsigned int inlen);
typedef void (WINAPI * FPMD5FINAL)(MD5_CTX* context);
bool alreadyRunning = false;
// Create MD5 digest from command line
2013-05-31 14:18:52 +00:00
HMODULE cryptDll = System::RmLoadLibrary(L"cryptdll.dll");
if (cryptDll)
{
FPMD5INIT MD5Init = (FPMD5INIT)GetProcAddress(cryptDll, "MD5Init");
FPMD5UPDATE MD5Update = (FPMD5UPDATE)GetProcAddress(cryptDll, "MD5Update");
FPMD5FINAL MD5Final = (FPMD5FINAL)GetProcAddress(cryptDll, "MD5Final");
if (MD5Init && MD5Update && MD5Final)
{
std::wstring data = m_IniFile;
_wcsupr(&data[0]);
MD5_CTX ctx = {0};
MD5Init(&ctx);
MD5Update(&ctx, (LPBYTE)&data[0], data.length() * sizeof(WCHAR));
MD5Final(&ctx);
FreeLibrary(cryptDll);
// Convert MD5 digest to mutex string (e.g. "Rainmeter0123456789abcdef0123456789abcdef")
const WCHAR hexChars[] = L"0123456789abcdef";
WCHAR mutexName[64] = L"Rainmeter";
WCHAR* pos = mutexName + (_countof(L"Rainmeter") - 1);
for (size_t i = 0; i < 16; ++i)
{
*(pos++) = hexChars[ctx.digest[i] >> 4];
*(pos++) = hexChars[ctx.digest[i] & 0xF];
}
*pos = L'\0';
2013-05-31 14:28:39 +00:00
m_Mutex = CreateMutex(nullptr, FALSE, mutexName);
if (GetLastError() == ERROR_ALREADY_EXISTS)
{
alreadyRunning = true;
2013-05-31 14:28:39 +00:00
m_Mutex = nullptr;
}
}
FreeLibrary(cryptDll);
}
return alreadyRunning;
}
2013-05-31 14:18:52 +00:00
int Rainmeter::MessagePump()
{
MSG msg;
BOOL ret;
// Run the standard window message loop
2013-05-31 14:28:39 +00:00
while ((ret = GetMessage(&msg, nullptr, 0, 0)) != 0)
{
if (ret == -1)
{
break;
}
2013-05-31 14:18:52 +00:00
if (!Dialog::HandleMessage(msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return (int)msg.wParam;
}
2013-05-31 14:18:52 +00:00
LRESULT CALLBACK Rainmeter::MainWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_DESTROY:
PostQuitMessage(0);
break;
case WM_COPYDATA:
{
COPYDATASTRUCT* cds = (COPYDATASTRUCT*)lParam;
if (cds)
{
const WCHAR* data = (const WCHAR*)cds->lpData;
if (cds->dwData == 1 && (cds->cbData > 0))
{
GetRainmeter().DelayedExecuteCommand(data);
}
}
}
break;
case WM_TIMER:
if (wParam == TIMER_NETSTATS)
{
2013-05-31 14:18:52 +00:00
MeasureNet::UpdateIFTable();
MeasureNet::UpdateStats();
GetRainmeter().WriteStats(false);
}
break;
case WM_RAINMETER_DELAYED_REFRESH_ALL:
GetRainmeter().RefreshAll();
break;
case WM_RAINMETER_DELAYED_EXECUTE:
if (lParam)
{
// Execute bang
WCHAR* bang = (WCHAR*)lParam;
GetRainmeter().ExecuteCommand(bang, nullptr);
free(bang); // _wcsdup()
}
break;
case WM_RAINMETER_EXECUTE:
if (GetRainmeter().HasMeterWindow((MeterWindow*)wParam))
{
GetRainmeter().ExecuteCommand((const WCHAR*)lParam, (MeterWindow*)wParam);
}
break;
default:
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
return 0;
}
2013-05-31 14:18:52 +00:00
void Rainmeter::SetNetworkStatisticsTimer()
{
2013-05-31 14:28:39 +00:00
static bool set = SetTimer(m_Window, TIMER_NETSTATS, INTERVAL_NETSTATS, nullptr);
}
2013-05-31 14:18:52 +00:00
void Rainmeter::CreateOptionsFile()
{
2013-05-31 14:28:39 +00:00
CreateDirectory(m_SettingsPath.c_str(), nullptr);
2012-10-02 14:44:47 +00:00
std::wstring defaultIni = GetDefaultLayoutPath();
defaultIni += L"illustro default\\Rainmeter.ini";
2013-05-31 14:18:52 +00:00
System::CopyFiles(defaultIni, m_IniFile);
}
2013-05-31 14:18:52 +00:00
void Rainmeter::CreateDataFile()
2012-05-05 11:45:34 +00:00
{
2012-06-10 09:17:47 +00:00
std::wstring tmpSz = m_SettingsPath + L"Plugins.ini";
2012-05-05 11:45:34 +00:00
const WCHAR* pluginsFile = tmpSz.c_str();
const WCHAR* dataFile = m_DataFile.c_str();
if (_waccess(pluginsFile, 0) == 0)
{
2012-05-13 09:30:20 +00:00
MoveFile(pluginsFile, dataFile);
2012-05-05 11:45:34 +00:00
}
else
{
// Create empty file
2013-05-31 14:28:39 +00:00
HANDLE file = CreateFile(dataFile, GENERIC_WRITE, 0, nullptr, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, nullptr);
2012-05-05 11:45:34 +00:00
if (file != INVALID_HANDLE_VALUE)
{
CloseHandle(file);
}
}
}
2013-05-31 14:18:52 +00:00
void Rainmeter::CreateComponentFolders(bool defaultIniLocation)
{
std::wstring path;
2013-05-31 14:28:39 +00:00
if (CreateDirectory(m_SkinPath.c_str(), nullptr))
{
// Folder just created, so copy default skins there
std::wstring from = GetDefaultSkinPath();
from += L"*.*";
2013-05-31 14:18:52 +00:00
System::CopyFiles(from, m_SkinPath);
}
else
{
path = m_SkinPath;
path += L"Backup";
if (_waccess(path.c_str(), 0) != -1)
{
std::wstring newPath = m_SkinPath + L"@Backup";
MoveFile(path.c_str(), newPath.c_str());
}
}
2012-10-02 14:44:47 +00:00
path = GetLayoutPath();
if (_waccess(path.c_str(), 0) == -1)
{
2012-10-02 14:44:47 +00:00
std::wstring themesPath = m_SettingsPath + L"Themes";
if (_waccess(themesPath.c_str(), 0) != -1)
{
// Migrate Themes into Layouts for backwards compatibility and rename
// Rainmeter.thm to Rainmeter.ini and RainThemes.bmp to Wallpaper.bmp.
MoveFile(themesPath.c_str(), path.c_str());
path += L'*'; // For FindFirstFile.
WIN32_FIND_DATA fd;
HANDLE hFind = FindFirstFile(path.c_str(), &fd);
path.pop_back(); // Remove '*'.
if (hFind != INVALID_HANDLE_VALUE)
{
do
{
if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY &&
PathUtil::IsDotOrDotDot(fd.cFileName))
2012-10-02 14:44:47 +00:00
{
std::wstring layoutFolder = path + fd.cFileName;
layoutFolder += L'\\';
std::wstring file = layoutFolder + L"Rainmeter.thm";
if (_waccess(file.c_str(), 0) != -1)
{
std::wstring newFile = layoutFolder + L"Rainmeter.ini";
MoveFile(file.c_str(), newFile.c_str());
}
file = layoutFolder + L"RainThemes.bmp";
if (_waccess(file.c_str(), 0) != -1)
{
2012-10-02 18:01:40 +00:00
std::wstring newFile = layoutFolder + L"Wallpaper.bmp";
2012-10-02 14:44:47 +00:00
MoveFile(file.c_str(), newFile.c_str());
}
}
}
while (FindNextFile(hFind, &fd));
FindClose(hFind);
}
}
else
{
2012-10-02 14:44:47 +00:00
std::wstring from = GetDefaultLayoutPath();
if (_waccess(from.c_str(), 0) != -1)
{
2013-05-31 14:18:52 +00:00
System::CopyFiles(from, m_SettingsPath);
2012-10-02 14:44:47 +00:00
}
}
}
else
{
path += L"Backup";
if (_waccess(path.c_str(), 0) != -1)
{
2012-10-02 14:44:47 +00:00
std::wstring newPath = GetLayoutPath();
newPath += L"@Backup";
MoveFile(path.c_str(), newPath.c_str());
}
}
if (defaultIniLocation)
{
path = GetUserPluginPath();
if (_waccess(path.c_str(), 0) == -1)
{
std::wstring from = GetDefaultPluginPath();
if (_waccess(from.c_str(), 0) != -1)
{
2013-05-31 14:18:52 +00:00
System::CopyFiles(from, m_SettingsPath);
}
}
path = GetAddonPath();
if (_waccess(path.c_str(), 0) == -1)
{
std::wstring from = GetDefaultAddonPath();
if (_waccess(from.c_str(), 0) != -1)
{
2013-05-31 14:18:52 +00:00
System::CopyFiles(from, m_SettingsPath);
}
}
path = m_SettingsPath;
path += L"Rainmeter.exe";
2012-06-11 13:15:03 +00:00
const WCHAR* pathSz = path.c_str();
if (_waccess(pathSz, 0) == -1)
{
2012-06-11 13:15:03 +00:00
// Create a hidden stub Rainmeter.exe into SettingsPath for old addon
// using relative path to Rainmeter.exe
2012-06-11 13:15:03 +00:00
std::wstring from = m_Path + L"Rainmeter.exe";
2013-05-31 14:18:52 +00:00
System::CopyFiles(from, path);
2012-06-11 13:15:03 +00:00
// Get rid of all resources from the stub executable
HANDLE stub = BeginUpdateResource(pathSz, TRUE);
// Add the manifest of Rainmeter.dll to the stub
HRSRC manifest = FindResource(m_Instance, MAKEINTRESOURCE(2), RT_MANIFEST);
DWORD manifestSize = SizeofResource(m_Instance, manifest);
HGLOBAL manifestLoad = LoadResource(m_Instance, manifest);
void* manifestLoadData = LockResource(manifestLoad);
if (manifestLoadData)
{
LANGID langID = MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT);
UpdateResource(stub, RT_MANIFEST, MAKEINTRESOURCE(1), langID, manifestLoadData, manifestSize);
}
EndUpdateResource(stub, FALSE);
SetFileAttributes(pathSz, FILE_ATTRIBUTE_HIDDEN);
}
}
}
2013-05-31 14:18:52 +00:00
void Rainmeter::ReloadSettings()
2009-02-10 18:37:48 +00:00
{
2012-06-10 09:17:47 +00:00
ScanForSkins();
2012-10-02 14:44:47 +00:00
ScanForLayouts();
2009-02-10 18:37:48 +00:00
ReadGeneralSettings(m_IniFile);
}
2013-05-31 14:18:52 +00:00
void Rainmeter::EditSettings()
2012-02-21 19:15:10 +00:00
{
std::wstring file = L'"' + m_IniFile;
file += L'"';
2013-05-31 14:18:52 +00:00
CommandHandler::RunFile(m_SkinEditor.c_str(), file.c_str());
2012-02-21 19:15:10 +00:00
}
2013-05-31 14:18:52 +00:00
void Rainmeter::EditSkinFile(const std::wstring& name, const std::wstring& iniFile)
2012-02-21 19:15:10 +00:00
{
std::wstring args = L'"' + m_SkinPath;
2012-08-18 06:45:16 +00:00
args += name;
2012-05-29 16:02:20 +00:00
args += L'\\';
args += iniFile;
args += L'"';
2013-05-31 14:18:52 +00:00
CommandHandler::RunFile(m_SkinEditor.c_str(), args.c_str());
2012-02-21 19:15:10 +00:00
}
2013-05-31 14:18:52 +00:00
void Rainmeter::OpenSkinFolder(const std::wstring& name)
2012-02-21 19:15:10 +00:00
{
std::wstring folderPath = m_SkinPath + name;
2013-05-31 14:18:52 +00:00
CommandHandler::RunFile(folderPath.c_str());
2012-02-21 19:15:10 +00:00
}
2013-05-31 14:18:52 +00:00
void Rainmeter::ActivateActiveSkins()
2011-09-04 18:06:19 +00:00
{
std::multimap<int, int>::const_iterator iter = m_SkinOrders.begin();
for ( ; iter != m_SkinOrders.end(); ++iter)
2011-09-04 18:06:19 +00:00
{
const SkinFolder& skinFolder = m_SkinFolders[(*iter).second];
if (skinFolder.active > 0 && skinFolder.active <= (int)skinFolder.files.size())
2011-09-04 18:06:19 +00:00
{
ActivateSkin((*iter).second, skinFolder.active - 1);
2011-09-04 18:06:19 +00:00
}
}
}
2013-05-31 14:18:52 +00:00
void Rainmeter::ActivateSkin(int folderIndex, int fileIndex)
2009-02-10 18:37:48 +00:00
{
if (folderIndex >= 0 && folderIndex < (int)m_SkinFolders.size() &&
fileIndex >= 0 && fileIndex < (int)m_SkinFolders[folderIndex].files.size())
2009-02-10 18:37:48 +00:00
{
2012-07-18 10:44:03 +00:00
SkinFolder& skinFolder = m_SkinFolders[folderIndex];
const std::wstring& file = skinFolder.files[fileIndex];
const WCHAR* fileSz = file.c_str();
std::wstring folderPath = GetFolderPath(folderIndex);
2009-02-10 18:37:48 +00:00
// Verify that the skin is not already active
2013-05-31 14:18:52 +00:00
std::map<std::wstring, MeterWindow*>::const_iterator iter = m_MeterWindows.find(folderPath);
if (iter != m_MeterWindows.end())
2009-02-10 18:37:48 +00:00
{
if (wcscmp(((*iter).second)->GetFileName().c_str(), fileSz) == 0)
2009-02-10 18:37:48 +00:00
{
2013-05-30 14:19:42 +00:00
LogWarningF(L"!ActivateConfig: \"%s\" already active", folderPath.c_str());
2009-02-10 18:37:48 +00:00
return;
}
else
{
// Deactivate the existing skin
DeactivateSkin((*iter).second, folderIndex);
2009-02-10 18:37:48 +00:00
}
}
// Verify whether the ini-file exists
std::wstring skinIniPath = m_SkinPath + folderPath;
2011-12-04 22:18:40 +00:00
skinIniPath += L'\\';
skinIniPath += file;
if (_waccess(skinIniPath.c_str(), 0) == -1)
2009-02-10 18:37:48 +00:00
{
std::wstring message = GetFormattedString(ID_STR_UNABLETOACTIVATESKIN, folderPath.c_str(), fileSz);
2013-05-31 14:28:39 +00:00
ShowMessage(nullptr, message.c_str(), MB_OK | MB_ICONEXCLAMATION);
return;
}
2009-02-10 18:37:48 +00:00
2012-07-18 10:44:03 +00:00
if (skinFolder.active != fileIndex + 1)
{
// Write only if changed.
skinFolder.active = fileIndex + 1;
WriteActive(folderPath, fileIndex);
}
CreateMeterWindow(folderPath, file);
2009-02-10 18:37:48 +00:00
}
}
2013-05-31 14:18:52 +00:00
void Rainmeter::DeactivateSkin(MeterWindow* meterWindow, int folderIndex, bool save)
2009-02-10 18:37:48 +00:00
{
if (folderIndex >= 0 && folderIndex < (int)m_SkinFolders.size())
2009-02-10 18:37:48 +00:00
{
m_SkinFolders[folderIndex].active = 0; // Deactivate the skin
2009-02-10 18:37:48 +00:00
}
else if (folderIndex == -1 && meterWindow)
2009-02-10 18:37:48 +00:00
{
folderIndex = FindSkinFolderIndex(meterWindow->GetFolderPath());
if (folderIndex != -1)
2009-02-10 18:37:48 +00:00
{
m_SkinFolders[folderIndex].active = 0;
2009-02-10 18:37:48 +00:00
}
}
if (meterWindow)
{
if (save)
{
// Disable the skin in the ini-file
WriteActive(meterWindow->GetFolderPath(), -1);
}
2009-02-10 18:37:48 +00:00
2012-02-02 12:05:14 +00:00
meterWindow->Deactivate();
2009-02-10 18:37:48 +00:00
}
}
2013-05-31 14:18:52 +00:00
void Rainmeter::ToggleSkin(int folderIndex, int fileIndex)
2011-10-29 10:36:07 +00:00
{
if (folderIndex >= 0 && folderIndex < (int)m_SkinFolders.size() &&
fileIndex >= 0 && fileIndex < (int)m_SkinFolders[folderIndex].files.size())
2011-10-29 10:36:07 +00:00
{
if (m_SkinFolders[folderIndex].active == fileIndex + 1)
2011-10-29 10:36:07 +00:00
{
MeterWindow* meterWindow = GetRainmeter().GetMeterWindow(GetFolderPath(folderIndex));
DeactivateSkin(meterWindow, folderIndex);
2011-10-29 10:36:07 +00:00
}
else
{
ActivateSkin(folderIndex, fileIndex);
2011-10-29 10:36:07 +00:00
}
}
}
2013-05-31 14:18:52 +00:00
void Rainmeter::SetSkinPath(const std::wstring& skinPath)
{
WritePrivateProfileString(L"Rainmeter", L"SkinPath", skinPath.c_str(), m_IniFile.c_str());
}
2013-05-31 14:18:52 +00:00
void Rainmeter::SetSkinEditor(const std::wstring& path)
{
2012-11-13 16:39:54 +00:00
if (!path.empty())
{
2012-11-13 16:39:54 +00:00
m_SkinEditor = path;
WritePrivateProfileString(L"Rainmeter", L"ConfigEditor", path.c_str(), m_IniFile.c_str());
}
}
2013-05-31 14:18:52 +00:00
void Rainmeter::WriteActive(const std::wstring& folderPath, int fileIndex)
{
WCHAR buffer[32];
_itow_s(fileIndex + 1, buffer, 10);
WritePrivateProfileString(folderPath.c_str(), L"Active", buffer, m_IniFile.c_str());
}
2013-05-31 14:18:52 +00:00
void Rainmeter::CreateMeterWindow(const std::wstring& folderPath, const std::wstring& file)
2009-02-10 18:37:48 +00:00
{
2013-05-31 14:18:52 +00:00
MeterWindow* mw = new MeterWindow(folderPath, file);
2009-02-10 18:37:48 +00:00
2012-07-18 06:22:47 +00:00
// Note: May modify existing key
m_MeterWindows[folderPath] = mw;
2012-07-18 06:22:47 +00:00
mw->Initialize();
2013-05-31 14:18:52 +00:00
DialogAbout::UpdateSkins();
DialogManage::UpdateSkins(mw);
2009-02-10 18:37:48 +00:00
}
2013-05-31 14:18:52 +00:00
void Rainmeter::DeleteAllMeterWindows()
2009-02-10 18:37:48 +00:00
{
auto it = m_MeterWindows.cbegin();
while (it != m_MeterWindows.cend())
{
MeterWindow* mw = (*it).second;
m_MeterWindows.erase(it++); // Remove before deleting MeterWindow
DialogManage::UpdateSkins(mw, true);
delete mw;
2012-02-02 12:05:14 +00:00
}
2009-02-10 18:37:48 +00:00
m_MeterWindows.clear();
2013-05-31 14:18:52 +00:00
DialogAbout::UpdateSkins();
}
2013-05-31 14:18:52 +00:00
void Rainmeter::DeleteAllUnmanagedMeterWindows()
{
for (auto it = m_UnmanagedMeterWindows.cbegin(); it != m_UnmanagedMeterWindows.cend(); ++it)
{
delete (*it);
}
m_UnmanagedMeterWindows.clear();
}
/*
** Removes the skin from m_MeterWindows. The skin should delete itself.
**
*/
2013-05-31 14:18:52 +00:00
void Rainmeter::RemoveMeterWindow(MeterWindow* meterWindow)
{
for (auto it = m_MeterWindows.cbegin(); it != m_MeterWindows.cend(); ++it)
{
if ((*it).second == meterWindow)
{
m_MeterWindows.erase(it);
2013-05-31 14:18:52 +00:00
DialogManage::UpdateSkins(meterWindow, true);
DialogAbout::UpdateSkins();
break;
}
2009-02-10 18:37:48 +00:00
}
}
/*
** Adds the skin to m_UnmanagedMeterWindows. The skin should remove itself by calling RemoveUnmanagedMeterWindow().
**
*/
2013-05-31 14:18:52 +00:00
void Rainmeter::AddUnmanagedMeterWindow(MeterWindow* meterWindow)
{
for (auto it = m_UnmanagedMeterWindows.cbegin(); it != m_UnmanagedMeterWindows.cend(); ++it)
{
if ((*it) == meterWindow) // already added
{
return;
}
}
m_UnmanagedMeterWindows.push_back(meterWindow);
}
2013-05-31 14:18:52 +00:00
void Rainmeter::RemoveUnmanagedMeterWindow(MeterWindow* meterWindow)
{
for (auto it = m_UnmanagedMeterWindows.cbegin(); it != m_UnmanagedMeterWindows.cend(); ++it)
{
if ((*it) == meterWindow)
{
m_UnmanagedMeterWindows.erase(it);
break;
}
}
}
2013-05-31 14:18:52 +00:00
bool Rainmeter::HasMeterWindow(const MeterWindow* meterWindow) const
{
for (auto it = m_MeterWindows.begin(); it != m_MeterWindows.end(); ++it)
{
if ((*it).second == meterWindow)
{
return true;
}
}
return false;
}
2013-05-31 14:18:52 +00:00
MeterWindow* Rainmeter::GetMeterWindow(const std::wstring& folderPath)
2009-02-10 18:37:48 +00:00
{
const WCHAR* folderSz = folderPath.c_str();
2013-05-31 14:18:52 +00:00
std::map<std::wstring, MeterWindow*>::const_iterator iter = m_MeterWindows.begin();
for (; iter != m_MeterWindows.end(); ++iter)
2009-02-10 18:37:48 +00:00
{
if (_wcsicmp((*iter).first.c_str(), folderSz) == 0)
2009-02-10 18:37:48 +00:00
{
return (*iter).second;
}
}
2013-05-31 14:28:39 +00:00
return nullptr;
2009-02-10 18:37:48 +00:00
}
2013-05-31 14:18:52 +00:00
MeterWindow* Rainmeter::GetMeterWindowByINI(const std::wstring& ini_searching)
{
if (_wcsnicmp(m_SkinPath.c_str(), ini_searching.c_str(), m_SkinPath.length()) == 0)
{
const std::wstring config_searching = ini_searching.substr(m_SkinPath.length());
2013-05-31 14:18:52 +00:00
std::map<std::wstring, MeterWindow*>::const_iterator iter = m_MeterWindows.begin();
for (; iter != m_MeterWindows.end(); ++iter)
{
std::wstring config_current = (*iter).second->GetFolderPath() + L'\\';
config_current += (*iter).second->GetFileName();
if (_wcsicmp(config_current.c_str(), config_searching.c_str()) == 0)
{
return (*iter).second;
}
}
}
2013-05-31 14:28:39 +00:00
return nullptr;
}
2013-05-31 14:18:52 +00:00
std::pair<int, int> Rainmeter::GetMeterWindowIndex(const std::wstring& folderPath, const std::wstring& file)
{
int index = FindSkinFolderIndex(folderPath);
if (index != -1)
{
const SkinFolder& skinFolder = m_SkinFolders[index];
const WCHAR* fileSz = file.c_str();
2013-01-27 09:39:13 +00:00
for (size_t i = 0, isize = skinFolder.files.size(); i < isize; ++i)
{
if (_wcsicmp(skinFolder.files[i].c_str(), fileSz) == 0)
{
2013-01-27 09:39:13 +00:00
return std::make_pair(index, (int)i);
}
}
}
return std::make_pair(-1, -1); // Error
}
2013-05-31 14:18:52 +00:00
std::pair<int, int> Rainmeter::GetMeterWindowIndex(UINT menuCommand)
2011-10-29 10:36:07 +00:00
{
if (menuCommand >= ID_CONFIG_FIRST && menuCommand <= ID_CONFIG_LAST)
{
// Check which skin was selected
for (size_t i = 0, isize = m_SkinFolders.size(); i < isize; ++i)
2011-10-29 10:36:07 +00:00
{
const SkinFolder& skinFolder = m_SkinFolders[i];
if (menuCommand >= skinFolder.commandBase &&
menuCommand < (skinFolder.commandBase + skinFolder.files.size()))
2011-10-29 10:36:07 +00:00
{
2013-01-27 09:39:13 +00:00
return std::make_pair((int)i, (int)(menuCommand - skinFolder.commandBase));
2011-10-29 10:36:07 +00:00
}
}
}
2013-01-27 09:39:13 +00:00
return std::make_pair(-1, -1); // error;
2011-10-29 10:36:07 +00:00
}
2013-05-31 14:18:52 +00:00
MeterWindow* Rainmeter::GetMeterWindow(HWND hwnd)
{
2013-05-31 14:18:52 +00:00
std::map<std::wstring, MeterWindow*>::const_iterator iter = m_MeterWindows.begin();
for (; iter != m_MeterWindows.end(); ++iter)
{
if ((*iter).second->GetWindow() == hwnd)
{
return (*iter).second;
}
}
2013-05-31 14:28:39 +00:00
return nullptr;
}
2013-05-31 14:18:52 +00:00
void Rainmeter::GetMeterWindowsByLoadOrder(std::multimap<int, MeterWindow*>& windows, const std::wstring& group)
{
2013-05-31 14:18:52 +00:00
std::map<std::wstring, MeterWindow*>::const_iterator iter = m_MeterWindows.begin();
for (; iter != m_MeterWindows.end(); ++iter)
{
2013-05-31 14:18:52 +00:00
MeterWindow* mw = (*iter).second;
if (mw && (group.empty() || mw->BelongsToGroup(group)))
{
2013-05-31 14:18:52 +00:00
windows.insert(std::pair<int, MeterWindow*>(GetLoadOrder((*iter).first), mw));
}
}
}
/*
** Returns the skin folder path relative to the skin folder (e.g. illustro\Clock).
**
*/
2013-05-31 14:18:52 +00:00
std::wstring Rainmeter::GetFolderPath(int folderIndex)
{
const SkinFolder& skinFolder = m_SkinFolders[folderIndex];
std::wstring path = skinFolder.name;
for (int i = skinFolder.level - 1, index = folderIndex; i >= 1; --i)
{
while (m_SkinFolders[index].level != i)
{
--index;
}
path.insert(0, L"\\");
path.insert(0, m_SkinFolders[index].name);
}
return path;
}
2013-05-31 14:18:52 +00:00
int Rainmeter::FindSkinFolderIndex(const std::wstring& folderPath)
{
if (!folderPath.empty())
{
const WCHAR* path = folderPath.c_str();
int len = 0;
while (path[len] && path[len] != L'\\') ++len;
int level = 1;
for (int i = 0, isize = (int)m_SkinFolders.size(); i < isize; ++i)
{
const SkinFolder& skinFolder = m_SkinFolders[i];
if (skinFolder.level == level)
{
if (skinFolder.name.length() == len && _wcsnicmp(skinFolder.name.c_str(), path, len) == 0)
{
path += len;
if (*path)
{
++path; // Skip backslash
len = 0;
while (path[len] && path[len] != L'\\') ++len;
}
else
{
// Match found
return i;
}
++level;
}
}
else if (skinFolder.level < level)
{
break;
}
}
}
return -1;
}
2013-05-31 14:18:52 +00:00
void Rainmeter::SetLoadOrder(int folderIndex, int order)
{
std::multimap<int, int>::iterator iter = m_SkinOrders.begin();
for ( ; iter != m_SkinOrders.end(); ++iter)
{
if ((*iter).second == folderIndex) // already exists
{
if ((*iter).first != order)
{
m_SkinOrders.erase(iter);
break;
}
else
{
return;
}
}
}
m_SkinOrders.insert(std::pair<int, int>(order, folderIndex));
}
2013-05-31 14:18:52 +00:00
int Rainmeter::GetLoadOrder(const std::wstring& folderPath)
{
int index = FindSkinFolderIndex(folderPath);
if (index != -1)
{
std::multimap<int, int>::const_iterator iter = m_SkinOrders.begin();
for ( ; iter != m_SkinOrders.end(); ++iter)
{
if ((*iter).second == index)
{
return (*iter).first;
}
}
}
// LoadOrder not specified
return 0;
}
2011-03-29 19:21:57 +00:00
/*
2009-02-10 18:37:48 +00:00
** Scans all the subfolders and locates the ini-files.
*/
2013-05-31 14:18:52 +00:00
void Rainmeter::ScanForSkins()
2009-02-10 18:37:48 +00:00
{
m_SkinFolders.clear();
m_SkinOrders.clear();
2009-02-10 18:37:48 +00:00
2012-06-10 09:17:47 +00:00
ScanForSkinsRecursive(m_SkinPath, L"", 0, 0);
2009-02-10 18:37:48 +00:00
}
2013-05-31 14:18:52 +00:00
int Rainmeter::ScanForSkinsRecursive(const std::wstring& path, std::wstring base, int index, UINT level)
2009-02-10 18:37:48 +00:00
{
2011-03-29 19:21:57 +00:00
WIN32_FIND_DATA fileData; // Data structure describes the file found
HANDLE hSearch; // Search handle returned by FindFirstFile
std::list<std::wstring> subfolders;
2009-02-10 18:37:48 +00:00
// Find all .ini files and subfolders
2011-07-15 11:48:50 +00:00
std::wstring filter = path + base;
filter += L"\\*";
hSearch = FindFirstFileEx(
filter.c_str(),
(Platform::IsAtLeastWin7()) ? FindExInfoBasic : FindExInfoStandard,
2011-07-15 11:48:50 +00:00
&fileData,
FindExSearchNameMatch,
2013-05-31 14:28:39 +00:00
nullptr,
2011-07-15 11:48:50 +00:00
0);
2012-06-01 10:47:35 +00:00
bool foundFiles = false;
2011-07-15 11:48:50 +00:00
if (hSearch != INVALID_HANDLE_VALUE)
2009-02-10 18:37:48 +00:00
{
SkinFolder skinFolder;
skinFolder.commandBase = ID_CONFIG_FIRST + index;
skinFolder.active = 0;
skinFolder.level = level;
2009-02-10 18:37:48 +00:00
do
{
2011-11-19 23:09:41 +00:00
const std::wstring filename = fileData.cFileName;
2011-07-15 11:48:50 +00:00
if (fileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
if (!PathUtil::IsDotOrDotDot(fileData.cFileName) &&
!(level == 0 && wcscmp(L"@Backup", fileData.cFileName) == 0) &&
2012-06-10 09:33:59 +00:00
!(level == 0 && wcscmp(L"Backup", fileData.cFileName) == 0) &&
!(level == 1 && wcscmp(L"@Resources", fileData.cFileName) == 0))
2011-07-15 11:48:50 +00:00
{
subfolders.push_back(filename);
2011-07-15 11:48:50 +00:00
}
}
else if (level != 0)
{
2011-07-15 11:48:50 +00:00
// Check whether the extension is ".ini"
2011-11-19 23:09:41 +00:00
size_t filenameLen = filename.size();
2011-07-15 11:48:50 +00:00
if (filenameLen >= 4 && _wcsicmp(fileData.cFileName + (filenameLen - 4), L".ini") == 0)
{
foundFiles = true;
skinFolder.files.push_back(filename);
2011-10-29 10:36:07 +00:00
++index;
2011-07-15 11:48:50 +00:00
}
}
}
while (FindNextFile(hSearch, &fileData));
2011-07-15 11:48:50 +00:00
FindClose(hSearch);
2009-02-10 18:37:48 +00:00
if (level > 0 && (foundFiles || !subfolders.empty()))
2009-02-10 18:37:48 +00:00
{
if (level == 1)
{
skinFolder.name = base;
}
else
{
std::wstring::size_type pos = base.rfind(L'\\') + 1;
skinFolder.name.assign(base, pos, base.length() - pos);
}
m_SkinFolders.push_back(std::move(skinFolder));
2009-02-10 18:37:48 +00:00
}
2011-07-15 11:48:50 +00:00
}
2009-02-10 18:37:48 +00:00
if (level != 0)
2011-07-15 11:48:50 +00:00
{
2011-12-04 22:18:40 +00:00
base += L'\\';
2009-02-10 18:37:48 +00:00
}
if (!subfolders.empty())
2009-02-10 18:37:48 +00:00
{
2012-06-01 10:47:35 +00:00
bool popFolder = !foundFiles;
std::list<std::wstring>::const_iterator iter = subfolders.begin();
for ( ; iter != subfolders.end(); ++iter)
{
int newIndex = ScanForSkinsRecursive(path, base + (*iter), index, level + 1);
if (newIndex != index)
{
2012-06-01 10:47:35 +00:00
popFolder = false;
}
2009-02-10 18:37:48 +00:00
index = newIndex;
}
2009-02-10 18:37:48 +00:00
2012-06-01 10:47:35 +00:00
if (popFolder)
2012-05-20 13:12:13 +00:00
{
m_SkinFolders.pop_back();
2009-02-10 18:37:48 +00:00
}
2011-07-15 11:48:50 +00:00
}
2009-02-10 18:37:48 +00:00
return index;
}
2011-03-29 19:21:57 +00:00
/*
2012-10-02 14:44:47 +00:00
** Scans the given folder for layouts
*/
2013-05-31 14:18:52 +00:00
void Rainmeter::ScanForLayouts()
{
2012-10-02 14:44:47 +00:00
m_Layouts.clear();
WIN32_FIND_DATA fileData; // Data structure describes the file found
2011-03-29 19:21:57 +00:00
HANDLE hSearch; // Search handle returned by FindFirstFile
// Scan for folders
2012-10-02 14:44:47 +00:00
std::wstring folders = GetLayoutPath();
2012-06-10 09:17:47 +00:00
folders += L'*';
2011-07-15 11:48:50 +00:00
hSearch = FindFirstFileEx(
folders.c_str(),
(Platform::IsAtLeastWin7()) ? FindExInfoBasic : FindExInfoStandard,
2011-07-15 11:48:50 +00:00
&fileData,
FindExSearchNameMatch,
2013-05-31 14:28:39 +00:00
nullptr,
2011-07-15 11:48:50 +00:00
0);
if (hSearch != INVALID_HANDLE_VALUE)
{
do
{
2011-07-15 11:48:50 +00:00
if (fileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY &&
!PathUtil::IsDotOrDotDot(fileData.cFileName))
2011-07-15 11:48:50 +00:00
{
2012-10-02 14:44:47 +00:00
m_Layouts.push_back(fileData.cFileName);
2011-07-15 11:48:50 +00:00
}
}
while (FindNextFile(hSearch, &fileData));
2011-07-15 11:48:50 +00:00
FindClose(hSearch);
}
}
2013-05-31 14:18:52 +00:00
void Rainmeter::ExecuteBang(const WCHAR* bang, std::vector<std::wstring>& args, MeterWindow* meterWindow)
2009-02-10 18:37:48 +00:00
{
m_CommandHandler.ExecuteBang(bang, args, meterWindow);
2012-05-06 19:27:59 +00:00
}
/*
** Runs the given command or bang
**
*/
2013-05-31 14:18:52 +00:00
void Rainmeter::ExecuteCommand(const WCHAR* command, MeterWindow* meterWindow, bool multi)
2012-05-06 19:27:59 +00:00
{
m_CommandHandler.ExecuteCommand(command, meterWindow, multi);
2009-02-10 18:37:48 +00:00
}
/*
** Executes command when current processing is done.
**
*/
2013-05-31 14:18:52 +00:00
void Rainmeter::DelayedExecuteCommand(const WCHAR* command)
{
WCHAR* bang = _wcsdup(command);
2013-05-31 14:28:39 +00:00
PostMessage(m_Window, WM_RAINMETER_DELAYED_EXECUTE, (WPARAM)nullptr, (LPARAM)bang);
}
2009-02-10 18:37:48 +00:00
/*
** Reads the general settings from the Rainmeter.ini file
**
*/
2013-05-31 14:18:52 +00:00
void Rainmeter::ReadGeneralSettings(const std::wstring& iniFile)
2009-02-10 18:37:48 +00:00
{
2010-12-21 04:49:01 +00:00
WCHAR buffer[MAX_PATH];
// Clear old settings
m_DesktopWorkAreas.clear();
2013-05-31 14:18:52 +00:00
ConfigParser parser;
2013-05-31 14:28:39 +00:00
parser.Initialize(iniFile, nullptr, nullptr);
2009-02-10 18:37:48 +00:00
m_UseD2D = 0!=parser.ReadInt(L"Rainmeter", L"UseD2D", 0);
m_Debug = 0!=parser.ReadInt(L"Rainmeter", L"Debug", 0);
// Read Logging settings
2013-06-13 14:22:42 +00:00
Logger& logger = GetLogger();
const bool logging = parser.ReadInt(L"Rainmeter", L"Logging", 0) != 0;
logger.SetLogToFile(logging);
if (logging)
{
logger.StartLogFile();
}
2009-02-10 18:37:48 +00:00
if (m_TrayWindow)
{
2012-05-30 18:53:44 +00:00
m_TrayWindow->ReadOptions(parser);
2009-02-10 18:37:48 +00:00
}
m_GlobalOptions.netInSpeed = parser.ReadFloat(L"Rainmeter", L"NetInSpeed", 0.0);
m_GlobalOptions.netOutSpeed = parser.ReadFloat(L"Rainmeter", L"NetOutSpeed", 0.0);
2009-02-10 18:37:48 +00:00
m_DisableDragging = 0!=parser.ReadInt(L"Rainmeter", L"DisableDragging", 0);
2010-11-25 22:00:34 +00:00
m_DisableRDP = 0!=parser.ReadInt(L"Rainmeter", L"DisableRDP", 0);
m_SkinEditor = parser.ReadString(L"Rainmeter", L"ConfigEditor", L"");
if (m_SkinEditor.empty())
{
// Get the program path associated with .ini files
DWORD cchOut = MAX_PATH;
HRESULT hr = AssocQueryString(ASSOCF_NOTRUNCATE, ASSOCSTR_EXECUTABLE, L".ini", L"open", buffer, &cchOut);
m_SkinEditor = (SUCCEEDED(hr) && cchOut > 0) ? buffer : L"Notepad";
}
if (m_Debug)
{
2013-05-30 14:19:42 +00:00
LogNoticeF(L"ConfigEditor: %s", m_SkinEditor.c_str());
}
m_TrayExecuteR = parser.ReadString(L"Rainmeter", L"TrayExecuteR", L"", false);
m_TrayExecuteM = parser.ReadString(L"Rainmeter", L"TrayExecuteM", L"", false);
m_TrayExecuteDR = parser.ReadString(L"Rainmeter", L"TrayExecuteDR", L"", false);
m_TrayExecuteDM = parser.ReadString(L"Rainmeter", L"TrayExecuteDM", L"", false);
m_DisableVersionCheck = 0!=parser.ReadInt(L"Rainmeter", L"DisableVersionCheck", 0);
2011-11-16 16:47:20 +00:00
const std::wstring& area = parser.ReadString(L"Rainmeter", L"DesktopWorkArea", L"");
2009-02-10 18:37:48 +00:00
if (!area.empty())
{
m_DesktopWorkAreas[0] = parser.ParseRECT(area.c_str());
2009-02-10 18:37:48 +00:00
m_DesktopWorkAreaChanged = true;
}
2013-05-31 14:18:52 +00:00
for (UINT i = 1, isize = System::GetMonitorCount(); i <= isize; ++i)
{
_snwprintf_s(buffer, _TRUNCATE, L"DesktopWorkArea@%i", i);
2011-11-16 16:47:20 +00:00
const std::wstring& area = parser.ReadString(L"Rainmeter", buffer, L"");
if (!area.empty())
{
m_DesktopWorkAreas[i] = parser.ParseRECT(area.c_str());
m_DesktopWorkAreaChanged = true;
}
}
m_DesktopWorkAreaType = 0!=parser.ReadInt(L"Rainmeter", L"DesktopWorkAreaType", 0);
m_NormalStayDesktop = 0!=parser.ReadInt(L"Rainmeter", L"NormalStayDesktop", 1);
for (auto iter = parser.GetSections().cbegin(); iter != parser.GetSections().end(); ++iter)
2009-02-10 18:37:48 +00:00
{
const WCHAR* section = (*iter).c_str();
if (wcscmp(section, L"Rainmeter") == 0 ||
wcscmp(section, L"TrayMeasure") == 0)
{
continue;
}
int index = FindSkinFolderIndex(*iter);
if (index == -1)
{
continue;
}
SkinFolder& skinFolder = m_SkinFolders[index];
2009-02-10 18:37:48 +00:00
// Make sure there is a ini file available
int active = parser.ReadInt(section, L"Active", 0);
if (active > 0 && active <= (int)skinFolder.files.size())
2009-02-10 18:37:48 +00:00
{
skinFolder.active = active;
2009-02-10 18:37:48 +00:00
}
int order = parser.ReadInt(section, L"LoadOrder", 0);
SetLoadOrder(index, order);
2009-02-10 18:37:48 +00:00
}
}
2011-03-29 19:21:57 +00:00
/*
** Refreshes all active meter windows.
2013-05-31 14:18:52 +00:00
** Note: This function calls MeterWindow::Refresh() directly for synchronization. Be careful about crash.
2009-02-10 18:37:48 +00:00
**
*/
2013-05-31 14:18:52 +00:00
void Rainmeter::RefreshAll()
{
// Read skins and settings
ReloadSettings();
// Change the work area if necessary
if (m_DesktopWorkAreaChanged)
{
UpdateDesktopWorkArea(false);
}
// Make the sending order by using LoadOrder
2013-05-31 14:18:52 +00:00
std::multimap<int, MeterWindow*> windows;
GetMeterWindowsByLoadOrder(windows);
// Prepare the helper window
2013-05-31 14:18:52 +00:00
System::PrepareHelperWindow();
// Refresh all
2013-05-31 14:18:52 +00:00
std::multimap<int, MeterWindow*>::const_iterator iter = windows.begin();
for ( ; iter != windows.end(); ++iter)
{
2013-05-31 14:18:52 +00:00
MeterWindow* mw = (*iter).second;
if (mw)
{
// Verify whether the cached information is valid
int index = FindSkinFolderIndex(mw->GetFolderPath());
if (index != -1)
{
SkinFolder& skinFolder = m_SkinFolders[index];
const WCHAR* skinIniFile = mw->GetFileName().c_str();
bool found = false;
for (int i = 0, isize = (int)skinFolder.files.size(); i < isize; ++i)
{
if (_wcsicmp(skinIniFile, skinFolder.files[i].c_str()) == 0)
{
found = true;
if (skinFolder.active != i + 1)
{
// Switch to new ini-file order
skinFolder.active = i + 1;
WriteActive(mw->GetFolderPath(), i);
}
break;
}
}
if (!found)
{
const WCHAR* skinFolderPath = mw->GetFolderPath().c_str();
std::wstring error = GetFormattedString(ID_STR_UNABLETOREFRESHSKIN, skinFolderPath, skinIniFile);
DeactivateSkin(mw, index);
2013-05-31 14:28:39 +00:00
ShowMessage(nullptr, error.c_str(), MB_OK | MB_ICONEXCLAMATION);
continue;
}
}
else
{
const WCHAR* skinFolderPath = mw->GetFolderPath().c_str();
std::wstring error = GetFormattedString(ID_STR_UNABLETOREFRESHSKIN, skinFolderPath, L"");
DeactivateSkin(mw, -2); // -2 = Force deactivate
2013-05-31 14:28:39 +00:00
ShowMessage(nullptr, error.c_str(), MB_OK | MB_ICONEXCLAMATION);
continue;
}
2012-08-12 14:02:08 +00:00
mw->Refresh(false, true);
}
}
2013-05-31 14:18:52 +00:00
DialogAbout::UpdateSkins();
2013-05-31 14:28:39 +00:00
DialogManage::UpdateSkins(nullptr);
}
2009-02-10 18:37:48 +00:00
2013-05-31 14:18:52 +00:00
bool Rainmeter::LoadLayout(const std::wstring& name)
{
2012-10-06 17:12:40 +00:00
// Replace Rainmeter.ini with layout
std::wstring layout = GetLayoutPath();
layout += name;
std::wstring wallpaper = layout + L"\\Wallpaper.bmp";
layout += L"\\Rainmeter.ini";
if (_waccess(layout.c_str(), 0) == -1)
{
return false;
}
DeleteAllUnmanagedMeterWindows();
DeleteAllMeterWindows();
2012-10-02 14:44:47 +00:00
std::wstring backup = GetLayoutPath();
backup += L"@Backup";
2013-05-31 14:28:39 +00:00
CreateDirectory(backup.c_str(), nullptr);
2012-10-02 14:44:47 +00:00
backup += L"\\Rainmeter.ini";
2012-10-06 17:12:40 +00:00
bool backupLayout = (_wcsicmp(name.c_str(), L"@Backup") == 0);
if (!backupLayout)
{
// Make a copy of current Rainmeter.ini
2013-05-31 14:18:52 +00:00
System::CopyFiles(m_IniFile, backup);
2012-10-06 17:12:40 +00:00
}
2013-05-31 14:18:52 +00:00
System::CopyFiles(layout, m_IniFile);
2012-10-06 17:12:40 +00:00
if (!backupLayout)
{
PreserveSetting(backup, L"SkinPath");
PreserveSetting(backup, L"ConfigEditor");
PreserveSetting(backup, L"LogViewer");
PreserveSetting(backup, L"Logging");
PreserveSetting(backup, L"DisableVersionCheck");
2011-10-01 17:55:33 +00:00
PreserveSetting(backup, L"Language");
2011-11-16 16:47:20 +00:00
PreserveSetting(backup, L"NormalStayDesktop");
PreserveSetting(backup, L"TrayExecuteM", false);
PreserveSetting(backup, L"TrayExecuteR", false);
PreserveSetting(backup, L"TrayExecuteDM", false);
PreserveSetting(backup, L"TrayExecuteDR", false);
2013-03-31 16:33:36 +00:00
PreserveSetting(backup, L"UseD2D");
// Set wallpaper if it exists
if (_waccess(wallpaper.c_str(), 0) != -1)
{
SystemParametersInfo(SPI_SETDESKWALLPAPER, 0, (void*)wallpaper.c_str(), SPIF_UPDATEINIFILE);
}
}
ReloadSettings();
// Create meter windows for active skins
ActivateActiveSkins();
2012-10-06 17:12:40 +00:00
return true;
}
2013-05-31 14:18:52 +00:00
void Rainmeter::PreserveSetting(const std::wstring& from, LPCTSTR key, bool replace)
{
WCHAR* buffer = new WCHAR[MAX_LINE_LENGTH];
if ((replace || GetPrivateProfileString(L"Rainmeter", key, L"", buffer, 4, m_IniFile.c_str()) == 0) &&
GetPrivateProfileString(L"Rainmeter", key, L"", buffer, MAX_LINE_LENGTH, from.c_str()) > 0)
{
WritePrivateProfileString(L"Rainmeter", key, buffer, m_IniFile.c_str());
}
delete [] buffer;
}
2011-03-29 19:21:57 +00:00
/*
** Applies given DesktopWorkArea and DesktopWorkArea@n.
**
*/
2013-05-31 14:18:52 +00:00
void Rainmeter::UpdateDesktopWorkArea(bool reset)
{
bool changed = false;
if (reset)
{
if (!m_OldDesktopWorkAreas.empty())
{
int i = 1;
for (auto iter = m_OldDesktopWorkAreas.cbegin(); iter != m_OldDesktopWorkAreas.cend(); ++iter, ++i)
{
RECT r = (*iter);
BOOL result = SystemParametersInfo(SPI_SETWORKAREA, 0, &r, 0);
if (m_Debug)
{
std::wstring format = L"Resetting WorkArea@%i: L=%i, T=%i, R=%i, B=%i (W=%i, H=%i)";
if (!result)
{
format += L" => FAIL";
}
2013-05-30 14:19:42 +00:00
LogDebugF(format.c_str(), i, r.left, r.top, r.right, r.bottom, r.right - r.left, r.bottom - r.top);
}
}
changed = true;
}
}
else
{
2013-05-31 14:18:52 +00:00
const size_t numOfMonitors = System::GetMonitorCount();
const MultiMonitorInfo& monitorsInfo = System::GetMultiMonitorInfo();
const std::vector<MonitorInfo>& monitors = monitorsInfo.monitors;
if (m_OldDesktopWorkAreas.empty())
{
// Store old work areas for changing them back
for (size_t i = 0; i < numOfMonitors; ++i)
{
m_OldDesktopWorkAreas.push_back(monitors[i].work);
}
}
if (m_Debug)
{
2013-05-30 14:19:42 +00:00
LogDebugF(L"DesktopWorkAreaType: %s", m_DesktopWorkAreaType ? L"Margin" : L"Default");
}
for (UINT i = 0; i <= numOfMonitors; ++i)
{
std::map<UINT, RECT>::const_iterator it = m_DesktopWorkAreas.find(i);
if (it != m_DesktopWorkAreas.end())
{
RECT r = (*it).second;
// Move rect to correct offset
if (m_DesktopWorkAreaType)
{
RECT margin = r;
r = (i == 0) ? monitors[monitorsInfo.primary - 1].screen : monitors[i - 1].screen;
r.left += margin.left;
r.top += margin.top;
r.right -= margin.right;
r.bottom -= margin.bottom;
}
else
{
if (i != 0)
{
const RECT screenRect = monitors[i - 1].screen;
r.left += screenRect.left;
r.top += screenRect.top;
r.right += screenRect.left;
r.bottom += screenRect.top;
}
}
BOOL result = SystemParametersInfo(SPI_SETWORKAREA, 0, &r, 0);
if (result)
{
changed = true;
}
if (m_Debug)
{
std::wstring format = L"Applying DesktopWorkArea";
if (i != 0)
{
2010-09-13 20:06:52 +00:00
WCHAR buffer[64];
2011-12-09 19:49:06 +00:00
size_t len = _snwprintf_s(buffer, _TRUNCATE, L"@%i", i);
format.append(buffer, len);
}
format += L": L=%i, T=%i, R=%i, B=%i (W=%i, H=%i)";
if (!result)
{
format += L" => FAIL";
}
2013-05-30 14:19:42 +00:00
LogDebugF(format.c_str(), r.left, r.top, r.right, r.bottom, r.right - r.left, r.bottom - r.top);
}
}
}
}
2013-05-31 14:18:52 +00:00
if (changed && System::GetWindow())
{
2013-05-31 14:18:52 +00:00
// Update System::MultiMonitorInfo for for work area variables
2013-05-31 14:28:39 +00:00
SendMessageTimeout(System::GetWindow(), WM_SETTINGCHANGE, SPI_SETWORKAREA, 0, SMTO_ABORTIFHUNG, 1000, nullptr);
}
}
2009-02-10 18:37:48 +00:00
/*
** Reads the statistics from the ini-file
**
*/
2013-05-31 14:18:52 +00:00
void Rainmeter::ReadStats()
2009-02-10 18:37:48 +00:00
{
const WCHAR* statsFile = m_StatsFile.c_str();
// If m_StatsFile doesn't exist, create it and copy the stats section from m_IniFile
if (_waccess(statsFile, 0) == -1)
{
const WCHAR* iniFile = m_IniFile.c_str();
WCHAR* tmpSz = new WCHAR[SHRT_MAX]; // Max size returned by GetPrivateProfileSection()
if (GetPrivateProfileSection(L"Statistics", tmpSz, SHRT_MAX, iniFile) > 0)
{
2013-05-31 14:28:39 +00:00
WritePrivateProfileString(L"Statistics", nullptr, nullptr, iniFile);
}
else
{
tmpSz[0] = tmpSz[1] = L'\0';
}
WritePrivateProfileSection(L"Statistics", tmpSz, statsFile);
delete [] tmpSz;
}
2009-02-10 18:37:48 +00:00
// Only Net measure has stats at the moment
2013-05-31 14:18:52 +00:00
MeasureNet::ReadStats(m_StatsFile, m_StatsDate);
2009-02-10 18:37:48 +00:00
}
/*
** Writes the statistics to the ini-file. If bForce is false the stats are written only once per an appropriate interval.
2009-02-10 18:37:48 +00:00
**
*/
2013-05-31 14:18:52 +00:00
void Rainmeter::WriteStats(bool bForce)
2009-02-10 18:37:48 +00:00
{
static ULONGLONG lastWrite = 0;
2009-02-10 18:37:48 +00:00
2013-05-31 14:18:52 +00:00
ULONGLONG ticks = System::GetTickCount64();
if (bForce || (lastWrite + INTERVAL_NETSTATS < ticks))
{
lastWrite = ticks;
// Only Net measure has stats at the moment
const WCHAR* statsFile = m_StatsFile.c_str();
2013-05-31 14:18:52 +00:00
MeasureNet::WriteStats(statsFile, m_StatsDate);
2013-05-31 14:28:39 +00:00
WritePrivateProfileString(nullptr, nullptr, nullptr, statsFile);
}
2009-02-10 18:37:48 +00:00
}
/*
** Clears the statistics
**
*/
2013-05-31 14:18:52 +00:00
void Rainmeter::ResetStats()
2009-02-10 18:37:48 +00:00
{
// Set the stats-date string
2012-06-01 13:13:01 +00:00
tm* newtime;
2011-03-29 19:21:57 +00:00
time_t long_time;
time(&long_time);
newtime = localtime(&long_time);
2009-02-10 18:37:48 +00:00
m_StatsDate = _wasctime(newtime);
2011-07-14 00:26:53 +00:00
m_StatsDate.erase(m_StatsDate.size() - 1);
2011-03-29 19:21:57 +00:00
2009-02-10 18:37:48 +00:00
// Only Net measure has stats at the moment
2013-05-31 14:18:52 +00:00
MeasureNet::ResetStats();
2009-02-10 18:37:48 +00:00
}
/*
** Wraps MessageBox(). Sets RTL flag if necessary.
**
*/
2013-05-31 14:18:52 +00:00
int Rainmeter::ShowMessage(HWND parent, const WCHAR* text, UINT type)
{
type |= MB_TOPMOST;
if (*GetString(ID_STR_ISRTL) == L'1')
{
type |= MB_RTLREADING;
}
return MessageBox(parent, text, APPNAME, type);
};
2009-02-10 18:37:48 +00:00
/*
** Opens the context menu in given coordinates.
**
*/
2013-05-31 14:18:52 +00:00
void Rainmeter::ShowContextMenu(POINT pos, MeterWindow* meterWindow)
2009-02-10 18:37:48 +00:00
{
static const MenuTemplate s_Menu[] =
{
MENU_ITEM(IDM_MANAGE, ID_STR_MANAGE),
MENU_ITEM(IDM_ABOUT, ID_STR_ABOUT),
MENU_ITEM(IDM_SHOW_HELP, ID_STR_HELP),
MENU_SEPARATOR(),
MENU_SUBMENU(ID_STR_SKINS,
2012-12-07 17:42:31 +00:00
MENU_ITEM_GRAYED(0, ID_STR_NOSKINS),
MENU_SEPARATOR(),
2012-12-07 17:42:31 +00:00
MENU_ITEM(IDM_OPENSKINSFOLDER, ID_STR_OPENFOLDER),
MENU_ITEM(IDM_DISABLEDRAG, ID_STR_DISABLEDRAGGING)),
MENU_SUBMENU(ID_STR_THEMES,
2012-12-07 17:42:31 +00:00
MENU_ITEM_GRAYED(0, ID_STR_NOTHEMES)),
MENU_SEPARATOR(),
MENU_ITEM(IDM_EDITCONFIG, ID_STR_EDITSETTINGS),
MENU_ITEM(IDM_REFRESH, ID_STR_REFRESHALL),
MENU_SEPARATOR(),
MENU_SUBMENU(ID_STR_LOGGING,
MENU_ITEM(IDM_SHOWLOGFILE, ID_STR_SHOWLOGFILE),
MENU_SEPARATOR(),
MENU_ITEM(IDM_STARTLOG, ID_STR_STARTLOGGING),
MENU_ITEM(IDM_STOPLOG, ID_STR_STOPLOGGING),
MENU_SEPARATOR(),
MENU_ITEM(IDM_DELETELOGFILE, ID_STR_DELETELOGFILE),
MENU_ITEM(IDM_DEBUGLOG, ID_STR_DEBUGMODE)),
MENU_SEPARATOR(),
MENU_ITEM(IDM_QUIT, ID_STR_EXIT)
};
2013-03-10 19:59:05 +00:00
if (!m_MenuActive && (!meterWindow || !meterWindow->IsClosing()))
2009-02-10 18:37:48 +00:00
{
m_MenuActive = true;
// Show context menu, if no actions were executed
HMENU menu = MenuTemplate::CreateMenu(s_Menu, _countof(s_Menu), GetString);
2011-03-29 19:21:57 +00:00
if (menu)
2009-02-10 18:37:48 +00:00
{
SetMenuDefaultItem(menu, IDM_MANAGE, MF_BYCOMMAND);
2013-06-13 14:22:42 +00:00
if (_waccess(GetLogger().GetLogFilePath().c_str(), 0) == -1)
{
EnableMenuItem(menu, IDM_SHOWLOGFILE, MF_BYCOMMAND | MF_GRAYED);
EnableMenuItem(menu, IDM_DELETELOGFILE, MF_BYCOMMAND | MF_GRAYED);
EnableMenuItem(menu, IDM_STOPLOG, MF_BYCOMMAND | MF_GRAYED);
}
else
{
EnableMenuItem(
menu,
2013-06-13 14:22:42 +00:00
(GetLogger().IsLogToFile()) ? IDM_STARTLOG : IDM_STOPLOG,
MF_BYCOMMAND | MF_GRAYED);
}
if (m_Debug)
{
CheckMenuItem(menu, IDM_DEBUGLOG, MF_BYCOMMAND | MF_CHECKED);
}
HMENU allSkinsMenu = GetSubMenu(menu, 4);
if (allSkinsMenu)
{
if (!m_SkinFolders.empty())
{
DeleteMenu(allSkinsMenu, 0, MF_BYPOSITION); // "No skins available" menuitem
CreateAllSkinsMenu(allSkinsMenu);
}
if (m_DisableDragging)
{
CheckMenuItem(allSkinsMenu, IDM_DISABLEDRAG, MF_BYCOMMAND | MF_CHECKED);
}
}
2009-02-10 18:37:48 +00:00
HMENU layoutMenu = GetSubMenu(menu, 5);
if (layoutMenu)
{
if (!m_Layouts.empty())
2009-02-10 18:37:48 +00:00
{
DeleteMenu(layoutMenu, 0, MF_BYPOSITION); // "No layouts available" menuitem
CreateLayoutMenu(layoutMenu);
2009-02-10 18:37:48 +00:00
}
}
if (meterWindow)
{
HMENU rainmeterMenu = menu;
menu = CreateSkinMenu(meterWindow, 0, allSkinsMenu);
2009-02-10 18:37:48 +00:00
2012-12-02 20:41:42 +00:00
InsertMenu(menu, IDM_CLOSESKIN, MF_BYCOMMAND | MF_POPUP, (UINT_PTR)rainmeterMenu, L"Rainmeter");
2013-05-31 14:28:39 +00:00
InsertMenu(menu, IDM_CLOSESKIN, MF_BYCOMMAND | MF_SEPARATOR, 0, nullptr);
}
else
{
2013-05-31 14:28:39 +00:00
InsertMenu(menu, 12, MF_BYPOSITION | MF_SEPARATOR, 0, nullptr);
// Create a menu for all active skins
int index = 0;
2013-05-31 14:18:52 +00:00
std::map<std::wstring, MeterWindow*>::const_iterator iter = m_MeterWindows.begin();
for (; iter != m_MeterWindows.end(); ++iter)
{
2013-05-31 14:18:52 +00:00
MeterWindow* mw = ((*iter).second);
HMENU skinMenu = CreateSkinMenu(mw, index, allSkinsMenu);
InsertMenu(menu, 12, MF_BYPOSITION | MF_POPUP, (UINT_PTR)skinMenu, mw->GetFolderPath().c_str());
++index;
}
// Add update notification item
if (m_NewVersion)
{
InsertMenu(menu, 0, MF_BYPOSITION, IDM_NEW_VERSION, GetString(ID_STR_UPDATEAVAILABLE));
HiliteMenuItem(GetTrayWindow()->GetWindow(), menu, 0, MF_BYPOSITION | MF_HILITE);
2013-05-31 14:28:39 +00:00
InsertMenu(menu, 1, MF_BYPOSITION | MF_SEPARATOR, 0, nullptr);
}
}
HWND hWnd = WindowFromPoint(pos);
2013-05-31 14:28:39 +00:00
if (hWnd != nullptr)
{
2013-05-31 14:18:52 +00:00
MeterWindow* 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)
{
2013-05-31 14:28:39 +00:00
DWORD foregroundThreadID = GetWindowThreadProcessId(hWndForeground, nullptr);
DWORD currentThreadID = GetCurrentThreadId();
AttachThreadInput(currentThreadID, foregroundThreadID, TRUE);
SetForegroundWindow(hWnd);
AttachThreadInput(currentThreadID, foregroundThreadID, FALSE);
}
// Show context menu
TrackPopupMenu(
menu,
TPM_RIGHTBUTTON | TPM_LEFTALIGN | (*GetString(ID_STR_ISRTL) == L'1' ? TPM_LAYOUTRTL : 0),
pos.x,
pos.y,
0,
hWnd,
2013-05-31 14:28:39 +00:00
nullptr);
if (meterWindow)
{
DestroyMenu(menu);
}
2009-02-10 18:37:48 +00:00
}
DestroyMenu(menu);
m_MenuActive = false;
2009-02-10 18:37:48 +00:00
}
}
2013-05-31 14:18:52 +00:00
int Rainmeter::CreateAllSkinsMenuRecursive(HMENU skinMenu, int index)
2009-02-10 18:37:48 +00:00
{
int initialLevel = m_SkinFolders[index].level;
int menuIndex = 0;
const size_t max = GetRainmeter().m_SkinFolders.size();
while (index < max)
2009-02-10 18:37:48 +00:00
{
const SkinFolder& skinFolder = GetRainmeter().m_SkinFolders[index];
if (skinFolder.level != initialLevel)
{
return index - 1;
}
HMENU subMenu = CreatePopupMenu();
// Add current folder
InsertMenu(skinMenu, menuIndex, MF_POPUP | MF_BYPOSITION, (UINT_PTR)subMenu, skinFolder.name.c_str());
// Add subfolders
const bool hasSubfolder = (index + 1) < max && m_SkinFolders[index + 1].level == initialLevel + 1;
if (hasSubfolder)
{
index = CreateAllSkinsMenuRecursive(subMenu, index + 1);
}
2009-02-10 18:37:48 +00:00
// Add files
2009-02-10 18:37:48 +00:00
{
int fileIndex = 0;
int fileCount = (int)skinFolder.files.size();
for ( ; fileIndex < fileCount; ++fileIndex)
2009-02-10 18:37:48 +00:00
{
InsertMenu(subMenu, fileIndex, MF_STRING | MF_BYPOSITION, skinFolder.commandBase + fileIndex, skinFolder.files[fileIndex].c_str());
}
if (skinFolder.active)
{
UINT checkPos = skinFolder.active - 1;
CheckMenuRadioItem(subMenu, checkPos, checkPos, checkPos, MF_BYPOSITION);
2009-02-10 18:37:48 +00:00
}
if (hasSubfolder && fileIndex != 0)
2009-02-10 18:37:48 +00:00
{
2013-05-31 14:28:39 +00:00
InsertMenu(subMenu, fileIndex, MF_SEPARATOR | MF_BYPOSITION, 0, nullptr);
2009-02-10 18:37:48 +00:00
}
}
++menuIndex;
++index;
2009-02-10 18:37:48 +00:00
}
return index;
}
2013-05-31 14:18:52 +00:00
HMENU Rainmeter::CreateSkinMenu(MeterWindow* meterWindow, int index, HMENU menu)
2009-02-10 18:37:48 +00:00
{
static const MenuTemplate s_Menu[] =
{
MENU_ITEM(IDM_SKIN_OPENSKINSFOLDER, 0),
MENU_SEPARATOR(),
MENU_SUBMENU(ID_STR_VARIANTS,
MENU_SEPARATOR()),
MENU_SEPARATOR(),
MENU_SUBMENU(ID_STR_SETTINGS,
MENU_SUBMENU(ID_STR_POSITION,
MENU_SUBMENU(ID_STR_DISPLAYMONITOR,
MENU_ITEM(IDM_SKIN_MONITOR_PRIMARY, ID_STR_USEDEFAULTMONITOR),
MENU_ITEM(ID_MONITOR_FIRST, ID_STR_VIRTUALSCREEN),
MENU_SEPARATOR(),
MENU_SEPARATOR(),
MENU_ITEM(IDM_SKIN_MONITOR_AUTOSELECT, ID_STR_AUTOSELECTMONITOR)),
MENU_SEPARATOR(),
MENU_ITEM(IDM_SKIN_VERYTOPMOST, ID_STR_STAYTOPMOST),
MENU_ITEM(IDM_SKIN_TOPMOST, ID_STR_TOPMOST),
MENU_ITEM(IDM_SKIN_NORMAL, ID_STR_NORMAL),
MENU_ITEM(IDM_SKIN_BOTTOM, ID_STR_BOTTOM),
MENU_ITEM(IDM_SKIN_ONDESKTOP, ID_STR_ONDESKTOP),
MENU_SEPARATOR(),
MENU_ITEM(IDM_SKIN_FROMRIGHT, ID_STR_FROMRIGHT),
MENU_ITEM(IDM_SKIN_FROMBOTTOM, ID_STR_FROMBOTTOM),
MENU_ITEM(IDM_SKIN_XPERCENTAGE, ID_STR_XASPERCENTAGE),
MENU_ITEM(IDM_SKIN_YPERCENTAGE, ID_STR_YASPERCENTAGE)),
MENU_SUBMENU(ID_STR_TRANSPARENCY,
MENU_ITEM(IDM_SKIN_TRANSPARENCY_0, ID_STR_0PERCENT),
MENU_ITEM(IDM_SKIN_TRANSPARENCY_10, ID_STR_10PERCENT),
MENU_ITEM(IDM_SKIN_TRANSPARENCY_20, ID_STR_20PERCENT),
MENU_ITEM(IDM_SKIN_TRANSPARENCY_30, ID_STR_30PERCENT),
MENU_ITEM(IDM_SKIN_TRANSPARENCY_40, ID_STR_40PERCENT),
MENU_ITEM(IDM_SKIN_TRANSPARENCY_50, ID_STR_50PERCENT),
MENU_ITEM(IDM_SKIN_TRANSPARENCY_60, ID_STR_60PERCENT),
MENU_ITEM(IDM_SKIN_TRANSPARENCY_70, ID_STR_70PERCENT),
MENU_ITEM(IDM_SKIN_TRANSPARENCY_80, ID_STR_80PERCENT),
MENU_ITEM(IDM_SKIN_TRANSPARENCY_90, ID_STR_90PERCENT),
MENU_SEPARATOR(),
MENU_ITEM(IDM_SKIN_TRANSPARENCY_FADEIN, ID_STR_FADEIN),
MENU_ITEM(IDM_SKIN_TRANSPARENCY_FADEOUT, ID_STR_FADEOUT)),
MENU_SEPARATOR(),
MENU_ITEM(IDM_SKIN_HIDEONMOUSE, ID_STR_HIDEONMOUSEOVER),
MENU_ITEM(IDM_SKIN_DRAGGABLE, ID_STR_DRAGGABLE),
MENU_ITEM(IDM_SKIN_REMEMBERPOSITION, ID_STR_SAVEPOSITION),
MENU_ITEM(IDM_SKIN_SNAPTOEDGES, ID_STR_SNAPTOEDGES),
MENU_ITEM(IDM_SKIN_CLICKTHROUGH, ID_STR_CLICKTHROUGH),
MENU_ITEM(IDM_SKIN_KEEPONSCREEN, ID_STR_KEEPONSCREEN)),
MENU_SEPARATOR(),
MENU_ITEM(IDM_SKIN_MANAGESKIN, ID_STR_MANAGESKIN),
MENU_ITEM(IDM_SKIN_EDITSKIN, ID_STR_EDITSKIN),
MENU_ITEM(IDM_SKIN_REFRESH, ID_STR_REFRESHSKIN),
MENU_SEPARATOR(),
MENU_ITEM(IDM_CLOSESKIN, ID_STR_UNLOADSKIN)
};
HMENU skinMenu = MenuTemplate::CreateMenu(s_Menu, _countof(s_Menu), GetString);
2009-02-10 18:37:48 +00:00
if (skinMenu)
{
// Tick the position
HMENU settingsMenu = GetSubMenu(skinMenu, 4);
if (settingsMenu)
2009-02-10 18:37:48 +00:00
{
HMENU posMenu = GetSubMenu(settingsMenu, 0);
if (posMenu)
2009-02-10 18:37:48 +00:00
{
UINT checkPos = IDM_SKIN_NORMAL - (UINT)meterWindow->GetWindowZPosition();
CheckMenuRadioItem(posMenu, checkPos, checkPos, checkPos, MF_BYCOMMAND);
2009-02-10 18:37:48 +00:00
if (meterWindow->GetXFromRight()) CheckMenuItem(posMenu, IDM_SKIN_FROMRIGHT, MF_BYCOMMAND | MF_CHECKED);
if (meterWindow->GetYFromBottom()) CheckMenuItem(posMenu, IDM_SKIN_FROMBOTTOM, MF_BYCOMMAND | MF_CHECKED);
if (meterWindow->GetXPercentage()) CheckMenuItem(posMenu, IDM_SKIN_XPERCENTAGE, MF_BYCOMMAND | MF_CHECKED);
if (meterWindow->GetYPercentage()) CheckMenuItem(posMenu, IDM_SKIN_YPERCENTAGE, MF_BYCOMMAND | MF_CHECKED);
HMENU monitorMenu = GetSubMenu(posMenu, 0);
if (monitorMenu)
{
CreateMonitorMenu(monitorMenu, meterWindow);
}
* Changed the way to get the information of the multiple display monitors. This change brings the order of monitors close to the order of "Display Properties" due to using EnumDisplayDevices and EnumDisplaySettings instead of EnumDisplayMonitors. (If EnumDisplayDevices failed, EnumDisplayMonitors is used as before.) ----- * Added the "Display Monitor" submenu in [Skins Menu]-[Position]. These menus convert the present position to the relative position from the specified monitor. (But the meter window doesn't move to the specified monitor area immediately. Only converts.) - "Use default: Primary monitor" removes the @-directive from WindowX/Y. - @0(@1, @2, ...) adds the specified monitor number to WindowX/Y. @0 means "The Virtual Screen". (http://msdn.microsoft.com/en-us/library/dd145136%28VS.85%29.aspx) - If "Auto-select based on window position" is checked, the WindowX and WindowY "@n" settings are made automatically based on the position of the meter's window. If a monitor is selected directly using "Display Monitor" in the Rainmeter / skin context menu, this menu is unchecked. This setting can be manually made in either the [Rainmeter] (all configs) or individual config sections of Rainmeter.ini. AutoSelectScreen If set to 1, the WindowX and WindowY "@n" settings are made automatically based on the position of the meter's window. If a monitor is selected directly using "Display Monitor" in the Rainmeter / skin context menu, this setting is reset to 0. ----- * Added the variables for multiple display monitors and the virtual screen. All X/Y positions are represented in the virtual screen coordinates. The following variables are for the virtual screen. #VSCREENAREAX# is the X-position of the left-side of the virtual screen. #VSCREENAREAY# is the Y-position of the top-side of the virtual screen. #VSCREENAREAWIDTH# is the width of the virtual screen. #VSCREENAREAHEIGHT# is the height of the virtual screen. The following variables are for the PRESENT monitor. Note that these variables automatically change by the WindowX and WindowY "@n" settings. If "@n" is not set, these variables return the value of the primary monitor. #WORKAREAX# is the X-position of the left-side of the work area. #WORKAREAY# is the Y-position of the top-side of the work area. #WORKAREAWIDTH# is the width of the work area. #WORKAREAHEIGHT# is the height of the work area. #SCREENAREAX# is the X-position of the left-side of the monitor screen. #SCREENAREAY# is the Y-position of the top-side of the monitor screen. #SCREENAREAWIDTH# is the width of the display resolution. #SCREENAREAHEIGHT# is the height of the display resolution. The following variables are for the PRIMARY monitor. #PWORKAREAX# is the X-position of the left-side of the work area. #PWORKAREAY# is the Y-position of the top-side of the work area. #PWORKAREAWIDTH# is the width of the work area. #PWORKAREAHEIGHT# is the height of the work area. #PSCREENAREAX# is the X-position of the left-side of the monitor screen. (maybe, always 0) #PSCREENAREAY# is the Y-position of the top-side of the monitor screen. (maybe, always 0) #PSCREENAREAWIDTH# is the width of the display resolution. #PSCREENAREAHEIGHT# is the height of the display resolution. The following variables are for the SPECIFIED monitor. (@n = @1, @2, ...) #WORKAREAX@n# is the X-position of the left-side of the work area. #WORKAREAY@n# is the Y-position of the top-side of the work area. #WORKAREAWIDTH@n# is the width of the work area. #WORKAREAHEIGHT@n# is the height of the work area. #SCREENAREAX@n# is the X-position of the left-side of the monitor screen. #SCREENAREAY@n# is the Y-position of the top-side of the monitor screen. #SCREENAREAWIDTH@n# is the width of the display resolution. #SCREENAREAHEIGHT@n# is the height of the display resolution. ----- * Other related changes: - Fixed the problem that the primary monitor isn't recognized correctly. - Fixed the problem that the information of the multiple display monitors is refreshed excessively. - For DynamicVariables, when display setting or workarea size has been changed, all variables are now updated to apply changed WORKAREA/SCREENAREA variables. - Fixed the problem that the "On Desktop" window isn't dragged correctly when the top-left corner of the virtual screen has negative coordinates. - Changed the way to stick the "On Desktop" window. ("SysListView32/FolderView" is used instead of "Progman/Program Manager".) ----- * Other changes: - When the meter window is draggable and isn't dragged, LeftMouseUpAction is now executed. - Added MouseDoubleClickAction (LeftMouseDoubleClickAction, RightMouseDoubleClickAction, MiddleMouseDoubleClickAction). If MouseDoubleClickAction is empty when mouse button is double-clicked, MouseDownAction is executed instead. - Fixed the problem that the Meter's hit-test code checks outside the area. - Changed the way to set the #CURRENTCONFIG#. (CMeterWindow::GetSkinName() is now used instead of parsing the path.)
2009-12-18 05:58:37 +00:00
}
2009-02-10 18:37:48 +00:00
// Tick the transparency
HMENU alphaMenu = GetSubMenu(settingsMenu, 1);
if (alphaMenu)
2009-02-10 18:37:48 +00:00
{
UINT checkPos = (UINT)(10 - meterWindow->GetAlphaValue() / 25.5);
checkPos = min(9, checkPos);
checkPos = max(0, checkPos);
CheckMenuRadioItem(alphaMenu, checkPos, checkPos, checkPos, MF_BYPOSITION);
switch (meterWindow->GetWindowHide())
2009-02-10 18:37:48 +00:00
{
case HIDEMODE_FADEIN:
CheckMenuItem(alphaMenu, IDM_SKIN_TRANSPARENCY_FADEIN, MF_BYCOMMAND | MF_CHECKED);
EnableMenuItem(alphaMenu, IDM_SKIN_TRANSPARENCY_FADEOUT, MF_BYCOMMAND | MF_GRAYED);
break;
case HIDEMODE_FADEOUT:
CheckMenuItem(alphaMenu, IDM_SKIN_TRANSPARENCY_FADEOUT, MF_BYCOMMAND | MF_CHECKED);
EnableMenuItem(alphaMenu, IDM_SKIN_TRANSPARENCY_FADEIN, MF_BYCOMMAND | MF_GRAYED);
break;
case HIDEMODE_HIDE:
EnableMenuItem(alphaMenu, IDM_SKIN_TRANSPARENCY_FADEIN, MF_BYCOMMAND | MF_GRAYED);
EnableMenuItem(alphaMenu, IDM_SKIN_TRANSPARENCY_FADEOUT, MF_BYCOMMAND | MF_GRAYED);
break;
2009-02-10 18:37:48 +00:00
}
}
// Tick the settings
switch (meterWindow->GetWindowHide())
{
case HIDEMODE_HIDE:
CheckMenuItem(settingsMenu, IDM_SKIN_HIDEONMOUSE, MF_BYCOMMAND | MF_CHECKED);
break;
case HIDEMODE_FADEIN:
case HIDEMODE_FADEOUT:
EnableMenuItem(settingsMenu, IDM_SKIN_HIDEONMOUSE, MF_BYCOMMAND | MF_GRAYED);
break;
}
2009-02-10 18:37:48 +00:00
if (meterWindow->GetSnapEdges())
{
CheckMenuItem(settingsMenu, IDM_SKIN_SNAPTOEDGES, MF_BYCOMMAND | MF_CHECKED);
}
2009-02-10 18:37:48 +00:00
if (meterWindow->GetSavePosition())
{
CheckMenuItem(settingsMenu, IDM_SKIN_REMEMBERPOSITION, MF_BYCOMMAND | MF_CHECKED);
}
2009-02-10 18:37:48 +00:00
if (m_DisableDragging)
{
EnableMenuItem(settingsMenu, IDM_SKIN_DRAGGABLE, MF_BYCOMMAND | MF_GRAYED);
}
else if (meterWindow->GetWindowDraggable())
{
CheckMenuItem(settingsMenu, IDM_SKIN_DRAGGABLE, MF_BYCOMMAND | MF_CHECKED);
}
2009-02-10 18:37:48 +00:00
if (meterWindow->GetClickThrough())
{
CheckMenuItem(settingsMenu, IDM_SKIN_CLICKTHROUGH, MF_BYCOMMAND | MF_CHECKED);
}
2009-02-10 18:37:48 +00:00
if (meterWindow->GetKeepOnScreen())
{
CheckMenuItem(settingsMenu, IDM_SKIN_KEEPONSCREEN, MF_BYCOMMAND | MF_CHECKED);
}
2009-02-10 18:37:48 +00:00
}
// Add the name of the Skin to the menu
const std::wstring& skinName = meterWindow->GetFolderPath();
ModifyMenu(skinMenu, IDM_SKIN_OPENSKINSFOLDER, MF_BYCOMMAND, IDM_SKIN_OPENSKINSFOLDER, skinName.c_str());
SetMenuDefaultItem(skinMenu, IDM_SKIN_OPENSKINSFOLDER, FALSE);
// Remove dummy menuitem from the variants menu
HMENU variantsMenu = GetSubMenu(skinMenu, 2);
if (variantsMenu)
{
DeleteMenu(variantsMenu, 0, MF_BYPOSITION);
}
2011-03-29 19:21:57 +00:00
// Give the menuitem the unique id that depends on the skin
2009-02-10 18:37:48 +00:00
ChangeSkinIndex(skinMenu, index);
// Add the variants menu
if (variantsMenu)
{
const SkinFolder& skinFolder = m_SkinFolders[FindSkinFolderIndex(skinName)];
for (int i = 0, isize = (int)skinFolder.files.size(); i < isize; ++i)
{
InsertMenu(variantsMenu, i, MF_BYPOSITION, skinFolder.commandBase + i, skinFolder.files[i].c_str());
}
if (skinFolder.active)
{
UINT checkPos = skinFolder.active - 1;
CheckMenuRadioItem(variantsMenu, checkPos, checkPos, checkPos, MF_BYPOSITION);
}
}
// Add skin root menu
int itemCount = GetMenuItemCount(menu);
if (itemCount > 0)
{
std::wstring root = meterWindow->GetFolderPath();
std::wstring::size_type pos = root.find_first_of(L'\\');
if (pos != std::wstring::npos)
{
root.erase(pos);
}
for (int i = 0; i < itemCount; ++i)
{
UINT state = GetMenuState(menu, i, MF_BYPOSITION);
if (state == 0xFFFFFFFF || (state & MF_POPUP) == 0) break;
WCHAR buffer[MAX_PATH];
if (GetMenuString(menu, i, buffer, MAX_PATH, MF_BYPOSITION))
{
2010-09-17 08:47:22 +00:00
if (_wcsicmp(root.c_str(), buffer) == 0)
{
HMENU skinRootMenu = GetSubMenu(menu, i);
if (skinRootMenu)
{
InsertMenu(skinMenu, 3, MF_BYPOSITION | MF_POPUP, (UINT_PTR)skinRootMenu, root.c_str());
}
break;
}
}
}
}
// Add custom actions to the context menu
std::wstring contextTitle = meterWindow->GetParser().ReadString(L"Rainmeter", L"ContextTitle", L"");
2013-03-10 20:08:00 +00:00
if (!contextTitle.empty())
{
2013-03-10 20:08:00 +00:00
auto isTitleSeparator = [](const std::wstring& title)
{
return title.find_first_not_of(L'-') == std::wstring::npos;
};
2013-03-10 20:08:00 +00:00
std::wstring contextAction = meterWindow->GetParser().ReadString(L"Rainmeter", L"ContextAction", L"");
if (!contextAction.empty() || isTitleSeparator(contextTitle))
{
2013-03-10 20:08:00 +00:00
std::vector<std::wstring> cTitles;
WCHAR buffer[128];
int i = 1;
while (!contextTitle.empty() &&
(!contextAction.empty() || isTitleSeparator(contextTitle)) &&
(IDM_SKIN_CUSTOMCONTEXTMENU_FIRST + i - 1) <= IDM_SKIN_CUSTOMCONTEXTMENU_LAST) // Set maximum context items in resource.h
{
2013-03-10 20:08:00 +00:00
// Trim long titles
if (contextTitle.size() > 30)
{
contextTitle.replace(27, contextTitle.size() - 27, L"...");
}
2013-03-10 20:08:00 +00:00
cTitles.push_back(contextTitle);
2013-03-10 20:08:00 +00:00
_snwprintf_s(buffer, _TRUNCATE, L"ContextTitle%i", ++i);
contextTitle = meterWindow->GetParser().ReadString(L"Rainmeter", buffer, L"");
_snwprintf_s(buffer, _TRUNCATE, L"ContextAction%i", i);
contextAction = meterWindow->GetParser().ReadString(L"Rainmeter", buffer, L"");
}
2013-03-10 20:08:00 +00:00
// Build a sub-menu if more than three items
size_t titleSize = cTitles.size();
if (titleSize <= 3)
{
2013-03-10 20:08:00 +00:00
size_t position = 0;
for (size_t i = 0; i < titleSize; ++i)
{
2013-03-10 20:08:00 +00:00
if (isTitleSeparator(cTitles[i]))
{
// Separators not allowed in main top-level menu
--position;
}
else
{
InsertMenu(skinMenu, position + 1, MF_BYPOSITION | MF_STRING, (index << 16) | (IDM_SKIN_CUSTOMCONTEXTMENU_FIRST + i), cTitles[i].c_str());
}
++position;
}
2013-03-10 20:08:00 +00:00
if (position != 0)
{
2013-05-31 14:28:39 +00:00
InsertMenu(skinMenu, 1, MF_BYPOSITION | MF_STRING | MF_GRAYED, 0, L"Custom skin actions");
InsertMenu(skinMenu, 1, MF_BYPOSITION | MF_SEPARATOR, 0, nullptr);
}
}
2013-03-10 20:08:00 +00:00
else
2012-10-05 05:30:45 +00:00
{
2013-03-10 20:08:00 +00:00
HMENU customMenu = CreatePopupMenu();
InsertMenu(skinMenu, 1, MF_BYPOSITION | MF_POPUP, (UINT_PTR)customMenu, L"Custom skin actions");
2013-03-10 20:08:00 +00:00
for (size_t i = 0; i < titleSize; ++i)
{
2013-03-10 20:08:00 +00:00
if (isTitleSeparator(cTitles[i]))
{
2013-05-31 14:28:39 +00:00
AppendMenu(customMenu, MF_BYPOSITION | MF_SEPARATOR, 0, nullptr);
2013-03-10 20:08:00 +00:00
}
else
{
AppendMenu(customMenu, MF_BYPOSITION | MF_STRING, (index << 16) | (IDM_SKIN_CUSTOMCONTEXTMENU_FIRST + i), cTitles[i].c_str());
}
}
2013-05-31 14:28:39 +00:00
InsertMenu(skinMenu, 1, MF_BYPOSITION | MF_SEPARATOR, 0, nullptr);
2013-03-10 20:08:00 +00:00
}
2012-10-05 05:30:45 +00:00
}
}
2009-02-10 18:37:48 +00:00
}
return skinMenu;
}
2013-05-31 14:18:52 +00:00
void Rainmeter::CreateLayoutMenu(HMENU layoutMenu)
{
2012-10-02 14:44:47 +00:00
for (size_t i = 0, isize = m_Layouts.size(); i < isize; ++i)
{
2012-10-02 14:44:47 +00:00
InsertMenu(layoutMenu, i, MF_BYPOSITION, ID_THEME_FIRST + i, m_Layouts[i].c_str());
}
}
2013-05-31 14:18:52 +00:00
void Rainmeter::CreateMonitorMenu(HMENU monitorMenu, MeterWindow* meterWindow)
* Changed the way to get the information of the multiple display monitors. This change brings the order of monitors close to the order of "Display Properties" due to using EnumDisplayDevices and EnumDisplaySettings instead of EnumDisplayMonitors. (If EnumDisplayDevices failed, EnumDisplayMonitors is used as before.) ----- * Added the "Display Monitor" submenu in [Skins Menu]-[Position]. These menus convert the present position to the relative position from the specified monitor. (But the meter window doesn't move to the specified monitor area immediately. Only converts.) - "Use default: Primary monitor" removes the @-directive from WindowX/Y. - @0(@1, @2, ...) adds the specified monitor number to WindowX/Y. @0 means "The Virtual Screen". (http://msdn.microsoft.com/en-us/library/dd145136%28VS.85%29.aspx) - If "Auto-select based on window position" is checked, the WindowX and WindowY "@n" settings are made automatically based on the position of the meter's window. If a monitor is selected directly using "Display Monitor" in the Rainmeter / skin context menu, this menu is unchecked. This setting can be manually made in either the [Rainmeter] (all configs) or individual config sections of Rainmeter.ini. AutoSelectScreen If set to 1, the WindowX and WindowY "@n" settings are made automatically based on the position of the meter's window. If a monitor is selected directly using "Display Monitor" in the Rainmeter / skin context menu, this setting is reset to 0. ----- * Added the variables for multiple display monitors and the virtual screen. All X/Y positions are represented in the virtual screen coordinates. The following variables are for the virtual screen. #VSCREENAREAX# is the X-position of the left-side of the virtual screen. #VSCREENAREAY# is the Y-position of the top-side of the virtual screen. #VSCREENAREAWIDTH# is the width of the virtual screen. #VSCREENAREAHEIGHT# is the height of the virtual screen. The following variables are for the PRESENT monitor. Note that these variables automatically change by the WindowX and WindowY "@n" settings. If "@n" is not set, these variables return the value of the primary monitor. #WORKAREAX# is the X-position of the left-side of the work area. #WORKAREAY# is the Y-position of the top-side of the work area. #WORKAREAWIDTH# is the width of the work area. #WORKAREAHEIGHT# is the height of the work area. #SCREENAREAX# is the X-position of the left-side of the monitor screen. #SCREENAREAY# is the Y-position of the top-side of the monitor screen. #SCREENAREAWIDTH# is the width of the display resolution. #SCREENAREAHEIGHT# is the height of the display resolution. The following variables are for the PRIMARY monitor. #PWORKAREAX# is the X-position of the left-side of the work area. #PWORKAREAY# is the Y-position of the top-side of the work area. #PWORKAREAWIDTH# is the width of the work area. #PWORKAREAHEIGHT# is the height of the work area. #PSCREENAREAX# is the X-position of the left-side of the monitor screen. (maybe, always 0) #PSCREENAREAY# is the Y-position of the top-side of the monitor screen. (maybe, always 0) #PSCREENAREAWIDTH# is the width of the display resolution. #PSCREENAREAHEIGHT# is the height of the display resolution. The following variables are for the SPECIFIED monitor. (@n = @1, @2, ...) #WORKAREAX@n# is the X-position of the left-side of the work area. #WORKAREAY@n# is the Y-position of the top-side of the work area. #WORKAREAWIDTH@n# is the width of the work area. #WORKAREAHEIGHT@n# is the height of the work area. #SCREENAREAX@n# is the X-position of the left-side of the monitor screen. #SCREENAREAY@n# is the Y-position of the top-side of the monitor screen. #SCREENAREAWIDTH@n# is the width of the display resolution. #SCREENAREAHEIGHT@n# is the height of the display resolution. ----- * Other related changes: - Fixed the problem that the primary monitor isn't recognized correctly. - Fixed the problem that the information of the multiple display monitors is refreshed excessively. - For DynamicVariables, when display setting or workarea size has been changed, all variables are now updated to apply changed WORKAREA/SCREENAREA variables. - Fixed the problem that the "On Desktop" window isn't dragged correctly when the top-left corner of the virtual screen has negative coordinates. - Changed the way to stick the "On Desktop" window. ("SysListView32/FolderView" is used instead of "Progman/Program Manager".) ----- * Other changes: - When the meter window is draggable and isn't dragged, LeftMouseUpAction is now executed. - Added MouseDoubleClickAction (LeftMouseDoubleClickAction, RightMouseDoubleClickAction, MiddleMouseDoubleClickAction). If MouseDoubleClickAction is empty when mouse button is double-clicked, MouseDownAction is executed instead. - Fixed the problem that the Meter's hit-test code checks outside the area. - Changed the way to set the #CURRENTCONFIG#. (CMeterWindow::GetSkinName() is now used instead of parsing the path.)
2009-12-18 05:58:37 +00:00
{
bool screenDefined = meterWindow->GetXScreenDefined();
int screenIndex = meterWindow->GetXScreen();
// for the "Specified monitor" (@n)
2013-05-31 14:18:52 +00:00
const size_t numOfMonitors = System::GetMonitorCount(); // intentional
const std::vector<MonitorInfo>& monitors = System::GetMultiMonitorInfo().monitors;
* Changed the way to get the information of the multiple display monitors. This change brings the order of monitors close to the order of "Display Properties" due to using EnumDisplayDevices and EnumDisplaySettings instead of EnumDisplayMonitors. (If EnumDisplayDevices failed, EnumDisplayMonitors is used as before.) ----- * Added the "Display Monitor" submenu in [Skins Menu]-[Position]. These menus convert the present position to the relative position from the specified monitor. (But the meter window doesn't move to the specified monitor area immediately. Only converts.) - "Use default: Primary monitor" removes the @-directive from WindowX/Y. - @0(@1, @2, ...) adds the specified monitor number to WindowX/Y. @0 means "The Virtual Screen". (http://msdn.microsoft.com/en-us/library/dd145136%28VS.85%29.aspx) - If "Auto-select based on window position" is checked, the WindowX and WindowY "@n" settings are made automatically based on the position of the meter's window. If a monitor is selected directly using "Display Monitor" in the Rainmeter / skin context menu, this menu is unchecked. This setting can be manually made in either the [Rainmeter] (all configs) or individual config sections of Rainmeter.ini. AutoSelectScreen If set to 1, the WindowX and WindowY "@n" settings are made automatically based on the position of the meter's window. If a monitor is selected directly using "Display Monitor" in the Rainmeter / skin context menu, this setting is reset to 0. ----- * Added the variables for multiple display monitors and the virtual screen. All X/Y positions are represented in the virtual screen coordinates. The following variables are for the virtual screen. #VSCREENAREAX# is the X-position of the left-side of the virtual screen. #VSCREENAREAY# is the Y-position of the top-side of the virtual screen. #VSCREENAREAWIDTH# is the width of the virtual screen. #VSCREENAREAHEIGHT# is the height of the virtual screen. The following variables are for the PRESENT monitor. Note that these variables automatically change by the WindowX and WindowY "@n" settings. If "@n" is not set, these variables return the value of the primary monitor. #WORKAREAX# is the X-position of the left-side of the work area. #WORKAREAY# is the Y-position of the top-side of the work area. #WORKAREAWIDTH# is the width of the work area. #WORKAREAHEIGHT# is the height of the work area. #SCREENAREAX# is the X-position of the left-side of the monitor screen. #SCREENAREAY# is the Y-position of the top-side of the monitor screen. #SCREENAREAWIDTH# is the width of the display resolution. #SCREENAREAHEIGHT# is the height of the display resolution. The following variables are for the PRIMARY monitor. #PWORKAREAX# is the X-position of the left-side of the work area. #PWORKAREAY# is the Y-position of the top-side of the work area. #PWORKAREAWIDTH# is the width of the work area. #PWORKAREAHEIGHT# is the height of the work area. #PSCREENAREAX# is the X-position of the left-side of the monitor screen. (maybe, always 0) #PSCREENAREAY# is the Y-position of the top-side of the monitor screen. (maybe, always 0) #PSCREENAREAWIDTH# is the width of the display resolution. #PSCREENAREAHEIGHT# is the height of the display resolution. The following variables are for the SPECIFIED monitor. (@n = @1, @2, ...) #WORKAREAX@n# is the X-position of the left-side of the work area. #WORKAREAY@n# is the Y-position of the top-side of the work area. #WORKAREAWIDTH@n# is the width of the work area. #WORKAREAHEIGHT@n# is the height of the work area. #SCREENAREAX@n# is the X-position of the left-side of the monitor screen. #SCREENAREAY@n# is the Y-position of the top-side of the monitor screen. #SCREENAREAWIDTH@n# is the width of the display resolution. #SCREENAREAHEIGHT@n# is the height of the display resolution. ----- * Other related changes: - Fixed the problem that the primary monitor isn't recognized correctly. - Fixed the problem that the information of the multiple display monitors is refreshed excessively. - For DynamicVariables, when display setting or workarea size has been changed, all variables are now updated to apply changed WORKAREA/SCREENAREA variables. - Fixed the problem that the "On Desktop" window isn't dragged correctly when the top-left corner of the virtual screen has negative coordinates. - Changed the way to stick the "On Desktop" window. ("SysListView32/FolderView" is used instead of "Progman/Program Manager".) ----- * Other changes: - When the meter window is draggable and isn't dragged, LeftMouseUpAction is now executed. - Added MouseDoubleClickAction (LeftMouseDoubleClickAction, RightMouseDoubleClickAction, MiddleMouseDoubleClickAction). If MouseDoubleClickAction is empty when mouse button is double-clicked, MouseDownAction is executed instead. - Fixed the problem that the Meter's hit-test code checks outside the area. - Changed the way to set the #CURRENTCONFIG#. (CMeterWindow::GetSkinName() is now used instead of parsing the path.)
2009-12-18 05:58:37 +00:00
int i = 1;
for (auto iter = monitors.cbegin(); iter != monitors.cend(); ++iter, ++i)
{
WCHAR buffer[64];
size_t len = _snwprintf_s(buffer, _TRUNCATE, L"@%i: ", i);
* Changed the way to get the information of the multiple display monitors. This change brings the order of monitors close to the order of "Display Properties" due to using EnumDisplayDevices and EnumDisplaySettings instead of EnumDisplayMonitors. (If EnumDisplayDevices failed, EnumDisplayMonitors is used as before.) ----- * Added the "Display Monitor" submenu in [Skins Menu]-[Position]. These menus convert the present position to the relative position from the specified monitor. (But the meter window doesn't move to the specified monitor area immediately. Only converts.) - "Use default: Primary monitor" removes the @-directive from WindowX/Y. - @0(@1, @2, ...) adds the specified monitor number to WindowX/Y. @0 means "The Virtual Screen". (http://msdn.microsoft.com/en-us/library/dd145136%28VS.85%29.aspx) - If "Auto-select based on window position" is checked, the WindowX and WindowY "@n" settings are made automatically based on the position of the meter's window. If a monitor is selected directly using "Display Monitor" in the Rainmeter / skin context menu, this menu is unchecked. This setting can be manually made in either the [Rainmeter] (all configs) or individual config sections of Rainmeter.ini. AutoSelectScreen If set to 1, the WindowX and WindowY "@n" settings are made automatically based on the position of the meter's window. If a monitor is selected directly using "Display Monitor" in the Rainmeter / skin context menu, this setting is reset to 0. ----- * Added the variables for multiple display monitors and the virtual screen. All X/Y positions are represented in the virtual screen coordinates. The following variables are for the virtual screen. #VSCREENAREAX# is the X-position of the left-side of the virtual screen. #VSCREENAREAY# is the Y-position of the top-side of the virtual screen. #VSCREENAREAWIDTH# is the width of the virtual screen. #VSCREENAREAHEIGHT# is the height of the virtual screen. The following variables are for the PRESENT monitor. Note that these variables automatically change by the WindowX and WindowY "@n" settings. If "@n" is not set, these variables return the value of the primary monitor. #WORKAREAX# is the X-position of the left-side of the work area. #WORKAREAY# is the Y-position of the top-side of the work area. #WORKAREAWIDTH# is the width of the work area. #WORKAREAHEIGHT# is the height of the work area. #SCREENAREAX# is the X-position of the left-side of the monitor screen. #SCREENAREAY# is the Y-position of the top-side of the monitor screen. #SCREENAREAWIDTH# is the width of the display resolution. #SCREENAREAHEIGHT# is the height of the display resolution. The following variables are for the PRIMARY monitor. #PWORKAREAX# is the X-position of the left-side of the work area. #PWORKAREAY# is the Y-position of the top-side of the work area. #PWORKAREAWIDTH# is the width of the work area. #PWORKAREAHEIGHT# is the height of the work area. #PSCREENAREAX# is the X-position of the left-side of the monitor screen. (maybe, always 0) #PSCREENAREAY# is the Y-position of the top-side of the monitor screen. (maybe, always 0) #PSCREENAREAWIDTH# is the width of the display resolution. #PSCREENAREAHEIGHT# is the height of the display resolution. The following variables are for the SPECIFIED monitor. (@n = @1, @2, ...) #WORKAREAX@n# is the X-position of the left-side of the work area. #WORKAREAY@n# is the Y-position of the top-side of the work area. #WORKAREAWIDTH@n# is the width of the work area. #WORKAREAHEIGHT@n# is the height of the work area. #SCREENAREAX@n# is the X-position of the left-side of the monitor screen. #SCREENAREAY@n# is the Y-position of the top-side of the monitor screen. #SCREENAREAWIDTH@n# is the width of the display resolution. #SCREENAREAHEIGHT@n# is the height of the display resolution. ----- * Other related changes: - Fixed the problem that the primary monitor isn't recognized correctly. - Fixed the problem that the information of the multiple display monitors is refreshed excessively. - For DynamicVariables, when display setting or workarea size has been changed, all variables are now updated to apply changed WORKAREA/SCREENAREA variables. - Fixed the problem that the "On Desktop" window isn't dragged correctly when the top-left corner of the virtual screen has negative coordinates. - Changed the way to stick the "On Desktop" window. ("SysListView32/FolderView" is used instead of "Progman/Program Manager".) ----- * Other changes: - When the meter window is draggable and isn't dragged, LeftMouseUpAction is now executed. - Added MouseDoubleClickAction (LeftMouseDoubleClickAction, RightMouseDoubleClickAction, MiddleMouseDoubleClickAction). If MouseDoubleClickAction is empty when mouse button is double-clicked, MouseDownAction is executed instead. - Fixed the problem that the Meter's hit-test code checks outside the area. - Changed the way to set the #CURRENTCONFIG#. (CMeterWindow::GetSkinName() is now used instead of parsing the path.)
2009-12-18 05:58:37 +00:00
std::wstring item(buffer, len);
2011-12-09 19:49:06 +00:00
if ((*iter).monitorName.size() > 32)
{
item.append((*iter).monitorName, 0, 32);
item += L"...";
* Changed the way to get the information of the multiple display monitors. This change brings the order of monitors close to the order of "Display Properties" due to using EnumDisplayDevices and EnumDisplaySettings instead of EnumDisplayMonitors. (If EnumDisplayDevices failed, EnumDisplayMonitors is used as before.) ----- * Added the "Display Monitor" submenu in [Skins Menu]-[Position]. These menus convert the present position to the relative position from the specified monitor. (But the meter window doesn't move to the specified monitor area immediately. Only converts.) - "Use default: Primary monitor" removes the @-directive from WindowX/Y. - @0(@1, @2, ...) adds the specified monitor number to WindowX/Y. @0 means "The Virtual Screen". (http://msdn.microsoft.com/en-us/library/dd145136%28VS.85%29.aspx) - If "Auto-select based on window position" is checked, the WindowX and WindowY "@n" settings are made automatically based on the position of the meter's window. If a monitor is selected directly using "Display Monitor" in the Rainmeter / skin context menu, this menu is unchecked. This setting can be manually made in either the [Rainmeter] (all configs) or individual config sections of Rainmeter.ini. AutoSelectScreen If set to 1, the WindowX and WindowY "@n" settings are made automatically based on the position of the meter's window. If a monitor is selected directly using "Display Monitor" in the Rainmeter / skin context menu, this setting is reset to 0. ----- * Added the variables for multiple display monitors and the virtual screen. All X/Y positions are represented in the virtual screen coordinates. The following variables are for the virtual screen. #VSCREENAREAX# is the X-position of the left-side of the virtual screen. #VSCREENAREAY# is the Y-position of the top-side of the virtual screen. #VSCREENAREAWIDTH# is the width of the virtual screen. #VSCREENAREAHEIGHT# is the height of the virtual screen. The following variables are for the PRESENT monitor. Note that these variables automatically change by the WindowX and WindowY "@n" settings. If "@n" is not set, these variables return the value of the primary monitor. #WORKAREAX# is the X-position of the left-side of the work area. #WORKAREAY# is the Y-position of the top-side of the work area. #WORKAREAWIDTH# is the width of the work area. #WORKAREAHEIGHT# is the height of the work area. #SCREENAREAX# is the X-position of the left-side of the monitor screen. #SCREENAREAY# is the Y-position of the top-side of the monitor screen. #SCREENAREAWIDTH# is the width of the display resolution. #SCREENAREAHEIGHT# is the height of the display resolution. The following variables are for the PRIMARY monitor. #PWORKAREAX# is the X-position of the left-side of the work area. #PWORKAREAY# is the Y-position of the top-side of the work area. #PWORKAREAWIDTH# is the width of the work area. #PWORKAREAHEIGHT# is the height of the work area. #PSCREENAREAX# is the X-position of the left-side of the monitor screen. (maybe, always 0) #PSCREENAREAY# is the Y-position of the top-side of the monitor screen. (maybe, always 0) #PSCREENAREAWIDTH# is the width of the display resolution. #PSCREENAREAHEIGHT# is the height of the display resolution. The following variables are for the SPECIFIED monitor. (@n = @1, @2, ...) #WORKAREAX@n# is the X-position of the left-side of the work area. #WORKAREAY@n# is the Y-position of the top-side of the work area. #WORKAREAWIDTH@n# is the width of the work area. #WORKAREAHEIGHT@n# is the height of the work area. #SCREENAREAX@n# is the X-position of the left-side of the monitor screen. #SCREENAREAY@n# is the Y-position of the top-side of the monitor screen. #SCREENAREAWIDTH@n# is the width of the display resolution. #SCREENAREAHEIGHT@n# is the height of the display resolution. ----- * Other related changes: - Fixed the problem that the primary monitor isn't recognized correctly. - Fixed the problem that the information of the multiple display monitors is refreshed excessively. - For DynamicVariables, when display setting or workarea size has been changed, all variables are now updated to apply changed WORKAREA/SCREENAREA variables. - Fixed the problem that the "On Desktop" window isn't dragged correctly when the top-left corner of the virtual screen has negative coordinates. - Changed the way to stick the "On Desktop" window. ("SysListView32/FolderView" is used instead of "Progman/Program Manager".) ----- * Other changes: - When the meter window is draggable and isn't dragged, LeftMouseUpAction is now executed. - Added MouseDoubleClickAction (LeftMouseDoubleClickAction, RightMouseDoubleClickAction, MiddleMouseDoubleClickAction). If MouseDoubleClickAction is empty when mouse button is double-clicked, MouseDownAction is executed instead. - Fixed the problem that the Meter's hit-test code checks outside the area. - Changed the way to set the #CURRENTCONFIG#. (CMeterWindow::GetSkinName() is now used instead of parsing the path.)
2009-12-18 05:58:37 +00:00
}
else
{
item += (*iter).monitorName;
}
InsertMenu(monitorMenu,
i + 2,
MF_BYPOSITION | ((screenDefined && screenIndex == i) ? MF_CHECKED : MF_UNCHECKED) | ((!(*iter).active) ? MF_GRAYED : MF_ENABLED),
ID_MONITOR_FIRST + i,
item.c_str());
* Changed the way to get the information of the multiple display monitors. This change brings the order of monitors close to the order of "Display Properties" due to using EnumDisplayDevices and EnumDisplaySettings instead of EnumDisplayMonitors. (If EnumDisplayDevices failed, EnumDisplayMonitors is used as before.) ----- * Added the "Display Monitor" submenu in [Skins Menu]-[Position]. These menus convert the present position to the relative position from the specified monitor. (But the meter window doesn't move to the specified monitor area immediately. Only converts.) - "Use default: Primary monitor" removes the @-directive from WindowX/Y. - @0(@1, @2, ...) adds the specified monitor number to WindowX/Y. @0 means "The Virtual Screen". (http://msdn.microsoft.com/en-us/library/dd145136%28VS.85%29.aspx) - If "Auto-select based on window position" is checked, the WindowX and WindowY "@n" settings are made automatically based on the position of the meter's window. If a monitor is selected directly using "Display Monitor" in the Rainmeter / skin context menu, this menu is unchecked. This setting can be manually made in either the [Rainmeter] (all configs) or individual config sections of Rainmeter.ini. AutoSelectScreen If set to 1, the WindowX and WindowY "@n" settings are made automatically based on the position of the meter's window. If a monitor is selected directly using "Display Monitor" in the Rainmeter / skin context menu, this setting is reset to 0. ----- * Added the variables for multiple display monitors and the virtual screen. All X/Y positions are represented in the virtual screen coordinates. The following variables are for the virtual screen. #VSCREENAREAX# is the X-position of the left-side of the virtual screen. #VSCREENAREAY# is the Y-position of the top-side of the virtual screen. #VSCREENAREAWIDTH# is the width of the virtual screen. #VSCREENAREAHEIGHT# is the height of the virtual screen. The following variables are for the PRESENT monitor. Note that these variables automatically change by the WindowX and WindowY "@n" settings. If "@n" is not set, these variables return the value of the primary monitor. #WORKAREAX# is the X-position of the left-side of the work area. #WORKAREAY# is the Y-position of the top-side of the work area. #WORKAREAWIDTH# is the width of the work area. #WORKAREAHEIGHT# is the height of the work area. #SCREENAREAX# is the X-position of the left-side of the monitor screen. #SCREENAREAY# is the Y-position of the top-side of the monitor screen. #SCREENAREAWIDTH# is the width of the display resolution. #SCREENAREAHEIGHT# is the height of the display resolution. The following variables are for the PRIMARY monitor. #PWORKAREAX# is the X-position of the left-side of the work area. #PWORKAREAY# is the Y-position of the top-side of the work area. #PWORKAREAWIDTH# is the width of the work area. #PWORKAREAHEIGHT# is the height of the work area. #PSCREENAREAX# is the X-position of the left-side of the monitor screen. (maybe, always 0) #PSCREENAREAY# is the Y-position of the top-side of the monitor screen. (maybe, always 0) #PSCREENAREAWIDTH# is the width of the display resolution. #PSCREENAREAHEIGHT# is the height of the display resolution. The following variables are for the SPECIFIED monitor. (@n = @1, @2, ...) #WORKAREAX@n# is the X-position of the left-side of the work area. #WORKAREAY@n# is the Y-position of the top-side of the work area. #WORKAREAWIDTH@n# is the width of the work area. #WORKAREAHEIGHT@n# is the height of the work area. #SCREENAREAX@n# is the X-position of the left-side of the monitor screen. #SCREENAREAY@n# is the Y-position of the top-side of the monitor screen. #SCREENAREAWIDTH@n# is the width of the display resolution. #SCREENAREAHEIGHT@n# is the height of the display resolution. ----- * Other related changes: - Fixed the problem that the primary monitor isn't recognized correctly. - Fixed the problem that the information of the multiple display monitors is refreshed excessively. - For DynamicVariables, when display setting or workarea size has been changed, all variables are now updated to apply changed WORKAREA/SCREENAREA variables. - Fixed the problem that the "On Desktop" window isn't dragged correctly when the top-left corner of the virtual screen has negative coordinates. - Changed the way to stick the "On Desktop" window. ("SysListView32/FolderView" is used instead of "Progman/Program Manager".) ----- * Other changes: - When the meter window is draggable and isn't dragged, LeftMouseUpAction is now executed. - Added MouseDoubleClickAction (LeftMouseDoubleClickAction, RightMouseDoubleClickAction, MiddleMouseDoubleClickAction). If MouseDoubleClickAction is empty when mouse button is double-clicked, MouseDownAction is executed instead. - Fixed the problem that the Meter's hit-test code checks outside the area. - Changed the way to set the #CURRENTCONFIG#. (CMeterWindow::GetSkinName() is now used instead of parsing the path.)
2009-12-18 05:58:37 +00:00
}
if (!screenDefined)
{
CheckMenuItem(monitorMenu, IDM_SKIN_MONITOR_PRIMARY, MF_BYCOMMAND | MF_CHECKED);
}
* Changed the way to get the information of the multiple display monitors. This change brings the order of monitors close to the order of "Display Properties" due to using EnumDisplayDevices and EnumDisplaySettings instead of EnumDisplayMonitors. (If EnumDisplayDevices failed, EnumDisplayMonitors is used as before.) ----- * Added the "Display Monitor" submenu in [Skins Menu]-[Position]. These menus convert the present position to the relative position from the specified monitor. (But the meter window doesn't move to the specified monitor area immediately. Only converts.) - "Use default: Primary monitor" removes the @-directive from WindowX/Y. - @0(@1, @2, ...) adds the specified monitor number to WindowX/Y. @0 means "The Virtual Screen". (http://msdn.microsoft.com/en-us/library/dd145136%28VS.85%29.aspx) - If "Auto-select based on window position" is checked, the WindowX and WindowY "@n" settings are made automatically based on the position of the meter's window. If a monitor is selected directly using "Display Monitor" in the Rainmeter / skin context menu, this menu is unchecked. This setting can be manually made in either the [Rainmeter] (all configs) or individual config sections of Rainmeter.ini. AutoSelectScreen If set to 1, the WindowX and WindowY "@n" settings are made automatically based on the position of the meter's window. If a monitor is selected directly using "Display Monitor" in the Rainmeter / skin context menu, this setting is reset to 0. ----- * Added the variables for multiple display monitors and the virtual screen. All X/Y positions are represented in the virtual screen coordinates. The following variables are for the virtual screen. #VSCREENAREAX# is the X-position of the left-side of the virtual screen. #VSCREENAREAY# is the Y-position of the top-side of the virtual screen. #VSCREENAREAWIDTH# is the width of the virtual screen. #VSCREENAREAHEIGHT# is the height of the virtual screen. The following variables are for the PRESENT monitor. Note that these variables automatically change by the WindowX and WindowY "@n" settings. If "@n" is not set, these variables return the value of the primary monitor. #WORKAREAX# is the X-position of the left-side of the work area. #WORKAREAY# is the Y-position of the top-side of the work area. #WORKAREAWIDTH# is the width of the work area. #WORKAREAHEIGHT# is the height of the work area. #SCREENAREAX# is the X-position of the left-side of the monitor screen. #SCREENAREAY# is the Y-position of the top-side of the monitor screen. #SCREENAREAWIDTH# is the width of the display resolution. #SCREENAREAHEIGHT# is the height of the display resolution. The following variables are for the PRIMARY monitor. #PWORKAREAX# is the X-position of the left-side of the work area. #PWORKAREAY# is the Y-position of the top-side of the work area. #PWORKAREAWIDTH# is the width of the work area. #PWORKAREAHEIGHT# is the height of the work area. #PSCREENAREAX# is the X-position of the left-side of the monitor screen. (maybe, always 0) #PSCREENAREAY# is the Y-position of the top-side of the monitor screen. (maybe, always 0) #PSCREENAREAWIDTH# is the width of the display resolution. #PSCREENAREAHEIGHT# is the height of the display resolution. The following variables are for the SPECIFIED monitor. (@n = @1, @2, ...) #WORKAREAX@n# is the X-position of the left-side of the work area. #WORKAREAY@n# is the Y-position of the top-side of the work area. #WORKAREAWIDTH@n# is the width of the work area. #WORKAREAHEIGHT@n# is the height of the work area. #SCREENAREAX@n# is the X-position of the left-side of the monitor screen. #SCREENAREAY@n# is the Y-position of the top-side of the monitor screen. #SCREENAREAWIDTH@n# is the width of the display resolution. #SCREENAREAHEIGHT@n# is the height of the display resolution. ----- * Other related changes: - Fixed the problem that the primary monitor isn't recognized correctly. - Fixed the problem that the information of the multiple display monitors is refreshed excessively. - For DynamicVariables, when display setting or workarea size has been changed, all variables are now updated to apply changed WORKAREA/SCREENAREA variables. - Fixed the problem that the "On Desktop" window isn't dragged correctly when the top-left corner of the virtual screen has negative coordinates. - Changed the way to stick the "On Desktop" window. ("SysListView32/FolderView" is used instead of "Progman/Program Manager".) ----- * Other changes: - When the meter window is draggable and isn't dragged, LeftMouseUpAction is now executed. - Added MouseDoubleClickAction (LeftMouseDoubleClickAction, RightMouseDoubleClickAction, MiddleMouseDoubleClickAction). If MouseDoubleClickAction is empty when mouse button is double-clicked, MouseDownAction is executed instead. - Fixed the problem that the Meter's hit-test code checks outside the area. - Changed the way to set the #CURRENTCONFIG#. (CMeterWindow::GetSkinName() is now used instead of parsing the path.)
2009-12-18 05:58:37 +00:00
if (screenDefined && screenIndex == 0)
* Changed the way to get the information of the multiple display monitors. This change brings the order of monitors close to the order of "Display Properties" due to using EnumDisplayDevices and EnumDisplaySettings instead of EnumDisplayMonitors. (If EnumDisplayDevices failed, EnumDisplayMonitors is used as before.) ----- * Added the "Display Monitor" submenu in [Skins Menu]-[Position]. These menus convert the present position to the relative position from the specified monitor. (But the meter window doesn't move to the specified monitor area immediately. Only converts.) - "Use default: Primary monitor" removes the @-directive from WindowX/Y. - @0(@1, @2, ...) adds the specified monitor number to WindowX/Y. @0 means "The Virtual Screen". (http://msdn.microsoft.com/en-us/library/dd145136%28VS.85%29.aspx) - If "Auto-select based on window position" is checked, the WindowX and WindowY "@n" settings are made automatically based on the position of the meter's window. If a monitor is selected directly using "Display Monitor" in the Rainmeter / skin context menu, this menu is unchecked. This setting can be manually made in either the [Rainmeter] (all configs) or individual config sections of Rainmeter.ini. AutoSelectScreen If set to 1, the WindowX and WindowY "@n" settings are made automatically based on the position of the meter's window. If a monitor is selected directly using "Display Monitor" in the Rainmeter / skin context menu, this setting is reset to 0. ----- * Added the variables for multiple display monitors and the virtual screen. All X/Y positions are represented in the virtual screen coordinates. The following variables are for the virtual screen. #VSCREENAREAX# is the X-position of the left-side of the virtual screen. #VSCREENAREAY# is the Y-position of the top-side of the virtual screen. #VSCREENAREAWIDTH# is the width of the virtual screen. #VSCREENAREAHEIGHT# is the height of the virtual screen. The following variables are for the PRESENT monitor. Note that these variables automatically change by the WindowX and WindowY "@n" settings. If "@n" is not set, these variables return the value of the primary monitor. #WORKAREAX# is the X-position of the left-side of the work area. #WORKAREAY# is the Y-position of the top-side of the work area. #WORKAREAWIDTH# is the width of the work area. #WORKAREAHEIGHT# is the height of the work area. #SCREENAREAX# is the X-position of the left-side of the monitor screen. #SCREENAREAY# is the Y-position of the top-side of the monitor screen. #SCREENAREAWIDTH# is the width of the display resolution. #SCREENAREAHEIGHT# is the height of the display resolution. The following variables are for the PRIMARY monitor. #PWORKAREAX# is the X-position of the left-side of the work area. #PWORKAREAY# is the Y-position of the top-side of the work area. #PWORKAREAWIDTH# is the width of the work area. #PWORKAREAHEIGHT# is the height of the work area. #PSCREENAREAX# is the X-position of the left-side of the monitor screen. (maybe, always 0) #PSCREENAREAY# is the Y-position of the top-side of the monitor screen. (maybe, always 0) #PSCREENAREAWIDTH# is the width of the display resolution. #PSCREENAREAHEIGHT# is the height of the display resolution. The following variables are for the SPECIFIED monitor. (@n = @1, @2, ...) #WORKAREAX@n# is the X-position of the left-side of the work area. #WORKAREAY@n# is the Y-position of the top-side of the work area. #WORKAREAWIDTH@n# is the width of the work area. #WORKAREAHEIGHT@n# is the height of the work area. #SCREENAREAX@n# is the X-position of the left-side of the monitor screen. #SCREENAREAY@n# is the Y-position of the top-side of the monitor screen. #SCREENAREAWIDTH@n# is the width of the display resolution. #SCREENAREAHEIGHT@n# is the height of the display resolution. ----- * Other related changes: - Fixed the problem that the primary monitor isn't recognized correctly. - Fixed the problem that the information of the multiple display monitors is refreshed excessively. - For DynamicVariables, when display setting or workarea size has been changed, all variables are now updated to apply changed WORKAREA/SCREENAREA variables. - Fixed the problem that the "On Desktop" window isn't dragged correctly when the top-left corner of the virtual screen has negative coordinates. - Changed the way to stick the "On Desktop" window. ("SysListView32/FolderView" is used instead of "Progman/Program Manager".) ----- * Other changes: - When the meter window is draggable and isn't dragged, LeftMouseUpAction is now executed. - Added MouseDoubleClickAction (LeftMouseDoubleClickAction, RightMouseDoubleClickAction, MiddleMouseDoubleClickAction). If MouseDoubleClickAction is empty when mouse button is double-clicked, MouseDownAction is executed instead. - Fixed the problem that the Meter's hit-test code checks outside the area. - Changed the way to set the #CURRENTCONFIG#. (CMeterWindow::GetSkinName() is now used instead of parsing the path.)
2009-12-18 05:58:37 +00:00
{
CheckMenuItem(monitorMenu, ID_MONITOR_FIRST, MF_BYCOMMAND | MF_CHECKED);
* Changed the way to get the information of the multiple display monitors. This change brings the order of monitors close to the order of "Display Properties" due to using EnumDisplayDevices and EnumDisplaySettings instead of EnumDisplayMonitors. (If EnumDisplayDevices failed, EnumDisplayMonitors is used as before.) ----- * Added the "Display Monitor" submenu in [Skins Menu]-[Position]. These menus convert the present position to the relative position from the specified monitor. (But the meter window doesn't move to the specified monitor area immediately. Only converts.) - "Use default: Primary monitor" removes the @-directive from WindowX/Y. - @0(@1, @2, ...) adds the specified monitor number to WindowX/Y. @0 means "The Virtual Screen". (http://msdn.microsoft.com/en-us/library/dd145136%28VS.85%29.aspx) - If "Auto-select based on window position" is checked, the WindowX and WindowY "@n" settings are made automatically based on the position of the meter's window. If a monitor is selected directly using "Display Monitor" in the Rainmeter / skin context menu, this menu is unchecked. This setting can be manually made in either the [Rainmeter] (all configs) or individual config sections of Rainmeter.ini. AutoSelectScreen If set to 1, the WindowX and WindowY "@n" settings are made automatically based on the position of the meter's window. If a monitor is selected directly using "Display Monitor" in the Rainmeter / skin context menu, this setting is reset to 0. ----- * Added the variables for multiple display monitors and the virtual screen. All X/Y positions are represented in the virtual screen coordinates. The following variables are for the virtual screen. #VSCREENAREAX# is the X-position of the left-side of the virtual screen. #VSCREENAREAY# is the Y-position of the top-side of the virtual screen. #VSCREENAREAWIDTH# is the width of the virtual screen. #VSCREENAREAHEIGHT# is the height of the virtual screen. The following variables are for the PRESENT monitor. Note that these variables automatically change by the WindowX and WindowY "@n" settings. If "@n" is not set, these variables return the value of the primary monitor. #WORKAREAX# is the X-position of the left-side of the work area. #WORKAREAY# is the Y-position of the top-side of the work area. #WORKAREAWIDTH# is the width of the work area. #WORKAREAHEIGHT# is the height of the work area. #SCREENAREAX# is the X-position of the left-side of the monitor screen. #SCREENAREAY# is the Y-position of the top-side of the monitor screen. #SCREENAREAWIDTH# is the width of the display resolution. #SCREENAREAHEIGHT# is the height of the display resolution. The following variables are for the PRIMARY monitor. #PWORKAREAX# is the X-position of the left-side of the work area. #PWORKAREAY# is the Y-position of the top-side of the work area. #PWORKAREAWIDTH# is the width of the work area. #PWORKAREAHEIGHT# is the height of the work area. #PSCREENAREAX# is the X-position of the left-side of the monitor screen. (maybe, always 0) #PSCREENAREAY# is the Y-position of the top-side of the monitor screen. (maybe, always 0) #PSCREENAREAWIDTH# is the width of the display resolution. #PSCREENAREAHEIGHT# is the height of the display resolution. The following variables are for the SPECIFIED monitor. (@n = @1, @2, ...) #WORKAREAX@n# is the X-position of the left-side of the work area. #WORKAREAY@n# is the Y-position of the top-side of the work area. #WORKAREAWIDTH@n# is the width of the work area. #WORKAREAHEIGHT@n# is the height of the work area. #SCREENAREAX@n# is the X-position of the left-side of the monitor screen. #SCREENAREAY@n# is the Y-position of the top-side of the monitor screen. #SCREENAREAWIDTH@n# is the width of the display resolution. #SCREENAREAHEIGHT@n# is the height of the display resolution. ----- * Other related changes: - Fixed the problem that the primary monitor isn't recognized correctly. - Fixed the problem that the information of the multiple display monitors is refreshed excessively. - For DynamicVariables, when display setting or workarea size has been changed, all variables are now updated to apply changed WORKAREA/SCREENAREA variables. - Fixed the problem that the "On Desktop" window isn't dragged correctly when the top-left corner of the virtual screen has negative coordinates. - Changed the way to stick the "On Desktop" window. ("SysListView32/FolderView" is used instead of "Progman/Program Manager".) ----- * Other changes: - When the meter window is draggable and isn't dragged, LeftMouseUpAction is now executed. - Added MouseDoubleClickAction (LeftMouseDoubleClickAction, RightMouseDoubleClickAction, MiddleMouseDoubleClickAction). If MouseDoubleClickAction is empty when mouse button is double-clicked, MouseDownAction is executed instead. - Fixed the problem that the Meter's hit-test code checks outside the area. - Changed the way to set the #CURRENTCONFIG#. (CMeterWindow::GetSkinName() is now used instead of parsing the path.)
2009-12-18 05:58:37 +00:00
}
if (meterWindow->GetAutoSelectScreen())
{
CheckMenuItem(monitorMenu, IDM_SKIN_MONITOR_AUTOSELECT, MF_BYCOMMAND | MF_CHECKED);
}
* Changed the way to get the information of the multiple display monitors. This change brings the order of monitors close to the order of "Display Properties" due to using EnumDisplayDevices and EnumDisplaySettings instead of EnumDisplayMonitors. (If EnumDisplayDevices failed, EnumDisplayMonitors is used as before.) ----- * Added the "Display Monitor" submenu in [Skins Menu]-[Position]. These menus convert the present position to the relative position from the specified monitor. (But the meter window doesn't move to the specified monitor area immediately. Only converts.) - "Use default: Primary monitor" removes the @-directive from WindowX/Y. - @0(@1, @2, ...) adds the specified monitor number to WindowX/Y. @0 means "The Virtual Screen". (http://msdn.microsoft.com/en-us/library/dd145136%28VS.85%29.aspx) - If "Auto-select based on window position" is checked, the WindowX and WindowY "@n" settings are made automatically based on the position of the meter's window. If a monitor is selected directly using "Display Monitor" in the Rainmeter / skin context menu, this menu is unchecked. This setting can be manually made in either the [Rainmeter] (all configs) or individual config sections of Rainmeter.ini. AutoSelectScreen If set to 1, the WindowX and WindowY "@n" settings are made automatically based on the position of the meter's window. If a monitor is selected directly using "Display Monitor" in the Rainmeter / skin context menu, this setting is reset to 0. ----- * Added the variables for multiple display monitors and the virtual screen. All X/Y positions are represented in the virtual screen coordinates. The following variables are for the virtual screen. #VSCREENAREAX# is the X-position of the left-side of the virtual screen. #VSCREENAREAY# is the Y-position of the top-side of the virtual screen. #VSCREENAREAWIDTH# is the width of the virtual screen. #VSCREENAREAHEIGHT# is the height of the virtual screen. The following variables are for the PRESENT monitor. Note that these variables automatically change by the WindowX and WindowY "@n" settings. If "@n" is not set, these variables return the value of the primary monitor. #WORKAREAX# is the X-position of the left-side of the work area. #WORKAREAY# is the Y-position of the top-side of the work area. #WORKAREAWIDTH# is the width of the work area. #WORKAREAHEIGHT# is the height of the work area. #SCREENAREAX# is the X-position of the left-side of the monitor screen. #SCREENAREAY# is the Y-position of the top-side of the monitor screen. #SCREENAREAWIDTH# is the width of the display resolution. #SCREENAREAHEIGHT# is the height of the display resolution. The following variables are for the PRIMARY monitor. #PWORKAREAX# is the X-position of the left-side of the work area. #PWORKAREAY# is the Y-position of the top-side of the work area. #PWORKAREAWIDTH# is the width of the work area. #PWORKAREAHEIGHT# is the height of the work area. #PSCREENAREAX# is the X-position of the left-side of the monitor screen. (maybe, always 0) #PSCREENAREAY# is the Y-position of the top-side of the monitor screen. (maybe, always 0) #PSCREENAREAWIDTH# is the width of the display resolution. #PSCREENAREAHEIGHT# is the height of the display resolution. The following variables are for the SPECIFIED monitor. (@n = @1, @2, ...) #WORKAREAX@n# is the X-position of the left-side of the work area. #WORKAREAY@n# is the Y-position of the top-side of the work area. #WORKAREAWIDTH@n# is the width of the work area. #WORKAREAHEIGHT@n# is the height of the work area. #SCREENAREAX@n# is the X-position of the left-side of the monitor screen. #SCREENAREAY@n# is the Y-position of the top-side of the monitor screen. #SCREENAREAWIDTH@n# is the width of the display resolution. #SCREENAREAHEIGHT@n# is the height of the display resolution. ----- * Other related changes: - Fixed the problem that the primary monitor isn't recognized correctly. - Fixed the problem that the information of the multiple display monitors is refreshed excessively. - For DynamicVariables, when display setting or workarea size has been changed, all variables are now updated to apply changed WORKAREA/SCREENAREA variables. - Fixed the problem that the "On Desktop" window isn't dragged correctly when the top-left corner of the virtual screen has negative coordinates. - Changed the way to stick the "On Desktop" window. ("SysListView32/FolderView" is used instead of "Progman/Program Manager".) ----- * Other changes: - When the meter window is draggable and isn't dragged, LeftMouseUpAction is now executed. - Added MouseDoubleClickAction (LeftMouseDoubleClickAction, RightMouseDoubleClickAction, MiddleMouseDoubleClickAction). If MouseDoubleClickAction is empty when mouse button is double-clicked, MouseDownAction is executed instead. - Fixed the problem that the Meter's hit-test code checks outside the area. - Changed the way to set the #CURRENTCONFIG#. (CMeterWindow::GetSkinName() is now used instead of parsing the path.)
2009-12-18 05:58:37 +00:00
}
2013-05-31 14:18:52 +00:00
void Rainmeter::ChangeSkinIndex(HMENU menu, int index)
2009-02-10 18:37:48 +00:00
{
if (index > 0)
2009-02-10 18:37:48 +00:00
{
int count = GetMenuItemCount(menu);
for (int i = 0; i < count; ++i)
2009-02-10 18:37:48 +00:00
{
HMENU subMenu = GetSubMenu(menu, i);
if (subMenu)
{
ChangeSkinIndex(subMenu, index);
}
else
{
MENUITEMINFO mii = {sizeof(MENUITEMINFO)};
mii.fMask = MIIM_FTYPE | MIIM_ID;
GetMenuItemInfo(menu, i, TRUE, &mii);
if ((mii.fType & MFT_SEPARATOR) == 0)
{
mii.wID |= (index << 16);
mii.fMask = MIIM_ID;
SetMenuItemInfo(menu, i, TRUE, &mii);
}
}
2009-02-10 18:37:48 +00:00
}
}
}
2013-05-31 14:18:52 +00:00
void Rainmeter::ShowLogFile()
2012-02-21 19:15:10 +00:00
{
2013-06-13 14:22:42 +00:00
std::wstring logFile = L'"' + GetLogger().GetLogFilePath();
logFile += L'"';
2013-05-31 14:18:52 +00:00
CommandHandler::RunFile(m_SkinEditor.c_str(), logFile.c_str());
2012-02-21 19:15:10 +00:00
}
2013-05-31 14:18:52 +00:00
void Rainmeter::SetDebug(bool debug)
{
m_Debug = debug;
WritePrivateProfileString(L"Rainmeter", L"Debug", debug ? L"1" : L"0", m_IniFile.c_str());
}
2013-05-31 14:18:52 +00:00
void Rainmeter::SetDisableDragging(bool dragging)
{
m_DisableDragging = dragging;
WritePrivateProfileString(L"Rainmeter", L"DisableDragging", dragging ? L"1" : L"0", m_IniFile.c_str());
}
2013-05-31 14:18:52 +00:00
void Rainmeter::SetDisableVersionCheck(bool check)
{
m_DisableVersionCheck = check;
WritePrivateProfileString(L"Rainmeter", L"DisableVersionCheck", check ? L"1" : L"0" , m_IniFile.c_str());
}
2013-05-31 14:18:52 +00:00
void Rainmeter::TestSettingsFile(bool bDefaultIniLocation)
2009-02-10 18:37:48 +00:00
{
2012-05-30 04:08:50 +00:00
const WCHAR* iniFile = m_IniFile.c_str();
2013-05-31 14:18:52 +00:00
if (!System::IsFileWritable(iniFile))
2009-02-10 18:37:48 +00:00
{
2011-09-24 09:13:13 +00:00
std::wstring error = GetString(ID_STR_SETTINGSNOTWRITABLE);
2009-02-10 18:37:48 +00:00
if (!bDefaultIniLocation)
{
std::wstring strTarget = L"%APPDATA%\\Rainmeter\\";
PathUtil::ExpandEnvironmentVariables(strTarget);
2012-05-30 04:08:50 +00:00
error += GetFormattedString(ID_STR_SETTINGSMOVEFILE, iniFile, strTarget.c_str());
}
else
{
2012-05-30 04:08:50 +00:00
error += GetFormattedString(ID_STR_SETTINGSREADONLY, iniFile);
}
2009-02-10 18:37:48 +00:00
2013-05-31 14:28:39 +00:00
ShowMessage(nullptr, error.c_str(), MB_OK | MB_ICONERROR);
2009-02-10 18:37:48 +00:00
}
}