xref: /reactos/dll/win32/browseui/ACLCustomMRU.cpp (revision 7b1049c8)
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 
260