Revert "[SHELL32] SHChangeNotify: Use tree for CDirectoryList (#6784)" (#6800)
[reactos.git] / dll / win32 / browseui / ACLCustomMRU.cpp
1 /*
2 * PROJECT: ReactOS browseui
3 * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
4 * PURPOSE: Custom MRU AutoComplete List
5 * COPYRIGHT: Copyright 2017 Mark Jansen (mark.jansen@reactos.org)
6 * Copyright 2020 Katayama Hirofumi MZ (katayama.hirofumi.mz@gmail.com)
7 */
8
9 #include "precomp.h"
10
11 #define TYPED_URLS_KEY L"Software\\Microsoft\\Internet Explorer\\TypedURLs"
12
13 CACLCustomMRU::CACLCustomMRU()
14 : m_bDirty(false), m_bTypedURLs(FALSE), m_ielt(0)
15 {
16 }
17
18 CACLCustomMRU::~CACLCustomMRU()
19 {
20 PersistMRU();
21 }
22
23 STDMETHODIMP CACLCustomMRU::Next(ULONG celt, LPWSTR *rgelt, ULONG *pceltFetched)
24 {
25 if (!pceltFetched || !rgelt)
26 return E_POINTER;
27
28 *pceltFetched = 0;
29 if (celt == 0)
30 return S_OK;
31
32 *rgelt = NULL;
33 if (INT(m_ielt) >= m_MRUData.GetSize())
34 return S_FALSE;
35
36 CStringW str = m_MRUData[m_ielt];
37
38 if (!m_bTypedURLs)
39 {
40 // Erase the last "\\1" etc. (indicates SW_* value)
41 INT ich = str.ReverseFind(L'\\');
42 if (ich >= 0)
43 str = str.Left(ich);
44 }
45
46 size_t cb = (str.GetLength() + 1) * sizeof(WCHAR);
47 LPWSTR psz = (LPWSTR)CoTaskMemAlloc(cb);
48 if (!psz)
49 return S_FALSE;
50
51 CopyMemory(psz, (LPCWSTR)str, cb);
52 *rgelt = psz;
53 *pceltFetched = 1;
54 ++m_ielt;
55 return S_OK;
56 }
57
58 STDMETHODIMP CACLCustomMRU::Skip(ULONG celt)
59 {
60 return E_NOTIMPL;
61 }
62
63 STDMETHODIMP CACLCustomMRU::Reset()
64 {
65 m_ielt = 0;
66 return S_OK;
67 }
68
69 STDMETHODIMP CACLCustomMRU::Clone(IEnumString ** ppenum)
70 {
71 *ppenum = NULL;
72 return E_NOTIMPL;
73 }
74
75 STDMETHODIMP CACLCustomMRU::Expand(LPCOLESTR pszExpand)
76 {
77 return E_NOTIMPL;
78 }
79
80 void CACLCustomMRU::PersistMRU()
81 {
82 if (!m_bDirty || m_bTypedURLs)
83 return;
84
85 WCHAR Key[2] = { 0, 0 };
86
87 m_bDirty = false;
88
89 if (m_Key.m_hKey)
90 {
91 m_Key.SetStringValue(L"MRUList", m_MRUList);
92 for (int Index = 0; Index < m_MRUList.GetLength(); ++Index)
93 {
94 Key[0] = Index + 'a';
95 m_Key.SetStringValue(Key, m_MRUData[Index]);
96 }
97 }
98 }
99
100 static LSTATUS
101 RegQueryCStringW(CRegKey& key, LPCWSTR pszValueName, CStringW& str)
102 {
103 // Check type and size
104 DWORD dwType, cbData;
105 LSTATUS ret = key.QueryValue(pszValueName, &dwType, NULL, &cbData);
106 if (ret != ERROR_SUCCESS)
107 return ret;
108 if (dwType != REG_SZ && dwType != REG_EXPAND_SZ)
109 return ERROR_INVALID_DATA;
110
111 // Allocate buffer
112 LPWSTR pszBuffer = str.GetBuffer(cbData / sizeof(WCHAR) + 1);
113 if (pszBuffer == NULL)
114 return ERROR_OUTOFMEMORY;
115
116 // Get the data
117 ret = key.QueryValue(pszValueName, NULL, pszBuffer, &cbData);
118
119 // Release buffer
120 str.ReleaseBuffer();
121 return ret;
122 }
123
124 HRESULT CACLCustomMRU::LoadTypedURLs(DWORD dwMax)
125 {
126 dwMax = max(0, dwMax);
127 dwMax = min(29, dwMax);
128
129 WCHAR szName[32];
130 CStringW strData;
131 LSTATUS status;
132 for (DWORD i = 1; i <= dwMax; ++i)
133 {
134 // Build a registry value name
135 StringCbPrintfW(szName, sizeof(szName), L"url%lu", i);
136
137 // Read a registry value
138 status = RegQueryCStringW(m_Key, szName, strData);
139 if (status != ERROR_SUCCESS)
140 break;
141
142 m_MRUData.Add(strData);
143 }
144
145 return S_OK;
146 }
147
148 // *** IACLCustomMRU methods ***
149 HRESULT STDMETHODCALLTYPE CACLCustomMRU::Initialize(LPCWSTR pwszMRURegKey, DWORD dwMax)
150 {
151 m_ielt = 0;
152
153 LSTATUS Status = m_Key.Create(HKEY_CURRENT_USER, pwszMRURegKey);
154 if (Status != ERROR_SUCCESS)
155 return HRESULT_FROM_WIN32(Status);
156
157 m_MRUData.RemoveAll();
158 if (lstrcmpiW(pwszMRURegKey, TYPED_URLS_KEY) == 0)
159 {
160 m_bTypedURLs = TRUE;
161 return LoadTypedURLs(dwMax);
162 }
163 else
164 {
165 m_bTypedURLs = FALSE;
166 return LoadMRUList(dwMax);
167 }
168 }
169
170 HRESULT CACLCustomMRU::LoadMRUList(DWORD dwMax)
171 {
172 dwMax = max(0, dwMax);
173 dwMax = min(29, dwMax);
174 while (dwMax--)
175 m_MRUData.Add(CStringW());
176
177 WCHAR MRUList[40];
178 ULONG nChars = _countof(MRUList);
179
180 LSTATUS Status = m_Key.QueryStringValue(L"MRUList", MRUList, &nChars);
181 if (Status != ERROR_SUCCESS)
182 return S_OK;
183
184 if (nChars > 0 && MRUList[nChars-1] == '\0')
185 nChars--;
186
187 if (nChars > (ULONG)m_MRUData.GetSize())
188 return S_OK;
189
190 for (ULONG n = 0; n < nChars; ++n)
191 {
192 if (MRUList[n] >= 'a' && MRUList[n] <= '}' && m_MRUList.Find(MRUList[n]) < 0)
193 {
194 WCHAR Key[2] = { MRUList[n], NULL };
195 WCHAR Value[MAX_PATH * 2];
196 ULONG nValueChars = _countof(Value);
197
198 m_MRUList += MRUList[n];
199 int Index = MRUList[n] - 'a';
200
201 if (Index < m_MRUData.GetSize())
202 {
203 Status = m_Key.QueryStringValue(Key, Value, &nValueChars);
204 if (Status == ERROR_SUCCESS)
205 {
206 m_MRUData[Index] = CStringW(Value, nValueChars);
207 }
208 }
209 }
210 }
211
212 return S_OK;
213 }
214
215 HRESULT STDMETHODCALLTYPE CACLCustomMRU::AddMRUString(LPCWSTR pwszEntry)
216 {
217 if (m_bTypedURLs)
218 return E_FAIL;
219
220 ATLASSERT(m_MRUData.GetSize() <= m_MRUList.GetLength());
221 m_bDirty = true;
222
223 CStringW NewElement = pwszEntry;
224 WCHAR Key[2] = { 0, 0 };
225 int Index = m_MRUData.Find(NewElement);
226 if (Index >= 0)
227 {
228 /* Move the key to the front */
229 Key[0] = Index + 'a';
230 m_MRUList.Replace(Key, L"");
231 m_MRUList = Key + m_MRUList;
232 return S_OK;
233 }
234
235 int TotalLen = m_MRUList.GetLength();
236 if (m_MRUData.GetSize() == TotalLen)
237 {
238 /* Find oldest element, move that to the front */
239 Key[0] = m_MRUList[TotalLen-1];
240 m_MRUList = Key + m_MRUList.Left(TotalLen-1);
241 Index = Key[0] - 'a';
242 }
243 else
244 {
245 /* Find the first empty entry */
246 for (Index = 0; Index < m_MRUData.GetSize(); ++Index)
247 {
248 if (m_MRUData[Index].IsEmpty())
249 break;
250 }
251 Key[0] = Index + 'a';
252 m_MRUList = Key + m_MRUList;
253 }
254 m_MRUData[Index] = NewElement;
255
256 PersistMRU();
257 return S_OK;
258 }
259