[FONTEXT] Return the correct error in GetDisplayNameOf
[reactos.git] / dll / shellext / fontext / CFontExt.cpp
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: CFontExt implementation
5 * COPYRIGHT: Copyright 2019 Mark Jansen (mark.jansen@reactos.org)
6 */
7
8 #include "precomp.h"
9
10 WINE_DEFAULT_DEBUG_CHANNEL(fontext);
11
12
13 struct FolderViewColumns
14 {
15 int iResource;
16 DWORD dwDefaultState;
17 int cxChar;
18 int fmt;
19 };
20
21 static FolderViewColumns g_ColumnDefs[] =
22 {
23 { IDS_COL_NAME, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, 25, LVCFMT_LEFT },
24 };
25
26
27
28 // Should fix our headers..
29 EXTERN_C HRESULT WINAPI SHCreateFileExtractIconW(LPCWSTR pszPath, DWORD dwFileAttributes, REFIID riid, void **ppv);
30
31
32 // Helper functions to translate a guid to a readable name
33 bool GetInterfaceName(const WCHAR* InterfaceString, WCHAR* buf, size_t size)
34 {
35 WCHAR LocalBuf[100];
36 DWORD dwType = 0, dwDataSize = size * sizeof(WCHAR);
37
38 if (!SUCCEEDED(StringCchPrintfW(LocalBuf, _countof(LocalBuf), L"Interface\\%s", InterfaceString)))
39 return false;
40
41 return RegGetValueW(HKEY_CLASSES_ROOT, LocalBuf, NULL, RRF_RT_REG_SZ, &dwType, buf, &dwDataSize) == ERROR_SUCCESS;
42 }
43
44 WCHAR* g2s(REFCLSID iid)
45 {
46 static WCHAR buf[2][300];
47 static int idx = 0;
48
49 idx ^= 1;
50
51 LPOLESTR tmp;
52 HRESULT hr = ProgIDFromCLSID(iid, &tmp);
53 if (SUCCEEDED(hr))
54 {
55 wcscpy(buf[idx], tmp);
56 CoTaskMemFree(tmp);
57 return buf[idx];
58 }
59 StringFromGUID2(iid, buf[idx], _countof(buf[idx]));
60 if (GetInterfaceName(buf[idx], buf[idx], _countof(buf[idx])))
61 {
62 return buf[idx];
63 }
64 StringFromGUID2(iid, buf[idx], _countof(buf[idx]));
65
66 return buf[idx];
67 }
68
69
70 CFontExt::CFontExt()
71 {
72 InterlockedIncrement(&g_ModuleRefCnt);
73 }
74
75 CFontExt::~CFontExt()
76 {
77 InterlockedDecrement(&g_ModuleRefCnt);
78 }
79
80 // *** IShellFolder2 methods ***
81 STDMETHODIMP CFontExt::GetDefaultSearchGUID(GUID *lpguid)
82 {
83 ERR("%s() UNIMPLEMENTED\n", __FUNCTION__);
84 return E_NOTIMPL;
85 }
86
87 STDMETHODIMP CFontExt::EnumSearches(IEnumExtraSearch **ppenum)
88 {
89 ERR("%s() UNIMPLEMENTED\n", __FUNCTION__);
90 return E_NOTIMPL;
91 }
92
93 STDMETHODIMP CFontExt::GetDefaultColumn(DWORD dwReserved, ULONG *pSort, ULONG *pDisplay)
94 {
95 ERR("%s() UNIMPLEMENTED\n", __FUNCTION__);
96 return E_NOTIMPL;
97 }
98
99 STDMETHODIMP CFontExt::GetDefaultColumnState(UINT iColumn, SHCOLSTATEF *pcsFlags)
100 {
101 if (!pcsFlags || iColumn >= _countof(g_ColumnDefs))
102 return E_INVALIDARG;
103
104 *pcsFlags = g_ColumnDefs[iColumn].dwDefaultState;
105 return S_OK;
106 }
107
108 STDMETHODIMP CFontExt::GetDetailsEx(PCUITEMID_CHILD pidl, const SHCOLUMNID *pscid, VARIANT *pv)
109 {
110 ERR("%s() UNIMPLEMENTED\n", __FUNCTION__);
111 return E_NOTIMPL;
112 }
113
114 STDMETHODIMP CFontExt::GetDetailsOf(PCUITEMID_CHILD pidl, UINT iColumn, SHELLDETAILS *psd)
115 {
116 if (iColumn >= _countof(g_ColumnDefs))
117 return E_FAIL;
118
119 psd->cxChar = g_ColumnDefs[iColumn].cxChar;
120 psd->fmt = g_ColumnDefs[iColumn].fmt;
121
122 // No item requested, so return the column name
123 if (pidl == NULL)
124 {
125 return SHSetStrRet(&psd->str, _AtlBaseModule.GetResourceInstance(), g_ColumnDefs[iColumn].iResource);
126 }
127
128 // Validate that this pidl is the last one
129 PCUIDLIST_RELATIVE curpidl = ILGetNext(pidl);
130 if (curpidl->mkid.cb != 0)
131 {
132 ERR("ERROR, unhandled PIDL!\n");
133 return E_FAIL;
134 }
135
136 switch (iColumn)
137 {
138 case 0: /* Name, ReactOS specific? */
139 return GetDisplayNameOf(pidl, 0, &psd->str);
140 default:
141 break;
142 }
143
144 UNIMPLEMENTED;
145 return E_NOTIMPL;
146 }
147
148 STDMETHODIMP CFontExt::MapColumnToSCID(UINT iColumn, SHCOLUMNID *pscid)
149 {
150 //ERR("%s() UNIMPLEMENTED\n", __FUNCTION__);
151 return E_NOTIMPL;
152 }
153
154 // *** IShellFolder2 methods ***
155 STDMETHODIMP CFontExt::ParseDisplayName(HWND hwndOwner, LPBC pbc, LPOLESTR lpszDisplayName, DWORD *pchEaten, PIDLIST_RELATIVE *ppidl, DWORD *pdwAttributes)
156 {
157 ERR("%s() UNIMPLEMENTED\n", __FUNCTION__);
158 return E_NOTIMPL;
159 }
160
161 STDMETHODIMP CFontExt::EnumObjects(HWND hwndOwner, DWORD dwFlags, LPENUMIDLIST *ppEnumIDList)
162 {
163 return _CEnumFonts_CreateInstance(this, dwFlags, IID_PPV_ARG(IEnumIDList, ppEnumIDList));
164 }
165
166 STDMETHODIMP CFontExt::BindToObject(PCUIDLIST_RELATIVE pidl, LPBC pbcReserved, REFIID riid, LPVOID *ppvOut)
167 {
168 ERR("%s(riid=%S) UNIMPLEMENTED\n", __FUNCTION__, g2s(riid));
169 return E_NOTIMPL;
170 }
171
172 STDMETHODIMP CFontExt::BindToStorage(PCUIDLIST_RELATIVE pidl, LPBC pbcReserved, REFIID riid, LPVOID *ppvOut)
173 {
174 ERR("%s() UNIMPLEMENTED\n", __FUNCTION__);
175 return E_NOTIMPL;
176 }
177
178 STDMETHODIMP CFontExt::CompareIDs(LPARAM lParam, PCUIDLIST_RELATIVE pidl1, PCUIDLIST_RELATIVE pidl2)
179 {
180 const FontPidlEntry* fontEntry1 = _FontFromIL(pidl1);
181 const FontPidlEntry* fontEntry2 = _FontFromIL(pidl2);
182
183 if (!fontEntry1 || !fontEntry2)
184 return E_INVALIDARG;
185
186 int result = (int)fontEntry1->Index - (int)fontEntry2->Index;
187
188 return MAKE_COMPARE_HRESULT(result);
189 }
190
191 STDMETHODIMP CFontExt::CreateViewObject(HWND hwndOwner, REFIID riid, LPVOID *ppvOut)
192 {
193 HRESULT hr = E_NOINTERFACE;
194
195 *ppvOut = NULL;
196
197 if (IsEqualIID(riid, IID_IDropTarget))
198 {
199 // Needed to drop files into the fonts folder, we should probably install them?
200 ERR("IDropTarget not implemented\n");
201 hr = E_NOTIMPL;
202 }
203 else if (IsEqualIID(riid, IID_IContextMenu))
204 {
205 ERR("IContextMenu not implemented\n");
206 hr = E_NOTIMPL;
207 }
208 else if (IsEqualIID(riid, IID_IShellView))
209 {
210 // Just create a default shell folder view, and register ourself as folder
211 SFV_CREATE sfv = { sizeof(SFV_CREATE) };
212 sfv.pshf = this;
213 hr = SHCreateShellFolderView(&sfv, (IShellView**)ppvOut);
214 }
215
216 return hr;
217 }
218
219 STDMETHODIMP CFontExt::GetAttributesOf(UINT cidl, PCUITEMID_CHILD_ARRAY apidl, DWORD *rgfInOut)
220 {
221 if (!rgfInOut || !cidl || !apidl)
222 return E_INVALIDARG;
223
224 DWORD rgf = 0;
225 while (cidl > 0 && *apidl)
226 {
227 const FontPidlEntry* fontEntry = _FontFromIL(*apidl);
228 if (fontEntry)
229 {
230 // We don't support delete yet
231 rgf |= (/*SFGAO_CANDELETE |*/ SFGAO_HASPROPSHEET | SFGAO_CANCOPY | SFGAO_FILESYSTEM);
232 }
233 else
234 {
235 rgf = 0;
236 break;
237 }
238
239 apidl++;
240 cidl--;
241 }
242
243 *rgfInOut = rgf;
244 return S_OK;
245 }
246
247
248 STDMETHODIMP CFontExt::GetUIObjectOf(HWND hwndOwner, UINT cidl, PCUITEMID_CHILD_ARRAY apidl, REFIID riid, UINT * prgfInOut, LPVOID * ppvOut)
249 {
250 if (riid == IID_IContextMenu ||
251 riid == IID_IContextMenu2 ||
252 riid == IID_IContextMenu3)
253 {
254 return _CFontMenu_CreateInstance(hwndOwner, cidl, apidl, this, riid, ppvOut);
255 }
256 else if (riid == IID_IExtractIconA || riid == IID_IExtractIconW)
257 {
258 if (cidl == 1)
259 {
260 const FontPidlEntry* fontEntry = _FontFromIL(*apidl);
261 if (fontEntry)
262 {
263 DWORD dwAttributes = FILE_ATTRIBUTE_NORMAL;
264 CStringW File = g_FontCache->Filename(fontEntry);
265 // Just create a default icon extractor based on the filename
266 // We might want to create a preview with the font to get really fancy one day.
267 return SHCreateFileExtractIconW(File, dwAttributes, riid, ppvOut);
268 }
269 }
270 else
271 {
272 ERR("IID_IExtractIcon with cidl != 1 UNIMPLEMENTED\n");
273 }
274 }
275 else if (riid == IID_IDataObject)
276 {
277 if (cidl >= 1)
278 {
279 return _CDataObject_CreateInstance(m_Folder, cidl, apidl, riid, ppvOut);
280 }
281 else
282 {
283 ERR("IID_IDataObject with cidl == 0 UNIMPLEMENTED\n");
284 }
285 }
286
287 //ERR("%s(riid=%S) UNIMPLEMENTED\n", __FUNCTION__, g2s(riid));
288 return E_NOTIMPL;
289 }
290
291 STDMETHODIMP CFontExt::GetDisplayNameOf(PCUITEMID_CHILD pidl, DWORD dwFlags, LPSTRRET strRet)
292 {
293 if (!pidl)
294 return E_NOTIMPL;
295
296 // Validate that this pidl is the last one
297 PCUIDLIST_RELATIVE curpidl = ILGetNext(pidl);
298 if (curpidl->mkid.cb != 0)
299 {
300 ERR("ERROR, unhandled PIDL!\n");
301 return E_FAIL;
302 }
303
304 const FontPidlEntry* fontEntry = _FontFromIL(pidl);
305 if (!fontEntry)
306 return E_FAIL;
307
308 return SHSetStrRet(strRet, fontEntry->Name);
309 }
310
311 STDMETHODIMP CFontExt::SetNameOf(HWND hwndOwner, PCUITEMID_CHILD pidl, LPCOLESTR lpName, DWORD dwFlags, PITEMID_CHILD *pPidlOut)
312 {
313 ERR("%s() UNIMPLEMENTED\n", __FUNCTION__);
314 return E_NOTIMPL;
315 }
316
317 // *** IPersistFolder2 methods ***
318 STDMETHODIMP CFontExt::GetCurFolder(LPITEMIDLIST *ppidl)
319 {
320 if (ppidl && m_Folder)
321 {
322 *ppidl = ILClone(m_Folder);
323 return S_OK;
324 }
325
326 return E_POINTER;
327 }
328
329
330 // *** IPersistFolder methods ***
331 STDMETHODIMP CFontExt::Initialize(LPCITEMIDLIST pidl)
332 {
333 WCHAR PidlPath[MAX_PATH + 1] = {0}, Expected[MAX_PATH + 1];
334 if (!SHGetPathFromIDListW(pidl, PidlPath))
335 {
336 ERR("Unable to extract path from pidl\n");
337 return E_FAIL;
338 }
339
340 HRESULT hr = SHGetFolderPathW(NULL, CSIDL_FONTS, NULL, 0, Expected);
341 if (!SUCCEEDED(hr))
342 {
343 ERR("Unable to get fonts path (0x%x)\n", hr);
344 return hr;
345 }
346
347 if (_wcsicmp(PidlPath, Expected))
348 {
349 ERR("CFontExt View initializing on unexpected folder: '%S'\n", PidlPath);
350 return E_FAIL;
351 }
352
353 m_Folder.Attach(ILClone(pidl));
354
355 return S_OK;
356 }
357
358
359 // *** IPersist methods ***
360 STDMETHODIMP CFontExt::GetClassID(CLSID *lpClassId)
361 {
362 *lpClassId = CLSID_CFontExt;
363 return S_OK;
364 }
365