xref: /reactos/dll/shellext/fontext/CFontMenu.cpp (revision 177ae91b)
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 
160