FileView: Added support for opening the properties or context menu for each item. A user-defined item can also be used on parent measures (in case someone wants to open the properties/context menu from another source - a "dummy" parent measure will be needed).

Usage:
!CommandMeasure MeasureName ContextMenu
!CommandMeasure MeasureName Properties
This commit is contained in:
Brian Ferguson 2014-02-07 12:34:16 -07:00
parent d0fd184063
commit 05d256ced3
5 changed files with 206 additions and 37 deletions

View File

@ -17,6 +17,8 @@
*/
#include "PluginFileView.h"
#include "../../Common/Platform.h"
#include "../../Common/StringUtil.h"
#define MAX_LINE_LENGTH 4096
#define INVALID_FILE L"/<>\\"
@ -50,6 +52,7 @@ HRESULT SaveIcon(HICON hIcon, FILE* fp);
static std::vector<ParentMeasure*> g_ParentMeasures;
static CRITICAL_SECTION g_CriticalSection;
static std::string g_SysProperties;
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
@ -74,6 +77,15 @@ PLUGIN_EXPORT void Initialize(void** data, void* rm)
{
ChildMeasure* child = new ChildMeasure;
*data = child;
if (g_SysProperties.empty())
{
std::wstring dir = RmReplaceVariables(rm, L"%WINDIR%");
dir.append(L"\\system32\\control.exe");
dir.append(Platform::IsAtLeastWinVista() ? L" system" : L" sysdm.cpl");
g_SysProperties = StringUtil::Narrow(dir);
}
}
PLUGIN_EXPORT void Reload(void* data, void* rm, double* maxValue)
@ -98,7 +110,7 @@ PLUGIN_EXPORT void Reload(void* data, void* rm, double* maxValue)
if (!child->parent)
{
RmLog(LOG_ERROR, L"FileView.dll: Invalid Path");
RmLogF(rm, LOG_ERROR, L"Invalid Path: \"%s\"", path.c_str());
return;
}
}
@ -110,6 +122,8 @@ PLUGIN_EXPORT void Reload(void* data, void* rm, double* maxValue)
child->parent->skin = skin;
child->parent->name = RmGetMeasureName(rm);
child->parent->ownerChild = child;
child->parent->hwnd = RmGetSkinWindow(rm);
child->parent->rm = rm;
g_ParentMeasures.push_back(child->parent);
}
@ -495,20 +509,33 @@ PLUGIN_EXPORT void ExecuteBang(void* data, LPCWSTR args)
return;
}
auto runFile = [&](std::wstring& filename, std::wstring& dir)
auto runFile = [&](std::wstring fileName, std::wstring dir, bool isProperty) -> void
{
std::wstring file = dir + filename;
// Display computer system properties
if (isProperty && dir.empty() && fileName.empty())
{
WinExec(g_SysProperties.c_str(), SW_SHOWNORMAL);
return;
}
CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
std::wstring file = dir + fileName;
SHELLEXECUTEINFO si = {sizeof(SHELLEXECUTEINFO)};
si.lpVerb = nullptr;
si.lpVerb = isProperty ? L"properties" : nullptr;
si.lpFile = file.c_str();
si.nShow = SW_SHOWNORMAL;
si.lpDirectory = dir.c_str();
si.fMask = SEE_MASK_FLAG_NO_UI | SEE_MASK_ASYNCOK;
if (isProperty) si.fMask |= SEE_MASK_INVOKEIDLIST;
ShellExecuteEx(&si);
CoUninitialize();
};
// Parent only commands
if (parent->ownerChild == child)
{
if ((int)parent->files.size() > parent->count)
@ -573,57 +600,110 @@ PLUGIN_EXPORT void ExecuteBang(void* data, LPCWSTR args)
parent->needsIcons = true;
parent->needsUpdating = true;
}
else if (_wcsicmp(args, L"CONTEXTMENU") == 0)
{
if (!ShowContextMenu(parent->hwnd, parent->path))
{
RmLogF(parent->rm, LOG_ERROR, L"Cannot open context menu for \"%s\"", parent->path.c_str());
}
}
else if (_wcsicmp(args, L"PROPERTIES") == 0)
{
runFile(L"", parent->path, true);
}
else if (parent->recursiveType != RECURSIVE_FULL && _wcsicmp(args, L"PREVIOUSFOLDER") == 0)
{
std::vector<std::wstring> path = Tokenize(parent->path, L"\\");
if (path.size() < 2)
{
parent->path.clear();
}
else
{
parent->path.clear();
for (size_t i = 0; i < path.size() - 1; ++i)
{
parent->path += path[i];
parent->path += L"\\";
}
}
GetParentFolder(parent->path);
parent->indexOffset = 0;
parent->needsUpdating = true;
parent->needsIcons = true;
}
else
{
// Special commands that allow for a user defined file/folder
std::wstring arg = args;
std::wstring::size_type pos = arg.find_first_of(L' ');
if (pos != std::wstring::npos)
{
arg = arg.substr(pos);
if (!arg.empty())
{
arg.erase(0, 1); // Skip the space
if (_wcsnicmp(args, L"CONTEXTMENU", 11) == 0)
{
if (!ShowContextMenu(parent->hwnd, arg))
{
RmLogF(parent->rm, LOG_ERROR, L"Cannot open context menu for \"%s\"", arg.c_str());
}
}
else if (_wcsnicmp(args, L"PROPERTIES", 10) == 0)
{
runFile(arg, L"", true);
}
else
{
RmLogF(parent->rm, LOG_WARNING, L"!CommandMeasure: Unknown path: %s", arg.c_str());
}
}
else
{
RmLogF(parent->rm, LOG_WARNING, L"!CommandMeasure: Unknown command: %s", args);
}
}
}
LeaveCriticalSection(&g_CriticalSection);
return;
}
// Child only commands
int trueIndex = child->ignoreCount ? child->index : ((child->index % parent->count) + parent->indexOffset);
if (!parent->files.empty() && trueIndex >= 0 && trueIndex < (int)parent->files.size())
{
if (_wcsicmp(args, L"OPEN") == 0)
{
runFile(parent->files[trueIndex].fileName, parent->files[trueIndex].path);
runFile(parent->files[trueIndex].fileName, parent->files[trueIndex].path, false);
}
else if (_wcsicmp(args, L"CONTEXTMENU") == 0)
{
std::wstring path = parent->files[trueIndex].path;
std::wstring fileName = parent->files[trueIndex].fileName;
if (_wcsicmp(fileName.c_str(), L"..") == 0)
{
path = parent->path;
GetParentFolder(path);
fileName = L"";
}
path.append(fileName);
if (!ShowContextMenu(parent->hwnd, path))
{
RmLogF(parent->rm, LOG_ERROR, L"Cannot open context menu for \"%s\"", path.c_str());
}
}
else if (_wcsicmp(args, L"PROPERTIES") == 0)
{
std::wstring path = parent->files[trueIndex].path;
std::wstring fileName = parent->files[trueIndex].fileName;
if (_wcsicmp(fileName.c_str(), L"..") == 0)
{
path = parent->path;
GetParentFolder(path);
fileName = L"";
}
runFile(fileName, path, true);
}
else if (parent->recursiveType != RECURSIVE_FULL && _wcsicmp(args, L"FOLLOWPATH") == 0)
{
if (_wcsicmp(parent->files[trueIndex].fileName.c_str(), L"..") == 0)
{
std::vector<std::wstring> path = Tokenize(parent->path, L"\\");
if (path.size() < 2)
{
parent->path.clear();
}
else
{
parent->path.clear();
for (size_t i = 0; i < path.size() - 1; ++i)
{
parent->path += path[i];
parent->path += L"\\";
}
}
GetParentFolder(parent->path);
parent->indexOffset = 0;
parent->needsUpdating = true;
@ -643,16 +723,20 @@ PLUGIN_EXPORT void ExecuteBang(void* data, LPCWSTR args)
}
else
{
runFile(parent->files[trueIndex].fileName, parent->files[trueIndex].path);
runFile(parent->files[trueIndex].fileName, parent->files[trueIndex].path, false);
}
}
else
{
RmLogF(parent->rm, LOG_WARNING, L"!CommandMeasure: Unknown command: %s", args);
}
LeaveCriticalSection(&g_CriticalSection);
return;
}
LeaveCriticalSection(&g_CriticalSection);
RmLog(LOG_ERROR, L"FileView.dll: Invalid command");
RmLogF(parent->rm, LOG_WARNING, L"!CommandMeasure: Unknown command: %s", args);
}
PLUGIN_EXPORT void Finalize(void* data)

