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-2021 Mark Jansen <mark.jansen@reactos.org>
6 * Copyright 2019 Katayama Hirofumi MZ (katayama.hirofumi.mz@gmail.com)
7 */
8
9 #include "precomp.h"
10 #include "undocgdi.h" // for GetFontResourceInfoW
11
12 WINE_DEFAULT_DEBUG_CHANNEL(fontext);
13
14 #ifndef SHCIDS_ALLFIELDS
15 #define SHCIDS_ALLFIELDS 0x80000000L
16 #endif
17
18 struct FolderViewColumns
19 {
20 int iResource;
21 DWORD dwDefaultState;
22 int cxChar;
23 int fmt;
24 };
25
26 enum font_columns
27 {
28 FONTEXT_COL_NAME,
29 FONTEXT_COL_FILENAME,
30 FONTEXT_COL_SIZE,
31 FONTEXT_COL_MODIFIED,
32 FONTEXT_COL_ATTR,
33 };
34
35 static FolderViewColumns g_ColumnDefs[] =
36 {
37 { IDS_COL_NAME, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, 25, LVCFMT_LEFT },
38 { IDS_COL_FILENAME, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, 20, LVCFMT_LEFT },
39 { IDS_COL_SIZE, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, 10, LVCFMT_RIGHT },
40 { IDS_COL_MODIFIED, SHCOLSTATE_TYPE_DATE | SHCOLSTATE_ONBYDEFAULT, 15, LVCFMT_LEFT },
41 { IDS_COL_ATTR, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, 12, LVCFMT_RIGHT },
42 };
43
44
45 // Helper functions to translate a guid to a readable name
GetInterfaceName(const WCHAR * InterfaceString,WCHAR * buf,size_t size)46 bool GetInterfaceName(const WCHAR* InterfaceString, WCHAR* buf, size_t size)
47 {
48 WCHAR LocalBuf[100];
49 DWORD dwType = 0, dwDataSize = size * sizeof(WCHAR);
50
51 if (!SUCCEEDED(StringCchPrintfW(LocalBuf, _countof(LocalBuf), L"Interface\\%s", InterfaceString)))
52 return false;
53
54 return RegGetValueW(HKEY_CLASSES_ROOT, LocalBuf, NULL, RRF_RT_REG_SZ, &dwType, buf, &dwDataSize) == ERROR_SUCCESS;
55 }
56
g2s(REFCLSID iid)57 WCHAR* g2s(REFCLSID iid)
58 {
59 static WCHAR buf[2][300];
60 static int idx = 0;
61
62 idx ^= 1;
63
64 LPOLESTR tmp;
65 HRESULT hr = ProgIDFromCLSID(iid, &tmp);
66 if (SUCCEEDED(hr))
67 {
68 wcscpy(buf[idx], tmp);
69 CoTaskMemFree(tmp);
70 return buf[idx];
71 }
72 StringFromGUID2(iid, buf[idx], _countof(buf[idx]));
73 if (GetInterfaceName(buf[idx], buf[idx], _countof(buf[idx])))
74 {
75 return buf[idx];
76 }
77 StringFromGUID2(iid, buf[idx], _countof(buf[idx]));
78
79 return buf[idx];
80 }
81
82
CFontExt()83 CFontExt::CFontExt()
84 {
85 InterlockedIncrement(&g_ModuleRefCnt);
86 }
87
~CFontExt()88 CFontExt::~CFontExt()
89 {
90 InterlockedDecrement(&g_ModuleRefCnt);
91 }
92
93 // *** IShellFolder2 methods ***
GetDefaultSearchGUID(GUID * lpguid)94 STDMETHODIMP CFontExt::GetDefaultSearchGUID(GUID *lpguid)
95 {
96 ERR("%s() UNIMPLEMENTED\n", __FUNCTION__);
97 return E_NOTIMPL;
98 }
99
EnumSearches(IEnumExtraSearch ** ppenum)100 STDMETHODIMP CFontExt::EnumSearches(IEnumExtraSearch **ppenum)
101 {
102 ERR("%s() UNIMPLEMENTED\n", __FUNCTION__);
103 return E_NOTIMPL;
104 }
105
GetDefaultColumn(DWORD dwReserved,ULONG * pSort,ULONG * pDisplay)106 STDMETHODIMP CFontExt::GetDefaultColumn(DWORD dwReserved, ULONG *pSort, ULONG *pDisplay)
107 {
108 ERR("%s() UNIMPLEMENTED\n", __FUNCTION__);
109 return E_NOTIMPL;
110 }
111
GetDefaultColumnState(UINT iColumn,SHCOLSTATEF * pcsFlags)112 STDMETHODIMP CFontExt::GetDefaultColumnState(UINT iColumn, SHCOLSTATEF *pcsFlags)
113 {
114 if (!pcsFlags || iColumn >= _countof(g_ColumnDefs))
115 return E_INVALIDARG;
116
117 *pcsFlags = g_ColumnDefs[iColumn].dwDefaultState;
118 return S_OK;
119 }
120
GetDetailsEx(PCUITEMID_CHILD pidl,const SHCOLUMNID * pscid,VARIANT * pv)121 STDMETHODIMP CFontExt::GetDetailsEx(PCUITEMID_CHILD pidl, const SHCOLUMNID *pscid, VARIANT *pv)
122 {
123 ERR("%s() UNIMPLEMENTED\n", __FUNCTION__);
124 return E_NOTIMPL;
125 }
126
GetDetailsOf(PCUITEMID_CHILD pidl,UINT iColumn,SHELLDETAILS * psd)127 STDMETHODIMP CFontExt::GetDetailsOf(PCUITEMID_CHILD pidl, UINT iColumn, SHELLDETAILS *psd)
128 {
129 if (iColumn >= _countof(g_ColumnDefs))
130 return E_FAIL;
131
132 psd->cxChar = g_ColumnDefs[iColumn].cxChar;
133 psd->fmt = g_ColumnDefs[iColumn].fmt;
134
135 // No item requested, so return the column name
136 if (pidl == NULL)
137 {
138 return SHSetStrRet(&psd->str, _AtlBaseModule.GetResourceInstance(), g_ColumnDefs[iColumn].iResource);
139 }
140
141 // Validate that this pidl is the last one
142 PCUIDLIST_RELATIVE curpidl = ILGetNext(pidl);
143 if (curpidl->mkid.cb != 0)
144 {
145 ERR("ERROR, unhandled PIDL!\n");
146 return E_FAIL;
147 }
148
149 // Name, ReactOS specific?
150 if (iColumn == FONTEXT_COL_NAME)
151 return GetDisplayNameOf(pidl, 0, &psd->str);
152
153 const FontPidlEntry* fontEntry = _FontFromIL(pidl);
154 if (!fontEntry)
155 {
156 ERR("ERROR, not a font PIDL!\n");
157 return E_FAIL;
158 }
159
160 // If we got here, we are in details view!
161 auto info = g_FontCache->Find(fontEntry);
162 if (info == nullptr)
163 {
164 ERR("Unable to query info about %S\n", fontEntry->Name);
165 return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
166 }
167
168 int ret;
169 CStringA AttrLetters;
170 DWORD dwAttributes;
171 SYSTEMTIME time;
172 switch (iColumn)
173 {
174 case FONTEXT_COL_FILENAME:
175 return SHSetStrRet(&psd->str, PathFindFileNameW(info->File()));
176 case FONTEXT_COL_SIZE:
177 psd->str.uType = STRRET_CSTR;
178 StrFormatKBSizeA(info->FileSize().QuadPart, psd->str.cStr, MAX_PATH);
179 return S_OK;
180 case FONTEXT_COL_MODIFIED:
181 psd->str.uType = STRRET_CSTR;
182 FileTimeToSystemTime(&info->FileWriteTime(), &time);
183 ret = GetDateFormatA(LOCALE_USER_DEFAULT, DATE_SHORTDATE, &time, NULL, psd->str.cStr, MAX_PATH);
184 if (ret < 1)
185 {
186 ERR("GetDateFormatA failed\n");
187 return E_FAIL;
188 }
189 psd->str.cStr[ret-1] = ' ';
190 GetTimeFormatA(LOCALE_USER_DEFAULT, TIME_NOSECONDS, &time, NULL, &psd->str.cStr[ret], MAX_PATH - ret);
191 return S_OK;
192 case FONTEXT_COL_ATTR:
193 AttrLetters.LoadString(IDS_COL_ATTR_LETTERS);
194 if (AttrLetters.GetLength() != 5)
195 {
196 ERR("IDS_COL_ATTR_LETTERS does not contain 5 letters!\n");
197 return E_FAIL;
198 }
199 psd->str.uType = STRRET_CSTR;
200 dwAttributes = info->FileAttributes();
201 ret = 0;
202 if (dwAttributes & FILE_ATTRIBUTE_READONLY)
203 psd->str.cStr[ret++] = AttrLetters[0];
204 if (dwAttributes & FILE_ATTRIBUTE_HIDDEN)
205 psd->str.cStr[ret++] = AttrLetters[1];
206 if (dwAttributes & FILE_ATTRIBUTE_SYSTEM)
207 psd->str.cStr[ret++] = AttrLetters[2];
208 if (dwAttributes & FILE_ATTRIBUTE_ARCHIVE)
209 psd->str.cStr[ret++] = AttrLetters[3];
210 if (dwAttributes & FILE_ATTRIBUTE_COMPRESSED)
211 psd->str.cStr[ret++] = AttrLetters[4];
212 psd->str.cStr[ret] = '\0';
213 return S_OK;
214 default:
215 break;
216 }
217
218 UNIMPLEMENTED;
219 return E_NOTIMPL;
220 }
221
MapColumnToSCID(UINT iColumn,SHCOLUMNID * pscid)222 STDMETHODIMP CFontExt::MapColumnToSCID(UINT iColumn, SHCOLUMNID *pscid)
223 {
224 //ERR("%s() UNIMPLEMENTED\n", __FUNCTION__);
225 return E_NOTIMPL;
226 }
227
228 // *** IShellFolder2 methods ***
ParseDisplayName(HWND hwndOwner,LPBC pbc,LPOLESTR lpszDisplayName,DWORD * pchEaten,PIDLIST_RELATIVE * ppidl,DWORD * pdwAttributes)229 STDMETHODIMP CFontExt::ParseDisplayName(HWND hwndOwner, LPBC pbc, LPOLESTR lpszDisplayName, DWORD *pchEaten, PIDLIST_RELATIVE *ppidl, DWORD *pdwAttributes)
230 {
231 ERR("%s() UNIMPLEMENTED\n", __FUNCTION__);
232 return E_NOTIMPL;
233 }
234
EnumObjects(HWND hwndOwner,DWORD dwFlags,LPENUMIDLIST * ppEnumIDList)235 STDMETHODIMP CFontExt::EnumObjects(HWND hwndOwner, DWORD dwFlags, LPENUMIDLIST *ppEnumIDList)
236 {
237 return _CEnumFonts_CreateInstance(this, dwFlags, IID_PPV_ARG(IEnumIDList, ppEnumIDList));
238 }
239
BindToObject(PCUIDLIST_RELATIVE pidl,LPBC pbcReserved,REFIID riid,LPVOID * ppvOut)240 STDMETHODIMP CFontExt::BindToObject(PCUIDLIST_RELATIVE pidl, LPBC pbcReserved, REFIID riid, LPVOID *ppvOut)
241 {
242 ERR("%s(riid=%S) UNIMPLEMENTED\n", __FUNCTION__, g2s(riid));
243 return E_NOTIMPL;
244 }
245
BindToStorage(PCUIDLIST_RELATIVE pidl,LPBC pbcReserved,REFIID riid,LPVOID * ppvOut)246 STDMETHODIMP CFontExt::BindToStorage(PCUIDLIST_RELATIVE pidl, LPBC pbcReserved, REFIID riid, LPVOID *ppvOut)
247 {
248 ERR("%s() UNIMPLEMENTED\n", __FUNCTION__);
249 return E_NOTIMPL;
250 }
251
CompareIDs(LPARAM lParam,PCUIDLIST_RELATIVE pidl1,PCUIDLIST_RELATIVE pidl2)252 STDMETHODIMP CFontExt::CompareIDs(LPARAM lParam, PCUIDLIST_RELATIVE pidl1, PCUIDLIST_RELATIVE pidl2)
253 {
254 const FontPidlEntry* fontEntry1 = _FontFromIL(pidl1);
255 const FontPidlEntry* fontEntry2 = _FontFromIL(pidl2);
256
257 if (!fontEntry1 || !fontEntry2)
258 return E_INVALIDARG;
259
260 int result;
261 DWORD sortMode = lParam & 0xFFFF0000;
262 DWORD column = lParam & 0x0000FFFF;
263 if (sortMode == SHCIDS_ALLFIELDS)
264 {
265 UNIMPLEMENTED;
266 result = (int)fontEntry1->Index - (int)fontEntry2->Index;
267 }
268 else
269 {
270 auto info1 = g_FontCache->Find(fontEntry1);
271 auto info2 = g_FontCache->Find(fontEntry2);
272
273 if (!info1 || !info2)
274 {
275 ERR("Unable to find font %S or %S in cache!\n", fontEntry1->Name, fontEntry2->Name);
276 return E_INVALIDARG;
277 }
278
279 switch (column)
280 {
281 case 0xffff:
282 /* ROS bug? */
283 case FONTEXT_COL_NAME:
284 // These items are already ordered by name
285 result = (int)fontEntry1->Index - (int)fontEntry2->Index;
286 break;
287 case FONTEXT_COL_FILENAME:
288 result = _wcsicmp(PathFindFileNameW(info1->File()), PathFindFileNameW(info2->File()));
289 break;
290 case FONTEXT_COL_SIZE:
291 result = (int)info1->FileSize().HighPart - info2->FileSize().HighPart;
292 if (result == 0)
293 result = (int)info1->FileSize().LowPart - info2->FileSize().LowPart;
294 break;
295 case FONTEXT_COL_MODIFIED:
296 result = CompareFileTime(&info1->FileWriteTime(), &info2->FileWriteTime());
297 break;
298 case FONTEXT_COL_ATTR:
299 // FIXME: how to compare attributes?
300 result = (int)info1->FileAttributes() - info2->FileAttributes();
301 break;
302 default:
303 ERR("Unimplemented column %u\n", column);
304 return E_INVALIDARG;
305 }
306 }
307
308 return MAKE_COMPARE_HRESULT(result);
309 }
310
CreateViewObject(HWND hwndOwner,REFIID riid,LPVOID * ppvOut)311 STDMETHODIMP CFontExt::CreateViewObject(HWND hwndOwner, REFIID riid, LPVOID *ppvOut)
312 {
313 HRESULT hr = E_NOINTERFACE;
314
315 *ppvOut = NULL;
316
317 if (IsEqualIID(riid, IID_IDropTarget))
318 {
319 ERR("IDropTarget not implemented\n");
320 *ppvOut = static_cast<IDropTarget *>(this);
321 AddRef();
322 hr = S_OK;
323 }
324 else if (IsEqualIID(riid, IID_IContextMenu))
325 {
326 ERR("IContextMenu not implemented\n");
327 hr = E_NOTIMPL;
328 }
329 else if (IsEqualIID(riid, IID_IShellView))
330 {
331 // Just create a default shell folder view, and register ourself as folder
332 SFV_CREATE sfv = { sizeof(SFV_CREATE) };
333 sfv.pshf = this;
334 hr = SHCreateShellFolderView(&sfv, (IShellView**)ppvOut);
335 }
336
337 return hr;
338 }
339
GetAttributesOf(UINT cidl,PCUITEMID_CHILD_ARRAY apidl,DWORD * rgfInOut)340 STDMETHODIMP CFontExt::GetAttributesOf(UINT cidl, PCUITEMID_CHILD_ARRAY apidl, DWORD *rgfInOut)
341 {
342 if (!rgfInOut || !cidl || !apidl)
343 return E_INVALIDARG;
344
345 DWORD rgf = 0;
346 while (cidl > 0 && *apidl)
347 {
348 const FontPidlEntry* fontEntry = _FontFromIL(*apidl);
349 if (fontEntry)
350 {
351 // We don't support delete yet
352 rgf |= (/*SFGAO_CANDELETE |*/ SFGAO_HASPROPSHEET | SFGAO_CANCOPY | SFGAO_FILESYSTEM);
353 }
354 else
355 {
356 rgf = 0;
357 break;
358 }
359
360 apidl++;
361 cidl--;
362 }
363
364 *rgfInOut = rgf;
365 return S_OK;
366 }
367
368
GetUIObjectOf(HWND hwndOwner,UINT cidl,PCUITEMID_CHILD_ARRAY apidl,REFIID riid,UINT * prgfInOut,LPVOID * ppvOut)369 STDMETHODIMP CFontExt::GetUIObjectOf(HWND hwndOwner, UINT cidl, PCUITEMID_CHILD_ARRAY apidl, REFIID riid, UINT * prgfInOut, LPVOID * ppvOut)
370 {
371 if (riid == IID_IContextMenu ||
372 riid == IID_IContextMenu2 ||
373 riid == IID_IContextMenu3)
374 {
375 return _CFontMenu_CreateInstance(hwndOwner, cidl, apidl, this, riid, ppvOut);
376 }
377 else if (riid == IID_IExtractIconA || riid == IID_IExtractIconW)
378 {
379 if (cidl == 1)
380 {
381 const FontPidlEntry* fontEntry = _FontFromIL(*apidl);
382 if (fontEntry)
383 {
384 DWORD dwAttributes = FILE_ATTRIBUTE_NORMAL;
385 CStringW File = g_FontCache->Filename(g_FontCache->Find(fontEntry));
386 // Just create a default icon extractor based on the filename
387 // We might want to create a preview with the font to get really fancy one day.
388 return SHCreateFileExtractIconW(File, dwAttributes, riid, ppvOut);
389 }
390 }
391 else
392 {
393 ERR("IID_IExtractIcon with cidl != 1 UNIMPLEMENTED\n");
394 }
395 }
396 else if (riid == IID_IDataObject)
397 {
398 if (cidl >= 1)
399 {
400 return _CDataObject_CreateInstance(m_Folder, cidl, apidl, riid, ppvOut);
401 }
402 else
403 {
404 ERR("IID_IDataObject with cidl == 0 UNIMPLEMENTED\n");
405 }
406 }
407
408 //ERR("%s(riid=%S) UNIMPLEMENTED\n", __FUNCTION__, g2s(riid));
409 return E_NOTIMPL;
410 }
411
GetDisplayNameOf(PCUITEMID_CHILD pidl,DWORD dwFlags,LPSTRRET strRet)412 STDMETHODIMP CFontExt::GetDisplayNameOf(PCUITEMID_CHILD pidl, DWORD dwFlags, LPSTRRET strRet)
413 {
414 if (!pidl)
415 return E_NOTIMPL;
416
417 // Validate that this pidl is the last one
418 PCUIDLIST_RELATIVE curpidl = ILGetNext(pidl);
419 if (curpidl->mkid.cb != 0)
420 {
421 ERR("ERROR, unhandled PIDL!\n");
422 return E_FAIL;
423 }
424
425 const FontPidlEntry* fontEntry = _FontFromIL(pidl);
426 if (!fontEntry)
427 return E_FAIL;
428
429 if (dwFlags & SHGDN_FORPARSING)
430 {
431 CStringW File = g_FontCache->Filename(g_FontCache->Find(fontEntry), true);
432 if (!File.IsEmpty())
433 {
434 return SHSetStrRet(strRet, File);
435 }
436 }
437
438 return SHSetStrRet(strRet, fontEntry->Name);
439 }
440
SetNameOf(HWND hwndOwner,PCUITEMID_CHILD pidl,LPCOLESTR lpName,DWORD dwFlags,PITEMID_CHILD * pPidlOut)441 STDMETHODIMP CFontExt::SetNameOf(HWND hwndOwner, PCUITEMID_CHILD pidl, LPCOLESTR lpName, DWORD dwFlags, PITEMID_CHILD *pPidlOut)
442 {
443 ERR("%s() UNIMPLEMENTED\n", __FUNCTION__);
444 return E_NOTIMPL;
445 }
446
447 // *** IPersistFolder2 methods ***
GetCurFolder(LPITEMIDLIST * ppidl)448 STDMETHODIMP CFontExt::GetCurFolder(LPITEMIDLIST *ppidl)
449 {
450 if (ppidl && m_Folder)
451 {
452 *ppidl = ILClone(m_Folder);
453 return S_OK;
454 }
455
456 return E_POINTER;
457 }
458
459
460 // *** IPersistFolder methods ***
Initialize(LPCITEMIDLIST pidl)461 STDMETHODIMP CFontExt::Initialize(LPCITEMIDLIST pidl)
462 {
463 WCHAR PidlPath[MAX_PATH + 1] = {0}, FontsDir[MAX_PATH + 1];
464 if (!SHGetPathFromIDListW(pidl, PidlPath))
465 {
466 ERR("Unable to extract path from pidl\n");
467 return E_FAIL;
468 }
469
470 HRESULT hr = SHGetFolderPathW(NULL, CSIDL_FONTS, NULL, 0, FontsDir);
471 if (FAILED_UNEXPECTEDLY(hr))
472 {
473 ERR("Unable to get fonts path (0x%x)\n", hr);
474 return hr;
475 }
476
477 if (_wcsicmp(PidlPath, FontsDir))
478 {
479 ERR("CFontExt View initializing on unexpected folder: '%S'\n", PidlPath);
480 return E_FAIL;
481 }
482
483 m_Folder.Attach(ILClone(pidl));
484 StringCchCatW(FontsDir, _countof(FontsDir), L"\\");
485 g_FontCache->SetFontDir(FontsDir);
486
487 return S_OK;
488 }
489
490
491 // *** IPersist methods ***
GetClassID(CLSID * lpClassId)492 STDMETHODIMP CFontExt::GetClassID(CLSID *lpClassId)
493 {
494 *lpClassId = CLSID_CFontExt;
495 return S_OK;
496 }
497
498 // *** IDropTarget methods ***
DragEnter(IDataObject * pDataObj,DWORD grfKeyState,POINTL pt,DWORD * pdwEffect)499 STDMETHODIMP CFontExt::DragEnter(IDataObject* pDataObj, DWORD grfKeyState, POINTL pt, DWORD* pdwEffect)
500 {
501 *pdwEffect = DROPEFFECT_NONE;
502
503 CDataObjectHIDA cida(pDataObj);
504 if (FAILED_UNEXPECTEDLY(cida.hr()))
505 return cida.hr();
506
507 *pdwEffect = DROPEFFECT_COPY;
508 return S_OK;
509 }
510
DragOver(DWORD grfKeyState,POINTL pt,DWORD * pdwEffect)511 STDMETHODIMP CFontExt::DragOver(DWORD grfKeyState, POINTL pt, DWORD* pdwEffect)
512 {
513 return S_OK;
514 }
515
DragLeave()516 STDMETHODIMP CFontExt::DragLeave()
517 {
518 return S_OK;
519 }
520
Drop(IDataObject * pDataObj,DWORD grfKeyState,POINTL pt,DWORD * pdwEffect)521 STDMETHODIMP CFontExt::Drop(IDataObject* pDataObj, DWORD grfKeyState, POINTL pt, DWORD* pdwEffect)
522 {
523 *pdwEffect = DROPEFFECT_NONE;
524
525 CDataObjectHIDA cida(pDataObj);
526 if (!cida)
527 return E_UNEXPECTED;
528
529 PCUIDLIST_ABSOLUTE pidlParent = HIDA_GetPIDLFolder(cida);
530 if (!pidlParent)
531 {
532 ERR("pidlParent is NULL\n");
533 return E_FAIL;
534 }
535
536 BOOL bOK = TRUE;
537 CAtlArray<CStringW> FontPaths;
538 for (UINT n = 0; n < cida->cidl; ++n)
539 {
540 PCUIDLIST_RELATIVE pidlRelative = HIDA_GetPIDLItem(cida, n);
541 if (!pidlRelative)
542 continue;
543
544 PIDLIST_ABSOLUTE pidl = ILCombine(pidlParent, pidlRelative);
545 if (!pidl)
546 {
547 ERR("ILCombine failed\n");
548 bOK = FALSE;
549 break;
550 }
551
552 WCHAR szPath[MAX_PATH];
553 BOOL ret = SHGetPathFromIDListW(pidl, szPath);
554 ILFree(pidl);
555
556 if (!ret)
557 {
558 ERR("SHGetPathFromIDListW failed\n");
559 bOK = FALSE;
560 break;
561 }
562
563 if (PathIsDirectoryW(szPath))
564 {
565 ERR("PathIsDirectory\n");
566 bOK = FALSE;
567 break;
568 }
569
570 LPCWSTR pchDotExt = PathFindExtensionW(szPath);
571 if (!IsFontDotExt(pchDotExt))
572 {
573 ERR("'%S' is not supported\n", pchDotExt);
574 bOK = FALSE;
575 break;
576 }
577
578 FontPaths.Add(szPath);
579 }
580
581 if (!bOK)
582 return E_FAIL;
583
584 CRegKey keyFonts;
585 if (keyFonts.Open(FONT_HIVE, FONT_KEY, KEY_WRITE) != ERROR_SUCCESS)
586 {
587 ERR("keyFonts.Open failed\n");
588 return E_FAIL;
589 }
590
591 for (size_t iItem = 0; iItem < FontPaths.GetCount(); ++iItem)
592 {
593 HRESULT hr = DoInstallFontFile(FontPaths[iItem], g_FontCache->FontPath(), keyFonts.m_hKey);
594 if (FAILED_UNEXPECTEDLY(hr))
595 {
596 bOK = FALSE;
597 break;
598 }
599 }
600
601 // Invalidate our cache
602 g_FontCache->Read();
603
604 // Notify the system that a font was added
605 SendMessageW(HWND_BROADCAST, WM_FONTCHANGE, 0, 0);
606
607 // Notify the shell that the folder contents are changed
608 SHChangeNotify(SHCNE_UPDATEDIR, SHCNF_PATHW, g_FontCache->FontPath().GetString(), NULL);
609
610 // TODO: Show message
611
612 return bOK ? S_OK : E_FAIL;
613 }
614
DoInstallFontFile(LPCWSTR pszFontPath,LPCWSTR pszFontsDir,HKEY hkeyFonts)615 HRESULT CFontExt::DoInstallFontFile(LPCWSTR pszFontPath, LPCWSTR pszFontsDir, HKEY hkeyFonts)
616 {
617 WCHAR szDestFile[MAX_PATH];
618
619 // Add this font to the font list, so we can query the name
620 if (!AddFontResourceW(pszFontPath))
621 {
622 ERR("AddFontResourceW('%S') failed\n", pszFontPath);
623 DeleteFileW(szDestFile);
624 return E_FAIL;
625 }
626
627 CStringW strFontName;
628 HRESULT hr = DoGetFontTitle(pszFontPath, strFontName);
629
630 // We got the name, remove it again
631 RemoveFontResourceW(pszFontPath);
632
633 if (!SUCCEEDED(hr))
634 {
635 ERR("DoGetFontTitle failed (err=0x%x)!\n", hr);
636 return hr;
637 }
638
639 StringCchCopyW(szDestFile, sizeof(szDestFile), pszFontsDir);
640
641 LPCWSTR pszFileTitle = PathFindFileName(pszFontPath);
642 PathAppendW(szDestFile, pszFileTitle);
643 if (!CopyFileW(pszFontPath, szDestFile, FALSE))
644 {
645 ERR("CopyFileW('%S', '%S') failed\n", pszFontPath, szDestFile);
646 return E_FAIL;
647 }
648
649 DWORD cbData = (wcslen(pszFileTitle) + 1) * sizeof(WCHAR);
650 LONG nError = RegSetValueExW(hkeyFonts, strFontName, 0, REG_SZ,
651 (const BYTE *)pszFileTitle, cbData);
652 if (nError)
653 {
654 ERR("RegSetValueExW failed with %ld\n", nError);
655 DeleteFileW(szDestFile);
656 return E_FAIL;
657 }
658
659 AddFontResourceW(szDestFile);
660
661 return S_OK;
662 }
663
664 HRESULT
DoGetFontTitle(IN LPCWSTR pszFontPath,OUT CStringW & strFontName)665 CFontExt::DoGetFontTitle(IN LPCWSTR pszFontPath, OUT CStringW& strFontName)
666 {
667 DWORD cbInfo = 0;
668 BOOL ret = GetFontResourceInfoW(pszFontPath, &cbInfo, NULL, 1);
669 if (!ret || !cbInfo)
670 {
671 ERR("GetFontResourceInfoW failed (err: %u)\n", GetLastError());
672 return E_FAIL;
673 }
674
675 LPWSTR pszBuffer = strFontName.GetBuffer(cbInfo / sizeof(WCHAR));
676 ret = GetFontResourceInfoW(pszFontPath, &cbInfo, pszBuffer, 1);
677 DWORD dwErr = GetLastError();;
678 strFontName.ReleaseBuffer();
679 if (ret)
680 {
681 TRACE("pszFontName: %S\n", (LPCWSTR)strFontName);
682 return S_OK;
683 }
684
685 ERR("GetFontResourceInfoW failed (err: %u)\n", dwErr);
686 return E_FAIL;
687 }
688