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