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 Mark Jansen (mark.jansen@reactos.org)
10 WINE_DEFAULT_DEBUG_CHANNEL(fontext
);
12 static CLIPFORMAT g_cfHIDA
;
14 HRESULT
_GetCidlFromDataObject(IDataObject
*pDataObject
, CIDA
** ppcida
)
18 g_cfHIDA
= (CLIPFORMAT
)RegisterClipboardFormatW(CFSTR_SHELLIDLIST
);
21 FORMATETC fmt
= { g_cfHIDA
, NULL
, DVASPECT_CONTENT
, -1, TYMED_HGLOBAL
};
24 HRESULT hr
= pDataObject
->GetData(&fmt
, &medium
);
25 if (FAILED_UNEXPECTEDLY(hr
))
28 LPVOID lpSrc
= GlobalLock(medium
.hGlobal
);
29 SIZE_T cbSize
= GlobalSize(medium
.hGlobal
);
31 *ppcida
= (CIDA
*)::CoTaskMemAlloc(cbSize
);
34 memcpy(*ppcida
, lpSrc
, cbSize
);
41 ReleaseStgMedium(&medium
);
45 const char* DFM_TO_STR(UINT uMsg
)
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";
67 static void RunFontViewer(HWND hwnd
, const FontPidlEntry
* fontEntry
)
69 WCHAR FontViewerPath
[MAX_PATH
] = L
"%SystemRoot%\\System32\\fontview.exe";
70 WCHAR FontPathArg
[MAX_PATH
+ 3];
72 CStringW Path
= g_FontCache
->Filename(fontEntry
);
75 if (PathIsRelativeW(Path
))
77 WCHAR FontsDir
[MAX_PATH
];
78 HRESULT hr
= SHGetFolderPathW(NULL
, CSIDL_FONTS
, NULL
, 0, FontsDir
);
79 if (!FAILED_UNEXPECTEDLY(hr
))
81 StringCchCatW(FontsDir
, _countof(FontsDir
), L
"\\");
82 Path
= FontsDir
+ Path
;
86 // '/d' disables the install button
87 StringCchPrintfW(FontPathArg
, _countof(FontPathArg
), L
"/d %s", Path
.GetString());
88 PathQuoteSpacesW(FontPathArg
+ 3);
90 SHELLEXECUTEINFOW si
= { sizeof(si
) };
91 si
.fMask
= SEE_MASK_DOENVSUBST
;
93 si
.lpFile
= FontViewerPath
;
94 si
.lpParameters
= FontPathArg
;
95 si
.nShow
= SW_SHOWNORMAL
;
100 static HRESULT CALLBACK
FontFolderMenuCallback(IShellFolder
*psf
, HWND hwnd
, IDataObject
*pdtobj
,
101 UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
103 TRACE("FontFolderMenuCallback(%u {%s})\n", uMsg
, DFM_TO_STR(uMsg
));
106 case DFM_MERGECONTEXTMENU
:
108 QCMINFO
*pqcminfo
= (QCMINFO
*)lParam
;
110 CStringW
menuText(MAKEINTRESOURCEW(IDS_FONT_PREVIEW
));
111 MENUITEMINFOW cmi
= { sizeof(cmi
) };
112 cmi
.fMask
= MIIM_ID
| MIIM_STRING
| MIIM_STATE
;
113 cmi
.fType
= MFT_STRING
;
114 cmi
.fState
= MFS_DEFAULT
;
115 cmi
.wID
= pqcminfo
->idCmdFirst
++;
116 cmi
.dwTypeData
= (LPWSTR
)menuText
.GetString();
117 InsertMenuItemW(pqcminfo
->hmenu
, pqcminfo
->indexMenu
, TRUE
, &cmi
);
121 case DFM_INVOKECOMMAND
:
122 // Preview is the only item we can handle
125 CComHeapPtr
<CIDA
> cida
;
126 HRESULT hr
= _GetCidlFromDataObject(pdtobj
, &cida
);
127 if (FAILED_UNEXPECTEDLY(hr
))
130 for (UINT n
= 0; n
< cida
->cidl
; ++n
)
132 const FontPidlEntry
* fontEntry
= _FontFromIL(HIDA_GetPIDLItem(cida
, n
));
133 RunFontViewer(hwnd
, fontEntry
);
139 case DFM_INVOKECOMMANDEX
:
141 case DFM_GETDEFSTATICID
: // Required for Windows 7 to pick a default
148 HRESULT
_CFontMenu_CreateInstance(HWND hwnd
, UINT cidl
, PCUITEMID_CHILD_ARRAY apidl
,
149 IShellFolder
*psf
, REFIID riid
, LPVOID
* ppvOut
)
155 CComPtr
<IContextMenu
> spMenu
;
157 // Use the default context menu handler, but augment it from the callbacks
158 HRESULT hr
= CDefFolderMenu_Create2(NULL
, hwnd
, cidl
, apidl
, psf
, FontFolderMenuCallback
, nkeys
, keys
, &spMenu
);
160 if (FAILED_UNEXPECTEDLY(hr
))
163 // See if the requested interface (e.g. IContextMenu3) is also available
164 return spMenu
->QueryInterface(riid
, ppvOut
);
167 // We can't create a background menu