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