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
);
164 /* We couldn't parse a relative path, attempt to parse an absolute path */
165 hr
= psfDesktop
->ParseDisplayName(topLevelWindow
, NULL
, address
, &eaten
, &pidlLastParsed
, &attributes
);
170 if (address
!= input
)
177 HRESULT STDMETHODCALLTYPE
CAddressEditBox::ShowFileNotFoundError(HRESULT hRet
)
179 CComHeapPtr
<WCHAR
> input
;
180 int inputLength
= fCombobox
.GetWindowTextLength() + 2;
182 input
.Allocate(inputLength
);
183 fCombobox
.GetWindowText(input
, inputLength
);
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
);
190 HRESULT STDMETHODCALLTYPE
CAddressEditBox::Execute(long paramC
)
195 * Parse the path is it wasn't parsed
201 * If the destination path doesn't exist then display an error message
203 if (hr
== HRESULT_FROM_WIN32(ERROR_INVALID_DRIVE
) || hr
== HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND
))
204 return ShowFileNotFoundError(hr
);
210 * Get the IShellBrowser and IBrowserService interfaces of the shell browser
212 CComPtr
<IShellBrowser
> pisb
;
213 hr
= IUnknown_QueryService(fSite
, SID_SShellBrowser
, IID_PPV_ARG(IShellBrowser
, &pisb
));
217 CComPtr
<IBrowserService
> pbs
;
218 pisb
->QueryInterface(IID_PPV_ARG(IBrowserService
, &pbs
));
223 * Get the current pidl of the shellbrowser and check if it is the same with the parsed one
225 PIDLIST_ABSOLUTE pidl
;
226 hr
= pbs
->GetPidl(&pidl
);
230 CComPtr
<IShellFolder
> psf
;
231 hr
= SHGetDesktopFolder(&psf
);
235 hr
= psf
->CompareIDs(0, pidl
, pidlLastParsed
);
242 * Attempt to browse to the parsed pidl
244 hr
= pisb
->BrowseObject(pidlLastParsed
, 0);
249 * Browsing to the pidl failed so it's not a folder. So invoke its defaule command.
252 hr
= IUnknown_GetWindow(pisb
, &topLevelWindow
);
256 LPCITEMIDLIST pidlChild
;
257 CComPtr
<IShellFolder
> sf
;
258 hr
= SHBindToParent(pidlLastParsed
, IID_PPV_ARG(IShellFolder
, &sf
), &pidlChild
);
262 hr
= SHInvokeDefaultCommand(topLevelWindow
, sf
, pidlChild
);
269 HRESULT STDMETHODCALLTYPE
CAddressEditBox::Save(long paramC
)
274 HRESULT STDMETHODCALLTYPE
CAddressEditBox::OnWinEvent(
275 HWND hWnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
, LRESULT
*theResult
)
286 if (HIWORD(wParam
) == CBN_SELCHANGE
)
288 UINT selectedIndex
= SendMessageW((HWND
)lParam
, CB_GETCURSEL
, 0, 0);
289 pidlLastParsed
= ILClone((LPITEMIDLIST
)SendMessageW((HWND
)lParam
, CB_GETITEMDATA
, selectedIndex
, 0));
296 hdr
= (LPNMHDR
) lParam
;
297 if (hdr
->code
== CBEN_ENDEDIT
)
299 NMCBEENDEDITW
*endEdit
= (NMCBEENDEDITW
*) lParam
;
300 if (endEdit
->iWhy
== CBENF_RETURN
)
304 else if (endEdit
->iWhy
== CBENF_ESCAPE
)
306 /* Reset the contents of the combo box */
309 else if (hdr
->code
== CBEN_DELETEITEM
)
311 PNMCOMBOBOXEX pCBEx
= (PNMCOMBOBOXEX
) lParam
;
312 LPITEMIDLIST itemPidl
= (LPITEMIDLIST
)pCBEx
->ceItem
.lParam
;
324 HRESULT STDMETHODCALLTYPE
CAddressEditBox::IsWindowOwner(HWND hWnd
)
326 if (fCombobox
.m_hWnd
== hWnd
)
328 if (fEditWindow
.m_hWnd
== hWnd
)
333 HRESULT STDMETHODCALLTYPE
CAddressEditBox::QueryStatus(
334 const GUID
*pguidCmdGroup
, ULONG cCmds
, OLECMD prgCmds
[ ], OLECMDTEXT
*pCmdText
)
339 HRESULT STDMETHODCALLTYPE
CAddressEditBox::Exec(const GUID
*pguidCmdGroup
, DWORD nCmdID
,
340 DWORD nCmdexecopt
, VARIANT
*pvaIn
, VARIANT
*pvaOut
)
345 HRESULT STDMETHODCALLTYPE
CAddressEditBox::GetTypeInfoCount(UINT
*pctinfo
)
350 HRESULT STDMETHODCALLTYPE
CAddressEditBox::GetTypeInfo(UINT iTInfo
, LCID lcid
, ITypeInfo
**ppTInfo
)
355 HRESULT STDMETHODCALLTYPE
CAddressEditBox::GetIDsOfNames(
356 REFIID riid
, LPOLESTR
*rgszNames
, UINT cNames
, LCID lcid
, DISPID
*rgDispId
)
361 HRESULT STDMETHODCALLTYPE
CAddressEditBox::Invoke(DISPID dispIdMember
, REFIID riid
, LCID lcid
,
362 WORD wFlags
, DISPPARAMS
*pDispParams
, VARIANT
*pVarResult
, EXCEPINFO
*pExcepInfo
, UINT
*puArgErr
)
364 CComPtr
<IBrowserService
> isb
;
365 CComPtr
<IShellFolder
> sf
;
367 PIDLIST_ABSOLUTE absolutePIDL
;
368 LPCITEMIDLIST pidlChild
;
372 if (pDispParams
== NULL
)
375 switch (dispIdMember
)
377 case DISPID_NAVIGATECOMPLETE2
:
378 case DISPID_DOCUMENTCOMPLETE
:
381 ILFree(pidlLastParsed
);
382 pidlLastParsed
= NULL
;
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
))
389 hr
= isb
->GetPidl(&absolutePIDL
);
390 if (FAILED_UNEXPECTEDLY(hr
))
395 ERR("Got no PIDL, investigate me!\n");
399 /* Fill the combobox */
400 PopulateComboBox(absolutePIDL
);
402 /* Find the current item in the combobox and select it */
403 CComPtr
<IShellFolder
> psfDesktop
;
404 hr
= SHGetDesktopFolder(&psfDesktop
);
405 if (FAILED_UNEXPECTEDLY(hr
))
408 hr
= psfDesktop
->GetDisplayNameOf(absolutePIDL
, SHGDN_FORADDRESSBAR
, &ret
);
409 if (FAILED_UNEXPECTEDLY(hr
))
412 hr
= StrRetToBufW(&ret
, absolutePIDL
, buf
, 4095);
413 if (FAILED_UNEXPECTEDLY(hr
))
416 int index
= SendMessageW(hComboBoxEx
, CB_FINDSTRINGEXACT
, 0, (LPARAM
)buf
);
418 SendMessageW(hComboBoxEx
, CB_SETCURSEL
, index
, 0);
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
))
425 hr
= sf
->GetDisplayNameOf(pidlChild
, SHGDN_FORADDRESSBAR
| SHGDN_FORPARSING
, &ret
);
426 if (FAILED_UNEXPECTEDLY(hr
))
429 hr
= StrRetToBufW(&ret
, pidlChild
, buf
, 4095);
430 if (FAILED_UNEXPECTEDLY(hr
))
433 INT indexClosed
, indexOpen
;
434 indexClosed
= SHMapPIDLToSystemImageListIndex(sf
, pidlChild
, &indexOpen
);
436 COMBOBOXEXITEMW item
= {0};
437 item
.mask
= CBEIF_IMAGE
| CBEIF_SELECTEDIMAGE
| CBEIF_TEXT
| CBEIF_LPARAM
;
439 item
.iImage
= indexClosed
;
440 item
.iSelectedImage
= indexOpen
;
442 item
.lParam
= reinterpret_cast<LPARAM
>(absolutePIDL
);
443 fCombobox
.SendMessage(CBEM_SETITEM
, 0, reinterpret_cast<LPARAM
>(&item
));
448 HRESULT STDMETHODCALLTYPE
CAddressEditBox::GetClassID(CLSID
*pClassID
)
450 if (pClassID
== NULL
)
452 *pClassID
= CLSID_AddressEditBox
;
456 HRESULT STDMETHODCALLTYPE
CAddressEditBox::IsDirty()
461 HRESULT STDMETHODCALLTYPE
CAddressEditBox::Load(IStream
*pStm
)
466 HRESULT STDMETHODCALLTYPE
CAddressEditBox::Save(IStream
*pStm
, BOOL fClearDirty
)
471 HRESULT STDMETHODCALLTYPE
CAddressEditBox::GetSizeMax(ULARGE_INTEGER
*pcbSize
)
476 void CAddressEditBox::PopulateComboBox(LPITEMIDLIST pidlCurrent
)
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);
488 /* Calculate the indent level. No need to clone the pidl */
494 pidl
= ILGetNext(pidl
);
499 /* Add every id from the pidl in the combo box */
500 pidl
= ILClone(pidlCurrent
);
503 AddComboBoxItem(pidl
, 0, index
);
504 ILRemoveLastID(pidl
);
506 } while (index
>= 0);
509 /* Add the items of the desktop */
510 FillOneLevel(0, 1, indent
);
512 /* Add the items of My Computer */
513 hr
= SHGetSpecialFolderLocation(0, CSIDL_DRIVES
, &pidl
);
514 if (FAILED_UNEXPECTEDLY(hr
))
517 for(LPITEMIDLIST i
= GetItemData(0); i
; i
= GetItemData(index
))
519 if (ILIsEqual(i
, pidl
))
521 FillOneLevel(index
, 2, indent
);
529 void CAddressEditBox::AddComboBoxItem(LPITEMIDLIST pidl
, int index
, int indent
)
534 LPCITEMIDLIST pidlChild
;
535 CComPtr
<IShellFolder
> sf
;
536 hr
= SHBindToParent(pidl
, IID_PPV_ARG(IShellFolder
, &sf
), &pidlChild
);
537 if (FAILED_UNEXPECTEDLY(hr
))
541 hr
= sf
->GetDisplayNameOf(pidlChild
, SHGDN_FORADDRESSBAR
, &strret
);
542 if (FAILED_UNEXPECTEDLY(hr
))
545 hr
= StrRetToBufW(&strret
, pidlChild
, buf
, 4095);
546 if (FAILED_UNEXPECTEDLY(hr
))
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
);
553 item
.lParam
= (LPARAM
)(ILClone(pidl
));
554 item
.iIndent
= indent
;
556 SendMessageW(hComboBoxEx
, CBEM_INSERTITEMW
, 0, (LPARAM
)&item
);
559 void CAddressEditBox::FillOneLevel(int index
, int levelIndent
, int indent
)
564 LPITEMIDLIST pidl
, pidl2
, pidl3
, pidl4
;
567 pidl
= GetItemData(index
);
568 pidl2
= GetItemData(count
);
571 CComPtr
<IShellFolder
> psfDesktop
;
572 CComPtr
<IShellFolder
> psfItem
;
574 hr
= SHGetDesktopFolder(&psfDesktop
);
575 if (FAILED_UNEXPECTEDLY(hr
))
580 psfItem
= psfDesktop
;
584 hr
= psfDesktop
->BindToObject(pidl
, NULL
, IID_PPV_ARG(IShellFolder
, &psfItem
));
585 if (FAILED_UNEXPECTEDLY(hr
))
589 CComPtr
<IEnumIDList
> pEnumIDList
;
590 hr
= psfItem
->EnumObjects(0, SHCONTF_FOLDERS
| SHCONTF_INCLUDEHIDDEN
, &pEnumIDList
);
591 if (FAILED_UNEXPECTEDLY(hr
))
596 hr
= pEnumIDList
->Next(1, &pidl3
, &numObj
);
597 if(hr
!= S_OK
|| !numObj
)
600 pidl4
= ILCombine(pidl
, pidl3
);
601 if (pidl2
&& ILIsEqual(pidl4
, pidl2
))
602 count
+= (indent
- levelIndent
);
604 AddComboBoxItem(pidl4
, count
, levelIndent
);
612 LPITEMIDLIST
CAddressEditBox::GetItemData(int index
)
614 COMBOBOXEXITEMW item
;
616 memset(&item
, 0, sizeof(COMBOBOXEXITEMW
));
617 item
.mask
= CBEIF_LPARAM
;
619 SendMessageW(hComboBoxEx
, CBEM_GETITEMW
, 0, (LPARAM
)&item
);
620 return (LPITEMIDLIST
)item
.lParam
;