[FONTEXT] Add detail view columns to font folder

CORE-16476
This commit is contained in:
Mark Jansen 2020-01-20 21:43:19 +01:00
parent e00ea7dadd
commit c09636f946
No known key found for this signature in database
GPG key ID: B39240EE84BEAE8B
15 changed files with 228 additions and 57 deletions

View file

@ -2,7 +2,7 @@
* 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 Mark Jansen (mark.jansen@reactos.org)
* COPYRIGHT: Copyright 2019,2020 Mark Jansen (mark.jansen@reactos.org)
*/
#include "precomp.h"
@ -45,11 +45,6 @@ HRESULT _CDataObject_CreateInstance(PCIDLIST_ABSOLUTE folder, UINT cidl, PCUITEM
// Now that we have an IDataObject with the shell itemid list (CFSTR_SHELLIDLIST, aka HIDA) format
// we will augment this IDataObject with the CF_HDROP format. (Full filepaths)
// This enabled the objects for the 'copy' and drag to copy actions
WCHAR FontsDir[MAX_PATH];
hr = SHGetFolderPathW(NULL, CSIDL_FONTS, NULL, 0, FontsDir);
if (FAILED_UNEXPECTEDLY(hr))
return S_OK;
StringCchCatW(FontsDir, _countof(FontsDir), L"\\");
CComHeapPtr<BYTE> data;
@ -63,15 +58,9 @@ HRESULT _CDataObject_CreateInstance(PCIDLIST_ABSOLUTE folder, UINT cidl, PCUITEM
const FontPidlEntry* fontEntry = _FontFromIL(apidl[n]);
if (fontEntry)
{
CStringW File = g_FontCache->Filename(fontEntry);
CStringW File = g_FontCache->Filename(fontEntry, true);
if (!File.IsEmpty())
{
// Ensure this is a full path
if (PathIsRelativeW(File))
{
File = FontsDir + File;
}
// Now append the path (+ nullterminator) to the buffer
UINT len = offset + (File.GetLength() + 1) * sizeof(WCHAR);
data.ReallocateBytes(len);

View file

@ -2,7 +2,7 @@
* PROJECT: ReactOS Font Shell Extension
* LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
* PURPOSE: font list cache handling
* COPYRIGHT: Copyright 2019 Mark Jansen (mark.jansen@reactos.org)
* COPYRIGHT: Copyright 2019,2020 Mark Jansen (mark.jansen@reactos.org)
*/
#include "precomp.h"
@ -68,6 +68,12 @@ CFontCache::CFontCache()
{
}
void CFontCache::SetFontDir(const LPCWSTR Path)
{
if (m_FontFolderPath.IsEmpty())
m_FontFolderPath = Path;
}
size_t CFontCache::Size()
{
if (m_Fonts.GetCount() == 0u)
@ -87,23 +93,34 @@ CStringW CFontCache::Name(size_t Index)
return m_Fonts[Index].Name();
}
CStringW CFontCache::Filename(const FontPidlEntry* fontEntry)
CStringW CFontCache::Filename(const FontPidlEntry* fontEntry, bool alwaysFullPath)
{
CStringW File;
if (fontEntry->Index < m_Fonts.GetCount())
{
CFontInfo& info = m_Fonts[fontEntry->Index];
if (info.Name().CompareNoCase(fontEntry->Name) == 0)
return info.File();
File = info.File();
}
for (UINT n = 0; n < Size(); ++n)
for (UINT n = 0; File.IsEmpty() && n < Size(); ++n)
{
if (m_Fonts[n].Name().CompareNoCase(fontEntry->Name) == 0)
return m_Fonts[n].File();
File = m_Fonts[n].File();
}
return CStringW();
if (!File.IsEmpty() && alwaysFullPath)
{
// Ensure this is a full path
if (PathIsRelativeW(File))
{
File = m_FontFolderPath + File;
}
}
return File;
}
void CFontCache::Insert(CAtlList<CFontInfo>& fonts, const CStringW& KeyName)

View file

@ -2,7 +2,7 @@
* PROJECT: ReactOS Font Shell Extension
* LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
* PURPOSE: font list cache handling
* COPYRIGHT: Copyright 2019 Mark Jansen (mark.jansen@reactos.org)
* COPYRIGHT: Copyright 2019,2020 Mark Jansen (mark.jansen@reactos.org)
*/
#pragma once
@ -27,6 +27,7 @@ class CFontCache
{
private:
CAtlArray<CFontInfo> m_Fonts;
CStringW m_FontFolderPath;
protected:
CFontCache();
@ -35,9 +36,12 @@ protected:
void Read();
public:
void SetFontDir(const LPCWSTR Path);
const CStringW& FontPath() const { return m_FontFolderPath; }
size_t Size();
CStringW Name(size_t Index);
CStringW Filename(const FontPidlEntry* fontEntry);
CStringW Filename(const FontPidlEntry* fontEntry, bool alwaysFullPath = false);
friend class CFontExtModule;
};

View file

@ -2,7 +2,7 @@
* 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 Mark Jansen (mark.jansen@reactos.org)
* COPYRIGHT: Copyright 2019,2020 Mark Jansen (mark.jansen@reactos.org)
* Copyright 2019 Katayama Hirofumi MZ (katayama.hirofumi.mz@gmail.com)
*/
@ -22,6 +22,10 @@ struct FolderViewColumns
static FolderViewColumns g_ColumnDefs[] =
{
{ IDS_COL_NAME, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, 25, LVCFMT_LEFT },
{ IDS_COL_FILENAME, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, 20, LVCFMT_LEFT },
{ IDS_COL_SIZE, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, 10, LVCFMT_RIGHT },
{ IDS_COL_MODIFIED, SHCOLSTATE_TYPE_DATE | SHCOLSTATE_ONBYDEFAULT, 15, LVCFMT_LEFT },
{ IDS_COL_ATTR, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, 12, LVCFMT_RIGHT },
};
@ -134,10 +138,83 @@ STDMETHODIMP CFontExt::GetDetailsOf(PCUITEMID_CHILD pidl, UINT iColumn, SHELLDET
return E_FAIL;
}
// Name, ReactOS specific?
if (iColumn == 0)
return GetDisplayNameOf(pidl, 0, &psd->str);
const FontPidlEntry* fontEntry = _FontFromIL(pidl);
if (!fontEntry)
{
ERR("ERROR, not a font PIDL!\n");
return E_FAIL;
}
// 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)
{
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;
}
// Most code borrowed from CFSFolder::GetDetailsOf
FILETIME lft;
SYSTEMTIME time;
int ret;
LARGE_INTEGER FileSize;
CStringA AttrLetters;
switch (iColumn)
{
case 0: /* Name, ReactOS specific? */
return GetDisplayNameOf(pidl, 0, &psd->str);
case 1: // Filename
return SHSetStrRet(&psd->str, m_LastDetailsFileData.cFileName);
case 2: // Size
psd->str.uType = STRRET_CSTR;
FileSize.HighPart = m_LastDetailsFileData.nFileSizeHigh;
FileSize.LowPart = m_LastDetailsFileData.nFileSizeLow;
StrFormatKBSizeA(FileSize.QuadPart, psd->str.cStr, MAX_PATH);
return S_OK;
case 3: // Modified
FileTimeToLocalFileTime(&m_LastDetailsFileData.ftLastWriteTime, &lft);
FileTimeToSystemTime (&lft, &time);
psd->str.uType = STRRET_CSTR;
ret = GetDateFormatA(LOCALE_USER_DEFAULT, DATE_SHORTDATE, &time, NULL, psd->str.cStr, MAX_PATH);
if (ret < 1)
{
ERR("GetDateFormatA failed\n");
return E_FAIL;
}
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
AttrLetters.LoadString(IDS_COL_ATTR_LETTERS);
if (AttrLetters.GetLength() != 5)
{
ERR("IDS_COL_ATTR_LETTERS does not contain 5 letters!\n");
return E_FAIL;
}
psd->str.uType = STRRET_CSTR;
ret = 0;
if (m_LastDetailsFileData.dwFileAttributes & FILE_ATTRIBUTE_READONLY)
psd->str.cStr[ret++] = AttrLetters[0];
if (m_LastDetailsFileData.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN)
psd->str.cStr[ret++] = AttrLetters[1];
if (m_LastDetailsFileData.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM)
psd->str.cStr[ret++] = AttrLetters[2];
if (m_LastDetailsFileData.dwFileAttributes & FILE_ATTRIBUTE_ARCHIVE)
psd->str.cStr[ret++] = AttrLetters[3];
if (m_LastDetailsFileData.dwFileAttributes & FILE_ATTRIBUTE_COMPRESSED)
psd->str.cStr[ret++] = AttrLetters[4];
psd->str.cStr[ret] = '\0';
return S_OK;
default:
break;
}
@ -332,27 +409,29 @@ STDMETHODIMP CFontExt::GetCurFolder(LPITEMIDLIST *ppidl)
// *** IPersistFolder methods ***
STDMETHODIMP CFontExt::Initialize(LPCITEMIDLIST pidl)
{
WCHAR PidlPath[MAX_PATH + 1] = {0}, Expected[MAX_PATH + 1];
WCHAR PidlPath[MAX_PATH + 1] = {0}, FontsDir[MAX_PATH + 1];
if (!SHGetPathFromIDListW(pidl, PidlPath))
{
ERR("Unable to extract path from pidl\n");
return E_FAIL;
}
HRESULT hr = SHGetFolderPathW(NULL, CSIDL_FONTS, NULL, 0, Expected);
if (!SUCCEEDED(hr))
HRESULT hr = SHGetFolderPathW(NULL, CSIDL_FONTS, NULL, 0, FontsDir);
if (FAILED_UNEXPECTEDLY(hr))
{
ERR("Unable to get fonts path (0x%x)\n", hr);
return hr;
}
if (_wcsicmp(PidlPath, Expected))
if (_wcsicmp(PidlPath, FontsDir))
{
ERR("CFontExt View initializing on unexpected folder: '%S'\n", PidlPath);
return E_FAIL;
}
m_Folder.Attach(ILClone(pidl));
StringCchCatW(FontsDir, _countof(FontsDir), L"\\");
g_FontCache->SetFontDir(FontsDir);
return S_OK;
}
@ -397,13 +476,8 @@ STDMETHODIMP CFontExt::Drop(IDataObject* pDataObj, DWORD grfKeyState, POINTL pt,
{
*pdwEffect = DROPEFFECT_NONE;
WCHAR szFontsDir[MAX_PATH];
HRESULT hr = SHGetFolderPathW(NULL, CSIDL_FONTS, NULL, 0, szFontsDir);
if (FAILED_UNEXPECTEDLY(hr))
return E_FAIL;
CComHeapPtr<CIDA> cida;
hr = _GetCidlFromDataObject(pDataObj, &cida);
HRESULT hr = _GetCidlFromDataObject(pDataObj, &cida);
if (FAILED_UNEXPECTEDLY(hr))
return hr;
@ -471,7 +545,7 @@ STDMETHODIMP CFontExt::Drop(IDataObject* pDataObj, DWORD grfKeyState, POINTL pt,
for (size_t iItem = 0; iItem < FontPaths.GetCount(); ++iItem)
{
HRESULT hr = DoInstallFontFile(FontPaths[iItem], szFontsDir, keyFonts.m_hKey);
HRESULT hr = DoInstallFontFile(FontPaths[iItem], g_FontCache->FontPath(), keyFonts.m_hKey);
if (FAILED_UNEXPECTEDLY(hr))
{
bOK = FALSE;

View file

@ -2,7 +2,7 @@
* PROJECT: ReactOS Font Shell Extension
* LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
* PURPOSE: CFontExt definition
* COPYRIGHT: Copyright 2019 Mark Jansen (mark.jansen@reactos.org)
* COPYRIGHT: Copyright 2019,2020 Mark Jansen (mark.jansen@reactos.org)
* Copyright 2019 Katayama Hirofumi MZ (katayama.hirofumi.mz@gmail.com)
*/
@ -16,6 +16,8 @@ class CFontExt :
public IDropTarget
{
CComHeapPtr<ITEMIDLIST> m_Folder;
CStringW m_LastDetailsFontName;
WIN32_FIND_DATAW m_LastDetailsFileData;
public:

View file

@ -2,7 +2,7 @@
* 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 Mark Jansen (mark.jansen@reactos.org)
* COPYRIGHT: Copyright 2019,2020 Mark Jansen (mark.jansen@reactos.org)
*/
#include "precomp.h"
@ -69,20 +69,9 @@ 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(fontEntry);
CStringW Path = g_FontCache->Filename(fontEntry, true);
if (!Path.IsEmpty())
{
if (PathIsRelativeW(Path))
{
WCHAR FontsDir[MAX_PATH];
HRESULT hr = SHGetFolderPathW(NULL, CSIDL_FONTS, NULL, 0, FontsDir);
if (!FAILED_UNEXPECTEDLY(hr))
{
StringCchCatW(FontsDir, _countof(FontsDir), L"\\");
Path = FontsDir + Path;
}
}
// '/d' disables the install button
StringCchPrintfW(FontPathArg, _countof(FontPathArg), L"/d %s", Path.GetString());
PathQuoteSpacesW(FontPathArg + 3);

View file

@ -22,7 +22,8 @@ list(APPEND SOURCE
fontext.cpp
fontpidl.cpp
fontpidl.hpp
precomp.h)
precomp.h
resource.h)
add_library(fontext MODULE
${SOURCE}

View file

@ -2,7 +2,7 @@
* PROJECT: ReactOS Font Shell Extension
* LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
* PURPOSE: Shell extension entry point
* COPYRIGHT: Copyright 2019 Mark Jansen (mark.jansen@reactos.org)
* COPYRIGHT: Copyright 2019,2020 Mark Jansen (mark.jansen@reactos.org)
*/
#include "precomp.h"
@ -59,11 +59,11 @@ STDAPI DllRegisterServer()
HRESULT hr;
hr = gModule.DllRegisterServer(FALSE);
if (FAILED(hr))
if (FAILED_UNEXPECTEDLY(hr))
return hr;
hr = SHGetFolderPathW(NULL, CSIDL_FONTS, NULL, 0, Path);
if (FAILED(hr))
if (FAILED_UNEXPECTEDLY(hr))
return hr;
// Make this a system folder:

View file

@ -2,7 +2,7 @@
* PROJECT: ReactOS Font Shell Extension
* LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
* PURPOSE: pidl handling
* COPYRIGHT: Copyright 2019 Mark Jansen (mark.jansen@reactos.org)
* COPYRIGHT: Copyright 2019,2020 Mark Jansen (mark.jansen@reactos.org)
*/
#include "precomp.h"
@ -31,8 +31,8 @@ LPITEMIDLIST _ILCreate(LPCWSTR lpString, ULONG Index)
const FontPidlEntry* _FontFromIL(LPCITEMIDLIST pidl)
{
const FontPidlEntry* zipPidl = (const FontPidlEntry*)pidl;
if (zipPidl->Magic == 'fp')
return zipPidl;
const FontPidlEntry* fontEntry = (const FontPidlEntry*)pidl;
if (fontEntry->Magic == 'fp')
return fontEntry;
return NULL;
}

View file

@ -13,6 +13,15 @@ BEGIN
IDS_COL_NAME "Name"
END
STRINGTABLE
BEGIN
IDS_COL_FILENAME "Filename"
IDS_COL_SIZE "Size"
IDS_COL_MODIFIED "Modified"
IDS_COL_ATTR "Attributes"
IDS_COL_ATTR_LETTERS "RHSAC"
END
STRINGTABLE
BEGIN

View file

@ -11,3 +11,23 @@ STRINGTABLE
BEGIN
IDS_REACTOS_FONTS_FOLDER "Cartella dei font di ReactOS"
END
STRINGTABLE
BEGIN
IDS_COL_NAME "Name"
END
STRINGTABLE
BEGIN
IDS_COL_FILENAME "Filename"
IDS_COL_SIZE "Size"
IDS_COL_MODIFIED "Modified"
IDS_COL_ATTR "Attributes"
IDS_COL_ATTR_LETTERS "RHSAC"
END
STRINGTABLE
BEGIN
IDS_FONT_PREVIEW "Preview"
END

View file

@ -6,3 +6,23 @@ STRINGTABLE
BEGIN
IDS_REACTOS_FONTS_FOLDER "Folder czcionek ReactOS"
END
STRINGTABLE
BEGIN
IDS_COL_NAME "Name"
END
STRINGTABLE
BEGIN
IDS_COL_FILENAME "Filename"
IDS_COL_SIZE "Size"
IDS_COL_MODIFIED "Modified"
IDS_COL_ATTR "Attributes"
IDS_COL_ATTR_LETTERS "RHSAC"
END
STRINGTABLE
BEGIN
IDS_FONT_PREVIEW "Preview"
END

View file

@ -11,3 +11,23 @@ STRINGTABLE
BEGIN
IDS_REACTOS_FONTS_FOLDER "Folder-ul de fonturi de ReactOS"
END
STRINGTABLE
BEGIN
IDS_COL_NAME "Name"
END
STRINGTABLE
BEGIN
IDS_COL_FILENAME "Filename"
IDS_COL_SIZE "Size"
IDS_COL_MODIFIED "Modified"
IDS_COL_ATTR "Attributes"
IDS_COL_ATTR_LETTERS "RHSAC"
END
STRINGTABLE
BEGIN
IDS_FONT_PREVIEW "Preview"
END

View file

@ -6,3 +6,23 @@ STRINGTABLE
BEGIN
IDS_REACTOS_FONTS_FOLDER "ReactOS Yazı Tipi Klasörü"
END
STRINGTABLE
BEGIN
IDS_COL_NAME "Name"
END
STRINGTABLE
BEGIN
IDS_COL_FILENAME "Filename"
IDS_COL_SIZE "Size"
IDS_COL_MODIFIED "Modified"
IDS_COL_ATTR "Attributes"
IDS_COL_ATTR_LETTERS "RHSAC"
END
STRINGTABLE
BEGIN
IDS_FONT_PREVIEW "Preview"
END

View file

@ -4,6 +4,12 @@
#define IDS_REACTOS_FONTS_FOLDER 151
#define IDS_COL_NAME 301
#define IDS_COL_FILENAME 304
#define IDS_COL_SIZE 305
#define IDS_COL_MODIFIED 306
#define IDS_COL_ATTR 307
#define IDS_COL_ATTR_LETTERS 308
#define IDS_FONT_PREVIEW 401
#define IDR_FONTEXT 8000