View File

@ -114,6 +114,8 @@ struct ParentMeasure
int indexOffset;
HANDLE thread;
void* rm;
HWND hwnd;
void* skin;
LPCWSTR name;
ChildMeasure* ownerChild;
@ -139,6 +141,8 @@ struct ParentMeasure
skin(nullptr),
name(),
ownerChild(nullptr),
rm(),
hwnd(),
thread(nullptr),
fileCount(0),
folderCount(0),
@ -188,6 +192,81 @@ std::vector<std::wstring> Tokenize(const std::wstring& str, const std::wstring&
return tokens;
}
void GetParentFolder(std::wstring& path)
{
std::vector<std::wstring> tokens = Tokenize(path, L"\\");
if (tokens.size() < 2)
{
path.clear();
}
else
{
path.clear();
for (size_t i = 0; i < tokens.size() - 1; ++i)
{
path += tokens[i];
path += L"\\";
}
}
}
bool ShowContextMenu(HWND hwnd, std::wstring& path)
{
POINT pos;
GetCursorPos(&pos);
// If the mouse is outside of the boundaries of
// the skin, use the upper-left corner of the skin
RECT rect;
GetWindowRect(hwnd, &rect);
if (pos.x < rect.left || pos.x > rect.right ||
pos.y < rect.top || pos.y > rect.bottom)
{
pos.x = rect.left;
pos.y = rect.top;
}
ITEMIDLIST* id = nullptr;
HRESULT result = SHParseDisplayName(path.c_str(), nullptr, &id, 0, nullptr);
if (!SUCCEEDED(result) || !id)
return false;
IShellFolder* iFolder = nullptr;
LPCITEMIDLIST idChild = nullptr;
result = SHBindToParent(id, IID_IShellFolder, (void**)&iFolder, &idChild);
if (!SUCCEEDED(result) || !iFolder)
return false;
IContextMenu* iMenu = nullptr;
result = iFolder->GetUIObjectOf(hwnd, 1, (const ITEMIDLIST **)&idChild, IID_IContextMenu, nullptr, (void**)&iMenu);
if (!SUCCEEDED(result) || !iFolder)
return false;
HMENU hMenu = CreatePopupMenu();
if (!hMenu)
return false;
if (SUCCEEDED(iMenu->QueryContextMenu(hMenu, 0, 1, 0x7FFF, CMF_NORMAL)))
{
int iCmd = TrackPopupMenuEx(hMenu, TPM_RETURNCMD, pos.x, pos.y, hwnd, NULL);
if (iCmd > 0)
{
CMINVOKECOMMANDINFOEX info = { 0 };
info.cbSize = sizeof(info);
info.fMask = CMIC_MASK_UNICODE | CMIC_MASK_ASYNCOK;
info.hwnd = hwnd;
info.lpVerb = MAKEINTRESOURCEA(iCmd - 1);
info.lpVerbW = MAKEINTRESOURCEW(iCmd - 1);
info.nShow = SW_SHOWNORMAL;
iMenu->InvokeCommand((LPCMINVOKECOMMANDINFO)&info);
}
}
DestroyMenu(hMenu);
return true;
}
/*std::wstring UINT64_To_String(UINT64 value)
{
std::wstring result;

View File

@ -7,7 +7,7 @@
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION 2,0,2,0
FILEVERSION 2,0,3,0
PRODUCTVERSION PRODUCTVER
FILEFLAGSMASK 0x17L
#ifdef _DEBUG
@ -23,7 +23,7 @@ VS_VERSION_INFO VERSIONINFO
{
BLOCK "040904E4"
{
VALUE "FileVersion", "2.0.2.0"
VALUE "FileVersion", "2.0.3.0"
VALUE "LegalCopyright", "© 2012 - Brian Ferguson"
VALUE "ProductName", "Rainmeter"
#ifdef _WIN64

View File

@ -36,6 +36,11 @@
<ItemGroup>
<ResourceCompile Include="PluginFileView.rc" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Common\Common.vcxproj">
<Project>{19312085-aa51-4bd6-be92-4b6098cca539}</Project>
</ProjectReference>
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>

View File

@ -26,6 +26,7 @@
#include <process.h>
#include <Shellapi.h>
#include <Shlwapi.h>
#include <ShlObj.h>
// STL
#include <string>