// OXShellObjectList.cpp : implementation file // // Version: 9.3 #include "stdafx.h" #include "OXShellObjectList.h" #include "OXMainRes.h" #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif // delimiter for filter const TCHAR OXSHELLLIST_EXT_DELIMITER=_T('|'); ///////////////////////////////////////////////////////////////////////////// // COXShellObjectList COXShellObjectList::COXShellObjectList(BOOL bEnableContextMenu/*=TRUE*/, BOOL bOnlyFileSystemItems/*=TRUE*/, BOOL bNotifyError/*=TRUE*/, LPCTSTR lpszFilter/*=_T("")*/) { SetEnableContextMenu(bEnableContextMenu); SetOnlyFileSystemItems(bOnlyFileSystemItems); SetNotifyError(bNotifyError); SetFilter(lpszFilter); m_lpsfCurrentParentFolder=NULL; m_lpFullIDLCurrentParentFolder=NULL; m_hShellImageListLarge=NULL; m_hShellImageListSmall=NULL; m_nRedraw=0; m_bInitialized=FALSE; } COXShellObjectList::~COXShellObjectList() { } BEGIN_MESSAGE_MAP(COXShellObjectList, CListCtrl) //{{AFX_MSG_MAP(COXShellObjectList) ON_NOTIFY_REFLECT_EX(NM_DBLCLK, OnDblClick) ON_NOTIFY_REFLECT_EX(LVN_BEGINLABELEDIT, OnBeginLabelEdit) ON_NOTIFY_REFLECT_EX(LVN_ENDLABELEDIT, OnEndLabelEdit) #ifdef OXSHELLLIST_WATCHFORDIR ON_MESSAGE(WM_OX_FILE_NOTIFY, OnDirChangeNotify) #endif // OXSHELLLIST_WATCHFORDIR ON_WM_CONTEXTMENU() //}}AFX_MSG_MAP END_MESSAGE_MAP() ///////////////////////////////////////////////////////////////////////////// // COXShellObjectList message handlers BOOL COXShellObjectList::PopulateList(const LPSHELLFOLDER lpFolder, const LPITEMIDLIST lpFullIDL) { ASSERT_VALID(this); ASSERT(lpFolder!=NULL); if(!::IsWindow(GetSafeHwnd())) return FALSE; if(!m_bInitialized) { if(!Init()) return FALSE; } // Clean up ListCtrl DeleteAllItems(); // Fill ListCtrl with objects in lpFolder folder. FillListWithSubitems(lpFolder,lpFullIDL); // We cannot sort item using just their display names. We have to // request shell to sort items. SortItems(COXShellNamespaceNavigator::CompareObjectsProc,NULL); return TRUE; } BOOL COXShellObjectList::PopulateList(CString sFolderStartFrom/*=_T("")*/) { ASSERT_VALID(this); if(!::IsWindow(GetSafeHwnd())) return FALSE; if(!m_bInitialized) { if(!Init()) return FALSE; } // now let's fill ListCtrl 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; } // Clean up ListCtrl DeleteAllItems(); // Fill ListCtrl with objects in lpFolder folder. FillListWithSubitems(lpFolder,lpidlFull); lpFolder->Release(); // We cannot sort item using just their display names. We have to // request shell to sort items. SortItems(COXShellNamespaceNavigator::CompareObjectsProc,NULL); return TRUE; } CString COXShellObjectList::GetFullPath(int nItem) const { ASSERT(nItem>=0 && nItemlpFullIDL); } BOOL COXShellObjectList::FillListWithSubitems(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 ListCtrl LV_ITEM lvi; BOOL bLast; // Loop through Shell Namespace items LPNAMESPACEOBJECT lpNameSpaceObject=m_navigator.EnumerateObjectNext(bLast); while(!bLast) { BOOL bQualified=lpNameSpaceObject!=NULL; if(bQualified && GetOnlyFileSystemItems()) { bQualified=(lpNameSpaceObject->dwFlags&SFGAO_FILESYSANCESTOR || lpNameSpaceObject->dwFlags&SFGAO_FILESYSTEM); } 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 lvi.mask=LVIF_TEXT|LVIF_IMAGE|LVIF_PARAM; // NAMESPACEOBJECT structure is already filled lvi.lParam=(LPARAM)lpNameSpaceObject; // display name lvi.pszText=lpNameSpaceObject->szDisplayName; // image int nListImageIndex=-1; if(!m_mapImageIndex.Lookup(lpNameSpaceObject->nImageSmall,nListImageIndex)) { nListImageIndex=PtrToInt(m_mapImageIndex.GetCount()); m_mapImageIndex.SetAt(lpNameSpaceObject->nImageSmall,nListImageIndex); HICON hIcon=ImageList_ExtractIcon( 0,m_hShellImageListLarge,lpNameSpaceObject->nImageSmall); ASSERT(hIcon!=NULL); lvi.iImage=m_imageListLarge.Add(hIcon); ASSERT(lvi.iImage!=-1 && lvi.iImage==nListImageIndex); VERIFY(::DestroyIcon(hIcon)); hIcon=ImageList_ExtractIcon( 0,m_hShellImageListSmall,lpNameSpaceObject->nImageSmall); ASSERT(hIcon!=NULL); VERIFY(m_imageListSmall.Add(hIcon)==lvi.iImage); VERIFY(::DestroyIcon(hIcon)); } else { lvi.iImage=nListImageIndex; } ///////////////////// // index and subitem lvi.iItem=GetItemCount(); lvi.iSubItem=0; // We collected all information we needed. It's time to insert // new item in our ListCtrl. int nItem=InsertItem(&lvi); ASSERT(nItem!=-1); // retrieve info about size, type and modification date of the // file system object if(lpNameSpaceObject->dwFlags&SFGAO_FILESYSTEM) { CString sFullPath=m_navigator.GetFullPath(lpNameSpaceObject->lpFullIDL); ASSERT(!sFullPath.IsEmpty()); CFileStatus fileStatus; if(CFile::GetStatus(sFullPath,fileStatus)) { if((lpNameSpaceObject->dwFlags&SFGAO_FOLDER)!=SFGAO_FOLDER) { // request size CString sFileSize; sFileSize.Format(_T("%dKB"), (fileStatus.m_size>1024 ? fileStatus.m_size/1024 : 1)); SetItem(nItem,1,LVIF_TEXT,sFileSize,0,0,0,0); } // modified CString sModified=fileStatus.m_mtime.Format(_T("%c")); SetItem(nItem,3,LVIF_TEXT,sModified,0,0,0,0); } SHFILEINFO shfi; if(SHGetFileInfo(sFullPath,NULL,&shfi,sizeof(shfi), SHGFI_TYPENAME)!=0) { // file type CString sItemType(_T("")); sItemType=(LPCTSTR)shfi.szTypeName; SetItem(nItem,2,LVIF_TEXT,sItemType,0,0,0,0); } } } lpNameSpaceObject=m_navigator.EnumerateObjectNext(bLast); } // Release enumerator m_navigator.ReleaseObjectsEnumerator(); #ifdef OXSHELLLIST_WATCHFORDIR if(m_lpFullIDLCurrentParentFolder!=NULL) { CString sCurrentFolder=m_navigator.GetFullPath(m_lpFullIDLCurrentParentFolder); if(!sCurrentFolder.IsEmpty()) m_fileWatcher.RemoveWatch(sCurrentFolder); sCurrentFolder=m_navigator.GetFullPath(lpFullIDL); if(!sCurrentFolder.IsEmpty()) { if(m_fileWatcher.AddWatch(sCurrentFolder,FALSE, COXFileWatcher::OXFileWatchChangeDirName| COXFileWatcher::OXFileWatchChangeFileName)) { m_fileWatcher.EnableWindowNotification(sCurrentFolder,this,TRUE); } else { TRACE(_T("COXShellObjectList::FillListWithSubitems: failed to set a file watcher for the current parent folder\n")); } } } #endif // OXSHELLLIST_WATCHFORDIR m_lpsfCurrentParentFolder=lpFolder; m_lpFullIDLCurrentParentFolder=lpFullIDL; return TRUE; } BOOL COXShellObjectList::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("COXShellObjectList::OnDblClick: GetCursorPos() failed\n")); return FALSE; } ScreenToClient(&ptMouseCursor); // Use HitTest function to define the list item by coordinate UINT nFlags=NULL; int nItem=HitTest(ptMouseCursor,&nFlags); if(nItem!=-1 && (nFlags&LVHT_ONITEM)!=0) { // Get folder info associated with item LPNAMESPACEOBJECT lpNameSpaceObject= (LPNAMESPACEOBJECT)GetItemData(nItem); ASSERT(lpNameSpaceObject!=NULL); if((lpNameSpaceObject->dwFlags&SFGAO_FOLDER)!=SFGAO_FOLDER) { if(!m_navigator.InvokeDefaultCommand(lpNameSpaceObject->lpsfParent, lpNameSpaceObject->lpRelativeIDL)) { TRACE(_T("COXShellObjectList::OnDblClick: InvokeDefaultCommand() failed")); } } } return FALSE; } BOOL COXShellObjectList::Init() { ASSERT_VALID(this); if(!::IsWindow(GetSafeHwnd())) return FALSE; SetNotifyError(m_bNotifyError); // use system image list as tree control's image list // m_mapImageIndex.RemoveAll(); // large images if((HIMAGELIST)m_imageListLarge!=NULL) m_imageListLarge.DeleteImageList(); // retrieve Shell image list if(m_hShellImageListLarge==NULL) { m_hShellImageListLarge=m_navigator.GetShellImageList(FALSE); if(m_hShellImageListLarge==NULL) return FALSE; } IMAGEINFO imageInfo; VERIFY(ImageList_GetImageInfo(m_hShellImageListLarge,0,&imageInfo)); CRect rect(imageInfo.rcImage); VERIFY(m_imageListLarge.Create(rect.Width(),rect.Height(),ILC_COLOR32|ILC_MASK,0,0)); SetImageList(&m_imageListLarge,TVSIL_NORMAL); //////////////////////////////////////////////// // small images if((HIMAGELIST)m_imageListSmall!=NULL) m_imageListSmall.DeleteImageList(); // retrieve Shell image list if(m_hShellImageListSmall==NULL) { m_hShellImageListSmall=m_navigator.GetShellImageList(TRUE); if(m_hShellImageListSmall==NULL) return FALSE; } VERIFY(ImageList_GetImageInfo(m_hShellImageListSmall,0,&imageInfo)); rect=imageInfo.rcImage; VERIFY(m_imageListSmall.Create(rect.Width(),rect.Height(),ILC_COLOR32|ILC_MASK,0,0)); SetImageList(&m_imageListSmall,LVSIL_SMALL); //////////////////////////////////////////////// // Clean up ListCtrl DeleteAllItems(); // check the columns LVCOLUMN lvc={0}; lvc.mask=LVCF_FMT; int nColumnCount=0; while(GetColumn(nColumnCount,&lvc)) nColumnCount++; if(nColumnCount!=4) { for(int nIndex=0; nIndex=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 COXShellObjectList::OnBeginLabelEdit(NMHDR* pNMHDR, LRESULT* pResult) { NMLVDISPINFO* plvdi=(NMLVDISPINFO*)pNMHDR; ASSERT(plvdi!=NULL); // TODO: Add your control notification handler code here (*pResult)=FALSE; // retrieve associated structure LPNAMESPACEOBJECT lpNameSpaceObject=(LPNAMESPACEOBJECT)plvdi->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; } return FALSE; } BOOL COXShellObjectList::OnEndLabelEdit(NMHDR* pNMHDR, LRESULT* pResult) { NMLVDISPINFO* plvdi=(NMLVDISPINFO*)pNMHDR; ASSERT(plvdi!=NULL); // TODO: Add your control notification handler code here *pResult=TRUE;; CString sNewText=plvdi->item.pszText; CWaitCursor waitCusor; // retrieve associated structure LPNAMESPACEOBJECT lpNameSpaceObject=(LPNAMESPACEOBJECT)plvdi->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; *pResult=TRUE; } return TRUE; } BOOL COXShellObjectList::Refresh() { SetRedraw(FALSE); PopulateList(m_lpsfCurrentParentFolder,m_lpFullIDLCurrentParentFolder); SetRedraw(TRUE); return TRUE; } #ifdef OXSHELLLIST_WATCHFORDIR LRESULT COXShellObjectList::OnDirChangeNotify(WPARAM wParam, LPARAM lParam) { UNREFERENCED_PARAMETER(lParam); CString str; COXFileWatchNotifier fwNotifier; fwNotifier=m_fileWatcher.GetFileWatchNotifier(wParam); // If the wParam is an invalid ID then the notifier is empty if(!fwNotifier.IsEmpty()) { CString sDirPath=fwNotifier.GetPath(); ASSERT(!sDirPath.IsEmpty()); VERIFY(Refresh()); } return 0; } #endif // OXSHELLLIST_WATCHFORDIR void COXShellObjectList::SetFilter(LPCTSTR lpszExtentions) { m_arrFilter.RemoveAll(); CString sExtentions=lpszExtentions; if(sExtentions.IsEmpty()) return; for(;;) { int nDelimiterPos=sExtentions.Find(OXSHELLLIST_EXT_DELIMITER); if(nDelimiterPos==-1) { m_arrFilter.Add(sExtentions); break; } else { if(nDelimiterPos>0) m_arrFilter.Add(sExtentions.Left(nDelimiterPos)); sExtentions=sExtentions.Mid(nDelimiterPos+1); } } } CString COXShellObjectList::GetFilter() const { TCHAR sDelimiter[2]; sDelimiter[0]=OXSHELLLIST_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; nIndexdwFlags&SFGAO_CANRENAME)==SFGAO_CANRENAME)) ? CMF_CANRENAME : NULL); HMENU hMenu=m_navigator.GetObjectContextMenu(lpNameSpaceObject->lpsfParent, lpNameSpaceObject->lpRelativeIDL,dwMenuFlags); if(hMenu==NULL) { TRACE(_T("COXShellObjectList::OnContextMenu: GetObjectContextMenu() failed\n")); return; } // 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) { switch(nCmdID) { case IDCMD_RENAME: { EditLabel(nItem); break; } default: { if(!m_navigator.InvokeCommand(lpNameSpaceObject->lpsfParent, lpNameSpaceObject->lpRelativeIDL,nCmdID,dwMenuFlags)) { TRACE(_T("COXShellObjectList::OnContextMenu: InvokeCommand() failed")); } else { switch(nCmdID) { case IDCMD_DELETE: { DeleteItem(nItem); break; } case IDCMD_PASTE: { Refresh(); break; } } } break; } } } ::DestroyMenu(hMenu); } else if(m_lpsfCurrentParentFolder!=NULL) { LPITEMIDLIST lpRelativeIDL=NULL; LPSHELLFOLDER pShellFolder= m_navigator.GetParentShellFolder( m_lpFullIDLCurrentParentFolder,&lpRelativeIDL); if(pShellFolder==NULL) { TRACE(_T("COXShellObjectList::OnContextMenu: GetParentShellFolder() failed\n")); return; } ASSERT(lpRelativeIDL!=NULL || m_lpFullIDLCurrentParentFolder==NULL); DWORD dwMenuFlags=CMF_EXPLORE; HMENU hMenu=m_navigator.GetObjectContextMenu( pShellFolder,lpRelativeIDL,dwMenuFlags); if(hMenu==NULL) { TRACE(_T("COXShellObjectList::OnContextMenu: GetObjectContextMenu() failed\n")); return; } CMenu menuPopup; VERIFY(menuPopup.Attach(hMenu)); // 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(menuPopup.m_hMenu, TPM_LEFTALIGN|TPM_RETURNCMD|TPM_RIGHTBUTTON, ptMouseCursor.x,ptMouseCursor.y,0,GetSafeHwnd(),NULL); if(nCmdID) { switch(nCmdID) { case IDCMD_RENAME: { ASSERT(FALSE); break; } default: { if(!m_navigator.InvokeCommand(pShellFolder,lpRelativeIDL, nCmdID,dwMenuFlags)) { TRACE(_T("COXShellObjectList::OnContextMenu: InvokeCommand() failed")); } else { switch(nCmdID) { case IDCMD_DELETE: { ASSERT(FALSE); break; } case IDCMD_PASTE: { Refresh(); break; } } } break; } } } VERIFY(menuPopup.DestroyMenu()); } } }