// XTPShellListBase.cpp : implementation file // // This file is a part of the XTREME CONTROLS MFC class library. // (c)1998-2012 Codejock Software, All Rights Reserved. // // THIS SOURCE FILE IS THE PROPERTY OF CODEJOCK SOFTWARE AND IS NOT TO BE // RE-DISTRIBUTED BY ANY MEANS WHATSOEVER WITHOUT THE EXPRESSED WRITTEN // CONSENT OF CODEJOCK SOFTWARE. // // THIS SOURCE CODE CAN ONLY BE USED UNDER THE TERMS AND CONDITIONS OUTLINED // IN THE XTREME TOOLKIT PRO LICENSE AGREEMENT. CODEJOCK SOFTWARE GRANTS TO // YOU (ONE SOFTWARE DEVELOPER) THE LIMITED RIGHT TO USE THIS SOFTWARE ON A // SINGLE COMPUTER. // // CONTACT INFORMATION: // support@codejock.com // http://www.codejock.com // ///////////////////////////////////////////////////////////////////////////// #include "stdafx.h" #include "../Resource.h" #include "Common/XTPVC80Helpers.h" // Visual Studio 2005 helper functions #include "Common/XTPResourceManager.h" #include "Common/XTPWinThemeWrapper.h" #include "Common/XTPSystemHelpers.h" #include "../Util/XTPControlTheme.h" #include "../Defines.h" #include "../Header/XTPHeaderCtrl.h" #include "../List/XTPListBase.h" #include "XTPShellPidl.h" #include "XTPShellSettings.h" #include "XTPDirWatcher.h" #include "XTPShellListBase.h" #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif #define ISZIPFILE(dw) (BOOL)(dw & SFGAO_FOLDER) && (dw & SFGAO_STREAM) #define ISFOLDER(dw) (BOOL)(dw & SFGAO_FOLDER) ///////////////////////////////////////////////////////////////////////////// // CXTPShellListBase CXTPShellListBase::CXTPShellListBase() : m_pDirThread(0) { m_bRowColor = TRUE; m_bContextMenu = TRUE; m_uFlags = SHCONTF_FOLDERS | SHCONTF_NONFOLDERS; m_ulSFGAOFlags = SFGAO_FOLDER | SFGAO_STREAM | SFGAO_FILESYSANCESTOR | SFGAO_DISPLAYATTRMASK | SFGAO_REMOVABLE | SFGAO_COMPRESSED | SFGAO_ENCRYPTED; if (m_shSettings.ShowAllFiles() && !m_shSettings.ShowSysFiles()) { m_uFlags |= SHCONTF_INCLUDEHIDDEN; } if (!SUCCEEDED(::SHGetSpecialFolderLocation(NULL, CSIDL_INTERNET, &m_pidlINet))) { m_pidlINet = NULL; } m_nNameColumnWidth = 200; m_lpsfFolder = NULL; } CXTPShellListBase::~CXTPShellListBase() { // End the directory monitoring thread. if (m_pDirThread) { m_pDirThread->StopNotifications(); m_pDirThread = NULL; } if (m_pidlINet) { CShellMalloc lpMalloc; lpMalloc.Free(m_pidlINet); } SAFE_RELEASE(m_lpsfFolder); } ///////////////////////////////////////////////////////////////////////////// // CXTPShellListBase message handlers BOOL CXTPShellListBase::PopulateListView(XTP_TVITEMDATA* lptvid, LPSHELLFOLDER lpsf) { // Turn off redraw so the user does't see resorting m_pListCtrl->SetRedraw(false); //clear the view for new items m_pListCtrl->DeleteAllItems(); SAFE_RELEASE(m_lpsfFolder); if (InitListViewItems(lptvid, lpsf)) { SortList((m_nSortedCol > -1) ? m_nSortedCol : 0, (m_nSortedCol > -1) ? m_bAscending : 1); // create the directory monitoring thread. if (m_pDirThread == NULL) { m_pDirThread = (CXTPDirWatcher*)AfxBeginThread(RUNTIME_CLASS(CXTPDirWatcher), THREAD_PRIORITY_LOWEST, 0, CREATE_SUSPENDED, NULL); m_pDirThread->SetFolderData(m_pListCtrl, lptvid); m_pDirThread->ResumeThread(); } // if the folder changed, update the folder data. else { TCHAR szFolderPath[_MAX_PATH]; if (::SHGetPathFromIDList(lptvid->lpifq, szFolderPath)) { CString strFolderPath = m_pDirThread->GetFolderPath(); if (strFolderPath.CompareNoCase(szFolderPath) != 0) { m_pDirThread->SuspendThread(); m_pDirThread->SetFolderData(m_pListCtrl, lptvid); m_pDirThread->ResumeThread(); } } } m_lpsfFolder = lpsf; m_lpsfFolder->AddRef(); m_pListCtrl->SetRedraw(true); return TRUE; } m_pListCtrl->SetRedraw(true); return FALSE; } void CXTPShellListBase::BuildDefaultColumns() { CString strLabel; CXTPResourceManager::AssertValid(XTPResourceManager()->LoadString(&strLabel, XTP_IDS_NAME)); m_pListCtrl->InsertColumn(0, strLabel, LVCFMT_LEFT, m_nNameColumnWidth, 0); CXTPResourceManager::AssertValid(XTPResourceManager()->LoadString(&strLabel, XTP_IDS_SIZE)); m_pListCtrl->InsertColumn(1, strLabel, LVCFMT_RIGHT, 90, 1); CXTPResourceManager::AssertValid(XTPResourceManager()->LoadString(&strLabel, XTP_IDS_TYPE)); m_pListCtrl->InsertColumn(2, strLabel, LVCFMT_LEFT, 120, 2); CXTPResourceManager::AssertValid(XTPResourceManager()->LoadString(&strLabel, XTP_IDS_MODIFIED)); m_pListCtrl->InsertColumn(3, strLabel, LVCFMT_LEFT, 120, 3); } BOOL CXTPShellListBase::InitSystemImageLists() { HIMAGELIST himlSmall = GetSystemImageList(SHGFI_SMALLICON); HIMAGELIST himlLarge = GetSystemImageList(SHGFI_LARGEICON); if (himlSmall && himlLarge) { ListView_SetImageList(m_pListCtrl->GetSafeHwnd(), himlSmall, LVSIL_SMALL); ListView_SetImageList(m_pListCtrl->GetSafeHwnd(), himlLarge, LVSIL_NORMAL); return TRUE; } return FALSE; } BOOL CXTPShellListBase::IsItemFiltered(LPCTSTR lpszItemName, ULONG ulItemAttrs) { if (ISFOLDER(ulItemAttrs)) return FALSE; if (!m_csIncludeEXT.IsEmpty()) { TCHAR szDrive[_MAX_DRIVE]; TCHAR szDir[_MAX_DIR]; TCHAR szFileName[_MAX_FNAME]; TCHAR szExt[_MAX_EXT]; SPLITPATH_S(lpszItemName, szDrive, szDir, szFileName, szExt); if (_tcsclen(szExt) == 0) return m_csIncludeEXT.Find(_T("*.;")) == -1; return m_csIncludeEXT.Find(szExt) == -1; } return FALSE; } BOOL CXTPShellListBase::InitListViewItems(XTP_TVITEMDATA* lptvid, LPSHELLFOLDER lpsf) { CShellMalloc lpMalloc; if (!lpMalloc) return FALSE; LPENUMIDLIST lpe = NULL; if (FAILED(lpsf->EnumObjects(::GetParent(m_pListCtrl->m_hWnd), m_uFlags, &lpe))) return FALSE; if (lpe == NULL) return FALSE; int iCtr = 0; ULONG ulFetched = 0; LPITEMIDLIST lpi = NULL; while (lpe->Next(1, &lpi, &ulFetched) == S_OK) { // Now get the friendly name that we'll put in the treeview... CString szFileName, szFullFilePath; GetName(lpsf, lpi, SHGDN_NORMAL, szFileName); GetName(lpsf, lpi, SHGDN_FORPARSING, szFullFilePath); // Note that since we are interested in the display attributes as well as // the other attributes, we need to set ulAttrs to SFGAO_DISPLAYATTRMASK // before calling GetAttributesOf(); // Determine what type of object we have. ULONG ulAttrs = GetAttributes(lpsf, lpi, m_ulSFGAOFlags); BOOL bIncludeFile = !IsItemFiltered(szFullFilePath, ulAttrs); if (bIncludeFile) { // allocate memory for ITEMDATA struct XTP_LVITEMDATA* lplvid = new XTP_LVITEMDATA; if (lplvid == NULL) { if (lpe) { lpe->Release(); } return FALSE; } lplvid->ulAttribs = ulAttrs; LPITEMIDLIST lpifqThisItem = ConcatPidls(lpMalloc, lptvid->lpifq, lpi); LV_ITEM lvi; lvi.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM; lvi.iItem = iCtr++; lvi.iSubItem = 0; lvi.pszText = (LPTSTR)(LPCTSTR)szFileName; lvi.cchTextMax = 0; lvi.iImage = GetItemIcon(lpifqThisItem, SHGFI_PIDL | SHGFI_SYSICONINDEX | SHGFI_SMALLICON); lplvid->lpsfParent = lpsf; lpsf->AddRef(); // Make a copy of the ITEMIDLIST lplvid->lpi = DuplicateItem(lpMalloc, lpi); lvi.lParam = (LPARAM)lplvid; // Add the item to the listview int iIndex = m_pListCtrl->InsertItem(&lvi); SetAttributes(iIndex, ulAttrs); if (iIndex >= 0) { TCHAR szItemPath[_MAX_PATH]; ::SHGetPathFromIDList(lpifqThisItem, szItemPath); WIN32_FIND_DATA fdata; HANDLE handle = ::FindFirstFile(szItemPath, &fdata); if (handle != INVALID_HANDLE_VALUE) { if (!ISFOLDER(ulAttrs) || ISZIPFILE(ulAttrs)) { LONGLONG fsize = fdata.nFileSizeHigh*((LONGLONG)ULONG_MAX + 1) + fdata.nFileSizeLow; TCHAR szBuffer[16]; CString strSize; strSize.Format(_T("%s KB"), InsertCommas((fsize + 1024)/1024, szBuffer, 15)); m_pListCtrl->SetItemText(iIndex, 1, strSize); } // insert date modified. FILETIME ltime; ::FileTimeToLocalFileTime(&fdata.ftLastWriteTime, <ime); SYSTEMTIME time; ::FileTimeToSystemTime(<ime, &time); if ((time.wYear >= 1970 && time.wYear <= 2038) && (time.wMonth >= 1 && time.wMonth <= 12)) { CTime cTime; cTime = CTime( time.wYear, time.wMonth, time.wDay, time.wHour, time.wMinute, time.wSecond); m_pListCtrl->SetItemText(iIndex, 3, cTime.Format(_T("%m/%d/%y %I:%M %p"))); } ::FindClose(handle); } SHFILEINFO sfi; ::SHGetFileInfo((TCHAR*)lpifqThisItem, 0, &sfi, sizeof(SHFILEINFO), SHGFI_PIDL | SHGFI_TYPENAME); m_pListCtrl->SetItemText(iIndex, 2, sfi.szTypeName); } else { if (lpifqThisItem) { lpMalloc.Free(lpifqThisItem); } return FALSE; } if (lpifqThisItem) { lpMalloc.Free(lpifqThisItem); } } // Free the pidl that the shell gave us. if (lpi) { lpMalloc.Free(lpi); lpi = NULL; } } if (lpe) { lpe->Release(); } return TRUE; } TCHAR* CXTPShellListBase::InsertCommas(LONGLONG value, TCHAR* szBufferOut, UINT nSize) { CString strValue; // 30 digits is a really big number TCHAR szBufferIn[30]; // 30 digits is a really big number TCHAR szDecimalSep[ 5 ]; TCHAR szThousandSep[ 5 ]; NUMBERFMT fmt; fmt.NumDigits = 0; // No decimal places ::GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_ILZERO, szBufferIn, 2); fmt.LeadingZero = _ttoi(szBufferIn); fmt.Grouping = 3; ::GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SDECIMAL, szDecimalSep, 4); fmt.lpDecimalSep = szDecimalSep; ::GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_STHOUSAND, szThousandSep, 4); fmt.lpThousandSep = szThousandSep; ::GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_INEGNUMBER, szBufferIn, 2); fmt.NegativeOrder = _ttoi(szBufferIn); strValue.Format(_T("%I64d"), value); ::GetNumberFormat(LOCALE_USER_DEFAULT, 0, strValue, &fmt, szBufferOut, nSize); return szBufferOut; } void CXTPShellListBase::GetNormalAndSelectedIcons(LPITEMIDLIST lpifq, LPTV_ITEM lptvitem) { // Note that we don't check the return value here because if GetIcon() // fails, then we're in big trouble... lptvitem->iImage = GetItemIcon(lpifq, SHGFI_PIDL | SHGFI_SYSICONINDEX | SHGFI_SMALLICON); lptvitem->iSelectedImage = GetItemIcon(lpifq, SHGFI_PIDL | SHGFI_SYSICONINDEX | SHGFI_SMALLICON | SHGFI_OPENICON); } int CXTPShellListBase::GetDoubleClickedItem() { CPoint point; ::GetCursorPos(&point); m_pListCtrl->ScreenToClient(&point); LV_HITTESTINFO lvhti; lvhti.pt = point; m_pListCtrl->HitTest(&lvhti); if ((lvhti.flags & LVHT_ONITEM) == 0) { return -1; } return lvhti.iItem; } void CXTPShellListBase::ShowShellContextMenu(CPoint point) { if (m_bContextMenu == FALSE) return; CPoint ptClient(point); m_pListCtrl->ScreenToClient(&ptClient); int nIndex = point == CPoint(-1, -1) ? m_pListCtrl->GetNextItem(-1, LVNI_FOCUSED) : m_pListCtrl->HitTest(ptClient); if (nIndex != -1) { if (point == CPoint(-1, -1)) { CRect rcItem; m_pListCtrl->GetItemRect(nIndex, &rcItem, LVIR_ICON); point = rcItem.CenterPoint(); m_pListCtrl->ClientToScreen(&point); } XTP_LVITEMDATA* lplvid = (XTP_LVITEMDATA*)m_pListCtrl->GetItemData(nIndex); ASSERT(lplvid != NULL); if (!lplvid) return; CPtrArray arrItems; arrItems.Add(lplvid->lpi); POSITION pos = m_pListCtrl->GetFirstSelectedItemPosition(); while (NULL != pos) { int nSelItem = m_pListCtrl->GetNextSelectedItem(pos); XTP_LVITEMDATA* lplvidSelected = (XTP_LVITEMDATA*)m_pListCtrl->GetItemData(nSelItem); ASSERT(lplvidSelected != NULL); if (lplvidSelected != lplvid && lplvidSelected) arrItems.Add(lplvidSelected->lpi); } CXTPShellPidl::ShowContextMenu(m_pListCtrl->m_hWnd, lplvid->lpsfParent, (LPCITEMIDLIST*)arrItems.GetData(), (int)arrItems.GetSize(), &point); } else if (m_lpsfFolder) { ShowContextMenu(m_pListCtrl->m_hWnd, m_lpsfFolder, 0, 0, &point); } } bool CXTPShellListBase::ShellOpenItem(int iItem) { // Long pointer to ListView item data XTP_LVITEMDATA* lplvid = (XTP_LVITEMDATA*)m_pListCtrl->GetItemData(iItem); return ShellOpenItem(lplvid); } bool CXTPShellListBase::ShellOpenItem(XTP_LVITEMDATA* lplvid) { // Long pointer to ListView item data if (!ISFOLDER(lplvid->ulAttribs)) { if (lplvid->ulAttribs & (SFGAO_FILESYSANCESTOR|SFGAO_REMOVABLE)) return false; SHELLEXECUTEINFO sei; ::ZeroMemory(&sei, sizeof(SHELLEXECUTEINFO)); sei.cbSize = sizeof(SHELLEXECUTEINFO); sei.fMask = SEE_MASK_INVOKEIDLIST; sei.hwnd = ::GetParent(m_pListCtrl->GetSafeHwnd()); sei.nShow = SW_SHOWNORMAL; sei.hInstApp = AfxGetInstanceHandle(); sei.lpIDList = GetFullyQualPidl(lplvid->lpsfParent, lplvid->lpi); if (::ShellExecuteEx(&sei)) { return true; } } return false; } BOOL CXTPShellListBase::GetItemPath(int iItem, CString& strItemPath) { if (iItem >= 0) { // Long pointer to TreeView item data XTP_LVITEMDATA* lplvid = (XTP_LVITEMDATA*)m_pListCtrl->GetItemData(iItem); if (lplvid != 0) { LPITEMIDLIST lpid = GetFullyQualPidl(lplvid->lpsfParent, lplvid->lpi); TCHAR szItemPath[_MAX_PATH]; if (::SHGetPathFromIDList(lpid, szItemPath)) { strItemPath = szItemPath; return TRUE; } } } return FALSE; } void CXTPShellListBase::OnDeleteListItem(NMHDR* pNMHDR, LRESULT* pResult) { NM_LISTVIEW* pNMTreeView = (NM_LISTVIEW*)pNMHDR; XTP_LVITEMDATA* lplvid = (XTP_LVITEMDATA*)pNMTreeView->lParam; if (lplvid != NULL) { CShellMalloc lpMalloc; if (lplvid->lpi) { lpMalloc.Free(lplvid->lpi); lplvid->lpi = NULL; } if (lplvid->lpsfParent) { lplvid->lpsfParent->Release(); lplvid->lpsfParent = NULL; } delete lplvid; } *pResult = 0; } void CXTPShellListBase::UpdateList(int nMessage, XTP_TVITEMDATA* pItemData) { switch (nMessage) { case SHN_XTP_CONTENTSCHANGED: case SHN_XTP_TREESELCHANGE: { CWnd* pOwner = m_pListCtrl->GetOwner(); ASSERT_VALID(pOwner); if (!pOwner) return; // The tree view selection has changed, so update the contents // of the list view XTP_TVITEMDATA* lptvid = (XTP_TVITEMDATA*)pItemData; ASSERT(lptvid != NULL); if (!lptvid) return; if (lptvid->lpsfParent == NULL) { CShellMalloc lpMalloc; if (!lpMalloc) { return; } LPSHELLFOLDER lpShellFolder; if (FAILED(::SHGetDesktopFolder(&lpShellFolder))) { return; } LPITEMIDLIST pidlDesktop = NULL; if (FAILED(::SHGetSpecialFolderLocation(NULL, CSIDL_DESKTOP, &pidlDesktop))) { lpShellFolder->Release(); return; } IShellFolder *pFolder = NULL; if (lpShellFolder->CompareIDs(0, lptvid->lpifq, pidlDesktop) == 0) { pFolder = lpShellFolder; } else { lpShellFolder->BindToObject(lptvid->lpifq, 0, IID_IShellFolder, (LPVOID*)&pFolder); lpShellFolder->Release(); } ASSERT (pFolder != 0); PopulateListView(lptvid, pFolder); if (m_pidlINet && (pFolder->CompareIDs(0, lptvid->lpifq, m_pidlINet) == 0)) { pOwner->SendMessage(WM_XTP_SHELL_NOTIFY, SHN_XTP_INETFOLDER); } else { pOwner->SendMessage(WM_XTP_SHELL_NOTIFY); } if (pidlDesktop) { lpMalloc.Free(pidlDesktop); } if (pFolder) { pFolder->Release(); } } else { LPSHELLFOLDER lpsf = NULL; if (SUCCEEDED(lptvid->lpsfParent->BindToObject(lptvid->lpi, 0, IID_IShellFolder, (LPVOID*)&lpsf))) { PopulateListView(lptvid, lpsf); lpsf->Release(); if (SUCCEEDED(::SHGetDesktopFolder(&lpsf))) { if (m_pidlINet && (lpsf->CompareIDs(0, lptvid->lpifq, m_pidlINet) == 0)) { pOwner->SendMessage(WM_XTP_SHELL_NOTIFY, SHN_XTP_INETFOLDER); } else { pOwner->SendMessage(WM_XTP_SHELL_NOTIFY); } lpsf->Release(); } } else { if (SUCCEEDED(::SHGetDesktopFolder(&lpsf))) { if (m_pidlINet && (lpsf->CompareIDs(0, lptvid->lpifq, m_pidlINet) == 0)) { pOwner->SendMessage(WM_XTP_SHELL_NOTIFY, SHN_XTP_INETFOLDER); } lpsf->Release(); } } } break; } case SHN_XTP_REFRESHFOLDER: case SHN_XTP_REFRESHTREE: { // Directory monitory thread has issued an update notification, // refresh the list control. XTP_TVITEMDATA* lpTVID = (XTP_TVITEMDATA*)pItemData; ASSERT(lpTVID); if (!lpTVID) return; PopulateListView(lpTVID, lpTVID->lpsfParent); break; } case SHN_XTP_NOFOLDER: { // The item double clicked was not found in the treeview // so it sent us back a confirmation to execute it XTP_LVITEMDATA* lplvid = (XTP_LVITEMDATA*)pItemData; ASSERT(lplvid); if (!lplvid) return; ShellOpenItem(lplvid); } break; default: break; } } BOOL CXTPShellListBase::RefreshRowColors() { // if show compressed or encrypted disabled, return FALSE. if (!m_shSettings.ShowCompColor()) return FALSE; // reset color list and settings values. m_arRowColor.RemoveAll(); m_shSettings.RefreshSettings(); for (int i = 0; i < m_pListCtrl->GetItemCount(); ++i) { XTP_LVITEMDATA* lplvid = (XTP_LVITEMDATA*)m_pListCtrl->GetItemData(i); if (lplvid) { if ((lplvid->ulAttribs & SFGAO_COMPRESSED) || (lplvid->ulAttribs & SFGAO_ENCRYPTED)) { ROWCOLOR rowColor; ::ZeroMemory(&rowColor, sizeof(ROWCOLOR)); rowColor.iRow = i; rowColor.crBack = GetListBackColor(); rowColor.lParam = lplvid->ulAttribs; if (lplvid->ulAttribs & SFGAO_COMPRESSED) { rowColor.crText = m_shSettings.m_crCompColor; SetRowColor(&rowColor, FALSE); } else if (lplvid->ulAttribs & SFGAO_ENCRYPTED) { rowColor.crText = m_shSettings.m_crEncrColor; SetRowColor(&rowColor, FALSE); } } } } return TRUE; } bool CXTPShellListBase::SortList(int nCol, bool bAscending) { if (m_nSortedCol >= 0) { CXTPHeaderCtrl* pHeaderCtrl = XTPGetHeaderCtrl(); if (pHeaderCtrl && pHeaderCtrl->GetSortedCol() < 0) { pHeaderCtrl->SetSortImage(nCol, bAscending); } } m_pListCtrl->SortItems( ListViewCompareProc, (nCol + 1)*(bAscending? 1: -1)); RefreshRowColors(); return true; } void CXTPShellListBase::SetAttributes(int iItem, DWORD dwAttributes) { MapShellFlagsToItemAttributes(m_pListCtrl, iItem, dwAttributes); } void CXTPShellListBase::OnDragDrop(NM_LISTVIEW* /*pNMListView*/) { COleDataSource oleDataSource; HGLOBAL hgDrop; DROPFILES* pDrop; CStringList lsDraggedFiles; POSITION pos; int nSelItem; CString sFile; UINT uBuffSize = 0; TCHAR* pszBuff; FORMATETC etc = { CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL }; // For every selected item in the list, put the filename into lsDraggedFiles. pos = m_pListCtrl->GetFirstSelectedItemPosition(); while (NULL != pos) { nSelItem = m_pListCtrl->GetNextSelectedItem(pos); GetItemPath(nSelItem, sFile); lsDraggedFiles.AddTail (sFile); // Calculate the # of chars required to hold this string. uBuffSize += lstrlen (sFile) + 1; } // Add 1 extra for the final null char, and the size of the DROPFILES struct. uBuffSize = sizeof(DROPFILES) + sizeof(TCHAR) * (uBuffSize + 1); // Allocate memory from the heap for the DROPFILES struct. hgDrop = ::GlobalAlloc (GHND | GMEM_SHARE, uBuffSize); if (NULL == hgDrop) return; pDrop = (DROPFILES*) ::GlobalLock (hgDrop); if (NULL == pDrop) { ::GlobalFree (hgDrop); return; } // Fill in the DROPFILES struct. pDrop->pFiles = sizeof(DROPFILES); #ifdef _UNICODE // If we're compiling for Unicode, set the Unicode flag in the struct to // indicate it contains Unicode strings. pDrop->fWide = TRUE; #endif // Copy all the filenames into memory after the end of the DROPFILES struct. pos = lsDraggedFiles.GetHeadPosition(); pszBuff = (TCHAR*) (LPBYTE(pDrop) + sizeof(DROPFILES)); while (NULL != pos) { lstrcpy (pszBuff, (LPCTSTR) lsDraggedFiles.GetNext (pos)); pszBuff = 1 + _tcschr (pszBuff, '\0'); } ::GlobalUnlock (hgDrop); // Put the data in the data source. oleDataSource.CacheGlobalData (CF_HDROP, hgDrop, &etc); // Add in our own custom data, so we know that the drag originated from our // window. OnDragEnter() checks for this custom format, and // doesn't allow the drop if it's present. This is how we prevent the user // from dragging and then dropping in our own window. // The data will just be a dummy bool. HGLOBAL hgBool; hgBool = ::GlobalAlloc (GHND | GMEM_SHARE, sizeof(bool)); if (NULL == hgBool) { ::GlobalFree (hgDrop); return; } static CLIPFORMAT clpFormat = (CLIPFORMAT) ::RegisterClipboardFormat(_T("{B0D76F7A-B5D9-436c-8F10-BA16AEE69D42}")); // Put the data in the data source. etc.cfFormat = clpFormat; oleDataSource.CacheGlobalData(clpFormat, hgBool, &etc); // Start the drag 'n' drop! DROPEFFECT dwEffect = oleDataSource.DoDragDrop (DROPEFFECT_COPY | DROPEFFECT_MOVE); // If the DnD completed OK, we remove all of the dragged items from our // switch (dwEffect) { case DROPEFFECT_MOVE: { // The files were copied or moved. // Note: Don't call ::GlobalFree() because the data will be freed by the drop target. for (nSelItem = m_pListCtrl->GetNextItem (-1, LVNI_SELECTED); nSelItem != -1; nSelItem = m_pListCtrl->GetNextItem (nSelItem, LVNI_SELECTED)) { m_pListCtrl->DeleteItem (nSelItem); nSelItem--; } } break; case DROPEFFECT_COPY: break; case DROPEFFECT_NONE: { // This needs special handling, because on NT, DROPEFFECT_NONE // is returned for move operations, instead of DROPEFFECT_MOVE. // See Q182219 for the details. // So if we're on NT, we check each selected item, and if the // file no longer exists, it was moved successfully and we can // remove it from the if ((GetVersion() & 0x80000000) == 0) { bool bDeletedAnything = false; for (nSelItem = m_pListCtrl->GetNextItem (-1, LVNI_SELECTED); nSelItem != -1; nSelItem = m_pListCtrl->GetNextItem(nSelItem, LVNI_SELECTED)) { GetItemPath(nSelItem, sFile); if (GetFileAttributes(sFile) == DWORD(-1) && GetLastError() == ERROR_FILE_NOT_FOUND) { // We couldn't read the file's attributes, and GetLastError() // says the file doesn't exist, so remove the corresponding // item from the m_pListCtrl->DeleteItem(nSelItem); nSelItem--; bDeletedAnything = true; } } // Resize the list columns if we deleted any items. if (bDeletedAnything) { m_pListCtrl->SetColumnWidth (0, LVSCW_AUTOSIZE_USEHEADER); m_pListCtrl->SetColumnWidth (1, LVSCW_AUTOSIZE_USEHEADER); m_pListCtrl->SetColumnWidth (2, LVSCW_AUTOSIZE_USEHEADER); // Note: Don't call ::GlobalFree() because the data belongs to // the caller. } else { // The DnD operation wasn't accepted, or was canceled, so we // should call ::GlobalFree() to clean up. ::GlobalFree (hgDrop); ::GlobalFree (hgBool); } } // end if (NT) else { // We're on 9x, and a return of DROPEFFECT_NONE always means // that the DnD operation was aborted. We need to free the // allocated memory. ::GlobalFree (hgDrop); ::GlobalFree (hgBool); } } break; // end case DROPEFFECT_NONE } // end switch } bool CXTPShellListBase::Init() { if (!CXTPListBase::Init()) return false; if (!m_pListCtrl->GetImageList(LVSIL_SMALL)) { // Initialize the columns and image list for the list control. BuildDefaultColumns(); InitSystemImageLists(); m_pListCtrl->ModifyStyle(NULL, LVS_REPORT | LVS_SHAREIMAGELISTS); SubclassHeader(FALSE); XTPGetHeaderCtrl()->ShowSortArrow(TRUE); m_pListCtrl->DragAcceptFiles(TRUE); } return true; } #ifndef LVS_EX_BORDERSELECT #define LVS_EX_BORDERSELECT 0x00008000 // border selection style instead of highlight #endif #ifndef LVS_EX_DOUBLEBUFFER #define LVS_EX_DOUBLEBUFFER 0x00010000 #endif void CXTPShellListBase::SetExplorerStyle() { if (XTPSystemVersion()->IsWinVistaOrGreater()) { CXTPWinThemeWrapper().SetWindowTheme(m_pListCtrl->GetSafeHwnd(), L"EXPLORER", NULL); } }