diff --git a/dll/shellext/fontext/CFontBackgroundMenu.cpp b/dll/shellext/fontext/CFontBackgroundMenu.cpp new file mode 100644 index 00000000000..88797a1ac44 --- /dev/null +++ b/dll/shellext/fontext/CFontBackgroundMenu.cpp @@ -0,0 +1,125 @@ +/* + * PROJECT: ReactOS Font Shell Extension + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) + * PURPOSE: Fonts folder shell extension background menu + * COPYRIGHT: Copyright 2026 Katayama Hirofumi MZ + */ + +#include "precomp.h" +#include + +WINE_DEFAULT_DEBUG_CHANNEL(fontext); + +CFontBackgroundMenu::CFontBackgroundMenu() +{ +} + +CFontBackgroundMenu::~CFontBackgroundMenu() +{ +} + +HRESULT WINAPI CFontBackgroundMenu::Initialize(CFontExt* pFontExt, const DEFCONTEXTMENU *pdcm) +{ + m_pFontExt = pFontExt; + m_psf = pdcm->psf; + m_pmcb = pdcm->pcmcb; + m_hwnd = pdcm->hwnd; + return S_OK; +} + +// IContextMenu +STDMETHODIMP CFontBackgroundMenu::QueryContextMenu(HMENU hMenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags) +{ + TRACE("%d\n", idCmdFirst); + CString strProp(MAKEINTRESOURCEW(IDS_PROPERTIES)); + INT idCmd = idCmdFirst; + AppendMenuW(hMenu, MF_STRING, idCmd++, strProp); + return MAKE_HRESULT(SEVERITY_SUCCESS, 0, idCmd - idCmdFirst); +} + +STDMETHODIMP CFontBackgroundMenu::InvokeCommand(LPCMINVOKECOMMANDINFO lpcmi) +{ + INT idCmd = IS_INTRESOURCE(lpcmi->lpVerb) ? LOWORD(lpcmi->lpVerb) : -1; + TRACE("%d\n", idCmd); + + if (idCmd == 0 || (idCmd == -1 && !lstrcmpiA(lpcmi->lpVerb, "properties"))) + { + // Open "Fonts" properties + LPITEMIDLIST pidl = NULL; + HRESULT hr = SHGetSpecialFolderLocation(m_hwnd, CSIDL_FONTS, &pidl); + if (FAILED_UNEXPECTEDLY(hr) || !pidl) + return E_FAIL; + + SHELLEXECUTEINFOW sei = { + sizeof(sei), SEE_MASK_INVOKEIDLIST | SEE_MASK_ASYNCOK, NULL, L"properties", + NULL, NULL, NULL, SW_SHOWNORMAL, NULL, const_cast(pidl) + }; + BOOL bOK = ShellExecuteExW(&sei); + if (pidl) + CoTaskMemFree(pidl); + return bOK ? S_OK : E_FAIL; + } + + if (idCmd == FCIDM_SHVIEW_INSERT || (idCmd == -1 && !lstrcmpiA(lpcmi->lpVerb, "paste"))) + { + CComPtr pDataObj; + HRESULT hr = OleGetClipboard(&pDataObj); + if (FAILED_UNEXPECTEDLY(hr) || !CheckDataObject(pDataObj)) + { + // Show error message + CStringW text, title; + title.LoadStringW(IDS_REACTOS_FONTS_FOLDER); + text.LoadStringW(IDS_INSTALL_FAILED); + MessageBoxW(m_hwnd, text, title, MB_ICONERROR); + return E_FAIL; + } + + return SHSimulateDrop(m_pFontExt, pDataObj, 0, NULL, NULL); + } + + return S_OK; +} + +STDMETHODIMP CFontBackgroundMenu::GetCommandString(UINT_PTR idCommand, UINT uFlags, UINT *lpReserved, LPSTR lpszName, UINT uMaxNameLen) +{ + TRACE("%d\n", idCommand); + if (idCommand == 0) + { + lstrcpynA(lpszName, "properties", uMaxNameLen); + return S_OK; + } + return E_FAIL; +} + +// IContextMenu2 +STDMETHODIMP CFontBackgroundMenu::HandleMenuMsg(UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + return HandleMenuMsg2(uMsg, wParam, lParam, NULL); +} + +// IContextMenu3 +STDMETHODIMP CFontBackgroundMenu::HandleMenuMsg2(UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *plResult) +{ + if (uMsg == WM_INITMENUPOPUP) + { + HMENU hMenu = (HMENU)wParam; + ::DeleteMenu(hMenu, FCIDM_SHVIEW_INSERTLINK, MF_BYCOMMAND); + return S_OK; + } + return E_NOTIMPL; +} + +HRESULT +APIENTRY +CFontBackgroundMenu_Create( + CFontExt* pFontExt, + HWND hwnd, + IShellFolder* psf, + IContextMenu** ppcm) +{ + DEFCONTEXTMENU dcm; + ZeroMemory(&dcm, sizeof(dcm)); + dcm.hwnd = hwnd; + dcm.psf = psf; + return ShellObjectCreatorInit(pFontExt, &dcm, IID_PPV_ARG(IContextMenu, ppcm)); +} diff --git a/dll/shellext/fontext/CFontBackgroundMenu.h b/dll/shellext/fontext/CFontBackgroundMenu.h new file mode 100644 index 00000000000..dd32b8ddcaf --- /dev/null +++ b/dll/shellext/fontext/CFontBackgroundMenu.h @@ -0,0 +1,41 @@ +/* + * PROJECT: ReactOS Font Shell Extension + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) + * PURPOSE: Fonts folder shell extension background menu + * COPYRIGHT: Copyright 2026 Katayama Hirofumi MZ + */ + +#pragma once + +class CFontBackgroundMenu + : public CComObjectRootEx + , public IContextMenu3 +{ + HWND m_hwnd = nullptr; + CFontExt* m_pFontExt = nullptr; + CComPtr m_psf; + CComPtr m_pmcb; + LPFNDFMCALLBACK m_pfnmcb = nullptr; + +public: + CFontBackgroundMenu(); + virtual ~CFontBackgroundMenu(); + HRESULT WINAPI Initialize(CFontExt* pFontExt, const DEFCONTEXTMENU *pdcm); + + // IContextMenu + STDMETHODIMP QueryContextMenu(HMENU hMenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags) override; + STDMETHODIMP InvokeCommand(LPCMINVOKECOMMANDINFO lpcmi) override; + STDMETHODIMP GetCommandString(UINT_PTR idCommand, UINT uFlags, UINT *lpReserved, LPSTR lpszName, UINT uMaxNameLen) override; + + // IContextMenu2 + STDMETHODIMP HandleMenuMsg(UINT uMsg, WPARAM wParam, LPARAM lParam) override; + + // IContextMenu3 + STDMETHODIMP HandleMenuMsg2(UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *plResult) override; + + BEGIN_COM_MAP(CFontBackgroundMenu) + COM_INTERFACE_ENTRY_IID(IID_IContextMenu, IContextMenu) + COM_INTERFACE_ENTRY_IID(IID_IContextMenu2, IContextMenu2) + COM_INTERFACE_ENTRY_IID(IID_IContextMenu3, IContextMenu3) + END_COM_MAP() +}; diff --git a/dll/shellext/fontext/CFontExt.cpp b/dll/shellext/fontext/CFontExt.cpp index ccc4d4c0854..8bff3dec509 100644 --- a/dll/shellext/fontext/CFontExt.cpp +++ b/dll/shellext/fontext/CFontExt.cpp @@ -87,6 +87,27 @@ CFontExt::~CFontExt() InterlockedDecrement(&g_ModuleRefCnt); } +void CFontExt::SetViewWindow(HWND hwndView) +{ + m_hwndView = hwndView; +} + +HRESULT CALLBACK +CFontExt::MenuCallBack(IShellFolder *psf, HWND hwndOwner, IDataObject *pdtobj, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + TRACE("%u, %p, %p\n", uMsg, wParam, lParam); + switch (uMsg) + { + case DFM_MERGECONTEXTMENU: + return S_OK; // Yes, I want verbs + case DFM_INVOKECOMMAND: + return S_FALSE; // Do it for me please + case DFM_GETDEFSTATICID: + return S_FALSE; // Supposedly "required for Windows 7 to pick a default" + } + return E_NOTIMPL; +} + // *** IShellFolder2 methods *** STDMETHODIMP CFontExt::GetDefaultSearchGUID(GUID *lpguid) { @@ -317,8 +338,8 @@ STDMETHODIMP CFontExt::CreateViewObject(HWND hwndOwner, REFIID riid, LPVOID *ppv } else if (IsEqualIID(riid, IID_IContextMenu)) { - ERR("IContextMenu not implemented\n"); - hr = E_NOTIMPL; + TRACE("IContextMenu\n"); + return CFontBackgroundMenu_Create(this, hwndOwner, this, (IContextMenu**)ppvOut); } else if (IsEqualIID(riid, IID_IShellView)) { @@ -363,13 +384,51 @@ STDMETHODIMP CFontExt::GetAttributesOf(UINT cidl, PCUITEMID_CHILD_ARRAY apidl, D return S_OK; } +HRESULT CFontExt::CreateForegroundMenu(HWND hwndOwner, UINT cidl, PCUITEMID_CHILD_ARRAY apidl, LPVOID* ppvOut) +{ + if (cidl <= 0) + { + ERR("cidl: %u\n", cidl); + return E_NOTIMPL; + } + + const FontPidlEntry* pEntry = _FontFromIL(apidl[0]); + if (!pEntry) + { + ERR("!pEntry\n"); + return E_FAIL; + } + auto info = g_FontCache->Find(pEntry); + if (!info) + { + ERR("!info\n"); + return E_FAIL; + } + LPCWSTR extension = PathFindExtensionW(info->File()); + + CRegKeyHandleArray keys; + + WCHAR wszClass[MAX_PATH]; + DWORD dwSize = sizeof(wszClass); + if (RegGetValueW(HKEY_CLASSES_ROOT, extension, NULL, RRF_RT_REG_SZ, NULL, wszClass, &dwSize) != ERROR_SUCCESS || + !*wszClass || AddClassKeyToArray(wszClass, keys, keys) != ERROR_SUCCESS) + { + AddClassKeyToArray(extension, keys, keys); + + if (cidl == 1) + AddClassKeyToArray(L"Unknown", keys, keys); + } + + return CDefFolderMenu_Create2(m_Folder, hwndOwner, cidl, apidl, this, MenuCallBack, keys, keys, (IContextMenu**)ppvOut); +} + STDMETHODIMP CFontExt::GetUIObjectOf(HWND hwndOwner, UINT cidl, PCUITEMID_CHILD_ARRAY apidl, REFIID riid, UINT * prgfInOut, LPVOID * ppvOut) { if (riid == IID_IContextMenu || riid == IID_IContextMenu2 || riid == IID_IContextMenu3) { - return _CFontMenu_CreateInstance(hwndOwner, cidl, apidl, this, riid, ppvOut); + return CreateForegroundMenu(hwndOwner, cidl, apidl, ppvOut); } else if (riid == IID_IExtractIconA || riid == IID_IExtractIconW) { @@ -398,7 +457,12 @@ STDMETHODIMP CFontExt::GetUIObjectOf(HWND hwndOwner, UINT cidl, PCUITEMID_CHILD_ } else { - ERR("IID_IDataObject with cidl == 0 UNIMPLEMENTED\n"); + CComPtr pDataObj; + HRESULT hr = OleGetClipboard(&pDataObj); + if (FAILED_UNEXPECTEDLY(hr)) + return E_FAIL; + *ppvOut = pDataObj.Detach(); + return S_OK; } } else if (riid == IID_IDropTarget) @@ -499,20 +563,7 @@ STDMETHODIMP CFontExt::GetClassID(CLSID *lpClassId) // *** IDropTarget methods *** STDMETHODIMP CFontExt::DragEnter(IDataObject* pDataObj, DWORD grfKeyState, POINTL pt, DWORD* pdwEffect) { - m_bDragAccepted = FALSE; - - STGMEDIUM stg; - HDROP hDrop = GetDropFromDataObject(stg, pDataObj); - if (!hDrop) - { - *pdwEffect = DROPEFFECT_NONE; - DragLeave(); - return E_FAIL; - } - - m_bDragAccepted = CheckDropFontFiles(hDrop); - ::ReleaseStgMedium(&stg); - + m_bDragAccepted = CheckDataObject(pDataObj); if (!m_bDragAccepted) { *pdwEffect = DROPEFFECT_NONE; @@ -539,138 +590,31 @@ STDMETHODIMP CFontExt::DragLeave() return S_OK; } -DWORD WINAPI CFontExt::InstallThreadProc(LPVOID lpParameter) -{ - PINSTALL_FONT_DATA pData = (PINSTALL_FONT_DATA)lpParameter; - ATLASSERT(pData); - pData->hrResult = InstallFontFiles(pData); - if (pData->bCanceled) - pData->hrResult = S_FALSE; - TRACE("hrResult: 0x%08X\n", pData->hrResult); - ::PostMessageW(pData->hwnd, WM_COMMAND, IDOK, 0); - pData->pDataObj->Release(); - return 0; -} - -INT_PTR CALLBACK -CFontExt::InstallDlgProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, PINSTALL_FONT_DATA pData) -{ - switch (uMsg) - { - case WM_INITDIALOG: - { - pData->hwnd = hwnd; - ATLASSERT(pData->cSteps >= 0); - SendDlgItemMessageW(hwnd, IDC_INSTALL_PROGRESS, PBM_SETRANGE, 0, MAKELPARAM(0, pData->cSteps)); - if (!SHCreateThread(CFontExt::InstallThreadProc, pData, CTF_COINIT, NULL)) - { - WARN("!SHCreateThread\n"); - pData->pDataObj->Release(); - pData->hrResult = E_ABORT; - EndDialog(hwnd, IDABORT); - } - return TRUE; - } - case WM_COMMAND: - { - switch (LOWORD(wParam)) - { - case IDOK: - EndDialog(hwnd, IDOK); - break; - case IDCANCEL: - pData->bCanceled = TRUE; - EndDialog(hwnd, IDCANCEL); - break; - case IDCONTINUE: - pData->iStep += 1; - ATLASSERT(pData->iStep <= pData->cSteps); - SendDlgItemMessageW(hwnd, IDC_INSTALL_PROGRESS, PBM_SETPOS, pData->iStep, 0); - break; - } - break; - } - } - return 0; -} - -INT_PTR CALLBACK -CFontExt::InstallDialogProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) -{ - PINSTALL_FONT_DATA pData = (PINSTALL_FONT_DATA)GetWindowLongPtrW(hwnd, DWLP_USER); - if (uMsg == WM_INITDIALOG) - { - pData = (PINSTALL_FONT_DATA)lParam; - SetWindowLongPtrW(hwnd, DWLP_USER, lParam); - } - - ATLASSERT(pData); - ATLASSERT(pData->pFontExt); - return pData->pFontExt->InstallDlgProc(hwnd, uMsg, wParam, lParam, pData); -} - STDMETHODIMP CFontExt::Drop(IDataObject* pDataObj, DWORD grfKeyState, POINTL pt, DWORD* pdwEffect) { - // NOTE: Getting cida in the other thread fails - CDataObjectHIDA cida(pDataObj); - if (!cida || cida->cidl <= 0) - { - ERR("E_UNEXPECTED\n"); - return E_UNEXPECTED; - } - - PCUIDLIST_ABSOLUTE pidlParent = HIDA_GetPIDLFolder(cida); - if (!pidlParent) - { - ERR("pidlParent is NULL\n"); - return E_FAIL; - } - - CAtlArray apidl; - for (UINT n = 0; n < cida->cidl; ++n) - { - PCUIDLIST_RELATIVE pidlRelative = HIDA_GetPIDLItem(cida, n); - if (!pidlRelative) - { - ERR("!pidlRelative\n"); - return E_FAIL; - } - apidl.Add(pidlRelative); - } - - // Show progress dialog - INSTALL_FONT_DATA data; - data.pFontExt = this; - data.pDataObj = pDataObj; - data.pidlParent = pidlParent; - data.apidl = &apidl[0]; - data.cSteps = cida->cidl; - pDataObj->AddRef(); - DialogBoxParamW(_AtlBaseModule.GetResourceInstance(), MAKEINTRESOURCEW(IDD_INSTALL), - m_hwndView, CFontExt::InstallDialogProc, (LPARAM)&data); - if (data.bCanceled) - return E_ABORT; + ATLASSERT(m_hwndView); + HRESULT hr = InstallFontsFromDataObject(m_hwndView, pDataObj); CStringW text, title; title.LoadStringW(IDS_REACTOS_FONTS_FOLDER); - if (SUCCEEDED(data.hrResult)) - { - // Invalidate our cache - g_FontCache->Read(); - - // Notify the system that a font was added - SendMessageW(HWND_BROADCAST, WM_FONTCHANGE, 0, 0); - - // Show successful message - text.LoadStringW(IDS_INSTALL_OK); - MessageBoxW(m_hwndView, text, title, MB_ICONINFORMATION); - } - else + if (FAILED_UNEXPECTEDLY(hr)) { // Show error message text.LoadStringW(IDS_INSTALL_FAILED); MessageBoxW(m_hwndView, text, title, MB_ICONERROR); } + else if (hr == S_OK) + { + // Refresh font cache and notify the system about the font change + if (g_FontCache) + g_FontCache->Read(); - return data.hrResult; + SendMessageTimeoutW(HWND_BROADCAST, WM_FONTCHANGE, 0, 0, SMTO_ABORTIFHUNG, 1000, NULL); + + // Show successful message + text.LoadStringW(IDS_INSTALL_OK); + MessageBoxW(m_hwndView, text, title, MB_ICONINFORMATION); + } + + return hr; } diff --git a/dll/shellext/fontext/CFontExt.hpp b/dll/shellext/fontext/CFontExt.hpp index c6451ea4b9a..b6404d7ebbc 100644 --- a/dll/shellext/fontext/CFontExt.hpp +++ b/dll/shellext/fontext/CFontExt.hpp @@ -18,18 +18,14 @@ class CFontExt : CComHeapPtr m_Folder; BOOL m_bDragAccepted = FALSE; HWND m_hwndView = nullptr; - static INT_PTR CALLBACK InstallDialogProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); - INT_PTR CALLBACK InstallDlgProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, PINSTALL_FONT_DATA pData); - static DWORD WINAPI InstallThreadProc(LPVOID lpParameter); public: CFontExt(); ~CFontExt(); - void SetViewWindow(HWND hwndView) - { - m_hwndView = hwndView; - } + void SetViewWindow(HWND hwndView); + static HRESULT CALLBACK MenuCallBack(IShellFolder *psf, HWND hwndOwner, IDataObject *pdtobj, UINT uMsg, WPARAM wParam, LPARAM lParam); + HRESULT CreateForegroundMenu(HWND hwndOwner, UINT cidl, PCUITEMID_CHILD_ARRAY apidl, LPVOID* ppvOut); // *** IShellFolder2 methods *** STDMETHODIMP GetDefaultSearchGUID(GUID *lpguid) override; diff --git a/dll/shellext/fontext/CFontMenu.cpp b/dll/shellext/fontext/CFontMenu.cpp deleted file mode 100644 index a8c70f359b7..00000000000 --- a/dll/shellext/fontext/CFontMenu.cpp +++ /dev/null @@ -1,132 +0,0 @@ -/* - * PROJECT: ReactOS Font Shell Extension - * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) - * PURPOSE: CFontMenu implementation - * COPYRIGHT: Copyright 2019-2021 Mark Jansen - */ - -#include "precomp.h" - -WINE_DEFAULT_DEBUG_CHANNEL(fontext); - -const char* DFM_TO_STR(UINT uMsg) -{ - switch(uMsg) - { - case DFM_MERGECONTEXTMENU: return "DFM_MERGECONTEXTMENU"; - case DFM_INVOKECOMMAND: return "DFM_INVOKECOMMAND"; - case DFM_MODIFYQCMFLAGS: return "DFM_MODIFYQCMFLAGS"; - case DFM_MERGECONTEXTMENU_TOP: return "DFM_MERGECONTEXTMENU_TOP"; - case DFM_MERGECONTEXTMENU_BOTTOM: return "DFM_MERGECONTEXTMENU_BOTTOM"; - case DFM_GETHELPTEXTW: return "DFM_GETHELPTEXTW"; - case DFM_GETVERBW: return "DFM_GETVERBW"; - case DFM_GETVERBA: return "DFM_GETVERBA"; - case DFM_WM_INITMENUPOPUP: return "DFM_WM_INITMENUPOPUP"; - case DFM_INVOKECOMMANDEX: return "DFM_INVOKECOMMANDEX"; - case DFM_GETDEFSTATICID: return "DFM_GETDEFSTATICID"; - case 3: return "MENU_BEGIN"; - case 4: return "MENU_END"; - default: return ""; - } -} - -static void RunFontViewer(HWND hwnd, const FontPidlEntry* fontEntry) -{ - WCHAR FontViewerPath[MAX_PATH] = L"%SystemRoot%\\System32\\fontview.exe"; - WCHAR FontPathArg[MAX_PATH + 3]; - - CStringW Path = g_FontCache->Filename(g_FontCache->Find(fontEntry), true); - if (!Path.IsEmpty()) - { - // '/d' disables the install button - StringCchPrintfW(FontPathArg, _countof(FontPathArg), L"/d %s", Path.GetString()); - PathQuoteSpacesW(FontPathArg + 3); - - SHELLEXECUTEINFOW si = { sizeof(si) }; - si.fMask = SEE_MASK_DOENVSUBST; - si.hwnd = hwnd; - si.lpFile = FontViewerPath; - si.lpParameters = FontPathArg; - si.nShow = SW_SHOWNORMAL; - ShellExecuteExW(&si); - } -} - -static HRESULT CALLBACK FontFolderMenuCallback(IShellFolder *psf, HWND hwnd, IDataObject *pdtobj, - UINT uMsg, WPARAM wParam, LPARAM lParam) -{ - TRACE("FontFolderMenuCallback(%u {%s})\n", uMsg, DFM_TO_STR(uMsg)); - switch (uMsg) - { - case DFM_MERGECONTEXTMENU: - { - QCMINFO *pqcminfo = (QCMINFO *)lParam; - - CStringW menuText(MAKEINTRESOURCEW(IDS_FONT_PREVIEW)); - MENUITEMINFOW cmi = { sizeof(cmi) }; - cmi.fMask = MIIM_ID | MIIM_STRING | MIIM_STATE; - cmi.fType = MFT_STRING; - cmi.fState = MFS_DEFAULT; - cmi.wID = pqcminfo->idCmdFirst++; - cmi.dwTypeData = (LPWSTR)menuText.GetString(); - InsertMenuItemW(pqcminfo->hmenu, pqcminfo->indexMenu, TRUE, &cmi); - - return S_OK; - } - case DFM_INVOKECOMMAND: - // Preview is the only item we handle - if (wParam == 0) - { - CDataObjectHIDA cida(pdtobj); - - if (FAILED_UNEXPECTEDLY(cida.hr())) - return cida.hr(); - - for (UINT n = 0; n < cida->cidl; ++n) - { - const FontPidlEntry* fontEntry = _FontFromIL(HIDA_GetPIDLItem(cida, n)); - RunFontViewer(hwnd, fontEntry); - } - return S_OK; - } - else if (wParam == DFM_CMD_PROPERTIES) - { - TRACE("Default properties handling!\n"); - return S_FALSE; - } - else - { - ERR("Unhandled DFM_INVOKECOMMAND(wParam=0x%x)\n", wParam); - } - return S_FALSE; - - case DFM_INVOKECOMMANDEX: - return E_NOTIMPL; - case DFM_GETDEFSTATICID: // Required for Windows 7 to pick a default - return S_FALSE; - } - return E_NOTIMPL; -} - -HRESULT _CFontMenu_CreateInstance(HWND hwnd, UINT cidl, PCUITEMID_CHILD_ARRAY apidl, - IShellFolder *psf, REFIID riid, LPVOID* ppvOut) -{ - if (cidl > 0) - { - HKEY keys[1] = {0}; - int nkeys = 0; - CComPtr spMenu; - - // Use the default context menu handler, but augment it from the callbacks - HRESULT hr = CDefFolderMenu_Create2(NULL, hwnd, cidl, apidl, psf, FontFolderMenuCallback, nkeys, keys, &spMenu); - - if (FAILED_UNEXPECTEDLY(hr)) - return hr; - - // See if the requested interface (e.g. IContextMenu3) is also available - return spMenu->QueryInterface(riid, ppvOut); - } - - // We can't create a background menu - return E_FAIL; -} diff --git a/dll/shellext/fontext/CMakeLists.txt b/dll/shellext/fontext/CMakeLists.txt index 1affe6f1ab9..c1269033e84 100644 --- a/dll/shellext/fontext/CMakeLists.txt +++ b/dll/shellext/fontext/CMakeLists.txt @@ -7,10 +7,10 @@ spec2def(fontext.dll fontext.spec) list(APPEND SOURCE CDataObject.cpp CEnumFonts.cpp + CFontBackgroundMenu.cpp CFontCache.cpp CFontExt.cpp CFontFolderViewCB.cpp - CFontMenu.cpp fontext.cpp fontpidl.cpp precomp.h) diff --git a/dll/shellext/fontext/fontext.cpp b/dll/shellext/fontext/fontext.cpp index b9013febdf1..eeac271e2c0 100644 --- a/dll/shellext/fontext/fontext.cpp +++ b/dll/shellext/fontext/fontext.cpp @@ -100,6 +100,27 @@ STDAPI DllUnregisterServer() return gModule.DllUnregisterServer(FALSE); } +void CloseRegKeyArray(HKEY* array, UINT cKeys) +{ + for (UINT i = 0; i < cKeys; ++i) + RegCloseKey(array[i]); +} + +LSTATUS AddClassKeyToArray(const WCHAR* szClass, HKEY* array, UINT* cKeys) +{ + if (*cKeys >= 16) + return ERROR_MORE_DATA; + + HKEY hkey; + LSTATUS result = RegOpenKeyExW(HKEY_CLASSES_ROOT, szClass, 0, KEY_READ | KEY_QUERY_VALUE, &hkey); + if (result == ERROR_SUCCESS) + { + array[*cKeys] = hkey; + *cKeys += 1; + } + return result; +} + HRESULT InstallFontFiles( _Inout_ PINSTALL_FONT_DATA pData) @@ -207,10 +228,6 @@ DoInstallFontFile( return E_FAIL; } - // Delete file - if (DeleteFileW(szDestFile)) - SHChangeNotify(SHCNE_DELETE, SHCNF_PATHW, (PCWSTR)szDestFile, NULL); - // Copy file if (!CopyFileW(pszFontPath, szDestFile, FALSE)) { @@ -288,13 +305,135 @@ BOOL CheckDropFontFiles(HDROP hDrop) return TRUE; } -HDROP GetDropFromDataObject(STGMEDIUM& stg, IDataObject *pDataObj) +BOOL CheckDataObject(IDataObject *pDataObj) { + STGMEDIUM stg; FORMATETC etc = { CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL }; HRESULT hr = pDataObj->GetData(&etc, &stg); if (FAILED_UNEXPECTEDLY(hr)) - return NULL; - return reinterpret_cast(stg.hGlobal); + return FALSE; + HDROP hDrop = reinterpret_cast(stg.hGlobal); + BOOL bOK = CheckDropFontFiles(hDrop); + ReleaseStgMedium(&stg); + return bOK; +} + +static DWORD WINAPI InstallThreadProc(LPVOID lpParameter) +{ + PINSTALL_FONT_DATA pData = (PINSTALL_FONT_DATA)lpParameter; + ATLASSERT(pData); + pData->hrResult = InstallFontFiles(pData); + if (pData->bCanceled) + pData->hrResult = S_FALSE; + TRACE("hrResult: 0x%08X\n", pData->hrResult); + ::PostMessageW(pData->hwnd, WM_COMMAND, IDOK, 0); + pData->pDataObj->Release(); + return 0; +} + +static INT_PTR CALLBACK +InstallDlgProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, PINSTALL_FONT_DATA pData) +{ + switch (uMsg) + { + case WM_INITDIALOG: + { + pData->hwnd = hwnd; + ATLASSERT(pData->cSteps >= 0); + SendDlgItemMessageW(hwnd, IDC_INSTALL_PROGRESS, PBM_SETRANGE, 0, MAKELPARAM(0, pData->cSteps)); + if (!SHCreateThread(InstallThreadProc, pData, CTF_COINIT, NULL)) + { + WARN("!SHCreateThread\n"); + pData->pDataObj->Release(); + pData->hrResult = E_ABORT; + EndDialog(hwnd, IDABORT); + } + return TRUE; + } + case WM_COMMAND: + { + switch (LOWORD(wParam)) + { + case IDOK: + EndDialog(hwnd, IDOK); + break; + case IDCANCEL: + pData->bCanceled = TRUE; + EndDialog(hwnd, IDCANCEL); + break; + case IDCONTINUE: + pData->iStep += 1; + ATLASSERT(pData->iStep <= pData->cSteps); + SendDlgItemMessageW(hwnd, IDC_INSTALL_PROGRESS, PBM_SETPOS, pData->iStep, 0); + break; + } + break; + } + } + return 0; +} + +static INT_PTR CALLBACK +InstallDialogProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + PINSTALL_FONT_DATA pData = (PINSTALL_FONT_DATA)GetWindowLongPtrW(hwnd, DWLP_USER); + if (uMsg == WM_INITDIALOG) + { + pData = (PINSTALL_FONT_DATA)lParam; + SetWindowLongPtrW(hwnd, DWLP_USER, lParam); + } + + ATLASSERT(pData); + return InstallDlgProc(hwnd, uMsg, wParam, lParam, pData); +} + +HRESULT InstallFontsFromDataObject(HWND hwndView, IDataObject* pDataObj) +{ + if (!CheckDataObject(pDataObj)) + { + ERR("!CheckDataObject\n"); + return E_FAIL; + } + + CDataObjectHIDA cida(pDataObj); + if (!cida || cida->cidl <= 0) + { + ERR("E_UNEXPECTED\n"); + return E_FAIL; + } + + PCUIDLIST_ABSOLUTE pidlParent = HIDA_GetPIDLFolder(cida); + if (!pidlParent) + { + ERR("pidlParent is NULL\n"); + return E_FAIL; + } + + CAtlArray apidl; + for (UINT n = 0; n < cida->cidl; ++n) + { + PCUIDLIST_RELATIVE pidlRelative = HIDA_GetPIDLItem(cida, n); + if (!pidlRelative) + { + ERR("!pidlRelative\n"); + return E_FAIL; + } + apidl.Add(pidlRelative); + } + + // Show progress dialog + INSTALL_FONT_DATA data; + data.pDataObj = pDataObj; + data.pidlParent = pidlParent; + data.apidl = &apidl[0]; + data.cSteps = cida->cidl; + pDataObj->AddRef(); + DialogBoxParamW(_AtlBaseModule.GetResourceInstance(), MAKEINTRESOURCEW(IDD_INSTALL), + hwndView, InstallDialogProc, (LPARAM)&data); + if (data.bCanceled) + return S_FALSE; + + return FAILED_UNEXPECTEDLY(data.hrResult) ? E_FAIL : S_OK; } EXTERN_C diff --git a/dll/shellext/fontext/lang/de-DE.rc b/dll/shellext/fontext/lang/de-DE.rc index 5910a794097..98c17f3b10d 100644 --- a/dll/shellext/fontext/lang/de-DE.rc +++ b/dll/shellext/fontext/lang/de-DE.rc @@ -22,6 +22,7 @@ BEGIN IDS_REACTOS_FONTS_FOLDER "ReactOS Schriftartenordner" IDS_INSTALL_OK "The font files have been installed successfully." IDS_INSTALL_FAILED "Failed to install the font files." + IDS_PROPERTIES "P&roperties" END STRINGTABLE diff --git a/dll/shellext/fontext/lang/en-US.rc b/dll/shellext/fontext/lang/en-US.rc index 8a250d6555b..c8cbe6f3873 100644 --- a/dll/shellext/fontext/lang/en-US.rc +++ b/dll/shellext/fontext/lang/en-US.rc @@ -17,6 +17,7 @@ BEGIN IDS_REACTOS_FONTS_FOLDER "ReactOS Font Folder" IDS_INSTALL_OK "The font files have been installed successfully." IDS_INSTALL_FAILED "Failed to install the font files." + IDS_PROPERTIES "P&roperties" END STRINGTABLE diff --git a/dll/shellext/fontext/lang/it-IT.rc b/dll/shellext/fontext/lang/it-IT.rc index b73e0dbf304..90f284d2eb3 100644 --- a/dll/shellext/fontext/lang/it-IT.rc +++ b/dll/shellext/fontext/lang/it-IT.rc @@ -22,6 +22,7 @@ BEGIN IDS_REACTOS_FONTS_FOLDER "Cartella dei font di ReactOS" IDS_INSTALL_OK "The font files have been installed successfully." IDS_INSTALL_FAILED "Failed to install the font files." + IDS_PROPERTIES "P&roperties" END STRINGTABLE diff --git a/dll/shellext/fontext/lang/pl-PL.rc b/dll/shellext/fontext/lang/pl-PL.rc index d88f9c1ed7d..390f80945f4 100644 --- a/dll/shellext/fontext/lang/pl-PL.rc +++ b/dll/shellext/fontext/lang/pl-PL.rc @@ -17,6 +17,7 @@ BEGIN IDS_REACTOS_FONTS_FOLDER "Folder czcionek ReactOS" IDS_INSTALL_OK "The font files have been installed successfully." IDS_INSTALL_FAILED "Failed to install the font files." + IDS_PROPERTIES "P&roperties" END STRINGTABLE diff --git a/dll/shellext/fontext/lang/ro-RO.rc b/dll/shellext/fontext/lang/ro-RO.rc index 47eb365dd04..5831d194a13 100644 --- a/dll/shellext/fontext/lang/ro-RO.rc +++ b/dll/shellext/fontext/lang/ro-RO.rc @@ -23,6 +23,7 @@ BEGIN IDS_REACTOS_FONTS_FOLDER "Folder-ul de fonturi de ReactOS" IDS_INSTALL_OK "The font files have been installed successfully." IDS_INSTALL_FAILED "Failed to install the font files." + IDS_PROPERTIES "P&roperties" END STRINGTABLE diff --git a/dll/shellext/fontext/lang/tr-TR.rc b/dll/shellext/fontext/lang/tr-TR.rc index 69aec11ff0b..7eccac73bde 100644 --- a/dll/shellext/fontext/lang/tr-TR.rc +++ b/dll/shellext/fontext/lang/tr-TR.rc @@ -17,6 +17,7 @@ BEGIN IDS_REACTOS_FONTS_FOLDER "ReactOS Yazı Tipi Dizini" IDS_INSTALL_OK "The font files have been installed successfully." IDS_INSTALL_FAILED "Failed to install the font files." + IDS_PROPERTIES "P&roperties" END STRINGTABLE diff --git a/dll/shellext/fontext/lang/zh-CN.rc b/dll/shellext/fontext/lang/zh-CN.rc index 9fca1fdff84..8595bf2fd0c 100644 --- a/dll/shellext/fontext/lang/zh-CN.rc +++ b/dll/shellext/fontext/lang/zh-CN.rc @@ -22,6 +22,7 @@ BEGIN IDS_REACTOS_FONTS_FOLDER "ReactOS 字体文件夹" IDS_INSTALL_OK "The font files have been installed successfully." IDS_INSTALL_FAILED "Failed to install the font files." + IDS_PROPERTIES "P&roperties" END STRINGTABLE diff --git a/dll/shellext/fontext/lang/zh-HK.rc b/dll/shellext/fontext/lang/zh-HK.rc index 84eb1dc0b45..8e3a5ee3315 100644 --- a/dll/shellext/fontext/lang/zh-HK.rc +++ b/dll/shellext/fontext/lang/zh-HK.rc @@ -22,6 +22,7 @@ BEGIN IDS_REACTOS_FONTS_FOLDER "ReactOS 字型資料夾" IDS_INSTALL_OK "The font files have been installed successfully." IDS_INSTALL_FAILED "Failed to install the font files." + IDS_PROPERTIES "P&roperties" END STRINGTABLE diff --git a/dll/shellext/fontext/lang/zh-TW.rc b/dll/shellext/fontext/lang/zh-TW.rc index af8e8987f6c..270b24b6978 100644 --- a/dll/shellext/fontext/lang/zh-TW.rc +++ b/dll/shellext/fontext/lang/zh-TW.rc @@ -22,6 +22,7 @@ BEGIN IDS_REACTOS_FONTS_FOLDER "ReactOS 字型資料夾" IDS_INSTALL_OK "The font files have been installed successfully." IDS_INSTALL_FAILED "Failed to install the font files." + IDS_PROPERTIES "P&roperties" END STRINGTABLE diff --git a/dll/shellext/fontext/precomp.h b/dll/shellext/fontext/precomp.h index 5f7f0d280ea..0727b10f975 100644 --- a/dll/shellext/fontext/precomp.h +++ b/dll/shellext/fontext/precomp.h @@ -27,11 +27,8 @@ extern const GUID CLSID_CFontExt; extern LONG g_ModuleRefCnt; -class CFontExt; - typedef struct tagINSTALL_FONT_DATA { - CFontExt* pFontExt = nullptr; IDataObject* pDataObj = nullptr; HRESULT hrResult = S_OK; HWND hwnd = nullptr; @@ -47,13 +44,12 @@ typedef struct tagINSTALL_FONT_DATA #include "CFontCache.hpp" #include "CFontExt.hpp" #include "CFontFolderViewCB.h" +#include "CFontBackgroundMenu.h" #define FONT_HIVE HKEY_LOCAL_MACHINE #define FONT_KEY L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Fonts" HRESULT _CEnumFonts_CreateInstance(CFontExt* zip, DWORD flags, REFIID riid, LPVOID* ppvOut); -HRESULT _CFontMenu_CreateInstance(HWND hwnd, UINT cidl, PCUITEMID_CHILD_ARRAY apidl, - IShellFolder *psf, REFIID riid, LPVOID* ppvOut); HRESULT _CDataObject_CreateInstance(PCIDLIST_ABSOLUTE folder, UINT cidl, PCUITEMID_CHILD_ARRAY apidl, REFIID riid, LPVOID* ppvOut); @@ -84,4 +80,29 @@ HRESULT DoGetFontTitle( _Out_ CStringW& strFontName); BOOL CheckDropFontFiles(HDROP hDrop); -HDROP GetDropFromDataObject(STGMEDIUM& stg, IDataObject *pDataObj); +BOOL CheckDataObject(IDataObject *pDataObj); +HRESULT InstallFontsFromDataObject(HWND hwndView, IDataObject* pDataObj); + +HRESULT +APIENTRY +CFontBackgroundMenu_Create( + CFontExt* pFontExt, + HWND hwnd, + IShellFolder* psf, + IContextMenu** ppcm); + +LSTATUS AddClassKeyToArray(const WCHAR* szClass, HKEY* array, UINT* cKeys); +void CloseRegKeyArray(HKEY* array, UINT cKeys); + +struct CRegKeyHandleArray +{ + HKEY hKeys[16]; + UINT cKeys; + + CRegKeyHandleArray() : cKeys(0) {} + ~CRegKeyHandleArray() { CloseRegKeyArray(hKeys, cKeys); } + operator HKEY*() { return hKeys; } + operator UINT*() { return &cKeys; } + operator UINT() { return cKeys; } + HKEY& operator [](SIZE_T i) { return hKeys[i]; } +}; diff --git a/dll/shellext/fontext/resource.h b/dll/shellext/fontext/resource.h index f2695a26fa9..b1b871d934b 100644 --- a/dll/shellext/fontext/resource.h +++ b/dll/shellext/fontext/resource.h @@ -7,6 +7,8 @@ #define IDS_REACTOS_FONTS_FOLDER 151 #define IDS_INSTALL_OK 152 #define IDS_INSTALL_FAILED 153 +#define IDS_PROPERTIES 154 + #define IDS_COL_NAME 301 #define IDS_COL_FILENAME 304 #define IDS_COL_SIZE 305