2016-06-03 19:35:33 +00:00
/*
* ReactOS Explorer
*
* Copyright 2016 Sylvain Deverre < deverre dot sylv at gmail dot com >
*
* This library is free software ; you can redistribute it and / or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation ; either
* version 2.1 of the License , or ( at your option ) any later version .
*
* This library is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the GNU
* Lesser General Public License for more details .
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library ; if not , write to the Free Software
* Foundation , Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA
*/
2014-12-03 16:11:46 +00:00
# include "precomp.h"
2016-06-03 19:35:33 +00:00
# include <commoncontrols.h>
# include <undocshell.h>
2014-12-03 16:11:46 +00:00
# if 1
# undef UNIMPLEMENTED
# define UNIMPLEMENTED DbgPrint("%s is UNIMPLEMENTED!\n", __FUNCTION__)
# endif
2017-02-05 13:41:45 +00:00
/*
* TODO :
* - Monitor correctly " external " shell interrupts ( seems like we need to register / deregister them for each folder )
* - find and fix what cause explorer crashes sometimes ( seems to be explorer that does more releases than addref )
* - TESTING
*/
typedef struct _PIDLDATA
{
BYTE type ;
BYTE data [ 1 ] ;
} PIDLDATA , * LPPIDLDATA ;
# define PT_GUID 0x1F
# define PT_SHELLEXT 0x2E
# define PT_YAGUID 0x70
static BOOL _ILIsSpecialFolder ( LPCITEMIDLIST pidl )
{
LPPIDLDATA lpPData = ( LPPIDLDATA ) & pidl - > mkid . abID ;
return ( pidl & &
( ( lpPData & & ( PT_GUID = = lpPData - > type | | PT_SHELLEXT = = lpPData - > type | |
PT_YAGUID = = lpPData - > type ) ) | | ( pidl & & pidl - > mkid . cb = = 0x00 ) ) ) ;
}
HRESULT GetDisplayName ( LPCITEMIDLIST pidlDirectory , TCHAR * szDisplayName , UINT cchMax , DWORD uFlags )
{
IShellFolder * pShellFolder = NULL ;
LPCITEMIDLIST pidlRelative = NULL ;
STRRET str ;
HRESULT hr ;
if ( pidlDirectory = = NULL | | szDisplayName = = NULL )
{
return E_FAIL ;
}
hr = SHBindToParent ( pidlDirectory , IID_PPV_ARG ( IShellFolder , & pShellFolder ) , & pidlRelative ) ;
if ( SUCCEEDED ( hr ) )
{
hr = pShellFolder - > GetDisplayNameOf ( pidlRelative , uFlags , & str ) ;
if ( SUCCEEDED ( hr ) )
{
hr = StrRetToBuf ( & str , pidlDirectory , szDisplayName , cchMax ) ;
}
pShellFolder - > Release ( ) ;
}
return hr ;
}
/*
This is a Windows hack , because shell event messages in Windows gives an
ill - formed PIDL stripped from useful data that parses incorrectly with SHGetFileInfo .
So we need to re - enumerate subfolders until we find one with the same name .
*/
HRESULT _ReparsePIDL ( LPITEMIDLIST buggyPidl , LPITEMIDLIST * cleanPidl )
{
HRESULT hr ;
CComPtr < IShellFolder > folder ;
CComPtr < IPersistFolder2 > persist ;
CComPtr < IEnumIDList > pEnumIDList ;
LPITEMIDLIST childPidl ;
LPITEMIDLIST correctChild ;
LPITEMIDLIST correctParent ;
ULONG fetched ;
DWORD EnumFlags ;
EnumFlags = SHCONTF_FOLDERS | SHCONTF_INCLUDEHIDDEN ;
hr = SHBindToParent ( buggyPidl , IID_PPV_ARG ( IShellFolder , & folder ) , ( LPCITEMIDLIST * ) & childPidl ) ;
* cleanPidl = NULL ;
if ( ! SUCCEEDED ( hr ) )
{
ERR ( " Can't bind to parent folder \n " ) ;
return hr ;
}
hr = folder - > QueryInterface ( IID_PPV_ARG ( IPersistFolder2 , & persist ) ) ;
if ( ! SUCCEEDED ( hr ) )
{
ERR ( " PIDL doesn't belong to the shell namespace, aborting \n " ) ;
return hr ;
}
hr = persist - > GetCurFolder ( & correctParent ) ;
if ( ! SUCCEEDED ( hr ) )
{
ERR ( " Unable to get current folder \n " ) ;
return hr ;
}
hr = folder - > EnumObjects ( NULL , EnumFlags , & pEnumIDList ) ;
// avoid broken IShellFolder implementations that return null pointer with success
if ( ! SUCCEEDED ( hr ) | | ! pEnumIDList )
{
ERR ( " Can't enum the folder ! \n " ) ;
return hr ;
}
while ( SUCCEEDED ( pEnumIDList - > Next ( 1 , & correctChild , & fetched ) ) & & correctChild & & fetched )
{
if ( ! folder - > CompareIDs ( 0 , childPidl , correctChild ) )
{
* cleanPidl = ILCombine ( correctParent , correctChild ) ;
ILFree ( correctChild ) ;
goto Cleanup ;
}
ILFree ( correctChild ) ;
}
Cleanup :
ILFree ( correctParent ) ;
return hr ;
}
2016-06-01 21:12:17 +00:00
CExplorerBand : : CExplorerBand ( ) :
2017-02-05 13:41:45 +00:00
pSite ( NULL ) , fVisible ( FALSE ) , bNavigating ( FALSE ) , dwBandID ( 0 ) , pidlCurrent ( NULL )
2016-06-01 20:13:27 +00:00
{
2014-12-03 16:11:46 +00:00
}
CExplorerBand : : ~ CExplorerBand ( )
{
2017-02-05 13:41:45 +00:00
if ( pidlCurrent )
{
ILFree ( pidlCurrent ) ;
}
2014-12-03 16:11:46 +00:00
}
2016-06-01 21:12:17 +00:00
void CExplorerBand : : InitializeExplorerBand ( )
{
2016-06-03 19:35:33 +00:00
// Init the treeview here
HRESULT hr ;
LPITEMIDLIST pidl ;
CComPtr < IWebBrowser2 > browserService ;
SHChangeNotifyEntry shcne ;
hr = SHGetDesktopFolder ( & pDesktop ) ;
if ( FAILED_UNEXPECTEDLY ( hr ) )
return ;
hr = SHGetFolderLocation ( m_hWnd , CSIDL_DESKTOP , NULL , 0 , & pidl ) ;
if ( FAILED_UNEXPECTEDLY ( hr ) )
return ;
IImageList * piml ;
hr = SHGetImageList ( SHIL_SMALL , IID_PPV_ARG ( IImageList , & piml ) ) ;
if ( FAILED_UNEXPECTEDLY ( hr ) )
return ;
TreeView_SetImageList ( m_hWnd , ( HIMAGELIST ) piml , TVSIL_NORMAL ) ;
// Insert the root node
2016-07-26 14:31:38 +00:00
hRoot = InsertItem ( 0 , pDesktop , pidl , pidl , FALSE ) ;
if ( ! hRoot )
2016-06-03 19:35:33 +00:00
{
ERR ( " Failed to create root item \n " ) ;
return ;
}
2016-06-01 21:12:17 +00:00
2016-07-26 14:31:38 +00:00
NodeInfo * pNodeInfo = GetNodeInfo ( hRoot ) ;
2016-06-03 19:35:33 +00:00
// Insert child nodes
2016-07-26 14:31:38 +00:00
InsertSubitems ( hRoot , pNodeInfo ) ;
TreeView_Expand ( m_hWnd , hRoot , TVE_EXPAND ) ;
2016-06-03 19:35:33 +00:00
// Navigate to current folder position
2016-07-26 14:31:38 +00:00
NavigateToCurrentFolder ( ) ;
2016-06-03 19:35:33 +00:00
// Register shell notification
shcne . pidl = pidl ;
shcne . fRecursive = TRUE ;
shellRegID = SHChangeNotifyRegister (
m_hWnd ,
SHCNRF_ShellLevel | SHCNRF_InterruptLevel | SHCNRF_RecursiveInterrupt ,
SHCNE_DISKEVENTS | SHCNE_RENAMEFOLDER | SHCNE_RMDIR | SHCNE_MKDIR ,
WM_USER_SHELLEVENT ,
1 ,
& shcne ) ;
if ( ! shellRegID )
{
ERR ( " Something went wrong, error %08x \n " , GetLastError ( ) ) ;
}
// Register browser connection endpoint
hr = IUnknown_QueryService ( pSite , SID_SWebBrowserApp , IID_PPV_ARG ( IWebBrowser2 , & browserService ) ) ;
if ( FAILED_UNEXPECTEDLY ( hr ) )
return ;
hr = AtlAdvise ( browserService , dynamic_cast < IDispatch * > ( this ) , DIID_DWebBrowserEvents , & adviseCookie ) ;
if ( FAILED_UNEXPECTEDLY ( hr ) )
return ;
ILFree ( pidl ) ;
2016-06-01 21:12:17 +00:00
}
void CExplorerBand : : DestroyExplorerBand ( )
{
2016-06-03 19:35:33 +00:00
HRESULT hr ;
CComPtr < IWebBrowser2 > browserService ;
TRACE ( " Cleaning up explorer band ... \n " ) ;
hr = IUnknown_QueryService ( pSite , SID_SWebBrowserApp , IID_PPV_ARG ( IWebBrowser2 , & browserService ) ) ;
if ( FAILED_UNEXPECTEDLY ( hr ) )
return ;
hr = AtlUnadvise ( browserService , DIID_DWebBrowserEvents , adviseCookie ) ;
/* Remove all items of the treeview */
RevokeDragDrop ( m_hWnd ) ;
TreeView_DeleteAllItems ( m_hWnd ) ;
pDesktop = NULL ;
hRoot = NULL ;
TRACE ( " Cleanup done ! \n " ) ;
}
CExplorerBand : : NodeInfo * CExplorerBand : : GetNodeInfo ( HTREEITEM hItem )
{
TVITEM tvItem ;
tvItem . mask = TVIF_PARAM ;
tvItem . hItem = hItem ;
if ( ! TreeView_GetItem ( m_hWnd , & tvItem ) )
return 0 ;
return reinterpret_cast < NodeInfo * > ( tvItem . lParam ) ;
}
2016-06-01 21:12:17 +00:00
2016-07-26 15:09:04 +00:00
HRESULT CExplorerBand : : ExecuteCommand ( CComPtr < IContextMenu > & menu , UINT nCmd )
{
CComPtr < IOleWindow > pBrowserOleWnd ;
CMINVOKECOMMANDINFO cmi ;
HWND browserWnd ;
HRESULT hr ;
hr = IUnknown_QueryService ( pSite , SID_SShellBrowser , IID_PPV_ARG ( IOleWindow , & pBrowserOleWnd ) ) ;
if ( FAILED_UNEXPECTEDLY ( hr ) )
return hr ;
hr = pBrowserOleWnd - > GetWindow ( & browserWnd ) ;
if ( FAILED_UNEXPECTEDLY ( hr ) )
return hr ;
ZeroMemory ( & cmi , sizeof ( cmi ) ) ;
cmi . cbSize = sizeof ( cmi ) ;
cmi . lpVerb = MAKEINTRESOURCEA ( nCmd ) ;
cmi . hwnd = browserWnd ;
if ( GetKeyState ( VK_SHIFT ) & 0x8000 )
cmi . fMask | = CMIC_MASK_SHIFT_DOWN ;
if ( GetKeyState ( VK_CONTROL ) & 0x8000 )
cmi . fMask | = CMIC_MASK_CONTROL_DOWN ;
return menu - > InvokeCommand ( & cmi ) ;
}
2016-06-03 21:25:34 +00:00
HRESULT CExplorerBand : : UpdateBrowser ( LPITEMIDLIST pidlGoto )
{
CComPtr < IShellBrowser > pBrowserService ;
HRESULT hr ;
hr = IUnknown_QueryService ( pSite , SID_STopLevelBrowser , IID_PPV_ARG ( IShellBrowser , & pBrowserService ) ) ;
if ( FAILED_UNEXPECTEDLY ( hr ) )
return hr ;
hr = pBrowserService - > BrowseObject ( pidlGoto , SBSP_SAMEBROWSER | SBSP_ABSOLUTE ) ;
if ( FAILED_UNEXPECTEDLY ( hr ) )
return hr ;
2017-02-05 13:41:45 +00:00
if ( pidlCurrent )
{
ILFree ( pidlCurrent ) ;
pidlCurrent = ILClone ( pidlGoto ) ;
}
2016-06-03 21:25:34 +00:00
return hr ;
}
2016-06-03 19:35:33 +00:00
// *** notifications handling ***
BOOL CExplorerBand : : OnTreeItemExpanding ( LPNMTREEVIEW pnmtv )
{
NodeInfo * pNodeInfo ;
if ( pnmtv - > action = = TVE_COLLAPSE ) {
if ( pnmtv - > itemNew . hItem = = hRoot )
{
// Prenvent root from collapsing
pnmtv - > itemNew . mask | = TVIF_STATE ;
pnmtv - > itemNew . stateMask | = TVIS_EXPANDED ;
pnmtv - > itemNew . state & = ~ TVIS_EXPANDED ;
pnmtv - > action = TVE_EXPAND ;
return TRUE ;
}
}
if ( pnmtv - > action = = TVE_EXPAND ) {
// Grab our directory PIDL
pNodeInfo = GetNodeInfo ( pnmtv - > itemNew . hItem ) ;
// We have it, let's try
if ( pNodeInfo & & ! pNodeInfo - > expanded )
if ( ! InsertSubitems ( pnmtv - > itemNew . hItem , pNodeInfo ) ) {
// remove subitem "+" since we failed to add subitems
TV_ITEM tvItem ;
tvItem . mask = TVIF_CHILDREN ;
tvItem . hItem = pnmtv - > itemNew . hItem ;
tvItem . cChildren = 0 ;
TreeView_SetItem ( m_hWnd , & tvItem ) ;
}
}
return FALSE ;
}
2017-02-05 13:41:45 +00:00
BOOL CExplorerBand : : OnTreeItemDeleted ( LPNMTREEVIEW pnmtv )
{
/* Destroy memory associated to our node */
NodeInfo * ptr = GetNodeInfo ( pnmtv - > itemNew . hItem ) ;
2017-02-13 15:04:03 +00:00
if ( ptr )
{
ILFree ( ptr - > relativePidl ) ;
ILFree ( ptr - > absolutePidl ) ;
delete ptr ;
}
2017-02-05 13:41:45 +00:00
return TRUE ;
}
2016-06-03 21:25:34 +00:00
void CExplorerBand : : OnSelectionChanged ( LPNMTREEVIEW pnmtv )
{
NodeInfo * pNodeInfo = GetNodeInfo ( pnmtv - > itemNew . hItem ) ;
2016-07-26 14:31:38 +00:00
/* Prevents navigation if selection is initiated inside the band */
if ( bNavigating )
return ;
2016-07-26 15:09:04 +00:00
UpdateBrowser ( pNodeInfo - > absolutePidl ) ;
2016-06-03 21:25:34 +00:00
SetFocus ( ) ;
// Expand the node
//TreeView_Expand(m_hWnd, pnmtv->itemNew.hItem, TVE_EXPAND);
}
2017-02-05 13:41:45 +00:00
void CExplorerBand : : OnTreeItemDragging ( LPNMTREEVIEW pnmtv , BOOL isRightClick )
{
CComPtr < IShellFolder > pSrcFolder ;
CComPtr < IDataObject > pObj ;
LPCITEMIDLIST pLast ;
HRESULT hr ;
DWORD dwEffect ;
DWORD dwEffect2 ;
dwEffect = DROPEFFECT_COPY | DROPEFFECT_MOVE ;
if ( ! pnmtv - > itemNew . lParam )
return ;
NodeInfo * pNodeInfo = GetNodeInfo ( pnmtv - > itemNew . hItem ) ;
hr = SHBindToParent ( pNodeInfo - > absolutePidl , IID_PPV_ARG ( IShellFolder , & pSrcFolder ) , & pLast ) ;
if ( ! SUCCEEDED ( hr ) )
return ;
hr = pSrcFolder - > GetUIObjectOf ( m_hWnd , 1 , & pLast , IID_IDataObject , 0 , reinterpret_cast < void * * > ( & pObj ) ) ;
if ( ! SUCCEEDED ( hr ) )
return ;
DoDragDrop ( pObj , this , dwEffect , & dwEffect2 ) ;
return ;
}
2016-07-26 15:09:04 +00:00
// *** ATL event handlers ***
LRESULT CExplorerBand : : OnContextMenu ( UINT uMsg , WPARAM wParam , LPARAM lParam , BOOL & bHandled )
{
HTREEITEM item ;
NodeInfo * info ;
HMENU treeMenu ;
WORD x ;
WORD y ;
CComPtr < IShellFolder > pFolder ;
CComPtr < IContextMenu > contextMenu ;
HRESULT hr ;
UINT uCommand ;
LPITEMIDLIST pidlChild ;
treeMenu = NULL ;
item = TreeView_GetSelection ( m_hWnd ) ;
bHandled = TRUE ;
if ( ! item )
{
goto Cleanup ;
}
x = LOWORD ( lParam ) ;
y = HIWORD ( lParam ) ;
if ( x = = - 1 & & y = = - 1 )
{
// TODO: grab position of tree item and position it correctly
}
info = GetNodeInfo ( item ) ;
if ( ! info )
{
ERR ( " No node data, something has gone wrong ! \n " ) ;
goto Cleanup ;
}
hr = SHBindToParent ( info - > absolutePidl , IID_PPV_ARG ( IShellFolder , & pFolder ) ,
( LPCITEMIDLIST * ) & pidlChild ) ;
if ( ! SUCCEEDED ( hr ) )
{
ERR ( " Can't bind to folder! \n " ) ;
goto Cleanup ;
}
hr = pFolder - > GetUIObjectOf ( m_hWnd , 1 , ( LPCITEMIDLIST * ) & pidlChild , IID_IContextMenu ,
NULL , reinterpret_cast < void * * > ( & contextMenu ) ) ;
if ( ! SUCCEEDED ( hr ) )
{
ERR ( " Can't get IContextMenu interface \n " ) ;
goto Cleanup ;
}
2017-03-12 09:48:25 +00:00
IUnknown_SetSite ( contextMenu , ( IDeskBand * ) this ) ;
2016-07-26 15:09:04 +00:00
treeMenu = CreatePopupMenu ( ) ;
hr = contextMenu - > QueryContextMenu ( treeMenu , 0 , FCIDM_SHVIEWFIRST , FCIDM_SHVIEWLAST ,
CMF_EXPLORE ) ;
if ( ! SUCCEEDED ( hr ) )
{
WARN ( " Can't get context menu for item \n " ) ;
DestroyMenu ( treeMenu ) ;
goto Cleanup ;
}
uCommand = TrackPopupMenu ( treeMenu , TPM_LEFTALIGN | TPM_RETURNCMD | TPM_LEFTBUTTON | TPM_RIGHTBUTTON ,
x , y , 0 , m_hWnd , NULL ) ;
ExecuteCommand ( contextMenu , uCommand ) ;
2017-03-12 09:48:25 +00:00
2016-07-26 15:09:04 +00:00
Cleanup :
2017-03-12 09:48:25 +00:00
if ( contextMenu )
IUnknown_SetSite ( contextMenu , NULL ) ;
2016-07-26 15:09:04 +00:00
if ( treeMenu )
DestroyMenu ( treeMenu ) ;
bNavigating = TRUE ;
TreeView_SelectItem ( m_hWnd , oldSelected ) ;
bNavigating = FALSE ;
return TRUE ;
}
LRESULT CExplorerBand : : ContextMenuHack ( UINT uMsg , WPARAM wParam , LPARAM lParam , BOOL & bHandled )
{
bHandled = FALSE ;
if ( uMsg = = WM_RBUTTONDOWN )
{
TVHITTESTINFO info ;
info . pt . x = LOWORD ( lParam ) ;
info . pt . y = HIWORD ( lParam ) ;
info . flags = TVHT_ONITEM ;
info . hItem = NULL ;
// Save the current location
oldSelected = TreeView_GetSelection ( m_hWnd ) ;
// Move to the item selected by the treeview (don't change right pane)
TreeView_HitTest ( m_hWnd , & info ) ;
bNavigating = TRUE ;
TreeView_SelectItem ( m_hWnd , info . hItem ) ;
bNavigating = FALSE ;
}
return FALSE ; /* let the wndproc process the message */
}
2017-02-05 13:41:45 +00:00
LRESULT CExplorerBand : : OnShellEvent ( UINT uMsg , WPARAM wParam , LPARAM lParam , BOOL & bHandled )
{
LPITEMIDLIST * dest ;
LPITEMIDLIST clean ;
HTREEITEM pItem ;
dest = ( LPITEMIDLIST * ) wParam ;
/* TODO: handle shell notifications */
switch ( lParam & ~ SHCNE_INTERRUPT )
{
case SHCNE_MKDIR :
if ( ! SUCCEEDED ( _ReparsePIDL ( dest [ 0 ] , & clean ) ) )
{
ERR ( " Can't reparse PIDL to a valid one \n " ) ;
return FALSE ;
}
NavigateToPIDL ( clean , & pItem , FALSE , TRUE , FALSE ) ;
ILFree ( clean ) ;
break ;
case SHCNE_RMDIR :
DeleteItem ( dest [ 0 ] ) ;
break ;
case SHCNE_RENAMEFOLDER :
if ( ! SUCCEEDED ( _ReparsePIDL ( dest [ 1 ] , & clean ) ) )
{
ERR ( " Can't reparse PIDL to a valid one \n " ) ;
return FALSE ;
}
if ( NavigateToPIDL ( dest [ 0 ] , & pItem , FALSE , FALSE , FALSE ) )
RenameItem ( pItem , clean ) ;
ILFree ( clean ) ;
break ;
case SHCNE_UPDATEDIR :
// We don't take care of this message
TRACE ( " Directory updated \n " ) ;
break ;
default :
TRACE ( " Unhandled message \n " ) ;
}
return TRUE ;
}
LRESULT CExplorerBand : : OnSetFocus ( UINT uMsg , WPARAM wParam , LPARAM lParam , BOOL & bHandled )
{
bFocused = TRUE ;
IUnknown_OnFocusChangeIS ( pSite , reinterpret_cast < IUnknown * > ( this ) , TRUE ) ;
bHandled = FALSE ;
return TRUE ;
}
LRESULT CExplorerBand : : OnKillFocus ( UINT uMsg , WPARAM wParam , LPARAM lParam , BOOL & bHandled )
{
IUnknown_OnFocusChangeIS ( pSite , reinterpret_cast < IUnknown * > ( this ) , FALSE ) ;
bHandled = FALSE ;
return TRUE ;
}
2016-06-03 19:35:33 +00:00
// *** Helper functions ***
HTREEITEM CExplorerBand : : InsertItem ( HTREEITEM hParent , IShellFolder * psfParent , LPITEMIDLIST pElt , LPITEMIDLIST pEltRelative , BOOL bSort )
{
TV_INSERTSTRUCT tvInsert ;
HTREEITEM htiCreated ;
/* Get the attributes of the node */
SFGAOF attrs = SFGAO_STREAM | SFGAO_HASSUBFOLDER ;
HRESULT hr = psfParent - > GetAttributesOf ( 1 , & pEltRelative , & attrs ) ;
if ( FAILED_UNEXPECTEDLY ( hr ) )
return NULL ;
/* Ignore streams */
if ( ( attrs & SFGAO_STREAM ) )
{
TRACE ( " Ignoring stream \n " ) ;
return NULL ;
}
/* Get the name of the node */
WCHAR wszDisplayName [ MAX_PATH ] ;
2017-08-27 09:35:03 +00:00
STRRET strret ;
hr = psfParent - > GetDisplayNameOf ( pEltRelative , SHGDN_INFOLDER , & strret ) ;
if ( FAILED_UNEXPECTEDLY ( hr ) )
return NULL ;
hr = StrRetToBufW ( & strret , pEltRelative , wszDisplayName , MAX_PATH ) ;
if ( FAILED_UNEXPECTEDLY ( hr ) )
2016-06-03 19:35:33 +00:00
return NULL ;
/* Get the icon of the node */
INT iIcon = SHMapPIDLToSystemImageListIndex ( psfParent , pEltRelative , NULL ) ;
NodeInfo * pChildInfo = new NodeInfo ;
if ( ! pChildInfo )
{
ERR ( " Failed to allocate NodeInfo \n " ) ;
return FALSE ;
}
// Store our node info
pChildInfo - > absolutePidl = ILClone ( pElt ) ;
pChildInfo - > relativePidl = ILClone ( pEltRelative ) ;
pChildInfo - > expanded = FALSE ;
// Set up our treeview template
tvInsert . hParent = hParent ;
tvInsert . hInsertAfter = TVI_LAST ;
tvInsert . item . mask = TVIF_PARAM | TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_CHILDREN ;
tvInsert . item . cchTextMax = MAX_PATH ;
tvInsert . item . pszText = wszDisplayName ;
tvInsert . item . iImage = tvInsert . item . iSelectedImage = iIcon ;
tvInsert . item . cChildren = ( attrs & SFGAO_HASSUBFOLDER ) ? 1 : 0 ;
tvInsert . item . lParam = ( LPARAM ) pChildInfo ;
htiCreated = TreeView_InsertItem ( m_hWnd , & tvInsert ) ;
2017-02-05 13:41:45 +00:00
if ( bSort )
{
TVSORTCB sortCallback ;
sortCallback . hParent = hParent ;
sortCallback . lpfnCompare = CompareTreeItems ;
sortCallback . lParam = ( LPARAM ) this ;
SendMessage ( TVM_SORTCHILDRENCB , 0 , ( LPARAM ) & sortCallback ) ;
}
2016-06-03 19:35:33 +00:00
return htiCreated ;
}
2016-07-26 14:31:38 +00:00
/* This is the slow version of the above method */
HTREEITEM CExplorerBand : : InsertItem ( HTREEITEM hParent , LPITEMIDLIST pElt , LPITEMIDLIST pEltRelative , BOOL bSort )
{
CComPtr < IShellFolder > psfFolder ;
HRESULT hr = SHBindToParent ( pElt , IID_PPV_ARG ( IShellFolder , & psfFolder ) , NULL ) ;
if ( FAILED_UNEXPECTEDLY ( hr ) )
return NULL ;
return InsertItem ( hParent , psfFolder , pElt , pEltRelative , bSort ) ;
}
2016-06-03 19:35:33 +00:00
BOOL CExplorerBand : : InsertSubitems ( HTREEITEM hItem , NodeInfo * pNodeInfo )
{
CComPtr < IEnumIDList > pEnumIDList ;
LPITEMIDLIST pidlSub ;
LPITEMIDLIST entry ;
SHCONTF EnumFlags ;
HRESULT hr ;
ULONG fetched ;
ULONG uItemCount ;
CComPtr < IShellFolder > pFolder ;
2017-02-05 13:41:45 +00:00
TVSORTCB sortCallback ;
2016-06-03 19:35:33 +00:00
entry = pNodeInfo - > absolutePidl ;
fetched = 1 ;
uItemCount = 0 ;
EnumFlags = SHCONTF_FOLDERS ;
hr = SHGetFolderLocation ( m_hWnd , CSIDL_DESKTOP , NULL , 0 , & pidlSub ) ;
if ( ! SUCCEEDED ( hr ) )
{
ERR ( " Can't get desktop PIDL ! \n " ) ;
return FALSE ;
}
if ( ! pDesktop - > CompareIDs ( NULL , pidlSub , entry ) )
{
// We are the desktop, so use pDesktop as pFolder
pFolder = pDesktop ;
}
else
{
// Get an IShellFolder of our pidl
hr = pDesktop - > BindToObject ( entry , NULL , IID_PPV_ARG ( IShellFolder , & pFolder ) ) ;
if ( ! SUCCEEDED ( hr ) )
{
ILFree ( pidlSub ) ;
ERR ( " Can't bind folder to desktop ! \n " ) ;
return FALSE ;
}
}
ILFree ( pidlSub ) ;
// TODO: handle hidden folders according to settings !
EnumFlags | = SHCONTF_INCLUDEHIDDEN ;
// Enum through objects
hr = pFolder - > EnumObjects ( NULL , EnumFlags , & pEnumIDList ) ;
// avoid broken IShellFolder implementations that return null pointer with success
if ( ! SUCCEEDED ( hr ) | | ! pEnumIDList )
{
ERR ( " Can't enum the folder ! \n " ) ;
return FALSE ;
}
/* Don't redraw while we add stuff into the tree */
SendMessage ( WM_SETREDRAW , FALSE , 0 ) ;
while ( SUCCEEDED ( pEnumIDList - > Next ( 1 , & pidlSub , & fetched ) ) & & pidlSub & & fetched )
{
LPITEMIDLIST pidlSubComplete ;
pidlSubComplete = ILCombine ( entry , pidlSub ) ;
if ( InsertItem ( hItem , pFolder , pidlSubComplete , pidlSub , FALSE ) )
uItemCount + + ;
ILFree ( pidlSubComplete ) ;
ILFree ( pidlSub ) ;
}
pNodeInfo - > expanded = TRUE ;
2017-02-05 13:41:45 +00:00
/* Let's do sorting */
sortCallback . hParent = hItem ;
sortCallback . lpfnCompare = CompareTreeItems ;
sortCallback . lParam = ( LPARAM ) this ;
SendMessage ( TVM_SORTCHILDRENCB , 0 , ( LPARAM ) & sortCallback ) ;
2016-06-03 19:35:33 +00:00
/* Now we can redraw */
SendMessage ( WM_SETREDRAW , TRUE , 0 ) ;
return ( uItemCount > 0 ) ? TRUE : FALSE ;
2016-06-01 21:12:17 +00:00
}
2016-07-26 14:31:38 +00:00
/**
* Navigate to a given PIDL in the treeview , and return matching tree item handle
* - dest : The absolute PIDL we should navigate in the treeview
* - item : Handle of the tree item matching the PIDL
* - bExpand : expand collapsed nodes in order to find the right element
* - bInsert : insert the element at the right place if we don ' t find it
* - bSelect : select the item after we found it
*/
BOOL CExplorerBand : : NavigateToPIDL ( LPITEMIDLIST dest , HTREEITEM * item , BOOL bExpand , BOOL bInsert ,
BOOL bSelect )
{
HTREEITEM current ;
HTREEITEM tmp ;
HTREEITEM parent ;
BOOL found ;
NodeInfo * nodeData ;
LPITEMIDLIST relativeChild ;
TVITEM tvItem ;
if ( ! item )
return FALSE ;
found = FALSE ;
current = hRoot ;
parent = NULL ;
while ( ! found )
{
nodeData = GetNodeInfo ( current ) ;
if ( ! nodeData )
{
ERR ( " Something has gone wrong, no data associated to node ! \n " ) ;
* item = NULL ;
return FALSE ;
}
// If we found our node, give it back
if ( ! pDesktop - > CompareIDs ( 0 , nodeData - > absolutePidl , dest ) )
{
if ( bSelect )
TreeView_SelectItem ( m_hWnd , current ) ;
* item = current ;
return TRUE ;
}
// Check if we are a parent of the requested item
relativeChild = ILFindChild ( nodeData - > absolutePidl , dest ) ;
if ( relativeChild ! = 0 )
{
// Notify treeview we have children
tvItem . mask = TVIF_CHILDREN ;
tvItem . hItem = current ;
tvItem . cChildren = 1 ;
TreeView_SetItem ( m_hWnd , & tvItem ) ;
// If we can expand and the node isn't expanded yet, do it
if ( bExpand )
{
if ( ! nodeData - > expanded )
InsertSubitems ( current , nodeData ) ;
TreeView_Expand ( m_hWnd , current , TVE_EXPAND ) ;
}
// Try to get a child
tmp = TreeView_GetChild ( m_hWnd , current ) ;
if ( tmp )
{
// We have a child, let's continue with it
parent = current ;
current = tmp ;
continue ;
}
if ( bInsert & & nodeData - > expanded )
{
// Happens when we have to create a subchild inside a child
current = InsertItem ( current , dest , relativeChild , TRUE ) ;
}
// We end up here, without any children, so we found nothing
// Tell the parent node it has children
ZeroMemory ( & tvItem , sizeof ( tvItem ) ) ;
* item = NULL ;
return FALSE ;
}
// Find sibling
tmp = TreeView_GetNextSibling ( m_hWnd , current ) ;
if ( tmp )
{
current = tmp ;
continue ;
}
if ( bInsert )
{
current = InsertItem ( parent , dest , ILFindLastID ( dest ) , TRUE ) ;
* item = current ;
return TRUE ;
}
* item = NULL ;
return FALSE ;
}
return FALSE ;
}
BOOL CExplorerBand : : NavigateToCurrentFolder ( )
{
LPITEMIDLIST explorerPidl ;
CComPtr < IBrowserService > pBrowserService ;
HRESULT hr ;
HTREEITEM dummy ;
BOOL result ;
explorerPidl = NULL ;
hr = IUnknown_QueryService ( pSite , SID_STopLevelBrowser , IID_PPV_ARG ( IBrowserService , & pBrowserService ) ) ;
if ( ! SUCCEEDED ( hr ) )
{
ERR ( " Can't get IBrowserService ! \n " ) ;
return FALSE ;
}
hr = pBrowserService - > GetPidl ( & explorerPidl ) ;
if ( ! SUCCEEDED ( hr ) | | ! explorerPidl )
{
ERR ( " Unable to get browser PIDL ! \n " ) ;
return FALSE ;
}
bNavigating = TRUE ;
/* find PIDL into our explorer */
result = NavigateToPIDL ( explorerPidl , & dummy , TRUE , FALSE , TRUE ) ;
bNavigating = FALSE ;
return result ;
}
2017-02-05 13:41:45 +00:00
BOOL CExplorerBand : : DeleteItem ( LPITEMIDLIST idl )
{
HTREEITEM toDelete ;
TVITEM tvItem ;
HTREEITEM parentNode ;
if ( ! NavigateToPIDL ( idl , & toDelete , FALSE , FALSE , FALSE ) )
return FALSE ;
// TODO: check that the treeview item is really deleted
parentNode = TreeView_GetParent ( m_hWnd , toDelete ) ;
// Navigate to parent when deleting child item
if ( ! pDesktop - > CompareIDs ( 0 , idl , pidlCurrent ) )
{
TreeView_SelectItem ( m_hWnd , parentNode ) ;
}
// Remove the child item
TreeView_DeleteItem ( m_hWnd , toDelete ) ;
// Probe parent to see if it has children
if ( ! TreeView_GetChild ( m_hWnd , parentNode ) )
{
// Decrement parent's child count
ZeroMemory ( & tvItem , sizeof ( tvItem ) ) ;
tvItem . mask = TVIF_CHILDREN ;
tvItem . hItem = parentNode ;
tvItem . cChildren = 0 ;
TreeView_SetItem ( m_hWnd , & tvItem ) ;
}
return TRUE ;
}
BOOL CExplorerBand : : RenameItem ( HTREEITEM toRename , LPITEMIDLIST newPidl )
{
WCHAR wszDisplayName [ MAX_PATH ] ;
TVITEM itemInfo ;
LPCITEMIDLIST relPidl ;
NodeInfo * treeInfo ;
TVSORTCB sortCallback ;
HTREEITEM child ;
ZeroMemory ( & itemInfo , sizeof ( itemInfo ) ) ;
itemInfo . mask = TVIF_PARAM ;
itemInfo . hItem = toRename ;
// Change PIDL associated to the item
relPidl = ILFindLastID ( newPidl ) ;
TreeView_GetItem ( m_hWnd , & itemInfo ) ;
if ( ! itemInfo . lParam )
{
ERR ( " Unable to fetch lParam \n " ) ;
return FALSE ;
}
SendMessage ( WM_SETREDRAW , FALSE , 0 ) ;
treeInfo = ( NodeInfo * ) itemInfo . lParam ;
ILFree ( treeInfo - > absolutePidl ) ;
ILFree ( treeInfo - > relativePidl ) ;
treeInfo - > absolutePidl = ILClone ( newPidl ) ;
treeInfo - > relativePidl = ILClone ( relPidl ) ;
// Change the display name
GetDisplayName ( newPidl , wszDisplayName , MAX_PATH , SHGDN_INFOLDER ) ;
ZeroMemory ( & itemInfo , sizeof ( itemInfo ) ) ;
itemInfo . hItem = toRename ;
itemInfo . mask = TVIF_TEXT ;
itemInfo . pszText = wszDisplayName ;
TreeView_SetItem ( m_hWnd , & itemInfo ) ;
if ( ( child = TreeView_GetChild ( m_hWnd , toRename ) ) ! = NULL )
{
RefreshTreePidl ( child , newPidl ) ;
}
// Sorting
sortCallback . hParent = TreeView_GetParent ( m_hWnd , toRename ) ;
sortCallback . lpfnCompare = CompareTreeItems ;
sortCallback . lParam = ( LPARAM ) this ;
SendMessage ( TVM_SORTCHILDRENCB , 0 , ( LPARAM ) & sortCallback ) ;
SendMessage ( WM_SETREDRAW , TRUE , 0 ) ;
return TRUE ;
}
BOOL CExplorerBand : : RefreshTreePidl ( HTREEITEM tree , LPITEMIDLIST pidlParent )
{
HTREEITEM tmp ;
NodeInfo * pInfo ;
// Update our node data
pInfo = GetNodeInfo ( tree ) ;
if ( ! pInfo )
{
WARN ( " No tree info ! \n " ) ;
return FALSE ;
}
ILFree ( pInfo - > absolutePidl ) ;
pInfo - > absolutePidl = ILCombine ( pidlParent , pInfo - > relativePidl ) ;
if ( ! pInfo - > absolutePidl )
{
WARN ( " PIDL allocation failed \n " ) ;
return FALSE ;
}
// Recursively update children
if ( ( tmp = TreeView_GetChild ( m_hWnd , tree ) ) ! = NULL )
{
RefreshTreePidl ( tmp , pInfo - > absolutePidl ) ;
}
tmp = TreeView_GetNextSibling ( m_hWnd , tree ) ;
while ( tmp ! = NULL )
{
pInfo = GetNodeInfo ( tmp ) ;
if ( ! pInfo )
{
WARN ( " No tree info ! \n " ) ;
continue ;
}
ILFree ( pInfo - > absolutePidl ) ;
pInfo - > absolutePidl = ILCombine ( pidlParent , pInfo - > relativePidl ) ;
tmp = TreeView_GetNextSibling ( m_hWnd , tmp ) ;
}
return TRUE ;
}
// *** Tree item sorting callback ***
int CALLBACK CExplorerBand : : CompareTreeItems ( LPARAM p1 , LPARAM p2 , LPARAM p3 )
{
/*
* We first sort drive letters ( Path root ) , then PIDLs and then regular folder
* display name .
* This is not how Windows sorts item , but it gives decent results .
*/
NodeInfo * info1 ;
NodeInfo * info2 ;
CExplorerBand * pThis ;
WCHAR wszFolder1 [ MAX_PATH ] ;
WCHAR wszFolder2 [ MAX_PATH ] ;
info1 = ( NodeInfo * ) p1 ;
info2 = ( NodeInfo * ) p2 ;
pThis = ( CExplorerBand * ) p3 ;
GetDisplayName ( info1 - > absolutePidl , wszFolder1 , MAX_PATH , SHGDN_FORPARSING ) ;
GetDisplayName ( info2 - > absolutePidl , wszFolder2 , MAX_PATH , SHGDN_FORPARSING ) ;
if ( PathIsRoot ( wszFolder1 ) & & PathIsRoot ( wszFolder2 ) )
{
return lstrcmpiW ( wszFolder1 , wszFolder2 ) ;
}
if ( PathIsRoot ( wszFolder1 ) & & ! PathIsRoot ( wszFolder2 ) )
{
return - 1 ;
}
if ( ! PathIsRoot ( wszFolder1 ) & & PathIsRoot ( wszFolder2 ) )
{
return 1 ;
}
// Now, we compare non-root folders, grab display name
GetDisplayName ( info1 - > absolutePidl , wszFolder1 , MAX_PATH , SHGDN_INFOLDER ) ;
GetDisplayName ( info2 - > absolutePidl , wszFolder2 , MAX_PATH , SHGDN_INFOLDER ) ;
if ( _ILIsSpecialFolder ( info1 - > relativePidl ) & & ! _ILIsSpecialFolder ( info2 - > relativePidl ) )
{
return - 1 ;
}
if ( ! _ILIsSpecialFolder ( info1 - > relativePidl ) & & _ILIsSpecialFolder ( info2 - > relativePidl ) )
{
return 1 ;
}
if ( _ILIsSpecialFolder ( info1 - > relativePidl ) & & ! _ILIsSpecialFolder ( info2 - > relativePidl ) )
{
HRESULT hr ;
hr = pThis - > pDesktop - > CompareIDs ( 0 , info1 - > absolutePidl , info2 - > absolutePidl ) ;
if ( ! hr ) return 0 ;
return ( hr > 0 ) ? - 1 : 1 ;
}
return StrCmpLogicalW ( wszFolder1 , wszFolder2 ) ;
}
2014-12-03 16:11:46 +00:00
// *** IOleWindow methods ***
HRESULT STDMETHODCALLTYPE CExplorerBand : : GetWindow ( HWND * lphwnd )
{
2016-06-01 21:12:17 +00:00
if ( ! lphwnd )
return E_INVALIDARG ;
* lphwnd = m_hWnd ;
return S_OK ;
2014-12-03 16:11:46 +00:00
}
HRESULT STDMETHODCALLTYPE CExplorerBand : : ContextSensitiveHelp ( BOOL fEnterMode )
{
UNIMPLEMENTED ;
return E_NOTIMPL ;
}
// *** IDockingWindow methods ***
HRESULT STDMETHODCALLTYPE CExplorerBand : : CloseDW ( DWORD dwReserved )
{
2016-06-01 21:12:17 +00:00
// We do nothing, we don't have anything to save yet
TRACE ( " CloseDW called \n " ) ;
return S_OK ;
2014-12-03 16:11:46 +00:00
}
HRESULT STDMETHODCALLTYPE CExplorerBand : : ResizeBorderDW ( const RECT * prcBorder , IUnknown * punkToolbarSite , BOOL fReserved )
{
2016-06-01 21:12:17 +00:00
/* Must return E_NOTIMPL according to MSDN */
2014-12-03 16:11:46 +00:00
return E_NOTIMPL ;
}
HRESULT STDMETHODCALLTYPE CExplorerBand : : ShowDW ( BOOL fShow )
{
2016-06-01 21:12:17 +00:00
fVisible = fShow ;
ShowWindow ( fShow ) ;
return S_OK ;
2014-12-03 16:11:46 +00:00
}
// *** IDeskBand methods ***
HRESULT STDMETHODCALLTYPE CExplorerBand : : GetBandInfo ( DWORD dwBandID , DWORD dwViewMode , DESKBANDINFO * pdbi )
{
2016-06-01 21:12:17 +00:00
if ( ! pdbi )
{
return E_INVALIDARG ;
}
this - > dwBandID = dwBandID ;
if ( pdbi - > dwMask & DBIM_MINSIZE )
{
pdbi - > ptMinSize . x = 200 ;
pdbi - > ptMinSize . y = 30 ;
}
if ( pdbi - > dwMask & DBIM_MAXSIZE )
{
pdbi - > ptMaxSize . y = - 1 ;
}
if ( pdbi - > dwMask & DBIM_INTEGRAL )
{
pdbi - > ptIntegral . y = 1 ;
}
if ( pdbi - > dwMask & DBIM_ACTUAL )
{
pdbi - > ptActual . x = 200 ;
pdbi - > ptActual . y = 30 ;
}
if ( pdbi - > dwMask & DBIM_TITLE )
{
2016-07-29 12:05:06 +00:00
if ( ! LoadStringW ( _AtlBaseModule . GetResourceInstance ( ) , IDS_FOLDERSLABEL , pdbi - > wszTitle , _countof ( pdbi - > wszTitle ) ) )
return HRESULT_FROM_WIN32 ( GetLastError ( ) ) ;
2016-06-01 21:12:17 +00:00
}
if ( pdbi - > dwMask & DBIM_MODEFLAGS )
{
pdbi - > dwModeFlags = DBIMF_NORMAL | DBIMF_VARIABLEHEIGHT ;
}
if ( pdbi - > dwMask & DBIM_BKCOLOR )
{
pdbi - > dwMask & = ~ DBIM_BKCOLOR ;
}
return S_OK ;
2014-12-03 16:11:46 +00:00
}
// *** IObjectWithSite methods ***
HRESULT STDMETHODCALLTYPE CExplorerBand : : SetSite ( IUnknown * pUnkSite )
{
2016-06-01 21:12:17 +00:00
HRESULT hr ;
HWND parentWnd ;
if ( pUnkSite = = pSite )
return S_OK ;
TRACE ( " SetSite called \n " ) ;
if ( ! pUnkSite )
{
DestroyExplorerBand ( ) ;
DestroyWindow ( ) ;
m_hWnd = NULL ;
}
if ( pUnkSite ! = pSite )
{
pSite = NULL ;
}
if ( ! pUnkSite )
return S_OK ;
hr = IUnknown_GetWindow ( pUnkSite , & parentWnd ) ;
if ( ! SUCCEEDED ( hr ) )
{
ERR ( " Could not get parent's window ! Status: %08lx \n " , hr ) ;
return E_INVALIDARG ;
}
pSite = pUnkSite ;
if ( m_hWnd )
{
// Change its parent
SetParent ( parentWnd ) ;
}
else
{
HWND wnd = CreateWindow ( WC_TREEVIEW , NULL ,
WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | TVS_HASLINES | TVS_HASBUTTONS | TVS_SHOWSELALWAYS | TVS_EDITLABELS /* | TVS_SINGLEEXPAND*/ , // remove TVS_SINGLEEXPAND for now since it has strange behaviour
0 , 0 , 0 , 0 , parentWnd , NULL , _AtlBaseModule . GetModuleInstance ( ) , NULL ) ;
// Subclass the window
SubclassWindow ( wnd ) ;
// Initialize our treeview now
InitializeExplorerBand ( ) ;
RegisterDragDrop ( m_hWnd , dynamic_cast < IDropTarget * > ( this ) ) ;
}
return S_OK ;
2014-12-03 16:11:46 +00:00
}
HRESULT STDMETHODCALLTYPE CExplorerBand : : GetSite ( REFIID riid , void * * ppvSite )
{
2016-06-01 21:12:17 +00:00
if ( ! ppvSite )
return E_POINTER ;
* ppvSite = pSite ;
return S_OK ;
2014-12-03 16:11:46 +00:00
}
// *** IOleCommandTarget methods ***
HRESULT STDMETHODCALLTYPE CExplorerBand : : QueryStatus ( const GUID * pguidCmdGroup , ULONG cCmds , OLECMD prgCmds [ ] , OLECMDTEXT * pCmdText )
{
UNIMPLEMENTED ;
return E_NOTIMPL ;
}
HRESULT STDMETHODCALLTYPE CExplorerBand : : Exec ( const GUID * pguidCmdGroup , DWORD nCmdID , DWORD nCmdexecopt , VARIANT * pvaIn , VARIANT * pvaOut )
{
UNIMPLEMENTED ;
return E_NOTIMPL ;
}
// *** IServiceProvider methods ***
HRESULT STDMETHODCALLTYPE CExplorerBand : : QueryService ( REFGUID guidService , REFIID riid , void * * ppvObject )
{
2017-03-12 09:48:25 +00:00
/* FIXME: we probably want to handle more services here */
return IUnknown_QueryService ( pSite , SID_SShellBrowser , riid , ppvObject ) ;
2014-12-03 16:11:46 +00:00
}
// *** IInputObject methods ***
HRESULT STDMETHODCALLTYPE CExplorerBand : : UIActivateIO ( BOOL fActivate , LPMSG lpMsg )
{
2016-06-01 21:12:17 +00:00
if ( fActivate )
{
//SetFocus();
SetActiveWindow ( ) ;
}
// TODO: handle message
if ( lpMsg )
{
TranslateMessage ( lpMsg ) ;
DispatchMessage ( lpMsg ) ;
}
return S_OK ;
2014-12-03 16:11:46 +00:00
}
HRESULT STDMETHODCALLTYPE CExplorerBand : : HasFocusIO ( )
{
2016-06-01 21:12:17 +00:00
return bFocused ? S_OK : S_FALSE ;
2014-12-03 16:11:46 +00:00
}
HRESULT STDMETHODCALLTYPE CExplorerBand : : TranslateAcceleratorIO ( LPMSG lpMsg )
{
2016-07-29 10:38:22 +00:00
if ( lpMsg - > hwnd = = m_hWnd )
{
TranslateMessage ( lpMsg ) ;
DispatchMessage ( lpMsg ) ;
return S_OK ;
}
2014-12-03 16:11:46 +00:00
2016-07-29 10:38:22 +00:00
return S_FALSE ;
}
2014-12-03 16:11:46 +00:00
// *** IPersist methods ***
HRESULT STDMETHODCALLTYPE CExplorerBand : : GetClassID ( CLSID * pClassID )
{
2016-06-01 21:12:17 +00:00
if ( ! pClassID )
return E_POINTER ;
memcpy ( pClassID , & CLSID_ExplorerBand , sizeof ( CLSID ) ) ;
return S_OK ;
2014-12-03 16:11:46 +00:00
}
// *** IPersistStream methods ***
HRESULT STDMETHODCALLTYPE CExplorerBand : : IsDirty ( )
{
UNIMPLEMENTED ;
return E_NOTIMPL ;
}
HRESULT STDMETHODCALLTYPE CExplorerBand : : Load ( IStream * pStm )
{
UNIMPLEMENTED ;
return E_NOTIMPL ;
}
HRESULT STDMETHODCALLTYPE CExplorerBand : : Save ( IStream * pStm , BOOL fClearDirty )
{
UNIMPLEMENTED ;
return E_NOTIMPL ;
}
HRESULT STDMETHODCALLTYPE CExplorerBand : : GetSizeMax ( ULARGE_INTEGER * pcbSize )
{
2017-02-05 13:41:45 +00:00
// TODO: calculate max size
2014-12-03 16:11:46 +00:00
UNIMPLEMENTED ;
return E_NOTIMPL ;
}
// *** IWinEventHandler methods ***
HRESULT STDMETHODCALLTYPE CExplorerBand : : OnWinEvent ( HWND hWnd , UINT uMsg , WPARAM wParam , LPARAM lParam , LRESULT * theResult )
{
2016-07-26 15:09:04 +00:00
BOOL bHandled ;
2018-03-08 09:28:33 +00:00
LRESULT result ;
2016-06-03 19:35:33 +00:00
if ( uMsg = = WM_NOTIFY )
{
NMHDR * pNotifyHeader = ( NMHDR * ) lParam ;
switch ( pNotifyHeader - > code )
{
case TVN_ITEMEXPANDING :
2018-03-08 09:28:33 +00:00
result = OnTreeItemExpanding ( ( LPNMTREEVIEW ) lParam ) ;
if ( theResult )
* theResult = result ;
2016-06-03 19:35:33 +00:00
break ;
2016-06-03 21:25:34 +00:00
case TVN_SELCHANGED :
OnSelectionChanged ( ( LPNMTREEVIEW ) lParam ) ;
break ;
2017-02-05 13:41:45 +00:00
case TVN_DELETEITEM :
OnTreeItemDeleted ( ( LPNMTREEVIEW ) lParam ) ;
break ;
2016-07-26 15:09:04 +00:00
case NM_RCLICK :
OnContextMenu ( WM_CONTEXTMENU , ( WPARAM ) m_hWnd , GetMessagePos ( ) , bHandled ) ;
2018-03-08 09:28:33 +00:00
if ( theResult )
* theResult = 1 ;
2016-07-26 15:09:04 +00:00
break ;
2017-02-05 13:41:45 +00:00
case TVN_BEGINDRAG :
case TVN_BEGINRDRAG :
OnTreeItemDragging ( ( LPNMTREEVIEW ) lParam , pNotifyHeader - > code = = TVN_BEGINRDRAG ) ;
2016-07-26 15:09:04 +00:00
case TVN_BEGINLABELEDITW :
{
// TODO: put this in a function ? (mostly copypasta from CDefView)
DWORD dwAttr = SFGAO_CANRENAME ;
LPNMTVDISPINFO dispInfo = ( LPNMTVDISPINFO ) lParam ;
CComPtr < IShellFolder > pParent ;
LPCITEMIDLIST pChild ;
HRESULT hr ;
2018-03-08 09:28:33 +00:00
if ( theResult )
* theResult = 1 ;
2016-07-26 15:09:04 +00:00
NodeInfo * info = GetNodeInfo ( dispInfo - > item . hItem ) ;
if ( ! info )
return E_FAIL ;
hr = SHBindToParent ( info - > absolutePidl , IID_PPV_ARG ( IShellFolder , & pParent ) , & pChild ) ;
if ( ! SUCCEEDED ( hr ) | | ! pParent . p )
return E_FAIL ;
hr = pParent - > GetAttributesOf ( 1 , & pChild , & dwAttr ) ;
2018-03-08 09:28:33 +00:00
if ( SUCCEEDED ( hr ) & & ( dwAttr & SFGAO_CANRENAME ) & & theResult )
2016-07-26 15:09:04 +00:00
* theResult = 0 ;
return S_OK ;
}
case TVN_ENDLABELEDITW :
{
LPNMTVDISPINFO dispInfo = ( LPNMTVDISPINFO ) lParam ;
NodeInfo * info = GetNodeInfo ( dispInfo - > item . hItem ) ;
HRESULT hr ;
2018-03-08 09:28:33 +00:00
if ( theResult )
* theResult = 0 ;
2016-07-26 15:09:04 +00:00
if ( dispInfo - > item . pszText )
{
LPITEMIDLIST pidlNew ;
CComPtr < IShellFolder > pParent ;
LPCITEMIDLIST pidlChild ;
hr = SHBindToParent ( info - > absolutePidl , IID_PPV_ARG ( IShellFolder , & pParent ) , & pidlChild ) ;
2019-04-28 18:30:18 +00:00
if ( ! SUCCEEDED ( hr ) | | ! pParent . p )
return E_FAIL ;
2016-07-26 15:09:04 +00:00
hr = pParent - > SetNameOf ( 0 , pidlChild , dispInfo - > item . pszText , SHGDN_INFOLDER , & pidlNew ) ;
if ( SUCCEEDED ( hr ) & & pidlNew )
{
CComPtr < IPersistFolder2 > pPersist ;
LPITEMIDLIST pidlParent , pidlNewAbs ;
hr = pParent - > QueryInterface ( IID_PPV_ARG ( IPersistFolder2 , & pPersist ) ) ;
if ( ! SUCCEEDED ( hr ) )
return E_FAIL ;
hr = pPersist - > GetCurFolder ( & pidlParent ) ;
if ( ! SUCCEEDED ( hr ) )
return E_FAIL ;
pidlNewAbs = ILCombine ( pidlParent , pidlNew ) ;
// Navigate to our new location
UpdateBrowser ( pidlNewAbs ) ;
2019-09-10 10:11:34 +00:00
ILFree ( pidlParent ) ;
2016-07-26 15:09:04 +00:00
ILFree ( pidlNewAbs ) ;
ILFree ( pidlNew ) ;
2018-03-08 09:28:33 +00:00
if ( theResult )
* theResult = 1 ;
2016-07-26 15:09:04 +00:00
}
return S_OK ;
}
}
2016-06-03 19:35:33 +00:00
default :
break ;
}
}
return S_OK ;
2014-12-03 16:11:46 +00:00
}
HRESULT STDMETHODCALLTYPE CExplorerBand : : IsWindowOwner ( HWND hWnd )
{
2016-06-01 21:12:17 +00:00
return ( hWnd = = m_hWnd ) ? S_OK : S_FALSE ;
2014-12-03 16:11:46 +00:00
}
// *** IBandNavigate methods ***
HRESULT STDMETHODCALLTYPE CExplorerBand : : Select ( long paramC )
{
UNIMPLEMENTED ;
return E_NOTIMPL ;
}
// *** INamespaceProxy ***
HRESULT STDMETHODCALLTYPE CExplorerBand : : GetNavigateTarget ( long paramC , long param10 , long param14 )
{
UNIMPLEMENTED ;
return E_NOTIMPL ;
}
HRESULT STDMETHODCALLTYPE CExplorerBand : : Invoke ( long paramC )
{
UNIMPLEMENTED ;
return E_NOTIMPL ;
}
HRESULT STDMETHODCALLTYPE CExplorerBand : : OnSelectionChanged ( long paramC )
{
UNIMPLEMENTED ;
return E_NOTIMPL ;
}
HRESULT STDMETHODCALLTYPE CExplorerBand : : RefreshFlags ( long paramC , long param10 , long param14 )
{
UNIMPLEMENTED ;
return E_NOTIMPL ;
}
HRESULT STDMETHODCALLTYPE CExplorerBand : : CacheItem ( long paramC )
{
UNIMPLEMENTED ;
return E_NOTIMPL ;
}
// *** IDispatch methods ***
HRESULT STDMETHODCALLTYPE CExplorerBand : : GetTypeInfoCount ( UINT * pctinfo )
{
UNIMPLEMENTED ;
return E_NOTIMPL ;
}
HRESULT STDMETHODCALLTYPE CExplorerBand : : GetTypeInfo ( UINT iTInfo , LCID lcid , ITypeInfo * * ppTInfo )
{
UNIMPLEMENTED ;
return E_NOTIMPL ;
}
HRESULT STDMETHODCALLTYPE CExplorerBand : : GetIDsOfNames ( REFIID riid , LPOLESTR * rgszNames , UINT cNames , LCID lcid , DISPID * rgDispId )
{
UNIMPLEMENTED ;
return E_NOTIMPL ;
}
HRESULT STDMETHODCALLTYPE CExplorerBand : : Invoke ( DISPID dispIdMember , REFIID riid , LCID lcid , WORD wFlags , DISPPARAMS * pDispParams , VARIANT * pVarResult , EXCEPINFO * pExcepInfo , UINT * puArgErr )
{
2016-07-26 14:31:38 +00:00
switch ( dispIdMember )
{
case DISPID_DOWNLOADCOMPLETE :
case DISPID_NAVIGATECOMPLETE2 :
TRACE ( " DISPID_NAVIGATECOMPLETE2 received \n " ) ;
NavigateToCurrentFolder ( ) ;
return S_OK ;
}
TRACE ( " Unknown dispid requested: %08x \n " , dispIdMember ) ;
return E_INVALIDARG ;
2014-12-03 16:11:46 +00:00
}
2016-06-01 20:13:27 +00:00
// *** IDropTarget methods ***
HRESULT STDMETHODCALLTYPE CExplorerBand : : DragEnter ( IDataObject * pObj , DWORD glfKeyState , POINTL pt , DWORD * pdwEffect )
{
2017-02-05 13:41:45 +00:00
ERR ( " Entering drag \n " ) ;
pCurObject = pObj ;
oldSelected = TreeView_GetSelection ( m_hWnd ) ;
return DragOver ( glfKeyState , pt , pdwEffect ) ;
2016-06-01 20:13:27 +00:00
}
HRESULT STDMETHODCALLTYPE CExplorerBand : : DragOver ( DWORD glfKeyState , POINTL pt , DWORD * pdwEffect )
{
2017-02-05 13:41:45 +00:00
TVHITTESTINFO info ;
CComPtr < IShellFolder > pShellFldr ;
NodeInfo * nodeInfo ;
//LPCITEMIDLIST pChild;
HRESULT hr ;
info . pt . x = pt . x ;
info . pt . y = pt . y ;
info . flags = TVHT_ONITEM ;
info . hItem = NULL ;
ScreenToClient ( & info . pt ) ;
// Move to the item selected by the treeview (don't change right pane)
TreeView_HitTest ( m_hWnd , & info ) ;
if ( info . hItem )
{
bNavigating = TRUE ;
TreeView_SelectItem ( m_hWnd , info . hItem ) ;
bNavigating = FALSE ;
// Delegate to shell folder
if ( pDropTarget & & info . hItem ! = childTargetNode )
{
pDropTarget = NULL ;
}
if ( info . hItem ! = childTargetNode )
{
nodeInfo = GetNodeInfo ( info . hItem ) ;
if ( ! nodeInfo )
return E_FAIL ;
#if 0
hr = SHBindToParent ( nodeInfo - > absolutePidl , IID_PPV_ARG ( IShellFolder , & pShellFldr ) , & pChild ) ;
if ( ! SUCCEEDED ( hr ) )
return E_FAIL ;
hr = pShellFldr - > GetUIObjectOf ( m_hWnd , 1 , & pChild , IID_IDropTarget , NULL , reinterpret_cast < void * * > ( & pDropTarget ) ) ;
if ( ! SUCCEEDED ( hr ) )
return E_FAIL ;
# endif
if ( _ILIsDesktop ( nodeInfo - > absolutePidl ) )
pShellFldr = pDesktop ;
else
{
hr = pDesktop - > BindToObject ( nodeInfo - > absolutePidl , 0 , IID_PPV_ARG ( IShellFolder , & pShellFldr ) ) ;
if ( ! SUCCEEDED ( hr ) )
{
/* Don't allow dnd since we couldn't get our folder object */
ERR ( " Can't bind to folder object \n " ) ;
* pdwEffect = DROPEFFECT_NONE ;
return E_FAIL ;
}
}
hr = pShellFldr - > CreateViewObject ( m_hWnd , IID_PPV_ARG ( IDropTarget , & pDropTarget ) ) ;
if ( ! SUCCEEDED ( hr ) )
{
/* Don't allow dnd since we couldn't get our drop target */
ERR ( " Can't get drop target for folder object \n " ) ;
* pdwEffect = DROPEFFECT_NONE ;
return E_FAIL ;
}
hr = pDropTarget - > DragEnter ( pCurObject , glfKeyState , pt , pdwEffect ) ;
childTargetNode = info . hItem ;
}
2017-02-13 15:04:03 +00:00
if ( pDropTarget )
{
hr = pDropTarget - > DragOver ( glfKeyState , pt , pdwEffect ) ;
}
2017-02-05 13:41:45 +00:00
}
else
{
childTargetNode = NULL ;
pDropTarget = NULL ;
* pdwEffect = DROPEFFECT_NONE ;
}
return S_OK ;
2016-06-01 20:13:27 +00:00
}
HRESULT STDMETHODCALLTYPE CExplorerBand : : DragLeave ( )
{
2017-02-05 13:41:45 +00:00
bNavigating = TRUE ;
TreeView_SelectItem ( m_hWnd , oldSelected ) ;
bNavigating = FALSE ;
childTargetNode = NULL ;
if ( pCurObject )
{
pCurObject = NULL ;
}
return S_OK ;
2016-06-01 20:13:27 +00:00
}
HRESULT STDMETHODCALLTYPE CExplorerBand : : Drop ( IDataObject * pObj , DWORD glfKeyState , POINTL pt , DWORD * pdwEffect )
{
2017-02-05 13:41:45 +00:00
if ( ! pDropTarget )
return E_FAIL ;
pDropTarget - > Drop ( pObj , glfKeyState , pt , pdwEffect ) ;
DragLeave ( ) ;
return S_OK ;
2016-06-01 20:13:27 +00:00
}
// *** IDropSource methods ***
HRESULT STDMETHODCALLTYPE CExplorerBand : : QueryContinueDrag ( BOOL fEscapePressed , DWORD grfKeyState )
{
2017-02-05 13:41:45 +00:00
if ( fEscapePressed )
return DRAGDROP_S_CANCEL ;
if ( ( grfKeyState & MK_LBUTTON ) | | ( grfKeyState & MK_RBUTTON ) )
return S_OK ;
return DRAGDROP_S_DROP ;
2016-06-01 20:13:27 +00:00
}
HRESULT STDMETHODCALLTYPE CExplorerBand : : GiveFeedback ( DWORD dwEffect )
{
2017-02-05 13:41:45 +00:00
return DRAGDROP_S_USEDEFAULTCURSORS ;
2016-06-01 20:13:27 +00:00
}