645 lines
15 KiB
C++
645 lines
15 KiB
C++
//*******************************************************************************************
|
|
//
|
|
// Filename : Sfview.cpp
|
|
//
|
|
// Implementation file for CSFView
|
|
//
|
|
// Copyright (c) 1994 - 1996 Microsoft Corporation. All rights reserved
|
|
//
|
|
//*******************************************************************************************
|
|
|
|
#include "Pch.H"
|
|
|
|
#include "SFView.H"
|
|
#include "SFVWnd.H"
|
|
|
|
#include "Resource.H"
|
|
|
|
#include "ThisGuid.H"
|
|
|
|
#include "SFView.H"
|
|
|
|
struct SFSTATE_HDR
|
|
{
|
|
CLSID clsThis;
|
|
SFSTATE sfState;
|
|
UINT nCols;
|
|
} ;
|
|
|
|
|
|
CSFView::CSFView(IShellFolder *psf, IShellFolderViewCallback *psfvcb) :
|
|
m_psf(psf), m_erFolder(psf), m_erCB(psfvcb), m_pCDB(NULL), m_cView(this),
|
|
m_uState(SVUIA_DEACTIVATE), m_pcmSel(NULL), m_cAccel(IDA_MAIN)
|
|
{
|
|
m_psfvcb = psfvcb;
|
|
if (psfvcb)
|
|
{
|
|
psfvcb->AddRef();
|
|
}
|
|
|
|
psf->AddRef();
|
|
|
|
m_aParamSort = DPA_Create(4);
|
|
m_sfState.lParamSort = 0;
|
|
}
|
|
|
|
|
|
CSFView::~CSFView()
|
|
{
|
|
ReleaseSelContextMenu();
|
|
}
|
|
|
|
|
|
STDMETHODIMP CSFView::QueryInterface(REFIID riid, void ** ppvObj)
|
|
{
|
|
static const IID *apiid[] = { &IID_IShellView, NULL };
|
|
LPUNKNOWN aobj[] = { (IShellView *)this };
|
|
|
|
return(QIHelper(riid, ppvObj, apiid, aobj));
|
|
}
|
|
|
|
|
|
STDMETHODIMP_(ULONG) CSFView::AddRef()
|
|
{
|
|
return(AddRefHelper());
|
|
}
|
|
|
|
|
|
STDMETHODIMP_(ULONG) CSFView::Release()
|
|
{
|
|
return(ReleaseHelper());
|
|
}
|
|
|
|
|
|
STDMETHODIMP CSFView::GetWindow(HWND * lphwnd)
|
|
{
|
|
return(E_NOTIMPL);
|
|
}
|
|
|
|
|
|
STDMETHODIMP CSFView::ContextSensitiveHelp(BOOL fEnterMode)
|
|
{
|
|
return(E_NOTIMPL);
|
|
}
|
|
|
|
|
|
//*****************************************************************************
|
|
//
|
|
// CSFView::TranslateAccelerator
|
|
//
|
|
// Purpose:
|
|
// Handle the accelerator keystrokes
|
|
//
|
|
//
|
|
// Parameters:
|
|
// LPMSG lpmsg - message structure
|
|
//
|
|
//
|
|
// Comments:
|
|
//
|
|
//*****************************************************************************
|
|
|
|
STDMETHODIMP CSFView::TranslateAccelerator(LPMSG lpmsg)
|
|
{
|
|
return(m_cAccel.TranslateAccelerator(m_cView, lpmsg) ? S_OK : S_FALSE);
|
|
}
|
|
|
|
|
|
STDMETHODIMP CSFView::EnableModeless(BOOL fEnable)
|
|
{
|
|
return(E_NOTIMPL);
|
|
}
|
|
|
|
|
|
//*****************************************************************************
|
|
//
|
|
// CSFView:UIActivate
|
|
//
|
|
// Purpose:
|
|
// The explorer calls this member function whenever the activation
|
|
// state of the view window is changed by a certain event that is
|
|
// NOT caused by the shell view itself.
|
|
//
|
|
//
|
|
// Parameters:
|
|
//
|
|
// UINT uState - UI activate flag
|
|
//
|
|
// Comments:
|
|
//
|
|
//*****************************************************************************
|
|
|
|
STDMETHODIMP CSFView::UIActivate(UINT uState)
|
|
{
|
|
if (uState)
|
|
{
|
|
OnActivate(uState);
|
|
}
|
|
else
|
|
{
|
|
OnDeactivate();
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
STDMETHODIMP CSFView::Refresh()
|
|
{
|
|
FillList(FALSE);
|
|
return S_OK;// yes, this is potentially bad, but we risk app compat problems if we change it. 2000/02/10 -ccooney
|
|
}
|
|
|
|
|
|
//*****************************************************************************
|
|
//
|
|
// CSFView::CreateViewWindow
|
|
//
|
|
// Purpose:
|
|
//
|
|
// called by IShellBrowser to create a contents pane window
|
|
//
|
|
// Parameters:
|
|
//
|
|
// IShellView *lpPrevView - previous view
|
|
// LPCFOLDERSETTINGS lpfs - folder settings for the view
|
|
// IShellBrowser *psb - pointer to the shell browser
|
|
// RECT * prcView - view Rectangle
|
|
// HWND * phWnd - pointer to Window handle
|
|
//
|
|
//
|
|
// Comments:
|
|
//
|
|
//*****************************************************************************
|
|
|
|
STDMETHODIMP CSFView::CreateViewWindow(IShellView *lpPrevView,
|
|
LPCFOLDERSETTINGS lpfs, IShellBrowser * psb,
|
|
RECT * prcView, HWND *phWnd)
|
|
{
|
|
*phWnd = NULL;
|
|
|
|
if ((HWND)m_cView)
|
|
{
|
|
return(E_UNEXPECTED);
|
|
}
|
|
|
|
m_fs = *lpfs;
|
|
m_psb = psb;
|
|
|
|
// get the main window handle from shell browser
|
|
|
|
psb->GetWindow(&m_hwndMain);
|
|
|
|
// bring up the contents pane
|
|
|
|
if (!m_cView.DoModeless(IDD_VIEW, m_hwndMain))
|
|
{
|
|
return(E_OUTOFMEMORY);
|
|
}
|
|
|
|
*phWnd = m_cView;
|
|
|
|
// map the current view mode into menu id and set the contents pane
|
|
// view mode accordingly
|
|
|
|
OnCommand(NULL, GET_WM_COMMAND_MPS(GetMenuIDFromViewMode(), 0, 0));
|
|
|
|
AddColumns();
|
|
|
|
RestoreViewState();
|
|
|
|
// size the contents pane
|
|
SetWindowPos(m_cView, NULL, prcView->left, prcView->top,
|
|
prcView->right-prcView->left, prcView->bottom-prcView->top,
|
|
SWP_NOZORDER|SWP_SHOWWINDOW);
|
|
|
|
FillList(TRUE);
|
|
|
|
return(NOERROR);
|
|
}
|
|
|
|
|
|
STDMETHODIMP CSFView::DestroyViewWindow()
|
|
{
|
|
if (!(HWND)m_cView)
|
|
{
|
|
return(E_UNEXPECTED);
|
|
}
|
|
|
|
m_cView.DestroyWindow();
|
|
|
|
return(NOERROR);
|
|
}
|
|
|
|
|
|
STDMETHODIMP CSFView::GetCurrentInfo(LPFOLDERSETTINGS lpfs)
|
|
{
|
|
*lpfs = m_fs;
|
|
|
|
return(NOERROR);
|
|
}
|
|
|
|
|
|
STDMETHODIMP CSFView::AddPropertySheetPages(DWORD dwReserved,
|
|
LPFNADDPROPSHEETPAGE lpfn, LPARAM lparam)
|
|
{
|
|
return(E_NOTIMPL);
|
|
}
|
|
|
|
|
|
STDMETHODIMP CSFView::SaveViewState()
|
|
{
|
|
SFSTATE_HDR hdr;
|
|
LPSTREAM pstm;
|
|
|
|
HRESULT hres = m_psb->GetViewStateStream(STGM_WRITE, &pstm);
|
|
if (FAILED(hres))
|
|
{
|
|
return(hres);
|
|
}
|
|
CEnsureRelease erStr(pstm);
|
|
|
|
pstm->Write(&hdr, sizeof(hdr), NULL);
|
|
|
|
hdr.clsThis = CLSID_ThisDll;
|
|
hdr.sfState = m_sfState;
|
|
hdr.nCols = SaveColumns(pstm);
|
|
|
|
ULARGE_INTEGER libCurPosition;
|
|
LARGE_INTEGER dlibMove;
|
|
dlibMove.HighPart = 0;
|
|
dlibMove.LowPart = 0;
|
|
pstm->Seek(dlibMove, STREAM_SEEK_SET, &libCurPosition);
|
|
|
|
hres = pstm->Write(&hdr, sizeof(hdr), NULL);
|
|
|
|
return(hres);
|
|
}
|
|
|
|
|
|
STDMETHODIMP CSFView::SelectItem(LPCITEMIDLIST pidlItem, UINT uFlags)
|
|
{
|
|
return(E_NOTIMPL);
|
|
}
|
|
|
|
|
|
STDMETHODIMP CSFView::GetItemObject(UINT uItem, REFIID riid,
|
|
void **ppv)
|
|
{
|
|
return(E_NOTIMPL);
|
|
}
|
|
|
|
|
|
int CSFView::AddObject(LPCITEMIDLIST pidl)
|
|
{
|
|
// Check the commdlg hook to see if we should include this
|
|
// object.
|
|
if (IncludeObject(pidl) != S_OK)
|
|
{
|
|
return(-1);
|
|
}
|
|
|
|
return(m_cView.AddObject(pidl));
|
|
}
|
|
|
|
|
|
int CALLBACK CSFView::CompareIDs(LPVOID p1, LPVOID p2, LPARAM lParam)
|
|
{
|
|
PFNDPACOMPARE pfnCheckAPI = CompareIDs;
|
|
|
|
CSFView *pThis = (CSFView *)lParam;
|
|
|
|
HRESULT hres = pThis->m_psf->CompareIDs(pThis->m_sfState.lParamSort,
|
|
(LPITEMIDLIST)p1, (LPITEMIDLIST)p2);
|
|
|
|
|
|
return (hres);
|
|
}
|
|
|
|
//*****************************************************************************
|
|
//
|
|
// CSFView::FillList
|
|
//
|
|
// Purpose:
|
|
//
|
|
// Enumerates the objects in the namespace and fills up the
|
|
// data structures
|
|
//
|
|
//
|
|
// Comments:
|
|
//
|
|
//*****************************************************************************
|
|
|
|
HRESULT CSFView::FillList(BOOL bInteractive)
|
|
{
|
|
m_cView.DeleteAllItems();
|
|
|
|
// Setup the enum flags.
|
|
DWORD dwEnumFlags = SHCONTF_NONFOLDERS;
|
|
if (ShowAllObjects())
|
|
{
|
|
dwEnumFlags |= SHCONTF_INCLUDEHIDDEN ;
|
|
}
|
|
|
|
if (!(m_fs.fFlags & FWF_NOSUBFOLDERS))
|
|
{
|
|
dwEnumFlags |= SHCONTF_FOLDERS;
|
|
}
|
|
|
|
// Create an enum object and get the IEnumIDList ptr
|
|
LPENUMIDLIST peIDL;
|
|
HRESULT hres = m_psf->EnumObjects(bInteractive ? m_hwndMain : NULL,
|
|
dwEnumFlags, &peIDL);
|
|
|
|
// Note the return may be S_FALSE which indicates no enumerator.
|
|
// That's why we shouldn't use if (FAILED(hres))
|
|
if (hres != S_OK)
|
|
{
|
|
if (hres == S_FALSE)
|
|
{
|
|
return(NOERROR);
|
|
}
|
|
|
|
return(hres);
|
|
}
|
|
CEnsureRelease erEnum(peIDL);
|
|
|
|
HDPA hdpaNew = DPA_Create(16);
|
|
if (!hdpaNew)
|
|
{
|
|
return(E_OUTOFMEMORY);
|
|
}
|
|
|
|
LPITEMIDLIST pidl;
|
|
ULONG celt;
|
|
|
|
// Enumerate the idlist and insert into the DPA
|
|
|
|
while (peIDL->Next(1, &pidl, &celt) == S_OK)
|
|
{
|
|
if (DPA_InsertPtr(hdpaNew, 0x7fff, pidl) == -1)
|
|
{
|
|
m_cMalloc.Free(pidl);
|
|
}
|
|
}
|
|
|
|
DPA_Sort(hdpaNew, CompareIDs, (LPARAM)this);
|
|
|
|
int cNew = DPA_GetPtrCount(hdpaNew);
|
|
for (int i=0; i<cNew; ++i)
|
|
{
|
|
LPITEMIDLIST pidl = (LPITEMIDLIST)DPA_GetPtr(hdpaNew, i);
|
|
if (AddObject(pidl) < 0)
|
|
{
|
|
m_cMalloc.Free(pidl);
|
|
}
|
|
}
|
|
|
|
return(NOERROR);
|
|
}
|
|
|
|
|
|
//*****************************************************************************
|
|
//
|
|
// CSFView::AddColumns
|
|
//
|
|
// Purpose:
|
|
//
|
|
// Adds columns to the contents pane listview
|
|
//
|
|
// Comments:
|
|
//
|
|
//*****************************************************************************
|
|
void CSFView::AddColumns()
|
|
{
|
|
UINT cxChar = m_cView.CharWidth();
|
|
|
|
// add columns to the listview in the contents pane
|
|
for (int i=0; ; ++i)
|
|
{
|
|
SFVCB_GETDETAILSOF_DATA gdo;
|
|
gdo.pidl = NULL;
|
|
|
|
// get the first column
|
|
|
|
HRESULT hres = CallCB(SFVCB_GETDETAILSOF, i, (LPARAM)&gdo);
|
|
if (hres != S_OK)
|
|
{
|
|
if (i != 0)
|
|
{
|
|
break;
|
|
}
|
|
|
|
// If there is no first column, fake one up
|
|
gdo.fmt = LVCFMT_LEFT;
|
|
gdo.cChar = 40;
|
|
gdo.lParamSort = 0;
|
|
gdo.str.uType = STRRET_CSTR;
|
|
LoadString(g_ThisDll.GetInstance(), IDS_NAME, gdo.str.cStr, sizeof(gdo.str.cStr));
|
|
}
|
|
|
|
char szText[MAX_PATH];
|
|
// init the column info for the details view ...
|
|
LV_COLUMN col;
|
|
col.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;
|
|
col.fmt = gdo.fmt;
|
|
col.cx = gdo.cChar * cxChar;
|
|
col.pszText = szText;
|
|
col.cchTextMax = sizeof(szText);
|
|
col.iSubItem = i;
|
|
|
|
StrRetToStr(szText, sizeof(szText), &gdo.str, NULL);
|
|
|
|
// insert the column into the list view
|
|
if (m_cView.InsertColumn(i, &col)>=0 && m_aParamSort)
|
|
{
|
|
DPA_InsertPtr(m_aParamSort, 0x7fff, (LPVOID)gdo.lParamSort);
|
|
}
|
|
|
|
if (hres != S_OK)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Save (and check) column header information
|
|
// Returns TRUE if the columns are the default width, FALSE otherwise
|
|
// Side effect: the stream pointer is left right after the last column
|
|
//
|
|
BOOL CSFView::SaveColumns(LPSTREAM pstm)
|
|
{
|
|
UINT cxChar = m_cView.CharWidth();
|
|
BOOL bDefaultCols = TRUE;
|
|
|
|
for (int i=0; ; ++i)
|
|
{
|
|
SFVCB_GETDETAILSOF_DATA gdo;
|
|
gdo.pidl = NULL;
|
|
|
|
if (CallCB(SFVCB_GETDETAILSOF, i, (LPARAM)&gdo) != S_OK)
|
|
{
|
|
break;
|
|
}
|
|
|
|
LV_COLUMN col;
|
|
col.mask = LVCF_WIDTH;
|
|
|
|
if (!m_cView.GetColumn(i, &col))
|
|
{
|
|
// There is some problem, so just assume
|
|
// default column widths
|
|
bDefaultCols = TRUE;
|
|
break;
|
|
}
|
|
|
|
if (col.cx != (int)(gdo.cChar * cxChar))
|
|
{
|
|
bDefaultCols = FALSE;
|
|
}
|
|
|
|
// HACK: I don't really care about column widths larger
|
|
// than 64K
|
|
if (FAILED(pstm->Write(&col.cx, sizeof(USHORT), NULL)))
|
|
{
|
|
// There is some problem, so just assume
|
|
// default column widths
|
|
bDefaultCols = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return(bDefaultCols ? 0 : i);
|
|
}
|
|
|
|
|
|
void CSFView::RestoreColumns(LPSTREAM pstm, int nCols)
|
|
{
|
|
for (int i=0; i<nCols; ++i)
|
|
{
|
|
LV_COLUMN col;
|
|
col.mask = LVCF_WIDTH;
|
|
|
|
if (FAILED(pstm->Read(&col.cx, sizeof(USHORT), NULL)))
|
|
{
|
|
break;
|
|
}
|
|
|
|
m_cView.SetColumn(i, &col);
|
|
}
|
|
}
|
|
|
|
|
|
void CSFView::RestoreViewState()
|
|
{
|
|
SFSTATE_HDR hdr;
|
|
|
|
LPSTREAM pstm;
|
|
|
|
MergeToolBar();
|
|
|
|
// get the stream for storing view specific info
|
|
if (FAILED(m_psb->GetViewStateStream(STGM_READ, &pstm)))
|
|
{
|
|
return;
|
|
}
|
|
CEnsureRelease erStr(pstm);
|
|
|
|
if (FAILED(pstm->Read(&hdr, sizeof(hdr), NULL)))
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Validate the header
|
|
if (hdr.clsThis != CLSID_ThisDll)
|
|
{
|
|
return;
|
|
}
|
|
|
|
m_sfState = hdr.sfState;
|
|
RestoreColumns(pstm, hdr.nCols);
|
|
}
|
|
|
|
|
|
void CSFView::CheckToolbar()
|
|
{
|
|
UINT idCmdCurView = GetMenuIDFromViewMode();
|
|
|
|
for (UINT idCmd=IDC_VIEW_ICON; idCmd<=IDC_VIEW_DETAILS; ++idCmd)
|
|
{
|
|
m_psb->SendControlMsg(FCW_TOOLBAR, TB_CHECKBUTTON, idCmd,
|
|
(LPARAM)(idCmd == idCmdCurView), NULL);
|
|
}
|
|
}
|
|
|
|
|
|
void CSFView::MergeToolBar()
|
|
{
|
|
enum
|
|
{
|
|
IN_STD_BMP = 0x4000,
|
|
IN_VIEW_BMP = 0x8000,
|
|
} ;
|
|
static const TBBUTTON c_tbDefault[] =
|
|
{
|
|
{ STD_COPY | IN_STD_BMP, IDC_EDIT_COPY, TBSTATE_ENABLED, TBSTYLE_BUTTON, {0,0}, 0, -1},
|
|
{ 0, 0, TBSTATE_ENABLED, TBSTYLE_SEP, {0,0}, 0, -1 },
|
|
// the bitmap indexes here are relative to the view bitmap
|
|
{ VIEW_LARGEICONS | IN_VIEW_BMP, IDC_VIEW_ICON, TBSTATE_ENABLED, TBSTYLE_BUTTON, {0,0}, 0L, -1 },
|
|
{ VIEW_SMALLICONS | IN_VIEW_BMP, IDC_VIEW_SMALLICON, TBSTATE_ENABLED, TBSTYLE_BUTTON, {0,0}, 0L, -1 },
|
|
{ VIEW_LIST | IN_VIEW_BMP, IDC_VIEW_LIST, TBSTATE_ENABLED, TBSTYLE_BUTTON, {0,0}, 0L, -1 },
|
|
{ VIEW_DETAILS | IN_VIEW_BMP, IDC_VIEW_DETAILS, TBSTATE_ENABLED, TBSTYLE_BUTTON, {0,0}, 0L, -1 },
|
|
} ;
|
|
|
|
LRESULT iStdBMOffset;
|
|
LRESULT iViewBMOffset;
|
|
TBADDBITMAP ab;
|
|
ab.hInst = HINST_COMMCTRL; // hinstCommctrl
|
|
ab.nID = IDB_STD_SMALL_COLOR; // std bitmaps
|
|
m_psb->SendControlMsg(FCW_TOOLBAR, TB_ADDBITMAP, 8, (LPARAM)&ab, &iStdBMOffset);
|
|
|
|
ab.nID = IDB_VIEW_SMALL_COLOR; // std view bitmaps
|
|
m_psb->SendControlMsg(FCW_TOOLBAR, TB_ADDBITMAP, 8, (LPARAM)&ab, &iViewBMOffset);
|
|
|
|
TBBUTTON tbActual[ARRAYSIZE(c_tbDefault)];
|
|
|
|
for (int i=0; i<ARRAYSIZE(c_tbDefault); ++i)
|
|
{
|
|
tbActual[i] = c_tbDefault[i];
|
|
if (!(tbActual[i].fsStyle & TBSTYLE_SEP))
|
|
{
|
|
if (tbActual[i].iBitmap & IN_VIEW_BMP)
|
|
{
|
|
tbActual[i].iBitmap = (tbActual[i].iBitmap & ~IN_VIEW_BMP) + iViewBMOffset;
|
|
}
|
|
else if (tbActual[i].iBitmap & IN_STD_BMP)
|
|
{
|
|
tbActual[i].iBitmap = (tbActual[i].iBitmap & ~IN_STD_BMP) + iStdBMOffset;
|
|
}
|
|
}
|
|
}
|
|
|
|
m_psb->SetToolbarItems(tbActual, ARRAYSIZE(c_tbDefault), FCT_MERGE);
|
|
|
|
CheckToolbar();
|
|
}
|
|
|
|
|
|
HRESULT CreateShellFolderView(IShellFolder *psf, IShellFolderViewCallback *psfvcb,
|
|
LPSHELLVIEW * ppsv)
|
|
{
|
|
CSFView *pSFView = new CSFView(psf, psfvcb);
|
|
if (!pSFView)
|
|
{
|
|
return(E_OUTOFMEMORY);
|
|
}
|
|
|
|
pSFView->AddRef();
|
|
HRESULT hRes = pSFView->QueryInterface(IID_IShellView, (void **)ppsv);
|
|
pSFView->Release();
|
|
|
|
return(hRes);
|
|
}
|