[SHELL32] Implement SHBrowseForFolder items contextmenu (#6952)

* [SHELL32] Implement SHBrowseForFolder items contextmenu

CORE-16944 CORE-19597
This commit is contained in:
Whindmar Saksit 2024-05-30 17:53:44 +02:00 committed by GitHub
parent 6d7648d723
commit 3da9e7e251
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
41 changed files with 250 additions and 0 deletions

View file

@ -80,6 +80,16 @@ static const struct _StaticInvokeCommandMap_
{ "moveto", FCIDM_SHVIEW_MOVETO },
};
UINT MapVerbToDfmCmd(_In_ LPCSTR verba)
{
for (UINT i = 0; i < _countof(g_StaticInvokeCmdMap); ++i)
{
if (!lstrcmpiA(g_StaticInvokeCmdMap[i].szStringVerb, verba))
return (int)g_StaticInvokeCmdMap[i].DfmCmd;
}
return 0;
}
class CDefaultContextMenu :
public CComObjectRootEx<CComMultiThreadModelNoCS>,
public IContextMenu3,

View file

@ -25,6 +25,7 @@ struct BrFolder
LAYOUT_DATA* layout; // Filled by LayoutInit, used by LayoutUpdate
SIZE szMin;
ULONG hChangeNotify; // Change notification handle
IContextMenu* pContextMenu; // Active context menu
};
struct BrItemData
@ -811,6 +812,87 @@ BrFolder_OnCommand(BrFolder *info, UINT id)
}
}
static void
GetTreeViewItemContextMenuPos(HWND hWnd, HTREEITEM hItem, POINT *ppt)
{
RECT rc;
if (TreeView_GetItemRect(hWnd, hItem, &rc, TRUE))
{
ppt->x = (rc.left + rc.right) / 2;
ppt->y = (rc.top + rc.bottom) / 2;
}
ClientToScreen(hWnd, ppt);
}
static void
BrFolder_OnContextMenu(BrFolder &info, LPARAM lParam)
{
enum { IDC_TOGGLE = 1, ID_FIRSTCMD, ID_LASTCMD = 0xffff };
HTREEITEM hSelected = TreeView_GetSelection(info.hwndTreeView);
CMINVOKECOMMANDINFOEX ici = { sizeof(ici), CMIC_MASK_PTINVOKE, info.hWnd };
ici.fMask |= (GetKeyState(VK_SHIFT) < 0) ? CMIC_MASK_SHIFT_DOWN : 0;
ici.fMask |= (GetKeyState(VK_CONTROL) < 0) ? CMIC_MASK_CONTROL_DOWN : 0;
ici.nShow = SW_SHOW;
ici.ptInvoke.x = GET_X_LPARAM(lParam);
ici.ptInvoke.y = GET_Y_LPARAM(lParam);
if ((int)lParam == -1) // Keyboard
{
GetTreeViewItemContextMenuPos(info.hwndTreeView, hSelected, &ici.ptInvoke);
}
else // Get correct item for right-click on not current item
{
TVHITTESTINFO hti = { ici.ptInvoke };
ScreenToClient(info.hwndTreeView, &hti.pt);
hSelected = TreeView_HitTest(info.hwndTreeView, &hti);
}
BrItemData *item = BrFolder_GetItemData(&info, hSelected);
if (!item)
return; // Not on an item
TV_ITEM tvi;
tvi.mask = TVIF_STATE | TVIF_CHILDREN;
tvi.stateMask = TVIS_EXPANDED;
tvi.hItem = hSelected;
TreeView_GetItem(info.hwndTreeView, &tvi);
CComPtr<IContextMenu> pcm;
HRESULT hr = item->lpsfParent->GetUIObjectOf(info.hWnd, 1, &item->pidlChild,
IID_IContextMenu, NULL, (void**)&pcm);
if (FAILED(hr))
return;
HMENU hMenu = CreatePopupMenu();
if (!hMenu)
return;
info.pContextMenu = pcm;
UINT cmf = ((ici.fMask & CMIC_MASK_SHIFT_DOWN) ? CMF_EXTENDEDVERBS : 0) | CMF_CANRENAME;
hr = pcm->QueryContextMenu(hMenu, 0, ID_FIRSTCMD, ID_LASTCMD, CMF_NODEFAULT | cmf);
if (hr > 0)
_InsertMenuItemW(hMenu, 0, TRUE, 0, MFT_SEPARATOR, NULL, 0);
_InsertMenuItemW(hMenu, 0, TRUE, IDC_TOGGLE, MFT_STRING,
MAKEINTRESOURCEW((tvi.state & TVIS_EXPANDED) ? IDS_COLLAPSE : IDS_EXPAND),
MFS_DEFAULT | (tvi.cChildren ? 0 : MFS_GRAYED));
UINT cmd = TrackPopupMenuEx(hMenu, TPM_RETURNCMD, ici.ptInvoke.x, ici.ptInvoke.y, info.hWnd, NULL);
ici.lpVerb = MAKEINTRESOURCEA(cmd - ID_FIRSTCMD);
if (cmd == IDC_TOGGLE)
{
TreeView_SelectItem(info.hwndTreeView, hSelected);
TreeView_Expand(info.hwndTreeView, hSelected, TVE_TOGGLE);
}
else if (cmd != 0 && GetDfmCmd(pcm, ici.lpVerb) == DFM_CMD_RENAME)
{
TreeView_SelectItem(info.hwndTreeView, hSelected);
TreeView_EditLabel(info.hwndTreeView, hSelected);
}
else if (cmd != 0)
{
pcm->InvokeCommand((CMINVOKECOMMANDINFO*)&ici);
}
info.pContextMenu = NULL;
DestroyMenu(hMenu);
}
static BOOL
BrFolder_OnSetExpandedPidl(BrFolder *info, LPITEMIDLIST pidlSelection, HTREEITEM *phItem)
{
@ -1040,6 +1122,17 @@ BrFolderDlgProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
if (!info)
return 0;
if (info->pContextMenu)
{
LRESULT result;
if (SHForwardContextMenuMsg(info->pContextMenu, uMsg, wParam,
lParam, &result, TRUE) == S_OK)
{
SetWindowLongPtr(hWnd, DWLP_MSGRESULT, result);
return TRUE;
}
}
switch (uMsg)
{
case WM_NOTIFY:
@ -1050,6 +1143,11 @@ BrFolderDlgProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
BrFolder_OnCommand(info, wParam);
break;
case WM_CONTEXTMENU:
if (info->hwndTreeView == (HWND)wParam)
BrFolder_OnContextMenu(*info, lParam);
break;
case WM_GETMINMAXINFO:
((LPMINMAXINFO)lParam)->ptMinTrackSize.x = info->szMin.cx;
((LPMINMAXINFO)lParam)->ptMinTrackSize.y = info->szMin.cy;

View file

@ -925,6 +925,9 @@ BEGIN
IDS_CANTDISCONNECT "Unable to disconnect (Error Code: %lu)."
IDS_NONE "(None)"
IDS_EXPAND "Exp&and"
IDS_COLLAPSE "Coll&apse"
/* Friendly File Type Names */
IDS_DIRECTORY "Папка"
IDS_BAT_FILE "Пакетен файл на РеактОС"

View file

@ -925,6 +925,9 @@ BEGIN
IDS_CANTDISCONNECT "Unable to disconnect (Error Code: %lu)."
IDS_NONE "(None)"
IDS_EXPAND "Exp&and"
IDS_COLLAPSE "Coll&apse"
/* Friendly File Type Names */
IDS_DIRECTORY "Folder"
IDS_BAT_FILE "ReactOS Batch File"

View file

@ -933,6 +933,9 @@ BEGIN
IDS_CANTDISCONNECT "Unable to disconnect (Error Code: %lu)."
IDS_NONE "(None)"
IDS_EXPAND "Exp&and"
IDS_COLLAPSE "Coll&apse"
/* Friendly File Type Names */
IDS_DIRECTORY "Složka"
IDS_BAT_FILE "ReactOS dávkový soubor"

View file

@ -932,6 +932,9 @@ BEGIN
IDS_CANTDISCONNECT "Unable to disconnect (Error Code: %lu)."
IDS_NONE "(None)"
IDS_EXPAND "Exp&and"
IDS_COLLAPSE "Coll&apse"
/* Friendly File Type Names */
IDS_DIRECTORY "Folder"
IDS_BAT_FILE "ReactOS Batch File"

View file

@ -926,6 +926,9 @@ BEGIN
IDS_CANTDISCONNECT "Trennen nicht möglich (Fehlercode: %lu)."
IDS_NONE "(Leer)"
IDS_EXPAND "Exp&and"
IDS_COLLAPSE "Coll&apse"
/* Friendly File Type Names */
IDS_DIRECTORY "Dateiordner"
IDS_BAT_FILE "ReactOS-Stapelverarbeitungsdatei"

View file

@ -925,6 +925,9 @@ BEGIN
IDS_CANTDISCONNECT "Unable to disconnect (Error Code: %lu)."
IDS_NONE "(None)"
IDS_EXPAND "Exp&and"
IDS_COLLAPSE "Coll&apse"
/* Friendly File Type Names */
IDS_DIRECTORY "Folder"
IDS_BAT_FILE "ReactOS Batch File"

View file

@ -925,6 +925,9 @@ BEGIN
IDS_CANTDISCONNECT "Unable to disconnect (Error Code: %lu)."
IDS_NONE "(None)"
IDS_EXPAND "Exp&and"
IDS_COLLAPSE "Coll&apse"
/* Friendly File Type Names */
IDS_DIRECTORY "Folder"
IDS_BAT_FILE "ReactOS Batch File"

View file

@ -925,6 +925,9 @@ BEGIN
IDS_CANTDISCONNECT "Unable to disconnect (Error Code: %lu)."
IDS_NONE "(None)"
IDS_EXPAND "Exp&and"
IDS_COLLAPSE "Coll&apse"
/* Friendly File Type Names */
IDS_DIRECTORY "Folder"
IDS_BAT_FILE "ReactOS Batch File"

View file

@ -934,6 +934,9 @@ BEGIN
IDS_CANTDISCONNECT "No se pudo desconectar (Código de Error: %lu)."
IDS_NONE "(Nada)"
IDS_EXPAND "Exp&and"
IDS_COLLAPSE "Coll&apse"
/* Friendly File Type Names */
IDS_DIRECTORY "Directorio"
IDS_BAT_FILE "Archivo por lotes de ReactOS"

View file

@ -932,6 +932,9 @@ BEGIN
IDS_CANTDISCONNECT "Ei saa lahti ühendada (Veateade: %lu)."
IDS_NONE "(None)"
IDS_EXPAND "Exp&and"
IDS_COLLAPSE "Coll&apse"
/* Friendly File Type Names */
IDS_DIRECTORY "Kaust"
IDS_BAT_FILE "ReactOS Batch Fail"

View file

@ -930,6 +930,9 @@ BEGIN
IDS_CANTDISCONNECT "Ezin izan da deskonektatu (Error kodea: %lu)."
IDS_NONE "(Ezer)"
IDS_EXPAND "Exp&and"
IDS_COLLAPSE "Coll&apse"
/* Friendly File Type Names */
IDS_DIRECTORY "Direktorioa"
IDS_BAT_FILE "ReactOS lote fitxaetgiak"

View file

@ -925,6 +925,9 @@ BEGIN
IDS_CANTDISCONNECT "Unable to disconnect (Error Code: %lu)."
IDS_NONE "(None)"
IDS_EXPAND "Exp&and"
IDS_COLLAPSE "Coll&apse"
/* Friendly File Type Names */
IDS_DIRECTORY "Folder"
IDS_BAT_FILE "ReactOS Batch File"

View file

@ -925,6 +925,9 @@ BEGIN
IDS_CANTDISCONNECT "Impossible de déconnecter (code d'erreur : %lu)."
IDS_NONE "(aucun)"
IDS_EXPAND "Exp&and"
IDS_COLLAPSE "Coll&apse"
/* Friendly File Type Names */
IDS_DIRECTORY "Dossier"
IDS_BAT_FILE "Fichier Batch ReactOS"

View file

@ -932,6 +932,9 @@ BEGIN
IDS_CANTDISCONNECT "Unable to disconnect (Error Code: %lu)."
IDS_NONE "(None)"
IDS_EXPAND "Exp&and"
IDS_COLLAPSE "Coll&apse"
/* Friendly File Type Names */
IDS_DIRECTORY "תיקיה"
IDS_BAT_FILE "קובת אצווה של ReactOS"

View file

@ -927,6 +927,9 @@ BEGIN
IDS_CANTDISCONNECT "डिस्कनेक्ट करने में असमर्थ (एरर कोड: %lu)."
IDS_NONE "(None)"
IDS_EXPAND "Exp&and"
IDS_COLLAPSE "Coll&apse"
/* Friendly File Type Names */
IDS_DIRECTORY "फोल्डर"
IDS_BAT_FILE "रिऐक्ट ओएस बैच फ़ाइल"

View file

@ -924,6 +924,9 @@ BEGIN
IDS_CANTDISCONNECT "Nem sikerült a szétkapcsolódás (hibakód: %lu)."
IDS_NONE "(Nincs)"
IDS_EXPAND "Exp&and"
IDS_COLLAPSE "Coll&apse"
/* Friendly File Type Names */
IDS_DIRECTORY "Mappa"
IDS_BAT_FILE "ReactOS kötegfájl"

View file

@ -922,6 +922,9 @@ BEGIN
IDS_CANTDISCONNECT "Tidak bisa memutuskan (Kode Kesalahan: %lu)."
IDS_NONE "(Tidak ada)"
IDS_EXPAND "Exp&and"
IDS_COLLAPSE "Coll&apse"
/* Friendly File Type Names */
IDS_DIRECTORY "Folder"
IDS_BAT_FILE "Berkas Batch ReactOS"

View file

@ -925,6 +925,9 @@ BEGIN
IDS_CANTDISCONNECT "Unable to disconnect (Error Code: %lu)."
IDS_NONE "(None)"
IDS_EXPAND "Exp&and"
IDS_COLLAPSE "Coll&apse"
/* Friendly File Type Names */
IDS_DIRECTORY "Cartella"
IDS_BAT_FILE "File Batch ReactOS"

View file

@ -922,6 +922,9 @@ BEGIN
IDS_CANTDISCONNECT "接続を切れませんでした (エラーコード: %lu)."
IDS_NONE "(なし)"
IDS_EXPAND "Exp&and"
IDS_COLLAPSE "Coll&apse"
/* Friendly File Type Names */
IDS_DIRECTORY "フォルダ"
IDS_BAT_FILE "ReactOS バッチ ファイル"

View file

@ -932,6 +932,9 @@ BEGIN
IDS_CANTDISCONNECT "Unable to disconnect (Error Code: %lu)."
IDS_NONE "(None)"
IDS_EXPAND "Exp&and"
IDS_COLLAPSE "Coll&apse"
/* Friendly File Type Names */
IDS_DIRECTORY "폴더"
IDS_BAT_FILE "ReactOS Batch File"

View file

@ -925,6 +925,9 @@ BEGIN
IDS_CANTDISCONNECT "Unable to disconnect (Error Code: %lu)."
IDS_NONE "(None)"
IDS_EXPAND "Exp&and"
IDS_COLLAPSE "Coll&apse"
/* Friendly File Type Names */
IDS_DIRECTORY "Folder"
IDS_BAT_FILE "ReactOS Batch File"

View file

@ -925,6 +925,9 @@ BEGIN
IDS_CANTDISCONNECT "Unable to disconnect (Error Code: %lu)."
IDS_NONE "(None)"
IDS_EXPAND "Exp&and"
IDS_COLLAPSE "Coll&apse"
/* Friendly File Type Names */
IDS_DIRECTORY "Mappe"
IDS_BAT_FILE "ReactOS Batch fil"

View file

@ -934,6 +934,9 @@ BEGIN
IDS_CANTDISCONNECT "Nie można odłączyć (kod błędu: %lu)."
IDS_NONE "(brak)"
IDS_EXPAND "Exp&and"
IDS_COLLAPSE "Coll&apse"
/* Friendly File Type Names */
IDS_DIRECTORY "Folder"
IDS_BAT_FILE "Plik wsadowy ReactOS"

View file

@ -925,6 +925,9 @@ BEGIN
IDS_CANTDISCONNECT "Unable to disconnect (Error Code: %lu)."
IDS_NONE "(None)"
IDS_EXPAND "Exp&and"
IDS_COLLAPSE "Coll&apse"
/* Friendly File Type Names */
IDS_DIRECTORY "Pasta"
IDS_BAT_FILE "ReactOS Batch File"

View file

@ -924,6 +924,9 @@ BEGIN
IDS_CANTDISCONNECT "Não foi possível desconectar (Código de erro: %lu)."
IDS_NONE "(Nenhum)"
IDS_EXPAND "Exp&and"
IDS_COLLAPSE "Coll&apse"
/* Friendly File Type Names */
IDS_DIRECTORY "Pasta"
IDS_BAT_FILE "Ficheiro em lote do ReactOS"

View file

@ -933,6 +933,9 @@ BEGIN
IDS_CANTDISCONNECT "Imposibil de deconectat (Cod de eroare: %lu)."
IDS_NONE "(Nimic)"
IDS_EXPAND "Exp&and"
IDS_COLLAPSE "Coll&apse"
/* Friendly File Type Names */
IDS_DIRECTORY "Folder"
IDS_BAT_FILE "Fişier de comenzi ReactOS"

View file

@ -934,6 +934,9 @@ BEGIN
IDS_CANTDISCONNECT "Не удалось отсоединить сетевой диск (код ошибки: %lu)."
IDS_NONE "(None)"
IDS_EXPAND "Exp&and"
IDS_COLLAPSE "Coll&apse"
/* Friendly File Type Names */
IDS_DIRECTORY "Папка с файлами"
IDS_BAT_FILE "Пакетный файл ReactOS"

View file

@ -925,6 +925,9 @@ BEGIN
IDS_CANTDISCONNECT "Unable to disconnect (Error Code: %lu)."
IDS_NONE "(None)"
IDS_EXPAND "Exp&and"
IDS_COLLAPSE "Coll&apse"
/* Friendly File Type Names */
IDS_DIRECTORY "Priečinok"
IDS_BAT_FILE "Dávkový súbor systému ReactOS"

View file

@ -925,6 +925,9 @@ BEGIN
IDS_CANTDISCONNECT "Unable to disconnect (Error Code: %lu)."
IDS_NONE "(None)"
IDS_EXPAND "Exp&and"
IDS_COLLAPSE "Coll&apse"
/* Friendly File Type Names */
IDS_DIRECTORY "Folder"
IDS_BAT_FILE "ReactOS Batch File"

View file

@ -932,6 +932,9 @@ BEGIN
IDS_CANTDISCONNECT "Unable to disconnect (Error Code: %lu)."
IDS_NONE "(None)"
IDS_EXPAND "Exp&and"
IDS_COLLAPSE "Coll&apse"
/* Friendly File Type Names */
IDS_DIRECTORY "Dosje"
IDS_BAT_FILE "ReactOS Dokument Batch"

View file

@ -925,6 +925,9 @@ BEGIN
IDS_CANTDISCONNECT "Unable to disconnect (Error Code: %lu)."
IDS_NONE "(None)"
IDS_EXPAND "Exp&and"
IDS_COLLAPSE "Coll&apse"
/* Friendly File Type Names */
IDS_DIRECTORY "Mapp"
IDS_BAT_FILE "ReactOS Batch-fil"

View file

@ -934,6 +934,9 @@ BEGIN
IDS_CANTDISCONNECT "Bağlanılamıyor (Hata Kodu: %lu)."
IDS_NONE "(Hiçbir Şey)"
IDS_EXPAND "Exp&and"
IDS_COLLAPSE "Coll&apse"
/* Friendly File Type Names */
IDS_DIRECTORY "Dizin"
IDS_BAT_FILE "ReactOS Toplu İş Dosyası"

View file

@ -925,6 +925,9 @@ BEGIN
IDS_CANTDISCONNECT "Не вдалось відключити (код помилки: %lu)."
IDS_NONE "(None)"
IDS_EXPAND "Exp&and"
IDS_COLLAPSE "Coll&apse"
/* Friendly File Type Names */
IDS_DIRECTORY "Папка"
IDS_BAT_FILE "Пакетний файл ReactOS"

View file

@ -935,6 +935,9 @@ BEGIN
IDS_CANTDISCONNECT "无法断开连接(错误代码:%lu。"
IDS_NONE "(None)"
IDS_EXPAND "Exp&and"
IDS_COLLAPSE "Coll&apse"
/* Friendly File Type Names */
IDS_DIRECTORY "文件夹"
IDS_BAT_FILE "ReactOS 批处理文件"

View file

@ -933,6 +933,9 @@ BEGIN
IDS_CANTDISCONNECT "無法中斷(錯誤碼:%lu。"
IDS_NONE "(None)"
IDS_EXPAND "Exp&and"
IDS_COLLAPSE "Coll&apse"
/* Friendly File Type Names */
IDS_DIRECTORY "資料夾"
IDS_BAT_FILE "ReactOS 批次文件"

View file

@ -934,6 +934,9 @@ BEGIN
IDS_CANTDISCONNECT "無法中斷。(錯誤碼:%lu"
IDS_NONE "(None)"
IDS_EXPAND "Exp&and"
IDS_COLLAPSE "Coll&apse"
/* Friendly File Type Names */
IDS_DIRECTORY "資料夾"
IDS_BAT_FILE "ReactOS 批次文件"

View file

@ -146,6 +146,10 @@ HRESULT
SHELL32_ShowPropertiesDialog(IDataObject *pdtobj);
HRESULT
SHELL32_DefaultContextMenuCallBack(IShellFolder *psf, IDataObject *pdo, UINT msg);
UINT
MapVerbToDfmCmd(_In_ LPCSTR verba);
UINT
GetDfmCmd(_In_ IContextMenu *pCM, _In_ LPCSTR verba);
#define SHELL_ExecuteControlPanelCPL(hwnd, cpl) SHRunControlPanel((cpl), (hwnd))
// CStubWindow32 --- The owner window of file property sheets.

View file

@ -153,6 +153,9 @@
#define IDS_CANTDISCONNECT 160
#define IDS_NONE 161
#define IDS_EXPAND 170
#define IDS_COLLAPSE 171
/* Friendly File Type Names */
#define IDS_DIRECTORY 200
#define IDS_BAT_FILE 201

View file

@ -1294,3 +1294,30 @@ SHGetRealIDL(
return hr;
}
static HRESULT
GetCommandStringA(_In_ IContextMenu *pCM, _In_ UINT_PTR Id, _In_ UINT GCS, _Out_writes_(cchMax) LPSTR Buf, _In_ UINT cchMax)
{
HRESULT hr = pCM->GetCommandString(Id, GCS & ~GCS_UNICODE, NULL, Buf, cchMax);
if (FAILED(hr))
{
WCHAR buf[MAX_PATH];
hr = pCM->GetCommandString(Id, GCS | GCS_UNICODE, NULL, (LPSTR)buf, _countof(buf));
if (SUCCEEDED(hr))
hr = SHUnicodeToAnsi(buf, Buf, cchMax) > 0 ? S_OK : E_FAIL;
}
return hr;
}
UINT
GetDfmCmd(_In_ IContextMenu *pCM, _In_ LPCSTR verba)
{
CHAR buf[MAX_PATH];
if (IS_INTRESOURCE(verba))
{
if (FAILED(GetCommandStringA(pCM, LOWORD(verba), GCS_VERB, buf, _countof(buf))))
return 0;
verba = buf;
}
return MapVerbToDfmCmd(verba); // Returns DFM_CMD_* or 0
}