xref: /reactos/dll/shellext/fontext/CFontMenu.cpp (revision bbabe248)
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(g_FontCache->Find(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 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         else if (wParam == DFM_CMD_PROPERTIES)
127         {
128             ERR("Default properties handling!\n");
129             return S_FALSE;
130         }
131         else
132         {
133             ERR("Unhandled DFM_INVOKECOMMAND(wParam=0x%x)\n", wParam);
134         }
135         return S_FALSE;
136 
137     case DFM_INVOKECOMMANDEX:
138         return E_NOTIMPL;
139     case DFM_GETDEFSTATICID: // Required for Windows 7 to pick a default
140         return S_FALSE;
141     }
142     return E_NOTIMPL;
143 }
144 
145 
146 HRESULT _CFontMenu_CreateInstance(HWND hwnd, UINT cidl, PCUITEMID_CHILD_ARRAY apidl,
147                                   IShellFolder *psf, REFIID riid, LPVOID* ppvOut)
148 {
149     if (cidl > 0)
150     {
151         HKEY keys[1] = {0};
152         int nkeys = 0;
153         CComPtr<IContextMenu> spMenu;
154 
155         // Use the default context menu handler, but augment it from the callbacks
156         HRESULT hr = CDefFolderMenu_Create2(NULL, hwnd, cidl, apidl, psf, FontFolderMenuCallback, nkeys, keys, &spMenu);
157 
158         if (FAILED_UNEXPECTEDLY(hr))
159             return hr;
160 
161         // See if the requested interface (e.g. IContextMenu3) is also available
162         return spMenu->QueryInterface(riid, ppvOut);
163     }
164 
165     // We can't create a background menu
166     return E_FAIL;
167 }
168 
169