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-2021 Mark Jansen <mark.jansen@reactos.org> 6 */ 7 8 #include "precomp.h" 9 10 WINE_DEFAULT_DEBUG_CHANNEL(fontext); 11 12 13 const char* DFM_TO_STR(UINT uMsg) 14 { 15 switch(uMsg) 16 { 17 case DFM_MERGECONTEXTMENU: return "DFM_MERGECONTEXTMENU"; 18 case DFM_INVOKECOMMAND: return "DFM_INVOKECOMMAND"; 19 case DFM_MODIFYQCMFLAGS: return "DFM_MODIFYQCMFLAGS"; 20 case DFM_MERGECONTEXTMENU_TOP: return "DFM_MERGECONTEXTMENU_TOP"; 21 case DFM_MERGECONTEXTMENU_BOTTOM: return "DFM_MERGECONTEXTMENU_BOTTOM"; 22 case DFM_GETHELPTEXTW: return "DFM_GETHELPTEXTW"; 23 case DFM_GETVERBW: return "DFM_GETVERBW"; 24 case DFM_GETVERBA: return "DFM_GETVERBA"; 25 case DFM_WM_INITMENUPOPUP: return "DFM_WM_INITMENUPOPUP"; 26 case DFM_INVOKECOMMANDEX: return "DFM_INVOKECOMMANDEX"; 27 case DFM_GETDEFSTATICID: return "DFM_GETDEFSTATICID"; 28 case 3: return "MENU_BEGIN"; 29 case 4: return "MENU_END"; 30 default: return ""; 31 } 32 } 33 34 35 static void RunFontViewer(HWND hwnd, const FontPidlEntry* fontEntry) 36 { 37 WCHAR FontViewerPath[MAX_PATH] = L"%SystemRoot%\\System32\\fontview.exe"; 38 WCHAR FontPathArg[MAX_PATH + 3]; 39 40 CStringW Path = g_FontCache->Filename(g_FontCache->Find(fontEntry), true); 41 if (!Path.IsEmpty()) 42 { 43 // '/d' disables the install button 44 StringCchPrintfW(FontPathArg, _countof(FontPathArg), L"/d %s", Path.GetString()); 45 PathQuoteSpacesW(FontPathArg + 3); 46 47 SHELLEXECUTEINFOW si = { sizeof(si) }; 48 si.fMask = SEE_MASK_DOENVSUBST; 49 si.hwnd = hwnd; 50 si.lpFile = FontViewerPath; 51 si.lpParameters = FontPathArg; 52 si.nShow = SW_SHOWNORMAL; 53 ShellExecuteExW(&si); 54 } 55 } 56 57 static HRESULT CALLBACK FontFolderMenuCallback(IShellFolder *psf, HWND hwnd, IDataObject *pdtobj, 58 UINT uMsg, WPARAM wParam, LPARAM lParam) 59 { 60 TRACE("FontFolderMenuCallback(%u {%s})\n", uMsg, DFM_TO_STR(uMsg)); 61 switch (uMsg) 62 { 63 case DFM_MERGECONTEXTMENU: 64 { 65 QCMINFO *pqcminfo = (QCMINFO *)lParam; 66 67 CStringW menuText(MAKEINTRESOURCEW(IDS_FONT_PREVIEW)); 68 MENUITEMINFOW cmi = { sizeof(cmi) }; 69 cmi.fMask = MIIM_ID | MIIM_STRING | MIIM_STATE; 70 cmi.fType = MFT_STRING; 71 cmi.fState = MFS_DEFAULT; 72 cmi.wID = pqcminfo->idCmdFirst++; 73 cmi.dwTypeData = (LPWSTR)menuText.GetString(); 74 InsertMenuItemW(pqcminfo->hmenu, pqcminfo->indexMenu, TRUE, &cmi); 75 76 return S_OK; 77 } 78 case DFM_INVOKECOMMAND: 79 // Preview is the only item we handle 80 if (wParam == 0) 81 { 82 CDataObjectHIDA cida(pdtobj); 83 84 if (FAILED_UNEXPECTEDLY(cida.hr())) 85 return cida.hr(); 86 87 for (UINT n = 0; n < cida->cidl; ++n) 88 { 89 const FontPidlEntry* fontEntry = _FontFromIL(HIDA_GetPIDLItem(cida, n)); 90 RunFontViewer(hwnd, fontEntry); 91 } 92 return S_OK; 93 } 94 else if (wParam == DFM_CMD_PROPERTIES) 95 { 96 TRACE("Default properties handling!\n"); 97 return S_FALSE; 98 } 99 else 100 { 101 ERR("Unhandled DFM_INVOKECOMMAND(wParam=0x%x)\n", wParam); 102 } 103 return S_FALSE; 104 105 case DFM_INVOKECOMMANDEX: 106 return E_NOTIMPL; 107 case DFM_GETDEFSTATICID: // Required for Windows 7 to pick a default 108 return S_FALSE; 109 } 110 return E_NOTIMPL; 111 } 112 113 114 HRESULT _CFontMenu_CreateInstance(HWND hwnd, UINT cidl, PCUITEMID_CHILD_ARRAY apidl, 115 IShellFolder *psf, REFIID riid, LPVOID* ppvOut) 116 { 117 if (cidl > 0) 118 { 119 HKEY keys[1] = {0}; 120 int nkeys = 0; 121 CComPtr<IContextMenu> spMenu; 122 123 // Use the default context menu handler, but augment it from the callbacks 124 HRESULT hr = CDefFolderMenu_Create2(NULL, hwnd, cidl, apidl, psf, FontFolderMenuCallback, nkeys, keys, &spMenu); 125 126 if (FAILED_UNEXPECTEDLY(hr)) 127 return hr; 128 129 // See if the requested interface (e.g. IContextMenu3) is also available 130 return spMenu->QueryInterface(riid, ppvOut); 131 } 132 133 // We can't create a background menu 134 return E_FAIL; 135 } 136 137