You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

968 lines
24 KiB
C++

// 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, &ltime);
SYSTEMTIME time;
::FileTimeToSystemTime(&ltime, &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);
}
}