[SHELLEXT][FONTEXT] An attempt to implement IDropTarget (#2019)
[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 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);
73 if (!Path.IsEmpty())
74 {
75 if (PathIsRelativeW(Path))
76 {
77 WCHAR FontsDir[MAX_PATH];
78 HRESULT hr = SHGetFolderPathW(NULL, CSIDL_FONTS, NULL, 0, FontsDir);
79 if (!FAILED_UNEXPECTEDLY(hr))
80 {
81 StringCchCatW(FontsDir, _countof(FontsDir), L"\\");
82 Path = FontsDir + Path;
83 }
84 }
85
86 // '/d' disables the install button
87 StringCchPrintfW(FontPathArg, _countof(FontPathArg), L"/d %s", Path.GetString());
88 PathQuoteSpacesW(FontPathArg + 3);
89
90 SHELLEXECUTEINFOW si = { sizeof(si) };
91 si.fMask = SEE_MASK_DOENVSUBST;
92 si.hwnd = hwnd;
93 si.lpFile = FontViewerPath;
94 si.lpParameters = FontPathArg;
95 si.nShow = SW_SHOWNORMAL;
96 ShellExecuteExW(&si);
97 }
98 }
99
100 static HRESULT CALLBACK FontFolderMenuCallback(IShellFolder *psf, HWND hwnd, IDataObject *pdtobj,
101 UINT uMsg, WPARAM wParam, LPARAM lParam)
102 {
103 TRACE("FontFolderMenuCallback(%u {%s})\n", uMsg, DFM_TO_STR(uMsg));
104 switch (uMsg)
105 {
106 case DFM_MERGECONTEXTMENU:
107 {
108 QCMINFO *pqcminfo = (QCMINFO *)lParam;
109
110 CStringW menuText(MAKEINTRESOURCEW(IDS_FONT_PREVIEW));
111 MENUITEMINFOW cmi = { sizeof(cmi) };
112 cmi.fMask = MIIM_ID | MIIM_STRING | MIIM_STATE;
113 cmi.fType = MFT_STRING;
114 cmi.fState = MFS_DEFAULT;
115 cmi.wID = pqcminfo->idCmdFirst++;
116 cmi.dwTypeData = (LPWSTR)menuText.GetString();
117 InsertMenuItemW(pqcminfo->hmenu, pqcminfo->indexMenu, TRUE, &cmi);
118
119 return S_OK;
120 }
121 case DFM_INVOKECOMMAND:
122 // Preview is the only item we can handle
123 if (wParam == 0)
124 {
125 CComHeapPtr<CIDA> cida;
126 HRESULT hr = _GetCidlFromDataObject(pdtobj, &cida);
127 if (FAILED_UNEXPECTEDLY(hr))
128 return hr;
129
130 for (UINT n = 0; n < cida->cidl; ++n)
131 {
132 const FontPidlEntry* fontEntry = _FontFromIL(HIDA_GetPIDLItem(cida, n));
133 RunFontViewer(hwnd, fontEntry);
134 }
135 return S_OK;
136 }
137 return S_FALSE;
138
139 case DFM_INVOKECOMMANDEX:
140 return E_NOTIMPL;
141 case DFM_GETDEFSTATICID: // Required for Windows 7 to pick a default
142 return S_FALSE;
143 }
144 return E_NOTIMPL;
145 }
146
147
148 HRESULT _CFontMenu_CreateInstance(HWND hwnd, UINT cidl, PCUITEMID_CHILD_ARRAY apidl,
149 IShellFolder *psf, REFIID riid, LPVOID* ppvOut)
150 {
151 if (cidl > 0)
152 {
153 HKEY keys[1] = {0};
154 int nkeys = 0;
155 CComPtr<IContextMenu> spMenu;
156
157 // Use the default context menu handler, but augment it from the callbacks
158 HRESULT hr = CDefFolderMenu_Create2(NULL, hwnd, cidl, apidl, psf, FontFolderMenuCallback, nkeys, keys, &spMenu);
159
160 if (FAILED_UNEXPECTEDLY(hr))
161 return hr;
162
163 // See if the requested interface (e.g. IContextMenu3) is also available
164 return spMenu->QueryInterface(riid, ppvOut);
165 }
166
167 // We can't create a background menu
168 return E_FAIL;
169 }
170