4 * Copyright 2009 Andrew Hill <ash77 at domain reactos.org>
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.
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.
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
22 This class handles the combo box of the address band.
29 Add drag and drop of icon in edit box
30 Handle change notifies to update appropriately
33 CAddressEditBox::CAddressEditBox() :
34 fCombobox(NULL
, this, 1),
35 fEditWindow(NULL
, this, 1),
41 CAddressEditBox::~CAddressEditBox()
44 ILFree(pidlLastParsed
);
47 HRESULT STDMETHODCALLTYPE
CAddressEditBox::SetOwner(IUnknown
*pOwner
)
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
);
57 // connect to browser connection point
61 HRESULT STDMETHODCALLTYPE
CAddressEditBox::FileSysChange(long param8
, long paramC
)
66 HRESULT STDMETHODCALLTYPE
CAddressEditBox::Refresh(long param8
)
71 HRESULT STDMETHODCALLTYPE
CAddressEditBox::Init(HWND comboboxEx
, HWND editControl
, long param14
, IUnknown
*param18
)
73 CComPtr
<IBrowserService
> browserService
;
75 fCombobox
.SubclassWindow(comboboxEx
);
76 fEditWindow
.SubclassWindow(editControl
);
78 hComboBoxEx
= comboboxEx
;
80 SHAutoComplete(fEditWindow
.m_hWnd
, SHACF_FILESYSTEM
| SHACF_URLALL
| SHACF_USETAB
);
82 // take advice to watch events
83 HRESULT hResult
= IUnknown_QueryService(param18
, SID_SShellBrowser
, IID_PPV_ARG(IBrowserService
, &browserService
));
84 if (SUCCEEDED(hResult
))
86 hResult
= AtlAdvise(browserService
, static_cast<IDispatch
*>(this), DIID_DWebBrowserEvents
, &fAdviseCookie
);
92 HRESULT STDMETHODCALLTYPE
CAddressEditBox::SetCurrentDir(long paramC
)
97 HRESULT STDMETHODCALLTYPE
CAddressEditBox::ParseNow(long paramC
)
103 PIDLIST_ABSOLUTE pidlCurrent
= NULL
;
104 PIDLIST_RELATIVE pidlRelative
= NULL
;
105 CComPtr
<IShellFolder
> psfCurrent
;
107 CComPtr
<IBrowserService
> pbs
;
108 hr
= IUnknown_QueryService(fSite
, SID_SShellBrowser
, IID_PPV_ARG(IBrowserService
, &pbs
));
109 if (FAILED_UNEXPECTEDLY(hr
))
112 hr
= IUnknown_GetWindow(pbs
, &topLevelWindow
);
113 if (FAILED_UNEXPECTEDLY(hr
))
116 /* Get the path to browse and expand it if needed */
118 int inputLength
= fCombobox
.GetWindowTextLength() + 2;
120 input
= new WCHAR
[inputLength
];
121 fCombobox
.GetWindowText(input
, inputLength
);
124 int addressLength
= ExpandEnvironmentStrings(input
, NULL
, 0);
126 if (addressLength
<= 0)
133 address
= new WCHAR
[addressLength
];
134 if (!ExpandEnvironmentStrings(input
, address
, addressLength
))
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
))
147 hr
= pbs
->GetPidl(&pidlCurrent
);
148 if (FAILED_UNEXPECTEDLY(hr
))
151 hr
= psfDesktop
->BindToObject(pidlCurrent
, NULL
, IID_PPV_ARG(IShellFolder
, &psfCurrent
));
152 if (FAILED_UNEXPECTEDLY(hr
))
155 hr
= psfCurrent
->ParseDisplayName(topLevelWindow
, NULL
, address
, &eaten
, &pidlRelative
, &attributes
);
158 pidlLastParsed
= ILCombine(pidlCurrent
, pidlRelative
);
159 ILFree(pidlRelative
);
163 /* We couldn't parse a relative path, attempt to parse an absolute path */
164 hr
= psfDesktop
->ParseDisplayName(topLevelWindow
, NULL
, address
, &eaten
, &pidlLastParsed
, &attributes
);
169 if (address
!= input
)
176 HRESULT STDMETHODCALLTYPE
CAddressEditBox::Execute(long paramC
)
181 * Parse the path is it wasn't parsed
190 * Get the IShellBrowser and IBrowserService interfaces of the shell browser
192 CComPtr
<IShellBrowser
> pisb
;
193 hr
= IUnknown_QueryService(fSite
, SID_SShellBrowser
, IID_PPV_ARG(IShellBrowser
, &pisb
));
197 CComPtr
<IBrowserService
> pbs
;
198 pisb
->QueryInterface(IID_PPV_ARG(IBrowserService
, &pbs
));
203 * Get the current pidl of the shellbrowser and check if it is the same with the parsed one
205 PIDLIST_ABSOLUTE pidl
;
206 hr
= pbs
->GetPidl(&pidl
);
210 CComPtr
<IShellFolder
> psf
;
211 hr
= SHGetDesktopFolder(&psf
);
215 hr
= psf
->CompareIDs(0, pidl
, pidlLastParsed
);
222 * Attempt to browse to the parsed pidl
224 hr
= pisb
->BrowseObject(pidlLastParsed
, 0);
229 * Browsing to the pidl failed so it's not a folder. So invoke its defaule command.
232 hr
= IUnknown_GetWindow(pisb
, &topLevelWindow
);
236 LPCITEMIDLIST pidlChild
;
237 CComPtr
<IShellFolder
> sf
;
238 hr
= SHBindToParent(pidlLastParsed
, IID_PPV_ARG(IShellFolder
, &sf
), &pidlChild
);
242 hr
= SHInvokeDefaultCommand(topLevelWindow
, sf
, pidlChild
);
249 HRESULT STDMETHODCALLTYPE
CAddressEditBox::Save(long paramC
)
254 HRESULT STDMETHODCALLTYPE
CAddressEditBox::OnWinEvent(
255 HWND hWnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
, LRESULT
*theResult
)
266 if (HIWORD(wParam
) == CBN_SELCHANGE
)
268 UINT selectedIndex
= SendMessageW((HWND
)lParam
, CB_GETCURSEL
, 0, 0);
269 pidlLastParsed
= ILClone((LPITEMIDLIST
)SendMessageW((HWND
)lParam
, CB_GETITEMDATA
, selectedIndex
, 0));
276 hdr
= (LPNMHDR
) lParam
;
277 if (hdr
->code
== CBEN_ENDEDIT
)
279 NMCBEENDEDITW
*endEdit
= (NMCBEENDEDITW
*) lParam
;
280 if (endEdit
->iWhy
== CBENF_RETURN
)
284 else if (endEdit
->iWhy
== CBENF_ESCAPE
)
286 /* Reset the contents of the combo box */
289 else if (hdr
->code
== CBEN_DELETEITEM
)
291 PNMCOMBOBOXEX pCBEx
= (PNMCOMBOBOXEX
) lParam
;
292 LPITEMIDLIST itemPidl
= (LPITEMIDLIST
)pCBEx
->ceItem
.lParam
;
304 HRESULT STDMETHODCALLTYPE
CAddressEditBox::IsWindowOwner(HWND hWnd
)
306 if (fCombobox
.m_hWnd
== hWnd
)
308 if (fEditWindow
.m_hWnd
== hWnd
)
313 HRESULT STDMETHODCALLTYPE
CAddressEditBox::QueryStatus(
314 const GUID
*pguidCmdGroup
, ULONG cCmds
, OLECMD prgCmds
[ ], OLECMDTEXT
*pCmdText
)
319 HRESULT STDMETHODCALLTYPE
CAddressEditBox::Exec(const GUID
*pguidCmdGroup
, DWORD nCmdID
,
320 DWORD nCmdexecopt
, VARIANT
*pvaIn
, VARIANT
*pvaOut
)
325 HRESULT STDMETHODCALLTYPE
CAddressEditBox::GetTypeInfoCount(UINT
*pctinfo
)
330 HRESULT STDMETHODCALLTYPE
CAddressEditBox::GetTypeInfo(UINT iTInfo
, LCID lcid
, ITypeInfo
**ppTInfo
)
335 HRESULT STDMETHODCALLTYPE
CAddressEditBox::GetIDsOfNames(
336 REFIID riid
, LPOLESTR
*rgszNames
, UINT cNames
, LCID lcid
, DISPID
*rgDispId
)
341 HRESULT STDMETHODCALLTYPE
CAddressEditBox::Invoke(DISPID dispIdMember
, REFIID riid
, LCID lcid
,
342 WORD wFlags
, DISPPARAMS
*pDispParams
, VARIANT
*pVarResult
, EXCEPINFO
*pExcepInfo
, UINT
*puArgErr
)
344 CComPtr
<IBrowserService
> isb
;
345 CComPtr
<IShellFolder
> sf
;
347 PIDLIST_ABSOLUTE absolutePIDL
;
348 LPCITEMIDLIST pidlChild
;
352 if (pDispParams
== NULL
)
355 switch (dispIdMember
)
357 case DISPID_NAVIGATECOMPLETE2
:
358 case DISPID_DOCUMENTCOMPLETE
:
361 ILFree(pidlLastParsed
);
362 pidlLastParsed
= NULL
;
364 /* Get the current pidl of the browser */
365 hr
= IUnknown_QueryService(fSite
, SID_STopLevelBrowser
, IID_PPV_ARG(IBrowserService
, &isb
));
366 if (FAILED_UNEXPECTEDLY(hr
))
369 hr
= isb
->GetPidl(&absolutePIDL
);
370 if (FAILED_UNEXPECTEDLY(hr
))
373 /* Fill the combobox */
374 PopulateComboBox(absolutePIDL
);
376 /* Find the current item in the combobox and select it */
377 CComPtr
<IShellFolder
> psfDesktop
;
378 hr
= SHGetDesktopFolder(&psfDesktop
);
379 if (FAILED_UNEXPECTEDLY(hr
))
382 hr
= psfDesktop
->GetDisplayNameOf(absolutePIDL
, SHGDN_FORADDRESSBAR
, &ret
);
383 if (FAILED_UNEXPECTEDLY(hr
))
386 hr
= StrRetToBufW(&ret
, absolutePIDL
, buf
, 4095);
387 if (FAILED_UNEXPECTEDLY(hr
))
390 int index
= SendMessageW(hComboBoxEx
, CB_FINDSTRINGEXACT
, 0, (LPARAM
)buf
);
392 SendMessageW(hComboBoxEx
, CB_SETCURSEL
, index
, 0);
394 /* Add the item that will be visible when the combobox is not expanded */
395 hr
= SHBindToParent(absolutePIDL
, IID_PPV_ARG(IShellFolder
, &sf
), &pidlChild
);
396 if (FAILED_UNEXPECTEDLY(hr
))
399 hr
= sf
->GetDisplayNameOf(pidlChild
, SHGDN_FORADDRESSBAR
| SHGDN_FORPARSING
, &ret
);
400 if (FAILED_UNEXPECTEDLY(hr
))
403 hr
= StrRetToBufW(&ret
, pidlChild
, buf
, 4095);
404 if (FAILED_UNEXPECTEDLY(hr
))
407 INT indexClosed
, indexOpen
;
408 indexClosed
= SHMapPIDLToSystemImageListIndex(sf
, pidlChild
, &indexOpen
);
410 COMBOBOXEXITEMW item
= {0};
411 item
.mask
= CBEIF_IMAGE
| CBEIF_SELECTEDIMAGE
| CBEIF_TEXT
| CBEIF_LPARAM
;
413 item
.iImage
= indexClosed
;
414 item
.iSelectedImage
= indexOpen
;
416 item
.lParam
= reinterpret_cast<LPARAM
>(absolutePIDL
);
417 fCombobox
.SendMessage(CBEM_SETITEM
, 0, reinterpret_cast<LPARAM
>(&item
));
422 HRESULT STDMETHODCALLTYPE
CAddressEditBox::GetClassID(CLSID
*pClassID
)
424 if (pClassID
== NULL
)
426 *pClassID
= CLSID_AddressEditBox
;
430 HRESULT STDMETHODCALLTYPE
CAddressEditBox::IsDirty()
435 HRESULT STDMETHODCALLTYPE
CAddressEditBox::Load(IStream
*pStm
)
440 HRESULT STDMETHODCALLTYPE
CAddressEditBox::Save(IStream
*pStm
, BOOL fClearDirty
)
445 HRESULT STDMETHODCALLTYPE
CAddressEditBox::GetSizeMax(ULARGE_INTEGER
*pcbSize
)
450 void CAddressEditBox::PopulateComboBox(LPITEMIDLIST pidlCurrent
)
457 index
= SendMessageW(hComboBoxEx
, CB_GETCOUNT
, 0, 0);
458 for (int i
= 0; i
< index
; i
++)
459 SendMessageW(hComboBoxEx
, CBEM_DELETEITEM
, i
, 0);
460 SendMessageW(hComboBoxEx
, CB_RESETCONTENT
, 0, 0);
462 /* Calculate the indent level. No need to clone the pidl */
468 pidl
= ILGetNext(pidl
);
473 /* Add every id from the pidl in the combo box */
474 pidl
= ILClone(pidlCurrent
);
477 AddComboBoxItem(pidl
, 0, index
);
478 ILRemoveLastID(pidl
);
480 } while (index
>= 0);
483 /* Add the items of the desktop */
484 FillOneLevel(0, 1, indent
);
486 /* Add the items of My Computer */
487 hr
= SHGetSpecialFolderLocation(0, CSIDL_DRIVES
, &pidl
);
488 if (FAILED_UNEXPECTEDLY(hr
))
491 for(LPITEMIDLIST i
= GetItemData(0); i
; i
= GetItemData(index
))
493 if (ILIsEqual(i
, pidl
))
495 FillOneLevel(index
, 2, indent
);
503 void CAddressEditBox::AddComboBoxItem(LPITEMIDLIST pidl
, int index
, int indent
)
508 LPCITEMIDLIST pidlChild
;
509 CComPtr
<IShellFolder
> sf
;
510 hr
= SHBindToParent(pidl
, IID_PPV_ARG(IShellFolder
, &sf
), &pidlChild
);
511 if (FAILED_UNEXPECTEDLY(hr
))
515 hr
= sf
->GetDisplayNameOf(pidlChild
, SHGDN_FORADDRESSBAR
, &strret
);
516 if (FAILED_UNEXPECTEDLY(hr
))
519 hr
= StrRetToBufW(&strret
, pidlChild
, buf
, 4095);
520 if (FAILED_UNEXPECTEDLY(hr
))
523 COMBOBOXEXITEMW item
= {0};
524 item
.mask
= CBEIF_LPARAM
| CBEIF_INDENT
| CBEIF_SELECTEDIMAGE
| CBEIF_IMAGE
| CBEIF_TEXT
;
525 item
.iImage
= SHMapPIDLToSystemImageListIndex(sf
, pidlChild
, &item
.iSelectedImage
);
527 item
.lParam
= (LPARAM
)(ILClone(pidl
));
528 item
.iIndent
= indent
;
530 SendMessageW(hComboBoxEx
, CBEM_INSERTITEMW
, 0, (LPARAM
)&item
);
533 void CAddressEditBox::FillOneLevel(int index
, int levelIndent
, int indent
)
538 LPITEMIDLIST pidl
, pidl2
, pidl3
, pidl4
;
541 pidl
= GetItemData(index
);
542 pidl2
= GetItemData(count
);
545 CComPtr
<IShellFolder
> psfDesktop
;
546 CComPtr
<IShellFolder
> psfItem
;
548 hr
= SHGetDesktopFolder(&psfDesktop
);
549 if (FAILED_UNEXPECTEDLY(hr
))
554 psfItem
= psfDesktop
;
558 hr
= psfDesktop
->BindToObject(pidl
, NULL
, IID_PPV_ARG(IShellFolder
, &psfItem
));
559 if (FAILED_UNEXPECTEDLY(hr
))
563 CComPtr
<IEnumIDList
> pEnumIDList
;
564 hr
= psfItem
->EnumObjects(0, SHCONTF_FOLDERS
| SHCONTF_INCLUDEHIDDEN
, &pEnumIDList
);
565 if (FAILED_UNEXPECTEDLY(hr
))
570 hr
= pEnumIDList
->Next(1, &pidl3
, &numObj
);
571 if(hr
!= S_OK
|| !numObj
)
574 pidl4
= ILCombine(pidl
, pidl3
);
575 if (pidl2
&& ILIsEqual(pidl4
, pidl2
))
576 count
+= (indent
- levelIndent
);
578 AddComboBoxItem(pidl4
, count
, levelIndent
);
586 LPITEMIDLIST
CAddressEditBox::GetItemData(int index
)
588 COMBOBOXEXITEMW item
;
590 memset(&item
, 0, sizeof(COMBOBOXEXITEMW
));
591 item
.mask
= CBEIF_LPARAM
;
593 SendMessageW(hComboBoxEx
, CBEM_GETITEMW
, 0, (LPARAM
)&item
);
594 return (LPITEMIDLIST
)item
.lParam
;