2 * AutoComplete interfaces implementation.
4 * Copyright 2004 Maxime Bellengé <maxime.bellenge@laposte.net>
5 * Copyright 2009 Andrew Hill
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
24 - ACO_AUTOAPPEND style
25 - ACO_AUTOSUGGEST style
26 - ACO_UPDOWNKEYDROPSLIST style
28 - Handle pwzsRegKeyPath and pwszQuickComplete in Init
31 - implement ACO_SEARCH style
32 - implement ACO_FILTERPREFIXES style
33 - implement ACO_USETAB style
34 - implement ACO_RTLREADING style
40 static const WCHAR autocomplete_propertyW
[] = {'W','i','n','e',' ','A','u','t','o',
41 'c','o','m','p','l','e','t','e',' ',
42 'c','o','n','t','r','o','l',0};
44 /**************************************************************************
45 * IAutoComplete_Constructor
47 CAutoComplete::CAutoComplete()
51 options
= ACO_AUTOAPPEND
;
52 wpOrigEditProc
= NULL
;
57 wpOrigLBoxProc
= NULL
;
60 /**************************************************************************
61 * IAutoComplete_Destructor
63 CAutoComplete::~CAutoComplete()
65 TRACE(" destroying IAutoComplete(%p)\n", this);
66 HeapFree(GetProcessHeap(), 0, quickComplete
);
67 HeapFree(GetProcessHeap(), 0, txtbackup
);
68 RemovePropW(hwndEdit
, autocomplete_propertyW
);
69 SetWindowLongPtrW(hwndEdit
, GWLP_WNDPROC
, (LONG_PTR
)wpOrigEditProc
);
71 DestroyWindow(hwndListBox
);
74 /******************************************************************************
75 * IAutoComplete_fnEnable
77 HRESULT WINAPI
CAutoComplete::Enable(BOOL fEnable
)
81 TRACE("(%p)->(%s)\n", this, (fEnable
) ? "true" : "false");
88 /******************************************************************************
91 void CAutoComplete::CreateListbox()
93 HWND hwndParent
= GetParent(hwndEdit
);
95 /* FIXME : The listbox should be resizable with the mouse. WS_THICKFRAME looks ugly */
96 hwndListBox
= CreateWindowExW(0, WC_LISTBOXW
, NULL
,
97 WS_BORDER
| WS_CHILD
| WS_VSCROLL
| LBS_HASSTRINGS
| LBS_NOTIFY
| LBS_NOINTEGRALHEIGHT
,
98 CW_USEDEFAULT
, CW_USEDEFAULT
, CW_USEDEFAULT
, CW_USEDEFAULT
,
100 (HINSTANCE
)GetWindowLongPtrW(hwndParent
, GWLP_HINSTANCE
), NULL
);
104 wpOrigLBoxProc
= (WNDPROC
)SetWindowLongPtrW(hwndListBox
, GWLP_WNDPROC
, (LONG_PTR
)ACLBoxSubclassProc
);
105 SetWindowLongPtrW(hwndListBox
, GWLP_USERDATA
, (LONG_PTR
)this);
110 /******************************************************************************
111 * IAutoComplete_fnInit
113 HRESULT WINAPI
CAutoComplete::Init(HWND hwndEdit
, IUnknown
*punkACL
, LPCOLESTR pwzsRegKeyPath
, LPCOLESTR pwszQuickComplete
)
115 TRACE("(%p)->(0x%08lx, %p, %s, %s)\n",
116 this, hwndEdit
, punkACL
, debugstr_w(pwzsRegKeyPath
), debugstr_w(pwszQuickComplete
));
118 if (options
& ACO_AUTOSUGGEST
)
119 TRACE(" ACO_AUTOSUGGEST\n");
120 if (options
& ACO_AUTOAPPEND
)
121 TRACE(" ACO_AUTOAPPEND\n");
122 if (options
& ACO_SEARCH
)
123 FIXME(" ACO_SEARCH not supported\n");
124 if (options
& ACO_FILTERPREFIXES
)
125 FIXME(" ACO_FILTERPREFIXES not supported\n");
126 if (options
& ACO_USETAB
)
127 FIXME(" ACO_USETAB not supported\n");
128 if (options
& ACO_UPDOWNKEYDROPSLIST
)
129 TRACE(" ACO_UPDOWNKEYDROPSLIST\n");
130 if (options
& ACO_RTLREADING
)
131 FIXME(" ACO_RTLREADING not supported\n");
133 if (!hwndEdit
|| !punkACL
)
136 if (this->initialized
)
138 WARN("Autocompletion object is already initialized\n");
139 /* This->hwndEdit is set to NULL when the edit window is destroyed. */
140 return this->hwndEdit
? E_FAIL
: E_UNEXPECTED
;
143 if (!SUCCEEDED(punkACL
->QueryInterface(IID_PPV_ARG(IEnumString
,&enumstr
))))
145 TRACE("No IEnumString interface\n");
146 return E_NOINTERFACE
;
149 this->hwndEdit
= hwndEdit
;
150 this->initialized
= TRUE
;
151 this->wpOrigEditProc
= (WNDPROC
)SetWindowLongPtrW(hwndEdit
, GWLP_WNDPROC
, (LONG_PTR
) ACEditSubclassProc
);
152 /* Keep at least one reference to the object until the edit window is destroyed. */
154 SetPropW( this->hwndEdit
, autocomplete_propertyW
, (HANDLE
)this );
156 if (options
& ACO_AUTOSUGGEST
)
158 this->CreateListbox();
164 WCHAR result
[MAX_PATH
];
170 /* pwszRegKeyPath contains the key as well as the value, so we split */
171 key
= (WCHAR
*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, (wcslen(pwzsRegKeyPath
) + 1) * sizeof(WCHAR
));
175 wcscpy(key
, pwzsRegKeyPath
);
176 value
= const_cast<WCHAR
*>(wcsrchr(key
, '\\'));
182 /* Now value contains the value and buffer the key */
183 res
= RegOpenKeyExW(HKEY_CURRENT_USER
, key
, 0, KEY_READ
, &hKey
);
185 if (res
!= ERROR_SUCCESS
)
187 /* if the key is not found, MSDN states we must seek in HKEY_LOCAL_MACHINE */
188 res
= RegOpenKeyExW(HKEY_LOCAL_MACHINE
, key
, 0, KEY_READ
, &hKey
);
191 if (res
== ERROR_SUCCESS
)
193 len
= sizeof(result
);
194 res
= RegQueryValueW(hKey
, value
, result
, &len
);
195 if (res
== ERROR_SUCCESS
)
197 quickComplete
= (WCHAR
*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, len
* sizeof(WCHAR
));
198 wcscpy(quickComplete
, result
);
204 HeapFree(GetProcessHeap(), 0, key
);
208 TRACE("HeapAlloc Failed when trying to alloca %d bytes\n", (wcslen(pwzsRegKeyPath
) + 1) * sizeof(WCHAR
));
213 if ((pwszQuickComplete
) && (!quickComplete
))
215 quickComplete
= (WCHAR
*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, (wcslen(pwszQuickComplete
) + 1) * sizeof(WCHAR
));
219 wcscpy(quickComplete
, pwszQuickComplete
);
223 TRACE("HeapAlloc Failed when trying to alloca %d bytes\n", (wcslen(pwszQuickComplete
) + 1) * sizeof(WCHAR
));
231 /**************************************************************************
232 * IAutoComplete_fnGetOptions
234 HRESULT WINAPI
CAutoComplete::GetOptions(DWORD
*pdwFlag
)
238 TRACE("(%p) -> (%p)\n", this, pdwFlag
);
245 /**************************************************************************
246 * IAutoComplete_fnSetOptions
248 HRESULT WINAPI
CAutoComplete::SetOptions(DWORD dwFlag
)
252 TRACE("(%p) -> (0x%x)\n", this, dwFlag
);
254 options
= (AUTOCOMPLETEOPTIONS
)dwFlag
;
256 if ((options
& ACO_AUTOSUGGEST
) && hwndEdit
&& !hwndListBox
)
263 Window procedure for autocompletion
265 LRESULT APIENTRY
CAutoComplete::ACEditSubclassProc(HWND hwnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
267 CAutoComplete
*pThis
= static_cast<CAutoComplete
*>(GetPropW(hwnd
, autocomplete_propertyW
));
272 BOOL control
, filled
, displayall
= FALSE
;
273 int cpt
, height
, sel
;
278 return CallWindowProcW(pThis
->wpOrigEditProc
, hwnd
, uMsg
, wParam
, lParam
);
283 case CB_SHOWDROPDOWN
:
285 ShowWindow(pThis
->hwndListBox
, SW_HIDE
);
290 if ((pThis
->options
& ACO_AUTOSUGGEST
) && ((HWND
)wParam
!= pThis
->hwndListBox
))
292 ShowWindow(pThis
->hwndListBox
, SW_HIDE
);
294 return CallWindowProcW(pThis
->wpOrigEditProc
, hwnd
, uMsg
, wParam
, lParam
);
299 GetWindowTextW(hwnd
, (LPWSTR
)hwndText
, 255);
305 /* If quickComplete is set and control is pressed, replace the string */
306 control
= GetKeyState(VK_CONTROL
) & 0x8000;
307 if (control
&& pThis
->quickComplete
)
309 hwndQCText
= (WCHAR
*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
,
310 (wcslen(pThis
->quickComplete
)+wcslen(hwndText
))*sizeof(WCHAR
));
311 sel
= swprintf(hwndQCText
, pThis
->quickComplete
, hwndText
);
312 SendMessageW(hwnd
, WM_SETTEXT
, 0, (LPARAM
)hwndQCText
);
313 SendMessageW(hwnd
, EM_SETSEL
, 0, sel
);
314 HeapFree(GetProcessHeap(), 0, hwndQCText
);
317 ShowWindow(pThis
->hwndListBox
, SW_HIDE
);
331 - if the listbox is not visible, displays it
332 with all the entries if the style ACO_UPDOWNKEYDROPSLIST
333 is present but does not select anything.
334 - if the listbox is visible, change the selection
336 if ( (pThis
->options
& (ACO_AUTOSUGGEST
| ACO_UPDOWNKEYDROPSLIST
))
337 && (!IsWindowVisible(pThis
->hwndListBox
) && (! *hwndText
)) )
339 /* We must display all the entries */
344 if (IsWindowVisible(pThis
->hwndListBox
))
348 count
= SendMessageW(pThis
->hwndListBox
, LB_GETCOUNT
, 0, 0);
349 /* Change the selection */
350 sel
= SendMessageW(pThis
->hwndListBox
, LB_GETCURSEL
, 0, 0);
352 sel
= ((sel
-1) < 0) ? count
-1 : sel
-1;
354 sel
= ((sel
+1) >= count
) ? -1 : sel
+1;
356 SendMessageW(pThis
->hwndListBox
, LB_SETCURSEL
, sel
, 0);
363 len
= SendMessageW(pThis
->hwndListBox
, LB_GETTEXTLEN
, sel
, (LPARAM
)NULL
);
364 msg
= (WCHAR
*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, (len
+ 1) * sizeof(WCHAR
));
368 SendMessageW(pThis
->hwndListBox
, LB_GETTEXT
, sel
, (LPARAM
)msg
);
369 SendMessageW(hwnd
, WM_SETTEXT
, 0, (LPARAM
)msg
);
370 SendMessageW(hwnd
, EM_SETSEL
, wcslen(msg
), wcslen(msg
));
372 HeapFree(GetProcessHeap(), 0, msg
);
376 TRACE("HeapAlloc failed to allocate %d bytes\n", (len
+ 1) * sizeof(WCHAR
));
381 SendMessageW(hwnd
, WM_SETTEXT
, 0, (LPARAM
)pThis
->txtbackup
);
382 SendMessageW(hwnd
, EM_SETSEL
, wcslen(pThis
->txtbackup
), wcslen(pThis
->txtbackup
));
392 if ((! *hwndText
) && (pThis
->options
& ACO_AUTOSUGGEST
))
394 ShowWindow(pThis
->hwndListBox
, SW_HIDE
);
395 return CallWindowProcW(pThis
->wpOrigEditProc
, hwnd
, uMsg
, wParam
, lParam
);
398 if (pThis
->options
& ACO_AUTOAPPEND
)
401 SendMessageW(hwnd
, EM_GETSEL
, (WPARAM
)&b
, (LPARAM
)NULL
);
404 hwndText
[b
-1] = '\0';
409 SetWindowTextW(hwnd
, hwndText
);
418 SendMessageW(pThis
->hwndListBox
, LB_RESETCONTENT
, 0, 0);
420 HeapFree(GetProcessHeap(), 0, pThis
->txtbackup
);
422 pThis
->txtbackup
= (WCHAR
*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, (wcslen(hwndText
)+1)*sizeof(WCHAR
));
424 if (pThis
->txtbackup
)
426 wcscpy(pThis
->txtbackup
, hwndText
);
430 TRACE("HeapAlloc failed to allocate %d bytes\n", (wcslen(hwndText
)+1)*sizeof(WCHAR
));
433 /* Returns if there is no text to search and we doesn't want to display all the entries */
434 if ((!displayall
) && (! *hwndText
) )
437 pThis
->enumstr
->Reset();
439 size_t curlen
= wcslen(hwndText
);
443 CComHeapPtr
<OLECHAR
> strs
;
444 hr
= pThis
->enumstr
->Next(1, &strs
, &fetched
);
448 if (!_wcsnicmp(hwndText
, strs
, curlen
))
451 if (pThis
->options
& ACO_AUTOAPPEND
&& *hwndText
)
453 CComBSTR
str((PCWSTR
)strs
);
454 memcpy(str
.m_str
, hwndText
, curlen
* sizeof(WCHAR
));
455 SetWindowTextW(hwnd
, str
);
456 SendMessageW(hwnd
, EM_SETSEL
, curlen
, str
.Length());
457 if (!(pThis
->options
& ACO_AUTOSUGGEST
))
461 if (pThis
->options
& ACO_AUTOSUGGEST
)
463 SendMessageW(pThis
->hwndListBox
, LB_ADDSTRING
, 0, (LPARAM
)(LPOLESTR
)strs
);
470 if (pThis
->options
& ACO_AUTOSUGGEST
)
474 height
= SendMessageW(pThis
->hwndListBox
, LB_GETITEMHEIGHT
, 0, 0);
475 SendMessageW(pThis
->hwndListBox
, LB_CARETOFF
, 0, 0);
476 GetWindowRect(hwnd
, &r
);
477 SetParent(pThis
->hwndListBox
, HWND_DESKTOP
);
478 /* It seems that Windows XP displays 7 lines at most
479 and otherwise displays a vertical scroll bar */
480 SetWindowPos(pThis
->hwndListBox
, HWND_TOP
,
481 r
.left
, r
.bottom
+ 1, r
.right
- r
.left
, min(height
* 7, height
* (cpt
+ 1)),
486 ShowWindow(pThis
->hwndListBox
, SW_HIDE
);
494 /* Release our reference that we had since ->Init() */
502 return CallWindowProcW(pThis
->wpOrigEditProc
, hwnd
, uMsg
, wParam
, lParam
);
510 LRESULT APIENTRY
CAutoComplete::ACLBoxSubclassProc(HWND hwnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
512 CAutoComplete
*pThis
= reinterpret_cast<CAutoComplete
*>(GetWindowLongPtrW(hwnd
, GWLP_USERDATA
));
520 sel
= SendMessageW(hwnd
, LB_ITEMFROMPOINT
, 0, lParam
);
521 SendMessageW(hwnd
, LB_SETCURSEL
, (WPARAM
)sel
, (LPARAM
)0);
526 sel
= SendMessageW(hwnd
, LB_GETCURSEL
, 0, 0);
531 len
= SendMessageW(pThis
->hwndListBox
, LB_GETTEXTLEN
, sel
, 0);
532 msg
= (WCHAR
*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, (len
+ 1) * sizeof(WCHAR
));
536 SendMessageW(hwnd
, LB_GETTEXT
, sel
, (LPARAM
)msg
);
537 SendMessageW(pThis
->hwndEdit
, WM_SETTEXT
, 0, (LPARAM
)msg
);
538 SendMessageW(pThis
->hwndEdit
, EM_SETSEL
, 0, wcslen(msg
));
539 ShowWindow(hwnd
, SW_HIDE
);
541 HeapFree(GetProcessHeap(), 0, msg
);
545 TRACE("HeapAlloc failed to allocate %d bytes\n", (len
+ 1) * sizeof(WCHAR
));
551 return CallWindowProcW(pThis
->wpOrigLBoxProc
, hwnd
, uMsg
, wParam
, lParam
);
556 /**************************************************************************
557 * IAutoCompleteDropDown
559 HRESULT STDMETHODCALLTYPE
CAutoComplete::GetDropDownStatus(DWORD
*pdwFlags
, LPWSTR
*ppwszString
)
561 BOOL dropped
= IsWindowVisible(hwndListBox
);
564 *pdwFlags
= (dropped
? ACDD_VISIBLE
: 0);
572 int sel
= SendMessageW(hwndListBox
, LB_GETCURSEL
, 0, 0);
575 DWORD len
= SendMessageW(hwndListBox
, LB_GETTEXTLEN
, sel
, 0);
576 *ppwszString
= (LPWSTR
)CoTaskMemAlloc((len
+1)*sizeof(WCHAR
));
577 SendMessageW(hwndListBox
, LB_GETTEXT
, sel
, (LPARAM
)*ppwszString
);
585 HRESULT STDMETHODCALLTYPE
CAutoComplete::ResetEnumerator()
587 FIXME("(%p): stub\n", this);
591 /**************************************************************************
594 HRESULT STDMETHODCALLTYPE
CAutoComplete::Next(ULONG celt
, LPOLESTR
*rgelt
, ULONG
*pceltFetched
)
596 FIXME("(%p, %d, %p, %p): stub\n", this, celt
, rgelt
, pceltFetched
);
601 HRESULT STDMETHODCALLTYPE
CAutoComplete::Skip(ULONG celt
)
603 FIXME("(%p, %d): stub\n", this, celt
);
607 HRESULT STDMETHODCALLTYPE
CAutoComplete::Reset()
609 FIXME("(%p): stub\n", this);
613 HRESULT STDMETHODCALLTYPE
CAutoComplete::Clone(IEnumString
**ppOut
)
615 FIXME("(%p, %p): stub\n", this, ppOut
);