// OXShellFolderTree.cpp : implementation file // // Version: 9.3 #include "stdafx.h" #include "OXShellFolderTree.h" #include "OXMainRes.h" #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif // image index for the desktop item const int IDI_DESKTOPIMAGE_INDEX=34; // delimiter for filter const TCHAR OXSHELLTREE_EXT_DELIMITER=_T('|'); ///////////////////////////////////////////////////////////////////////////// // COXShellFolderTree COXShellFolderTree::COXShellFolderTree(BOOL bEnableContextMenu/*=TRUE*/, BOOL bOnlyFileSystemFolders/*=TRUE*/, BOOL bNotifyError/*=TRUE*/, BOOL bShowDesktopItem/*=TRUE*/, BOOL bShowFiles/*=FALSE*/, LPCTSTR lpszFilter/*=_T("")*/) : m_hShellImageList(NULL), m_bEditingItem(FALSE), m_nRedraw(0), m_hContextMenuItem(NULL), m_bNoRestoreContextMenuItem(FALSE) { SetEnableContextMenu(bEnableContextMenu); SetOnlyFileSystemFolders(bOnlyFileSystemFolders); SetNotifyError(bNotifyError); SetShowFiles(bShowFiles); SetShowDesktopItem(bShowDesktopItem); SetFilter(lpszFilter); } COXShellFolderTree::~COXShellFolderTree() { } BEGIN_MESSAGE_MAP(COXShellFolderTree, CTreeCtrl) //{{AFX_MSG_MAP(COXShellFolderTree) ON_WM_LBUTTONDOWN() ON_WM_RBUTTONDOWN() ON_WM_RBUTTONUP() ON_WM_MOUSEMOVE() ON_WM_CANCELMODE() ON_WM_CAPTURECHANGED() // ON_NOTIFY_REFLECT_EX(NM_RCLICK, OnRClick) ON_NOTIFY_REFLECT_EX(NM_DBLCLK, OnDblClick) ON_NOTIFY_REFLECT_EX(TVN_ITEMEXPANDING, OnItemExpanding) ON_NOTIFY_REFLECT_EX(TVN_BEGINLABELEDIT, OnBeginLabelEdit) ON_NOTIFY_REFLECT_EX(TVN_ENDLABELEDIT, OnEndLabelEdit) #ifdef OXSHELLTREE_WATCHFORDIR ON_MESSAGE(WM_OX_FILE_NOTIFY, OnDirChangeNotify) #endif // OXSHELLTREE_WATCHFORDIR //}}AFX_MSG_MAP END_MESSAGE_MAP() ///////////////////////////////////////////////////////////////////////////// // COXShellFolderTree message handlers BOOL COXShellFolderTree::PreCreateWindow(CREATESTRUCT& cs) { // TODO: Add your specialized code here and/or call the base class return CTreeCtrl::PreCreateWindow(cs); } void COXShellFolderTree::PreSubclassWindow() { // TODO: Add your specialized code here and/or call the base class CTreeCtrl::PreSubclassWindow(); } BOOL COXShellFolderTree::InitializeTree(CString sFolderStartFrom/*=_T("")*/) { ASSERT_VALID(this); if(!::IsWindow(GetSafeHwnd())) return FALSE; SetNotifyError(m_bNotifyError); // use system image list as tree control's image list // int nDesktopItemImageIndex=0; if((HIMAGELIST)m_imageList!=NULL) m_imageList.DeleteImageList(); m_mapImageIndex.RemoveAll(); // retrieve Shell image list if(m_hShellImageList==NULL) { m_hShellImageList=m_navigator.GetShellImageList(); if(m_hShellImageList==NULL) return FALSE; } IMAGEINFO imageInfo; VERIFY(ImageList_GetImageInfo(m_hShellImageList,0,&imageInfo)); CRect rect(imageInfo.rcImage); VERIFY(m_imageList.Create(rect.Width(),rect.Height(),ILC_COLOR32|ILC_MASK,0,0)); HICON hIcon=::ExtractIcon( AfxGetInstanceHandle(),_T("shell32.dll"),IDI_DESKTOPIMAGE_INDEX); ASSERT(hIcon!=NULL); nDesktopItemImageIndex=m_imageList.Add(hIcon); ASSERT(nDesktopItemImageIndex!=-1); VERIFY(::DestroyIcon(hIcon)); SetImageList(&m_imageList,TVSIL_NORMAL); // now let's fill TreeCtrl with shell namespace objects that is located // in sFolderStartFrom folder. If sFolderStartFrom is NULL then we use // Desktop as start folder // shell folder returned by that function have to be released afterwards LPSHELLFOLDER lpFolder=m_navigator.GetShellFolder(sFolderStartFrom); if(lpFolder==NULL) return FALSE; LPITEMIDLIST lpidlFull; if(!m_navigator.GetShellFolderFullIDL(sFolderStartFrom,&lpidlFull)) { // Release the folder lpFolder->Release(); return FALSE; } // unselect item SelectItem(NULL); // Clean up TreeCtrl DeleteAllItems(); HTREEITEM htiRoot=TVI_ROOT; if(m_bShowDesktopItem) { // structures for inserting new items in our TreeCtrl TV_INSERTSTRUCT tvins; tvins.item.mask=TVIF_TEXT|TVIF_STATE|TVIF_IMAGE|TVIF_SELECTEDIMAGE| TVIF_PARAM|TVIF_CHILDREN; // NAMESPACEOBJECT object is not defined for Desktop item tvins.item.lParam=(LPARAM)NULL; // Desktop item always has subfolders tvins.item.cChildren=1; // display name CString sItem; VERIFY(sItem.LoadString(IDS_OX_SHELLFOLDERTREEDESKTOP));//"Desktop" tvins.item.pszText=(LPTSTR) (LPCTSTR) sItem; // Request icon index for normal state tvins.item.iImage=nDesktopItemImageIndex; tvins.item.iSelectedImage=nDesktopItemImageIndex; // expanded from the beginning tvins.item.state=TVIS_EXPANDED|TVIS_EXPANDEDONCE; tvins.item.stateMask=tvins.item.state; // We collected all information we needed. It's time to insert // new item in our TreeCtrl. tvins.hInsertAfter=TVI_LAST; tvins.hParent=TVI_ROOT; htiRoot=InsertItem(&tvins); ASSERT(htiRoot!=NULL); } // Fill TreeCtrl with objects in lpFolder folder. FillTreeWithSubfolders(htiRoot,lpFolder,lpidlFull); lpFolder->Release(); // We cannot sort item using just their display names. We have to // request shell to sort items. SortChildren(TVI_ROOT); return TRUE; } BOOL COXShellFolderTree::OpenFolder(CString sFolderToOpen, BOOL bExpand/*=FALSE*/) { if(sFolderToOpen.IsEmpty()) return TRUE; LPITEMIDLIST lpidlFull; if(!m_navigator.GetShellFolderFullIDL(sFolderToOpen,&lpidlFull)) return FALSE; return OpenFolder(lpidlFull,bExpand); } BOOL COXShellFolderTree::OpenFolder(const LPITEMIDLIST lpFullIDL, BOOL bExpand/*=FALSE*/) { if(lpFullIDL==NULL) return FALSE; HTREEITEM hItem=GetRootItem(); ASSERT(hItem!=NULL); if(m_bShowDesktopItem) { hItem=GetNextItem(hItem,TVGN_CHILD); ASSERT(hItem!=NULL); } LPITEMIDLIST pidlCopy=lpFullIDL; LPITEMIDLIST pidlNext=NULL; BOOL bFound=FALSE; while(pidlCopy!=NULL) { pidlNext=m_navigator.GetNextIDLItem(pidlCopy); bFound=FALSE; while(hItem!=NULL) { // Get folder info associated with item LPNAMESPACEOBJECT lpNameSpaceObject= (LPNAMESPACEOBJECT)GetItemData(hItem); ASSERT(lpNameSpaceObject!=NULL); if(pidlCopy->mkid.cb==lpNameSpaceObject-> lpRelativeIDL->mkid.cb && memcmp((void*)pidlCopy,(void*)lpNameSpaceObject-> lpRelativeIDL,pidlCopy->mkid.cb)==0) { bFound=TRUE; if(pidlNext!=NULL) { Expand(hItem,TVE_EXPAND); hItem=GetNextItem(hItem,TVGN_CHILD); } else { SelectItem(hItem); if(bExpand) Expand(hItem,TVE_EXPAND); EnsureVisible(hItem); } break; } else hItem=GetNextItem(hItem,TVGN_NEXT); } if(!bFound) break; pidlCopy=pidlNext; } return bFound; } HTREEITEM COXShellFolderTree::FindFolder(CString sFolderToFind) { if(sFolderToFind.IsEmpty()) return NULL; LPITEMIDLIST lpidlFull; if(!m_navigator.GetShellFolderFullIDL(sFolderToFind,&lpidlFull)) return NULL; return FindFolder(lpidlFull); } HTREEITEM COXShellFolderTree::FindFolder(const LPITEMIDLIST lpFullIDL) { if(lpFullIDL==NULL) return NULL; HTREEITEM hItem=GetRootItem(); ASSERT(hItem!=NULL); if(m_bShowDesktopItem) { hItem=GetNextItem(hItem,TVGN_CHILD); ASSERT(hItem!=NULL); } LPITEMIDLIST pidlCopy=lpFullIDL; LPITEMIDLIST pidlNext=NULL; HTREEITEM hFoundItem=NULL; while(pidlCopy!=NULL) { pidlNext=m_navigator.GetNextIDLItem(pidlCopy); hFoundItem=NULL; while(hItem!=NULL ) { // Get folder info associated with item LPNAMESPACEOBJECT lpNameSpaceObject= (LPNAMESPACEOBJECT)GetItemData(hItem); ASSERT(lpNameSpaceObject!=NULL); if(pidlCopy->mkid.cb==lpNameSpaceObject-> lpRelativeIDL->mkid.cb && memcmp((void*)pidlCopy,(void*)lpNameSpaceObject-> lpRelativeIDL,pidlCopy->mkid.cb)==0) { hFoundItem=hItem; if(pidlNext!=NULL) { if(GetItemState(hItem,TVIS_EXPANDED)&TVIS_EXPANDED) hItem=GetNextItem(hItem,TVGN_CHILD); else hFoundItem=NULL; } break; } else hItem=GetNextItem(hItem,TVGN_NEXT); } if(hFoundItem==NULL) break; pidlCopy=pidlNext; } return hFoundItem; } CString COXShellFolderTree::GetFullPath(HTREEITEM hItem) const { ASSERT(hItem!=NULL); // retrieve associated structure LPNAMESPACEOBJECT lpNameSpaceObject=(LPNAMESPACEOBJECT)GetItemData(hItem); if(lpNameSpaceObject==NULL) return _T(""); return m_navigator.GetFullPath(lpNameSpaceObject->lpFullIDL); } BOOL COXShellFolderTree::FillTreeWithSubfolders(HTREEITEM htiParent, const LPSHELLFOLDER lpFolder, const LPITEMIDLIST lpFullIDL) { ASSERT(lpFolder); CWaitCursor wait; // Initialize enumeration of objects within given folder if(!m_navigator.InitObjectsEnumerator(lpFolder,lpFullIDL)) return FALSE; // After we get pointer to IEnumIDList interface we can use its methods to // retrieve information about all objects of given lpFolder // // structures for inserting new items in our TreeCtrl TV_ITEM tvi; TV_INSERTSTRUCT tvins; BOOL bLast; // Loop through folders and objects that have subfolders LPNAMESPACEOBJECT lpNameSpaceObject= m_navigator.EnumerateObjectNext(bLast); while(!bLast) { BOOL bQualified=TRUE; if(GetOnlyFileSystemFolders()) { if(GetShowFiles()) bQualified=lpNameSpaceObject!=NULL && (lpNameSpaceObject->dwFlags&SFGAO_FILESYSANCESTOR || lpNameSpaceObject->dwFlags&SFGAO_FILESYSTEM); else bQualified=lpNameSpaceObject!=NULL && ((lpNameSpaceObject->dwFlags&SFGAO_FILESYSANCESTOR && lpNameSpaceObject->dwFlags&SFGAO_HASSUBFOLDER) || (lpNameSpaceObject->dwFlags&SFGAO_FILESYSTEM && lpNameSpaceObject->dwFlags&SFGAO_FOLDER)); } else { if(GetShowFiles()) bQualified=lpNameSpaceObject!=NULL; else bQualified=lpNameSpaceObject!=NULL && (lpNameSpaceObject->dwFlags&SFGAO_HASSUBFOLDER || lpNameSpaceObject->dwFlags&SFGAO_FOLDER); } if(bQualified && (lpNameSpaceObject->dwFlags&SFGAO_FILESYSTEM) && !(lpNameSpaceObject->dwFlags&SFGAO_FOLDER)) { bQualified=IsMatchingFilter(lpNameSpaceObject->szDisplayName); } if(bQualified && IsQualified(lpNameSpaceObject)) { // We define text, image (including selected) and lParam value // which is going to be a pointer to NAMESPACEOBJECT structure tvi.mask=TVIF_TEXT|TVIF_IMAGE|TVIF_SELECTEDIMAGE|TVIF_PARAM; // NAMESPACEOBJECT structure is already filled tvi.lParam=(LPARAM)lpNameSpaceObject; // if object has subfolders then we define the corresponding // tree item as one that has children if(lpNameSpaceObject->dwFlags&SFGAO_FILESYSANCESTOR || lpNameSpaceObject->dwFlags&SFGAO_HASSUBFOLDER || (GetShowFiles() && lpNameSpaceObject->dwFlags&SFGAO_FOLDER)) { tvi.cChildren=1; tvi.mask|=TVIF_CHILDREN; } // display name tvi.pszText=lpNameSpaceObject->szDisplayName; // images int nTreeImageIndex=-1; if(!m_mapImageIndex.Lookup(lpNameSpaceObject->nImageSmall,nTreeImageIndex)) { nTreeImageIndex=PtrToInt(m_mapImageIndex.GetCount())+1; m_mapImageIndex.SetAt(lpNameSpaceObject->nImageSmall,nTreeImageIndex); HICON hIcon=ImageList_ExtractIcon( 0,m_hShellImageList,lpNameSpaceObject->nImageSmall); ASSERT(hIcon!=NULL); tvi.iImage=m_imageList.Add(hIcon); ASSERT(tvi.iImage!=-1 && tvi.iImage==nTreeImageIndex); VERIFY(::DestroyIcon(hIcon)); } else { tvi.iImage=nTreeImageIndex; } nTreeImageIndex=-1; if(!m_mapImageIndex.Lookup(lpNameSpaceObject->nImageSelectedSmall, nTreeImageIndex)) { nTreeImageIndex=PtrToInt(m_mapImageIndex.GetCount())+1; m_mapImageIndex.SetAt(lpNameSpaceObject->nImageSelectedSmall, nTreeImageIndex); HICON hIcon=ImageList_ExtractIcon( 0,m_hShellImageList,lpNameSpaceObject->nImageSelectedSmall); ASSERT(hIcon!=NULL); tvi.iSelectedImage=m_imageList.Add(hIcon); ASSERT(tvi.iSelectedImage!=-1 && tvi.iSelectedImage==nTreeImageIndex); VERIFY(::DestroyIcon(hIcon)); } else { tvi.iSelectedImage=nTreeImageIndex; } /////////////////////////// // We collected all information we needed. It's time to insert // new item in our TreeCtrl. tvins.item=tvi; tvins.hInsertAfter=TVI_LAST; tvins.hParent=htiParent; VERIFY(InsertItem(&tvins)!=NULL); } lpNameSpaceObject=m_navigator.EnumerateObjectNext(bLast); } // Release enumerator m_navigator.ReleaseObjectsEnumerator(); return TRUE; } BOOL COXShellFolderTree::OnItemExpanding(NMHDR* pNMHDR, LRESULT* pResult) { NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR; // TODO: Add your control notification handler code here *pResult = 0; ASSERT(pNMTreeView->itemNew.hItem!=NULL); // If folder at least once was expanded then it was already populated if((pNMTreeView->itemNew.state & TVIS_EXPANDEDONCE)) return TRUE; // retrieve associated structure LPNAMESPACEOBJECT lpNameSpaceObject= (LPNAMESPACEOBJECT)pNMTreeView->itemNew.lParam; ASSERT(lpNameSpaceObject!=NULL || GetShowDesktopItem()); CWaitCursor waitCusor; // get shell folder object from saved PIDLs LPSHELLFOLDER lpsfExpanded=m_navigator. GetShellFolder(lpNameSpaceObject->lpsfParent, lpNameSpaceObject->lpRelativeIDL); if(lpsfExpanded!=NULL) { // add new items FillTreeWithSubfolders(pNMTreeView->itemNew.hItem,lpsfExpanded, lpNameSpaceObject->lpFullIDL); SortChildren(pNMTreeView->itemNew.hItem); } #ifdef OXSHELLTREE_WATCHFORDIR if(lpNameSpaceObject!=NULL && ((lpNameSpaceObject->dwFlags&SFGAO_FILESYSANCESTOR)==SFGAO_FILESYSANCESTOR) ||(lpNameSpaceObject->dwFlags&SFGAO_FILESYSTEM)==SFGAO_FILESYSTEM) { CString sCurrentFolder=GetFullPath(pNMTreeView->itemNew.hItem); if(!sCurrentFolder.IsEmpty()) { if(m_fileWatcher.AddWatch(sCurrentFolder,/*FALSE*/TRUE, COXFileWatcher::OXFileWatchChangeDirName|(GetShowFiles() ? COXFileWatcher::OXFileWatchChangeFileName : NULL))) { m_fileWatcher.EnableWindowNotification(sCurrentFolder,this,TRUE); } else { TRACE(_T("COXShellFolderTree::OnItemExpanding: failed to set a file watcher for the expanding folder\n")); } } else { HTREEITEM hChildItem=this->GetNextItem( pNMTreeView->itemNew.hItem,TVGN_CHILD); while (hChildItem) { CString sFolder=GetFullPath(hChildItem); if (!sFolder.IsEmpty()) { if(m_fileWatcher.AddWatch(sFolder,/*FALSE*/TRUE, COXFileWatcher::OXFileWatchChangeDirName|(GetShowFiles() ? COXFileWatcher::OXFileWatchChangeFileName : NULL))) { m_fileWatcher.EnableWindowNotification(sFolder,this,TRUE); } else { TRACE(_T("COXShellFolderTree::OnItemExpanding: failed to set a file watcher for the expanding folder\n")); } } hChildItem=this->GetNextItem( hChildItem,TVGN_NEXT); } } } #endif // OXSHELLTREE_WATCHFORDIR return TRUE; } BOOL COXShellFolderTree::OnRClick(NMHDR* pNMHDR, LRESULT* pResult) { UNREFERENCED_PARAMETER(pNMHDR); // TODO: Add your control notification handler code here *pResult = 0; if(GetEnableContextMenu()) { // On right click we display context menu for any item // // Get the item that was right clicked in screen coordinates CPoint ptMouseCursor; if(!::GetCursorPos(&ptMouseCursor)) { TRACE(_T("COXShellFolderTree::OnRClick: GetCursorPos() failed\n")); return FALSE; } ScreenToClient(&ptMouseCursor); // Use HitTest function to define the tree item by coordinate TV_HITTESTINFO tvhti; tvhti.pt=ptMouseCursor; HitTest(&tvhti); if(tvhti.hItem) { // Get folder info associated with item LPNAMESPACEOBJECT lpNameSpaceObject= (LPNAMESPACEOBJECT)GetItemData(tvhti.hItem); if(m_bShowDesktopItem && lpNameSpaceObject==NULL) { return FALSE; } ASSERT(lpNameSpaceObject!=NULL); DWORD dwMenuFlags=CMF_EXPLORE | ((((GetStyle()&TVS_EDITLABELS)==TVS_EDITLABELS) && ((lpNameSpaceObject->dwFlags&SFGAO_CANRENAME)==SFGAO_CANRENAME)) ? CMF_CANRENAME : NULL); HMENU hMenu= m_navigator.GetObjectContextMenu(lpNameSpaceObject->lpsfParent, lpNameSpaceObject->lpRelativeIDL,dwMenuFlags); if(hMenu==NULL) { TRACE(_T("COXShellFolderTree::OnRClick: GetObjectContextMenu() failed\n")); return FALSE; } // Display popup menu using Windows API TrackPopupMenu function because // it provides easy way of getting selected menu item. We need to set // screen coordinates of the point where popup menu should be displayed. ClientToScreen(&ptMouseCursor); BOOL nCmdID=::TrackPopupMenu(hMenu, TPM_LEFTALIGN|TPM_RETURNCMD|TPM_RIGHTBUTTON, ptMouseCursor.x,ptMouseCursor.y,0,GetSafeHwnd(),NULL); if(nCmdID!=0) { switch(nCmdID) { case IDCMD_RENAME: { EditLabel(tvhti.hItem); break; } default: { if(!m_navigator.InvokeCommand(lpNameSpaceObject->lpsfParent, lpNameSpaceObject->lpRelativeIDL,nCmdID,dwMenuFlags)) { TRACE(_T("COXShellFolderTree::OnRClick: InvokeCommand() failed\n")); } else { switch(nCmdID) { case IDCMD_DELETE: { DeleteItem(tvhti.hItem); break; } case IDCMD_PASTE: { Refresh(tvhti.hItem); break; } } } break; } } } ::DestroyMenu(hMenu); } return TRUE; } return FALSE; } void COXShellFolderTree::OnRButtonDown(UINT nFlags, CPoint point) { if(GetEnableContextMenu()) { ASSERT(m_hContextMenuItem==NULL); TV_HITTESTINFO tvhti; tvhti.pt=point; HitTest(&tvhti); if(tvhti.hItem!=NULL) { m_hContextMenuItem=tvhti.hItem; // remove highlight from the currently selected item HTREEITEM hSelectedItem=GetSelectedItem(); if(hSelectedItem!=m_hContextMenuItem) { if(hSelectedItem!=NULL) { SetItemState(hSelectedItem,0,TVIS_SELECTED); } // highlight the item under mouse cursor SetItemState(m_hContextMenuItem,TVIS_DROPHILITED,TVIS_DROPHILITED); } } ::SetCapture(GetSafeHwnd()); } else { CTreeCtrl::OnRButtonDown(nFlags,point); } } void COXShellFolderTree::OnRButtonUp(UINT nFlags, CPoint point) { if(GetEnableContextMenu()) { // On right click we display context menu for any item // // set handle to context menu item to NULL in order to avoid triggering // preliminary restore routine in OnCancelMode() and OnCaptureChanged() m_bNoRestoreContextMenuItem=TRUE; if(m_hContextMenuItem!=NULL) { ASSERT(::GetCapture()==GetSafeHwnd()); // Get the item that was right clicked in screen coordinates CPoint ptMouseCursor=point; #ifdef _DEBUG // double check for context menu item TV_HITTESTINFO tvhti; tvhti.pt=ptMouseCursor; HitTest(&tvhti); VERIFY(tvhti.hItem==m_hContextMenuItem); #endif // Get folder info associated with item LPNAMESPACEOBJECT lpNameSpaceObject= (LPNAMESPACEOBJECT)GetItemData(m_hContextMenuItem); if(!m_bShowDesktopItem || lpNameSpaceObject!=NULL) { ASSERT(lpNameSpaceObject!=NULL); DWORD dwMenuFlags=CMF_EXPLORE | ((((GetStyle()&TVS_EDITLABELS)==TVS_EDITLABELS) && ((lpNameSpaceObject->dwFlags&SFGAO_CANRENAME)==SFGAO_CANRENAME)) ? CMF_CANRENAME : 0); HMENU hMenu=m_navigator.GetObjectContextMenu( lpNameSpaceObject->lpsfParent, lpNameSpaceObject->lpRelativeIDL,dwMenuFlags); if(hMenu!=NULL) { // Display popup menu using Windows API TrackPopupMenu function // because it provides an e+asy way of getting selected menu item. // We need to set screen coordinates of the point where popup menu // should be displayed. ClientToScreen(&ptMouseCursor); BOOL nCmdID=::TrackPopupMenu( hMenu,TPM_LEFTALIGN|TPM_RETURNCMD|TPM_RIGHTBUTTON, ptMouseCursor.x,ptMouseCursor.y,0,GetSafeHwnd(),NULL); if(nCmdID!=0) { switch(nCmdID) { case IDCMD_RENAME: { EditLabel(m_hContextMenuItem); break; } default: { if(!m_navigator.InvokeCommand( lpNameSpaceObject->lpsfParent, lpNameSpaceObject->lpRelativeIDL, nCmdID,dwMenuFlags)) { TRACE(_T("COXShellFolderTree::OnContextMenu: InvokeCommand() failed\n")); } else { switch(nCmdID) { case IDCMD_DELETE: { DeleteItem(m_hContextMenuItem); break; } case IDCMD_PASTE: { Refresh(m_hContextMenuItem); break; } } } break; } } } ::DestroyMenu(hMenu); } else { TRACE(_T("COXShellFolderTree::OnContextMenu: GetObjectContextMenu() failed\n")); } } } m_bNoRestoreContextMenuItem=FALSE; if(::GetCapture()==GetSafeHwnd()) { ::ReleaseCapture(); } else { RestoreStateAfterContextMenu(); } ASSERT(m_hContextMenuItem==NULL); } else { CTreeCtrl::OnRButtonUp(nFlags,point); } } void COXShellFolderTree::OnMouseMove(UINT nFlags, CPoint point) { if(GetEnableContextMenu() && (nFlags & MK_RBUTTON)!=0) { TV_HITTESTINFO tvhti; tvhti.pt=point; HitTest(&tvhti); if(tvhti.hItem!=m_hContextMenuItem) { HTREEITEM hSelectedItem=GetSelectedItem(); if(m_hContextMenuItem!=NULL) { if(m_hContextMenuItem!=hSelectedItem) { SetItemState(m_hContextMenuItem,0,TVIS_DROPHILITED); } else { SetItemState(m_hContextMenuItem,0,TVIS_SELECTED); } } m_hContextMenuItem=tvhti.hItem; if(m_hContextMenuItem!=NULL) { if(m_hContextMenuItem!=hSelectedItem) { SetItemState(m_hContextMenuItem,TVIS_DROPHILITED,TVIS_DROPHILITED); } else { SetItemState(m_hContextMenuItem,TVIS_SELECTED,TVIS_SELECTED); } } } } CTreeCtrl::OnMouseMove(nFlags,point); } void COXShellFolderTree::OnCancelMode() { CTreeCtrl::OnCancelMode(); RestoreStateAfterContextMenu(); } void COXShellFolderTree::OnCaptureChanged(CWnd* pWnd) { CTreeCtrl::OnCaptureChanged(pWnd); RestoreStateAfterContextMenu(); } BOOL COXShellFolderTree::OnDblClick(NMHDR* pNMHDR, LRESULT* pResult) { UNREFERENCED_PARAMETER(pNMHDR); // TODO: Add your control notification handler code here *pResult = 0; // if double click over a folder we expand it or otherwise invoke // the default command for a file // // Get the item that was double clicked in screen coordinates CPoint ptMouseCursor; if(!::GetCursorPos(&ptMouseCursor)) { TRACE(_T("COXShellFolderTree::OnDblClick: GetCursorPos() failed\n")); return FALSE; } ScreenToClient(&ptMouseCursor); // Use HitTest function to define the tree item by coordinate TV_HITTESTINFO tvhti; tvhti.pt=ptMouseCursor; HitTest(&tvhti); if(tvhti.hItem) { // Get folder info associated with item LPNAMESPACEOBJECT lpNameSpaceObject= (LPNAMESPACEOBJECT)GetItemData(tvhti.hItem); if(m_bShowDesktopItem && lpNameSpaceObject==NULL) { return FALSE; } ASSERT(lpNameSpaceObject!=NULL); if((lpNameSpaceObject->dwFlags&SFGAO_FOLDER)!=SFGAO_FOLDER) { if(!m_navigator.InvokeDefaultCommand(lpNameSpaceObject->lpsfParent, lpNameSpaceObject->lpRelativeIDL)) { TRACE(_T("COXShellFolderTree::OnDblClick: InvokeDefaultCommand() failed")); } } } return FALSE; } void COXShellFolderTree::OnLButtonDown(UINT nFlags, CPoint point) { if(!m_bEditingItem) { CTreeCtrl::OnLButtonDown(nFlags,point); } else { SetFocus(); } } BOOL COXShellFolderTree::SortChildren(const HTREEITEM htiParent) { TV_SORTCB tvscb; tvscb.hParent=htiParent; tvscb.lParam=0; tvscb.lpfnCompare=COXShellNamespaceNavigator::CompareObjectsProc; return CTreeCtrl::SortChildrenCB(&tvscb); } void COXShellFolderTree::SetRedraw(BOOL bRedraw/*=TRUE*/) { ASSERT(m_nRedraw>=0); if(!bRedraw) { if(m_nRedraw==0) { CWnd::SetRedraw(FALSE); } m_nRedraw++; } else { if(m_nRedraw>=1) m_nRedraw--; if(m_nRedraw==0) { CWnd::SetRedraw(TRUE); } } } BOOL COXShellFolderTree::OnBeginLabelEdit(NMHDR* pNMHDR, LRESULT* pResult) { LPNMTVDISPINFO ptvdi=(LPNMTVDISPINFO)pNMHDR; ASSERT(ptvdi!=NULL); // TODO: Add your control notification handler code here (*pResult)=FALSE; // retrieve associated structure LPNAMESPACEOBJECT lpNameSpaceObject=(LPNAMESPACEOBJECT)ptvdi->item.lParam; if(lpNameSpaceObject==NULL) { (*pResult)=TRUE; return TRUE; } // check if item is allowed to be edited if((lpNameSpaceObject->dwFlags&SFGAO_CANRENAME)!=SFGAO_CANRENAME) { (*pResult)=TRUE; return TRUE; } m_bEditingItem=TRUE; return FALSE; } BOOL COXShellFolderTree::OnEndLabelEdit(NMHDR* pNMHDR, LRESULT* pResult) { LPNMTVDISPINFO ptvdi=(LPNMTVDISPINFO)pNMHDR; ASSERT(ptvdi!=NULL); // TODO: Add your control notification handler code here *pResult=TRUE;; CString sNewText=ptvdi->item.pszText; CWaitCursor waitCusor; // retrieve associated structure LPNAMESPACEOBJECT lpNameSpaceObject=(LPNAMESPACEOBJECT)ptvdi->item.lParam; ASSERT(lpNameSpaceObject!=NULL); // rename the item LPITEMIDLIST lpNewRelativeIDL=NULL; if(sNewText.IsEmpty() || !m_navigator.RenameShellObject(lpNameSpaceObject->lpsfParent, lpNameSpaceObject->lpRelativeIDL,&lpNewRelativeIDL,sNewText)) { *pResult=FALSE; } else { ASSERT(lpNewRelativeIDL!=NULL); lpNameSpaceObject->lpRelativeIDL=lpNewRelativeIDL; m_navigator.FreeShellObject((void*)lpNameSpaceObject->lpFullIDL); HTREEITEM hItem=GetParentItem(ptvdi->item.hItem); if(hItem==NULL || GetItemData(hItem)==NULL) { lpNameSpaceObject->lpFullIDL=m_navigator. CopyPIDL(lpNameSpaceObject->lpRelativeIDL); } else { LPNAMESPACEOBJECT lpNameSpaceObjectParent= (LPNAMESPACEOBJECT)GetItemData(hItem); ASSERT(lpNameSpaceObjectParent!=NULL); lpNameSpaceObject->lpFullIDL=m_navigator. ConcatenatePIDLs(lpNameSpaceObjectParent->lpFullIDL, lpNameSpaceObject->lpRelativeIDL); ASSERT(lpNameSpaceObject->lpFullIDL!=NULL); } *pResult=TRUE; } m_bEditingItem=FALSE; return TRUE; } BOOL COXShellFolderTree::Refresh(LPCTSTR lpszFolder) { HTREEITEM hItem=FindFolder(lpszFolder); if(hItem==NULL) { return FALSE; } return Refresh(hItem); } BOOL COXShellFolderTree::Refresh(HTREEITEM htiParent/*=TVI_ROOT*/) { if(htiParent==NULL) { return FALSE; } // save currently selected folder CString sCurrentFolder(_T("")); HTREEITEM hItem=GetSelectedItem(); if(hItem!=NULL) { sCurrentFolder=GetFullPath(hItem); SelectItem(NULL); } ////////////////// if(htiParent!=TVI_ROOT && (GetItemState(htiParent,TVIS_EXPANDED)&TVIS_EXPANDED)!=TVIS_EXPANDED) { return (SetItemState(htiParent,NULL,TVIS_EXPANDEDONCE)!=0); } if(htiParent==TVI_ROOT) { hItem=GetRootItem(); ASSERT(hItem!=NULL); if(m_bShowDesktopItem && GetItemData(hItem)==NULL) { hItem=GetNextItem(hItem,TVGN_CHILD); } } else { hItem=GetNextItem(htiParent,TVGN_CHILD); } if(hItem==NULL) { return TRUE; } // save info about curently expanded folders CArray m_arrExpandedFolders; while(hItem!=NULL) { // Get full path for the item if(GetItemState(hItem,TVIS_EXPANDED)&TVIS_EXPANDED) { CString sPath=GetFullPath(hItem); if(!sPath.IsEmpty()) m_arrExpandedFolders.Add(sPath); } hItem=GetNextItem(hItem,TVGN_NEXTVISIBLE); } /////////////////////// SetRedraw(FALSE); if(htiParent==TVI_ROOT) { InitializeTree(); } else { BOOL bWasExpanded=(GetItemState(htiParent,TVIS_EXPANDED)!=0); // repopulate the branch for(;;) { HTREEITEM htiChild=GetNextItem(htiParent,TVGN_CHILD); if(htiChild==NULL) break; DeleteItem(htiChild); } SetItemState(htiParent,NULL,TVIS_EXPANDEDONCE); if(bWasExpanded) { Expand(htiParent,TVE_COLLAPSE); Expand(htiParent,TVE_EXPAND); } } // expand the previously expanded folders for(int nIndex=0; nIndex0) m_arrFilter.Add(sExtentions.Left(nDelimiterPos)); sExtentions=sExtentions.Mid(nDelimiterPos+1); } } } CString COXShellFolderTree::GetFilter() const { TCHAR sDelimiter[2]; sDelimiter[0]=OXSHELLTREE_EXT_DELIMITER; sDelimiter[1]=_T('\0'); // special case if(m_arrFilter.GetSize()==1 && m_arrFilter[0].IsEmpty()) return sDelimiter; CString sExtentions(_T("")); for(int nIndex=0; nIndex