xref: /reactos/dll/win32/shlwapi/autocomp.cpp (revision 8a92b556)
1 /*
2  * PROJECT:     ReactOS shlwapi
3  * LICENSE:     LGPL-2.0-or-later (https://spdx.org/licenses/LGPL-2.0-or-later)
4  * PURPOSE:     Implement SHAutoComplete
5  * COPYRIGHT:   Copyright 2020 Katayama Hirofumi MZ (katayama.hirofumi.mz@gmail.com)
6  */
7 #include <windef.h>
8 #include <shlobj.h>
9 #include <shlwapi.h>
10 #include <browseui_undoc.h>
11 #include <shlwapi_undoc.h>
12 #include <shlguid_undoc.h>
13 #include <atlbase.h>
14 #include <atlcom.h>
15 #include <wine/debug.h>
16 
17 WINE_DEFAULT_DEBUG_CHANNEL(shell);
18 
19 static HRESULT
20 AutoComplete_AddMRU(CComPtr<IObjMgr> pManager, LPCWSTR pszKey)
21 {
22     CComPtr<IACLCustomMRU> pMRU; // Create an MRU list
23     HRESULT hr = CoCreateInstance(CLSID_ACLCustomMRU, NULL, CLSCTX_INPROC_SERVER,
24                                   IID_IACLCustomMRU, (LPVOID *)&pMRU);
25     if (FAILED(hr))
26     {
27         ERR("CoCreateInstance(CLSID_ACLMRU) failed with 0x%08lX\n", hr);
28         return hr;
29     }
30 
31     hr = pMRU->Initialize(pszKey, 'z' - 'a' + 1); // Load the list from registry
32     if (FAILED(hr))
33     {
34         ERR("pMRU->Initialize(%ls) failed with 0x%08lX\n", pszKey, hr);
35         return hr;
36     }
37 
38     hr = pManager->Append(pMRU); // Add to the manager
39     if (FAILED(hr))
40         ERR("pManager->Append for '%ls' failed with 0x%08lX\n", pszKey, hr);
41     return hr;
42 }
43 
44 static HRESULT
45 IUnknown_AddOptions(CComPtr<IUnknown> punk, DWORD dwACLO)
46 {
47     CComPtr<IACList2> pList;
48     HRESULT hr = punk->QueryInterface(IID_IACList2, (LPVOID *)&pList);
49     if (FAILED(hr))
50     {
51         ERR("punk->QueryInterface failed: 0x%08lX\n", hr);
52         return hr;
53     }
54 
55     DWORD dwOptions = 0;
56     pList->GetOptions(&dwOptions);
57     dwOptions |= dwACLO;
58     hr = pList->SetOptions(dwOptions);
59     if (FAILED(hr))
60         ERR("pList->SetOptions failed: 0x%08lX\n", hr);
61     return hr;
62 }
63 
64 static CComPtr<IUnknown>
65 AutoComplete_LoadList(DWORD dwSHACF, DWORD dwACLO)
66 {
67     // Create a multiple list (with IEnumString interface)
68     CComPtr<IUnknown> pList;
69     HRESULT hr = CoCreateInstance(CLSID_ACLMulti, NULL, CLSCTX_INPROC_SERVER,
70                                   IID_IUnknown, (LPVOID *)&pList);
71     if (FAILED(hr)) // Failed to create the list
72     {
73         ERR("CoCreateInstance(CLSID_ACLMulti) failed with 0x%08lX\n", hr);
74         return NULL;
75     }
76 
77     CComPtr<IObjMgr> pManager; // This is the manager of the multiple list
78     hr = pList->QueryInterface(IID_IObjMgr, (LPVOID *)&pManager);
79     if (FAILED(hr)) // Failed to get interface
80     {
81         ERR("pList->QueryInterface failed: 0x%08lX\n", hr);
82         return NULL;
83     }
84 
85     if (dwSHACF & SHACF_URLMRU)
86     {
87         // The MRU (Most-Recently-Used) lists (with IEnumString interface)
88 #define RUN_MRU_KEY L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\RunMRU"
89 #define TYPED_URLS_KEY L"Software\\Microsoft\\Internet Explorer\\TypedURLs"
90         AutoComplete_AddMRU(pManager, RUN_MRU_KEY);
91         AutoComplete_AddMRU(pManager, TYPED_URLS_KEY);
92     }
93 
94     if (dwSHACF & SHACF_URLHISTORY)
95     {
96         // The history list (with IEnumString interface)
97         CComPtr<IUnknown> pHistory;
98         hr = CoCreateInstance(CLSID_ACLHistory, NULL, CLSCTX_INPROC_SERVER,
99                               IID_IUnknown, (LPVOID *)&pHistory);
100         if (SUCCEEDED(hr))
101         {
102             pManager->Append(pHistory); // Add to the manager
103             // Add ACLO_* options
104             IUnknown_AddOptions(pHistory, dwACLO);
105         }
106         else
107         {
108             ERR("CLSID_ACLHistory hr:%08lX\n", hr);
109         }
110     }
111 
112     if (dwSHACF & (SHACF_FILESYSTEM | SHACF_FILESYS_ONLY | SHACF_FILESYS_DIRS))
113     {
114         // The filesystem list (with IEnumString interface)
115         CComPtr<IUnknown> pISF;
116         hr = CoCreateInstance(CLSID_ACListISF, NULL, CLSCTX_INPROC_SERVER,
117                               IID_IUnknown, (LPVOID *)&pISF);
118         if (SUCCEEDED(hr))
119         {
120             pManager->Append(pISF); // Add to the manager
121             // Add ACLO_* options
122             IUnknown_AddOptions(pISF, dwACLO);
123         }
124         else
125         {
126             ERR("CLSID_ACListISF hr:%08lX\n", hr);
127         }
128     }
129 
130     return pList; // The list
131 }
132 
133 static VOID
134 AutoComplete_AdaptFlags(IN HWND hwndEdit,
135                         IN OUT LPDWORD pdwSHACF,
136                         OUT LPDWORD pdwACO,
137                         OUT LPDWORD pdwACLO)
138 {
139 #define AUTOCOMPLETE_KEY L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\AutoComplete"
140     DWORD dwSHACF = *pdwSHACF, dwACO = 0, dwACLO = 0;
141     if (dwSHACF == SHACF_DEFAULT)
142         dwSHACF = SHACF_FILESYSTEM | SHACF_URLALL;
143 
144     if (!(dwSHACF & SHACF_AUTOAPPEND_FORCE_OFF) &&
145         ((dwSHACF & SHACF_AUTOAPPEND_FORCE_ON) ||
146          SHRegGetBoolUSValueW(AUTOCOMPLETE_KEY, L"Append Completion", FALSE, FALSE)))
147     {
148         dwACO |= ACO_AUTOAPPEND;
149     }
150 
151     if (!(dwSHACF & SHACF_AUTOSUGGEST_FORCE_OFF) &&
152         ((dwSHACF & SHACF_AUTOSUGGEST_FORCE_ON) ||
153          SHRegGetBoolUSValueW(AUTOCOMPLETE_KEY, L"AutoSuggest", FALSE, TRUE)))
154     {
155         dwACO |= ACO_AUTOSUGGEST;
156     }
157 
158     if (dwSHACF & SHACF_FILESYS_DIRS)
159         dwACLO |= ACLO_FILESYSDIRS;
160     if (dwSHACF & SHACF_FILESYS_ONLY)
161         dwACLO |= ACLO_FILESYSONLY;
162 
163     static BOOL s_bAlwaysUseTab = 99;
164     if (s_bAlwaysUseTab == 99)
165         s_bAlwaysUseTab = SHRegGetBoolUSValueW(AUTOCOMPLETE_KEY, L"Always Use Tab", FALSE, FALSE);
166 
167     if (s_bAlwaysUseTab || (dwSHACF & SHACF_USETAB))
168         dwACO |= ACO_USETAB;
169 
170     if (GetWindowLongPtrW(hwndEdit, GWL_EXSTYLE) & WS_EX_LAYOUTRTL)
171         dwACO |= ACO_RTLREADING;
172 
173     *pdwSHACF = dwSHACF;
174     *pdwACO = dwACO;
175     *pdwACLO = dwACLO;
176 }
177 
178 /*************************************************************************
179  *      SHAutoComplete  	[SHLWAPI.@]
180  *
181  * Enable auto-completion for an edit control.
182  *
183  * PARAMS
184  *  hwndEdit [I] Handle of control to enable auto-completion for
185  *  dwFlags  [I] SHACF_ flags from "shlwapi.h"
186  *
187  * RETURNS
188  *  Success: S_OK. Auto-completion is enabled for the control.
189  *  Failure: An HRESULT error code indicating the error.
190  */
191 HRESULT WINAPI SHAutoComplete(HWND hwndEdit, DWORD dwFlags)
192 {
193     TRACE("SHAutoComplete(%p, 0x%lX)\n", hwndEdit, dwFlags);
194 
195     DWORD dwSHACF = dwFlags, dwACO = 0, dwACLO = 0;
196     AutoComplete_AdaptFlags(hwndEdit, &dwSHACF, &dwACO, &dwACLO);
197 
198     // Load the list (with IEnumString interface)
199     CComPtr<IUnknown> pList = AutoComplete_LoadList(dwSHACF, dwACLO);
200     if (!pList)
201     {
202         ERR("Out of memory\n");
203         return E_OUTOFMEMORY;
204     }
205 
206     // Create an auto-completion (IAutoComplete2)
207     CComPtr<IAutoComplete2> pAC2;
208     HRESULT hr = CoCreateInstance(CLSID_AutoComplete, NULL, CLSCTX_INPROC_SERVER,
209                                   IID_IAutoComplete2, (LPVOID *)&pAC2);
210     if (FAILED(hr))
211     {
212         ERR("CoCreateInstance(CLSID_AutoComplete) failed: 0x%lX\n", hr);
213         return hr;
214     }
215 
216     // Keep the DLLs of CLSID_ACListISF and CLSID_AutoComplete loaded
217     hr = E_FAIL;
218     if (SHPinDllOfCLSID(CLSID_ACListISF) && SHPinDllOfCLSID(CLSID_AutoComplete))
219     {
220         // Initialize IAutoComplete2 for auto-completion
221         hr = pAC2->Init(hwndEdit, pList, NULL, NULL);
222         if (SUCCEEDED(hr))
223             pAC2->SetOptions(dwACO); // Set ACO_* flags
224         else
225             ERR("IAutoComplete2::Init failed: 0x%lX\n", hr);
226     }
227 
228     return hr;
229 }
230