[FONTEXT] Initial implementation
[reactos.git] / dll / shellext / fontext / CFontCache.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: font list cache handling
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 #define FONT_HIVE HKEY_LOCAL_MACHINE
14 #define FONT_KEY L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Fonts"
15
16 CFontCache* g_FontCache = NULL;
17
18 CFontInfo::CFontInfo(LPCWSTR name)
19 : m_Name(name)
20 , m_FileRead(false)
21 {
22 }
23
24 const CStringW& CFontInfo::Name() const
25 {
26 return m_Name;
27 }
28
29 const bool CFontInfo::Valid() const
30 {
31 return !m_Name.IsEmpty();
32 }
33
34 const CStringW& CFontInfo::File()
35 {
36 if (!m_FileRead)
37 {
38 if (Valid())
39 {
40 // Read the filename stored in the registry.
41 // This can be either a filename or a full path
42 CRegKey key;
43 if (key.Open(FONT_HIVE, FONT_KEY, KEY_READ) == ERROR_SUCCESS)
44 {
45 CStringW Value;
46 DWORD dwAllocated = 128;
47 LSTATUS Status;
48 do
49 {
50 DWORD dwSize = dwAllocated;
51 PWSTR Buffer = Value.GetBuffer(dwSize);
52 Status = key.QueryStringValue(m_Name, Buffer, &dwSize);
53 Value.ReleaseBuffer(dwSize);
54 if (Status == ERROR_SUCCESS)
55 {
56 // Ensure we do not re-use the same string object, by passing it a PCWSTR
57 m_File = Value.GetString();
58 break;
59 }
60 dwAllocated += 128;
61 } while (Status == ERROR_MORE_DATA);
62 }
63 }
64 m_FileRead = true;
65 }
66 return m_File;
67 }
68
69
70
71 CFontCache::CFontCache()
72 {
73 }
74
75 size_t CFontCache::Size()
76 {
77 if (m_Fonts.GetCount() == 0u)
78 Read();
79
80 return m_Fonts.GetCount();
81 }
82
83 CStringW CFontCache::Name(size_t Index)
84 {
85 if (m_Fonts.GetCount() == 0u)
86 Read();
87
88 if (Index >= m_Fonts.GetCount())
89 return CStringW();
90
91 return m_Fonts[Index].Name();
92 }
93
94 CStringW CFontCache::Filename(const FontPidlEntry* fontEntry)
95 {
96 if (fontEntry->Index < m_Fonts.GetCount())
97 {
98 CFontInfo& info = m_Fonts[fontEntry->Index];
99
100 if (info.Name().CompareNoCase(fontEntry->Name) == 0)
101 return info.File();
102 }
103
104 for (UINT n = 0; n < Size(); ++n)
105 {
106 if (m_Fonts[n].Name().CompareNoCase(fontEntry->Name) == 0)
107 return m_Fonts[n].File();
108 }
109
110 return CStringW();
111 }
112
113 void CFontCache::Insert(CAtlList<CFontInfo>& fonts, const CStringW& KeyName)
114 {
115 POSITION it = fonts.GetHeadPosition();
116 while (it != NULL)
117 {
118 POSITION lastit = it;
119 const CFontInfo& info = fonts.GetNext(it);
120 if (info.Name().CompareNoCase(KeyName) >= 0)
121 {
122 fonts.InsertBefore(lastit, CFontInfo(KeyName));
123 return;
124 }
125 }
126 fonts.AddTail(CFontInfo(KeyName));
127 }
128
129 void CFontCache::Read()
130 {
131 CAtlList<CFontInfo> fonts;
132 CRegKey key;
133
134 // Enumerate all registered font names
135 if (key.Open(FONT_HIVE, FONT_KEY, KEY_READ) == ERROR_SUCCESS)
136 {
137 LSTATUS Status;
138 DWORD dwAllocated = 128;
139 DWORD ilIndex = 0;
140 CStringW KeyName;
141 do
142 {
143 DWORD dwSize = dwAllocated;
144 PWSTR Buffer = KeyName.GetBuffer(dwSize);
145 Status = RegEnumValueW(key.m_hKey, ilIndex, Buffer, &dwSize, NULL, NULL, NULL, NULL);
146 KeyName.ReleaseBuffer(dwSize);
147 if (Status == ERROR_SUCCESS)
148 {
149 // Insert will create an ordered list
150 Insert(fonts, KeyName);
151 ilIndex++;
152 continue;
153 }
154 if (Status == ERROR_NO_MORE_ITEMS)
155 break;
156 else if (Status == ERROR_MORE_DATA)
157 {
158 dwAllocated += 128;
159 }
160 } while (Status == ERROR_MORE_DATA || Status == ERROR_SUCCESS);
161 }
162
163 // Move the fonts from a list to an array (for easy indexing)
164 m_Fonts.SetCount(fonts.GetCount());
165 size_t Index = 0;
166 POSITION it = fonts.GetHeadPosition();
167 while (it != NULL)
168 {
169 m_Fonts[Index] = fonts.GetNext(it);
170 Index++;
171 }
172 }
173