shell32: Cast-qual warnings fix.
[wine] / dlls / shell32 / autocomplete.c
1 /*
2  *      AutoComplete interfaces implementation.
3  *
4  *      Copyright 2004  Maxime Bellengé <maxime.bellenge@laposte.net>
5  *
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.
10  *
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.
15  *
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
19  */
20
21 /*
22   Implemented:
23   - ACO_AUTOAPPEND style
24   - ACO_AUTOSUGGEST style
25   - ACO_UPDOWNKEYDROPSLIST style
26
27   - Handle pwzsRegKeyPath and pwszQuickComplete in Init
28
29   TODO:
30   - implement ACO_SEARCH style
31   - implement ACO_FILTERPREFIXES style
32   - implement ACO_USETAB style
33   - implement ACO_RTLREADING style
34   
35  */
36 #include "config.h"
37
38 #include <stdarg.h>
39 #include <stdlib.h>
40 #include <string.h>
41
42 #define COBJMACROS
43
44 #include "wine/debug.h"
45 #include "windef.h"
46 #include "winbase.h"
47 #include "winreg.h"
48 #include "undocshell.h"
49 #include "shlwapi.h"
50 #include "winerror.h"
51 #include "objbase.h"
52
53 #include "pidl.h"
54 #include "shlguid.h"
55 #include "shlobj.h"
56 #include "shldisp.h"
57 #include "debughlp.h"
58
59 #include "wine/unicode.h"
60
61 WINE_DEFAULT_DEBUG_CHANNEL(shell);
62
63 typedef struct
64 {
65     const IAutoCompleteVtbl  *lpVtbl;
66     const IAutoComplete2Vtbl *lpvtblAutoComplete2;
67     LONG ref;
68     BOOL  enabled;
69     HWND hwndEdit;
70     HWND hwndListBox;
71     WNDPROC wpOrigEditProc;
72     WNDPROC wpOrigLBoxProc;
73     WCHAR *txtbackup;
74     WCHAR *quickComplete;
75     IEnumString *enumstr;
76     AUTOCOMPLETEOPTIONS options;
77 } IAutoCompleteImpl;
78
79 static const IAutoCompleteVtbl acvt;
80 static const IAutoComplete2Vtbl ac2vt;
81
82 static inline IAutoCompleteImpl *impl_from_IAutoComplete2( IAutoComplete2 *iface )
83 {
84     return (IAutoCompleteImpl *)((char*)iface - FIELD_OFFSET(IAutoCompleteImpl, lpvtblAutoComplete2));
85 }
86
87
88 /*
89   converts This to an interface pointer
90 */
91 #define _IUnknown_(This) (IUnknown*)&(This->lpVtbl)
92 #define _IAutoComplete2_(This)  (IAutoComplete2*)&(This->lpvtblAutoComplete2) 
93
94 static LRESULT APIENTRY ACEditSubclassProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
95 static LRESULT APIENTRY ACLBoxSubclassProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
96
97 /**************************************************************************
98  *  IAutoComplete_Constructor
99  */
100 HRESULT WINAPI IAutoComplete_Constructor(IUnknown * pUnkOuter, REFIID riid, LPVOID * ppv)
101 {
102     IAutoCompleteImpl *lpac;
103
104     if (pUnkOuter && !IsEqualIID (riid, &IID_IUnknown))
105         return CLASS_E_NOAGGREGATION;
106
107     lpac = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(IAutoCompleteImpl));
108     if (!lpac) 
109         return E_OUTOFMEMORY;
110
111     lpac->ref = 1;
112     lpac->lpVtbl = &acvt;
113     lpac->lpvtblAutoComplete2 = &ac2vt;
114     lpac->enabled = TRUE;
115     lpac->enumstr = NULL;
116     lpac->options = ACO_AUTOAPPEND;
117     lpac->wpOrigEditProc = NULL;
118     lpac->hwndListBox = NULL;
119     lpac->txtbackup = NULL;
120     lpac->quickComplete = NULL;
121     
122     if (!SUCCEEDED (IUnknown_QueryInterface (_IUnknown_ (lpac), riid, ppv))) {
123         IUnknown_Release (_IUnknown_ (lpac));
124         return E_NOINTERFACE;
125     }
126     
127     TRACE("-- (%p)->\n",lpac);
128
129     return S_OK;
130 }
131
132 /**************************************************************************
133  *  AutoComplete_QueryInterface
134  */
135 static HRESULT WINAPI IAutoComplete_fnQueryInterface(
136     IAutoComplete * iface,
137     REFIID riid,
138     LPVOID *ppvObj)
139 {
140     IAutoCompleteImpl *This = (IAutoCompleteImpl *)iface;
141     
142     TRACE("(%p)->(\n\tIID:\t%s,%p)\n", This, shdebugstr_guid(riid), ppvObj);
143     *ppvObj = NULL;
144
145     if(IsEqualIID(riid, &IID_IUnknown)) 
146     {
147         *ppvObj = This;
148     } 
149     else if(IsEqualIID(riid, &IID_IAutoComplete))
150     {
151         *ppvObj = (IAutoComplete*)This;
152     }
153     else if(IsEqualIID(riid, &IID_IAutoComplete2))
154     {
155         *ppvObj = _IAutoComplete2_ (This);
156     }
157
158     if (*ppvObj)
159     {
160         IAutoComplete_AddRef((IAutoComplete*)*ppvObj);
161         TRACE("-- Interface: (%p)->(%p)\n", ppvObj, *ppvObj);
162         return S_OK;
163     }
164     TRACE("-- Interface: E_NOINTERFACE\n");
165     return E_NOINTERFACE;       
166 }
167
168 /******************************************************************************
169  * IAutoComplete_fnAddRef
170  */
171 static ULONG WINAPI IAutoComplete_fnAddRef(
172         IAutoComplete * iface)
173 {
174     IAutoCompleteImpl *This = (IAutoCompleteImpl *)iface;
175     ULONG refCount = InterlockedIncrement(&This->ref);
176     
177     TRACE("(%p)->(%lu)\n", This, refCount - 1);
178
179     return refCount;
180 }
181
182 /******************************************************************************
183  * IAutoComplete_fnRelease
184  */
185 static ULONG WINAPI IAutoComplete_fnRelease(
186         IAutoComplete * iface)
187 {
188     IAutoCompleteImpl *This = (IAutoCompleteImpl *)iface;
189     ULONG refCount = InterlockedDecrement(&This->ref);
190     
191     TRACE("(%p)->(%lu)\n", This, refCount + 1);
192
193     if (!refCount) {
194         TRACE(" destroying IAutoComplete(%p)\n",This);
195         HeapFree(GetProcessHeap(), 0, This->quickComplete);
196         HeapFree(GetProcessHeap(), 0, This->txtbackup);
197         if (This->hwndListBox)
198             DestroyWindow(This->hwndListBox);
199         if (This->enumstr)
200             IEnumString_Release(This->enumstr);
201         HeapFree(GetProcessHeap(), 0, This);
202     }
203     return refCount;
204 }
205
206 /******************************************************************************
207  * IAutoComplete_fnEnable
208  */
209 static HRESULT WINAPI IAutoComplete_fnEnable(
210     IAutoComplete * iface,
211     BOOL fEnable)
212 {
213     IAutoCompleteImpl *This = (IAutoCompleteImpl *)iface;
214
215     HRESULT hr = S_OK;
216
217     TRACE("(%p)->(%s)\n", This, (fEnable)?"true":"false");
218
219     This->enabled = fEnable;
220
221     return hr;
222 }
223
224 /******************************************************************************
225  * IAutoComplete_fnInit
226  */
227 static HRESULT WINAPI IAutoComplete_fnInit(
228     IAutoComplete * iface,
229     HWND hwndEdit,
230     IUnknown *punkACL,
231     LPCOLESTR pwzsRegKeyPath,
232     LPCOLESTR pwszQuickComplete)
233 {
234     IAutoCompleteImpl *This = (IAutoCompleteImpl *)iface;
235     static const WCHAR lbName[] = {'L','i','s','t','B','o','x',0};
236
237     TRACE("(%p)->(0x%08lx, %p, %s, %s)\n", 
238           This, (long)hwndEdit, punkACL, debugstr_w(pwzsRegKeyPath), debugstr_w(pwszQuickComplete));
239
240     if (This->options & ACO_AUTOSUGGEST) TRACE(" ACO_AUTOSUGGEST\n");
241     if (This->options & ACO_AUTOAPPEND) TRACE(" ACO_AUTOAPPEND\n");
242     if (This->options & ACO_SEARCH) FIXME(" ACO_SEARCH not supported\n");
243     if (This->options & ACO_FILTERPREFIXES) FIXME(" ACO_FILTERPREFIXES not supported\n");
244     if (This->options & ACO_USETAB) FIXME(" ACO_USETAB not supported\n");
245     if (This->options & ACO_UPDOWNKEYDROPSLIST) TRACE(" ACO_UPDOWNKEYDROPSLIST\n");
246     if (This->options & ACO_RTLREADING) FIXME(" ACO_RTLREADING not supported\n");
247
248     This->hwndEdit = hwndEdit;
249
250     if (!SUCCEEDED (IUnknown_QueryInterface (punkACL, &IID_IEnumString, (LPVOID*)&This->enumstr))) {
251         TRACE("No IEnumString interface\n");
252         return  E_NOINTERFACE;
253     }
254
255     This->wpOrigEditProc = (WNDPROC) SetWindowLongPtrW( hwndEdit, GWLP_WNDPROC, (LONG_PTR) ACEditSubclassProc);
256     SetWindowLongPtrW( hwndEdit, GWLP_USERDATA, (LONG_PTR)This);
257
258     if (This->options & ACO_AUTOSUGGEST) {
259         HWND hwndParent;
260
261         hwndParent = GetParent(This->hwndEdit);
262         
263         /* FIXME : The listbox should be resizable with the mouse. WS_THICKFRAME looks ugly */
264         This->hwndListBox = CreateWindowExW(0, lbName, NULL, 
265                                             WS_BORDER | WS_CHILD | WS_VSCROLL | LBS_HASSTRINGS | LBS_NOTIFY | LBS_NOINTEGRALHEIGHT, 
266                                             CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
267                                             hwndParent, NULL, 
268                                             (HINSTANCE)GetWindowLongPtrW( hwndParent, GWLP_HINSTANCE ), NULL);
269                                             
270         if (This->hwndListBox) {
271             This->wpOrigLBoxProc = (WNDPROC) SetWindowLongPtrW( This->hwndListBox, GWLP_WNDPROC, (LONG_PTR) ACLBoxSubclassProc);
272             SetWindowLongPtrW( This->hwndListBox, GWLP_USERDATA, (LONG_PTR)This);
273         }
274     }
275
276     if (pwzsRegKeyPath) {
277         WCHAR *key;
278         WCHAR result[MAX_PATH];
279         WCHAR *value;
280         HKEY hKey = 0;
281         LONG res;
282         LONG len;
283
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, '\\');
288         *value = 0;
289         value++;
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);  
295         }
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);
301             }
302             RegCloseKey(hKey);
303         }
304         HeapFree(GetProcessHeap(), 0, key);
305     }
306
307     if ((pwszQuickComplete) && (!This->quickComplete)) {
308         This->quickComplete = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, (lstrlenW(pwszQuickComplete)+1)*sizeof(WCHAR));
309         lstrcpyW(This->quickComplete, pwszQuickComplete);
310     }
311
312     return S_OK;
313 }
314
315 /**************************************************************************
316  *  IAutoComplete_fnVTable
317  */
318 static const IAutoCompleteVtbl acvt =
319 {
320     IAutoComplete_fnQueryInterface,
321     IAutoComplete_fnAddRef,
322     IAutoComplete_fnRelease,
323     IAutoComplete_fnInit,
324     IAutoComplete_fnEnable,
325 };
326
327 /**************************************************************************
328  *  AutoComplete2_QueryInterface
329  */
330 static HRESULT WINAPI IAutoComplete2_fnQueryInterface(
331     IAutoComplete2 * iface,
332     REFIID riid,
333     LPVOID *ppvObj)
334 {
335     IAutoCompleteImpl *This = impl_from_IAutoComplete2(iface);
336
337     TRACE ("(%p)->(%s,%p)\n", This, shdebugstr_guid (riid), ppvObj);
338
339     return IAutoComplete_QueryInterface((IAutoComplete*)This, riid, ppvObj);
340 }
341
342 /******************************************************************************
343  * IAutoComplete2_fnAddRef
344  */
345 static ULONG WINAPI IAutoComplete2_fnAddRef(
346         IAutoComplete2 * iface)
347 {
348     IAutoCompleteImpl *This = impl_from_IAutoComplete2(iface);
349
350     TRACE ("(%p)->(count=%lu)\n", This, This->ref);
351
352     return IAutoComplete2_AddRef((IAutoComplete*)This);
353 }
354
355 /******************************************************************************
356  * IAutoComplete2_fnRelease
357  */
358 static ULONG WINAPI IAutoComplete2_fnRelease(
359         IAutoComplete2 * iface)
360 {
361     IAutoCompleteImpl *This = impl_from_IAutoComplete2(iface);
362
363     TRACE ("(%p)->(count=%lu)\n", This, This->ref);
364
365     return IAutoComplete_Release((IAutoComplete*)This);
366 }
367
368 /******************************************************************************
369  * IAutoComplete2_fnEnable
370  */
371 static HRESULT WINAPI IAutoComplete2_fnEnable(
372     IAutoComplete2 * iface,
373     BOOL fEnable)
374 {
375     IAutoCompleteImpl *This = impl_from_IAutoComplete2(iface);
376
377     TRACE ("(%p)->(%s)\n", This, (fEnable)?"true":"false");
378
379     return IAutoComplete_Enable((IAutoComplete*)This, fEnable);
380 }
381
382 /******************************************************************************
383  * IAutoComplete2_fnInit
384  */
385 static HRESULT WINAPI IAutoComplete2_fnInit(
386     IAutoComplete2 * iface,
387     HWND hwndEdit,
388     IUnknown *punkACL,
389     LPCOLESTR pwzsRegKeyPath,
390     LPCOLESTR pwszQuickComplete)
391 {
392     IAutoCompleteImpl *This = impl_from_IAutoComplete2(iface);
393
394     TRACE("(%p)\n", This);
395
396     return IAutoComplete_Init((IAutoComplete*)This, hwndEdit, punkACL, pwzsRegKeyPath, pwszQuickComplete);
397 }
398
399 /**************************************************************************
400  *  IAutoComplete_fnGetOptions
401  */
402 static HRESULT WINAPI IAutoComplete2_fnGetOptions(
403     IAutoComplete2 * iface,
404     DWORD *pdwFlag)
405 {
406     HRESULT hr = S_OK;
407
408     IAutoCompleteImpl *This = impl_from_IAutoComplete2(iface);
409
410     TRACE("(%p) -> (%p)\n", This, pdwFlag);
411
412     *pdwFlag = This->options;
413
414     return hr;
415 }
416
417 /**************************************************************************
418  *  IAutoComplete_fnSetOptions
419  */
420 static HRESULT WINAPI IAutoComplete2_fnSetOptions(
421     IAutoComplete2 * iface,
422     DWORD dwFlag)
423 {
424     HRESULT hr = S_OK;
425
426     IAutoCompleteImpl *This = impl_from_IAutoComplete2(iface);
427
428     TRACE("(%p) -> (0x%lx)\n", This, dwFlag);
429
430     This->options = dwFlag;
431
432     return hr;
433 }
434
435 /**************************************************************************
436  *  IAutoComplete2_fnVTable
437  */
438 static const IAutoComplete2Vtbl ac2vt =
439 {
440     IAutoComplete2_fnQueryInterface,
441     IAutoComplete2_fnAddRef,
442     IAutoComplete2_fnRelease,
443     IAutoComplete2_fnInit,
444     IAutoComplete2_fnEnable,
445     /* IAutoComplete2 */
446     IAutoComplete2_fnSetOptions,
447     IAutoComplete2_fnGetOptions,
448 };
449
450 /*
451   Window procedure for autocompletion
452  */
453 static LRESULT APIENTRY ACEditSubclassProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
454 {
455     IAutoCompleteImpl *This = (IAutoCompleteImpl *)GetWindowLongPtrW(hwnd, GWLP_USERDATA);
456     LPOLESTR strs;
457     HRESULT hr;
458     WCHAR hwndText[255];
459     WCHAR *hwndQCText;
460     RECT r;
461     BOOL control, filled, displayall = FALSE;
462     int cpt, height, sel;
463
464     if (!This->enabled) return CallWindowProcW(This->wpOrigEditProc, hwnd, uMsg, wParam, lParam);
465
466     switch (uMsg)
467     {
468         case CB_SHOWDROPDOWN:
469             ShowWindow(This->hwndListBox, SW_HIDE);
470             break;
471         case WM_KILLFOCUS:
472             if ((This->options && ACO_AUTOSUGGEST) && 
473                 ((HWND)wParam != This->hwndListBox))
474             {
475                 ShowWindow(This->hwndListBox, SW_HIDE);
476             }
477             break;
478         case WM_KEYUP:
479             
480             GetWindowTextW( hwnd, (LPWSTR)hwndText, 255);
481       
482             switch(wParam) {
483                 case VK_RETURN:
484                     /* If quickComplete is set and control is pressed, replace the string */
485                     control = GetKeyState(VK_CONTROL) & 0x8000;             
486                     if (control && This->quickComplete) {
487                         hwndQCText = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, 
488                                                        (lstrlenW(This->quickComplete)+lstrlenW(hwndText))*sizeof(WCHAR));
489                         sel = sprintfW(hwndQCText, This->quickComplete, hwndText);
490                         SendMessageW(hwnd, WM_SETTEXT, 0, (LPARAM)hwndQCText);
491                         SendMessageW(hwnd, EM_SETSEL, 0, sel);
492                         HeapFree(GetProcessHeap(), 0, hwndQCText);
493                     }
494
495                     ShowWindow(This->hwndListBox, SW_HIDE);
496                     return 0;
497                 case VK_LEFT:
498                 case VK_RIGHT:
499                     return 0;
500                 case VK_UP:           
501                 case VK_DOWN:
502                     /* Two cases here : 
503                        - if the listbox is not visible, displays it 
504                        with all the entries if the style ACO_UPDOWNKEYDROPSLIST
505                        is present but does not select anything.
506                        - if the listbox is visible, change the selection
507                     */
508                     if ( (This->options & (ACO_AUTOSUGGEST | ACO_UPDOWNKEYDROPSLIST)) 
509                          && (!IsWindowVisible(This->hwndListBox) && (! *hwndText)) )
510                     {
511                          /* We must dispays all the entries */
512                          displayall = TRUE;
513                     } else {
514                         if (IsWindowVisible(This->hwndListBox)) {
515                             int count;
516
517                             count = SendMessageW(This->hwndListBox, LB_GETCOUNT, 0, 0);
518                             /* Change the selection */
519                             sel = SendMessageW(This->hwndListBox, LB_GETCURSEL, 0, 0);
520                             if (wParam == VK_UP)
521                                 sel = ((sel-1)<0)?count-1:sel-1;
522                             else
523                                 sel = ((sel+1)>= count)?-1:sel+1;
524                             SendMessageW(This->hwndListBox, LB_SETCURSEL, sel, 0);
525                             if (sel != -1) {
526                                 WCHAR *msg;
527                                 int len;
528                                 
529                                 len = SendMessageW(This->hwndListBox, LB_GETTEXTLEN, sel, (LPARAM)NULL);
530                                 msg = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, (len+1)*sizeof(WCHAR));
531                                 SendMessageW(This->hwndListBox, LB_GETTEXT, sel, (LPARAM)msg);
532                                 SendMessageW(hwnd, WM_SETTEXT, 0, (LPARAM)msg);
533                                 SendMessageW(hwnd, EM_SETSEL, lstrlenW(msg), lstrlenW(msg));
534                                 HeapFree(GetProcessHeap(), 0, msg);
535                             } else {
536                                 SendMessageW(hwnd, WM_SETTEXT, 0, (LPARAM)This->txtbackup);
537                                 SendMessageW(hwnd, EM_SETSEL, lstrlenW(This->txtbackup), lstrlenW(This->txtbackup));
538                             }                   
539                         }               
540                         return 0;
541                     }
542                     break;
543                 case VK_BACK:
544                 case VK_DELETE:
545                     if ((! *hwndText) && (This->options & ACO_AUTOSUGGEST)) {
546                         ShowWindow(This->hwndListBox, SW_HIDE);
547                         return CallWindowProcW(This->wpOrigEditProc, hwnd, uMsg, wParam, lParam);
548                     }
549                     if (This->options & ACO_AUTOAPPEND) {
550                         DWORD b;
551                         SendMessageW(hwnd, EM_GETSEL, (WPARAM)&b, (LPARAM)NULL);
552                         if (b>1) {
553                             hwndText[b-1] = '\0';
554                         } else {
555                             hwndText[0] = '\0';
556                             SetWindowTextW(hwnd, hwndText); 
557                         }                       
558                     }
559                     break;
560                 default:                    
561                     ;
562             }
563       
564             SendMessageW(This->hwndListBox, LB_RESETCONTENT, 0, 0);
565
566             HeapFree(GetProcessHeap(), 0, This->txtbackup);
567             This->txtbackup = HeapAlloc(GetProcessHeap(),
568                                                  HEAP_ZERO_MEMORY, (lstrlenW(hwndText)+1)*sizeof(WCHAR));                                                             
569             lstrcpyW(This->txtbackup, hwndText);
570
571             /* Returns if there is no text to search and we doesn't want to display all the entries */
572             if ((!displayall) && (! *hwndText) )
573                 break;
574             
575             IEnumString_Reset(This->enumstr);
576             filled = FALSE;
577             for(cpt = 0;;) {
578                 hr = IEnumString_Next(This->enumstr, 1, &strs, NULL);
579                 if (hr != S_OK)
580                     break;
581
582                 if ((LPWSTR)strstrW(strs, hwndText) == strs) {
583                     
584                     if (This->options & ACO_AUTOAPPEND) {
585                         SetWindowTextW(hwnd, strs);
586                         SendMessageW(hwnd, EM_SETSEL, lstrlenW(hwndText), lstrlenW(strs));
587                         break;
588                     }           
589
590                     if (This->options & ACO_AUTOSUGGEST) {
591                         SendMessageW(This->hwndListBox, LB_ADDSTRING, 0, (LPARAM)strs);
592                         filled = TRUE;
593                         cpt++;
594                     }
595                 }               
596             }
597             
598             if (This->options & ACO_AUTOSUGGEST) {
599                 if (filled) {
600                     height = SendMessageW(This->hwndListBox, LB_GETITEMHEIGHT, 0, 0);
601                     SendMessageW(This->hwndListBox, LB_CARETOFF, 0, 0);
602                     GetWindowRect(hwnd, &r);
603                     SetParent(This->hwndListBox, HWND_DESKTOP);
604                     /* It seems that Windows XP displays 7 lines at most 
605                        and otherwise displays a vertical scroll bar */
606                     SetWindowPos(This->hwndListBox, HWND_TOP, 
607                                  r.left, r.bottom + 1, r.right - r.left, min(height * 7, height*(cpt+1)), 
608                                  SWP_SHOWWINDOW );
609                 } else {
610                     ShowWindow(This->hwndListBox, SW_HIDE);
611                 }
612             }
613             
614             break; 
615         default:
616             return CallWindowProcW(This->wpOrigEditProc, hwnd, uMsg, wParam, lParam);
617             
618     }
619
620     return 0;
621 }
622
623 static LRESULT APIENTRY ACLBoxSubclassProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
624 {
625     IAutoCompleteImpl *This = (IAutoCompleteImpl *)GetWindowLongPtrW(hwnd, GWLP_USERDATA);
626     WCHAR *msg;
627     int sel = -1, len;
628
629     switch (uMsg) {
630         case WM_MOUSEMOVE:
631             sel = SendMessageW(hwnd, LB_ITEMFROMPOINT, 0, lParam);
632             SendMessageW(hwnd, LB_SETCURSEL, (WPARAM)sel, (LPARAM)0);
633             break;
634         case WM_LBUTTONDOWN:
635             len = SendMessageW(This->hwndListBox, LB_GETTEXTLEN, sel, (LPARAM)NULL);
636             msg = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, (len+1)*sizeof(WCHAR));
637             sel = (INT)SendMessageW(hwnd, LB_GETCURSEL, 0, 0);
638             SendMessageW(hwnd, LB_GETTEXT, sel, (LPARAM)msg);
639             SendMessageW(This->hwndEdit, WM_SETTEXT, 0, (LPARAM)msg);
640             SendMessageW(This->hwndEdit, EM_SETSEL, 0, lstrlenW(msg));
641             ShowWindow(hwnd, SW_HIDE);
642             HeapFree(GetProcessHeap(), 0, msg);
643             break;
644         default:
645             return CallWindowProcW(This->wpOrigLBoxProc, hwnd, uMsg, wParam, lParam);
646     }
647     return 0;
648 }