[SHELL32] SHChangeNotify: Use tree for CDirectoryList (#6784)
[reactos.git] / dll / shellext / fontext / CFontExt.cpp
index c157104..30a6291 100644 (file)
@@ -2,14 +2,18 @@
  * PROJECT:     ReactOS Font Shell Extension
  * LICENSE:     GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
  * PURPOSE:     CFontExt implementation
- * COPYRIGHT:   Copyright 2019,2020 Mark Jansen (mark.jansen@reactos.org)
+ * COPYRIGHT:   Copyright 2019-2021 Mark Jansen <mark.jansen@reactos.org>
  *              Copyright 2019 Katayama Hirofumi MZ (katayama.hirofumi.mz@gmail.com)
  */
 
 #include "precomp.h"
+#include "undocgdi.h" // for GetFontResourceInfoW
 
 WINE_DEFAULT_DEBUG_CHANNEL(fontext);
 
+#ifndef SHCIDS_ALLFIELDS
+#define SHCIDS_ALLFIELDS 0x80000000L
+#endif
 
 struct FolderViewColumns
 {
@@ -19,6 +23,15 @@ struct FolderViewColumns
     int fmt;
 };
 
+enum font_columns
+{
+    FONTEXT_COL_NAME,
+    FONTEXT_COL_FILENAME,
+    FONTEXT_COL_SIZE,
+    FONTEXT_COL_MODIFIED,
+    FONTEXT_COL_ATTR,
+};
+
 static FolderViewColumns g_ColumnDefs[] =
 {
     { IDS_COL_NAME,      SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT,   25, LVCFMT_LEFT },
@@ -29,11 +42,6 @@ static FolderViewColumns g_ColumnDefs[] =
 };
 
 
-
-// Should fix our headers..
-EXTERN_C HRESULT WINAPI SHCreateFileExtractIconW(LPCWSTR pszPath, DWORD dwFileAttributes, REFIID riid, void **ppv);
-
-
 // Helper functions to translate a guid to a readable name
 bool GetInterfaceName(const WCHAR* InterfaceString, WCHAR* buf, size_t size)
 {
@@ -139,7 +147,7 @@ STDMETHODIMP CFontExt::GetDetailsOf(PCUITEMID_CHILD pidl, UINT iColumn, SHELLDET
     }
 
     // Name, ReactOS specific?
-    if (iColumn == 0)
+    if (iColumn == FONTEXT_COL_NAME)
         return GetDisplayNameOf(pidl, 0, &psd->str);
 
     const FontPidlEntry* fontEntry = _FontFromIL(pidl);
@@ -150,41 +158,28 @@ STDMETHODIMP CFontExt::GetDetailsOf(PCUITEMID_CHILD pidl, UINT iColumn, SHELLDET
     }
 
     // If we got here, we are in details view!
-    // Let's see if we got info about this file that we can re-use
-    if (m_LastDetailsFontName != fontEntry->Name)
+    auto info = g_FontCache->Find(fontEntry);
+    if (info == nullptr)
     {
-        CStringW File = g_FontCache->Filename(fontEntry, true);
-        HANDLE hFile = FindFirstFileW(File, &m_LastDetailsFileData);
-        if (hFile == INVALID_HANDLE_VALUE)
-        {
-            m_LastDetailsFontName.Empty();
-            ERR("Unable to query info about %S\n", File.GetString());
-            return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
-        }
-        FindClose(hFile);
-        m_LastDetailsFontName = fontEntry->Name;
+        ERR("Unable to query info about %S\n", fontEntry->Name);
+        return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
     }
 
-    // Most code borrowed from CFSFolder::GetDetailsOf
-    FILETIME lft;
-    SYSTEMTIME time;
     int ret;
-    LARGE_INTEGER FileSize;
     CStringA AttrLetters;
+    DWORD dwAttributes;
+    SYSTEMTIME time;
     switch (iColumn)
     {
-    case 1: // Filename
-        return SHSetStrRet(&psd->str, m_LastDetailsFileData.cFileName);
-    case 2: // Size
+    case FONTEXT_COL_FILENAME:
+        return SHSetStrRet(&psd->str, PathFindFileNameW(info->File()));
+    case FONTEXT_COL_SIZE:
         psd->str.uType = STRRET_CSTR;
-        FileSize.HighPart = m_LastDetailsFileData.nFileSizeHigh;
-        FileSize.LowPart = m_LastDetailsFileData.nFileSizeLow;
-        StrFormatKBSizeA(FileSize.QuadPart, psd->str.cStr, MAX_PATH);
+        StrFormatKBSizeA(info->FileSize().QuadPart, psd->str.cStr, MAX_PATH);
         return S_OK;
-    case 3: // Modified
-        FileTimeToLocalFileTime(&m_LastDetailsFileData.ftLastWriteTime, &lft);
-        FileTimeToSystemTime (&lft, &time);
+    case FONTEXT_COL_MODIFIED:
         psd->str.uType = STRRET_CSTR;
+        FileTimeToSystemTime(&info->FileWriteTime(), &time);
         ret = GetDateFormatA(LOCALE_USER_DEFAULT, DATE_SHORTDATE, &time, NULL, psd->str.cStr, MAX_PATH);
         if (ret < 1)
         {
@@ -194,7 +189,7 @@ STDMETHODIMP CFontExt::GetDetailsOf(PCUITEMID_CHILD pidl, UINT iColumn, SHELLDET
         psd->str.cStr[ret-1] = ' ';
         GetTimeFormatA(LOCALE_USER_DEFAULT, TIME_NOSECONDS, &time, NULL, &psd->str.cStr[ret], MAX_PATH - ret);
         return S_OK;
-    case 4: // Attributes
+    case FONTEXT_COL_ATTR:
         AttrLetters.LoadString(IDS_COL_ATTR_LETTERS);
         if (AttrLetters.GetLength() != 5)
         {
@@ -202,16 +197,17 @@ STDMETHODIMP CFontExt::GetDetailsOf(PCUITEMID_CHILD pidl, UINT iColumn, SHELLDET
             return E_FAIL;
         }
         psd->str.uType = STRRET_CSTR;
+        dwAttributes = info->FileAttributes();
         ret = 0;
-        if (m_LastDetailsFileData.dwFileAttributes & FILE_ATTRIBUTE_READONLY)
+        if (dwAttributes & FILE_ATTRIBUTE_READONLY)
             psd->str.cStr[ret++] = AttrLetters[0];
-        if (m_LastDetailsFileData.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN)
+        if (dwAttributes & FILE_ATTRIBUTE_HIDDEN)
             psd->str.cStr[ret++] = AttrLetters[1];
-        if (m_LastDetailsFileData.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM)
+        if (dwAttributes & FILE_ATTRIBUTE_SYSTEM)
             psd->str.cStr[ret++] = AttrLetters[2];
-        if (m_LastDetailsFileData.dwFileAttributes & FILE_ATTRIBUTE_ARCHIVE)
+        if (dwAttributes & FILE_ATTRIBUTE_ARCHIVE)
             psd->str.cStr[ret++] = AttrLetters[3];
-        if (m_LastDetailsFileData.dwFileAttributes & FILE_ATTRIBUTE_COMPRESSED)
+        if (dwAttributes & FILE_ATTRIBUTE_COMPRESSED)
             psd->str.cStr[ret++] = AttrLetters[4];
         psd->str.cStr[ret] = '\0';
         return S_OK;
@@ -261,7 +257,53 @@ STDMETHODIMP CFontExt::CompareIDs(LPARAM lParam, PCUIDLIST_RELATIVE pidl1, PCUID
     if (!fontEntry1 || !fontEntry2)
         return E_INVALIDARG;
 
-    int result = (int)fontEntry1->Index - (int)fontEntry2->Index;
+    int result;
+    DWORD sortMode = lParam & 0xFFFF0000;
+    DWORD column = lParam & 0x0000FFFF;
+    if (sortMode == SHCIDS_ALLFIELDS)
+    {
+        UNIMPLEMENTED;
+        result = (int)fontEntry1->Index - (int)fontEntry2->Index;
+    }
+    else
+    {
+        auto info1 = g_FontCache->Find(fontEntry1);
+        auto info2 = g_FontCache->Find(fontEntry2);
+
+        if (!info1 || !info2)
+        {
+            ERR("Unable to find font %S or %S in cache!\n", fontEntry1->Name, fontEntry2->Name);
+            return E_INVALIDARG;
+        }
+
+        switch (column)
+        {
+        case 0xffff:
+            /* ROS bug? */
+        case FONTEXT_COL_NAME:
+            // These items are already ordered by name
+            result = (int)fontEntry1->Index - (int)fontEntry2->Index;
+            break;
+        case FONTEXT_COL_FILENAME:
+            result = wcsicmp(PathFindFileNameW(info1->File()), PathFindFileNameW(info2->File()));
+            break;
+        case FONTEXT_COL_SIZE:
+            result = (int)info1->FileSize().HighPart - info2->FileSize().HighPart;
+            if (result == 0)
+                result = (int)info1->FileSize().LowPart - info2->FileSize().LowPart;
+            break;
+        case FONTEXT_COL_MODIFIED:
+            result = CompareFileTime(&info1->FileWriteTime(), &info2->FileWriteTime());
+            break;
+        case FONTEXT_COL_ATTR:
+            // FIXME: how to compare attributes?
+            result = (int)info1->FileAttributes() - info2->FileAttributes();
+            break;
+        default:
+            ERR("Unimplemented column %u\n", column);
+            return E_INVALIDARG;
+        }
+    }
 
     return MAKE_COMPARE_HRESULT(result);
 }
@@ -340,7 +382,7 @@ STDMETHODIMP CFontExt::GetUIObjectOf(HWND hwndOwner, UINT cidl, PCUITEMID_CHILD_
             if (fontEntry)
             {
                 DWORD dwAttributes = FILE_ATTRIBUTE_NORMAL;
-                CStringW File = g_FontCache->Filename(fontEntry);
+                CStringW File = g_FontCache->Filename(g_FontCache->Find(fontEntry));
                 // Just create a default icon extractor based on the filename
                 // We might want to create a preview with the font to get really fancy one day.
                 return SHCreateFileExtractIconW(File, dwAttributes, riid, ppvOut);
@@ -384,6 +426,15 @@ STDMETHODIMP CFontExt::GetDisplayNameOf(PCUITEMID_CHILD pidl, DWORD dwFlags, LPS
     if (!fontEntry)
         return E_FAIL;
 
+    if (dwFlags == SHGDN_FORPARSING)
+    {
+        CStringW File = g_FontCache->Filename(g_FontCache->Find(fontEntry), true);
+        if (!File.IsEmpty())
+        {
+            return SHSetStrRet(strRet, File);
+        }
+    }
+
     return SHSetStrRet(strRet, fontEntry->Name);
 }
 
@@ -449,17 +500,12 @@ STDMETHODIMP CFontExt::DragEnter(IDataObject* pDataObj, DWORD grfKeyState, POINT
 {
     *pdwEffect = DROPEFFECT_NONE;
 
-    CComHeapPtr<CIDA> cida;
-    HRESULT hr = _GetCidlFromDataObject(pDataObj, &cida);
-    if (FAILED_UNEXPECTEDLY(hr))
-        return hr;
+    CDataObjectHIDA cida(pDataObj);
+    if (FAILED_UNEXPECTEDLY(cida.hr()))
+        return cida.hr();
 
-#if 1   // Please implement DoGetFontTitle
-    return DRAGDROP_S_CANCEL;
-#else
     *pdwEffect = DROPEFFECT_COPY;
     return S_OK;
-#endif
 }
 
 STDMETHODIMP CFontExt::DragOver(DWORD grfKeyState, POINTL pt, DWORD* pdwEffect)
@@ -476,10 +522,9 @@ STDMETHODIMP CFontExt::Drop(IDataObject* pDataObj, DWORD grfKeyState, POINTL pt,
 {
     *pdwEffect = DROPEFFECT_NONE;
 
-    CComHeapPtr<CIDA> cida;
-    HRESULT hr = _GetCidlFromDataObject(pDataObj, &cida);
-    if (FAILED_UNEXPECTEDLY(hr))
-        return hr;
+    CDataObjectHIDA cida(pDataObj);
+    if (!cida)
+        return E_UNEXPECTED;
 
     PCUIDLIST_ABSOLUTE pidlParent = HIDA_GetPIDLFolder(cida);
     if (!pidlParent)
@@ -553,10 +598,15 @@ STDMETHODIMP CFontExt::Drop(IDataObject* pDataObj, DWORD grfKeyState, POINTL pt,
         }
     }
 
-    // TODO: update g_FontCache
+    // Invalidate our cache
+    g_FontCache->Read();
 
+    // Notify the system that a font was added
     SendMessageW(HWND_BROADCAST, WM_FONTCHANGE, 0, 0);
 
+    // Notify the shell that the folder contents are changed
+    SHChangeNotify(SHCNE_UPDATEDIR, SHCNF_PATHW, g_FontCache->FontPath().GetString(), NULL);
+
     // TODO: Show message
 
     return bOK ? S_OK : E_FAIL;
@@ -565,15 +615,30 @@ STDMETHODIMP CFontExt::Drop(IDataObject* pDataObj, DWORD grfKeyState, POINTL pt,
 HRESULT CFontExt::DoInstallFontFile(LPCWSTR pszFontPath, LPCWSTR pszFontsDir, HKEY hkeyFonts)
 {
     WCHAR szDestFile[MAX_PATH];
-    LPCWSTR pszFileTitle = PathFindFileName(pszFontPath);
 
-    WCHAR szFontName[512];
-    if (!DoGetFontTitle(pszFontPath, szFontName))
+    // Add this font to the font list, so we can query the name
+    if (!AddFontResourceW(pszFontPath))
+    {
+        ERR("AddFontResourceW('%S') failed\n", pszFontPath);
+        DeleteFileW(szDestFile);
         return E_FAIL;
+    }
+
+    CStringW strFontName;
+    HRESULT hr = DoGetFontTitle(pszFontPath, strFontName);
+
+    // We got the name, remove it again
+    RemoveFontResourceW(pszFontPath);
 
-    RemoveFontResourceW(pszFileTitle);
+    if (!SUCCEEDED(hr))
+    {
+        ERR("DoGetFontTitle failed (err=0x%x)!\n", hr);
+        return hr;
+    }
 
     StringCchCopyW(szDestFile, sizeof(szDestFile), pszFontsDir);
+
+    LPCWSTR pszFileTitle = PathFindFileName(pszFontPath);
     PathAppendW(szDestFile, pszFileTitle);
     if (!CopyFileW(pszFontPath, szDestFile, FALSE))
     {
@@ -581,28 +646,42 @@ HRESULT CFontExt::DoInstallFontFile(LPCWSTR pszFontPath, LPCWSTR pszFontsDir, HK
         return E_FAIL;
     }
 
-    if (!AddFontResourceW(pszFileTitle))
-    {
-        ERR("AddFontResourceW('%S') failed\n", pszFileTitle);
-        DeleteFileW(szDestFile);
-        return E_FAIL;
-    }
-
     DWORD cbData = (wcslen(pszFileTitle) + 1) * sizeof(WCHAR);
-    LONG nError = RegSetValueExW(hkeyFonts, szFontName, 0, REG_SZ, (const BYTE *)szFontName, cbData);
+    LONG nError = RegSetValueExW(hkeyFonts, strFontName, 0, REG_SZ,
+                                 (const BYTE *)pszFileTitle, cbData);
     if (nError)
     {
         ERR("RegSetValueExW failed with %ld\n", nError);
-        RemoveFontResourceW(pszFileTitle);
         DeleteFileW(szDestFile);
         return E_FAIL;
     }
 
+    AddFontResourceW(szDestFile);
+
     return S_OK;
 }
 
-HRESULT CFontExt::DoGetFontTitle(LPCWSTR pszFontPath, LPCWSTR pszFontName)
+HRESULT
+CFontExt::DoGetFontTitle(IN LPCWSTR pszFontPath, OUT CStringW& strFontName)
 {
-    // TODO:
+    DWORD cbInfo = 0;
+    BOOL ret = GetFontResourceInfoW(pszFontPath, &cbInfo, NULL, 1);
+    if (!ret || !cbInfo)
+    {
+        ERR("GetFontResourceInfoW failed (err: %u)\n", GetLastError());
+        return E_FAIL;
+    }
+
+    LPWSTR pszBuffer = strFontName.GetBuffer(cbInfo / sizeof(WCHAR));
+    ret = GetFontResourceInfoW(pszFontPath, &cbInfo, pszBuffer, 1);
+    DWORD dwErr = GetLastError();;
+    strFontName.ReleaseBuffer();
+    if (ret)
+    {
+        TRACE("pszFontName: %S\n", (LPCWSTR)strFontName);
+        return S_OK;
+    }
+
+    ERR("GetFontResourceInfoW failed (err: %u)\n", dwErr);
     return E_FAIL;
 }