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++
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, <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);
|
|
}
|
|
}
|