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 /******************************************************************************
89 * IAutoComplete_fnInit
91 HRESULT WINAPI
CAutoComplete::Init(HWND hwndEdit
, IUnknown
*punkACL
, LPCOLESTR pwzsRegKeyPath
, LPCOLESTR pwszQuickComplete
)
93 static const WCHAR lbName
[] = {'L','i','s','t','B','o','x',0};
95 TRACE("(%p)->(0x%08lx, %p, %s, %s)\n",
96 this, hwndEdit
, punkACL
, debugstr_w(pwzsRegKeyPath
), debugstr_w(pwszQuickComplete
));
98 if (options
& ACO_AUTOSUGGEST
)
99 TRACE(" ACO_AUTOSUGGEST\n");
100 if (options
& ACO_AUTOAPPEND
)
101 TRACE(" ACO_AUTOAPPEND\n");
102 if (options
& ACO_SEARCH
)
103 FIXME(" ACO_SEARCH not supported\n");
104 if (options
& ACO_FILTERPREFIXES
)
105 FIXME(" ACO_FILTERPREFIXES not supported\n");
106 if (options
& ACO_USETAB
)
107 FIXME(" ACO_USETAB not supported\n");
108 if (options
& ACO_UPDOWNKEYDROPSLIST
)
109 TRACE(" ACO_UPDOWNKEYDROPSLIST\n");
110 if (options
& ACO_RTLREADING
)
111 FIXME(" ACO_RTLREADING not supported\n");
113 if (!hwndEdit
|| !punkACL
)
116 if (this->initialized
)
118 WARN("Autocompletion object is already initialized\n");
119 /* This->hwndEdit is set to NULL when the edit window is destroyed. */
120 return this->hwndEdit
? E_FAIL
: E_UNEXPECTED
;
123 if (!SUCCEEDED(punkACL
->QueryInterface(IID_PPV_ARG(IEnumString
,&enumstr
))))
125 TRACE("No IEnumString interface\n");
126 return E_NOINTERFACE
;
129 this->hwndEdit
= hwndEdit
;
130 this->initialized
= TRUE
;
131 wpOrigEditProc
= (WNDPROC
)SetWindowLongPtrW(hwndEdit
, GWLP_WNDPROC
, (LONG_PTR
) ACEditSubclassProc
);
132 // SetWindowLongPtrW(hwndEdit, GWLP_USERDATA, (LONG_PTR)this);
133 SetPropW( hwndEdit
, autocomplete_propertyW
, (HANDLE
)this );
135 if (options
& ACO_AUTOSUGGEST
)
139 hwndParent
= GetParent(hwndEdit
);
141 /* FIXME : The listbox should be resizable with the mouse. WS_THICKFRAME looks ugly */
142 hwndListBox
= CreateWindowExW(0, lbName
, NULL
,
143 WS_BORDER
| WS_CHILD
| WS_VSCROLL
| LBS_HASSTRINGS
| LBS_NOTIFY
| LBS_NOINTEGRALHEIGHT
,
144 CW_USEDEFAULT
, CW_USEDEFAULT
, CW_USEDEFAULT
, CW_USEDEFAULT
,
146 (HINSTANCE
)GetWindowLongPtrW(hwndParent
, GWLP_HINSTANCE
), NULL
);
150 wpOrigLBoxProc
= (WNDPROC
)SetWindowLongPtrW(hwndListBox
, GWLP_WNDPROC
, (LONG_PTR
)ACLBoxSubclassProc
);
151 SetWindowLongPtrW(hwndListBox
, GWLP_USERDATA
, (LONG_PTR
)this);
158 WCHAR result
[MAX_PATH
];
164 /* pwszRegKeyPath contains the key as well as the value, so we split */
165 key
= (WCHAR
*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, (wcslen(pwzsRegKeyPath
) + 1) * sizeof(WCHAR
));
169 wcscpy(key
, pwzsRegKeyPath
);
170 value
= const_cast<WCHAR
*>(wcsrchr(key
, '\\'));
176 /* Now value contains the value and buffer the key */
177 res
= RegOpenKeyExW(HKEY_CURRENT_USER
, key
, 0, KEY_READ
, &hKey
);
179 if (res
!= ERROR_SUCCESS
)
181 /* if the key is not found, MSDN states we must seek in HKEY_LOCAL_MACHINE */
182 res
= RegOpenKeyExW(HKEY_LOCAL_MACHINE
, key
, 0, KEY_READ
, &hKey
);
185 if (res
== ERROR_SUCCESS
)
187 len
= sizeof(result
);
188 res
= RegQueryValueW(hKey
, value
, result
, &len
);
189 if (res
== ERROR_SUCCESS
)
191 quickComplete
= (WCHAR
*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, len
* sizeof(WCHAR
));
192 wcscpy(quickComplete
, result
);
198 HeapFree(GetProcessHeap(), 0, key
);
202 TRACE("HeapAlloc Failed when trying to alloca %d bytes\n", (wcslen(pwzsRegKeyPath
) + 1) * sizeof(WCHAR
));
207 if ((pwszQuickComplete
) && (!quickComplete
))
209 quickComplete
= (WCHAR
*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, (wcslen(pwszQuickComplete
) + 1) * sizeof(WCHAR
));
213 wcscpy(quickComplete
, pwszQuickComplete
);
217 TRACE("HeapAlloc Failed when trying to alloca %d bytes\n", (wcslen(pwszQuickComplete
) + 1) * sizeof(WCHAR
));
225 /**************************************************************************
226 * IAutoComplete_fnGetOptions
228 HRESULT WINAPI
CAutoComplete::GetOptions(DWORD
*pdwFlag
)
232 TRACE("(%p) -> (%p)\n", this, pdwFlag
);
239 /**************************************************************************
240 * IAutoComplete_fnSetOptions
242 HRESULT WINAPI
CAutoComplete::SetOptions(DWORD dwFlag
)
246 TRACE("(%p) -> (0x%x)\n", this, dwFlag
);
248 options
= (AUTOCOMPLETEOPTIONS
)dwFlag
;
254 Window procedure for autocompletion
256 LRESULT APIENTRY
CAutoComplete::ACEditSubclassProc(HWND hwnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
258 CAutoComplete
*pThis
= static_cast<CAutoComplete
*>(GetPropW(hwnd
, autocomplete_propertyW
));//GetWindowLongPtrW(hwnd, GWLP_USERDATA);
264 BOOL control
, filled
, displayall
= FALSE
;
265 int cpt
, height
, sel
;
270 return CallWindowProcW(pThis
->wpOrigEditProc
, hwnd
, uMsg
, wParam
, lParam
);
275 case CB_SHOWDROPDOWN
:
277 ShowWindow(pThis
->hwndListBox
, SW_HIDE
);
282 if ((pThis
->options
& ACO_AUTOSUGGEST
) && ((HWND
)wParam
!= pThis
->hwndListBox
))
284 ShowWindow(pThis
->hwndListBox
, SW_HIDE
);
286 return CallWindowProcW(pThis
->wpOrigEditProc
, hwnd
, uMsg
, wParam
, lParam
);
291 GetWindowTextW(hwnd
, (LPWSTR
)hwndText
, 255);
297 /* If quickComplete is set and control is pressed, replace the string */
298 control
= GetKeyState(VK_CONTROL
) & 0x8000;
299 if (control
&& pThis
->quickComplete
)
301 hwndQCText
= (WCHAR
*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
,
302 (wcslen(pThis
->quickComplete
)+wcslen(hwndText
))*sizeof(WCHAR
));
303 sel
= swprintf(hwndQCText
, pThis
->quickComplete
, hwndText
);
304 SendMessageW(hwnd
, WM_SETTEXT
, 0, (LPARAM
)hwndQCText
);
305 SendMessageW(hwnd
, EM_SETSEL
, 0, sel
);
306 HeapFree(GetProcessHeap(), 0, hwndQCText
);
309 ShowWindow(pThis
->hwndListBox
, SW_HIDE
);
323 - if the listbox is not visible, displays it
324 with all the entries if the style ACO_UPDOWNKEYDROPSLIST
325 is present but does not select anything.
326 - if the listbox is visible, change the selection
328 if ( (pThis
->options
& (ACO_AUTOSUGGEST
| ACO_UPDOWNKEYDROPSLIST
))
329 && (!IsWindowVisible(pThis
->hwndListBox
) && (! *hwndText
)) )
331 /* We must display all the entries */
336 if (IsWindowVisible(pThis
->hwndListBox
))
340 count
= SendMessageW(pThis
->hwndListBox
, LB_GETCOUNT
, 0, 0);
341 /* Change the selection */
342 sel
= SendMessageW(pThis
->hwndListBox
, LB_GETCURSEL
, 0, 0);
344 sel
= ((sel
-1)<0)?count
-1:sel
-1;
346 sel
= ((sel
+1)>= count
)?-1:sel
+1;
348 SendMessageW(pThis
->hwndListBox
, LB_SETCURSEL
, sel
, 0);
355 len
= SendMessageW(pThis
->hwndListBox
, LB_GETTEXTLEN
, sel
, (LPARAM
)NULL
);
356 msg
= (WCHAR
*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, (len
+ 1) * sizeof(WCHAR
));
360 SendMessageW(pThis
->hwndListBox
, LB_GETTEXT
, sel
, (LPARAM
)msg
);
361 SendMessageW(hwnd
, WM_SETTEXT
, 0, (LPARAM
)msg
);
362 SendMessageW(hwnd
, EM_SETSEL
, wcslen(msg
), wcslen(msg
));
364 HeapFree(GetProcessHeap(), 0, msg
);
368 TRACE("HeapAlloc failed to allocate %d bytes\n", (len
+ 1) * sizeof(WCHAR
));
373 SendMessageW(hwnd
, WM_SETTEXT
, 0, (LPARAM
)pThis
->txtbackup
);
374 SendMessageW(hwnd
, EM_SETSEL
, wcslen(pThis
->txtbackup
), wcslen(pThis
->txtbackup
));
384 if ((! *hwndText
) && (pThis
->options
& ACO_AUTOSUGGEST
))
386 ShowWindow(pThis
->hwndListBox
, SW_HIDE
);
387 return CallWindowProcW(pThis
->wpOrigEditProc
, hwnd
, uMsg
, wParam
, lParam
);
390 if (pThis
->options
& ACO_AUTOAPPEND
)
393 SendMessageW(hwnd
, EM_GETSEL
, (WPARAM
)&b
, (LPARAM
)NULL
);
396 hwndText
[b
-1] = '\0';
401 SetWindowTextW(hwnd
, hwndText
);
410 SendMessageW(pThis
->hwndListBox
, LB_RESETCONTENT
, 0, 0);
412 HeapFree(GetProcessHeap(), 0, pThis
->txtbackup
);
414 pThis
->txtbackup
= (WCHAR
*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, (wcslen(hwndText
)+1)*sizeof(WCHAR
));
416 if (pThis
->txtbackup
)
418 wcscpy(pThis
->txtbackup
, hwndText
);
422 TRACE("HeapAlloc failed to allocate %d bytes\n", (wcslen(hwndText
)+1)*sizeof(WCHAR
));
425 /* Returns if there is no text to search and we doesn't want to display all the entries */
426 if ((!displayall
) && (! *hwndText
) )
429 pThis
->enumstr
->Reset();
434 hr
= pThis
->enumstr
->Next(1, &strs
, &fetched
);
438 if ((LPWSTR
)wcsstr(strs
, hwndText
) == strs
)
441 if (pThis
->options
& ACO_AUTOAPPEND
)
443 SetWindowTextW(hwnd
, strs
);
444 SendMessageW(hwnd
, EM_SETSEL
, wcslen(hwndText
), wcslen(strs
));
448 if (pThis
->options
& ACO_AUTOSUGGEST
)
450 SendMessageW(pThis
->hwndListBox
, LB_ADDSTRING
, 0, (LPARAM
)strs
);
457 if (pThis
->options
& ACO_AUTOSUGGEST
)
461 height
= SendMessageW(pThis
->hwndListBox
, LB_GETITEMHEIGHT
, 0, 0);
462 SendMessageW(pThis
->hwndListBox
, LB_CARETOFF
, 0, 0);
463 GetWindowRect(hwnd
, &r
);
464 SetParent(pThis
->hwndListBox
, HWND_DESKTOP
);
465 /* It seems that Windows XP displays 7 lines at most
466 and otherwise displays a vertical scroll bar */
467 SetWindowPos(pThis
->hwndListBox
, HWND_TOP
,
468 r
.left
, r
.bottom
+ 1, r
.right
- r
.left
, min(height
* 7, height
* (cpt
+ 1)),
473 ShowWindow(pThis
->hwndListBox
, SW_HIDE
);
481 return CallWindowProcW(pThis
->wpOrigEditProc
, hwnd
, uMsg
, wParam
, lParam
);
489 LRESULT APIENTRY
CAutoComplete::ACLBoxSubclassProc(HWND hwnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
491 CAutoComplete
*pThis
= reinterpret_cast<CAutoComplete
*>(GetWindowLongPtrW(hwnd
, GWLP_USERDATA
));
499 sel
= SendMessageW(hwnd
, LB_ITEMFROMPOINT
, 0, lParam
);
500 SendMessageW(hwnd
, LB_SETCURSEL
, (WPARAM
)sel
, (LPARAM
)0);
505 sel
= SendMessageW(hwnd
, LB_GETCURSEL
, 0, 0);
510 len
= SendMessageW(pThis
->hwndListBox
, LB_GETTEXTLEN
, sel
, 0);
511 msg
= (WCHAR
*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, (len
+ 1) * sizeof(WCHAR
));
515 SendMessageW(hwnd
, LB_GETTEXT
, sel
, (LPARAM
)msg
);
516 SendMessageW(pThis
->hwndEdit
, WM_SETTEXT
, 0, (LPARAM
)msg
);
517 SendMessageW(pThis
->hwndEdit
, EM_SETSEL
, 0, wcslen(msg
));
518 ShowWindow(hwnd
, SW_HIDE
);
520 HeapFree(GetProcessHeap(), 0, msg
);
524 TRACE("HeapAlloc failed to allocate %d bytes\n", (len
+ 1) * sizeof(WCHAR
));
530 return CallWindowProcW(pThis
->wpOrigLBoxProc
, hwnd
, uMsg
, wParam
, lParam
);
535 /**************************************************************************
536 * IAutoCompleteDropDown
538 HRESULT STDMETHODCALLTYPE
CAutoComplete::GetDropDownStatus(DWORD
*pdwFlags
, LPWSTR
*ppwszString
)
540 FIXME("(%p, %p, %p): stub\n", this, pdwFlags
, ppwszString
);
548 HRESULT STDMETHODCALLTYPE
CAutoComplete::ResetEnumerator()
550 FIXME("(%p): stub\n", this);
554 /**************************************************************************
557 HRESULT STDMETHODCALLTYPE
CAutoComplete::Next(ULONG celt
, LPOLESTR
*rgelt
, ULONG
*pceltFetched
)
559 FIXME("(%p, %d, %p, %p): stub\n", this, celt
, rgelt
, pceltFetched
);
564 HRESULT STDMETHODCALLTYPE
CAutoComplete::Skip(ULONG celt
)
566 FIXME("(%p, %d): stub\n", this, celt
);
570 HRESULT STDMETHODCALLTYPE
CAutoComplete::Reset()
572 FIXME("(%p): stub\n", this);
576 HRESULT STDMETHODCALLTYPE
CAutoComplete::Clone(IEnumString
**ppOut
)
578 FIXME("(%p, %p): stub\n", this, ppOut
);