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 WINE_DEFAULT_DEBUG_CHANNEL(shell
);
42 static const WCHAR autocomplete_propertyW
[] = {'W','i','n','e',' ','A','u','t','o',
43 'c','o','m','p','l','e','t','e',' ',
44 'c','o','n','t','r','o','l',0};
46 /**************************************************************************
47 * IAutoComplete_Constructor
49 CAutoComplete::CAutoComplete()
53 options
= ACO_AUTOAPPEND
;
54 wpOrigEditProc
= NULL
;
59 wpOrigLBoxProc
= NULL
;
62 /**************************************************************************
63 * IAutoComplete_Destructor
65 CAutoComplete::~CAutoComplete()
67 TRACE(" destroying IAutoComplete(%p)\n", this);
68 HeapFree(GetProcessHeap(), 0, quickComplete
);
69 HeapFree(GetProcessHeap(), 0, txtbackup
);
70 RemovePropW(hwndEdit
, autocomplete_propertyW
);
71 SetWindowLongPtrW(hwndEdit
, GWLP_WNDPROC
, (LONG_PTR
)wpOrigEditProc
);
73 DestroyWindow(hwndListBox
);
76 /******************************************************************************
77 * IAutoComplete_fnEnable
79 HRESULT WINAPI
CAutoComplete::Enable(BOOL fEnable
)
83 TRACE("(%p)->(%s)\n", this, (fEnable
) ? "true" : "false");
90 /******************************************************************************
91 * IAutoComplete_fnInit
93 HRESULT WINAPI
CAutoComplete::Init(HWND hwndEdit
, IUnknown
*punkACL
, LPCOLESTR pwzsRegKeyPath
, LPCOLESTR pwszQuickComplete
)
95 static const WCHAR lbName
[] = {'L','i','s','t','B','o','x',0};
97 TRACE("(%p)->(0x%08lx, %p, %s, %s)\n",
98 this, hwndEdit
, punkACL
, debugstr_w(pwzsRegKeyPath
), debugstr_w(pwszQuickComplete
));
100 if (options
& ACO_AUTOSUGGEST
)
101 TRACE(" ACO_AUTOSUGGEST\n");
102 if (options
& ACO_AUTOAPPEND
)
103 TRACE(" ACO_AUTOAPPEND\n");
104 if (options
& ACO_SEARCH
)
105 FIXME(" ACO_SEARCH not supported\n");
106 if (options
& ACO_FILTERPREFIXES
)
107 FIXME(" ACO_FILTERPREFIXES not supported\n");
108 if (options
& ACO_USETAB
)
109 FIXME(" ACO_USETAB not supported\n");
110 if (options
& ACO_UPDOWNKEYDROPSLIST
)
111 TRACE(" ACO_UPDOWNKEYDROPSLIST\n");
112 if (options
& ACO_RTLREADING
)
113 FIXME(" ACO_RTLREADING not supported\n");
115 if (!hwndEdit
|| !punkACL
)
118 if (this->initialized
)
120 WARN("Autocompletion object is already initialized\n");
121 /* This->hwndEdit is set to NULL when the edit window is destroyed. */
122 return this->hwndEdit
? E_FAIL
: E_UNEXPECTED
;
125 if (!SUCCEEDED(punkACL
->QueryInterface(IID_PPV_ARG(IEnumString
,&enumstr
))))
127 TRACE("No IEnumString interface\n");
128 return E_NOINTERFACE
;
131 this->hwndEdit
= hwndEdit
;
132 this->initialized
= TRUE
;
133 wpOrigEditProc
= (WNDPROC
)SetWindowLongPtrW(hwndEdit
, GWLP_WNDPROC
, (LONG_PTR
) ACEditSubclassProc
);
134 // SetWindowLongPtrW(hwndEdit, GWLP_USERDATA, (LONG_PTR)this);
135 SetPropW( hwndEdit
, autocomplete_propertyW
, (HANDLE
)this );
137 if (options
& ACO_AUTOSUGGEST
)
141 hwndParent
= GetParent(hwndEdit
);
143 /* FIXME : The listbox should be resizable with the mouse. WS_THICKFRAME looks ugly */
144 hwndListBox
= CreateWindowExW(0, lbName
, NULL
,
145 WS_BORDER
| WS_CHILD
| WS_VSCROLL
| LBS_HASSTRINGS
| LBS_NOTIFY
| LBS_NOINTEGRALHEIGHT
,
146 CW_USEDEFAULT
, CW_USEDEFAULT
, CW_USEDEFAULT
, CW_USEDEFAULT
,
148 (HINSTANCE
)GetWindowLongPtrW(hwndParent
, GWLP_HINSTANCE
), NULL
);
152 wpOrigLBoxProc
= (WNDPROC
)SetWindowLongPtrW(hwndListBox
, GWLP_WNDPROC
, (LONG_PTR
)ACLBoxSubclassProc
);
153 SetWindowLongPtrW(hwndListBox
, GWLP_USERDATA
, (LONG_PTR
)this);
160 WCHAR result
[MAX_PATH
];
166 /* pwszRegKeyPath contains the key as well as the value, so we split */
167 key
= (WCHAR
*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, (wcslen(pwzsRegKeyPath
) + 1) * sizeof(WCHAR
));
171 wcscpy(key
, pwzsRegKeyPath
);
172 value
= const_cast<WCHAR
*>(strrchrW(key
, '\\'));
178 /* Now value contains the value and buffer the key */
179 res
= RegOpenKeyExW(HKEY_CURRENT_USER
, key
, 0, KEY_READ
, &hKey
);
181 if (res
!= ERROR_SUCCESS
)
183 /* if the key is not found, MSDN states we must seek in HKEY_LOCAL_MACHINE */
184 res
= RegOpenKeyExW(HKEY_LOCAL_MACHINE
, key
, 0, KEY_READ
, &hKey
);
187 if (res
== ERROR_SUCCESS
)
189 res
= RegQueryValueW(hKey
, value
, result
, &len
);
190 if (res
== ERROR_SUCCESS
)
192 quickComplete
= (WCHAR
*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, len
* sizeof(WCHAR
));
193 wcscpy(quickComplete
, result
);
199 HeapFree(GetProcessHeap(), 0, key
);
203 TRACE("HeapAlloc Failed when trying to alloca %d bytes\n", (wcslen(pwzsRegKeyPath
) + 1) * sizeof(WCHAR
));
208 if ((pwszQuickComplete
) && (!quickComplete
))
210 quickComplete
= (WCHAR
*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, (wcslen(pwszQuickComplete
) + 1) * sizeof(WCHAR
));
214 wcscpy(quickComplete
, pwszQuickComplete
);
218 TRACE("HeapAlloc Failed when trying to alloca %d bytes\n", (wcslen(pwszQuickComplete
) + 1) * sizeof(WCHAR
));
226 /**************************************************************************
227 * IAutoComplete_fnGetOptions
229 HRESULT WINAPI
CAutoComplete::GetOptions(DWORD
*pdwFlag
)
233 TRACE("(%p) -> (%p)\n", this, pdwFlag
);
240 /**************************************************************************
241 * IAutoComplete_fnSetOptions
243 HRESULT WINAPI
CAutoComplete::SetOptions(DWORD dwFlag
)
247 TRACE("(%p) -> (0x%x)\n", this, dwFlag
);
249 options
= (AUTOCOMPLETEOPTIONS
)dwFlag
;
255 Window procedure for autocompletion
257 LRESULT APIENTRY
CAutoComplete::ACEditSubclassProc(HWND hwnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
259 CAutoComplete
*pThis
= static_cast<CAutoComplete
*>(GetPropW(hwnd
, autocomplete_propertyW
));//GetWindowLongPtrW(hwnd, GWLP_USERDATA);
265 BOOL control
, filled
, displayall
= FALSE
;
266 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
, NULL
);
438 if ((LPWSTR
)strstrW(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
);