[MMIXER] Fix additional data size initialization for different audio formats (#6753)
[reactos.git] / 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 Add drag and drop of icon in edit box
30 Handle change notifies to update appropriately
31 */
32
33 CAddressEditBox::CAddressEditBox() :
34 fCombobox(NULL, this, 1),
35 fEditWindow(NULL, this, 1),
36 fSite(NULL),
37 pidlLastParsed(NULL)
38 {
39 }
40
41 CAddressEditBox::~CAddressEditBox()
42 {
43 if (pidlLastParsed)
44 ILFree(pidlLastParsed);
45 }
46
47 HRESULT STDMETHODCALLTYPE CAddressEditBox::SetOwner(IUnknown *pOwner)
48 {
49 if (!pOwner)
50 {
51 CComPtr<IBrowserService> browserService;
52 HRESULT hResult = IUnknown_QueryService(fSite, SID_SShellBrowser, IID_PPV_ARG(IBrowserService, &browserService));
53 if (SUCCEEDED(hResult))
54 AtlUnadvise(browserService, DIID_DWebBrowserEvents, fAdviseCookie);
55 fSite = NULL;
56 }
57 // connect to browser connection point
58 return 0;
59 }
60
61 HRESULT STDMETHODCALLTYPE CAddressEditBox::FileSysChange(long param8, long paramC)
62 {
63 return E_NOTIMPL;
64 }
65
66 HRESULT STDMETHODCALLTYPE CAddressEditBox::Refresh(long param8)
67 {
68 return E_NOTIMPL;
69 }
70
71 HRESULT STDMETHODCALLTYPE CAddressEditBox::Init(HWND comboboxEx, HWND editControl, long param14, IUnknown *param18)
72 {
73 CComPtr<IBrowserService> browserService;
74
75 fCombobox.SubclassWindow(comboboxEx);
76 fEditWindow.SubclassWindow(editControl);
77 fSite = param18;
78 hComboBoxEx = comboboxEx;
79
80 SHAutoComplete(fEditWindow.m_hWnd, SHACF_FILESYSTEM | SHACF_URLALL | SHACF_USETAB);
81
82 // take advice to watch events
83 HRESULT hResult = IUnknown_QueryService(param18, SID_SShellBrowser, IID_PPV_ARG(IBrowserService, &browserService));
84 if (SUCCEEDED(hResult))
85 {
86 hResult = AtlAdvise(browserService, static_cast<IDispatch *>(this), DIID_DWebBrowserEvents, &fAdviseCookie);
87 }
88
89 return hResult;
90 }
91
92 HRESULT STDMETHODCALLTYPE CAddressEditBox::SetCurrentDir(long paramC)
93 {
94 return E_NOTIMPL;
95 }
96
97 HRESULT STDMETHODCALLTYPE CAddressEditBox::ParseNow(long paramC)
98 {
99 ULONG eaten;
100 ULONG attributes;
101 HRESULT hr;
102 HWND topLevelWindow;
103 PIDLIST_ABSOLUTE pidlCurrent= NULL;
104 PIDLIST_RELATIVE pidlRelative = NULL;
105 CComPtr<IShellFolder> psfCurrent;
106
107 CComPtr<IBrowserService> pbs;
108 hr = IUnknown_QueryService(fSite, SID_SShellBrowser, IID_PPV_ARG(IBrowserService, &pbs));
109 if (FAILED_UNEXPECTEDLY(hr))
110 return hr;
111
112 hr = IUnknown_GetWindow(pbs, &topLevelWindow);
113 if (FAILED_UNEXPECTEDLY(hr))
114 return hr;
115
116 /* Get the path to browse and expand it if needed */
117 LPWSTR input;
118 int inputLength = fCombobox.GetWindowTextLength() + 2;
119
120 input = new WCHAR[inputLength];
121 fCombobox.GetWindowText(input, inputLength);
122
123 LPWSTR address;
124 int addressLength = ExpandEnvironmentStrings(input, NULL, 0);
125
126 if (addressLength <= 0)
127 {
128 address = input;
129 }
130 else
131 {
132 addressLength += 2;
133 address = new WCHAR[addressLength];
134 if (!ExpandEnvironmentStrings(input, address, addressLength))
135 {
136 delete[] address;
137 address = input;
138 }
139 }
140
141 /* Try to parse a relative path and if it fails, try to browse an absolute path */
142 CComPtr<IShellFolder> psfDesktop;
143 hr = SHGetDesktopFolder(&psfDesktop);
144 if (FAILED_UNEXPECTEDLY(hr))
145 goto cleanup;
146
147 hr = pbs->GetPidl(&pidlCurrent);
148 if (FAILED_UNEXPECTEDLY(hr))
149 goto parseabsolute;
150
151 hr = psfDesktop->BindToObject(pidlCurrent, NULL, IID_PPV_ARG(IShellFolder, &psfCurrent));
152 if (FAILED_UNEXPECTEDLY(hr))
153 goto parseabsolute;
154
155 hr = psfCurrent->ParseDisplayName(topLevelWindow, NULL, address, &eaten, &pidlRelative, &attributes);
156 if (SUCCEEDED(hr))
157 {
158 pidlLastParsed = ILCombine(pidlCurrent, pidlRelative);
159 ILFree(pidlRelative);
160 goto cleanup;
161 }
162
163 parseabsolute:
164 /* We couldn't parse a relative path, attempt to parse an absolute path */
165 hr = psfDesktop->ParseDisplayName(topLevelWindow, NULL, address, &eaten, &pidlLastParsed, &attributes);
166
167 cleanup:
168 if (pidlCurrent)
169 ILFree(pidlCurrent);
170 if (address != input)
171 delete [] address;
172 delete [] input;
173
174 return hr;
175 }
176
177 HRESULT STDMETHODCALLTYPE CAddressEditBox::ShowFileNotFoundError(HRESULT hRet)
178 {
179 CComHeapPtr<WCHAR> input;
180 int inputLength = fCombobox.GetWindowTextLength() + 2;
181
182 input.Allocate(inputLength);
183 fCombobox.GetWindowText(input, inputLength);
184
185 ShellMessageBoxW(_AtlBaseModule.GetResourceInstance(), fCombobox.m_hWnd, MAKEINTRESOURCEW(IDS_PARSE_ADDR_ERR_TEXT), MAKEINTRESOURCEW(IDS_PARSE_ADDR_ERR_TITLE), MB_OK | MB_ICONERROR, input.m_pData);
186
187 return hRet;
188 }
189
190 HRESULT STDMETHODCALLTYPE CAddressEditBox::Execute(long paramC)
191 {
192 HRESULT hr;
193
194 /*
195 * Parse the path is it wasn't parsed
196 */
197 if (!pidlLastParsed)
198 hr = ParseNow(0);
199
200 /*
201 * If the destination path doesn't exist then display an error message
202 */
203 if (hr == HRESULT_FROM_WIN32(ERROR_INVALID_DRIVE) || hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND))
204 return ShowFileNotFoundError(hr);
205
206 if (!pidlLastParsed)
207 return E_FAIL;
208
209 /*
210 * Get the IShellBrowser and IBrowserService interfaces of the shell browser
211 */
212 CComPtr<IShellBrowser> pisb;
213 hr = IUnknown_QueryService(fSite, SID_SShellBrowser, IID_PPV_ARG(IShellBrowser, &pisb));
214 if (FAILED(hr))
215 return hr;
216
217 CComPtr<IBrowserService> pbs;
218 pisb->QueryInterface(IID_PPV_ARG(IBrowserService, &pbs));
219 if (FAILED(hr))
220 return hr;
221
222 /*
223 * Get the current pidl of the shellbrowser and check if it is the same with the parsed one
224 */
225 PIDLIST_ABSOLUTE pidl;
226 hr = pbs->GetPidl(&pidl);
227 if (FAILED(hr))
228 return hr;
229
230 CComPtr<IShellFolder> psf;
231 hr = SHGetDesktopFolder(&psf);
232 if (FAILED(hr))
233 return hr;
234
235 hr = psf->CompareIDs(0, pidl, pidlLastParsed);
236
237 SHFree(pidl);
238 if (hr == 0)
239 return S_OK;
240
241 /*
242 * Attempt to browse to the parsed pidl
243 */
244 hr = pisb->BrowseObject(pidlLastParsed, 0);
245 if (SUCCEEDED(hr))
246 return hr;
247
248 /*
249 * Browsing to the pidl failed so it's not a folder. So invoke its defaule command.
250 */
251 HWND topLevelWindow;
252 hr = IUnknown_GetWindow(pisb, &topLevelWindow);
253 if (FAILED(hr))
254 return hr;
255
256 LPCITEMIDLIST pidlChild;
257 CComPtr<IShellFolder> sf;
258 hr = SHBindToParent(pidlLastParsed, IID_PPV_ARG(IShellFolder, &sf), &pidlChild);
259 if (FAILED(hr))
260 return hr;
261
262 hr = SHInvokeDefaultCommand(topLevelWindow, sf, pidlChild);
263 if (FAILED(hr))
264 return hr;
265
266 return hr;
267 }
268
269 HRESULT STDMETHODCALLTYPE CAddressEditBox::Save(long paramC)
270 {
271 return E_NOTIMPL;
272 }
273
274 HRESULT STDMETHODCALLTYPE CAddressEditBox::OnWinEvent(
275 HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *theResult)
276 {
277 LPNMHDR hdr;
278
279 if (theResult)
280 *theResult = 0;
281
282 switch (uMsg)
283 {
284 case WM_COMMAND:
285 {
286 if (HIWORD(wParam) == CBN_SELCHANGE)
287 {
288 UINT selectedIndex = SendMessageW((HWND)lParam, CB_GETCURSEL, 0, 0);
289 pidlLastParsed = ILClone((LPITEMIDLIST)SendMessageW((HWND)lParam, CB_GETITEMDATA, selectedIndex, 0));
290 Execute(0);
291 }
292 break;
293 }
294 case WM_NOTIFY:
295 {
296 hdr = (LPNMHDR) lParam;
297 if (hdr->code == CBEN_ENDEDIT)
298 {
299 NMCBEENDEDITW *endEdit = (NMCBEENDEDITW*) lParam;
300 if (endEdit->iWhy == CBENF_RETURN)
301 {
302 Execute(0);
303 }
304 else if (endEdit->iWhy == CBENF_ESCAPE)
305 {
306 /* Reset the contents of the combo box */
307 }
308 }
309 else if (hdr->code == CBEN_DELETEITEM)
310 {
311 PNMCOMBOBOXEX pCBEx = (PNMCOMBOBOXEX) lParam;
312 LPITEMIDLIST itemPidl = (LPITEMIDLIST)pCBEx->ceItem.lParam;
313 if (itemPidl)
314 {
315 ILFree(itemPidl);
316 }
317 }
318 break;
319 }
320 }
321 return S_OK;
322 }
323
324 HRESULT STDMETHODCALLTYPE CAddressEditBox::IsWindowOwner(HWND hWnd)
325 {
326 if (fCombobox.m_hWnd == hWnd)
327 return S_OK;
328 if (fEditWindow.m_hWnd == hWnd)
329 return S_OK;
330 return S_FALSE;
331 }
332
333 HRESULT STDMETHODCALLTYPE CAddressEditBox::QueryStatus(
334 const GUID *pguidCmdGroup, ULONG cCmds, OLECMD prgCmds[ ], OLECMDTEXT *pCmdText)
335 {
336 return E_NOTIMPL;
337 }
338
339 HRESULT STDMETHODCALLTYPE CAddressEditBox::Exec(const GUID *pguidCmdGroup, DWORD nCmdID,
340 DWORD nCmdexecopt, VARIANT *pvaIn, VARIANT *pvaOut)
341 {
342 return E_NOTIMPL;
343 }
344
345 HRESULT STDMETHODCALLTYPE CAddressEditBox::GetTypeInfoCount(UINT *pctinfo)
346 {
347 return E_NOTIMPL;
348 }
349
350 HRESULT STDMETHODCALLTYPE CAddressEditBox::GetTypeInfo(UINT iTInfo, LCID lcid, ITypeInfo **ppTInfo)
351 {
352 return E_NOTIMPL;
353 }
354
355 HRESULT STDMETHODCALLTYPE CAddressEditBox::GetIDsOfNames(
356 REFIID riid, LPOLESTR *rgszNames, UINT cNames, LCID lcid, DISPID *rgDispId)
357 {
358 return E_NOTIMPL;
359 }
360
361 HRESULT STDMETHODCALLTYPE CAddressEditBox::Invoke(DISPID dispIdMember, REFIID riid, LCID lcid,
362 WORD wFlags, DISPPARAMS *pDispParams, VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr)
363 {
364 CComPtr<IBrowserService> isb;
365 CComPtr<IShellFolder> sf;
366 HRESULT hr;
367 PIDLIST_ABSOLUTE absolutePIDL;
368 LPCITEMIDLIST pidlChild;
369 STRRET ret;
370 WCHAR buf[4096];
371
372 if (pDispParams == NULL)
373 return E_INVALIDARG;
374
375 switch (dispIdMember)
376 {
377 case DISPID_NAVIGATECOMPLETE2:
378 case DISPID_DOCUMENTCOMPLETE:
379
380 if (pidlLastParsed)
381 ILFree(pidlLastParsed);
382 pidlLastParsed = NULL;
383
384 /* Get the current pidl of the browser */
385 hr = IUnknown_QueryService(fSite, SID_STopLevelBrowser, IID_PPV_ARG(IBrowserService, &isb));
386 if (FAILED_UNEXPECTEDLY(hr))
387 return hr;
388
389 hr = isb->GetPidl(&absolutePIDL);
390 if (FAILED_UNEXPECTEDLY(hr))
391 return hr;
392
393 if (!absolutePIDL)
394 {
395 ERR("Got no PIDL, investigate me!\n");
396 return S_OK;
397 }
398
399 /* Fill the combobox */
400 PopulateComboBox(absolutePIDL);
401
402 /* Find the current item in the combobox and select it */
403 CComPtr<IShellFolder> psfDesktop;
404 hr = SHGetDesktopFolder(&psfDesktop);
405 if (FAILED_UNEXPECTEDLY(hr))
406 return S_OK;
407
408 hr = psfDesktop->GetDisplayNameOf(absolutePIDL, SHGDN_FORADDRESSBAR, &ret);
409 if (FAILED_UNEXPECTEDLY(hr))
410 return S_OK;
411
412 hr = StrRetToBufW(&ret, absolutePIDL, buf, 4095);
413 if (FAILED_UNEXPECTEDLY(hr))
414 return S_OK;
415
416 int index = SendMessageW(hComboBoxEx, CB_FINDSTRINGEXACT, 0, (LPARAM)buf);
417 if (index != -1)
418 SendMessageW(hComboBoxEx, CB_SETCURSEL, index, 0);
419
420 /* Add the item that will be visible when the combobox is not expanded */
421 hr = SHBindToParent(absolutePIDL, IID_PPV_ARG(IShellFolder, &sf), &pidlChild);
422 if (FAILED_UNEXPECTEDLY(hr))
423 return hr;
424
425 hr = sf->GetDisplayNameOf(pidlChild, SHGDN_FORADDRESSBAR | SHGDN_FORPARSING, &ret);
426 if (FAILED_UNEXPECTEDLY(hr))
427 return hr;
428
429 hr = StrRetToBufW(&ret, pidlChild, buf, 4095);
430 if (FAILED_UNEXPECTEDLY(hr))
431 return hr;
432
433 INT indexClosed, indexOpen;
434 indexClosed = SHMapPIDLToSystemImageListIndex(sf, pidlChild, &indexOpen);
435
436 COMBOBOXEXITEMW item = {0};
437 item.mask = CBEIF_IMAGE | CBEIF_SELECTEDIMAGE | CBEIF_TEXT | CBEIF_LPARAM;
438 item.iItem = -1;
439 item.iImage = indexClosed;
440 item.iSelectedImage = indexOpen;
441 item.pszText = buf;
442 item.lParam = reinterpret_cast<LPARAM>(absolutePIDL);
443 fCombobox.SendMessage(CBEM_SETITEM, 0, reinterpret_cast<LPARAM>(&item));
444 }
445 return S_OK;
446 }
447
448 HRESULT STDMETHODCALLTYPE CAddressEditBox::GetClassID(CLSID *pClassID)
449 {
450 if (pClassID == NULL)
451 return E_POINTER;
452 *pClassID = CLSID_AddressEditBox;
453 return S_OK;
454 }
455
456 HRESULT STDMETHODCALLTYPE CAddressEditBox::IsDirty()
457 {
458 return E_NOTIMPL;
459 }
460
461 HRESULT STDMETHODCALLTYPE CAddressEditBox::Load(IStream *pStm)
462 {
463 return E_NOTIMPL;
464 }
465
466 HRESULT STDMETHODCALLTYPE CAddressEditBox::Save(IStream *pStm, BOOL fClearDirty)
467 {
468 return E_NOTIMPL;
469 }
470
471 HRESULT STDMETHODCALLTYPE CAddressEditBox::GetSizeMax(ULARGE_INTEGER *pcbSize)
472 {
473 return E_NOTIMPL;
474 }
475
476 void CAddressEditBox::PopulateComboBox(LPITEMIDLIST pidlCurrent)
477 {
478 HRESULT hr;
479 LPITEMIDLIST pidl;
480 int indent = 0;
481 int index;
482
483 index = SendMessageW(hComboBoxEx, CB_GETCOUNT, 0, 0);
484 for (int i = 0; i < index; i++)
485 SendMessageW(hComboBoxEx, CBEM_DELETEITEM, i, 0);
486 SendMessageW(hComboBoxEx, CB_RESETCONTENT, 0, 0);
487
488 /* Calculate the indent level. No need to clone the pidl */
489 pidl = pidlCurrent;
490 do
491 {
492 if(!pidl->mkid.cb)
493 break;
494 pidl = ILGetNext(pidl);
495 indent++;
496 } while (pidl);
497 index = indent;
498
499 /* Add every id from the pidl in the combo box */
500 pidl = ILClone(pidlCurrent);
501 do
502 {
503 AddComboBoxItem(pidl, 0, index);
504 ILRemoveLastID(pidl);
505 index--;
506 } while (index >= 0);
507 ILFree(pidl);
508
509 /* Add the items of the desktop */
510 FillOneLevel(0, 1, indent);
511
512 /* Add the items of My Computer */
513 hr = SHGetSpecialFolderLocation(0, CSIDL_DRIVES, &pidl);
514 if (FAILED_UNEXPECTEDLY(hr))
515 return;
516
517 for(LPITEMIDLIST i = GetItemData(0); i; i = GetItemData(index))
518 {
519 if (ILIsEqual(i, pidl))
520 {
521 FillOneLevel(index, 2, indent);
522 break;
523 }
524 index++;
525 }
526 ILFree(pidl);
527 }
528
529 void CAddressEditBox::AddComboBoxItem(LPITEMIDLIST pidl, int index, int indent)
530 {
531 HRESULT hr;
532 WCHAR buf[4096];
533
534 LPCITEMIDLIST pidlChild;
535 CComPtr<IShellFolder> sf;
536 hr = SHBindToParent(pidl, IID_PPV_ARG(IShellFolder, &sf), &pidlChild);
537 if (FAILED_UNEXPECTEDLY(hr))
538 return;
539
540 STRRET strret;
541 hr = sf->GetDisplayNameOf(pidlChild, SHGDN_FORADDRESSBAR, &strret);
542 if (FAILED_UNEXPECTEDLY(hr))
543 return;
544
545 hr = StrRetToBufW(&strret, pidlChild, buf, 4095);
546 if (FAILED_UNEXPECTEDLY(hr))
547 return;
548
549 COMBOBOXEXITEMW item = {0};
550 item.mask = CBEIF_LPARAM | CBEIF_INDENT | CBEIF_SELECTEDIMAGE | CBEIF_IMAGE | CBEIF_TEXT;
551 item.iImage = SHMapPIDLToSystemImageListIndex(sf, pidlChild, &item.iSelectedImage);
552 item.pszText = buf;
553 item.lParam = (LPARAM)(ILClone(pidl));
554 item.iIndent = indent;
555 item.iItem = index;
556 SendMessageW(hComboBoxEx, CBEM_INSERTITEMW, 0, (LPARAM)&item);
557 }
558
559 void CAddressEditBox::FillOneLevel(int index, int levelIndent, int indent)
560 {
561 HRESULT hr;
562 ULONG numObj;
563 int count;
564 LPITEMIDLIST pidl, pidl2, pidl3, pidl4;
565
566 count = index + 1;
567 pidl = GetItemData(index);
568 pidl2 = GetItemData(count);
569 if(pidl)
570 {
571 CComPtr<IShellFolder> psfDesktop;
572 CComPtr<IShellFolder> psfItem;
573
574 hr = SHGetDesktopFolder(&psfDesktop);
575 if (FAILED_UNEXPECTEDLY(hr))
576 return;
577
578 if (!pidl->mkid.cb)
579 {
580 psfItem = psfDesktop;
581 }
582 else
583 {
584 hr = psfDesktop->BindToObject(pidl, NULL, IID_PPV_ARG(IShellFolder, &psfItem));
585 if (FAILED_UNEXPECTEDLY(hr))
586 return;
587 }
588
589 CComPtr<IEnumIDList> pEnumIDList;
590 hr = psfItem->EnumObjects(0, SHCONTF_FOLDERS | SHCONTF_INCLUDEHIDDEN, &pEnumIDList);
591 if (FAILED_UNEXPECTEDLY(hr))
592 return;
593
594 do
595 {
596 hr = pEnumIDList->Next(1, &pidl3, &numObj);
597 if(hr != S_OK || !numObj)
598 break;
599
600 pidl4 = ILCombine(pidl, pidl3);
601 if (pidl2 && ILIsEqual(pidl4, pidl2))
602 count += (indent - levelIndent);
603 else
604 AddComboBoxItem(pidl4, count, levelIndent);
605 count++;
606 ILFree(pidl3);
607 ILFree(pidl4);
608 } while (true);
609 }
610 }
611
612 LPITEMIDLIST CAddressEditBox::GetItemData(int index)
613 {
614 COMBOBOXEXITEMW item;
615
616 memset(&item, 0, sizeof(COMBOBOXEXITEMW));
617 item.mask = CBEIF_LPARAM;
618 item.iItem = index;
619 SendMessageW(hComboBoxEx, CBEM_GETITEMW, 0, (LPARAM)&item);
620 return (LPITEMIDLIST)item.lParam;
621 }