4 * Copyright 2009 Andrew Hill <ash77 at domain reactos.org>
5 * Copyright 2023 Katayama Hirofumi MZ <katayama.hirofumi.mz@gmail.com>
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 This class handles the combo box of the address band.
30 Add drag and drop of icon in edit box
31 Handle change notifies to update appropriately
34 CAddressEditBox::CAddressEditBox() :
35 fCombobox(WC_COMBOBOXEXW
, this),
36 fEditWindow(WC_EDITW
, this),
42 CAddressEditBox::~CAddressEditBox()
45 ILFree(pidlLastParsed
);
48 HRESULT STDMETHODCALLTYPE
CAddressEditBox::SetOwner(IUnknown
*pOwner
)
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
);
58 // connect to browser connection point
62 HRESULT STDMETHODCALLTYPE
CAddressEditBox::FileSysChange(long param8
, long paramC
)
67 HRESULT STDMETHODCALLTYPE
CAddressEditBox::Refresh(long param8
)
72 HRESULT STDMETHODCALLTYPE
CAddressEditBox::Init(HWND comboboxEx
, HWND editControl
, long param14
, IUnknown
*param18
)
74 CComPtr
<IBrowserService
> browserService
;
76 fCombobox
.SubclassWindow(comboboxEx
);
77 fEditWindow
.SubclassWindow(editControl
);
79 hComboBoxEx
= comboboxEx
;
81 SHAutoComplete(fEditWindow
.m_hWnd
, SHACF_FILESYSTEM
| SHACF_URLALL
| SHACF_USETAB
);
83 // take advice to watch events
84 HRESULT hResult
= IUnknown_QueryService(param18
, SID_SShellBrowser
, IID_PPV_ARG(IBrowserService
, &browserService
));
85 if (SUCCEEDED(hResult
))
87 hResult
= AtlAdvise(browserService
, static_cast<IDispatch
*>(this), DIID_DWebBrowserEvents
, &fAdviseCookie
);
93 HRESULT STDMETHODCALLTYPE
CAddressEditBox::SetCurrentDir(long paramC
)
98 BOOL
CAddressEditBox::GetComboBoxText(CComHeapPtr
<WCHAR
>& pszText
)
101 INT cchMax
= fCombobox
.GetWindowTextLength() + sizeof(UNICODE_NULL
);
102 if (!pszText
.Allocate(cchMax
))
104 return fCombobox
.GetWindowText(pszText
, cchMax
);
107 HRESULT
CAddressEditBox::RefreshAddress()
109 /* Get the current pidl of the browser */
110 CComHeapPtr
<ITEMIDLIST
> absolutePIDL
;
111 HRESULT hr
= GetAbsolutePidl(&absolutePIDL
);
112 if (FAILED_UNEXPECTEDLY(hr
))
115 /* Fill the combobox */
116 ATLASSERT(absolutePIDL
!= NULL
);
117 PopulateComboBox(absolutePIDL
);
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
))
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
);
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
;
137 if (SUCCEEDED(IEGetNameAndFlags(absolutePIDL
, flags
, szPathOrName
, _countof(szPathOrName
), NULL
)))
138 item
.pszText
= szPathOrName
;
140 /* Ownership of absolutePIDL will be moved to fCombobox. See CBEN_DELETEITEM */
141 item
.lParam
= reinterpret_cast<LPARAM
>(absolutePIDL
.Detach());
143 fCombobox
.SendMessage(CBEM_SETITEM
, 0, reinterpret_cast<LPARAM
>(&item
)); /* Set it! */
147 HRESULT
CAddressEditBox::GetAbsolutePidl(PIDLIST_ABSOLUTE
*pAbsolutePIDL
)
149 CComPtr
<IBrowserService
> isb
;
150 HRESULT hr
= IUnknown_QueryService(fSite
, SID_STopLevelBrowser
, IID_PPV_ARG(IBrowserService
, &isb
));
151 if (FAILED_UNEXPECTEDLY(hr
))
154 hr
= isb
->GetPidl(pAbsolutePIDL
);
155 if (FAILED_UNEXPECTEDLY(hr
))
161 /* Execute command line from address bar */
162 BOOL
CAddressEditBox::ExecuteCommandLine()
164 /* Get command line */
165 CComHeapPtr
<WCHAR
> pszCmdLine
;
166 if (!GetComboBoxText(pszCmdLine
))
169 /* Split 1st parameter from trailing arguments */
170 PWCHAR args
= PathGetArgsW(pszCmdLine
);
171 PathRemoveArgsW(pszCmdLine
);
173 PathUnquoteSpacesW(pszCmdLine
); /* Unquote the 1st parameter */
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
;
181 /* Set current directory */
182 WCHAR dir
[MAX_PATH
] = L
"";
183 CComHeapPtr
<ITEMIDLIST
> pidl
;
184 if (SUCCEEDED(GetAbsolutePidl(&pidl
)))
186 if (SHGetPathFromIDListW(pidl
, dir
) && PathIsDirectoryW(dir
))
187 info
.lpDirectory
= dir
;
190 if (!::ShellExecuteExW(&info
)) /* Execute! */
197 HRESULT STDMETHODCALLTYPE
CAddressEditBox::ParseNow(long paramC
)
203 PIDLIST_ABSOLUTE pidlCurrent
= NULL
;
204 PIDLIST_RELATIVE pidlRelative
= NULL
;
205 CComPtr
<IShellFolder
> psfCurrent
;
207 CComPtr
<IBrowserService
> pbs
;
208 hr
= IUnknown_QueryService(fSite
, SID_SShellBrowser
, IID_PPV_ARG(IBrowserService
, &pbs
));
209 if (FAILED_UNEXPECTEDLY(hr
))
212 hr
= IUnknown_GetWindow(pbs
, &topLevelWindow
);
213 if (FAILED_UNEXPECTEDLY(hr
))
216 /* Get the path to browse and expand it if needed */
217 CComHeapPtr
<WCHAR
> input
, address
;
218 if (!GetComboBoxText(input
))
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
))
227 address
.Attach(input
.Detach());
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
))
236 hr
= pbs
->GetPidl(&pidlCurrent
);
237 if (FAILED_UNEXPECTEDLY(hr
))
240 hr
= psfDesktop
->BindToObject(pidlCurrent
, NULL
, IID_PPV_ARG(IShellFolder
, &psfCurrent
));
241 if (FAILED_UNEXPECTEDLY(hr
))
244 hr
= psfCurrent
->ParseDisplayName(topLevelWindow
, NULL
, address
, &eaten
, &pidlRelative
, &attributes
);
247 pidlLastParsed
= ILCombine(pidlCurrent
, pidlRelative
);
248 ILFree(pidlRelative
);
253 /* We couldn't parse a relative path, attempt to parse an absolute path */
254 hr
= psfDesktop
->ParseDisplayName(topLevelWindow
, NULL
, address
, &eaten
, &pidlLastParsed
, &attributes
);
262 HRESULT STDMETHODCALLTYPE
CAddressEditBox::ShowFileNotFoundError(HRESULT hRet
)
264 CComHeapPtr
<WCHAR
> input
;
265 if (!GetComboBoxText(input
))
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
);
273 HRESULT STDMETHODCALLTYPE
CAddressEditBox::Execute(long paramC
)
278 * Parse the path if it wasn't parsed
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
))
287 if (ExecuteCommandLine())
290 return ShowFileNotFoundError(hr
);
298 * Get the IShellBrowser and IBrowserService interfaces of the shell browser
300 CComPtr
<IShellBrowser
> pisb
;
301 hr
= IUnknown_QueryService(fSite
, SID_SShellBrowser
, IID_PPV_ARG(IShellBrowser
, &pisb
));
306 * Get the current pidl of the shellbrowser and check if it is the same with the parsed one
308 PIDLIST_ABSOLUTE pidl
;
309 hr
= GetAbsolutePidl(&pidl
);
313 CComPtr
<IShellFolder
> psf
;
314 hr
= SHGetDesktopFolder(&psf
);
318 hr
= psf
->CompareIDs(0, pidl
, pidlLastParsed
);
326 ILFree(pidlLastParsed
);
327 pidlLastParsed
= NULL
;
333 * Attempt to browse to the parsed pidl
335 hr
= pisb
->BrowseObject(pidlLastParsed
, 0);
340 * Browsing to the pidl failed so it's not a folder. So invoke its defaule command.
343 hr
= IUnknown_GetWindow(pisb
, &topLevelWindow
);
347 LPCITEMIDLIST pidlChild
;
348 CComPtr
<IShellFolder
> sf
;
349 hr
= SHBindToParent(pidlLastParsed
, IID_PPV_ARG(IShellFolder
, &sf
), &pidlChild
);
353 hr
= SHInvokeDefaultCommand(topLevelWindow
, sf
, pidlChild
);
360 HRESULT STDMETHODCALLTYPE
CAddressEditBox::Save(long paramC
)
365 HRESULT STDMETHODCALLTYPE
CAddressEditBox::OnWinEvent(
366 HWND hWnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
, LRESULT
*theResult
)
377 if (HIWORD(wParam
) == CBN_SELCHANGE
)
379 UINT selectedIndex
= SendMessageW((HWND
)lParam
, CB_GETCURSEL
, 0, 0);
380 pidlLastParsed
= ILClone((LPITEMIDLIST
)SendMessageW((HWND
)lParam
, CB_GETITEMDATA
, selectedIndex
, 0));
387 hdr
= (LPNMHDR
) lParam
;
388 if (hdr
->code
== CBEN_ENDEDIT
)
390 NMCBEENDEDITW
*endEdit
= (NMCBEENDEDITW
*) lParam
;
391 if (endEdit
->iWhy
== CBENF_RETURN
)
395 else if (endEdit
->iWhy
== CBENF_ESCAPE
)
397 /* Reset the contents of the combo box */
401 else if (hdr
->code
== CBEN_DELETEITEM
)
403 PNMCOMBOBOXEX pCBEx
= (PNMCOMBOBOXEX
) lParam
;
404 LPITEMIDLIST itemPidl
= (LPITEMIDLIST
)pCBEx
->ceItem
.lParam
;
416 HRESULT STDMETHODCALLTYPE
CAddressEditBox::IsWindowOwner(HWND hWnd
)
418 if (fCombobox
.m_hWnd
== hWnd
)
420 if (fEditWindow
.m_hWnd
== hWnd
)
425 HRESULT STDMETHODCALLTYPE
CAddressEditBox::QueryStatus(
426 const GUID
*pguidCmdGroup
, ULONG cCmds
, OLECMD prgCmds
[ ], OLECMDTEXT
*pCmdText
)
431 HRESULT STDMETHODCALLTYPE
CAddressEditBox::Exec(const GUID
*pguidCmdGroup
, DWORD nCmdID
,
432 DWORD nCmdexecopt
, VARIANT
*pvaIn
, VARIANT
*pvaOut
)
437 HRESULT STDMETHODCALLTYPE
CAddressEditBox::GetTypeInfoCount(UINT
*pctinfo
)
442 HRESULT STDMETHODCALLTYPE
CAddressEditBox::GetTypeInfo(UINT iTInfo
, LCID lcid
, ITypeInfo
**ppTInfo
)
447 HRESULT STDMETHODCALLTYPE
CAddressEditBox::GetIDsOfNames(
448 REFIID riid
, LPOLESTR
*rgszNames
, UINT cNames
, LCID lcid
, DISPID
*rgDispId
)
453 HRESULT STDMETHODCALLTYPE
CAddressEditBox::Invoke(DISPID dispIdMember
, REFIID riid
, LCID lcid
,
454 WORD wFlags
, DISPPARAMS
*pDispParams
, VARIANT
*pVarResult
, EXCEPINFO
*pExcepInfo
, UINT
*puArgErr
)
456 if (pDispParams
== NULL
)
459 switch (dispIdMember
)
461 case DISPID_NAVIGATECOMPLETE2
:
462 case DISPID_DOCUMENTCOMPLETE
:
465 ILFree(pidlLastParsed
);
466 pidlLastParsed
= NULL
;
475 HRESULT STDMETHODCALLTYPE
CAddressEditBox::GetClassID(CLSID
*pClassID
)
477 if (pClassID
== NULL
)
479 *pClassID
= CLSID_AddressEditBox
;
483 HRESULT STDMETHODCALLTYPE
CAddressEditBox::IsDirty()
488 HRESULT STDMETHODCALLTYPE
CAddressEditBox::Load(IStream
*pStm
)
493 HRESULT STDMETHODCALLTYPE
CAddressEditBox::Save(IStream
*pStm
, BOOL fClearDirty
)
498 HRESULT STDMETHODCALLTYPE
CAddressEditBox::GetSizeMax(ULARGE_INTEGER
*pcbSize
)
503 void CAddressEditBox::PopulateComboBox(LPITEMIDLIST pidlCurrent
)
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);
515 /* Calculate the indent level. No need to clone the pidl */
521 pidl
= ILGetNext(pidl
);
526 /* Add every id from the pidl in the combo box */
527 pidl
= ILClone(pidlCurrent
);
530 AddComboBoxItem(pidl
, 0, index
);
531 ILRemoveLastID(pidl
);
533 } while (index
>= 0);
536 /* Add the items of the desktop */
537 FillOneLevel(0, 1, indent
);
539 /* Add the items of My Computer */
540 hr
= SHGetSpecialFolderLocation(0, CSIDL_DRIVES
, &pidl
);
541 if (FAILED_UNEXPECTEDLY(hr
))
544 for(LPITEMIDLIST i
= GetItemData(0); i
; i
= GetItemData(index
))
546 if (ILIsEqual(i
, pidl
))
548 FillOneLevel(index
, 2, indent
);
556 void CAddressEditBox::AddComboBoxItem(LPITEMIDLIST pidl
, int index
, int indent
)
561 LPCITEMIDLIST pidlChild
;
562 CComPtr
<IShellFolder
> sf
;
563 hr
= SHBindToParent(pidl
, IID_PPV_ARG(IShellFolder
, &sf
), &pidlChild
);
564 if (FAILED_UNEXPECTEDLY(hr
))
568 hr
= sf
->GetDisplayNameOf(pidlChild
, SHGDN_FORADDRESSBAR
, &strret
);
569 if (FAILED_UNEXPECTEDLY(hr
))
572 hr
= StrRetToBufW(&strret
, pidlChild
, buf
, 4095);
573 if (FAILED_UNEXPECTEDLY(hr
))
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
);
580 item
.lParam
= (LPARAM
)(ILClone(pidl
));
581 item
.iIndent
= indent
;
583 SendMessageW(hComboBoxEx
, CBEM_INSERTITEMW
, 0, (LPARAM
)&item
);
586 void CAddressEditBox::FillOneLevel(int index
, int levelIndent
, int indent
)
591 LPITEMIDLIST pidl
, pidl2
, pidl3
, pidl4
;
594 pidl
= GetItemData(index
);
595 pidl2
= GetItemData(count
);
598 CComPtr
<IShellFolder
> psfDesktop
;
599 CComPtr
<IShellFolder
> psfItem
;
601 hr
= SHGetDesktopFolder(&psfDesktop
);
602 if (FAILED_UNEXPECTEDLY(hr
))
607 psfItem
= psfDesktop
;
611 hr
= psfDesktop
->BindToObject(pidl
, NULL
, IID_PPV_ARG(IShellFolder
, &psfItem
));
612 if (FAILED_UNEXPECTEDLY(hr
))
616 CComPtr
<IEnumIDList
> pEnumIDList
;
617 hr
= psfItem
->EnumObjects(0, SHCONTF_FOLDERS
| SHCONTF_INCLUDEHIDDEN
, &pEnumIDList
);
618 if (FAILED_UNEXPECTEDLY(hr
))
623 hr
= pEnumIDList
->Next(1, &pidl3
, &numObj
);
624 if(hr
!= S_OK
|| !numObj
)
627 pidl4
= ILCombine(pidl
, pidl3
);
628 if (pidl2
&& ILIsEqual(pidl4
, pidl2
))
629 count
+= (indent
- levelIndent
);
631 AddComboBoxItem(pidl4
, count
, levelIndent
);
639 LPITEMIDLIST
CAddressEditBox::GetItemData(int index
)
641 COMBOBOXEXITEMW item
;
643 memset(&item
, 0, sizeof(COMBOBOXEXITEMW
));
644 item
.mask
= CBEIF_LPARAM
;
646 SendMessageW(hComboBoxEx
, CBEM_GETITEMW
, 0, (LPARAM
)&item
);
647 return (LPITEMIDLIST
)item
.lParam
;
650 LRESULT
CAddressEditBox::OnSettingChange(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
&bHandled
)