From 05d256ced3eb0fb00ae6c6ccb7e826ee2e45b08b Mon Sep 17 00:00:00 2001 From: Brian Ferguson Date: Fri, 7 Feb 2014 12:34:16 -0700 Subject: [PATCH] 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 --- Plugins/PluginFileView/PluginFileView.cpp | 154 ++++++++++++++---- Plugins/PluginFileView/PluginFileView.h | 79 +++++++++ Plugins/PluginFileView/PluginFileView.rc | 4 +- Plugins/PluginFileView/PluginFileView.vcxproj | 5 + Plugins/PluginFileView/StdAfx.h | 1 + 5 files changed, 206 insertions(+), 37 deletions(-) diff --git a/Plugins/PluginFileView/PluginFileView.cpp b/Plugins/PluginFileView/PluginFileView.cpp index 8f7bec5d..2dbb7670 100644 --- a/Plugins/PluginFileView/PluginFileView.cpp +++ b/Plugins/PluginFileView/PluginFileView.cpp @@ -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 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 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 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) diff --git a/Plugins/PluginFileView/PluginFileView.h b/Plugins/PluginFileView/PluginFileView.h index eba81a55..82a441ff 100644 --- a/Plugins/PluginFileView/PluginFileView.h +++ b/Plugins/PluginFileView/PluginFileView.h @@ -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 Tokenize(const std::wstring& str, const std::wstring& return tokens; } +void GetParentFolder(std::wstring& path) +{ + std::vector 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; diff --git a/Plugins/PluginFileView/PluginFileView.rc b/Plugins/PluginFileView/PluginFileView.rc index 0ab70d9f..2a71669e 100644 --- a/Plugins/PluginFileView/PluginFileView.rc +++ b/Plugins/PluginFileView/PluginFileView.rc @@ -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 diff --git a/Plugins/PluginFileView/PluginFileView.vcxproj b/Plugins/PluginFileView/PluginFileView.vcxproj index 0f4af991..fdf97cd3 100644 --- a/Plugins/PluginFileView/PluginFileView.vcxproj +++ b/Plugins/PluginFileView/PluginFileView.vcxproj @@ -36,6 +36,11 @@ + + + {19312085-aa51-4bd6-be92-4b6098cca539} + + diff --git a/Plugins/PluginFileView/StdAfx.h b/Plugins/PluginFileView/StdAfx.h index 09d50e79..e47de8d0 100644 --- a/Plugins/PluginFileView/StdAfx.h +++ b/Plugins/PluginFileView/StdAfx.h @@ -26,6 +26,7 @@ #include #include #include +#include // STL #include