2 * AutoComplete interfaces implementation.
4 * Copyright 2004 Maxime Bellengé <maxime.bellenge@laposte.net>
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
23 - ACO_AUTOAPPEND style
24 - ACO_AUTOSUGGEST style
25 - ACO_UPDOWNKEYDROPSLIST style
27 - Handle pwzsRegKeyPath and pwszQuickComplete in Init
30 - implement ACO_SEARCH style
31 - implement ACO_FILTERPREFIXES style
32 - implement ACO_USETAB style
33 - implement ACO_RTLREADING style
34 - implement ResetEnumerator
35 - string compares should be case-insensitive, the content of the list should be sorted
46 #include "wine/debug.h"
50 #include "undocshell.h"
59 #include "shell32_main.h"
61 #include "wine/unicode.h"
63 WINE_DEFAULT_DEBUG_CHANNEL(shell
);
67 const IAutoComplete2Vtbl
*lpVtbl
;
68 const IAutoCompleteDropDownVtbl
*lpDropDownVtbl
;
73 WNDPROC wpOrigEditProc
;
74 WNDPROC wpOrigLBoxProc
;
78 AUTOCOMPLETEOPTIONS options
;
81 static const IAutoComplete2Vtbl acvt
;
82 static const IAutoCompleteDropDownVtbl acdropdownvt
;
86 converts This to an interface pointer
88 #define _IUnknown_(This) ((IUnknown*)&(This)->lpVtbl)
89 #define _IAutoComplete2_(This) ((IAutoComplete2*)&(This)->lpVtbl)
90 #define _IAutoCompleteDropDown_(This) (&(This)->lpDropDownVtbl)
92 static inline IAutoCompleteImpl
*impl_from_IAutoCompleteDropDown(IAutoCompleteDropDown
*iface
)
94 return (IAutoCompleteImpl
*)((char *)iface
- FIELD_OFFSET(IAutoCompleteImpl
, lpDropDownVtbl
));
97 static LRESULT APIENTRY
ACEditSubclassProc(HWND hwnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
);
98 static LRESULT APIENTRY
ACLBoxSubclassProc(HWND hwnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
);
100 static void create_listbox(IAutoCompleteImpl
*This
)
104 hwndParent
= GetParent(This
->hwndEdit
);
106 /* FIXME : The listbox should be resizable with the mouse. WS_THICKFRAME looks ugly */
107 This
->hwndListBox
= CreateWindowExW(0, WC_LISTBOXW
, NULL
,
108 WS_BORDER
| WS_CHILD
| WS_VSCROLL
| LBS_HASSTRINGS
| LBS_NOTIFY
| LBS_NOINTEGRALHEIGHT
,
109 CW_USEDEFAULT
, CW_USEDEFAULT
, CW_USEDEFAULT
, CW_USEDEFAULT
,
110 hwndParent
, NULL
, shell32_hInstance
, NULL
);
112 if (This
->hwndListBox
) {
113 This
->wpOrigLBoxProc
= (WNDPROC
) SetWindowLongPtrW( This
->hwndListBox
, GWLP_WNDPROC
, (LONG_PTR
) ACLBoxSubclassProc
);
114 SetWindowLongPtrW( This
->hwndListBox
, GWLP_USERDATA
, (LONG_PTR
)This
);
118 /**************************************************************************
119 * IAutoComplete_Constructor
121 HRESULT WINAPI
IAutoComplete_Constructor(IUnknown
* pUnkOuter
, REFIID riid
, LPVOID
* ppv
)
123 IAutoCompleteImpl
*lpac
;
125 if (pUnkOuter
&& !IsEqualIID (riid
, &IID_IUnknown
))
126 return CLASS_E_NOAGGREGATION
;
128 lpac
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(IAutoCompleteImpl
));
130 return E_OUTOFMEMORY
;
133 lpac
->lpVtbl
= &acvt
;
134 lpac
->lpDropDownVtbl
= &acdropdownvt
;
135 lpac
->enabled
= TRUE
;
136 lpac
->enumstr
= NULL
;
137 lpac
->options
= ACO_AUTOAPPEND
;
138 lpac
->wpOrigEditProc
= NULL
;
139 lpac
->hwndListBox
= NULL
;
140 lpac
->txtbackup
= NULL
;
141 lpac
->quickComplete
= NULL
;
143 if (FAILED (IUnknown_QueryInterface (_IUnknown_ (lpac
), riid
, ppv
))) {
144 IUnknown_Release (_IUnknown_ (lpac
));
145 return E_NOINTERFACE
;
148 TRACE("-- (%p)->\n",lpac
);
153 /**************************************************************************
154 * AutoComplete_QueryInterface
156 static HRESULT WINAPI
IAutoComplete2_fnQueryInterface(
157 IAutoComplete2
* iface
,
161 IAutoCompleteImpl
*This
= (IAutoCompleteImpl
*)iface
;
163 TRACE("(%p)->(IID:%s,%p)\n", This
, shdebugstr_guid(riid
), ppvObj
);
166 if (IsEqualIID(riid
, &IID_IUnknown
) ||
167 IsEqualIID(riid
, &IID_IAutoComplete
) ||
168 IsEqualIID(riid
, &IID_IAutoComplete2
))
172 else if (IsEqualIID(riid
, &IID_IAutoCompleteDropDown
))
174 *ppvObj
= _IAutoCompleteDropDown_(This
);
179 IUnknown_AddRef((IUnknown
*)*ppvObj
);
180 TRACE("-- Interface: (%p)->(%p)\n", ppvObj
, *ppvObj
);
183 WARN("unsupported interface: %s\n", debugstr_guid(riid
));
184 return E_NOINTERFACE
;
187 /******************************************************************************
188 * IAutoComplete2_fnAddRef
190 static ULONG WINAPI
IAutoComplete2_fnAddRef(
191 IAutoComplete2
* iface
)
193 IAutoCompleteImpl
*This
= (IAutoCompleteImpl
*)iface
;
194 ULONG refCount
= InterlockedIncrement(&This
->ref
);
196 TRACE("(%p)->(%u)\n", This
, refCount
- 1);
201 /******************************************************************************
202 * IAutoComplete2_fnRelease
204 static ULONG WINAPI
IAutoComplete2_fnRelease(
205 IAutoComplete2
* iface
)
207 IAutoCompleteImpl
*This
= (IAutoCompleteImpl
*)iface
;
208 ULONG refCount
= InterlockedDecrement(&This
->ref
);
210 TRACE("(%p)->(%u)\n", This
, refCount
+ 1);
213 TRACE(" destroying IAutoComplete(%p)\n",This
);
214 HeapFree(GetProcessHeap(), 0, This
->quickComplete
);
215 HeapFree(GetProcessHeap(), 0, This
->txtbackup
);
216 if (This
->hwndListBox
)
217 DestroyWindow(This
->hwndListBox
);
219 IEnumString_Release(This
->enumstr
);
220 HeapFree(GetProcessHeap(), 0, This
);
225 /******************************************************************************
226 * IAutoComplete2_fnEnable
228 static HRESULT WINAPI
IAutoComplete2_fnEnable(
229 IAutoComplete2
* iface
,
232 IAutoCompleteImpl
*This
= (IAutoCompleteImpl
*)iface
;
236 TRACE("(%p)->(%s)\n", This
, (fEnable
)?"true":"false");
238 This
->enabled
= fEnable
;
243 /******************************************************************************
244 * IAutoComplete2_fnInit
246 static HRESULT WINAPI
IAutoComplete2_fnInit(
247 IAutoComplete2
* iface
,
250 LPCOLESTR pwzsRegKeyPath
,
251 LPCOLESTR pwszQuickComplete
)
253 IAutoCompleteImpl
*This
= (IAutoCompleteImpl
*)iface
;
255 TRACE("(%p)->(0x%08lx, %p, %s, %s)\n",
256 This
, (long)hwndEdit
, punkACL
, debugstr_w(pwzsRegKeyPath
), debugstr_w(pwszQuickComplete
));
258 if (This
->options
& ACO_SEARCH
) FIXME(" ACO_SEARCH not supported\n");
259 if (This
->options
& ACO_FILTERPREFIXES
) FIXME(" ACO_FILTERPREFIXES not supported\n");
260 if (This
->options
& ACO_USETAB
) FIXME(" ACO_USETAB not supported\n");
261 if (This
->options
& ACO_RTLREADING
) FIXME(" ACO_RTLREADING not supported\n");
263 This
->hwndEdit
= hwndEdit
;
265 if (FAILED (IUnknown_QueryInterface (punkACL
, &IID_IEnumString
, (LPVOID
*)&This
->enumstr
))) {
266 TRACE("No IEnumString interface\n");
267 return E_NOINTERFACE
;
270 This
->wpOrigEditProc
= (WNDPROC
) SetWindowLongPtrW( hwndEdit
, GWLP_WNDPROC
, (LONG_PTR
) ACEditSubclassProc
);
271 SetWindowLongPtrW( hwndEdit
, GWLP_USERDATA
, (LONG_PTR
)This
);
273 if (This
->options
& ACO_AUTOSUGGEST
)
274 create_listbox(This
);
276 if (pwzsRegKeyPath
) {
278 WCHAR result
[MAX_PATH
];
284 /* pwszRegKeyPath contains the key as well as the value, so we split */
285 key
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, (lstrlenW(pwzsRegKeyPath
)+1)*sizeof(WCHAR
));
286 strcpyW(key
, pwzsRegKeyPath
);
287 value
= strrchrW(key
, '\\');
290 /* Now value contains the value and buffer the key */
291 res
= RegOpenKeyExW(HKEY_CURRENT_USER
, key
, 0, KEY_READ
, &hKey
);
292 if (res
!= ERROR_SUCCESS
) {
293 /* if the key is not found, MSDN states we must seek in HKEY_LOCAL_MACHINE */
294 res
= RegOpenKeyExW(HKEY_LOCAL_MACHINE
, key
, 0, KEY_READ
, &hKey
);
296 if (res
== ERROR_SUCCESS
) {
297 res
= RegQueryValueW(hKey
, value
, result
, &len
);
298 if (res
== ERROR_SUCCESS
) {
299 This
->quickComplete
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, len
*sizeof(WCHAR
));
300 strcpyW(This
->quickComplete
, result
);
304 HeapFree(GetProcessHeap(), 0, key
);
307 if ((pwszQuickComplete
) && (!This
->quickComplete
)) {
308 This
->quickComplete
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, (lstrlenW(pwszQuickComplete
)+1)*sizeof(WCHAR
));
309 lstrcpyW(This
->quickComplete
, pwszQuickComplete
);
315 /**************************************************************************
316 * IAutoComplete2_fnGetOptions
318 static HRESULT WINAPI
IAutoComplete2_fnGetOptions(
319 IAutoComplete2
* iface
,
324 IAutoCompleteImpl
*This
= (IAutoCompleteImpl
*)iface
;
326 TRACE("(%p) -> (%p)\n", This
, pdwFlag
);
328 *pdwFlag
= This
->options
;
333 /**************************************************************************
334 * IAutoComplete2_fnSetOptions
336 static HRESULT WINAPI
IAutoComplete2_fnSetOptions(
337 IAutoComplete2
* iface
,
342 IAutoCompleteImpl
*This
= (IAutoCompleteImpl
*)iface
;
344 TRACE("(%p) -> (0x%x)\n", This
, dwFlag
);
346 This
->options
= dwFlag
;
348 if ((This
->options
& ACO_AUTOSUGGEST
) && This
->hwndEdit
&& !This
->hwndListBox
)
349 create_listbox(This
);
354 /**************************************************************************
355 * IAutoCompleteDropDown_fnGetDropDownStatus
357 static HRESULT WINAPI
IAutoCompleteDropDown_fnGetDropDownStatus(
358 IAutoCompleteDropDown
*iface
,
362 IAutoCompleteImpl
*This
= impl_from_IAutoCompleteDropDown(iface
);
365 TRACE("(%p) -> (%p, %p)\n", This
, pdwFlags
, ppwszString
);
367 dropped
= IsWindowVisible(This
->hwndListBox
);
370 *pdwFlags
= (dropped
? ACDD_VISIBLE
: 0);
376 sel
= SendMessageW(This
->hwndListBox
, LB_GETCURSEL
, 0, 0);
381 len
= SendMessageW(This
->hwndListBox
, LB_GETTEXTLEN
, sel
, 0);
382 *ppwszString
= CoTaskMemAlloc((len
+1)*sizeof(WCHAR
));
383 SendMessageW(This
->hwndListBox
, LB_GETTEXT
, sel
, (LPARAM
)*ppwszString
);
395 /**************************************************************************
396 * IAutoCompleteDropDown_fnResetEnumarator
398 static HRESULT WINAPI
IAutoCompleteDropDown_fnResetEnumerator(
399 IAutoCompleteDropDown
*iface
)
401 IAutoCompleteImpl
*This
= impl_from_IAutoCompleteDropDown(iface
);
403 FIXME("(%p): stub\n", This
);
410 /**************************************************************************
411 * IAutoComplete2 VTable
413 static const IAutoComplete2Vtbl acvt
=
415 IAutoComplete2_fnQueryInterface
,
416 IAutoComplete2_fnAddRef
,
417 IAutoComplete2_fnRelease
,
418 IAutoComplete2_fnInit
,
419 IAutoComplete2_fnEnable
,
421 IAutoComplete2_fnSetOptions
,
422 IAutoComplete2_fnGetOptions
,
426 static HRESULT WINAPI
IAutoCompleteDropDown_fnQueryInterface(IAutoCompleteDropDown
*iface
,
427 REFIID riid
, LPVOID
*ppvObj
)
429 IAutoCompleteImpl
*This
= impl_from_IAutoCompleteDropDown(iface
);
430 return IAutoComplete2_fnQueryInterface(_IAutoComplete2_(This
), riid
, ppvObj
);
433 static ULONG WINAPI
IAutoCompleteDropDown_fnAddRef(IAutoCompleteDropDown
*iface
)
435 IAutoCompleteImpl
*This
= impl_from_IAutoCompleteDropDown(iface
);
436 return IAutoComplete2_fnAddRef(_IAutoComplete2_(This
));
439 static ULONG WINAPI
IAutoCompleteDropDown_fnRelease(IAutoCompleteDropDown
*iface
)
441 IAutoCompleteImpl
*This
= impl_from_IAutoCompleteDropDown(iface
);
442 return IAutoComplete2_fnRelease(_IAutoComplete2_(This
));
445 /**************************************************************************
446 * IAutoCompleteDropDown VTable
448 static const IAutoCompleteDropDownVtbl acdropdownvt
=
450 IAutoCompleteDropDown_fnQueryInterface
,
451 IAutoCompleteDropDown_fnAddRef
,
452 IAutoCompleteDropDown_fnRelease
,
453 IAutoCompleteDropDown_fnGetDropDownStatus
,
454 IAutoCompleteDropDown_fnResetEnumerator
,
458 Window procedure for autocompletion
460 static LRESULT APIENTRY
ACEditSubclassProc(HWND hwnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
462 IAutoCompleteImpl
*This
= (IAutoCompleteImpl
*)GetWindowLongPtrW(hwnd
, GWLP_USERDATA
);
468 BOOL control
, filled
, displayall
= FALSE
;
469 int cpt
, height
, sel
;
471 if (!This
->enabled
) return CallWindowProcW(This
->wpOrigEditProc
, hwnd
, uMsg
, wParam
, lParam
);
475 case CB_SHOWDROPDOWN
:
476 ShowWindow(This
->hwndListBox
, SW_HIDE
);
479 if ((This
->options
& ACO_AUTOSUGGEST
) &&
480 ((HWND
)wParam
!= This
->hwndListBox
))
482 ShowWindow(This
->hwndListBox
, SW_HIDE
);
484 return CallWindowProcW(This
->wpOrigEditProc
, hwnd
, uMsg
, wParam
, lParam
);
486 GetWindowTextW( hwnd
, hwndText
, 255);
490 /* If quickComplete is set and control is pressed, replace the string */
491 control
= GetKeyState(VK_CONTROL
) & 0x8000;
492 if (control
&& This
->quickComplete
) {
493 hwndQCText
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
,
494 (lstrlenW(This
->quickComplete
)+lstrlenW(hwndText
))*sizeof(WCHAR
));
495 sel
= sprintfW(hwndQCText
, This
->quickComplete
, hwndText
);
496 SendMessageW(hwnd
, WM_SETTEXT
, 0, (LPARAM
)hwndQCText
);
497 SendMessageW(hwnd
, EM_SETSEL
, 0, sel
);
498 HeapFree(GetProcessHeap(), 0, hwndQCText
);
501 ShowWindow(This
->hwndListBox
, SW_HIDE
);
509 - if the listbox is not visible, displays it
510 with all the entries if the style ACO_UPDOWNKEYDROPSLIST
511 is present but does not select anything.
512 - if the listbox is visible, change the selection
514 if ( (This
->options
& (ACO_AUTOSUGGEST
| ACO_UPDOWNKEYDROPSLIST
))
515 && (!IsWindowVisible(This
->hwndListBox
) && (! *hwndText
)) )
517 /* We must display all the entries */
520 if (IsWindowVisible(This
->hwndListBox
)) {
523 count
= SendMessageW(This
->hwndListBox
, LB_GETCOUNT
, 0, 0);
524 /* Change the selection */
525 sel
= SendMessageW(This
->hwndListBox
, LB_GETCURSEL
, 0, 0);
527 sel
= ((sel
-1)<0)?count
-1:sel
-1;
529 sel
= ((sel
+1)>= count
)?-1:sel
+1;
530 SendMessageW(This
->hwndListBox
, LB_SETCURSEL
, sel
, 0);
535 len
= SendMessageW(This
->hwndListBox
, LB_GETTEXTLEN
, sel
, 0);
536 msg
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, (len
+1)*sizeof(WCHAR
));
537 SendMessageW(This
->hwndListBox
, LB_GETTEXT
, sel
, (LPARAM
)msg
);
538 SendMessageW(hwnd
, WM_SETTEXT
, 0, (LPARAM
)msg
);
539 SendMessageW(hwnd
, EM_SETSEL
, lstrlenW(msg
), lstrlenW(msg
));
540 HeapFree(GetProcessHeap(), 0, msg
);
542 SendMessageW(hwnd
, WM_SETTEXT
, 0, (LPARAM
)This
->txtbackup
);
543 SendMessageW(hwnd
, EM_SETSEL
, lstrlenW(This
->txtbackup
), lstrlenW(This
->txtbackup
));
551 if ((! *hwndText
) && (This
->options
& ACO_AUTOSUGGEST
)) {
552 ShowWindow(This
->hwndListBox
, SW_HIDE
);
553 return CallWindowProcW(This
->wpOrigEditProc
, hwnd
, uMsg
, wParam
, lParam
);
555 if (This
->options
& ACO_AUTOAPPEND
) {
557 SendMessageW(hwnd
, EM_GETSEL
, (WPARAM
)&b
, 0);
559 hwndText
[b
-1] = '\0';
562 SetWindowTextW(hwnd
, hwndText
);
570 SendMessageW(This
->hwndListBox
, LB_RESETCONTENT
, 0, 0);
572 HeapFree(GetProcessHeap(), 0, This
->txtbackup
);
573 This
->txtbackup
= HeapAlloc(GetProcessHeap(),
574 HEAP_ZERO_MEMORY
, (lstrlenW(hwndText
)+1)*sizeof(WCHAR
));
575 lstrcpyW(This
->txtbackup
, hwndText
);
577 /* Returns if there is no text to search and we doesn't want to display all the entries */
578 if ((!displayall
) && (! *hwndText
) )
581 IEnumString_Reset(This
->enumstr
);
585 hr
= IEnumString_Next(This
->enumstr
, 1, &strs
, &fetched
);
589 if (strstrW(strs
, hwndText
) == strs
) {
590 if (!filled
&& (This
->options
& ACO_AUTOAPPEND
)) {
591 SetWindowTextW(hwnd
, strs
);
592 SendMessageW(hwnd
, EM_SETSEL
, lstrlenW(hwndText
), lstrlenW(strs
));
593 if (!(This
->options
& ACO_AUTOSUGGEST
))
597 if (This
->options
& ACO_AUTOSUGGEST
) {
598 SendMessageW(This
->hwndListBox
, LB_ADDSTRING
, 0, (LPARAM
)strs
);
606 if (This
->options
& ACO_AUTOSUGGEST
) {
608 height
= SendMessageW(This
->hwndListBox
, LB_GETITEMHEIGHT
, 0, 0);
609 SendMessageW(This
->hwndListBox
, LB_CARETOFF
, 0, 0);
610 GetWindowRect(hwnd
, &r
);
611 SetParent(This
->hwndListBox
, HWND_DESKTOP
);
612 /* It seems that Windows XP displays 7 lines at most
613 and otherwise displays a vertical scroll bar */
614 SetWindowPos(This
->hwndListBox
, HWND_TOP
,
615 r
.left
, r
.bottom
+ 1, r
.right
- r
.left
, min(height
* 7, height
*(cpt
+1)),
618 ShowWindow(This
->hwndListBox
, SW_HIDE
);
624 return CallWindowProcW(This
->wpOrigEditProc
, hwnd
, uMsg
, wParam
, lParam
);
631 static LRESULT APIENTRY
ACLBoxSubclassProc(HWND hwnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
633 IAutoCompleteImpl
*This
= (IAutoCompleteImpl
*)GetWindowLongPtrW(hwnd
, GWLP_USERDATA
);
639 sel
= SendMessageW(hwnd
, LB_ITEMFROMPOINT
, 0, lParam
);
640 SendMessageW(hwnd
, LB_SETCURSEL
, sel
, 0);
643 sel
= SendMessageW(hwnd
, LB_GETCURSEL
, 0, 0);
646 len
= SendMessageW(This
->hwndListBox
, LB_GETTEXTLEN
, sel
, 0);
647 msg
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, (len
+1)*sizeof(WCHAR
));
648 SendMessageW(hwnd
, LB_GETTEXT
, sel
, (LPARAM
)msg
);
649 SendMessageW(This
->hwndEdit
, WM_SETTEXT
, 0, (LPARAM
)msg
);
650 SendMessageW(This
->hwndEdit
, EM_SETSEL
, 0, lstrlenW(msg
));
651 ShowWindow(hwnd
, SW_HIDE
);
652 HeapFree(GetProcessHeap(), 0, msg
);
655 return CallWindowProcW(This
->wpOrigLBoxProc
, hwnd
, uMsg
, wParam
, lParam
);