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 static const WCHAR autocomplete_propertyW
[] = {'W','i','n','e',' ','A','u','t','o',
41 'c','o','m','p','l','e','t','e',' ',
42 'c','o','n','t','r','o','l',0};
44 /**************************************************************************
45 * IAutoComplete_Constructor
47 CAutoComplete::CAutoComplete()
51 options
= ACO_AUTOAPPEND
;
52 wpOrigEditProc
= NULL
;
57 wpOrigLBoxProc
= NULL
;
60 /**************************************************************************
61 * IAutoComplete_Destructor
63 CAutoComplete::~CAutoComplete()
65 TRACE(" destroying IAutoComplete(%p)\n", this);
66 HeapFree(GetProcessHeap(), 0, quickComplete
);
67 HeapFree(GetProcessHeap(), 0, txtbackup
);
68 RemovePropW(hwndEdit
, autocomplete_propertyW
);
69 SetWindowLongPtrW(hwndEdit
, GWLP_WNDPROC
, (LONG_PTR
)wpOrigEditProc
);
71 DestroyWindow(hwndListBox
);
74 /******************************************************************************
75 * IAutoComplete_fnEnable
77 HRESULT WINAPI
CAutoComplete::Enable(BOOL fEnable
)
81 TRACE("(%p)->(%s)\n", this, (fEnable
) ? "true" : "false");
88 /******************************************************************************
89 * IAutoComplete_fnInit
91 HRESULT WINAPI
CAutoComplete::Init(HWND hwndEdit
, IUnknown
*punkACL
, LPCOLESTR pwzsRegKeyPath
, LPCOLESTR pwszQuickComplete
)
93 static const WCHAR lbName
[] = {'L','i','s','t','B','o','x',0};
95 TRACE("(%p)->(0x%08lx, %p, %s, %s)\n",
96 this, hwndEdit
, punkACL
, debugstr_w(pwzsRegKeyPath
), debugstr_w(pwszQuickComplete
));
98 if (options
& ACO_AUTOSUGGEST
)
99 TRACE(" ACO_AUTOSUGGEST\n");
100 if (options
& ACO_AUTOAPPEND
)
101 TRACE(" ACO_AUTOAPPEND\n");
102 if (options
& ACO_SEARCH
)
103 FIXME(" ACO_SEARCH not supported\n");
104 if (options
& ACO_FILTERPREFIXES
)
105 FIXME(" ACO_FILTERPREFIXES not supported\n");
106 if (options
& ACO_USETAB
)
107 FIXME(" ACO_USETAB not supported\n");
108 if (options
& ACO_UPDOWNKEYDROPSLIST
)
109 TRACE(" ACO_UPDOWNKEYDROPSLIST\n");
110 if (options
& ACO_RTLREADING
)
111 FIXME(" ACO_RTLREADING not supported\n");
113 if (!hwndEdit
|| !punkACL
)
116 if (this->initialized
)
118 WARN("Autocompletion object is already initialized\n");
119 /* This->hwndEdit is set to NULL when the edit window is destroyed. */
120 return this->hwndEdit
? E_FAIL
: E_UNEXPECTED
;
123 if (!SUCCEEDED(punkACL
->QueryInterface(IID_PPV_ARG(IEnumString
,&enumstr
))))
125 TRACE("No IEnumString interface\n");
126 return E_NOINTERFACE
;
129 this->hwndEdit
= hwndEdit
;
130 this->initialized
= TRUE
;
131 wpOrigEditProc
= (WNDPROC
)SetWindowLongPtrW(hwndEdit
, GWLP_WNDPROC
, (LONG_PTR
) ACEditSubclassProc
);
132 // SetWindowLongPtrW(hwndEdit, GWLP_USERDATA, (LONG_PTR)this);
133 SetPropW( hwndEdit
, autocomplete_propertyW
, (HANDLE
)this );
135 if (options
& ACO_AUTOSUGGEST
)
139 hwndParent
= GetParent(hwndEdit
);
141 /* FIXME : The listbox should be resizable with the mouse. WS_THICKFRAME looks ugly */
142 hwndListBox
= CreateWindowExW(0, lbName
, NULL
,
143 WS_BORDER
| WS_CHILD
| WS_VSCROLL
| LBS_HASSTRINGS
| LBS_NOTIFY
| LBS_NOINTEGRALHEIGHT
,
144 CW_USEDEFAULT
, CW_USEDEFAULT
, CW_USEDEFAULT
, CW_USEDEFAULT
,
146 (HINSTANCE
)GetWindowLongPtrW(hwndParent
, GWLP_HINSTANCE
), NULL
);
150 wpOrigLBoxProc
= (WNDPROC
)SetWindowLongPtrW(hwndListBox
, GWLP_WNDPROC
, (LONG_PTR
)ACLBoxSubclassProc
);
151 SetWindowLongPtrW(hwndListBox
, GWLP_USERDATA
, (LONG_PTR
)this);
158 WCHAR result
[MAX_PATH
];
164 /* pwszRegKeyPath contains the key as well as the value, so we split */
165 key
= (WCHAR
*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, (wcslen(pwzsRegKeyPath
) + 1) * sizeof(WCHAR
));
169 wcscpy(key
, pwzsRegKeyPath
);
170 value
= const_cast<WCHAR
*>(wcsrchr(key
, '\\'));
176 /* Now value contains the value and buffer the key */
177 res
= RegOpenKeyExW(HKEY_CURRENT_USER
, key
, 0, KEY_READ
, &hKey
);
179 if (res
!= ERROR_SUCCESS
)
181 /* if the key is not found, MSDN states we must seek in HKEY_LOCAL_MACHINE */
182 res
= RegOpenKeyExW(HKEY_LOCAL_MACHINE
, key
, 0, KEY_READ
, &hKey
);
185 if (res
== ERROR_SUCCESS
)
187 res
= RegQueryValueW(hKey
, value
, result
, &len
);
188 if (res
== ERROR_SUCCESS
)
190 quickComplete
= (WCHAR
*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, len
* sizeof(WCHAR
));
191 wcscpy(quickComplete
, result
);
197 HeapFree(GetProcessHeap(), 0, key
);
201 TRACE("HeapAlloc Failed when trying to alloca %d bytes\n", (wcslen(pwzsRegKeyPath
) + 1) * sizeof(WCHAR
));
206 if ((pwszQuickComplete
) && (!quickComplete
))
208 quickComplete
= (WCHAR
*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, (wcslen(pwszQuickComplete
) + 1) * sizeof(WCHAR
));
212 wcscpy(quickComplete
, pwszQuickComplete
);
216 TRACE("HeapAlloc Failed when trying to alloca %d bytes\n", (wcslen(pwszQuickComplete
) + 1) * sizeof(WCHAR
));
224 /**************************************************************************
225 * IAutoComplete_fnGetOptions
227 HRESULT WINAPI
CAutoComplete::GetOptions(DWORD
*pdwFlag
)
231 TRACE("(%p) -> (%p)\n", this, pdwFlag
);
238 /**************************************************************************
239 * IAutoComplete_fnSetOptions
241 HRESULT WINAPI
CAutoComplete::SetOptions(DWORD dwFlag
)
245 TRACE("(%p) -> (0x%x)\n", this, dwFlag
);
247 options
= (AUTOCOMPLETEOPTIONS
)dwFlag
;
253 Window procedure for autocompletion
255 LRESULT APIENTRY
CAutoComplete::ACEditSubclassProc(HWND hwnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
257 CAutoComplete
*pThis
= static_cast<CAutoComplete
*>(GetPropW(hwnd
, autocomplete_propertyW
));//GetWindowLongPtrW(hwnd, GWLP_USERDATA);
263 BOOL control
, filled
, displayall
= FALSE
;
264 int cpt
, height
, sel
;
268 return CallWindowProcW(pThis
->wpOrigEditProc
, hwnd
, uMsg
, wParam
, lParam
);
273 case CB_SHOWDROPDOWN
:
275 ShowWindow(pThis
->hwndListBox
, SW_HIDE
);
280 if ((pThis
->options
& ACO_AUTOSUGGEST
) && ((HWND
)wParam
!= pThis
->hwndListBox
))
282 ShowWindow(pThis
->hwndListBox
, SW_HIDE
);
284 return CallWindowProcW(pThis
->wpOrigEditProc
, hwnd
, uMsg
, wParam
, lParam
);
289 GetWindowTextW(hwnd
, (LPWSTR
)hwndText
, 255);
295 /* If quickComplete is set and control is pressed, replace the string */
296 control
= GetKeyState(VK_CONTROL
) & 0x8000;
297 if (control
&& pThis
->quickComplete
)
299 hwndQCText
= (WCHAR
*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
,
300 (wcslen(pThis
->quickComplete
)+wcslen(hwndText
))*sizeof(WCHAR
));
301 sel
= swprintf(hwndQCText
, pThis
->quickComplete
, hwndText
);
302 SendMessageW(hwnd
, WM_SETTEXT
, 0, (LPARAM
)hwndQCText
);
303 SendMessageW(hwnd
, EM_SETSEL
, 0, sel
);
304 HeapFree(GetProcessHeap(), 0, hwndQCText
);
307 ShowWindow(pThis
->hwndListBox
, SW_HIDE
);
321 - if the listbox is not visible, displays it
322 with all the entries if the style ACO_UPDOWNKEYDROPSLIST
323 is present but does not select anything.
324 - if the listbox is visible, change the selection
326 if ( (pThis
->options
& (ACO_AUTOSUGGEST
| ACO_UPDOWNKEYDROPSLIST
))
327 && (!IsWindowVisible(pThis
->hwndListBox
) && (! *hwndText
)) )
329 /* We must display all the entries */
334 if (IsWindowVisible(pThis
->hwndListBox
))
338 count
= SendMessageW(pThis
->hwndListBox
, LB_GETCOUNT
, 0, 0);
339 /* Change the selection */
340 sel
= SendMessageW(pThis
->hwndListBox
, LB_GETCURSEL
, 0, 0);
342 sel
= ((sel
-1)<0)?count
-1:sel
-1;
344 sel
= ((sel
+1)>= count
)?-1:sel
+1;
346 SendMessageW(pThis
->hwndListBox
, LB_SETCURSEL
, sel
, 0);
353 len
= SendMessageW(pThis
->hwndListBox
, LB_GETTEXTLEN
, sel
, (LPARAM
)NULL
);
354 msg
= (WCHAR
*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, (len
+ 1) * sizeof(WCHAR
));
358 SendMessageW(pThis
->hwndListBox
, LB_GETTEXT
, sel
, (LPARAM
)msg
);
359 SendMessageW(hwnd
, WM_SETTEXT
, 0, (LPARAM
)msg
);
360 SendMessageW(hwnd
, EM_SETSEL
, wcslen(msg
), wcslen(msg
));
362 HeapFree(GetProcessHeap(), 0, msg
);
366 TRACE("HeapAlloc failed to allocate %d bytes\n", (len
+ 1) * sizeof(WCHAR
));
371 SendMessageW(hwnd
, WM_SETTEXT
, 0, (LPARAM
)pThis
->txtbackup
);
372 SendMessageW(hwnd
, EM_SETSEL
, wcslen(pThis
->txtbackup
), wcslen(pThis
->txtbackup
));
382 if ((! *hwndText
) && (pThis
->options
& ACO_AUTOSUGGEST
))
384 ShowWindow(pThis
->hwndListBox
, SW_HIDE
);
385 return CallWindowProcW(pThis
->wpOrigEditProc
, hwnd
, uMsg
, wParam
, lParam
);
388 if (pThis
->options
& ACO_AUTOAPPEND
)
391 SendMessageW(hwnd
, EM_GETSEL
, (WPARAM
)&b
, (LPARAM
)NULL
);
394 hwndText
[b
-1] = '\0';
399 SetWindowTextW(hwnd
, hwndText
);
408 SendMessageW(pThis
->hwndListBox
, LB_RESETCONTENT
, 0, 0);
410 HeapFree(GetProcessHeap(), 0, pThis
->txtbackup
);
412 pThis
->txtbackup
= (WCHAR
*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, (wcslen(hwndText
)+1)*sizeof(WCHAR
));
414 if (pThis
->txtbackup
)
416 wcscpy(pThis
->txtbackup
, hwndText
);
420 TRACE("HeapAlloc failed to allocate %d bytes\n", (wcslen(hwndText
)+1)*sizeof(WCHAR
));
423 /* Returns if there is no text to search and we doesn't want to display all the entries */
424 if ((!displayall
) && (! *hwndText
) )
427 pThis
->enumstr
->Reset();
432 hr
= pThis
->enumstr
->Next(1, &strs
, NULL
);
436 if ((LPWSTR
)wcsstr(strs
, hwndText
) == strs
)
439 if (pThis
->options
& ACO_AUTOAPPEND
)
441 SetWindowTextW(hwnd
, strs
);
442 SendMessageW(hwnd
, EM_SETSEL
, wcslen(hwndText
), wcslen(strs
));
446 if (pThis
->options
& ACO_AUTOSUGGEST
)
448 SendMessageW(pThis
->hwndListBox
, LB_ADDSTRING
, 0, (LPARAM
)strs
);
455 if (pThis
->options
& ACO_AUTOSUGGEST
)
459 height
= SendMessageW(pThis
->hwndListBox
, LB_GETITEMHEIGHT
, 0, 0);
460 SendMessageW(pThis
->hwndListBox
, LB_CARETOFF
, 0, 0);
461 GetWindowRect(hwnd
, &r
);
462 SetParent(pThis
->hwndListBox
, HWND_DESKTOP
);
463 /* It seems that Windows XP displays 7 lines at most
464 and otherwise displays a vertical scroll bar */
465 SetWindowPos(pThis
->hwndListBox
, HWND_TOP
,
466 r
.left
, r
.bottom
+ 1, r
.right
- r
.left
, min(height
* 7, height
* (cpt
+ 1)),
471 ShowWindow(pThis
->hwndListBox
, SW_HIDE
);
479 return CallWindowProcW(pThis
->wpOrigEditProc
, hwnd
, uMsg
, wParam
, lParam
);
487 LRESULT APIENTRY
CAutoComplete::ACLBoxSubclassProc(HWND hwnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
489 CAutoComplete
*pThis
= reinterpret_cast<CAutoComplete
*>(GetWindowLongPtrW(hwnd
, GWLP_USERDATA
));
497 sel
= SendMessageW(hwnd
, LB_ITEMFROMPOINT
, 0, lParam
);
498 SendMessageW(hwnd
, LB_SETCURSEL
, (WPARAM
)sel
, (LPARAM
)0);
503 sel
= SendMessageW(hwnd
, LB_GETCURSEL
, 0, 0);
508 len
= SendMessageW(pThis
->hwndListBox
, LB_GETTEXTLEN
, sel
, 0);
509 msg
= (WCHAR
*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, (len
+ 1) * sizeof(WCHAR
));
513 SendMessageW(hwnd
, LB_GETTEXT
, sel
, (LPARAM
)msg
);
514 SendMessageW(pThis
->hwndEdit
, WM_SETTEXT
, 0, (LPARAM
)msg
);
515 SendMessageW(pThis
->hwndEdit
, EM_SETSEL
, 0, wcslen(msg
));
516 ShowWindow(hwnd
, SW_HIDE
);
518 HeapFree(GetProcessHeap(), 0, msg
);
522 TRACE("HeapAlloc failed to allocate %d bytes\n", (len
+ 1) * sizeof(WCHAR
));
528 return CallWindowProcW(pThis
->wpOrigLBoxProc
, hwnd
, uMsg
, wParam
, lParam
);