9633d881507b21798c8ca05021ce4454d18c6d40
[reactos.git] / reactos / dll / win32 / browseui / addresseditbox.cpp
1 /*
2 * ReactOS Explorer
3 *
4 * Copyright 2009 Andrew Hill <ash77 at domain reactos.org>
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 This class handles the combo box of the address band.
23 */
24
25 #include "precomp.h"
26
27 /*
28 TODO:
29 Handle listbox dropdown message and fill contents
30 Add drag and drop of icon in edit box
31 Handle change notifies to update appropriately
32 Fix so selection in combo listbox navigates
33 */
34
35 CAddressEditBox::CAddressEditBox() :
36 fCombobox(NULL, this, 1),
37 fEditWindow(NULL, this, 1),
38 fSite(NULL)
39 {
40 }
41
42 CAddressEditBox::~CAddressEditBox()
43 {
44 }
45
46 HRESULT STDMETHODCALLTYPE CAddressEditBox::SetOwner(IUnknown *pOwner)
47 {
48 if (!pOwner)
49 {
50 CComPtr<IBrowserService> browserService;
51 HRESULT hResult = IUnknown_QueryService(fSite, SID_SShellBrowser, IID_PPV_ARG(IBrowserService, &browserService));
52 if (SUCCEEDED(hResult))
53 AtlUnadvise(browserService, DIID_DWebBrowserEvents, fAdviseCookie);
54 fSite = NULL;
55 }
56 // connect to browser connection point
57 return 0;
58 }
59
60 HRESULT STDMETHODCALLTYPE CAddressEditBox::FileSysChange(long param8, long paramC)
61 {
62 return E_NOTIMPL;
63 }
64
65 HRESULT STDMETHODCALLTYPE CAddressEditBox::Refresh(long param8)
66 {
67 return E_NOTIMPL;
68 }
69
70 HRESULT STDMETHODCALLTYPE CAddressEditBox::Init(HWND comboboxEx, HWND editControl, long param14, IUnknown *param18)
71 {
72 CComPtr<IBrowserService> browserService;
73
74 fCombobox.SubclassWindow(comboboxEx);
75 fEditWindow.SubclassWindow(editControl);
76 fSite = param18;
77
78 SHAutoComplete(fEditWindow.m_hWnd, SHACF_FILESYSTEM | SHACF_URLALL | SHACF_USETAB);
79
80 // take advice to watch events
81 HRESULT hResult = IUnknown_QueryService(param18, SID_SShellBrowser, IID_PPV_ARG(IBrowserService, &browserService));
82 if (SUCCEEDED(hResult))
83 {
84 hResult = AtlAdvise(browserService, static_cast<IDispatch *>(this), DIID_DWebBrowserEvents, &fAdviseCookie);
85 }
86
87 return hResult;
88 }
89
90 HRESULT STDMETHODCALLTYPE CAddressEditBox::SetCurrentDir(long paramC)
91 {
92 return E_NOTIMPL;
93 }
94
95 HRESULT STDMETHODCALLTYPE CAddressEditBox::ParseNow(long paramC)
96 {
97 ULONG eaten;
98 ULONG attributes;
99 HRESULT hr;
100 HWND topLevelWindow;
101 PIDLIST_ABSOLUTE pidlCurrent= NULL;
102 PIDLIST_RELATIVE pidlRelative = NULL;
103 CComPtr<IShellFolder> psfCurrent;
104
105 CComPtr<IBrowserService> pbs;
106 hr = IUnknown_QueryService(fSite, SID_SShellBrowser, IID_PPV_ARG(IBrowserService, &pbs));
107 if (FAILED_UNEXPECTEDLY(hr))
108 return hr;
109
110 hr = IUnknown_GetWindow(pbs, &topLevelWindow);
111 if (FAILED_UNEXPECTEDLY(hr))
112 return hr;
113
114 /* Get the path to browse and expand it if needed */
115 LPWSTR input;
116 int inputLength = fCombobox.GetWindowTextLength() + 2;
117
118 input = new WCHAR[inputLength];
119 fCombobox.GetWindowText(input, inputLength);
120
121 LPWSTR address;
122 int addressLength = ExpandEnvironmentStrings(input, NULL, 0);
123
124 if (addressLength <= 0)
125 {
126 address = input;
127 }
128 else
129 {
130 addressLength += 2;
131 address = new WCHAR[addressLength];
132 if (!ExpandEnvironmentStrings(input, address, addressLength))
133 {
134 delete[] address;
135 address = input;
136 }
137 }
138
139 /* Try to parse a relative path and if it fails, try to browse an absolute path */
140 CComPtr<IShellFolder> psfDesktop;
141 hr = SHGetDesktopFolder(&psfDesktop);
142 if (FAILED_UNEXPECTEDLY(hr))
143 goto cleanup;
144
145 hr = pbs->GetPidl(&pidlCurrent);
146 if (FAILED_UNEXPECTEDLY(hr))
147 goto cleanup;
148
149 hr = psfDesktop->BindToObject(pidlCurrent, NULL, IID_PPV_ARG(IShellFolder, &psfCurrent));
150 if (FAILED_UNEXPECTEDLY(hr))
151 goto cleanup;
152
153 hr = psfCurrent->ParseDisplayName(topLevelWindow, NULL, address, &eaten, &pidlRelative, &attributes);
154 if (SUCCEEDED(hr))
155 {
156 pidlLastParsed = ILCombine(pidlCurrent, pidlRelative);
157 ILFree(pidlRelative);
158 goto cleanup;
159 }
160
161 /* We couldn't parse a relative path, attempt to parse an absolute path */
162 hr = psfDesktop->ParseDisplayName(topLevelWindow, NULL, address, &eaten, &pidlLastParsed, &attributes);
163
164 cleanup:
165 if (pidlCurrent)
166 ILFree(pidlCurrent);
167 if (address != input)
168 delete [] address;
169 delete [] input;
170
171 return hr;
172 }
173
174 HRESULT STDMETHODCALLTYPE CAddressEditBox::Execute(long paramC)
175 {
176 HRESULT hr;
177
178 /*
179 * Parse the path is it wasn't parsed
180 */
181 if (!pidlLastParsed)
182 ParseNow(0);
183
184 if (!pidlLastParsed)
185 return E_FAIL;
186
187 /*
188 * Get the IShellBrowser and IBrowserService interfaces of the shell browser
189 */
190 CComPtr<IShellBrowser> pisb;
191 hr = IUnknown_QueryService(fSite, SID_SShellBrowser, IID_PPV_ARG(IShellBrowser, &pisb));
192 if (FAILED(hr))
193 return hr;
194
195 CComPtr<IBrowserService> pbs;
196 pisb->QueryInterface(IID_PPV_ARG(IBrowserService, &pbs));
197 if (FAILED(hr))
198 return hr;
199
200 /*
201 * Get the current pidl of the shellbrowser and check if it is the same with the parsed one
202 */
203 PIDLIST_ABSOLUTE pidl;
204 hr = pbs->GetPidl(&pidl);
205 if (FAILED(hr))
206 return hr;
207
208 CComPtr<IShellFolder> psf;
209 hr = SHGetDesktopFolder(&psf);
210 if (FAILED(hr))
211 return hr;
212
213 hr = psf->CompareIDs(0, pidl, pidlLastParsed);
214
215 SHFree(pidl);
216 if (hr == 0)
217 return S_OK;
218
219 /*
220 * Attempt to browse to the parsed pidl
221 */
222 hr = pisb->BrowseObject(pidlLastParsed, 0);
223 if (SUCCEEDED(hr))
224 return hr;
225
226 /*
227 * Browsing to the pidl failed so it's not a folder. So invoke its defaule command.
228 */
229 HWND topLevelWindow;
230 hr = IUnknown_GetWindow(pisb, &topLevelWindow);
231 if (FAILED(hr))
232 return hr;
233
234 LPCITEMIDLIST pidlChild;
235 CComPtr<IShellFolder> sf;
236 hr = SHBindToParent(pidlLastParsed, IID_PPV_ARG(IShellFolder, &sf), &pidlChild);
237 if (FAILED(hr))
238 return hr;
239
240 hr = SHInvokeDefaultCommand(topLevelWindow, sf, pidlChild);
241 if (FAILED(hr))
242 return hr;
243
244 return hr;
245 }
246
247 HRESULT STDMETHODCALLTYPE CAddressEditBox::Save(long paramC)
248 {
249 return E_NOTIMPL;
250 }
251
252 HRESULT STDMETHODCALLTYPE CAddressEditBox::OnWinEvent(
253 HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *theResult)
254 {
255 LPNMHDR hdr;
256
257 *theResult = 0;
258
259 switch (uMsg)
260 {
261 case WM_NOTIFY:
262 hdr = (LPNMHDR) lParam;
263 if (hdr->code == CBEN_ENDEDIT)
264 {
265 NMCBEENDEDITW *endEdit = (NMCBEENDEDITW*) lParam;
266 if (endEdit->iWhy == CBENF_RETURN)
267 {
268 Execute(0);
269 }
270 else if (endEdit->iWhy == CBENF_ESCAPE)
271 {
272 /* Reset the contents of the combo box */
273 }
274 }
275 break;
276 }
277 return S_OK;
278 }
279
280 HRESULT STDMETHODCALLTYPE CAddressEditBox::IsWindowOwner(HWND hWnd)
281 {
282 if (fCombobox.m_hWnd == hWnd)
283 return S_OK;
284 if (fEditWindow.m_hWnd == hWnd)
285 return S_OK;
286 return S_FALSE;
287 }
288
289 HRESULT STDMETHODCALLTYPE CAddressEditBox::QueryStatus(
290 const GUID *pguidCmdGroup, ULONG cCmds, OLECMD prgCmds[ ], OLECMDTEXT *pCmdText)
291 {
292 return E_NOTIMPL;
293 }
294
295 HRESULT STDMETHODCALLTYPE CAddressEditBox::Exec(const GUID *pguidCmdGroup, DWORD nCmdID,
296 DWORD nCmdexecopt, VARIANT *pvaIn, VARIANT *pvaOut)
297 {
298 return E_NOTIMPL;
299 }
300
301 HRESULT STDMETHODCALLTYPE CAddressEditBox::GetTypeInfoCount(UINT *pctinfo)
302 {
303 return E_NOTIMPL;
304 }
305
306 HRESULT STDMETHODCALLTYPE CAddressEditBox::GetTypeInfo(UINT iTInfo, LCID lcid, ITypeInfo **ppTInfo)
307 {
308 return E_NOTIMPL;
309 }
310
311 HRESULT STDMETHODCALLTYPE CAddressEditBox::GetIDsOfNames(
312 REFIID riid, LPOLESTR *rgszNames, UINT cNames, LCID lcid, DISPID *rgDispId)
313 {
314 return E_NOTIMPL;
315 }
316
317 HRESULT STDMETHODCALLTYPE CAddressEditBox::Invoke(DISPID dispIdMember, REFIID riid, LCID lcid,
318 WORD wFlags, DISPPARAMS *pDispParams, VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr)
319 {
320 CComPtr<IBrowserService> isb;
321 CComPtr<IShellFolder> sf;
322 HRESULT hr;
323 INT indexClosed, indexOpen, itemExists, oldIndex;
324 DWORD result;
325 COMBOBOXEXITEMW item;
326 PIDLIST_ABSOLUTE absolutePIDL;
327 LPCITEMIDLIST pidlChild;
328 LPITEMIDLIST pidlPrevious;
329 STRRET ret;
330 WCHAR buf[4096];
331
332 if (pDispParams == NULL)
333 return E_INVALIDARG;
334
335 switch (dispIdMember)
336 {
337 case DISPID_NAVIGATECOMPLETE2:
338 case DISPID_DOCUMENTCOMPLETE:
339 pidlLastParsed = NULL;
340
341 oldIndex = fCombobox.SendMessage(CB_GETCURSEL, 0, 0);
342
343 itemExists = FALSE;
344 pidlPrevious = NULL;
345
346 ZeroMemory(&item, sizeof(item));
347 item.mask = CBEIF_LPARAM;
348 item.iItem = 0;
349 if (fCombobox.SendMessage(CBEM_GETITEM, 0, reinterpret_cast<LPARAM>(&item)))
350 {
351 pidlPrevious = reinterpret_cast<LPITEMIDLIST>(item.lParam);
352 if (pidlPrevious)
353 itemExists = TRUE;
354 }
355
356 hr = IUnknown_QueryService(fSite, SID_STopLevelBrowser, IID_PPV_ARG(IBrowserService, &isb));
357 if (FAILED_UNEXPECTEDLY(hr))
358 return hr;
359
360 hr = isb->GetPidl(&absolutePIDL);
361 if (FAILED_UNEXPECTEDLY(hr))
362 return hr;
363
364 hr = SHBindToParent(absolutePIDL, IID_PPV_ARG(IShellFolder, &sf), &pidlChild);
365 if (FAILED_UNEXPECTEDLY(hr))
366 return hr;
367
368 hr = sf->GetDisplayNameOf(pidlChild, SHGDN_FORADDRESSBAR | SHGDN_FORPARSING, &ret);
369 if (FAILED_UNEXPECTEDLY(hr))
370 return hr;
371
372 hr = StrRetToBufW(&ret, pidlChild, buf, 4095);
373 if (FAILED_UNEXPECTEDLY(hr))
374 return hr;
375
376 indexClosed = SHMapPIDLToSystemImageListIndex(sf, pidlChild, &indexOpen);
377
378 item.mask = CBEIF_IMAGE | CBEIF_SELECTEDIMAGE | CBEIF_TEXT | CBEIF_LPARAM;
379 item.iItem = 0;
380 item.iImage = indexClosed;
381 item.iSelectedImage = indexOpen;
382 item.pszText = buf;
383 item.lParam = reinterpret_cast<LPARAM>(absolutePIDL);
384
385 if (itemExists)
386 {
387 result = fCombobox.SendMessage(CBEM_SETITEM, 0, reinterpret_cast<LPARAM>(&item));
388 oldIndex = 0;
389
390 if (result)
391 {
392 ILFree(pidlPrevious);
393 }
394 }
395 else
396 {
397 oldIndex = fCombobox.SendMessage(CBEM_INSERTITEM, 0, reinterpret_cast<LPARAM>(&item));
398
399 if (oldIndex < 0)
400 DbgPrint("ERROR %d\n", GetLastError());
401 }
402
403 fCombobox.SendMessage(CB_SETCURSEL, -1, 0);
404 fCombobox.SendMessage(CB_SETCURSEL, oldIndex, 0);
405
406 //fAddressEditBox->SetCurrentDir(index);
407 }
408 return S_OK;
409 }
410
411 HRESULT STDMETHODCALLTYPE CAddressEditBox::GetClassID(CLSID *pClassID)
412 {
413 if (pClassID == NULL)
414 return E_POINTER;
415 *pClassID = CLSID_AddressEditBox;
416 return S_OK;
417 }
418
419 HRESULT STDMETHODCALLTYPE CAddressEditBox::IsDirty()
420 {
421 return E_NOTIMPL;
422 }
423
424 HRESULT STDMETHODCALLTYPE CAddressEditBox::Load(IStream *pStm)
425 {
426 return E_NOTIMPL;
427 }
428
429 HRESULT STDMETHODCALLTYPE CAddressEditBox::Save(IStream *pStm, BOOL fClearDirty)
430 {
431 return E_NOTIMPL;
432 }
433
434 HRESULT STDMETHODCALLTYPE CAddressEditBox::GetSizeMax(ULARGE_INTEGER *pcbSize)
435 {
436 return E_NOTIMPL;
437 }