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