[SHELL32] SHCreateDefaultContextMenu: Pass HWND to callback (#6764)
[reactos.git] / dll / shellext / fontext / CFontMenu.cpp
1 /*
2 * PROJECT: ReactOS Font Shell Extension
3 * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
4 * PURPOSE: CFontMenu implementation
5 * COPYRIGHT: Copyright 2019,2020 Mark Jansen (mark.jansen@reactos.org)
6 */
7
8 #include "precomp.h"
9
10 WINE_DEFAULT_DEBUG_CHANNEL(fontext);
11
12 static CLIPFORMAT g_cfHIDA;
13
14 HRESULT _GetCidlFromDataObject(IDataObject *pDataObject, CIDA** ppcida)
15 {
16 if (g_cfHIDA == NULL)
17 {
18 g_cfHIDA = (CLIPFORMAT)RegisterClipboardFormatW(CFSTR_SHELLIDLIST);
19 }
20
21 FORMATETC fmt = { g_cfHIDA, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
22 STGMEDIUM medium;
23
24 HRESULT hr = pDataObject->GetData(&fmt, &medium);
25 if (FAILED_UNEXPECTEDLY(hr))
26 return hr;
27
28 LPVOID lpSrc = GlobalLock(medium.hGlobal);
29 SIZE_T cbSize = GlobalSize(medium.hGlobal);
30
31 *ppcida = (CIDA *)::CoTaskMemAlloc(cbSize);
32 if (*ppcida)
33 {
34 memcpy(*ppcida, lpSrc, cbSize);
35 hr = S_OK;
36 }
37 else
38 {
39 hr = E_FAIL;
40 }
41 ReleaseStgMedium(&medium);
42 return hr;
43 }
44
45 const char* DFM_TO_STR(UINT uMsg)
46 {
47 switch(uMsg)
48 {
49 case DFM_MERGECONTEXTMENU: return "DFM_MERGECONTEXTMENU";
50 case DFM_INVOKECOMMAND: return "DFM_INVOKECOMMAND";
51 case DFM_MODIFYQCMFLAGS: return "DFM_MODIFYQCMFLAGS";
52 case DFM_MERGECONTEXTMENU_TOP: return "DFM_MERGECONTEXTMENU_TOP";
53 case DFM_MERGECONTEXTMENU_BOTTOM: return "DFM_MERGECONTEXTMENU_BOTTOM";
54 case DFM_GETHELPTEXTW: return "DFM_GETHELPTEXTW";
55 case DFM_GETVERBW: return "DFM_GETVERBW";
56 case DFM_GETVERBA: return "DFM_GETVERBA";
57 case DFM_WM_INITMENUPOPUP: return "DFM_WM_INITMENUPOPUP";
58 case DFM_INVOKECOMMANDEX: return "DFM_INVOKECOMMANDEX";
59 case DFM_GETDEFSTATICID: return "DFM_GETDEFSTATICID";
60 case 3: return "MENU_BEGIN";
61 case 4: return "MENU_END";
62 default: return "";
63 }
64 }
65
66
67 static void RunFontViewer(HWND hwnd, const FontPidlEntry* fontEntry)
68 {
69 WCHAR FontViewerPath[MAX_PATH] = L"%SystemRoot%\\System32\\fontview.exe";
70 WCHAR FontPathArg[MAX_PATH + 3];
71
72 CStringW Path = g_FontCache->Filename(fontEntry, true);
73 if (!Path.IsEmpty())
74 {
75 // '/d' disables the install button
76 StringCchPrintfW(FontPathArg, _countof(FontPathArg), L"/d %s", Path.GetString());
77 PathQuoteSpacesW(FontPathArg + 3);
78
79 SHELLEXECUTEINFOW si = { sizeof(si) };
80 si.fMask = SEE_MASK_DOENVSUBST;
81 si.hwnd = hwnd;
82 si.lpFile = FontViewerPath;
83 si.lpParameters = FontPathArg;
84 si.nShow = SW_SHOWNORMAL;
85 ShellExecuteExW(&si);
86 }
87 }
88
89 static HRESULT CALLBACK FontFolderMenuCallback(IShellFolder *psf, HWND hwnd, IDataObject *pdtobj,
90 UINT uMsg, WPARAM wParam, LPARAM lParam)
91 {
92 TRACE("FontFolderMenuCallback(%u {%s})\n", uMsg, DFM_TO_STR(uMsg));
93 switch (uMsg)
94 {
95 case DFM_MERGECONTEXTMENU:
96 {
97 QCMINFO *pqcminfo = (QCMINFO *)lParam;
98
99 CStringW menuText(MAKEINTRESOURCEW(IDS_FONT_PREVIEW));
100 MENUITEMINFOW cmi = { sizeof(cmi) };
101 cmi.fMask = MIIM_ID | MIIM_STRING | MIIM_STATE;
102 cmi.fType = MFT_STRING;
103 cmi.fState = MFS_DEFAULT;
104 cmi.wID = pqcminfo->idCmdFirst++;
105 cmi.dwTypeData = (LPWSTR)menuText.GetString();
106 InsertMenuItemW(pqcminfo->hmenu, pqcminfo->indexMenu, TRUE, &cmi);
107
108 return S_OK;
109 }
110 case DFM_INVOKECOMMAND:
111 // Preview is the only item we can handle
112 if (wParam == 0)
113 {
114 CComHeapPtr<CIDA> cida;
115 HRESULT hr = _GetCidlFromDataObject(pdtobj, &cida);
116 if (FAILED_UNEXPECTEDLY(hr))
117 return hr;
118
119 for (UINT n = 0; n < cida->cidl; ++n)
120 {
121 const FontPidlEntry* fontEntry = _FontFromIL(HIDA_GetPIDLItem(cida, n));
122 RunFontViewer(hwnd, fontEntry);
123 }
124 return S_OK;
125 }
126 return S_FALSE;
127
128 case DFM_INVOKECOMMANDEX:
129 return E_NOTIMPL;
130 case DFM_GETDEFSTATICID: // Required for Windows 7 to pick a default
131 return S_FALSE;
132 }
133 return E_NOTIMPL;
134 }
135
136
137 HRESULT _CFontMenu_CreateInstance(HWND hwnd, UINT cidl, PCUITEMID_CHILD_ARRAY apidl,
138 IShellFolder *psf, REFIID riid, LPVOID* ppvOut)
139 {
140 if (cidl > 0)
141 {
142 HKEY keys[1] = {0};
143 int nkeys = 0;
144 CComPtr<IContextMenu> spMenu;
145
146 // Use the default context menu handler, but augment it from the callbacks
147 HRESULT hr = CDefFolderMenu_Create2(NULL, hwnd, cidl, apidl, psf, FontFolderMenuCallback, nkeys, keys, &spMenu);
148
149 if (FAILED_UNEXPECTEDLY(hr))
150 return hr;
151
152 // See if the requested interface (e.g. IContextMenu3) is also available
153 return spMenu->QueryInterface(riid, ppvOut);
154 }
155
156 // We can't create a background menu
157 return E_FAIL;
158 }
159