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