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