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