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