Windows NT 4.0 source code leak
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

8733 lines
258 KiB

#include "shellprv.h"
#pragma hdrstop
// Uncomment following line to turn on timing of view enumeration
// #define TIMING 1
//
#ifdef TIMING
DWORD dwFinish, dwStart;
#endif
#include "iid.h"
#include "iasynch.h"
void CloudHookBeginRename(LPTSTR, HWND, int);
void CloudHookEndRename(void);
HRESULT SHGetIconFromPIDL(IShellFolder *psf, IShellIcon *psi, LPCITEMIDLIST pidl, UINT flags, int *piImage);
#ifndef SIF_ALL
#define SIF_ALL (SIF_RANGE | SIF_PAGE | SIF_POS)
#endif
#define ID_LISTVIEW 1
#define ID_STATIC 2
#define WM_DVI_FILLSTUFF (WM_USER + 0x100)
#define WM_DVI_LOADSTUFF (WM_USER + 0x101)
#define OD_CROSSOVER_LIMIT (50)
#define INVALID_THREAD_ID ((DWORD)-1)
#define DV_CDB_OnDefaultCommand(_pdsv) \
(_pdsv->pcdb ? _pdsv->pcdb->lpVtbl->OnDefaultCommand(_pdsv->pcdb, \
_pdsv->psvOuter ? _pdsv->psvOuter : &_pdsv->sv) : E_NOTIMPL)
#define DV_CDB_OnStateChange(_pdsv, _code) \
(_pdsv->pcdb ? _pdsv->pcdb->lpVtbl->OnStateChange(_pdsv->pcdb, \
_pdsv->psvOuter ? _pdsv->psvOuter : &_pdsv->sv, _code) : E_NOTIMPL)
#define DV_CDB_IncludeObject(_pdsv, _pidl) \
(_pdsv->pcdb ? _pdsv->pcdb->lpVtbl->IncludeObject(_pdsv->pcdb, \
_pdsv->psvOuter ? _pdsv->psvOuter : &_pdsv->sv, _pidl) : S_OK)
BOOL WINAPI DAD_DragEnterEx(HWND hwndTarget, const POINT ptStart);
extern BOOL g_fDraggingOverSource;
#pragma data_seg(DATASEG_PERINSTANCE)
struct _DEFVIEWPROCINFO
{
UINT cRef;
HANDLE hThreadIdle;
DWORD idThreadIdle;
} gp_dvp =
{
0,
NULL,
0,
} ;
#pragma data_seg()
const TCHAR c_szDefViewClass[] = TEXT("SHELLDLL_DefView");
const TCHAR c_szAnimateClass[] = ANIMATE_CLASS;
#ifdef CUST_TOOLBAR
const TCHAR c_szDefViewToobar[] = TEXT("DefViewToolbar");
#endif
const TCHAR c_szDesktopKey[] = REGSTR_PATH_DESKTOP;
const TCHAR c_szWallpaper[] = TEXT("Wallpaper");
const TCHAR c_szPattern[] = TEXT("Pattern");
typedef struct
{
LPARAM lParamSort;
int iDirection;
int iLastColumnClick;
} DVSAVESTATE, *PDVSAVESTATE;
typedef struct
{
USHORT cbSize;
UINT ViewMode;
POINTS ptScroll;
USHORT cbColOffset;
USHORT cbPosOffset;
DVSAVESTATE dvState;
} DVSAVEHEADER, *PDVSAVEHEADER;
#define IsDefaultState(_dvHead) ((_dvHead).dvState.lParamSort == 0 && \
(_dvHead).dvState.iDirection == 1 && \
(_dvHead).dvState.iLastColumnClick == -1 && \
(_dvHead).ptScroll.x == 0 && (_dvHead).ptScroll.y == 0)
typedef struct
{
POINT pt;
ITEMIDLIST idl;
} DVITEM, *PDVITEM;
typedef struct
{
int xMul;
int xDiv;
int yMul;
int yDiv;
} SCALEINFO, *LPSCALEINFO;
//=============================================================================
// CDVDropTarget : class definition
//=============================================================================
typedef struct { // dvdt
IDropTarget dt;
LPDATAOBJECT pdtobj; // from DragEnter()/Drop()
RECT rcLockWindow; // WindowRect of hwnd for DAD_ENTER
int itemOver; // item we are visually dragging over
LPDROPTARGET pdtgtCur; // current drop target, derived from hit testing
DWORD dwEffectOut; // last *pdwEffect out
DWORD grfKeyState; // cached key state
POINT ptLast; // last dragged position
AUTO_SCROLL_DATA asd; // for auto scrolling
} CDVDropTarget;
//
// Class definition of CDefView
//
typedef struct { // dsv
IShellView sv;
CDVDropTarget dvdt;
UINT cRef;
IShellView * psvOuter; // May be NULL
IShellFolder *pshf;
IShellBrowser *psb;
ICommDlgBrowser *pcdb;
FOLDERSETTINGS fs;
IContextMenu *pcmSel; // pcm for selected objects.
DWORD dwAttrSel; // dwAttrs for selected objects
IShellIcon * psi; // for getting icon fast
IAsynchEnumIDList *pasyncenum;
HWND hwndMain;
HWND hwndView;
HWND hwndListview;
HWND hwndStatic;
HACCEL hAccel;
BOOL fEnumFailed; // TRUE if enum failed.
HRESULT hres; // Enum result
UINT uState; // SVUIA_*
HMENU hmenuCur;
ULONG uRegister;
POINT ptDrop;
POINT ptDragAnchor; // start of the drag
int itemCur; // The current item in the drop target
IDataObject *pdtobjHdrop; // for 3.1 HDROP drag/drop
IDropTarget *pdtgtBack; // of the background (shell folder)
// BUGBUG: this is obsolete, use callback (DVM_GETDETAILSOF and DVM_COLUMNCLICK)
IShellDetails *psd;
// Officially, pdr should be an IDelayedRelease interface, but it
// only has IUnknown member functions, so why bother?
IUnknown *pdr;
UINT cxChar;
LPCITEMIDLIST pidlMonitor;
LONG lFSEvents;
DVSAVESTATE dvState;
PDVSAVEHEADER pSaveHeader;
UINT uSaveHeaderLen;
HANDLE hThreadIdle; // handle of the idle proc thread
DWORD idThreadIdle; // ID of the idle proc thread
int cRefForIdle; // did idle thread forget
// to release this
BOOL bDragSource:1;
BOOL bDropAnchor:1;
BOOL bItemsMoved:1;
BOOL bClearItemPos:1;
BOOL bHaveCutStuff:1;
BOOL bClipViewer:1;
BOOL fShowAllObjects:1;
BOOL fInLabelEdit:1;
BOOL fDisabled:1;
BOOL bUpdatePending:1;
BOOL bBkFilling:1;
BOOL bContextMenuMode:1;
BOOL bMouseMenu:1;
BOOL fHasDeskWallPaper:1;
BOOL fIsOwnerData:1;
#ifdef CUST_TOOLBAR
BOOL fToolbarSaved; // avoid saving twice
#endif // CUST_TOOLBAR
HWND hwndNextViewer;
LONG lSelChangeInfo;
int iStdBMOffset;
int iViewBMOffset;
LPFNVIEWCALLBACK pfnCallback; // Optional client callback
LPITEMIDLIST pidlSelect; // Item to select when background
UINT uFlagsSelect; // enumeration is done
int iLastFind;
#ifdef DEBUG
TIMEVAR(Update);
TIMEVAR(Fill);
TIMEVAR(GetIcon);
TIMEVAR(GetName);
TIMEVAR(FSNotify);
TIMEVAR(AddObject);
TIMEVAR(EnumNext);
TIMEVAR(RestoreState);
TIMEVAR(WMNotify);
TIMEVAR(LVChanging);
TIMEVAR(LVChanged);
TIMEVAR(LVDelete);
TIMEVAR(LVGetDispInfo);
#endif
} CDefView, *LPDEFVIEW;
LPDEFVIEW WINAPI DV_HwndMain2DefView(HWND hwndMain);
#define DV_ISOWNERDATA(pdsv) (pdsv->fIsOwnerData)
//
// Note that it returns NULL, if iItem is -1.
//
__inline LPITEMIDLIST DSV_GetPIDL(LPDEFVIEW this, INT i)
{
//
// If the listview is in ownerdata mode, we need to go to the
// item cache in order to get the pidl. Otherwise, we just
// ask for the lParam of the listview item in question
//
if (DV_ISOWNERDATA(this))
{
LPITEMIDLIST pidl = NULL;
ASSERT(this->pasyncenum);
this->pasyncenum->lpVtbl->NextAt(this->pasyncenum, 1, i, &pidl, NULL);
return pidl;
}
else
{
return (LPITEMIDLIST) LVUtil_GetLParam(this->hwndListview, i);
}
}
DWORD CALLBACK DefView_LoadIcons(LPDEFVIEW this, BOOL bAnother);
LRESULT DefView_Command(LPDEFVIEW pdsv, LPCONTEXTMENU pcmSel, WPARAM wParam, LPARAM lParam);
STDMETHODIMP CDefView_SelectItem(IShellView * psv, LPCITEMIDLIST pidlItem, UINT uFlags);
STDMETHODIMP CDefView_GetItemObject(IShellView * psv, UINT uFlags, REFIID riid, LPVOID *ppv);
STDMETHODIMP CDefView_Refresh(LPSHELLVIEW psv);
STDMETHODIMP CDefView_SaveViewState(IShellView *psv);
LRESULT DV_OldDragMsgs(LPDEFVIEW pdsv, UINT iMessage, WPARAM wParam, const DROPSTRUCT * lpds);
void DV_UpdateStatusBar(LPDEFVIEW pdsv, BOOL fInitialize);
void DefView_MergeToolBar(LPDEFVIEW this, BOOL bCanRestore);
LRESULT DefView_TBNotify(LPDEFVIEW pdsv, NMHDR *pnm);
int DefView_FindItem(LPDEFVIEW this, LPCITEMIDLIST pidl, LPITEMIDLIST *ppidlFound, BOOL fSamePtr);
BOOL CALLBACK DV_ViewOptionsDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
BOOL CDefView_OnActivate(LPDEFVIEW this, UINT uState);
BOOL CDefView_OnDeactivate(LPDEFVIEW this);
void DefView_ExplorerCommand(LPDEFVIEW pdsv, UINT idFCIDM);
void DefView_DismissEdit(LPDEFVIEW pdsv);
void DefView_OnInitMenu(LPDEFVIEW pdsv);
LRESULT DefView_OnInitMenuPopup(LPDEFVIEW this, HMENU hSubMenu, int nIndex, BOOL fSystemMenu);
void DefView_InitViewMenu(LPDEFVIEW this, HMENU hmInit);
LRESULT DefView_OnMenuSelect(LPDEFVIEW pdsv, UINT id, UINT mf, HMENU hmenu);
void DV_GetMenuHelpText(LPDEFVIEW pdsv, UINT id, LPTSTR pszText, UINT cchText);
void DV_GetToolTipText(LPDEFVIEW pdsv, UINT id, LPTSTR pszText, UINT cchText);
void DefView_ContextMenu(LPDEFVIEW pdsv, DWORD dwPos);
void DV_DoDefaultStatusBar(LPDEFVIEW pdsv, BOOL fInitialize);
BOOL DefView_GetDropPoint(LPDEFVIEW pdv, POINT *lpPt);
BOOL DefView_GetDragPoint(LPDEFVIEW pdv, POINT *lpPt);
void DefView_MoveSelectedItems(LPDEFVIEW pdsv, int dx, int dy);
BOOL DefView_GetItemPosition(LPDEFVIEW pdv, LPCITEMIDLIST pidl, LPPOINT lpPt);
DWORD LVStyleFromView(LPDEFVIEW lpdv);
void CDVDropTarget_LeaveAndReleaseData(LPDEFVIEW this);
void DefView_AddCopyHook(LPDEFVIEW this);
void DefView_RemoveCopyHook(LPDEFVIEW this);
HRESULT DefView_SelectAndPositionItem(IShellView *psv, LPCITEMIDLIST pidlItem, UINT uFlags, POINT *ppt);
HRESULT DefView_GetItemObjects(LPDEFVIEW pdsv, LPCITEMIDLIST **ppidl,
UINT uItem);
void CDVDropTarget_ReleaseCurrentDropTarget(CDVDropTarget * this);
void CDVDropTarget_ReleaseDataObject(CDVDropTarget * this);
HRESULT DefView_FillObjectsShowHide(LPDEFVIEW pdsv, BOOL bRefresh,
PDVSAVEHEADER pSaveHeader, UINT uLen, BOOL fInteractive);
BOOL DefView_IdleDoStuff(LPDEFVIEW pdsv, UINT message, LPARAM lParam);
HRESULT DV_AllocRestOfStream(LPSTREAM pstm, LPVOID *ppData, UINT *puLen);
#define DV_ISDESKTOP(pdsv) (pdsv->fs.fFlags & FWF_DESKTOP)
// determine if color is light or dark
#define COLORISLIGHT(clr) ((5*GetGValue((clr)) + 2*GetRValue((clr)) + GetBValue((clr))) > 8*128)
//----------------------------------------------------------------------------
#define MINVIEWWIDTH 170
#define MINVIEWHEIGHT 132
// REVIEW UNDONE - calculate these, don't guess!
#define PARENTGAPWIDTH 40
#define PARENTGAPHEIGHT 32
//----------------------------------------------------------------------------
#ifdef DEBUG
void DefView_StartNotify(LPDEFVIEW pdsv, NMHDR *pnm)
{
if (pdsv == NULL)
return;
TIMESTART(pdsv->WMNotify);
if (pnm == NULL)
return;
switch (pnm->code) {
case LVN_ITEMCHANGING:
TIMESTART(pdsv->LVChanging);
break;
case LVN_ITEMCHANGED:
TIMESTART(pdsv->LVChanged);
break;
case LVN_DELETEITEM:
TIMESTART(pdsv->LVDelete);
break;
case LVN_GETDISPINFO:
TIMESTART(pdsv->LVGetDispInfo);
break;
}
}
void DefView_StopNotify(LPDEFVIEW pdsv, NMHDR *pnm)
{
if (pdsv == NULL)
return;
TIMESTOP(pdsv->WMNotify);
if (pnm == NULL)
return;
switch (pnm->code) {
case LVN_ITEMCHANGING:
TIMESTOP(pdsv->LVChanging);
break;
case LVN_ITEMCHANGED:
TIMESTOP(pdsv->LVChanged);
break;
case LVN_DELETEITEM:
TIMESTOP(pdsv->LVDelete);
break;
case LVN_GETDISPINFO:
TIMESTOP(pdsv->LVGetDispInfo);
break;
}
}
HRESULT DV_Next(LPDEFVIEW pdsv, LPENUMIDLIST peunk, int cnt, LPITEMIDLIST *ppidl, ULONG *pcelt)
{
HRESULT hres;
TIMESTART(pdsv->EnumNext);
hres = peunk->lpVtbl->Next(peunk, cnt, ppidl, pcelt);
TIMESTOP(pdsv->EnumNext);
return hres;
}
TCHAR *DV_Name(LPDEFVIEW pdsv)
{
static TCHAR ach[128];
GetWindowText(pdsv->hwndMain, ach, ARRAYSIZE(ach));
return ach;
}
#else
#define DefView_StartNotify(pdsv, pnm)
#define DefView_StopNotify(pdsv, pnm)
#define DV_Next(pdsv,peunk,cnt,ppidl,pcelt) peunk->lpVtbl->Next(peunk, cnt, ppidl, pcelt)
#define DV_Name(pdsv) NULL
#endif
//----------------------------------------------------------------------------
BOOL DV_IsDropOnSource(LPDEFVIEW pdsv, LPDROPTARGET pdtgt)
{
// context menu paste (bMouseMenu shows context menu, cut stuff shows source)
if (pdsv->bMouseMenu && pdsv->bHaveCutStuff) {
int iItem = ListView_GetNextItem(pdsv->hwndListview, -1, LVNI_SELECTED);
if (iItem == -1) {
return TRUE;
}
}
//
// If pdtgt is specified, it should match.
//
if (pdtgt && (pdtgt != pdsv->pdtgtBack))
{
return FALSE;
}
if (pdsv->itemCur != -1 || !pdsv->bDragSource)
{
// We did not drag onto the background of the source
return FALSE;
}
return TRUE;
}
void DV_MoveIcons(LPDEFVIEW pdsv, LPDATAOBJECT pDataObj)
{
STGMEDIUM medium;
POINT pt, *lpPts;
FORMATETC fmt = {g_cfOFFSETS, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
switch (LVStyleFromView(pdsv) & LVS_TYPEMASK) {
// We are in a mode that does not allow icon moving
case LVS_LIST:
case LVS_REPORT:
return;
}
if (SUCCEEDED(pDataObj->lpVtbl->GetData(pDataObj, &fmt, &medium)))
{
lpPts = (POINT *)medium.hGlobal;
if (lpPts)
{
DefView_GetDropPoint(pdsv, &pt);
DefView_MoveSelectedItems(pdsv, pt.x - lpPts[0].x, pt.y - lpPts[0].y);
}
SHReleaseStgMedium(&medium);
}
}
// Set the colors for the folder - taking care if it's the desktop.
void DSV_SetFolderColors(LPDEFVIEW pdsv)
{
COLORREF clrText, clrTextBk, clrWindow;
// Is this view for the desktop?
if (DV_ISDESKTOP(pdsv))
{
TCHAR szWallpaper[128];
TCHAR szPattern[128];
HKEY hkey;
UINT cb;
Shell_SysColorChange();
// Yep.
// Clear the background color of the desktop to make it
// properly handle transparency.
clrTextBk = GetSysColor(COLOR_BACKGROUND);
// set a text color that will show up over desktop color
if (COLORISLIGHT(clrTextBk))
clrText = 0x000000; // black
else
clrText = 0xFFFFFF; // white
//
// if there is no wallpaper or pattern we can use
// a solid color for the ListView. otherwise we
// need to use a transparent ListView, this is much
// slower so dont do it unless we need to.
//
// dont do this optimization in FailSafe boot, otherwise
// the "FailSafe" text on the desktop can't be seen.
//
// dont do this optimization in DEBUG either, for the
// same reason.
//
// too bad there is no SPI_GETWALLPAPER, we need to read
// from WIN.INI.
//
// BUGBUG we assume the string for none starts with a '('
// BUGBUG we dont know if a random app has subclassed
// BUGBUG ..the desktop, we should check this case too.
//
szWallpaper[0] = 0;
szPattern[0] = 0;
if (RegOpenKey(HKEY_CURRENT_USER, c_szDesktopKey, &hkey) == 0)
{
cb = SIZEOF(szWallpaper);
RegQueryValueEx(hkey, c_szWallpaper, NULL, NULL, (LPBYTE)szWallpaper, &cb);
cb = SIZEOF(szPattern);
RegQueryValueEx(hkey, c_szPattern, NULL, NULL, (LPBYTE)szPattern, &cb);
RegCloseKey(hkey);
}
if (GetSystemMetrics(SM_CLEANBOOT) == 0 &&
GetSystemMetrics(SM_DEBUG) == 0 &&
(!pdsv->fHasDeskWallPaper) &&
(szWallpaper[0] == 0 || szWallpaper[0] == TEXT('(')) &&
(szPattern[0] == 0 || szPattern[0] == TEXT('(')))
{
clrWindow = GetSysColor(COLOR_BACKGROUND);
}
else
{
clrWindow = CLR_NONE;
}
}
else
{
// Nope.
clrWindow = GetSysColor(COLOR_WINDOW);
clrTextBk = clrWindow;
clrText = GetSysColor(COLOR_WINDOWTEXT);
}
ListView_SetBkColor(pdsv->hwndListview, clrWindow);
ListView_SetTextBkColor(pdsv->hwndListview, clrTextBk);
ListView_SetTextColor(pdsv->hwndListview, clrText);
}
//----------------------------------------------------------------------------
DWORD LVStyleFromView(LPDEFVIEW pdv)
{
DWORD dwStyle;
if (DV_ISDESKTOP(pdv))
{
dwStyle = LVS_ICON | LVS_NOSCROLL | LVS_ALIGNLEFT;
}
else
{
switch (pdv->fs.ViewMode) {
case FVM_LIST:
dwStyle = LVS_LIST;
break;
case FVM_DETAILS:
dwStyle = LVS_REPORT;
break;
case FVM_SMALLICON:
dwStyle = LVS_SMALLICON;
break;
default:
DebugMsg(DM_ERROR, TEXT("Unknown ViewMode value"));
// fall through...
case FVM_ICON:
dwStyle = LVS_ICON;
break;
}
}
if (pdv->fs.fFlags & FWF_AUTOARRANGE)
dwStyle |= LVS_AUTOARRANGE;
if (pdv->fs.fFlags & FWF_SINGLESEL)
dwStyle |= LVS_SINGLESEL;
return dwStyle;
}
void DefView_AddColumns(LPDEFVIEW pdsv)
{
int i = 0, iEndWid;
USHORT *pColWid;
LV_COLUMN col;
TCHAR szInfo[MAX_PATH];
HDC hdc;
SIZE siz;
DETAILSINFO di;
// I also use this as a flag for whether to free pColWid
LPSTREAM pstmCols = NULL;
//
// Calculate a reasonable size to initialize the column width to.
hdc = GetDC(HWND_DESKTOP);
SelectFont(hdc, FORWARD_WM_GETFONT(pdsv->hwndListview, SendMessage));
GetTextExtentPoint(hdc, TEXT("0"), 1, &siz);
ReleaseDC(HWND_DESKTOP, hdc);
pdsv->cxChar = siz.cx;
if (!pdsv->psd && !pdsv->pfnCallback)
{
goto Error1;
}
if (pdsv->pfnCallback && SUCCEEDED(pdsv->pfnCallback(pdsv->psvOuter,
pdsv->pshf, pdsv->hwndMain, DVM_GETCOLSAVESTREAM, STGM_READ,
(LPARAM)&pstmCols)))
{
UINT uLen = 0;
HRESULT hres = DV_AllocRestOfStream(pstmCols, &pColWid, &uLen);
pstmCols->lpVtbl->Release(pstmCols);
if (SUCCEEDED(hres))
{
iEndWid = uLen / SIZEOF(*pColWid);
}
else
{
// Make sure we do not try to Free below
pstmCols = NULL;
iEndWid = 0;
}
}
else if (pdsv->pSaveHeader && pdsv->pSaveHeader->cbColOffset>=SIZEOF(DVSAVEHEADER))
{
USHORT *pEndWid;
pColWid = (USHORT *)(((BYTE *)pdsv->pSaveHeader)
+ pdsv->pSaveHeader->cbColOffset);
pEndWid = (USHORT *)(((BYTE *)pdsv->pSaveHeader)
+ pdsv->pSaveHeader->cbPosOffset);
iEndWid = pEndWid - pColWid;
}
else
{
iEndWid = 0;
}
for ( ; ; ++i)
{
di.fmt = LVCFMT_LEFT;
di.cxChar = 20;
di.str.uType = (UINT)-1;
di.pidl = NULL;
if (pdsv->psd)
{
if(FAILED(pdsv->psd->lpVtbl->GetDetailsOf(pdsv->psd, NULL, i,
(LPSHELLDETAILS)&di.fmt)))
{
break;
}
}
else if (pdsv->pfnCallback)
{
if (FAILED(pdsv->pfnCallback(pdsv->psvOuter, pdsv->pshf,
pdsv->hwndMain, DVM_GETDETAILSOF, i, (LPARAM)&di)))
{
break;
}
}
col.mask = LVCF_ALL;
col.fmt = di.fmt;
if (i < iEndWid)
{
col.cx = pColWid[i];
}
else
{
col.cx = di.cxChar * siz.cx;
}
col.pszText = szInfo;
col.cchTextMax = ARRAYSIZE(szInfo);
col.iSubItem = i;
StrRetToStrN(col.pszText, col.cchTextMax, &di.str, NULL);
ListView_InsertColumn(pdsv->hwndListview, i, &col);
}
if (pstmCols)
{
LocalFree((HLOCAL)pColWid);
}
Error1:;
if (pdsv->dvState.iLastColumnClick >= i) {
pdsv->dvState.lParamSort = 0;
pdsv->dvState.iDirection = 1;
pdsv->dvState.iLastColumnClick = -1;
}
// Add in Name Column, such that report view has at least one...
if (i == 0)
{
LoadString(HINST_THISDLL, IDS_NAME_COL, szInfo, ARRAYSIZE(szInfo));
col.mask = LVCF_ALL;
col.fmt = LVCFMT_LEFT;
col.pszText = szInfo;
col.cchTextMax = 0;
col.cx = 30 * siz.cx;
col.iSubItem = -1;
ListView_InsertColumn(pdsv->hwndListview, 0, &col);
}
}
TCHAR const c_szListViewClass[] = WC_LISTVIEW;
LRESULT DefView_CreateListView(LPDEFVIEW pdsv, HWND hWnd)
{
DWORD dwStyle;
DWORD dwExStyle;
HIMAGELIST himlLarge, himlSmall;
ULONG rgfAttr;
// Map the ViewMode to the proper listview style
dwStyle = LVStyleFromView(pdsv);
dwExStyle = 0;
rgfAttr = SFGAO_CANRENAME;
if (SUCCEEDED(pdsv->pshf->lpVtbl->GetAttributesOf(pdsv->pshf, 0, NULL, &rgfAttr))
&& (rgfAttr & SFGAO_CANRENAME))
{
dwStyle |= LVS_EDITLABELS;
}
//
// If this needs to be an ownerdata list view, set the style accordingly
//
if (DV_ISOWNERDATA(pdsv))
{
dwStyle |= LVS_OWNERDATA;
}
if (!DV_ISDESKTOP(pdsv))
{
dwExStyle |= WS_EX_CLIENTEDGE;
//dwStyle |= WS_HSCROLL | WS_VSCROLL;
}
// We don't do ownerdata desktops
ASSERT(FALSE == DV_ISOWNERDATA(pdsv) || FALSE == DV_ISDESKTOP(pdsv));
pdsv->hwndListview = CreateWindowEx(dwExStyle, c_szListViewClass, NULL,
WS_CHILD | WS_CLIPCHILDREN | dwStyle | LVS_SHAREIMAGELISTS,
0, 0, 0, 0, hWnd, (HMENU)ID_LISTVIEW, HINST_THISDLL, NULL);
if (!pdsv->hwndListview)
{
DebugMsg(DM_TRACE, TEXT("Failed to create view window"));
return -1L; // failure
}
Shell_GetImageLists(&himlLarge, &himlSmall);
ListView_SetImageList(pdsv->hwndListview, himlLarge, LVSIL_NORMAL);
ListView_SetImageList(pdsv->hwndListview, himlSmall, LVSIL_SMALL);
// Give the listview an initial item count if its ownerdata
if (DV_ISOWNERDATA(pdsv))
{
ULONG cCount = pdsv->pasyncenum->lpVtbl->GetCount(pdsv->pasyncenum);
ListView_SetItemCount(pdsv->hwndListview, cCount);
}
Assert(pdsv->psd == NULL); // this should not be set yet...
pdsv->pshf->lpVtbl->CreateViewObject(pdsv->pshf, pdsv->hwndMain, &IID_IShellDetails, &pdsv->psd);
DefView_AddColumns(pdsv);
DSV_SetFolderColors(pdsv);
return 0; // success
}
LRESULT DefView_WndCreate(HWND hWnd, LPCREATESTRUCT lpcs)
{
LPDEFVIEW pdsv = (LPDEFVIEW)lpcs->lpCreateParams;
SetWindowLong(hWnd, 0, (LONG)pdsv);
pdsv->sv.lpVtbl->AddRef(&pdsv->sv); // hwnd -> pdsv
pdsv->hwndView = hWnd;
pdsv->hmenuCur = NULL;
pdsv->uState = SVUIA_DEACTIVATE;
pdsv->hAccel = LoadAccelerators(HINST_THISDLL, MAKEINTRESOURCE(ACCEL_DEFVIEW));
//
// Note that we are going to get a WM_SIZE message soon, which will
// place this window correctly
//
return DefView_CreateListView(pdsv, hWnd);
}
LRESULT DefView_WndSize(HWND hWnd, LPDEFVIEW pdsv)
{
RECT rc;
// We need to dismiss "name edit" mode, if we are in.
DefView_DismissEdit(pdsv);
// Move the listview to match the View window.
GetClientRect(hWnd, &rc);
if (pdsv->hwndStatic)
{
MoveWindow(pdsv->hwndStatic, rc.left, rc.top, rc.right-rc.left,
rc.bottom-rc.top, TRUE);
RedrawWindow(pdsv->hwndStatic, NULL, NULL, RDW_ERASE |RDW_INVALIDATE);
}
MoveWindow(pdsv->hwndListview, rc.left, rc.top, rc.right-rc.left,
rc.bottom-rc.top, TRUE);
return(1);
}
UINT _DSV_GetMenuIDFromViewMode(UINT ViewMode)
{
switch (ViewMode) {
case FVM_SMALLICON:
return SFVIDM_VIEW_SMALLICON;
case FVM_LIST:
return SFVIDM_VIEW_LIST;
case FVM_DETAILS:
return SFVIDM_VIEW_DETAILS;
case FVM_ICON:
default:
return SFVIDM_VIEW_ICON;
}
}
void _DSV_CheckToolbar(LPDEFVIEW this)
{
UINT idCmd;
UINT idCmdCurView = _DSV_GetMenuIDFromViewMode(this->fs.ViewMode);
for (idCmd = SFVIDM_VIEW_ICON; idCmd <= SFVIDM_VIEW_DETAILS; idCmd++)
{
this->psb->lpVtbl->SendControlMsg(this->psb,
FCW_TOOLBAR, TB_CHECKBUTTON, idCmd, (LPARAM)(idCmd == idCmdCurView), NULL);
}
}
void DSV_OnListViewDelete(LPDEFVIEW this, int iItem, LPITEMIDLIST pidl)
{
if (pidl)
ILFree(pidl);
}
// NOTE: many keys are handled as accelerators
void DefView_HandleKeyDown(LPDEFVIEW this, LV_KEYDOWN *lpnmhdr)
{
// REVIEW: these are things not handled by accelerators, see if we can
// make them all based on accelerators
switch (lpnmhdr->wVKey) {
case VK_ESCAPE:
if (this->bHaveCutStuff)
SHSetClipboard(NULL);
break;
}
}
//
// DefView_GetItemPIDLS
//
// This function returns an array of LPCITEMIDLIST for "selected" objects in the
// listview. It always returns the number of selected objects. Typically, the
// client (1) calls this function with cItemMax==0 to know the required size for the
// array, (2) allocates a block of memory for the array, and (3) calls this function
// again to get the list of LPCITEMIDLIST.
//
// Notes: Note that this function returns LP*C*ITEMIDLIST. The caller is not
// supposed alter or delete them. Their lifetime are very short (until the
// list view is modified).
//
UINT DefView_GetItemPIDLS(LPDEFVIEW pdsv, LPCITEMIDLIST apidl[], UINT cItemMax,
UINT uItem)
{
// REVIEW: We should put the focused one at the top of the list.
int iItem = -1;
int iItemFocus = -1;
UINT cItem = 0;
UINT uType;
switch (uItem)
{
case SVGIO_SELECTION:
// special case for faster search
if (!cItemMax) {
return ListView_GetSelectedCount(pdsv->hwndListview);
}
iItemFocus = ListView_GetNextItem(pdsv->hwndListview, -1, LVNI_FOCUSED);
uType = LVNI_SELECTED;
break;
case SVGIO_ALLVIEW:
// special case for faster search
if (!cItemMax)
return ListView_GetItemCount(pdsv->hwndListview);
uType = LVNI_ALL;
break;
}
while((iItem = ListView_GetNextItem(pdsv->hwndListview, iItem, uType))!=-1)
{
if (cItem < cItemMax)
{
// Check if the item is the focused one or not.
if (iItem == iItemFocus)
{
// Yes, put it at the top.
apidl[cItem] = apidl[0];
apidl[0] = DSV_GetPIDL(pdsv, iItem);
}
else
{
// No, put it at the end of the list.
apidl[cItem] = DSV_GetPIDL(pdsv, iItem);
}
}
cItem++;
}
return cItem;
}
//
// This function get the array of IDList from the selection and calls
// IShellFolder::GetUIObjectOf member to get the specified UI object
// interface.
//
HRESULT DefView_GetUIObjectFromItem(LPDEFVIEW pdsv, REFIID riid, LPVOID * ppv,
UINT uItem)
{
LPCITEMIDLIST * apidl;
HRESULT hres = DefView_GetItemObjects(pdsv, &apidl, uItem);
UINT cItems = ShortFromResult(hres);
if (SUCCEEDED(hres))
{
if (cItems)
{
hres = pdsv->pshf->lpVtbl->GetUIObjectOf(pdsv->pshf,
pdsv->hwndMain, cItems, apidl, riid, 0, ppv);
LocalFree((HLOCAL)apidl);
}
else
{
hres = E_INVALIDARG;
}
}
return hres;
}
//
// This function creates the cached context menu for the current selection
// and update the cache, if it is not created yet. Then, returns a copied
// pointer to it. The caller should Release() it.
//
LPCONTEXTMENU DefView_GetContextMenuFromSelection(LPDEFVIEW pdsv)
{
LPCONTEXTMENU pcm = NULL;
if (pdsv->pcmSel == NULL)
{
if (FAILED(DefView_GetUIObjectFromItem(pdsv, &IID_IContextMenu,
&pdsv->pcmSel, SVGIO_SELECTION)))
{
pdsv->pcmSel = NULL;
}
}
if (pdsv->pcmSel)
{
pcm = pdsv->pcmSel;
pcm->lpVtbl->AddRef(pcm);
}
Assert(pcm == pdsv->pcmSel);
return pcm;
}
#define DEFAULT_ATTRIBUTES (DROPEFFECT_LINK | DROPEFFECT_MOVE | DROPEFFECT_COPY | SFGAO_CANDELETE | SFGAO_CANRENAME | SFGAO_HASPROPSHEET)
DWORD DefView_GetAttributesFromSelection(LPDEFVIEW pdsv, DWORD dwAttrMask)
{
DWORD dwAttrQuery = DEFAULT_ATTRIBUTES | dwAttrMask;
if ((pdsv->dwAttrSel == (DWORD)-1) || (dwAttrQuery != DEFAULT_ATTRIBUTES))
{
LPCITEMIDLIST *apidl;
HRESULT hres = DefView_GetItemObjects(pdsv, &apidl, SVGIO_SELECTION);
//
// this cache was written right before RC1 if you hit this after fix it
//
Assert(dwAttrQuery == DEFAULT_ATTRIBUTES);
if (SUCCEEDED(hres))
{
UINT cItems = ShortFromResult(hres);
if (cItems)
{
if (SUCCEEDED(pdsv->pshf->lpVtbl->GetAttributesOf(pdsv->pshf,
cItems, apidl, &dwAttrQuery)))
{
pdsv->dwAttrSel = dwAttrQuery;
}
LocalFree((HLOCAL)apidl);
} else {
// mask out attrib bits here... if there's no selection, we can't
// rename, delete, link, prop...
// BUGBUG: maybe this should be set to 0, but I'm afraid of what
// yanking SFGAO_FILESYS and other random stuff will do to us..
pdsv->dwAttrSel &= ~(DROPEFFECT_LINK |
DROPEFFECT_MOVE |
DROPEFFECT_COPY |
SFGAO_CANDELETE |
SFGAO_CANRENAME |
SFGAO_HASPROPSHEET);
}
}
}
return (pdsv->dwAttrSel & dwAttrMask);
}
void DV_FlushCachedMenu(LPDEFVIEW pdsv)
{
if (pdsv->pcmSel)
{
LPCONTEXTMENU pcm = pdsv->pcmSel;
pdsv->pcmSel = NULL;
pcm->lpVtbl->Release(pcm);
}
}
void DefView_ContextMenu(LPDEFVIEW pdsv, DWORD dwPos)
{
int iItem;
int idCmd;
int idDefault = -1;
int nInsert;
HMENU hmContext;
LPCONTEXTMENU pcm = NULL;
UINT fFlags = 0;
POINT pt;
// Find the selected item
iItem = ListView_GetNextItem(pdsv->hwndListview, -1, LVNI_SELECTED);
if (dwPos == (DWORD) -1)
{
if (iItem != -1)
{
RECT rc;
int iItemFocus = ListView_GetNextItem(pdsv->hwndListview, -1, LVNI_FOCUSED|LVNI_SELECTED);
if (iItemFocus == -1)
iItemFocus = iItem;
//
// Note that LV_GetItemRect returns it in client coordinate!
//
ListView_GetItemRect(pdsv->hwndListview, iItemFocus, &rc, LVIR_ICON);
pt.x = (rc.left+rc.right)/2;
pt.y = (rc.top+rc.bottom)/2;
}
else
{
pt.x = pt.y = 0;
}
MapWindowPoints(pdsv->hwndListview, HWND_DESKTOP, &pt, 1);
}
else
{
pt.x = LOWORD(dwPos);
pt.y = HIWORD(dwPos);
}
if (iItem == -1)
{
// No selected item; use the background context menu
hmContext = _LoadPopupMenu(POPUP_SFV_BACKGROUND);
nInsert = -1;
if (!hmContext)
{
// BUGBUG: There should be an error message here
return;
}
// HACK: we are only initializing the Paste command, so we don't
// need any attributes
Def_InitEditCommands(0, hmContext, SFVIDM_FIRST, pdsv->pdtgtBack, DIEC_BACKGROUNDCONTEXT);
DefView_InitViewMenu(pdsv, hmContext);
if (SUCCEEDED(pdsv->pshf->lpVtbl->CreateViewObject(pdsv->pshf, pdsv->hwndMain,
&IID_IContextMenu, &pcm)))
{
pdsv->pcmSel = pcm;
pcm->lpVtbl->AddRef(pcm);
}
else
{
Assert(FALSE);
}
#if DEBUG
if (GetKeyState(VK_CONTROL)<0)
{
HMENU hmenuDebug = _LoadPopupMenu(POPUP_DEBUG_DEFVIEW);
if (hmenuDebug)
{
InsertMenu(hmContext, (UINT)-1, MF_POPUP|MF_BYPOSITION, (UINT)hmenuDebug, TEXT("(&Debug)"));
}
}
#endif
}
else
{
fFlags |= CMF_CANRENAME;
hmContext = CreatePopupMenu();
nInsert = 0;
if (!hmContext)
{
// BUGBUG: There should be an error message here
return;
}
// One or more items are selected, let the folder add menuitems.
pcm = DefView_GetContextMenuFromSelection(pdsv);
}
if (pcm)
{
if (pdsv->psb) {
HWND hwnd = NULL;
pdsv->psb->lpVtbl->GetControlWindow(pdsv->psb, FCW_TREE, &hwnd);
if (hwnd) {
fFlags |= CMF_EXPLORE;
}
}
pcm->lpVtbl->QueryContextMenu(pcm, hmContext, nInsert,
SFVIDM_CONTEXT_FIRST, SFVIDM_CONTEXT_LAST, fFlags);
// If this is the common dialog browser, we need to make the
// default command "Select" so that double-clicking (which is
// open in common dialog) makes sense.
if (pdsv->pcdb)
{
// make sure this is an item
if (iItem != -1)
{
HMENU hmSelect = _LoadPopupMenu(POPUP_COMMDLG_POPUPMERGE);
// NOTE: Since commdlg always eats the default command,
// we don't care what id we assign hmSelect, as long as it
// doesn't conflict with any other context menu id.
// SFVIDM_CONTEXT_FIRST-1 won't conflict with anyone.
Shell_MergeMenus(hmContext, hmSelect, 0,
(UINT)(SFVIDM_CONTEXT_FIRST-1), (UINT)-1,
MM_ADDSEPARATOR);
SetMenuDefaultItem(hmContext, 0, MF_BYPOSITION);
DestroyMenu(hmSelect);
}
}
idDefault = GetMenuDefaultItem(hmContext, MF_BYCOMMAND, 0);
}
_SHPrettyMenu(hmContext);
idCmd = TrackPopupMenu(hmContext,
TPM_RETURNCMD | TPM_RIGHTBUTTON | TPM_LEFTALIGN,
pt.x, pt.y, 0, pdsv->hwndView, NULL);
if ((idCmd == idDefault) &&
DV_CDB_OnDefaultCommand(pdsv) == S_OK)
{
// commdlg browser ate the default command
}
else if (idCmd == 0)
{
// No item selected
}
else
{
// A default menu item was selected; let everybody
// process it normally
// Note that this must be called before clearing out pdsv->pcmSel,
// so we cannot do this with a PostMessage
DefView_Command(pdsv, pcm, GET_WM_COMMAND_MPS(idCmd, 0, 0));
}
if (pcm)
{
pcm->lpVtbl->Release(pcm);
if (pdsv->pcmSel == pcm)
{
DV_FlushCachedMenu(pdsv);
}
}
DestroyMenu(hmContext);
}
void CALLBACK DefView_GetDataPoint(LPCITEMIDLIST pidl, LPPOINT lpPt, LPARAM lParam)
{
#define pdsv ((LPDEFVIEW)lParam)
if (pidl)
DefView_GetItemPosition(pdsv, pidl, lpPt);
else
DefView_GetDragPoint(pdsv, lpPt);
#undef pdsv
}
HRESULT DataObj_SetGlobal(IDataObject *pdtobj, UINT cf, HGLOBAL hGlobal)
{
FORMATETC fmte = {cf, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
STGMEDIUM medium;
medium.tymed = TYMED_HGLOBAL;
medium.hGlobal = hGlobal;
medium.pUnkForRelease = NULL;
// give the data object ownership of ths
return pdtobj->lpVtbl->SetData(pdtobj, &fmte, &medium, TRUE);
}
HRESULT DataObj_SetPoints(LPDATAOBJECT pdtobj, LPFNCIDLPOINTS lpfnPts, LPARAM lParam, LPSCALEINFO lpsi)
{
HRESULT hres = (E_OUTOFMEMORY);
STGMEDIUM medium;
LPIDA pida = DataObj_GetHIDA(pdtobj, &medium); // This should never fail
if (pida)
{
POINT *ppt = GlobalAlloc(GPTR, SIZEOF(POINT) * (1 + pida->cidl));
if (ppt)
{
UINT i;
POINT ptOrigin;
// Grab the anchor point
lpfnPts(NULL, &ptOrigin, lParam);
ppt[0] = ptOrigin;
for (i = 1; i <= pida->cidl; i++)
{
LPCITEMIDLIST pidl = IDA_GetIDListPtr(pida, i - 1);
lpfnPts(pidl, &ppt[i], lParam);
ppt[i].x -= ptOrigin.x;
ppt[i].y -= ptOrigin.y;
ppt[i].x = (ppt[i].x * lpsi->xMul) / lpsi->xDiv;
ppt[i].y = (ppt[i].y * lpsi->yMul) / lpsi->yDiv;
}
hres = DataObj_SetGlobal(pdtobj, g_cfOFFSETS, ppt);
if (FAILED(hres))
GlobalFree((HGLOBAL)ppt);
}
HIDA_ReleaseStgMedium(pida, &medium);
}
return hres;
}
///returns TRUE if in a small view
BOOL DV_GetItemSpacing(LPDEFVIEW pdsv, LPITEMSPACING lpis)
{
DWORD dwSize;
dwSize = ListView_GetItemSpacing(pdsv->hwndListview, TRUE);
lpis->cxSmall = LOWORD(dwSize);
lpis->cySmall = HIWORD(dwSize);
dwSize = ListView_GetItemSpacing(pdsv->hwndListview, FALSE);
lpis->cxLarge = LOWORD(dwSize);
lpis->cyLarge = HIWORD(dwSize);
return (pdsv->fs.ViewMode != FVM_ICON);
}
void DefView_SetPoints(LPDEFVIEW pdsv, LPDATAOBJECT pdtobj)
{
SCALEINFO si;
// convert coordinates to large icon view spacing
if (pdsv->fs.ViewMode == FVM_ICON) {
si.xMul = si.yMul = si.xDiv = si.yDiv = 1;
} else {
ITEMSPACING is;
DV_GetItemSpacing(pdsv, &is);
si.xDiv = is.cxSmall;
si.yDiv = is.cySmall;
si.xMul = is.cxLarge;
si.yMul = is.cyLarge;
}
// Assuming this is a CIDLData thing, poke the icon
// locations into it
DataObj_SetPoints(pdtobj, DefView_GetDataPoint, (LPARAM)pdsv, &si);
}
//
// REVIEW: Currently, we are not doing any serialization assuming that
// only one GUI thread can come here at a time.
//
LRESULT DefView_BeginDrag(LPDEFVIEW pdsv, NM_LISTVIEW * lpnm)
{
LPDATAOBJECT pdtobj;
POINT ptOffset = lpnm->ptAction; // hwndLV client coords
//
// Get the dwEffect from the selection.
//
DWORD dwEffect = DefView_GetAttributesFromSelection(pdsv, DROPEFFECT_LINK | DROPEFFECT_MOVE | DROPEFFECT_COPY) & (DROPEFFECT_LINK | DROPEFFECT_MOVE | DROPEFFECT_COPY);
/// not when you dragfrom wastebasket
///Assert(dwEffect & DROPEFFECT_LINK); // We should always have DROPEFFECT_LINK
// Somebody began dragging in our window, so store that fact
pdsv->bDragSource = TRUE;
// save away the anchor point;
pdsv->ptDragAnchor = lpnm->ptAction;
LVUtil_ClientToLV(pdsv->hwndListview, &pdsv->ptDragAnchor);
ClientToScreen(pdsv->hwndListview, &ptOffset); // now in screen
if (DAD_SetDragImageFromListView(pdsv->hwndListview, ptOffset))
{
if (SUCCEEDED(DefView_GetUIObjectFromItem(pdsv, &IID_IDataObject, &pdtobj,
SVGIO_SELECTION)))
{
HRESULT hres;
DefView_SetPoints(pdsv, pdtobj);
hres = SHDoDragDrop(pdsv->hwndMain, pdtobj, NULL, dwEffect, &dwEffect);
if (SUCCEEDED(hres) && dwEffect && pdsv->pfnCallback)
{
pdsv->pfnCallback(pdsv->psvOuter,
pdsv->pshf, pdsv->hwndMain, DVM_DIDDRAGDROP,
(WPARAM)dwEffect, (LPARAM)pdtobj);
}
pdtobj->lpVtbl->Release(pdtobj);
}
//
// We need to clear the dragged image only if we still have the drag context.
//
DAD_SetDragImage((HIMAGELIST)-1, NULL);
}
// All done dragging
pdsv->bDragSource = FALSE;
return 0L;
}
void DV_FocusOnSomething(LPDEFVIEW pdsv)
{
int iFocus;
iFocus = ListView_GetNextItem(pdsv->hwndListview, -1, LVNI_FOCUSED);
if (iFocus == -1) {
// set the focus on the first item.
ListView_SetItemState(pdsv->hwndListview, 0, LVIS_FOCUSED, LVIS_FOCUSED);
}
}
HRESULT DefView_InvokeCommand(LPDEFVIEW pdsv, IContextMenu *pcm,
CMINVOKECOMMANDINFOEX *pici)
{
TCHAR szWorkingDir[MAX_PATH];
HRESULT hres;
#ifdef UNICODE
CHAR szWorkingDirAnsi[MAX_PATH];
#endif
if (SUCCEEDED(pdsv->pfnCallback(pdsv->psvOuter, pdsv->pshf,
pdsv->hwndMain, DVM_GETWORKINGDIR,
ARRAYSIZE(szWorkingDir), (LPARAM)szWorkingDir)))
{
#ifdef UNICODE
// Fill in both the ansi working dir and the unicode one
// since we don't know who's gonna be processing this thing.
WideCharToMultiByte(CP_ACP, 0,
szWorkingDir, -1,
szWorkingDirAnsi, ARRAYSIZE(szWorkingDirAnsi),
NULL, NULL);
pici->lpDirectory = szWorkingDirAnsi;
pici->lpDirectoryW = szWorkingDir;
pici->fMask |= CMIC_MASK_UNICODE;
#else
pici->lpDirectory = szWorkingDir;
#endif
}
hres = pcm->lpVtbl->InvokeCommand(pcm, (LPCMINVOKECOMMANDINFO)pici);
return(hres);
}
//----------------------------------------------------------------------------
#define CMD_ID_FIRST 1
#define CMD_ID_LAST 0x7fff
void DefView_ProcessDblClick(LPDEFVIEW pdsv)
{
// Use the cached context menu object if there is one, else
// make it.
LPCONTEXTMENU pcmSel;
// SHIFT invokes the "alternate" command (usually Print)
int iShowCmd = SW_NORMAL;
DECLAREWAITCURSOR;
if (DV_CDB_OnDefaultCommand(pdsv) == S_OK)
{
return; /* commdlg browser ate the message */
}
SetWaitCursor();
pcmSel = DefView_GetContextMenuFromSelection(pdsv);
if (pcmSel)
{
HMENU hmenu = CreatePopupMenu();
if (hmenu)
{
CMINVOKECOMMANDINFOEX ici = {
SIZEOF(CMINVOKECOMMANDINFOEX),
0L,
pdsv->hwndMain,
NULL,
NULL, NULL,
iShowCmd,
};
if (GetAsyncKeyState(VK_MENU) < 0)
{
#ifdef UNICODE
// Fill in both the ansi verb and the unicode verb since we
// don't know who is going to be processing this thing.
ici.lpVerb = c_szPropertiesAnsi;
ici.lpVerbW = c_szProperties;
ici.fMask |= CMIC_MASK_UNICODE;
#else
ici.lpVerb = c_szProperties;
#endif
// If ALT double click, accelerator for "Properties..."
// need to reset it so that user won't blow off the app starting cursor
// also so that if we won't leave the wait cursor up when we're not waiting
// (like in a prop sheet or something that has a message loop
ResetWaitCursor();
hcursor_wait_cursor_save = NULL;
DefView_InvokeCommand(pdsv, pcmSel, &ici);
}
else
{
UINT idCmd;
UINT fFlags = CMF_DEFAULTONLY;
if (pdsv->psb) {
HWND hwnd = NULL;
pdsv->psb->lpVtbl->GetControlWindow(pdsv->psb, FCW_TREE, &hwnd);
if (hwnd) {
fFlags |= CMF_EXPLORE;
}
}
//
// SHIFT+dblclick does a Explore by default
//
if (GetAsyncKeyState(VK_SHIFT) < 0)
fFlags |= CMF_EXPLORE;
pcmSel->lpVtbl->QueryContextMenu(pcmSel, hmenu, 0, CMD_ID_FIRST, CMD_ID_LAST,
fFlags);
idCmd = GetMenuDefaultItem(hmenu, MF_BYCOMMAND, 0);
// need to reset it so that user won't blow off the app starting cursor
// also so that if we won't leave the wait cursor up when we're not waiting
// (like in a prop sheet or something that has a message loop
ResetWaitCursor();
hcursor_wait_cursor_save = NULL;
if (idCmd)
{
ici.lpVerb = (LPSTR)MAKEINTRESOURCE(idCmd - CMD_ID_FIRST);
DefView_InvokeCommand(pdsv, pcmSel, &ici);
}
DestroyMenu(hmenu);
}
}
// Release our use of the context menu
pcmSel->lpVtbl->Release(pcmSel);
if (pdsv->pcmSel == pcmSel)
{
DV_FlushCachedMenu(pdsv);
}
}
if (hcursor_wait_cursor_save)
ResetWaitCursor();
}
LRESULT DefView_OnLVNotify(LPDEFVIEW pdsv, NMHDR *pnm)
{
switch (pnm->code) {
case NM_KILLFOCUS:
// force update on inactive to not ruin save bits
DV_CDB_OnStateChange(pdsv, CDBOSC_KILLFOCUS);
if (GetForegroundWindow() != pdsv->hwndMain)
UpdateWindow(pdsv->hwndListview);
break;
case NM_SETFOCUS:
{
LPSHELLVIEW psv = (pdsv->psvOuter ? pdsv->psvOuter : &pdsv->sv);
// We should call IShellBrowser::OnViewWindowActive() before
// calling its InsertMenus().
pdsv->psb->lpVtbl->OnViewWindowActive(pdsv->psb, psv);
DV_CDB_OnStateChange(pdsv, CDBOSC_SETFOCUS);
CDefView_OnActivate(pdsv, SVUIA_ACTIVATE_FOCUS);
DV_FocusOnSomething(pdsv);
}
break;
#define lplvn ((NM_LISTVIEW *)pnm)
case NM_RETURN:
case NM_DBLCLK:
if (!pdsv->fDisabled)
DefView_ProcessDblClick(pdsv);
break;
case LVN_BEGINDRAG:
case LVN_BEGINRDRAG:
{
if (pdsv->fDisabled)
return FALSE; /* commdlg doesn't want user dragging */
return DefView_BeginDrag(pdsv, lplvn);
}
case LVN_ITEMCHANGING:
if (pdsv->fDisabled)
return TRUE;
break;
// Something changed in the listview. Delete any data that
// we might have cached away.
//
case LVN_ITEMCHANGED:
// We only care about STATECHANGE messages
if (!(lplvn->uChanged & LVIF_STATE))
{
// If the text is changed, we need to flush the cached
// context menu.
if (lplvn->uChanged&LVIF_TEXT)
{
DV_FlushCachedMenu(pdsv);
}
break;
}
// tell commdlg that selection may have changed
DV_CDB_OnStateChange(pdsv, CDBOSC_SELCHANGE);
// The rest only cares about SELCHANGE messages
if ((lplvn->uNewState^lplvn->uOldState) & LVIS_SELECTED)
{
// toss the cached context menu
DV_FlushCachedMenu(pdsv);
// throw away cached attribute bits
pdsv->dwAttrSel = (DWORD)-1;
// Tell the defview client that the selection may have changed
if (pdsv->pfnCallback)
{
DVSELCHANGEINFO dvsci;
dvsci.uNewState = lplvn->uNewState;
dvsci.uOldState = lplvn->uOldState;
dvsci.plParam = &pdsv->lSelChangeInfo;
dvsci.lParamItem = lplvn->lParam;
pdsv->pfnCallback(pdsv->psvOuter, pdsv->pshf, pdsv->hwndMain,
DVM_SELCHANGE,
MAKEWPARAM(SFVIDM_CLIENT_FIRST, lplvn->iItem),
(LPARAM)&dvsci);
}
DV_UpdateStatusBar(pdsv, FALSE);
}
break;
case LVN_DELETEITEM:
//
// We should not be called to clean up items when in ownerdata mode,
// since there's nothing to clean up
//
ASSERT(FALSE == DV_ISOWNERDATA(pdsv));
DSV_OnListViewDelete(pdsv, lplvn->iItem, (LPITEMIDLIST)lplvn->lParam);
DV_FlushCachedMenu(pdsv);
break;
case LVN_COLUMNCLICK:
// Quick hack to allow clicking on columns to set the sort order
if (pdsv->psd || pdsv->pfnCallback)
{
// toggle the direction of the sort if on the same column
if (pdsv->dvState.iLastColumnClick == lplvn->iSubItem)
pdsv->dvState.iDirection = -pdsv->dvState.iDirection;
else
pdsv->dvState.iDirection = 1;
pdsv->dvState.iLastColumnClick = lplvn->iSubItem;
if (pdsv->psd)
pdsv->psd->lpVtbl->ColumnClick(pdsv->psd, lplvn->iSubItem);
else
pdsv->pfnCallback(pdsv->psvOuter, pdsv->pshf,
pdsv->hwndMain, DVM_COLUMNCLICK, lplvn->iSubItem, 0L);
}
break;
#undef lplvn
case LVN_KEYDOWN:
DefView_HandleKeyDown(pdsv, ((LV_KEYDOWN *)pnm));
break;
#define lpdi ((LV_DISPINFO *)pnm)
case LVN_BEGINLABELEDIT:
{
ULONG rgfAttr;
LPCITEMIDLIST pidl;
//
// If this is an owner-data listview, we need to retrieve the pidl
// based on the index of the item. Otherwise, we just just take the
// lParam (which is the pidl in non-ownerdata cases)
//
if (DV_ISOWNERDATA(pdsv))
{
pidl = DSV_GetPIDL(pdsv, lpdi->item.iItem);
}
else
{
pidl = (LPITEMIDLIST)lpdi->item.lParam;
}
ASSERT(pidl);
rgfAttr = SFGAO_CANRENAME;
if (!pidl || FAILED(pdsv->pshf->lpVtbl->GetAttributesOf(pdsv->pshf, 1, &pidl, &rgfAttr))
|| !(rgfAttr & SFGAO_CANRENAME))
{
MessageBeep(0);
return TRUE; // Don't allow label edit
}
pdsv->fInLabelEdit = TRUE;
if (pdsv->pfnCallback)
{
HWND hwndEdit = ListView_GetEditControl(pdsv->hwndListview);
int cchMax = 0;
HRESULT hres = pdsv->pfnCallback(pdsv->psvOuter, pdsv->pshf,
pdsv->hwndMain, DVM_GETCCHMAX, (WPARAM)pidl, (LPARAM)&cchMax);
DebugMsg(DM_TRACE, TEXT("sh TR - DefView_OnBeginLabelEdit cchMax=%d hwndEdit=%x"),
cchMax, hwndEdit);
if (SUCCEEDED(hres) && cchMax>0 && hwndEdit)
{
Assert(cchMax < 1024);
SendMessage(hwndEdit, EM_LIMITTEXT, cchMax, 0);
}
}
}
break;
case LVN_ENDLABELEDIT:
pdsv->fInLabelEdit = FALSE;
if (lpdi->item.pszText)
{
LPCITEMIDLIST pidl;
//
// If this is an owner-data listview, we need to retrieve the pidl
// based on the index of the item. Otherwise, we just just take the
// lParam (which is the pidl in non-ownerdata cases)
//
if (DV_ISOWNERDATA(pdsv))
{
pidl = DSV_GetPIDL(pdsv, lpdi->item.iItem);
}
else
{
pidl = (LPITEMIDLIST)lpdi->item.lParam;
}
if (pidl)
{
// BUGBUG I made this lstrlenA, since i think its always ansi->wchar conversion
LPOLESTR pwsz = (LPOLESTR)LocalAlloc(LPTR, (lstrlen(lpdi->item.pszText) + 1) * SIZEOF(WCHAR));
if (pwsz)
{
CloudHookBeginRename(lpdi->item.pszText,
pdsv->hwndListview, lpdi->item.iItem);
StrToOleStr(pwsz, lpdi->item.pszText);
if (SUCCEEDED(pdsv->pshf->lpVtbl->SetNameOf(pdsv->pshf, pdsv->hwndMain,
pidl, pwsz, SHGDN_INFOLDER, NULL)))
{
SHChangeNotifyHandleEvents();
DV_CDB_OnStateChange(pdsv, CDBOSC_RENAME);
}
else
SendMessage(pdsv->hwndListview, LVM_EDITLABEL, lpdi->item.iItem,
(LPARAM)lpdi->item.pszText);
LocalFree((HLOCAL)pwsz);
CloudHookEndRename();
}
}
}
else
{
// The user canceled. so return TRUE to let things like the mouse
// click be processed.
return(TRUE);
}
break;
case LVN_GETDISPINFO:
{
LPCITEMIDLIST pidl;
LV_ITEM item;
TCHAR szIconFile[MAX_PATH];
DETAILSINFO di;
if (!(lpdi->item.mask & (LVIF_TEXT | LVIF_IMAGE)))
break;
//
// If this is an owner-data listview, we need to retrieve the pidl
// based on the index of the item. Otherwise, we just just take the
// lParam (which is the pidl in non-ownerdata cases)
//
if (DV_ISOWNERDATA(pdsv))
{
pidl = DSV_GetPIDL(pdsv, lpdi->item.iItem);
}
else
{
pidl = (LPITEMIDLIST)lpdi->item.lParam;
}
if (!pidl)
break;
item.mask = lpdi->item.mask & (LVIF_TEXT | LVIF_IMAGE);
item.iItem = lpdi->item.iItem;
item.iSubItem = lpdi->item.iSubItem;
item.iImage = lpdi->item.iImage = -1; // for iSubItem!=0 case
if (item.iSubItem==0 && (item.mask&LVIF_IMAGE))
{
DWORD uFlags = SFGAO_DISPLAYATTRMASK;
pdsv->pshf->lpVtbl->GetAttributesOf(pdsv->pshf, 1, &pidl, &uFlags);
// set the mask
item.mask |= LVIF_STATE;
lpdi->item.mask |= LVIF_STATE;
item.stateMask = LVIS_OVERLAYMASK | LVIS_CUT;
// Pick the right overlay icon. The order is significant.
item.state = 0;
if (uFlags & SFGAO_LINK) {
item.state = INDEXTOOVERLAYMASK(IDOI_LINK);
} else if (uFlags & SFGAO_SHARE) {
item.state = INDEXTOOVERLAYMASK(IDOI_SHARE);
} else if (uFlags & SFGAO_READONLY) {
item.state = INDEXTOOVERLAYMASK(IDOI_READONLY);
}
if (uFlags & SFGAO_GHOSTED) {
item.state |= LVIS_CUT;
}
lpdi->item.stateMask = item.stateMask;
lpdi->item.state = item.state;
// Get the image
TIMESTART(pdsv->GetIcon);
SHGetIconFromPIDL(pdsv->pshf,pdsv->psi,pidl,0,&item.iImage);
TIMESTOP(pdsv->GetIcon);
lpdi->item.iImage = item.iImage;
}
// Note that TEXT must come after IMAGE, since we reuse
// szIconFile
if (item.mask & LVIF_TEXT)
{
if (lpdi->item.cchTextMax)
*lpdi->item.pszText = TEXT('\0');
di.str.uType = STRRET_CSTR;
di.str.cStr[0] = '\0';
// Note that we do something different for index 0 = NAME
if (item.iSubItem == 0)
{
item.pszText = szIconFile;
item.cchTextMax = ARRAYSIZE(szIconFile);
TIMESTART(pdsv->GetName);
if (SUCCEEDED(pdsv->pshf->lpVtbl->GetDisplayNameOf(pdsv->pshf,
pidl, SHGDN_INFOLDER, &di.str)))
{
if (di.str.uType == STRRET_OFFSET)
{
// If an offset, we just pass the name back
// but don't store it to save on space
// BUGBUG (DavePl) Note that this is someone's notify
// message, and we are placing a text pointer in it...
// a unicode text pointer out of a pidl, which is therefore
// not even aligned!
lpdi->item.pszText = (LPTSTR)(((LPBYTE)pidl) + di.str.uOffset);
item.mask &= ~LVIF_TEXT;
}
else
{
StrRetToStrN(item.pszText, item.cchTextMax,
&di.str, pidl);
lstrcpyn(lpdi->item.pszText, item.pszText, lpdi->item.cchTextMax);
}
}
TIMESTOP(pdsv->GetName);
}
else
{
if (pdsv->psd)
{
if (SUCCEEDED(pdsv->psd->lpVtbl->GetDetailsOf(pdsv->psd,
pidl, item.iSubItem, (LPSHELLDETAILS)&di.fmt)))
{
StrRetToStrN(lpdi->item.pszText, lpdi->item.cchTextMax,
&di.str, pidl);
}
}
else if (pdsv->pfnCallback)
{
di.pidl = pidl;
if (SUCCEEDED(pdsv->pfnCallback(pdsv->psvOuter, pdsv->pshf,
pdsv->hwndMain, DVM_GETDETAILSOF,
item.iSubItem, (LPARAM)&di)))
{
StrRetToStrN(lpdi->item.pszText, lpdi->item.cchTextMax,
&di.str, pidl);
}
}
}
}
// We only store the name; all other info comes on demand
if (item.iSubItem == 0)
{
lpdi->item.mask |= LVIF_DI_SETITEM;
//ListView_SetItem(pdsv->hwndListview, &item);
}
break;
} // LVN_GETDISPINFO
}
#undef lpdi
return 0;
}
#define IN_VIEW_BMP 0x8000
#define IN_STD_BMP 0x0000
#ifdef CUST_TOOLBAR
#pragma data_seg(".text", "CODE")
struct {
USHORT idCmd;
USHORT idBmp;
} c_DVButtons[] = {
SFVIDM_EDIT_CUT, 0,
SFVIDM_EDIT_COPY, 1,
SFVIDM_EDIT_PASTE, 2,
SFVIDM_EDIT_UNDO, 3,
SFVIDM_FILE_LINK,
SFVIDM_FILE_DELETE, 5,
SFVIDM_FILE_RENAME, 6,
SFVIDM_FILE_PROPERTIES, 10,
//SFVIDM_SELECT_ALL
//SFVIDM_SELECT_INVERT
SFVIDM_VIEW_ICON, 0 | IN_VIEW_BMP,
SFVIDM_VIEW_SMALLICON, 1 | IN_VIEW_BMP,
SFVIDM_VIEW_LIST, 2 | IN_VIEW_BMP,
SFVIDM_VIEW_DETAILS, 3 | IN_VIEW_BMP,
SFVIDM_VIEW_OPTIONS, 10,
FSIDM_SORTBYNAME + SFVIDM_CLIENT_FIRST, 4 | IN_VIEW_BMP,
FSIDM_SORTBYSIZE + SFVIDM_CLIENT_FIRST, 5 | IN_VIEW_BMP,
FSIDM_SORTBYTYPE + SFVIDM_CLIENT_FIRST, 6 | IN_VIEW_BMP,
FSIDM_SORTBYDATE + SFVIDM_CLIENT_FIRST, 7 | IN_VIEW_BMP
// FEIDM_FINDFILES, 11, // this is 0!
//SFVIDM_ARRANGE_AUTO
//SFVIDM_ARRANGE_GRID
};
#pragma data_seg()
const USHORT c_CmdRemap[] = {
SFVIDM_EDIT_CUT,
SFVIDM_EDIT_COPY,
SFVIDM_EDIT_PASTE,
SFVIDM_EDIT_UNDO,
0, // redo
SFVIDM_FILE_DELETE,
0, // new doc
0, // file open
0, // file save
0, // print preview
SFVIDM_FILE_PROPERTIES,
// 0, // context help
// 0 // find
};
void ReMapTBCommands(LPDEFVIEW this)
{
int nItem;
TBBUTTON tb;
HWND hwndTB;
this->psb->lpVtbl->GetControlWindow(this->psb, FCW_TREE, &hwndTB);
for (nItem = 0; SendMessage(hwndTB, TB_GETBUTTON, nItem, (LPARAM)&tb); nItem++)
{
if (!(tb.fsStyle & TBSTYLE_SEP))
{
int iCmdOffset = tb.idCommand - this->iStdBMOffset;
if ((iCmdOffset >= 0) && (iCmdOffset < ARRAYSIZE(c_CmdRemap)) && c_CmdRemap[iCmdOffset])
SendMessage(hwndTB, TB_SETCMDID, nItem, c_CmdRemap[iCmdOffset]);
}
}
}
#endif // CUST_TOOLBAR
LRESULT DefView_OnNotify(LPDEFVIEW pdsv, NMHDR *pnm)
{
switch (pnm->idFrom) {
case FCIDM_TOOLBAR:
return DefView_TBNotify(pdsv, pnm);
case ID_LISTVIEW:
return DefView_OnLVNotify(pdsv, pnm);
default:
switch (pnm->code) {
case TTN_NEEDTEXT:
#define ptt ((LPTOOLTIPTEXT)pnm)
DV_GetToolTipText(pdsv, ptt->hdr.idFrom, ptt->szText, ARRAYSIZE(ptt->szText));
#undef ptt
break;
case NM_RCLICK:
if (GetParent(pnm->hwndFrom) == pdsv->hwndListview)
return 1;
}
}
return 0;
}
int CALLBACK DefView_Compare(LPARAM p1, LPARAM p2, LPARAM lParam)
{
#define pdsv ((LPDEFVIEW)lParam)
HRESULT hres = pdsv->pshf->lpVtbl->CompareIDs(pdsv->pshf, pdsv->dvState.lParamSort, (LPITEMIDLIST)p1, (LPITEMIDLIST)p2);
Assert(SUCCEEDED(hres))
Assert(pdsv->dvState.iDirection != 0);
return (short)SCODE_CODE(GetScode(hres)) * pdsv->dvState.iDirection;
#undef pdsv
}
void DV_ReArrange(LPDEFVIEW pdsv)
{
TIMEVAR(DV_ReArrange);
TIMEIN(DV_ReArrange);
TIMESTART(DV_ReArrange);
ListView_SortItems(pdsv->hwndListview, DefView_Compare, (LPARAM)pdsv);
TIMESTOP(DV_ReArrange);
TIMEOUT(DV_ReArrange);
}
int CALLBACK DefView_DVITEM_Compare(LPVOID p1, LPVOID p2, LPARAM lParam)
{
PDVITEM pdvi1 = (PDVITEM)p1;
PDVITEM pdvi2 = (PDVITEM)p2;
LPITEMIDLIST pFakeEnd1, pFakeEnd2;
USHORT uSave1, uSave2;
int nCmp;
// BUGBUG: Note that all this would be unnecessary if
// IShellFolder::CompareIDs took a bRecurse flag
pFakeEnd1 = _ILNext(&pdvi1->idl);
uSave1 = pFakeEnd1->mkid.cb;
pFakeEnd1->mkid.cb = 0;
pFakeEnd2 = _ILNext(&pdvi2->idl);
uSave2 = pFakeEnd2->mkid.cb;
pFakeEnd2->mkid.cb = 0;
nCmp = DefView_Compare((LPARAM)&pdvi1->idl, (LPARAM)&pdvi2->idl, lParam);
pFakeEnd2->mkid.cb = uSave2;
pFakeEnd1->mkid.cb = uSave1;
return(nCmp);
}
//
// This returns TRUE if everything went OK, FALSE otherwise
// Side effect: the listview items are always arranged after this call
//
BOOL DefView_RestorePos(LPDEFVIEW pdsv, PDVSAVEHEADER pSaveHeader, UINT uLen)
{
PDVITEM pDVItem, pDVEnd;
HDPA dpaItems;
UNALIGNED DVITEM * UNALIGNED * ppDVItem, * UNALIGNED * ppEndDVItems;
BOOL bOK = FALSE;
int iCount, i;
DWORD dwStyle;
// Do the specified sorting for both the ListView and the DPA,
// so we can traverse them both in order (like the merge step of a
// merge sort), which should be pretty quick
DV_ReArrange(pdsv);
pDVItem = (PDVITEM)(((LPBYTE)pSaveHeader) + pSaveHeader->cbPosOffset);
// BUGBUG more runtime size checking, should be init in case you don't get
// here the day you happen to break its validity (DavePl)
Assert(SIZEOF(DVSAVEHEADER) >= SIZEOF(DVITEM));
pDVEnd = (PDVITEM)(((LPBYTE)pSaveHeader) + uLen - SIZEOF(DVITEM));
// Grow every 16 items
dpaItems = DPA_Create(16);
if (!dpaItems)
{
return bOK;
}
for ( ; ; pDVItem=(PDVITEM)_ILNext(&pDVItem->idl))
{
if (pDVItem > pDVEnd)
{
// Invalid list
break;
}
// End normally when we reach a NULL IDList
if (pDVItem->idl.mkid.cb == 0)
{
break;
}
if (DPA_InsertPtr(dpaItems, INT_MAX, pDVItem) < 0)
{
break;
}
}
if (!DPA_Sort(dpaItems, DefView_DVITEM_Compare, (LPARAM)pdsv))
{
goto Error1;
}
ppDVItem = (PDVITEM *)DPA_GetPtrPtr(dpaItems);
ppEndDVItems = ppDVItem + DPA_GetPtrCount(dpaItems);
// Turn off auto-arrange if it's on at the mo.
dwStyle = GetWindowStyle(pdsv->hwndListview);
if (dwStyle & LVS_AUTOARRANGE)
SetWindowLong(pdsv->hwndListview, GWL_STYLE, dwStyle & ~LVS_AUTOARRANGE);
iCount = ListView_GetItemCount(pdsv->hwndListview);
for (i=0; i<iCount; ++i)
{
LPITEMIDLIST pidl;
pidl = DSV_GetPIDL(pdsv, i);
// need to check for pidl because this could be on a background
// thread and an fsnotify could be coming through to blow it away
for ( ; pidl ; )
{
int nCmp;
LPITEMIDLIST pFakeEnd;
USHORT uSave;
if (ppDVItem < ppEndDVItems)
{
// We terminate the IDList manually after saving
// the needed information. Note we will not GP fault
// since we added sizeof(ITEMIDLIST) onto the Alloc
pFakeEnd = _ILNext(&(*ppDVItem)->idl);
uSave = pFakeEnd->mkid.cb;
pFakeEnd->mkid.cb = 0;
nCmp = DefView_Compare((LPARAM)&((*ppDVItem)->idl), (LPARAM)pidl, (LPARAM)pdsv);
pFakeEnd->mkid.cb = uSave;
} else {
// do this by default. this prevents overlap of icons
//
// i.e. if we've run out of saved positions information,
// we need to just loop through and set all remaining items
// to position 0x7FFFFFFFF so that when it's really shown,
// the listview will pick a new (unoccupied) spot.
// breaking out now would leave it were the DV_ReArrange
// put it, but another item with saved state info could
// have come and be placed on top of it.
nCmp = 1;
}
if (nCmp > 0)
{
// We did not find the item
// reset it's position to be recomputed
ListView_SetItemPosition32(pdsv->hwndListview, i,
0x7FFFFFFF, 0x7FFFFFFF);
break;
}
else if (nCmp == 0)
{
UNALIGNED DVITEM * pDVItem = *ppDVItem;
// They are equal
ListView_SetItemPosition32(pdsv->hwndListview,
i, pDVItem->pt.x, pDVItem->pt.y);
// Don't check this one again
++ppDVItem;
break;
}
// It's less than the current item, so try the next one
++ppDVItem;
}
}
// Turn auto-arrange back on if needed...
if (dwStyle & LVS_AUTOARRANGE)
SetWindowLong(pdsv->hwndListview, GWL_STYLE, dwStyle);
bOK = TRUE;
if (DPA_GetPtrCount(dpaItems) > 0)
{
// If we read in any icon positions, we should save them later
// unless the user does something to cause us to go back to
// the default state
pdsv->bItemsMoved = TRUE;
}
Error1:;
DPA_Destroy(dpaItems);
return(bOK);
}
//
// 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 DefView_SaveCols(LPDEFVIEW this, LPSTREAM pstm)
{
BOOL bDefaultCols = TRUE;
LV_COLUMN col;
DETAILSINFO di;
int i;
LPSTREAM pstmCols;
if (!this->psd && !this->pfnCallback)
return TRUE;
di.pidl = NULL;
if (this->pfnCallback && SUCCEEDED(this->pfnCallback(this->psvOuter,
this->pshf, this->hwndMain, DVM_GETCOLSAVESTREAM, STGM_WRITE,
(LPARAM)&pstmCols)))
{
pstm = pstmCols;
}
else
{
pstmCols = NULL;
}
for (i = 0; ; ++i)
{
di.fmt = LVCFMT_LEFT;
di.cxChar = 20;
di.str.uType = (UINT)-1;
if (this->psd)
{
if(FAILED(this->psd->lpVtbl->GetDetailsOf(this->psd, NULL, i, (LPSHELLDETAILS)&di.fmt)))
{
break;
}
}
else if (this->pfnCallback)
{
if (FAILED(this->pfnCallback(this->psvOuter, this->pshf, this->hwndMain,
DVM_GETDETAILSOF, i, (LPARAM)&di)))
{
break;
}
}
col.mask = LVCF_WIDTH;
if (!ListView_GetColumn(this->hwndListview, i, &col))
{
// There is some problem, so just assume
// default column widths
bDefaultCols = TRUE;
break;
}
if (col.cx != (int)(di.cxChar * this->cxChar))
{
bDefaultCols = FALSE;
}
// HACK: I don't really care about column widths larger
// than 64K
if (FAILED(pstm->lpVtbl->Write(pstm, &col.cx, SIZEOF(USHORT), NULL)))
{
// There is some problem, so just assume
// default column widths
bDefaultCols = TRUE;
break;
}
}
if (pstmCols)
{
pstmCols->lpVtbl->Release(pstmCols);
// Always pretend we got default columns
return(TRUE);
}
return bDefaultCols;
}
//
// Save (and check) icon position information
// Returns TRUE if the positions are the default, FALSE otherwise
// Side effect: the stream pointer is left right after the last icon
//
BOOL DefView_SavePos(LPDEFVIEW this, LPSTREAM pstm)
{
int iCount, i;
DVITEM dvitem;
LPITEMIDLIST pidl;
BOOL bDefault = TRUE;
HRESULT hres;
if (!this->bItemsMoved || this->fs.ViewMode == FVM_DETAILS || this->fs.ViewMode == FVM_LIST)
return bDefault;
iCount = ListView_GetItemCount(this->hwndListview);
for (i = 0; ; i++)
{
if (i >= iCount)
{
bDefault = FALSE;
break;
}
ListView_GetItemPosition(this->hwndListview, i, &dvitem.pt);
pidl = DSV_GetPIDL(this, i);
hres = pstm->lpVtbl->Write(pstm, &dvitem.pt, SIZEOF(dvitem.pt), NULL);
if (FAILED(hres))
{
break;
}
hres = pstm->lpVtbl->Write(pstm, pidl, pidl->mkid.cb, NULL);
if (FAILED(hres))
{
break;
}
}
// Terminate the list with a NULL IDList
dvitem.idl.mkid.cb = 0;
hres = pstm->lpVtbl->Write(pstm, &dvitem, SIZEOF(dvitem), NULL);
if (FAILED(hres))
{
bDefault = TRUE;
}
return(bDefault);
}
// this should NOT check for whether the item is already in the listview
// if it does, we'll have some serious performance problems
int DefView_AddObject(LPDEFVIEW pdsv, LPITEMIDLIST pidl)
{
int i;
LV_ITEM item;
TIMESTART(pdsv->AddObject);
ASSERT(FALSE == DV_ISOWNERDATA(pdsv));
//
// We can't support adding a pidl in this fashion if we are in ownerdata
// mode. BUGBUG Maybe this just needs to invalidate the cache?
//
if (DV_ISOWNERDATA(pdsv))
{
TIMESTOP(pdsv->AddObject);
return(-1);
}
// Check the commdlg hook to see if we should include this
// object.
if (DV_CDB_IncludeObject(pdsv, pidl) != S_OK)
{
TIMESTOP(pdsv->AddObject);
return(-1);
}
// Check with callback first to sanction the add
if (pdsv->pfnCallback) {
DVSELCHANGEINFO dvsci;
dvsci.lParamItem = (LPARAM)pidl;
dvsci.plParam = (LPARAM*)&pdsv->lSelChangeInfo;
if (S_FALSE == pdsv->pfnCallback(pdsv->psvOuter, pdsv->pshf,
pdsv->hwndMain, DVM_INSERTITEM,
(WPARAM)pidl, (LPARAM)&dvsci))
{
// Don't add this object
TIMESTOP(pdsv->AddObject);
return(-1);
}
}
item.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM;
item.iItem = INT_MAX; // add at end
item.iSubItem = 0;
item.iImage = I_IMAGECALLBACK;
item.pszText = LPSTR_TEXTCALLBACK;
item.lParam = (LPARAM)pidl;
i = ListView_InsertItem(pdsv->hwndListview, &item);
TIMESTOP(pdsv->AddObject);
return i;
}
int DefView_FindItem(LPDEFVIEW pdsv, LPCITEMIDLIST pidl, LPITEMIDLIST *ppidlFound, BOOL fSamePtr)
{
LV_ITEM item;
int cItems;
int cCounter;
item.mask = LVIF_PARAM;
item.iSubItem = 0;
cItems = ListView_GetItemCount(pdsv->hwndListview);
if (pdsv->iLastFind >= cItems)
pdsv->iLastFind = 0;
for (cCounter = 0, item.iItem = pdsv->iLastFind; cCounter < cItems; item.iItem = (item.iItem + 1) % cItems, cCounter++) {
HRESULT hres = ResultFromShort(-1);
ListView_GetItem(pdsv->hwndListview, &item);
// BUGBUG: this passes 0 for the lParam
if (item.lParam == (LPARAM)pidl) {
hres = ResultFromShort(0);
} else if (!fSamePtr) {
// if we don't insist on being the same pointer, do the ole style compare
//BUGBUGLPARAM hres = pdsv->pshf->lpVtbl->CompareIDs(pdsv->pshf, 0, pidl, (LPITEMIDLIST)item.lParam);
}
Assert(SUCCEEDED(hres));
if (FAILED(hres))
return -1;
if (ShortFromResult(hres) == 0)
{
if (ppidlFound)
*ppidlFound = (LPITEMIDLIST)item.lParam;
pdsv->iLastFind = item.iItem;
#ifdef FINDCACHE_DEBUG
DebugMsg(DM_TRACE, TEXT("###############FIND CACHE RESULT --- %s by %d"), cCounter < item.iItem ? TEXT("WIN") : TEXT("LOSE"), item.iItem - cCounter);
#endif
return item.iItem;
}
}
pdsv->iLastFind = 0;
return -1; // not found
}
//
// Function to process the SFVM_REMOVEOBJECT message, by searching
// through the list for a match of the pidl. If a match is found, the
// item is removed from the list and the index number is returned, else
// -1 is returned.
int DefView_RemoveObject(LPDEFVIEW pdsv, LPITEMIDLIST pidl, BOOL fSamePtr)
{
int i;
ASSERT(FALSE == DV_ISOWNERDATA(pdsv));
//
// We can't support adding a pidl in this fashion if we are in ownerdata
// mode. BUGBUG Maybe this just needs to invalidate the cache?
//
if (DV_ISOWNERDATA(pdsv))
{
return(-1);
}
// Docfind will pass in a null pointer to tell us that it wants
// to refresh the window by deleting all of the items from it.
if (pidl == NULL)
{
// notify the iSHellFolder
if (pdsv->pfnCallback) {
DVSELCHANGEINFO dvsci;
dvsci.lParamItem = 0;
dvsci.plParam = (LPARAM*)&pdsv->lSelChangeInfo;
pdsv->pfnCallback(pdsv->psvOuter, pdsv->pshf, pdsv->hwndMain, DVM_DELETEITEM, 0, (LPARAM)&dvsci);
}
ListView_DeleteAllItems(pdsv->hwndListview);
return(0);
}
// Non null go look for item.
i = DefView_FindItem(pdsv, pidl, NULL, fSamePtr);
if (i >= 0)
{
UINT uState = ListView_GetItemState(pdsv->hwndListview, i, LVIS_ALL);
RECT rc;
if (uState & LVIS_FOCUSED) {
ListView_GetItemRect(pdsv->hwndListview, i, &rc, LVIR_ICON);
}
// let IShellFolder know we're deleting something.
if (pdsv->pfnCallback) {
LPITEMIDLIST pidlReal = DSV_GetPIDL(pdsv, i);
if (pidlReal) {
DVSELCHANGEINFO dvsci;
dvsci.lParamItem = (LPARAM)pidlReal;
dvsci.plParam = (LPARAM*)&pdsv->lSelChangeInfo;
pdsv->pfnCallback(pdsv->psvOuter, pdsv->pshf, pdsv->hwndMain, DVM_DELETEITEM, 0, (LPARAM)&dvsci);
}
}
// do the actual delete
ListView_DeleteItem(pdsv->hwndListview, i);
// we deleted the focused item.. replace the focus to the nearest item.
if (uState & LVIS_FOCUSED) {
int iFocus = i;
if ((pdsv->fs.ViewMode == FVM_SMALLICON) ||
(pdsv->fs.ViewMode == FVM_ICON)) {
LV_FINDINFO lvfi;
lvfi.flags = LVFI_NEARESTXY;
lvfi.pt.x = rc.left;
lvfi.pt.y = rc.top;
lvfi.vkDirection = 0;
iFocus = ListView_FindItem(pdsv->hwndListview, -1, &lvfi);
} else {
if (ListView_GetItemCount(pdsv->hwndListview) >= iFocus)
iFocus--;
}
if (iFocus != -1) {
ListView_SetItemState(pdsv->hwndListview, iFocus, LVIS_FOCUSED, LVIS_FOCUSED);
ListView_EnsureVisible(pdsv->hwndListview, iFocus, FALSE);
}
}
}
return(i);
}
//
// Function to process the SFVM_UPDATEOBJECT message, by searching
// through the list for a match of the first pidl. If a match is found,
// the item is updated to the second pidl...
int DefView_UpdateObject(LPDEFVIEW pdsv, LPITEMIDLIST *ppidl)
{
LPITEMIDLIST pidlOld;
int i;
ASSERT(FALSE == DV_ISOWNERDATA(pdsv));
//
// We can't support adding a pidl in this fashion if we are in ownerdata
// mode. BUGBUG Maybe this just needs to invalidate the cache?
//
if (DV_ISOWNERDATA(pdsv))
{
return(-1);
}
i = DefView_FindItem(pdsv, ppidl[0], &pidlOld, FALSE);
if (i >= 0)
{
LV_ITEM item;
//
// We found the item so lets now update it in the
// the view.
//
item.mask = LVIF_PARAM | LVIF_TEXT | LVIF_IMAGE;
item.iItem = i;
item.lParam = (LPARAM)ppidl[1]; // update the second item.
item.pszText = LPSTR_TEXTCALLBACK;
item.iImage = I_IMAGECALLBACK;
item.iSubItem = 0; // REVIEW: bug in listview?
ListView_SetItem(pdsv->hwndListview, &item);
// Free the old pidl after we've added the new one
ILFree(pidlOld);
}
return(i);
}
//
// invalidates all items with the given image index.
//
// or update all items if iImage == -1
//
void DefView_UpdateImage(LPDEFVIEW pdsv, int iImage)
{
LV_ITEM item;
int cItems;
DebugMsg(DM_TRACE, TEXT("DefView_UpdateImage: %d"), iImage);
ASSERT(FALSE == DV_ISOWNERDATA(pdsv));
// Can't do this in owner data.
// BUGBUG invalidate the cache?
if (DV_ISOWNERDATA(pdsv))
{
return;
}
//
// -1 means update all
// reset the imagelists incase the size has changed, and do
// a full update.
//
if (iImage == -1)
{
HIMAGELIST himlLarge, himlSmall;
Shell_GetImageLists(&himlLarge, &himlSmall);
ListView_SetImageList(pdsv->hwndListview, himlLarge, LVSIL_NORMAL);
ListView_SetImageList(pdsv->hwndListview, himlSmall, LVSIL_SMALL);
pdsv->sv.lpVtbl->Refresh(&pdsv->sv);
return;
}
item.iSubItem = 0;
cItems = ListView_GetItemCount(pdsv->hwndListview);
for (item.iItem = 0; item.iItem < cItems; item.iItem++)
{
item.mask = LVIF_IMAGE|LVIF_PARAM;
ListView_GetItem(pdsv->hwndListview, &item);
if (item.iImage == iImage)
{
Icon_FSEvent(SHCNE_UPDATEITEM, (LPCITEMIDLIST)item.lParam, NULL);
item.mask = LVIF_IMAGE;
item.iImage = I_IMAGECALLBACK;
item.iSubItem = 0;
ListView_SetItem(pdsv->hwndListview, &item);
}
}
}
// Function to process the SFVM_REFRESHOBJECT message, by searching
// through the list for a match of the first pidl. If a match is found,
// the item is redrawn.
int DefView_RefreshObject(LPDEFVIEW pdsv, LPITEMIDLIST *ppidl)
{
int i;
// BUGBUG: should support refreshing a range of pidls
ASSERT(FALSE == DV_ISOWNERDATA(pdsv));
// Can't do this in ownerdata mode
// BUGBUG invalidate cache?
if (DV_ISOWNERDATA(pdsv))
{
return -1;
}
i = DefView_FindItem(pdsv, ppidl[0], NULL, FALSE);
if (i >= 0)
{
ListView_RedrawItems(pdsv->hwndListview, i, i);
}
return(i);
}
//
// Function to process the SFVM_GETSELECTEDOBJECTS message
//
HRESULT DefView_GetItemObjects(LPDEFVIEW pdsv, LPCITEMIDLIST **ppidl,
UINT uItem)
{
UINT cItems = DefView_GetItemPIDLS(pdsv, NULL, 0, uItem);
LPITEMIDLIST * apidl;
if (ppidl != NULL)
{
*ppidl = NULL;
if (cItems == 0)
return(ResultFromShort(0)); // nothing allocated...
apidl=LocalAlloc(LPTR, SIZEOF(LPITEMIDLIST) * cItems);
if (!apidl)
return(E_OUTOFMEMORY);
*ppidl = apidl;
cItems = DefView_GetItemPIDLS(pdsv, apidl, cItems, uItem);
}
return(ResultFromShort(cItems));
}
void DefView_SetItemPos(LPDEFVIEW pdsv, LPSFV_SETITEMPOS psip)
{
int i;
ASSERT(FALSE == DV_ISOWNERDATA(pdsv));
// Can't do this in ownerdata mode
// BUGBUG invalidate cache?
if (DV_ISOWNERDATA(pdsv))
{
return;
}
i = DefView_FindItem(pdsv, psip->pidl, NULL, FALSE);
if (i >= 0)
{
ListView_SetItemPosition32(pdsv->hwndListview, i, psip->pt.x, psip->pt.y);
pdsv->bItemsMoved = TRUE;
pdsv->bClearItemPos = FALSE;
}
}
#define DV_IDTIMER 1
#define ENUMTIMEOUT 3000 // 3 seconds
#define SHORTENUMTIMEOUT 500 // 1/2 second
HRESULT DV_AllocRestOfStream(LPSTREAM pstm, LPVOID *ppData, UINT *puLen)
{
UINT uLen;
ULARGE_INTEGER libCurPos;
ULARGE_INTEGER libEndPos;
LARGE_INTEGER dlibMove = {0, 0};
pstm->lpVtbl->Seek(pstm, dlibMove, STREAM_SEEK_CUR, &libCurPos);
pstm->lpVtbl->Seek(pstm, dlibMove, STREAM_SEEK_END, &libEndPos);
uLen = libEndPos.LowPart - libCurPos.LowPart;
// Note that we add room for an extra ITEMIDLIST so we don't GP fault
// when we manually terminate the last ID list
if (uLen == 0)
{
return(E_UNEXPECTED);
}
// Allow the caller to have some extra room
*puLen += uLen;
if ((*ppData = (void*)LocalAlloc(LPTR, *puLen)) == NULL)
{
return(E_OUTOFMEMORY);
}
pstm->lpVtbl->Seek(pstm, *(LARGE_INTEGER *)&libCurPos, STREAM_SEEK_SET, NULL);
// This really should not fail
pstm->lpVtbl->Read(pstm, *ppData, uLen, NULL);
return S_OK;
}
UINT DefView_GetSaveHeader(LPDEFVIEW pdsv, PDVSAVEHEADER *ppSaveHeader)
{
LPSTREAM pstm;
UINT uLen = SIZEOF(ITEMIDLIST);
*ppSaveHeader = NULL;
if (FAILED(pdsv->psb->lpVtbl->GetViewStateStream(pdsv->psb, STGM_READ, &pstm)))
return 0;
if (FAILED(DV_AllocRestOfStream(pstm, ppSaveHeader, &uLen)))
{
goto Error1;
}
if (uLen<SIZEOF(DVSAVEHEADER)+SIZEOF(ITEMIDLIST)
|| (*ppSaveHeader)->cbSize != SIZEOF(DVSAVEHEADER))
{
LocalFree((HLOCAL)*ppSaveHeader);
*ppSaveHeader = NULL;
goto Error1;
}
goto CleanUp;
Error1:
uLen = 0;
CleanUp:
pstm->lpVtbl->Release(pstm);
return uLen;
}
// restore the window state
//
// icon positions
// window scroll position
//
void DefView_RestoreState(LPDEFVIEW pdsv, PDVSAVEHEADER pInSaveHeader, UINT uLen)
{
PDVSAVEHEADER pSaveHeader;
if (pInSaveHeader)
{
pSaveHeader = pInSaveHeader;
}
else
{
uLen = DefView_GetSaveHeader(pdsv, &pSaveHeader);
if (uLen == 0)
{
DV_ReArrange(pdsv);
return;
}
}
pdsv->dvState = pSaveHeader->dvState;
// Columns get restored during window creation
// make sure the view modes of the saved state match what we have now
if (pSaveHeader->ViewMode == pdsv->fs.ViewMode)
{
// If we restored all the icon positions restore the scroll position too
if (DefView_RestorePos(pdsv, pSaveHeader, uLen))
ListView_Scroll(pdsv->hwndListview, pSaveHeader->ptScroll.x, pSaveHeader->ptScroll.y);
}
else
{
DebugMsg(DM_TRACE, TEXT("restore state view modes don't match"));
}
if (!pInSaveHeader)
LocalFree((HLOCAL)pSaveHeader);
}
//
// This function can be called only when we are filling listview items
// (from within DefView_FillObjects. It is very important to pass consistent
// dwFlags to GetDisplayNameOf and SetNameOf.
//
void DefView_UpdateGlobalFlags(LPDEFVIEW pdsv)
{
SHELLSTATE ss;
SHGetSetSettings(&ss, SSF_SHOWALLOBJECTS, FALSE);
pdsv->fShowAllObjects = ss.fShowAllObjects;
}
void DSV_FilterDPAs(LPDEFVIEW pdsv, HDPA hdpa, HDPA hdpaOld)
{
int i, j;
LPITEMIDLIST pidl, pidlOld;
#ifdef DPA_FILTER_TEST
for (i = 0; i < DPA_GetPtrCount(hdpaOld); i++) {
pidl = DPA_FastGetPtr(hdpaOld, i);
DebugMsg(DM_TRACE, TEXT("pidl = %x, %x, %x"), pidl, *(DWORD*)pidl, i);
}
#endif
// do the compares
for (;;) {
int iCompare;
i = DPA_GetPtrCount(hdpaOld);
j = DPA_GetPtrCount(hdpa);
if (!i && !j)
break;
if (!i) {
// only new ones left. Insert all of them.
iCompare = -1;
pidl = DPA_FastGetPtr(hdpa, 0);
} else if (!j) {
// only old ones left. remove them all.
iCompare = 1;
pidlOld = DPA_FastGetPtr(hdpaOld, 0);
} else {
pidlOld = DPA_FastGetPtr(hdpaOld, 0);
pidl = DPA_FastGetPtr(hdpa, 0);
iCompare = DefView_Compare((LPARAM)pidl, (LPARAM)pidlOld, (LPARAM)pdsv);
}
if (iCompare == 0) {
// they're the same ,remove one of each.
DPA_DeletePtr(hdpa, 0);
DPA_DeletePtr(hdpaOld, 0);
ILFree(pidl);
} else if (iCompare < 0) {
// the new item!
if (DefView_AddObject(pdsv, pidl) == -1)
ILFree(pidl);
DPA_DeletePtr(hdpa, 0);
} else {
#ifdef DPA_FILTER_TEST
// old item, delete it.
DebugMsg(DM_TRACE, TEXT("remove pidl = %x, %x"), pidlOld, *(DWORD*)pidlOld);
#endif
DefView_RemoveObject(pdsv, pidlOld, TRUE);
DPA_DeletePtr(hdpaOld, 0);
}
}
}
// this is only called from within SHCNE_* don't put up ui on the enum error.
void DefView_Update(LPDEFVIEW pdsv)
{
if (pdsv->bBkFilling)
{
pdsv->bUpdatePending = TRUE;
}
else
{
DefView_FillObjectsShowHide(pdsv, FALSE, NULL, 0, FALSE);
}
}
#define HM_OK 0
#define HM_ABORT 1
#define HM_DESTROY 2
#ifdef DEADCODE
UINT HandleMessages(HWND hwnd, HWND hwndMain)
{
MSG msg;
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
// intercept these messages
if ((msg.message == WM_SYSKEYDOWN || msg.message == WM_KEYDOWN) && msg.wParam == VK_ESCAPE)
return HM_ABORT;
if (((msg.hwnd == hwnd) || (msg.hwnd == hwndMain)) &&
( ((msg.message == WM_SYSCOMMAND) && (msg.wParam == SC_CLOSE)) ||
(msg.message == WM_DESTROY) || (msg.message == WM_CLOSE) ||
(msg.message == WM_QUIT)))
{
PostMessage(msg.hwnd, msg.message, msg.wParam, msg.lParam);
DebugMsg(DM_TRACE, TEXT("Got WM_SYSCOMMAND SC_CLOSE!"));
return HM_DESTROY;
}
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return HM_OK;
}
#endif
//----------------------------------------------------------------------------
// Alter the size of the parent to best fit around the items we have.
void ViewWindow_BestFit(LPDEFVIEW pdsv, BOOL bTimeout)
{
const int cxMin = MINVIEWWIDTH, cyMin = MINVIEWHEIGHT;
const int cxSpacing = GetSystemMetrics(SM_CXICONSPACING);
const int cySpacing = GetSystemMetrics(SM_CYICONSPACING);
DWORD dwStyle = GetWindowStyle(pdsv->hwndListview);
RECT rc;
WINDOWPLACEMENT wp;
int cItems, cxScreen, cyScreen;
int cxMax, cyMax;
RECT rcWork;
int cxWorkArea;
int cyWorkArea;
SystemParametersInfo(SPI_GETWORKAREA, 0, &rcWork, 0);
cxWorkArea = (rcWork.right - rcWork.left);
cyWorkArea = (rcWork.bottom - rcWork.top);
cxMax = (cxWorkArea * 3) / 5;
cyMax = (cyWorkArea * 3) / 5;
if (!(pdsv->fs.fFlags & FWF_BESTFITWINDOW))
{
return;
}
// Don't try to do it again.
pdsv->fs.fFlags &= ~FWF_BESTFITWINDOW;
// Find the largest square.
// 4000 should be close enough to infinite
if (bTimeout)
{
cItems = 4000;
if (pdsv->pfnCallback)
{
// Give Control Panel a chance to tell us it will
// probably only have a few items, even though it timed
// out.
pdsv->pfnCallback(pdsv->psvOuter, pdsv->pshf,
pdsv->hwndMain, DVM_DEFITEMCOUNT,
0, (LPARAM)&cItems);
}
}
else
{
cItems = ListView_GetItemCount(pdsv->hwndListview);
}
rc.left = rc.top = rc.right = rc.bottom = 0;
if (cItems)
{
int cy, i1, i2, i3;
i1 = 1;
i2 = 0;
i3 = cItems;
while (i3 > 0)
{
i3 -= i1;
i1 += 2;
i2++;
}
// Convert this into an equivalent rect for the effective
// client area.
// The width.
rc.right = (i2 * cxSpacing);
if (rc.right > cxMax)
{
rc.right = cxMax;
// Now recalculate the number of rows
i2 = rc.right / cxSpacing;
}
// The height.
cy = (cItems + i2 - 1) / i2;
rc.bottom = (cy * cySpacing);
}
// Make sure we are in the "default" view
if ((dwStyle&LVS_TYPEMASK) == LVS_ICON)
{
// If it's going to be too big then flip into listview.
pdsv->fs.ViewMode = (rc.bottom > (cyMax * 3))
? FVM_LIST : FVM_ICON;
if (pdsv->pfnCallback)
{
// Give Briefcase a chance to tell us it likes details
pdsv->pfnCallback(pdsv->psvOuter, pdsv->pshf,
pdsv->hwndMain, DVM_DEFVIEWMODE,
0, (LPARAM)&pdsv->fs.ViewMode);
}
dwStyle = (LVStyleFromView(pdsv) & LVS_TYPEMASK) | (dwStyle & ~LVS_TYPEMASK);
SetWindowLong(pdsv->hwndListview, GWL_STYLE, dwStyle);
if (pdsv->fs.ViewMode == FVM_DETAILS)
{
// Need to special-case details because it could be any width
// I need to add an item in case there were none to start with
LV_ITEM item;
int i;
item.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM;
item.iItem = INT_MAX; // add at end
item.iSubItem = 0;
item.iImage = 0;
item.pszText = (LPTSTR)c_szNULL;
item.lParam = 0;
i = ListView_InsertItem(pdsv->hwndListview, &item);
if (i >= 0)
{
ListView_GetItemRect(pdsv->hwndListview, i, &rc, LVIR_BOUNDS);
ListView_DeleteItem(pdsv->hwndListview, i);
}
}
}
// Make sure things aren't too small, or too big
rc.left = rc.top = 0;
rc.right = max(min(rc.right, cxMax), cxMin);
rc.bottom = max(min(rc.bottom, cyMax), cyMin);
// Allow room for the parent, toolbars etc.
// Also allow room for some text.
InflateRect(&rc, PARENTGAPWIDTH, PARENTGAPHEIGHT + (cySpacing / 2));
// Make sure it will still fit on the screen.
wp.length = SIZEOF(wp);
GetWindowPlacement(pdsv->hwndMain, &wp);
// Make sure the window fits in the work area.
cxScreen = cxWorkArea - GetSystemMetrics(SM_CXFRAME);
cyScreen = cyWorkArea - GetSystemMetrics(SM_CYFRAME);
if (wp.rcNormalPosition.left + rc.right > cxScreen)
rc.right = cxScreen - wp.rcNormalPosition.left;
if (wp.rcNormalPosition.top + rc.bottom > cyScreen)
rc.bottom = cyScreen - wp.rcNormalPosition.top;
// Resize the parent.
SetWindowPos(pdsv->hwndMain, NULL, 0, 0, rc.right, rc.bottom, SWP_NOMOVE | SWP_NOZORDER);
// If we're still in icon mode but AutoArrange is off -
// things will look weird so fix it now.
if ((pdsv->fs.ViewMode==FVM_ICON || pdsv->fs.ViewMode==FVM_SMALLICON)
&& !(dwStyle & LVS_AUTOARRANGE))
{
// Cause everything to fit again.
DebugMsg(DM_TRACE, TEXT("vw_bf: Re-arrange."));
ListView_Arrange(pdsv->hwndListview, LVA_DEFAULT);
}
}
//----------------------------------------------------------------------------
BOOL EnumerationTimeout(LPDEFVIEW pdsv, BOOL bRefresh)
{
DebugMsg(DM_TRACE, TEXT("s.et: Enumeration is taking too long."));
// The static window could already exist during a refresh
if (!pdsv->hwndStatic && bRefresh)
{
RECT rc;
// Note that new windows go to the bottom of the Z order
pdsv->hwndStatic = CreateWindowEx(WS_EX_CLIENTEDGE, c_szAnimateClass, c_szNULL,
WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | ACS_TRANSPARENT | ACS_AUTOPLAY | ACS_CENTER,
0, 0, 0, 0, pdsv->hwndView, (HMENU)ID_STATIC, HINST_THISDLL, NULL);
if (!pdsv->hwndStatic)
{
// We need this window to exist so that we do not get strange
// painting and clicking results
return(FALSE);
}
GetClientRect(pdsv->hwndView, &rc);
// Move this window to the top so the user sees the looking icon
SetWindowPos(pdsv->hwndStatic, HWND_TOP, 0, 0,
rc.right, rc.bottom, 0);
ShowWindow(pdsv->hwndListview, SW_HIDE);
}
ViewWindow_BestFit(pdsv, TRUE);
return(TRUE);
}
void DefView_CheckForFillDoneOnDestroy(LPDEFVIEW pdsv, HWND hwndView)
{
MSG msg;
HDPA hdpa;
int i;
if (PeekMessage(&msg, hwndView, WM_DSV_DESTROYSTATIC, WM_DSV_DESTROYSTATIC, PM_NOREMOVE))
{
DebugMsg(DM_TRACE, TEXT("DefView: Fill_Completed after WM_DESTROY!!!"));
hdpa = (HDPA)msg.lParam;
if (hdpa)
{
for (i=DPA_GetPtrCount(hdpa)-1; i >= 0; i--)
{
ILFree((LPITEMIDLIST)DPA_FastGetPtr(hdpa, i));
}
DPA_Destroy(hdpa);
}
}
}
void DefView_FillDone(LPDEFVIEW pdsv, HDPA hdpaNew,
PDVSAVEHEADER pSaveHeader, UINT uLen, BOOL bRefresh, BOOL fInteractive)
{
HDPA hdpaOld = NULL;
int i, j;
SendMessage(pdsv->hwndListview, WM_SETREDRAW, (WPARAM)FALSE, 0);
if (FALSE == DV_ISOWNERDATA(pdsv))
{
//
// Make it sure that we have pdsv around while in this function.
//
pdsv->sv.lpVtbl->AddRef(&pdsv->sv);
hdpaOld = DPA_Create(16);
if (!hdpaOld)
{
goto Error1;
}
// now get the dpa of what's currently being viewed.
i = ListView_GetItemCount(pdsv->hwndListview);
for (j = 0; j < i ; j++)
{
LPITEMIDLIST pidl = DSV_GetPIDL(pdsv, j);
if (pidl)
{
DPA_InsertPtr(hdpaOld, INT_MAX, pidl);
}
}
// sort the two for easy comparisions
// Note the new list should already be sorted
// I wish we could do this sort in the background, but we just can't
DPA_Sort(hdpaOld, (PFNDPACOMPARE)DefView_Compare, (LPARAM)pdsv);
DSV_FilterDPAs(pdsv, hdpaNew, hdpaOld);
}
if (hdpaOld)
{
DPA_Destroy(hdpaOld);
}
Error1:;
DPA_Destroy(hdpaNew);
if (bRefresh)
{
TIMESTART(pdsv->RestoreState);
DefView_RestoreState(pdsv, pSaveHeader, uLen);
TIMESTOP(pdsv->RestoreState);
}
// Bring this to the top while showing it to avoid a second paint when
// hwndStatic is destroyed
SetWindowPos(pdsv->hwndListview, HWND_TOP, 0, 0, 0, 0,
SWP_NOMOVE|SWP_NOSIZE|SWP_SHOWWINDOW);
if (pdsv->hwndStatic)
{
// Apparently, we were filling in the background
LPITEMIDLIST pidlSelect;
UINT uFlags;
DestroyWindow(pdsv->hwndStatic);
pdsv->hwndStatic = NULL;
ENTERCRITICAL;
pidlSelect = pdsv->pidlSelect;
uFlags = pdsv->uFlagsSelect;
pdsv->pidlSelect = NULL;
LEAVECRITICAL;
if (pidlSelect)
{
CDefView_SelectItem(&pdsv->sv, pidlSelect, uFlags);
ILFree(pidlSelect);
}
}
// set the focus on the first item.
DV_FocusOnSomething(pdsv);
// Tell the defview client that this window has been refreshed
if (pdsv->pfnCallback)
{
pdsv->pfnCallback(pdsv->psvOuter, pdsv->pshf, pdsv->hwndMain,
DVM_REFRESH, 0, pdsv->lSelChangeInfo);
}
DV_UpdateStatusBar(pdsv, TRUE);
if (pdsv->bUpdatePending)
{
pdsv->bUpdatePending = FALSE;
DefView_FillObjectsShowHide(pdsv, FALSE, NULL, 0, fInteractive);
}
else if (!DV_ISDESKTOP(pdsv) && !DV_ISOWNERDATA(pdsv))
{
// We don't need to get desktop icons in the background, since it
// doesn't scroll anyway
DefView_IdleDoStuff(pdsv, WM_DVI_LOADSTUFF, (LPARAM)pdsv);
}
//
// Decrement the reference count
//
pdsv->sv.lpVtbl->Release(&pdsv->sv);
SendMessage(pdsv->hwndListview, WM_SETREDRAW, (WPARAM)TRUE, 0);
if (pdsv->pSaveHeader)
{
LocalFree((HLOCAL)pdsv->pSaveHeader);
pdsv->pSaveHeader = NULL;
pdsv->uSaveHeaderLen = 0;
}
#ifdef TIMING
dwFinish = GetTickCount();
{
TCHAR sz[40];
wsprintf(sz, TEXT("%d\n"), dwFinish - dwStart);
OutputDebugString(sz);
}
#endif
}
void ChangeRefForIdle(LPDEFVIEW pdsv, BOOL bAdd)
{
if (bAdd)
{
if (InterlockedIncrement(&pdsv->cRefForIdle) == 0)
{
pdsv->sv.lpVtbl->AddRef(&pdsv->sv);
}
}
else
{
if (InterlockedDecrement(&pdsv->cRefForIdle) < 0)
{
pdsv->sv.lpVtbl->Release(&pdsv->sv);
}
}
}
typedef struct _ENUMTHREAD
{
LPDEFVIEW pdsv;
LPENUMIDLIST peunk;
HDPA hdpaNew;
BOOL bRefresh;
} ENUMTHREAD, *PENUMTHREAD;
//
// Enumeration loop for IDLE thread.
//
void DefView_EnumObjs(PENUMTHREAD pEnum, BOOL bAnother)
{
ENUMTHREAD et = *pEnum;
// We'll only QI for ASyncEnum once. If it's rejected, we'll set this to
// FALSE so we don't keep trying
BOOL fCouldDoOwnerData = TRUE;
LPITEMIDLIST pidl = NULL; // just in case
ULONG celt;
DWORD idThisThread = GetCurrentThreadId();
DWORD cFoundSoFar = 0;
Free(pEnum);
if (bAnother)
{
// I assume this is an invalid thread ID
idThisThread = INVALID_THREAD_ID;
}
while (et.pdsv->idThreadIdle==idThisThread
&& DV_Next(et.pdsv, et.peunk, 1, &pidl, &celt) == S_OK)
{
if (et.pdsv->idThreadIdle!=idThisThread
|| DPA_InsertPtr(et.hdpaNew, INT_MAX, pidl)==-1)
{
SHFree(pidl);
}
else
{
cFoundSoFar++;
if (fCouldDoOwnerData && cFoundSoFar >= OD_CROSSOVER_LIMIT)
{
if (FAILED(et.peunk->lpVtbl->QueryInterface(et.peunk,
&IID_IAsynchEnumIDList,
&(et.pdsv->pasyncenum))))
{
// This enumerator has never heard of async enumeration, so
// don't do it now and don't bother asking it again
fCouldDoOwnerData = FALSE;
}
else
{
// We're going to go async/ownerdata
ASSERT(et.pdsv->hwndView);
if (et.pdsv->hwndListview)
{
SendMessage(et.pdsv->hwndView, WM_DSV_CREATEODLISTVIEW, 0, 0);
// If the flashlight animation is playing, we can shut it
// down now
if (et.pdsv->hwndView && PostMessage(et.pdsv->hwndView, WM_DSV_DESTROYSTATIC,
et.bRefresh, (LPARAM)et.hdpaNew))
// Clean up
et.pdsv->bBkFilling = FALSE;
DPA_Destroy(et.hdpaNew);
et.peunk->lpVtbl->Release(et.peunk);
ChangeRefForIdle(et.pdsv, FALSE);
// We're outta here
return;
}
}
}
}
pidl = NULL;
}
et.pdsv->bBkFilling = FALSE;
if (et.pdsv->idThreadIdle == idThisThread)
{
HWND hwndView;
// Sort on this thread so we do not hang the main thread for as long
DPA_Sort(et.hdpaNew, (PFNDPACOMPARE)DefView_Compare, (LPARAM)et.pdsv);
// Tell the main thread to merge the items
hwndView = et.pdsv->hwndView;
if (hwndView && PostMessage(hwndView, WM_DSV_DESTROYSTATIC,
et.bRefresh, (LPARAM)et.hdpaNew))
{
// If hwndView is destroyed before receiving this message, we could
// have a memory leak. Oh well.
et.hdpaNew = NULL;
}
}
if (et.hdpaNew)
{
DPA_Destroy(et.hdpaNew);
}
et.peunk->lpVtbl->Release(et.peunk);
ChangeRefForIdle(et.pdsv, FALSE);
}
DWORD DefView_IdleThreadProc(MSG *pMsg)
{
MSG msg;
LPDEFVIEW pdsv = NULL;
HANDLE hThread;
DWORD idThread, idThisThread = GetCurrentThreadId();
BOOL bRet;
msg = *pMsg;
LocalFree((HLOCAL)pMsg);
do
{
// Note the first thing we do is create a message queue
MSG msgTemp;
BOOL bAnother = PeekMessage(&msgTemp, NULL,
msg.message, msg.message, PM_NOREMOVE);
switch (msg.message)
{
case WM_DVI_FILLSTUFF:
DefView_EnumObjs((PENUMTHREAD)msg.lParam, bAnother);
// Get the LPDEFVIEW for the last known message
pdsv = (LPDEFVIEW)msg.wParam;
break;
case WM_DVI_LOADSTUFF:
DefView_LoadIcons((LPDEFVIEW)msg.lParam, bAnother);
// Get the LPDEFVIEW for the last known message
pdsv = (LPDEFVIEW)msg.wParam;
break;
default:
// We got a message we did not expect. Very bad.
DebugMsg( DM_ERROR, TEXT("Defview_IdleThreadProc: Got unexpected message 0x%lx!!!"), msg.message );
break;
}
GetNextMessage:;
}
while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE));
bRet = TRUE;
ENTERCRITICAL;
if (pdsv)
{
// BUGBUG: If the thread exits early, we will blow it away. I can
// live with this for now.
hThread = pdsv->hThreadIdle;
idThread = pdsv->idThreadIdle;
if (idThread == idThisThread)
{
pdsv->hThreadIdle = NULL;
pdsv->idThreadIdle = 0;
}
else
{
hThread = NULL;
}
// Make sure I don't use this when I shouldn't
pdsv = NULL;
}
else
{
hThread = NULL;
}
if (hThread)
{
if (gp_dvp.hThreadIdle)
{
CloseHandle(hThread);
}
else
{
bRet = FALSE;
gp_dvp.hThreadIdle = hThread;
gp_dvp.idThreadIdle = idThread;
}
}
LEAVECRITICAL;
if (!bRet)
{
// Note the "last" thread may not return; it should always be
// safe to terminate it while in a wait
// We will only leave this thread around for about 5 minutes
// so it does not take up memory forever after a user has
// played with the Shell just once
if (MsgWaitForMultipleObjects(0, NULL, FALSE, 5*60*1000,
QS_POSTMESSAGE) != WAIT_TIMEOUT)
{
// We must have gotten a message, so wake up and
// process it
goto GetNextMessage;
}
ENTERCRITICAL;
if (gp_dvp.idThreadIdle == idThisThread)
{
CloseHandle(gp_dvp.hThreadIdle);
gp_dvp.hThreadIdle = NULL;
gp_dvp.idThreadIdle = 0;
}
LEAVECRITICAL;
// Check if somebody stole the global thread (and it was this
// one) just after the MsgWait
if (PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE))
{
goto GetNextMessage;
}
}
return(0);
}
DWORD WaitForSendMessageThread(HANDLE hThread, DWORD dwTimeout)
{
MSG msg;
DWORD dwRet;
DWORD dwEnd = GetTickCount() + dwTimeout;
// We will attempt to wait up to dwTimeout for the thread to
// terminate
do
{
dwRet = MsgWaitForMultipleObjects(1, &hThread, FALSE,
dwTimeout, QS_SENDMESSAGE);
if (dwRet == WAIT_OBJECT_0 ||
dwRet == WAIT_FAILED)
{
// The thread must have exited, so we are happy
break;
}
if (dwRet == WAIT_TIMEOUT)
{
// The thread is taking too long to finish, so just
// return and let the caller kill it
break;
}
// There must be a pending SendMessage from either the
// thread we are killing or some other thread/process besides
// this one. Do a PeekMessage to process the pending
// SendMessage and try waiting again
PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE);
if (dwTimeout != INFINITE)
dwTimeout = dwEnd - GetTickCount();
}
while((dwTimeout == INFINITE) || ((long)dwTimeout > 0));
return(dwRet);
}
void DefView_KillIdleThread(LPDEFVIEW pdsv, BOOL bTerminate)
{
HANDLE hThread;
DWORD idThread;
ENTERCRITICAL;
hThread = pdsv->hThreadIdle;
idThread = pdsv->idThreadIdle;
pdsv->hThreadIdle = NULL;
pdsv->idThreadIdle = 0;
LEAVECRITICAL;
if (hThread)
{
// still running!
// We will attempt to wait up to 2 seconds for the thread to
// terminate
if (WaitForSendMessageThread(hThread, 2000)==WAIT_TIMEOUT
&& bTerminate)
{
DebugMsg(DM_TRACE, TEXT("sh TR - DV_KillIdlleThread calling TerminateThread because of timeout!"));
// Blow it away!
TerminateThread(hThread, (DWORD)-1);
ENTERCRITICAL;
if (pdsv->cRefForIdle != -1)
{
// Note that we will err on the side of not
// releasing enough rather than too often,
// which should never happen.
// There is only a tiny window when the forgotme
// flag is not set and the release has happened,
// so the chance of a memory leak is nearly 0.
pdsv->cRefForIdle = 0;
ChangeRefForIdle(pdsv, FALSE);
}
LEAVECRITICAL;
}
CloseHandle(hThread);
}
}
//
// Returns: TRUE, if we successfully create the idle thread.
//
BOOL DV_StartIdle(LPDEFVIEW pdsv, LPENUMIDLIST peunk, HDPA hdpaNew, BOOL bRefresh)
{
BOOL fRet = FALSE;
if (EnumerationTimeout(pdsv, bRefresh))
{
PENUMTHREAD pEnum = Alloc(SIZEOF(ENUMTHREAD));
if (pEnum)
{
pEnum->pdsv = pdsv;
pEnum->peunk = peunk;
pEnum->hdpaNew = hdpaNew;
pEnum->bRefresh = bRefresh;
pdsv->bBkFilling = TRUE;
if (DefView_IdleDoStuff(pdsv, WM_DVI_FILLSTUFF, (LPARAM)pEnum))
{
SetTimer(pdsv->hwndView, DV_IDTIMER, ENUMTIMEOUT, NULL);
fRet = TRUE;
}
else
{
Free(pEnum);
pdsv->idThreadIdle = 0;
pdsv->bBkFilling = FALSE;
}
}
}
return fRet;
}
//----------------------------------------------------------------------------
//
// Enumeration loop for the main GUI thread.
//
HRESULT DefView_FillObjects(LPDEFVIEW pdsv, BOOL bRefresh,
PDVSAVEHEADER pSaveHeader, UINT uLen, BOOL fInteractive)
{
LPENUMIDLIST peunk;
LPITEMIDLIST pidl = NULL; // just in case
ULONG celt;
BOOL fCouldDoOwnerData = TRUE;
ULONG cFoundSoFar = 0;
DWORD dwTime;
BOOL fTimeOut;
HRESULT hres;
DWORD dwEnumFlags;
DWORD dwTimeout = pdsv->fs.fFlags&FWF_BESTFITWINDOW ?
ENUMTIMEOUT : SHORTENUMTIMEOUT;
HDPA hdpaNew;
DECLAREWAITCURSOR;
#ifdef TIMING
dwStart = GetTickCount();
#endif
// This is a potentially long operation
SetWaitCursor();
DefView_KillIdleThread(pdsv, FALSE);
if (pdsv->pidlSelect)
{
// We should not save the selection if we are doing a refresh
ILFree(pdsv->pidlSelect);
pdsv->pidlSelect = NULL;
}
if (bRefresh)
{
DefView_UpdateGlobalFlags(pdsv);
DefView_RemoveObject(pdsv, NULL, FALSE);
}
// Setup the enum flags.
dwEnumFlags = SHCONTF_NONFOLDERS;
if (pdsv->fShowAllObjects)
{
dwEnumFlags |= SHCONTF_INCLUDEHIDDEN ;
}
// Start menu view doesn't want folders.
if (!(pdsv->fs.fFlags & FWF_NOSUBFOLDERS))
{
dwEnumFlags |= SHCONTF_FOLDERS;
}
hres = pdsv->pshf->lpVtbl->EnumObjects(pdsv->pshf,
fInteractive ? pdsv->hwndMain:NULL,
dwEnumFlags, &peunk);
// Please note the it may return S_FALSE which indicates no enumerator.
// That's why we shouldn't use if (FAILED(hres))
if (hres != S_OK)
{
//
// Special case for doc-find.
//
if (hres == S_FALSE)
ShowWindow(pdsv->hwndListview, SW_SHOW);
goto Done;
}
hdpaNew = DPA_Create(16);
if (!hdpaNew)
{
goto Error1;
}
// If you want to hang around for a while, just support the
// IDelayedRelease interface
if (pdsv->pdr)
pdsv->pdr->lpVtbl->Release(pdsv->pdr);
peunk->lpVtbl->QueryInterface(peunk, &IID_IDelayedRelease, &pdsv->pdr);
TIMEIN(pdsv->Fill);
TIMEIN(pdsv->AddObject);
TIMEIN(pdsv->GetIcon);
TIMEIN(pdsv->GetName);
TIMEIN(pdsv->FSNotify);
TIMEIN(pdsv->EnumNext);
TIMEIN(pdsv->RestoreState);
TIMEIN(pdsv->WMNotify);
TIMEIN(pdsv->LVChanging);
TIMEIN(pdsv->LVChanged);
TIMEIN(pdsv->LVDelete);
TIMEIN(pdsv->LVGetDispInfo);
TIMESTART(pdsv->Fill);
DebugMsg(DM_TRACE, TEXT("DefView_FillObjects(%s) *****************************************"), DV_Name(pdsv));
//
// If the callback returns S_OK to DVM_BACKGROUNDENUM, start the idle
// thread immediately.
//
if ( pdsv->pfnCallback(pdsv->psvOuter, pdsv->pshf, pdsv->hwndMain,
DVM_BACKGROUNDENUM, 0, 0) == S_OK )
{
DebugMsg(DM_TRACE, TEXT("sh TR - DefView_FillObjects : Start idle thread immediately"));
if (DV_StartIdle(pdsv, peunk, hdpaNew, bRefresh)) {
goto Done;
}
fTimeOut = TRUE; // Failed to start, don't try it again.
}
else
{
fTimeOut = FALSE;
}
dwTime = GetTickCount();
while (DV_Next(pdsv, peunk, 1, &pidl, &celt) == S_OK)
{
if (DPA_InsertPtr(hdpaNew, INT_MAX, pidl) == -1)
{
SHFree(pidl);
}
else
{
cFoundSoFar++;
if (fCouldDoOwnerData && cFoundSoFar >= OD_CROSSOVER_LIMIT)
{
if (FAILED(peunk->lpVtbl->QueryInterface(peunk,
&IID_IAsynchEnumIDList,
&(pdsv->pasyncenum))))
{
// This enumerator has never heard of async enumeration, so
// don't do it now and don't bother asking it again
fCouldDoOwnerData = FALSE;
}
else
{
// We're going to go async/ownerdata
ASSERT(pdsv->hwndView);
if (pdsv->hwndListview)
{
// Tell the view we've hit the point at which we need
// to become ownerdata, and have it recreate the appropriate
// listview type
SendMessage(pdsv->hwndView, WM_DSV_CREATEODLISTVIEW, 0, 0);
// Clean up
DefView_FillDone(pdsv, hdpaNew, pSaveHeader, uLen, bRefresh, fInteractive);
peunk->lpVtbl->Release(peunk);
// We're outta here
return hres;
}
}
}
}
pidl = NULL;
// Are we taking too long?
if (!fTimeOut && ((GetTickCount() - dwTime) > dwTimeout))
{
fTimeOut = TRUE;
if (!DV_ISDESKTOP(pdsv))
{
if (DV_StartIdle(pdsv, peunk, hdpaNew, bRefresh)) {
goto Done;
}
}
}
}
DPA_Sort(hdpaNew, (PFNDPACOMPARE)DefView_Compare, (LPARAM)pdsv);
DefView_FillDone(pdsv, hdpaNew, pSaveHeader, uLen, bRefresh, fInteractive);
Error1:;
peunk->lpVtbl->Release(peunk);
Done:
ResetWaitCursor();
return hres;
}
HRESULT DefView_FillObjectsShowHide(LPDEFVIEW pdsv, BOOL bRefresh,
PDVSAVEHEADER pSaveHeader, UINT uLen, BOOL fInteractive)
{
HRESULT hres;
hres = DefView_FillObjects(pdsv, bRefresh, pSaveHeader, uLen, fInteractive);
if (!pdsv->hwndListview) {
return hres;
}
pdsv->hres = hres;
if (SUCCEEDED(hres))
{
//
// If previous enumeration failed, we need to make it visible.
//
if (pdsv->fEnumFailed)
{
pdsv->fEnumFailed = FALSE;
ShowWindow(pdsv->hwndListview, SW_SHOW);
}
}
else
{
//
// The fill objects failed for some reason, we should
// display an appropriate error message.
//
if (!pdsv->fEnumFailed)
{
ShowWindow(pdsv->hwndListview, SW_HIDE);
}
DebugMsg(DM_TRACE, TEXT("sh TR - TRACE: DefView_FillObjects failed (%x)"), hres);
pdsv->fEnumFailed = TRUE;
}
return hres;
}
void DefView_MoveSelectedItems(LPDEFVIEW pdsv, int dx, int dy)
{
LVUtil_MoveSelectedItems(pdsv->hwndListview, dx, dy);
pdsv->bItemsMoved = TRUE;
pdsv->bClearItemPos = FALSE;
}
TCHAR const c_szDelete[] = TEXT("delete");
TCHAR const c_szCut[] = TEXT("cut");
TCHAR const c_szCopy[] = TEXT("copy");
TCHAR const c_szLink[] = TEXT("link");
TCHAR const c_szProperties[] = TEXT("properties");
TCHAR const c_szPaste[] = TEXT("paste");
TCHAR const c_szPasteLink[] = TEXT("pastelink");
// char const c_szPasteSpecial[] = "pastespecial";
TCHAR const c_szRename[] = TEXT("rename");
//
// This function processes command from explorer menu (FCIDM_*)
//
// HACK ALERT:
// This implementation uses following assumptions.
// (1) The IShellFolder uses CDefFolderMenu.
// (2) The CDefFolderMenu always add the folder at the top.
//
#define EC_SELECTION 0
#define EC_BACKGROUND 1
#define EC_EITHER 3
void DefView_ExplorerCommand(LPDEFVIEW pdsv, UINT idFCIDM)
{
//
// s_idMap[i][0] = Defview menuitem ID
// s_idMap[i][1] = FALSE, context menu; TRUE; background menu
// s_idMap[i][2] = Folder menuitem ID
//
#pragma data_seg(".text", "CODE")
struct {
UINT idmFC;
UINT f_Background;
LPCTSTR pszVerb;
} const c_idMap[] = {
{ SFVIDM_FILE_RENAME, EC_SELECTION, c_szRename },
{ SFVIDM_FILE_DELETE, EC_SELECTION, c_szDelete },
{ SFVIDM_FILE_PROPERTIES, EC_EITHER, c_szProperties },
{ SFVIDM_EDIT_COPY, EC_SELECTION, c_szCopy },
{ SFVIDM_EDIT_CUT, EC_SELECTION, c_szCut },
{ SFVIDM_FILE_LINK, EC_SELECTION, c_szLink },
{ SFVIDM_EDIT_PASTE, EC_BACKGROUND, c_szPaste },
{ SFVIDM_EDIT_PASTELINK, EC_BACKGROUND, c_szPasteLink },
// { SFVIDM_EDIT_PASTESPECIAL,TRUE, c_szPasteSpecial },
};
#pragma data_seg()
int i;
for (i=0; i<ARRAYSIZE(c_idMap); i++)
{
if (c_idMap[i].idmFC==idFCIDM)
{
LPCONTEXTMENU pcm = NULL;
if (c_idMap[i].f_Background == EC_BACKGROUND)
{
TryBackground:
pdsv->pshf->lpVtbl->CreateViewObject(pdsv->pshf, pdsv->hwndMain,
&IID_IContextMenu, &pcm);
}
else
{
pcm = DefView_GetContextMenuFromSelection(pdsv);
if (!pcm && c_idMap[i].f_Background == EC_EITHER &&
!ListView_GetSelectedCount(pdsv->hwndListview)) {
goto TryBackground;
}
}
if (pcm)
{
CMINVOKECOMMANDINFOEX ici = {
SIZEOF(CMINVOKECOMMANDINFOEX),
0L,
pdsv->hwndMain,
NULL,
NULL, NULL,
SW_NORMAL,
};
//
// We need to call QueryContextMenu() so that CDefFolderMenu
// can initialize its dispatch table correctly.
//
HMENU hmenu = CreatePopupMenu();
#ifdef UNICODE
// Fill in both the ansi verb and the unicode verb since we
// don't know who is going to be processing this thing.
CHAR szVerbAnsi[40];
WideCharToMultiByte(CP_ACP, 0,
c_idMap[i].pszVerb, -1,
szVerbAnsi, ARRAYSIZE(szVerbAnsi),
NULL, NULL);
ici.lpVerb = szVerbAnsi;
ici.lpVerbW = c_idMap[i].pszVerb;
ici.fMask |= CMIC_MASK_UNICODE;
#else
ici.lpVerb = c_idMap[i].pszVerb;
#endif
if (hmenu)
{
pcm->lpVtbl->QueryContextMenu(pcm, hmenu, 0,
SFVIDM_CONTEXT_FIRST, SFVIDM_CONTEXT_LAST, 0);
pdsv->bContextMenuMode = TRUE;
DefView_InvokeCommand(pdsv, pcm, &ici);
pdsv->bContextMenuMode = FALSE;
DestroyMenu(hmenu);
}
pcm->lpVtbl->Release(pcm);
if (pdsv->pcmSel == pcm)
{
DV_FlushCachedMenu(pdsv);
}
}
else
{
//
// We should beep if when one of those object keys are
// pressed when there is no selection.
//
MessageBeep(0);
}
break;
}
}
Assert(i<ARRAYSIZE(c_idMap));
}
#ifdef MEMMON
void WINAPI MemMon_Msgs(BOOL fEnable);
void WINAPI MemMon_ResetCounters(void);
void WINAPI MemMon_GetCounters(LPDWORD pdwAllocs, LPDWORD pdwFrees, LPDWORD pdwReallocs, LPDWORD pdwAlloced);
//---------------------------------------------------------------------------
static g_fDebugOuts = 0;
#define IDT_MEMMON 1
//---------------------------------------------------------------------------
BOOL MemMonUI_UpdateStats(HWND hwnd)
{
static DWORD dwAllocsOld, dwFreesOld, dwReallocsOld, dwAllocatedOld, dwLeftOld;
DWORD dwAllocs, dwFrees, dwReallocs, dwAllocated, dwLeft;
TCHAR szAlloced[MAX_PATH];
MemMon_GetCounters(&dwAllocs, &dwFrees, &dwReallocs, &dwAllocated);
dwLeft = dwAllocs - dwFrees;
if (dwAllocs != dwAllocsOld)
SetDlgItemInt(hwnd, IDC_ALLOCS, dwAllocs, FALSE);
if (dwReallocs != dwReallocsOld)
SetDlgItemInt(hwnd, IDC_REALLOCS, dwReallocs, FALSE);
if (dwFrees != dwFreesOld)
SetDlgItemInt(hwnd, IDC_FREES, dwFrees, FALSE);
if (dwAllocated != dwAllocatedOld || dwLeft != dwLeftOld)
{
wsprintf(szAlloced, TEXT("%d (%d)"), dwLeft, dwAllocated);
SetDlgItemText(hwnd, IDC_ALLOCED, szAlloced);
}
// if (fUpdateAddress)
// SetDlgItemInt(hwnd, IDC_ADDRESS, g_dwAddress, FALSE);
dwAllocsOld = dwAllocs;
dwReallocsOld = dwReallocs;
dwFreesOld = dwFrees;
dwAllocatedOld = dwAllocated;
dwLeftOld = dwLeft;
return TRUE;
}
//---------------------------------------------------------------------------
BOOL MemMonUI_OnInitDlg(HWND hwnd)
{
CheckDlgButton(hwnd, IDC_DEBUGOUTS, g_fDebugOuts);
MemMon_Msgs(g_fDebugOuts);
MemMonUI_UpdateStats(hwnd);
SetTimer(hwnd, IDT_MEMMON, 1*1000, NULL);
return TRUE;
}
//---------------------------------------------------------------------------
BOOL MemMonUI_OnCommand(HWND hwnd, WPARAM wParam, LPARAM lParam)
{
UINT idCmd = GET_WM_COMMAND_ID(wParam, lParam);
switch(idCmd)
{
case IDCANCEL:
case IDCLOSE:
KillTimer(hwnd, IDT_MEMMON);
EndDialog(hwnd, idCmd);
return TRUE;
case IDC_RESET:
MemMon_ResetCounters();
MemMonUI_UpdateStats(hwnd);
return TRUE;
case IDC_DEBUGOUTS:
g_fDebugOuts = IsDlgButtonChecked(hwnd, IDC_DEBUGOUTS) ? FALSE : TRUE;
CheckDlgButton(hwnd, IDC_DEBUGOUTS, g_fDebugOuts);
MemMon_Msgs(g_fDebugOuts);
return TRUE;
}
return FALSE;
}
//---------------------------------------------------------------------------
BOOL MemMonUI_OnTimer(HWND hwnd)
{
MemMonUI_UpdateStats(hwnd);
return TRUE;
}
//---------------------------------------------------------------------------
BOOL CALLBACK MemMonUI_DlgProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch(uMsg)
{
case WM_INITDIALOG:
return MemMonUI_OnInitDlg(hwnd);
case WM_COMMAND:
return MemMonUI_OnCommand(hwnd, wParam, lParam);
case WM_TIMER:
return MemMonUI_OnTimer(hwnd);
}
return FALSE;
}
//---------------------------------------------------------------------------
void _MemMon(HWND hwnd)
{
CreateDialog(HINST_THISDLL, MAKEINTRESOURCE(DLG_MEMMON), hwnd, MemMonUI_DlgProc);
}
#endif
BOOL Def_IsPasteAvailable(LPDROPTARGET pdtgt, LPDWORD pdwEffect);
BOOL DefView_AllowCommand(LPDEFVIEW pdsv, UINT uID, WPARAM wParam, LPARAM lParam)
{
DWORD dwAttribsIn;
DWORD dwEffect;
switch (uID)
{
case SFVIDM_EDIT_PASTE:
return Def_IsPasteAvailable(pdsv->pdtgtBack, &dwEffect);
case SFVIDM_EDIT_PASTELINK:
Def_IsPasteAvailable(pdsv->pdtgtBack, &dwEffect);
return dwEffect & DROPEFFECT_LINK;
case SFVIDM_EDIT_COPY:
dwAttribsIn = SFGAO_CANCOPY;
break;
case SFVIDM_EDIT_CUT:
dwAttribsIn = SFGAO_CANMOVE;
break;
case SFVIDM_FILE_DELETE:
dwAttribsIn = SFGAO_CANDELETE;
break;
case SFVIDM_FILE_LINK:
dwAttribsIn = SFGAO_CANLINK;
break;
case SFVIDM_FILE_PROPERTIES:
dwAttribsIn = SFGAO_HASPROPSHEET;
break;
default:
Assert(FALSE);
return FALSE;
}
return (DefView_GetAttributesFromSelection(pdsv, dwAttribsIn) & dwAttribsIn);
}
TCHAR const c_szWindowsHlp[] = TEXT("windows.hlp");
LRESULT DefView_Command(LPDEFVIEW pdsv, LPCONTEXTMENU pcmSel, WPARAM wParam, LPARAM lParam)
{
DWORD dwStyle;
int iItem;
UINT uID = GET_WM_COMMAND_ID(wParam, lParam);
if (InRange(uID, SFVIDM_CONTEXT_FIRST, SFVIDM_CONTEXT_LAST))
{
TCHAR szCommandString[20];
BOOL bRenameIt = FALSE;
if (pcmSel == NULL)
{
// We hopefully have a pcmSel object cached away for the current
// selection. If not this wont work anyway so blow out.
pcmSel = pdsv->pcmSel;
}
if (pcmSel)
{
HRESULT hres;
// make sure this object exists throughout the operation.
// ie we dont want a change attributes callback delete this
// object while we are using it...
pcmSel->lpVtbl->AddRef(pcmSel);
// We need to special case the rename command
szCommandString[0] = TEXT('\0');
hres = pcmSel->lpVtbl->GetCommandString(pcmSel,
uID-SFVIDM_CONTEXT_FIRST, GCS_VERB, NULL,
(LPSTR)szCommandString, ARRAYSIZE(szCommandString));
if (FAILED(hres) || *szCommandString == TEXT('\0'))
{
#ifdef UNICODE
CHAR szCommand[20];
szCommand[0] = '\0';
hres = pcmSel->lpVtbl->GetCommandString(pcmSel,
uID-SFVIDM_CONTEXT_FIRST, GCS_VERBA, NULL,
szCommand, ARRAYSIZE(szCommand));
MultiByteToWideChar(CP_ACP, 0,
szCommand, -1,
szCommandString, ARRAYSIZE(szCommandString));
#else
WCHAR szCommand[20];
szCommand[0] = L'\0';
hres = pcmSel->lpVtbl->GetCommandString(pcmSel,
uID-SFVIDM_CONTEXT_FIRST, GCS_VERBW, NULL,
(LPSTR)szCommand, ARRAYSIZE(szCommand));
WideCharToMultiByte(CP_ACP, 0,
szCommand, -1,
szCommandString, ARRAYSIZE(szCommandString),
NULL, NULL);
#endif
}
if (SUCCEEDED(hres) && lstrcmpi(szCommandString, c_szRename)==0)
{
bRenameIt = TRUE;
}
else
{
CMINVOKECOMMANDINFOEX ici = {
SIZEOF(CMINVOKECOMMANDINFOEX),
0L,
pdsv->hwndMain,
(LPSTR)MAKEINTRESOURCE(uID - SFVIDM_CONTEXT_FIRST),
NULL, NULL,
SW_NORMAL,
};
DefView_InvokeCommand(pdsv, pcmSel, &ici);
}
// And release our use of it.
pcmSel->lpVtbl->Release(pcmSel);
if (bRenameIt)
{
goto RenameIt;
}
}
return 0L;
}
// Is the ID within the client's range?
if (InRange(uID, SFVIDM_CLIENT_FIRST, SFVIDM_CLIENT_LAST) && pdsv->pfnCallback)
{
// Yes; pass it on to the callback
pdsv->pfnCallback(pdsv->psvOuter, pdsv->pshf, pdsv->hwndMain,
DVM_INVOKECOMMAND, uID - SFVIDM_CLIENT_FIRST, 0);
return 0L;
}
switch (uID)
{
case SFVIDM_TOOL_CONNECT:
SHNetConnectionDialog(pdsv->hwndMain, NULL, RESOURCETYPE_DISK);
SHChangeNotifyHandleEvents(); // flush any drive notifications
break;
case SFVIDM_TOOL_DISCONNECT:
WNetDisconnectDialog(pdsv->hwndMain, RESOURCETYPE_DISK);
SHChangeNotifyHandleEvents(); // flush any drive notifications
break;
case SFVIDM_EDIT_UNDO:
Undo(pdsv->hwndMain);
break;
case SFVIDM_SELECT_ALL:
{
DECLAREWAITCURSOR;
if (!pdsv->pfnCallback || (pdsv->pfnCallback(pdsv->psvOuter, pdsv->pshf, pdsv->hwndMain, DVM_SELECTALL, 0, pdsv->lSelChangeInfo) != (S_FALSE))) {
SetWaitCursor();
SetFocus(pdsv->hwndListview);
ListView_SetItemState(pdsv->hwndListview, -1, LVIS_SELECTED, LVIS_SELECTED);
ResetWaitCursor();
}
break;
}
case SFVIDM_DESELECT_ALL:
ListView_SetItemState(pdsv->hwndListview, -1, 0, LVIS_SELECTED);
break;
case SFVIDM_SELECT_INVERT:
{
DECLAREWAITCURSOR;
SetWaitCursor();
SetFocus(pdsv->hwndListview);
iItem = -1;
while ((iItem = ListView_GetNextItem(pdsv->hwndListview, iItem, 0)) != -1)
{
UINT flag;
// flip the selection bit on each item
flag = ListView_GetItemState(pdsv->hwndListview, iItem, LVIS_SELECTED);
flag ^= LVNI_SELECTED;
ListView_SetItemState(pdsv->hwndListview, iItem, flag, LVIS_SELECTED);
}
ResetWaitCursor();
break;
}
case SFVIDM_ARRANGE_AUTO:
pdsv->fs.fFlags ^= FWF_AUTOARRANGE; // toggle
SetWindowLong(pdsv->hwndListview, GWL_STYLE, GetWindowStyle(pdsv->hwndListview) ^ LVS_AUTOARRANGE);
break;
case SFVIDM_ARRANGE_GRID:
#ifdef TEST_LVDISABLE
{
static BOOL fFlag = 0;
EnableWindow(pdsv->hwndListview, fFlag);
fFlag = !fFlag;
}
#endif
ListView_Arrange(pdsv->hwndListview, LVA_SNAPTOGRID);
break;
case SFVIDM_VIEW_ICON:
dwStyle = LVS_ICON;
pdsv->bClearItemPos = FALSE;
pdsv->fs.ViewMode = FVM_ICON;
goto SetStyle;
case SFVIDM_VIEW_SMALLICON:
dwStyle = LVS_SMALLICON;
pdsv->bClearItemPos = FALSE;
pdsv->fs.ViewMode = FVM_SMALLICON;
goto SetStyle;
case SFVIDM_VIEW_LIST:
dwStyle = LVS_LIST;
pdsv->bClearItemPos = TRUE;
pdsv->fs.ViewMode = FVM_LIST;
goto SetStyle;
case SFVIDM_VIEW_DETAILS:
dwStyle = LVS_REPORT;
pdsv->bClearItemPos = TRUE;
pdsv->fs.ViewMode = FVM_DETAILS;
goto SetStyle;
SetStyle:
pdsv->dvState.iDirection = 1;
SetWindowLong(pdsv->hwndListview, GWL_STYLE, dwStyle | (GetWindowStyle(pdsv->hwndListview) & ~LVS_TYPEMASK));
_DSV_CheckToolbar(pdsv);
break;
case SFVIDM_EDIT_PASTE:
case SFVIDM_EDIT_PASTELINK:
case SFVIDM_EDIT_COPY:
case SFVIDM_EDIT_CUT:
case SFVIDM_FILE_LINK:
case SFVIDM_FILE_DELETE:
case SFVIDM_FILE_PROPERTIES:
if (DefView_AllowCommand(pdsv, uID, wParam, lParam))
{
DefView_ExplorerCommand(pdsv, GET_WM_COMMAND_ID(wParam, lParam));
}
else
{
MessageBeep(0);
}
break;
case SFVIDM_FILE_RENAME:
// May need to add some more support here later, but...
{
int iItemFocus;
RenameIt:
iItemFocus = ListView_GetNextItem(pdsv->hwndListview, -1, LVNI_FOCUSED);
if (iItemFocus >= 0)
{
// Deselect all items...
UINT uState = ListView_GetItemState(pdsv->hwndListview, iItemFocus, LVIS_SELECTED);
// Deselect all items...
ListView_SetItemState(pdsv->hwndListview, -1, 0, LVIS_SELECTED);
if (uState)
ListView_SetItemState(pdsv->hwndListview, iItemFocus, LVIS_SELECTED, LVIS_SELECTED);
ListView_EditLabel(pdsv->hwndListview, iItemFocus);
}
}
break;
case SFVIDM_MISC_MENUTERM1:
// HACKHACK: After the menu goes away, this command is posted to the
// window, but note that any WM_COMMAND from the menu would still
// be in the queue, so we need to post another WM_COMMAND to delete
// the context menu.
FORWARD_WM_COMMAND(pdsv->hwndView, SFVIDM_MISC_MENUTERM2, 0, 0, PostMessage);
break;
case SFVIDM_MISC_MENUTERM2:
DV_FlushCachedMenu(pdsv);
if (pdsv->pfnCallback) {
pdsv->pfnCallback(pdsv->psvOuter, pdsv->pshf, pdsv->hwndMain,
DVM_EXITMENULOOP, 0, 0);
}
break;
case SFVIDM_HELP_TOPIC:
//
// HACK: Don't call WinHelp when we are in the common dialog.
//
if (pdsv->pcdb==NULL)
{
//
// REVIEW: Should we jump to a topic which describes current
// viewed folder?
//
WinHelp(pdsv->hwndMain, c_szWindowsHlp, HELP_FINDER, 0);
}
break;
#ifdef DEBUG
case SFVIDM_DEBUG_HASH:
DumpHashItemTable(NULL);
break;
case SFVIDM_DEBUG_ICON:
_IconCacheDump();
break;
case SFVIDM_DEBUG_ICON_SAVE:
_IconCacheSave();
break;
case SFVIDM_DEBUG_ICON_FLUSH:
_IconCacheFlush(TRUE);
break;
case (SFVIDM_DEBUG_FIRST) :
case (SFVIDM_DEBUG_FIRST+1):
case (SFVIDM_DEBUG_FIRST+2):
case (SFVIDM_DEBUG_FIRST+3):
case (SFVIDM_DEBUG_FIRST+4):
case (SFVIDM_DEBUG_FIRST+5):
case (SFVIDM_DEBUG_FIRST+6):
case (SFVIDM_DEBUG_FIRST+7):
case (SFVIDM_DEBUG_FIRST+8):
{
HRESULT hres;
UINT uFlags;
LPITEMIDLIST pidlNew = NULL;
int iItemFocus;
TCHAR szPath[MAX_PATH];
switch((uID-SFVIDM_DEBUG_FIRST)/3)
{
case 0:
uFlags = SBSP_PARENT;
break;
case 1:
uFlags = SBSP_ABSOLUTE;
GetWindowsDirectory(szPath, ARRAYSIZE(szPath));
pidlNew = ILCreateFromPath(szPath);
break;
case 2:
uFlags = SBSP_RELATIVE;
iItemFocus = ListView_GetNextItem(pdsv->hwndListview, -1, LVNI_FOCUSED);
if (iItemFocus!=-1) {
pidlNew = ILClone(DSV_GetPIDL(pdsv, iItemFocus));
}
break;
}
switch((uID-SFVIDM_DEBUG_FIRST)%3)
{
case 0: uFlags |= SBSP_SAMEBROWSER; break;
case 1: uFlags |= SBSP_NEWBROWSER; break;
case 2: uFlags |= SBSP_DEFBROWSER; break;
}
hres = pdsv->psb->lpVtbl->BrowseObject(pdsv->psb, pidlNew, uFlags);
DebugMsg(DM_TRACE, TEXT("sh TR - ISB:BrowseObject test returns (%x)"), hres);
if (pidlNew) {
ILFree(pidlNew);
}
}
break;
#endif
#ifdef MEMMON
case SFVIDM_DEBUG_MEMMON:
_MemMon(pdsv->hwndMain);
break;
#endif
default:
// BUGBUG: should not be any of these
// Ignore if this is a WM_COMMAND message from a control.
//
if (!lParam)
{
#ifdef DEBUG
MessageBox(pdsv->hwndView, TEXT("This feature is not implemented yet"),
TEXT("UNDER CONSTRUCTION"), MB_OK);
DebugMsg(DM_TRACE, TEXT("sh TR - command not processed in %s at %d (%x)"),
__FILE__, __LINE__, uID);
Assert(0);
#endif // DEBUG
}
return(1L);
}
return(0L);
}
LPITEMIDLIST DSV_SubObjectExists(LPDEFVIEW pdv, LPCITEMIDLIST pidl)
{
LPITEMIDLIST pidlReal = NULL;
SHGetRealIDL(pdv->pshf, pidl, &pidlReal);
return pidlReal;
}
LRESULT DSV_OnFSNotify(LPDEFVIEW pdsv, LONG lNotification, LPCITEMIDLIST* ppidl);
void DSV_HandleObjectRename(LPDEFVIEW pdsv, LPCITEMIDLIST* ppidl)
{
LPITEMIDLIST ppItems[2];
if (!pdsv->pidlMonitor)
return;
if (!ILIsParent(pdsv->pidlMonitor, ppidl[0], TRUE)) {
// move to this folder
DSV_OnFSNotify(pdsv, SHCNE_CREATE, &ppidl[1]);
} else if (!ILIsParent(pdsv->pidlMonitor, ppidl[1], TRUE)) {
// move from this folder
DSV_OnFSNotify(pdsv, SHCNE_DELETE, &ppidl[0]);
} else {
// rename within this folder
ppItems[0] = (LPITEMIDLIST)ILFindLastID(ppidl[0]);
ppItems[1] = DSV_SubObjectExists(pdsv, ILFindLastID(ppidl[1]));
if (ppItems[1]) {
if (DefView_UpdateObject(pdsv, ppItems) == -1) {
ILFree(ppItems[1]);
}
}
}
}
void DV_UpdateStatusBar(LPDEFVIEW pdsv, BOOL fInitialize)
{
if (pdsv->pfnCallback)
{
if (FAILED(pdsv->pfnCallback(pdsv->psvOuter, pdsv->pshf, pdsv->hwndMain, DVM_UPDATESTATUSBAR, fInitialize, pdsv->lSelChangeInfo)))
{
DV_DoDefaultStatusBar(pdsv, fInitialize);
}
}
}
//---------------------------------------------------------------------------
// Processes a WM_DSV_FSNOTIFY message
//
LRESULT DSV_OnFSNotify(LPDEFVIEW pdsv, LONG lNotification, LPCITEMIDLIST* ppidl)
{
LPITEMIDLIST pidl;
LPCITEMIDLIST pidlItem;
//
// Note that renames between directories are changed to
// create/delete pairs by SHChangeNotify.
//
#ifdef FSNDEBUG
DebugMsg(DM_TRACE, TEXT("DSV_OnFSNotify, hwnd = %d lEvent = %d"), pdsv->hwndView, lNotification);
#endif
switch(lNotification) {
case SHCNE_DRIVEADD:
case SHCNE_CREATE:
case SHCNE_MKDIR:
pidlItem = ILFindLastID(ppidl[0]);
if (pidl = DSV_SubObjectExists(pdsv, pidlItem))
{
// if the item is already in our listview, or if we fail to add it
// then cleanup because we didnt' store the pidl.
if (DefView_FindItem(pdsv, pidlItem, NULL, FALSE) != -1)
{
LPITEMIDLIST pp[2];
pp[0] = (LPITEMIDLIST)pidlItem;
pp[1] = pidl;
if (DefView_UpdateObject(pdsv, pp) < 0) {
// we're bummed
ILFree(pidl);
}
}
else if (DefView_AddObject(pdsv, pidl) == -1)
{
DebugMsg(DM_TRACE, TEXT("s.dsv_ofn: Item already exists."));
ILFree(pidl);
}
else
{
// Item got added OK, we'll need to save it's position.
pdsv->bItemsMoved = TRUE;
}
}
break;
case SHCNE_DRIVEREMOVED:
case SHCNE_DELETE:
case SHCNE_RMDIR:
pidlItem = ILFindLastID(ppidl[0]);
DefView_RemoveObject(pdsv, (LPITEMIDLIST)pidlItem, FALSE);
break;
case SHCNE_RENAMEITEM:
case SHCNE_RENAMEFOLDER:
DSV_HandleObjectRename(pdsv, ppidl);
break;
case SHCNE_UPDATEIMAGE:
//
// the system image cache is changing
//
// ppidl[0] is a IDLIST of image indexs that have changed
//
if (ppidl && ppidl[0])
{
int iImage = *(int UNALIGNED *)((BYTE *)ppidl[0] + 2);
DefView_UpdateImage(pdsv, iImage);
}
break;
case SHCNE_ASSOCCHANGED:
// For this one we will call refresh as we may need to reextract
// the icons and the like. Later we can optimize this somewhat if
// we can detect which ones changed and only update those.
pdsv->sv.lpVtbl->Refresh(&pdsv->sv);
break;
case SHCNE_MEDIAINSERTED:
case SHCNE_MEDIAREMOVED:
case SHCNE_NETUNSHARE:
case SHCNE_NETSHARE:
case SHCNE_UPDATEITEM:
if (ppidl)
{
LPITEMIDLIST pp[2];
pidlItem = ILFindLastID(ppidl[0]);
pp[0] = (LPITEMIDLIST)pidlItem;
pp[1] = DSV_SubObjectExists(pdsv, pidlItem);
if (pp[1]) {
if (DefView_UpdateObject(pdsv, (LPITEMIDLIST*)&pp) < 0)
{
// something went wrong
DefView_Update(pdsv);
ILFree(pp[1]);
}
}
}
else // ppidl == NULL means update all items (re-enum them)
{
DefView_Update(pdsv);
}
break;
case SHCNE_FREESPACE:
// BUGBUG: should only do this if the drive is ours
// and we should change the bool to an enum
{
TCHAR szPath[MAX_PATH];
int idDrive;
if (pdsv->pidlMonitor) {
if (SHGetPathFromIDList(pdsv->pidlMonitor, szPath)) {
DWORD dwChangedDrives = *(DWORD UNALIGNED *)((BYTE *)ppidl[0] + 2);
idDrive = PathGetDriveNumber(szPath);
DebugMsg(DM_TRACE, TEXT("Changed drives = %x"), dwChangedDrives);
if (idDrive != -1 &&
((1 << idDrive) & dwChangedDrives )) {
DV_UpdateStatusBar(pdsv, TRUE);
}
}
}
}
break;
default:
DebugMsg(DM_TRACE, TEXT("DefView: unknown FSNotify %08lX, doing full update"), lNotification);
DefView_Update(pdsv);
break;
}
DV_UpdateStatusBar(pdsv, FALSE);
return 0L;
}
BOOL DefView_GetItemPosition(LPDEFVIEW pdv, LPCITEMIDLIST pidl, LPPOINT lpPt)
{
int i = DefView_FindItem(pdv, pidl, NULL, FALSE);
if (i != -1)
return ListView_GetItemPosition(pdv->hwndListview, i, lpPt);
return FALSE;
}
//---------------------------------------------------------------------------
// called when some of our objects get put on the clipboard
//
LRESULT DSV_OnSetClipboard(LPDEFVIEW pdsv, UINT idCmd)
{
DebugMsg(DM_TRACE, TEXT("DSV_OnSetClipboard"));
Assert((idCmd == DFM_CMD_MOVE) || (idCmd == DFM_CMD_COPY));
if (idCmd == DFM_CMD_MOVE) // move
{
//
// mark all selected items as being "cut"
//
int i = -1;
while ((i = ListView_GetNextItem(pdsv->hwndListview, i, LVIS_SELECTED)) != -1)
{
ListView_SetItemState(pdsv->hwndListview, i, LVIS_CUT, LVIS_CUT);
//ListView_RedrawItems(pdsv->hwndListview, i, i+1); // is there a better way?
pdsv->bHaveCutStuff = TRUE;
}
//
// join the clipboard viewer chain so we will know when to
// "uncut" our selected items.
//
if (pdsv->bHaveCutStuff)
{
Assert(!pdsv->bClipViewer);
Assert(pdsv->hwndNextViewer == NULL);
pdsv->hwndNextViewer = SetClipboardViewer(pdsv->hwndView);
pdsv->bClipViewer = TRUE;
}
}
return 0;
}
//---------------------------------------------------------------------------
// called when the clipboard get changed, clear any items in the "cut" state
//
LRESULT DSV_OnClipboardChange(LPDEFVIEW pdsv)
{
//
// if we dont have any cut stuff we dont care.
//
if (!pdsv->bHaveCutStuff)
return 0;
Assert(pdsv->bClipViewer);
DebugMsg(DM_TRACE, TEXT("DSV_OnDrawClipboard"));
ListView_SetItemState(pdsv->hwndListview, -1, 0, LVIS_CUT);
pdsv->bHaveCutStuff = FALSE;
//
// unhook from the clipboard viewer chain.
//
ChangeClipboardChain(pdsv->hwndView, pdsv->hwndNextViewer);
pdsv->bClipViewer = FALSE;
pdsv->hwndNextViewer = NULL;
return 0;
}
//
// Note: this function returns the point in Listview Coordinate
// space. So any hit testing done with this needs to be converted
// back to Client coordinate space...
BOOL DefView_GetDropPoint(LPDEFVIEW pdv, POINT *lpPt)
{
// Check whether we already have gotten the drop anchor (before any
// menu processing)
if (pdv->bDropAnchor)
{
*lpPt = pdv->ptDrop;
LVUtil_ClientToLV(pdv->hwndListview, lpPt);
}
else if (pdv->bMouseMenu)
{
*lpPt = pdv->ptDragAnchor;
return TRUE;
}
else
{
// We need the most up-to-date cursor information, since this
// may be called during a drop, and the last time the current
// thread called GetMessage was about 10 minutes ago
GetCursorPos(lpPt);
LVUtil_ScreenToLV(pdv->hwndListview, lpPt);
}
return pdv->bDropAnchor;
}
BOOL DefView_GetDragPoint(LPDEFVIEW pdv, POINT *lpPt)
{
BOOL fSource = pdv->bDragSource || pdv->bMouseMenu;
if (fSource) {
// if anchor from mouse activity
*lpPt = pdv->ptDragAnchor;
} else {
// if anchor from keyboard activity... use the focused item
int i = ListView_GetNextItem(pdv->hwndListview, -1, LVNI_FOCUSED);
if (i != -1)
ListView_GetItemPosition(pdv->hwndListview, i, lpPt);
}
return fSource;
}
void DV_PaintErrMsg(HWND hWnd, LPDEFVIEW pdsv)
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
RECT rc;
GetClientRect(hWnd, &rc);
DebugMsg(DM_TRACE, TEXT("sh TR - DV_PaintErrMsg is called (%x,%d)"), pdsv->hres, SCODE_CODE(pdsv->hres));
DrawEdge(hdc, &rc, EDGE_SUNKEN, BF_RECT|BF_SOFT|BF_ADJUST|BF_MIDDLE);
EndPaint(hWnd, &ps);
}
void DV_DoDefaultStatusBar(LPDEFVIEW pdsv, BOOL fInitialize)
{
if (pdsv->psb)
{
// Some of the failure cases do not null hwnd...
HWND hwndStatus=NULL;
pdsv->psb->lpVtbl->GetControlWindow(pdsv->psb, FCW_STATUS, &hwndStatus);
if (hwndStatus)
{
int ciParts[] = {-1};
int nSelected;
TCHAR szTemp[30];
TCHAR szTemplate[80];
TCHAR szStatus[128];
if (fInitialize)
SendMessage(hwndStatus, SB_SETPARTS, ARRAYSIZE(ciParts), (LPARAM)ciParts);
if (nSelected = ListView_GetSelectedCount(pdsv->hwndListview))
{
LoadString(HINST_THISDLL,
IDS_FSSTATUSSELECTED,
szTemplate, ARRAYSIZE(szTemplate));
#ifdef WINDOWS_ME
wsprintf(&szStatus[2], szTemplate, AddCommas(nSelected, szTemp));
#else
wsprintf(szStatus, szTemplate, AddCommas(nSelected, szTemp));
#endif
}
else
{
int i = ListView_GetItemCount(pdsv->hwndListview);
LoadString(HINST_THISDLL, IDS_FSSTATUSNOHIDDENTEMPLATE,
szTemplate, ARRAYSIZE(szTemplate));
#ifdef WINDOWS_ME
wsprintf(&szStatus[2], szTemplate, AddCommas(i, szTemp));
#else
wsprintf(szStatus, szTemplate, AddCommas(i, szTemp));
#endif
}
#ifdef WINDOWS_ME
szStatus[0] = szStatus[1] = TEXT('\t');
SendMessage(hwndStatus, SB_SETTEXT, (WPARAM)SB_RTLREADING, (LPARAM)szStatus);
#else
SendMessage(hwndStatus, SB_SETTEXT, (WPARAM)0, (LPARAM)szStatus);
#endif
}
}
}
void DefView_OnWinIniChange(LPDEFVIEW pdsv, WPARAM wParam, LPCTSTR lpszSection)
{
if (!wParam || (lpszSection && !lstrcmpi(lpszSection, TEXT("intl"))))
{
// has the time format changed while we're in details mode?
if (FVM_DETAILS == pdsv->fs.ViewMode) {
InvalidateRect(pdsv->hwndListview, NULL, TRUE);
}
}
//
// we may need to rebuild the icon cache.
//
if (wParam == SPI_SETICONMETRICS ||
wParam == SPI_SETNONCLIENTMETRICS)
{
HIMAGELIST himlLarge, himlSmall;
Shell_GetImageLists(&himlLarge, &himlSmall);
ListView_SetImageList(pdsv->hwndListview, himlLarge, LVSIL_NORMAL);
ListView_SetImageList(pdsv->hwndListview, himlSmall, LVSIL_SMALL);
}
//
// we need to invalidate the cursor cache
//
if (wParam == SPI_SETCURSORS) {
extern void DAD_InvalidateCursors(void);
DAD_InvalidateCursors();
}
if (DV_ISDESKTOP(pdsv) &&
(wParam == SPI_SETDESKWALLPAPER || wParam == SPI_SETDESKPATTERN)) {
DSV_SetFolderColors(pdsv);
InvalidateRect(pdsv->hwndListview, NULL, TRUE);
}
}
LRESULT CALLBACK DefView_WndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam)
{
LPDEFVIEW pdsv;
LRESULT l;
if (iMessage==WM_CREATE) {
return DefView_WndCreate(hWnd, (LPCREATESTRUCT)lParam);
}
pdsv = (LPDEFVIEW)GetWindowLong(hWnd, 0);
if (pdsv==NULL)
{
if (iMessage!=WM_NCCALCSIZE && iMessage!=WM_NCCREATE)
{
Assert(0); // we are not supposed to hit this assert
}
goto DoDefWndProc;
}
switch (iMessage) {
case WM_DESTROY:
DefView_KillIdleThread(pdsv, TRUE);
// Depending on when it is closed we may have an outstanding post
// to us about the rest of the fill data which we should try to
// process in order to keep from leaking stuff...
DefView_CheckForFillDoneOnDestroy(pdsv, hWnd);
//
// remove ourself as a clipboard viewer
//
if (pdsv->bClipViewer)
{
ChangeClipboardChain(hWnd, pdsv->hwndNextViewer);
pdsv->bClipViewer = FALSE;
pdsv->hwndNextViewer = NULL;
}
if (pdsv->uRegister)
{
SHChangeNotifyDeregister(pdsv->uRegister);
pdsv->uRegister = 0;
}
if (pdsv->psd)
{
pdsv->psd->lpVtbl->Release(pdsv->psd);
pdsv->psd = NULL;
}
if (pdsv->pdtgtBack)
{
pdsv->pdtgtBack->lpVtbl->Release(pdsv->pdtgtBack);
pdsv->pdtgtBack = NULL;
}
if (pdsv->pSaveHeader)
{
LocalFree((HLOCAL)pdsv->pSaveHeader);
pdsv->pSaveHeader = NULL;
pdsv->uSaveHeaderLen = 0;
}
Assert(pdsv->pdtobjHdrop == NULL); // this should not still be around
if (pdsv->hwndListview)
{
SHRevokeDragDrop(pdsv->hwndListview);
}
break;
case WM_NCDESTROY:
pdsv->hwndView = NULL;
//
// Now we can release it.
//
pdsv->sv.lpVtbl->Release(&pdsv->sv);
SetWindowLong(hWnd, 0, 0);
//
// get rid of extra junk in the icon cache
//
_IconCacheFlush(FALSE);
#ifdef NO_LONGER_NEEDED
//
// call the PIFMGR code in SHELL(16).DLL and let it get rid of PIFMGR.DLL
// if no one is using it.
//
// BUGBUG: This is not necessary now that PifMgr has been moved over into
// shell32.dll
PifMgr_CloseProperties(0, 0);
#endif
break;
case WM_ENABLE:
pdsv->fDisabled = !wParam;
break;
case WM_ERASEBKGND:
if (ListView_GetBkColor(pdsv->hwndListview) == CLR_NONE)
return SendMessage(pdsv->hwndMain, iMessage, wParam, lParam);
// We want to reduce flash
return 1;
case WM_PAINT:
if (pdsv->fEnumFailed) {
DV_PaintErrMsg(hWnd, pdsv);
} else {
goto DoDefWndProc;
}
break;
case WM_LBUTTONUP:
if (pdsv->fEnumFailed) {
PostMessage(hWnd, WM_KEYDOWN, (WPARAM)VK_F5, 0);
} else {
goto DoDefWndProc;
}
break;
case WM_SETFOCUS:
if (!pdsv->hwndView) // Ignore if we are destroying hwndView.
break;
if (pdsv->pfnCallback)
pdsv->pfnCallback(pdsv->psvOuter, pdsv->pshf, pdsv->hwndMain,
DVM_SETFOCUS, 0, pdsv->lSelChangeInfo);
if (pdsv->hwndListview)
SetFocus(pdsv->hwndListview);
break;
// this keeps our window from comming to the front on button down
// instead, we activate the window on the up click
case WM_MOUSEACTIVATE:
if (LOWORD(lParam) == HTCLIENT)
return MA_NOACTIVATE;
goto DoDefWndProc;
case WM_ACTIVATE:
// force update on inactive to not ruin save bits
if (wParam == WA_INACTIVE)
UpdateWindow(pdsv->hwndListview);
break;
case WM_SIZE:
return DefView_WndSize(hWnd, pdsv);
case WM_NOTIFY:
pdsv->sv.lpVtbl->AddRef(&pdsv->sv); // just in case
DefView_StartNotify(pdsv, (NMHDR *)lParam);
l = DefView_OnNotify(pdsv, (NMHDR *)lParam);
DefView_StopNotify(pdsv, (NMHDR *)lParam);
pdsv->sv.lpVtbl->Release(&pdsv->sv); // release
return l;
case WM_CONTEXTMENU:
DebugMsg(DM_TRACE, TEXT("GOT WM_CONTEXTMENU %d %d !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"), wParam, lParam);
if (!pdsv->fDisabled) {
if (lParam != (DWORD) -1) {
pdsv->bMouseMenu = TRUE;
pdsv->ptDragAnchor.x = LOWORD(lParam);
pdsv->ptDragAnchor.y = HIWORD(lParam);
LVUtil_ScreenToLV(pdsv->hwndListview, &pdsv->ptDragAnchor);
}
pdsv->bContextMenuMode = TRUE;
DefView_ContextMenu(pdsv, lParam);
pdsv->bContextMenuMode = TRUE;
if (lParam != (DWORD)-1) {
pdsv->bMouseMenu = FALSE;
}
}
break;
case WM_COMMAND:
return DefView_Command(pdsv, NULL, wParam, lParam);
case WM_DRAGSELECT:
case WM_DRAGMOVE:
case WM_QUERYDROPOBJECT:
case WM_DROPOBJECT:
case WM_DROPFILES:
return DV_OldDragMsgs(pdsv, iMessage, wParam, (const DROPSTRUCT *)lParam);
case WM_DSV_FSNOTIFY:
{
LPSHChangeNotificationLock pshcnl;
LPCITEMIDLIST *ppidl;
LONG lEvent;
TIMESTART(pdsv->FSNotify);
pshcnl = SHChangeNotification_Lock((HANDLE)wParam, (DWORD)lParam, &ppidl, &lEvent);
if (pshcnl)
{
if (pdsv->fDisabled ||
(pdsv->pfnCallback &&
pdsv->pfnCallback(pdsv->psvOuter, pdsv->pshf, pdsv->hwndMain, DVM_FSNOTIFY,
(WPARAM)ppidl, (LPARAM)lParam) == (S_FALSE)))
lParam = 0L;
else
lParam = DSV_OnFSNotify(pdsv, lEvent, ppidl);
SHChangeNotification_Unlock(pshcnl);
}
TIMESTOP(pdsv->FSNotify);
}
return lParam;
case WM_DSV_DESTROYSTATIC:
DefView_FillDone(pdsv, (HDPA)lParam, pdsv->pSaveHeader, pdsv->uSaveHeaderLen, (BOOL)wParam, FALSE);
break;
// The WM_DSV_CREATEODLISTVIEW is used to tell us to destroy any existing listview we
// might have, then to create a new ownerdata one
case WM_DSV_CREATEODLISTVIEW:
{
// The king is dead...
if (pdsv->hwndListview)
{
SHRevokeDragDrop(pdsv->hwndListview);
DestroyWindow(pdsv->hwndListview);
pdsv->hwndListview = NULL;
}
// ...long live the king
pdsv->fIsOwnerData = TRUE;
if (-1 == DefView_CreateListView(pdsv, hWnd))
{
return -1;
}
else
{
return DefView_WndSize(hWnd, pdsv);
}
}
case GET_WM_CTLCOLOR_MSG(CTLCOLOR_STATIC):
SetBkColor(GET_WM_CTLCOLOR_HDC(wParam, lParam, iMessage),
GetSysColor(COLOR_WINDOW));
return (LRESULT)GetSysColorBrush(COLOR_WINDOW);
case WM_DRAWCLIPBOARD:
if (pdsv->hwndNextViewer != NULL)
SendMessage(pdsv->hwndNextViewer, iMessage, wParam, lParam);
if (pdsv->bClipViewer)
return DSV_OnClipboardChange(pdsv);
break;
case WM_CHANGECBCHAIN:
if ((HWND)wParam == pdsv->hwndNextViewer)
{
pdsv->hwndNextViewer = (HWND)lParam;
return TRUE;
}
if (pdsv->hwndNextViewer != NULL)
return SendMessage(pdsv->hwndNextViewer, iMessage, wParam, lParam);
break;
case WM_WININICHANGE:
DefView_OnWinIniChange(pdsv, wParam, (LPCTSTR)lParam);
SendMessage(pdsv->hwndListview, iMessage, wParam, lParam);
break;
case WM_SHELLNOTIFY:
#define SHELLNOTIFY_SETDESKWALLPAPER 0x0004
if (wParam == SHELLNOTIFY_SETDESKWALLPAPER) {
if (DV_ISDESKTOP(pdsv)) {
pdsv->fHasDeskWallPaper = (lParam != 0);
DSV_SetFolderColors(pdsv);
InvalidateRect(pdsv->hwndListview, NULL, TRUE);
}
}
break;
case WM_INITMENU:
DefView_OnInitMenu(pdsv);
break;
case WM_INITMENUPOPUP:
if (DefView_OnInitMenuPopup(pdsv, (HMENU)wParam, LOWORD(lParam), HIWORD(lParam)))
goto RelayToCM;
break;
case WM_EXITMENULOOP:
FORWARD_WM_COMMAND(hWnd, SFVIDM_MISC_MENUTERM1, 0, 0, PostMessage);
break;
case WM_TIMER:
if (pdsv->hwndStatic) {
Animate_Open(pdsv->hwndStatic, TEXT("#150"));
}
KillTimer(pdsv->hwndView, DV_IDTIMER);
break;
case WM_SETCURSOR:
if (pdsv->hwndStatic) {
//DebugMsg(DM_TRACE,"########### SET WAIT CURSOR WM_SETCURSOR %d", pfc->iWaitCount);
SetCursor(LoadCursor(NULL, IDC_WAIT));
return TRUE;
} else
goto DoDefWndProc;
case WM_DRAWITEM:
#define lpdis ((LPDRAWITEMSTRUCT)lParam)
if (lpdis->CtlType != ODT_MENU)
return 0L;
if (InRange(lpdis->itemID, SFVIDM_CLIENT_FIRST, SFVIDM_CLIENT_LAST) && pdsv->pfnCallback)
{
pdsv->pfnCallback(pdsv->psvOuter, pdsv->pshf, pdsv->hwndMain,
DVM_DRAWITEM, SFVIDM_CLIENT_FIRST, lParam);
return 1L;
}
else
goto RelayToCM;
#undef lpdis
case WM_MEASUREITEM:
#define lpmis ((LPMEASUREITEMSTRUCT)lParam)
if (lpmis->CtlType != ODT_MENU)
return 0L;
if (InRange(lpmis->itemID, SFVIDM_CLIENT_FIRST, SFVIDM_CLIENT_LAST) && pdsv->pfnCallback)
{
pdsv->pfnCallback(pdsv->psvOuter, pdsv->pshf, pdsv->hwndMain,
DVM_MEASUREITEM, SFVIDM_CLIENT_FIRST, lParam);
return 1L;
}
RelayToCM:
if (pdsv->pcmSel)
{
IContextMenu2 *pcm2;
if (SUCCEEDED(pdsv->pcmSel->lpVtbl->QueryInterface(pdsv->pcmSel, &IID_IContextMenu2, &pcm2)))
{
pcm2->lpVtbl->HandleMenuMsg(pcm2, iMessage, wParam, lParam);
pcm2->lpVtbl->Release(pcm2);
}
}
return 0L;
// there are two possible ways to put help texts in the
// status bar, (1) processing WM_MENUSELECT or (2) handling MenuHelp
// messages. (1) is compatible with OLE, but (2) is required anyway
// for tooltips.
case WM_MENUSELECT:
DefView_OnMenuSelect(pdsv, GET_WM_MENUSELECT_CMD(wParam, lParam), GET_WM_MENUSELECT_FLAGS(wParam, lParam), GET_WM_MENUSELECT_HMENU(wParam, lParam));
break;
case WM_SYSCOLORCHANGE:
DSV_SetFolderColors(pdsv);
SendMessage(pdsv->hwndListview, iMessage, wParam, lParam);
break;
// BUGBUG: some of these are dead
case SVM_SELECTITEM:
CDefView_SelectItem(&pdsv->sv, (LPCITEMIDLIST)lParam, wParam);
break;
case SVM_MOVESELECTEDITEMS:
DefView_MoveSelectedItems(pdsv, LOWORD(lParam), HIWORD(lParam));
break;
case SVM_GETANCHORPOINT:
if (wParam)
return DefView_GetDragPoint(pdsv, (POINT*)lParam);
else
return DefView_GetDropPoint(pdsv, (POINT*)lParam);
case SVM_GETITEMPOSITION:
return DefView_GetItemPosition(pdsv, (LPCITEMIDLIST)wParam, (POINT*)lParam);
case SVM_SELECTANDPOSITIONITEM:
{
UINT i;
SFM_SAP * psap = (SFM_SAP*)lParam;
for (i = 0; i < wParam; psap++, i++)
DefView_SelectAndPositionItem(&pdsv->sv, psap->pidl, psap->uSelectFlags, psap->fMove ? &psap->pt : NULL);
break;
}
default:
DoDefWndProc:
return DefWindowProc(hWnd, iMessage, wParam, lParam) ;
}
return 0;
}
BOOL DefView_RegisterWindow(void)
{
WNDCLASS wc;
if (!GetClassInfo(HINST_THISDLL, c_szDefViewClass, &wc))
{
// don't want vredraw and hredraw because that causes horrible
// flicker expecially with full drag
wc.style = CS_PARENTDC;
wc.lpfnWndProc = DefView_WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = SIZEOF(LPDEFVIEW);
wc.hInstance = HINST_THISDLL;
wc.hIcon = NULL;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wc.lpszMenuName = NULL;
wc.lpszClassName = c_szDefViewClass;
return RegisterClass(&wc);
}
return TRUE;
}
STDMETHODIMP CDefView_QueryInterface(LPSHELLVIEW psv, REFIID riid, LPVOID * ppvObj)
{
LPDEFVIEW this = IToClass(CDefView, sv, psv);
// If we just want the same interface or an unknown one, return
// this guy
//
if (IsEqualIID(riid, &IID_IShellView) || IsEqualIID(riid, &IID_IUnknown))
{
(LPSHELLVIEW)*ppvObj = &(this->sv);
this->cRef++;
return S_OK;
}
if (IsEqualIID(riid, &IID_IDropTarget))
{
(LPDROPTARGET)*ppvObj = &this->dvdt.dt;
this->cRef++;
return S_OK;
}
*ppvObj = NULL;
return E_NOINTERFACE;
}
STDMETHODIMP_(ULONG) CDefView_AddRef(LPSHELLVIEW psv)
{
LPDEFVIEW this = IToClass(CDefView, sv, psv);
this->cRef++;
return(this->cRef);
}
STDMETHODIMP_(ULONG) CDefView_Release(LPSHELLVIEW psv)
{
LPDEFVIEW this = IToClass(CDefView, sv, psv);
Assert(this->cRef);
this->cRef--;
if (this->cRef>0)
{
return(this->cRef);
}
//
// Just in case, there is a left over.
//
CDVDropTarget_LeaveAndReleaseData(this);
//
// We need to give it a chance to clean up.
//
if (this->pfnCallback) {
this->pfnCallback(this->psvOuter, this->pshf, this->hwndMain,
DVM_RELEASE, 0, (LPARAM)this->lSelChangeInfo);
}
this->sv.lpVtbl->DestroyViewWindow(&this->sv);
//
// We should release psb after pshf (for docfindx)
//
this->pshf->lpVtbl->Release(this->pshf);
if (this->psi) {
this->psi->lpVtbl->Release(this->psi);
}
if (this->pcdb) {
this->pcdb->lpVtbl->Release(this->pcdb);
}
if (this->psb) {
this->psb->lpVtbl->Release(this->psb);
}
if (this->psd)
{
this->psd->lpVtbl->Release(this->psd);
}
if (this->pdr)
{
this->pdr->lpVtbl->Release(this->pdr);
}
if (this->pcmSel)
{
this->pcmSel->lpVtbl->Release(this->pcmSel);
}
//
// Cleanup dvdt
//
CDVDropTarget_ReleaseDataObject(&this->dvdt);
CDVDropTarget_ReleaseCurrentDropTarget(&this->dvdt);
if (this->pidlSelect)
{
ILFree(this->pidlSelect);
}
ENTERCRITICAL;
--gp_dvp.cRef;
if (!gp_dvp.cRef)
{
if (gp_dvp.hThreadIdle)
{
// This thread should just be in a WaitForObject, so it should
// be safe to blow it away
TerminateThread(gp_dvp.hThreadIdle, (DWORD)-1);
CloseHandle(gp_dvp.hThreadIdle);
gp_dvp.hThreadIdle = NULL;
gp_dvp.idThreadIdle = 0;
}
}
LEAVECRITICAL;
//
// We MUST free this object at VERY end.
//
LocalFree((HLOCAL)this);
return(0);
}
HMENU _GetMenuFromID(HMENU hmMain, UINT uID)
{
MENUITEMINFO miiSubMenu;
miiSubMenu.cbSize = SIZEOF(MENUITEMINFO);
miiSubMenu.fMask = MIIM_SUBMENU;
miiSubMenu.cch = 0; // just in case
if (!GetMenuItemInfo(hmMain, uID, FALSE, &miiSubMenu))
return NULL;
return miiSubMenu.hSubMenu;
}
DWORD CALLBACK DefView_LoadIcons(LPDEFVIEW this, BOOL bAnother)
{
LV_ITEM item;
int i, iCount;
DWORD idThisThread = GetCurrentThreadId();
if (DV_ISOWNERDATA(this))
{
ChangeRefForIdle(this, FALSE);
return 0;
}
if (bAnother)
{
// I assume this is an invalid thread ID
idThisThread = INVALID_THREAD_ID;
}
item.iSubItem = 0;
item.mask = LVIF_IMAGE; // This should be the only slow part
for (i = 0, iCount = ListView_GetItemCount(this->hwndListview);
this->idThreadIdle==idThisThread && i < iCount ; i++)
{
item.iItem = i;
ListView_GetItem(this->hwndListview, &item);
}
ChangeRefForIdle(this, FALSE);
TIMESTOP(this->Fill);
TIMEOUT(this->Fill);
TIMEOUT(this->EnumNext);
TIMEOUT(this->AddObject);
TIMEOUT(this->GetIcon);
TIMEOUT(this->GetName);
TIMEOUT(this->RestoreState);
TIMEOUT(this->FSNotify);
TIMEOUT(this->WMNotify);
TIMEOUT(this->LVChanging);
TIMEOUT(this->LVChanged);
TIMEOUT(this->LVDelete);
TIMEOUT(this->LVGetDispInfo);
DebugMsg(DM_TRACE, TEXT("DefView_FillObjects(%s) done! **********************"), DV_Name(this));
return 0;
}
BOOL DefView_IdleDoStuff(LPDEFVIEW pdsv, UINT message, LPARAM lParam)
{
// The wParam for every message is the LPDEFVIEW
WPARAM wParam = (WPARAM)pdsv;
BOOL bRet = FALSE;
ENTERCRITICAL;
if (!pdsv->hThreadIdle)
{
pdsv->hThreadIdle = gp_dvp.hThreadIdle;
pdsv->idThreadIdle = gp_dvp.idThreadIdle;
gp_dvp.hThreadIdle = NULL;
gp_dvp.idThreadIdle = 0;
}
if (pdsv->hThreadIdle)
{
ChangeRefForIdle(pdsv, TRUE);
if (PostThreadMessage(pdsv->idThreadIdle, message, wParam, lParam))
{
bRet = TRUE;
}
}
else
{
MSG *pMsg = (MSG *)LocalAlloc(LPTR, SIZEOF(MSG));
if (pMsg)
{
pMsg->message = message;
pMsg->wParam = wParam;
pMsg->lParam = lParam;
ChangeRefForIdle(pdsv, TRUE);
pdsv->hThreadIdle = CreateThread(NULL, 0, DefView_IdleThreadProc,
pMsg, 0, &pdsv->idThreadIdle);
if (pdsv->hThreadIdle)
{
bRet = TRUE;
}
else
{
LocalFree(pMsg);
ChangeRefForIdle(pdsv, FALSE);
}
}
}
LEAVECRITICAL;
return(bRet);
}
#pragma data_seg(".text", "CODE")
const TBBUTTON c_tbDefView[] = {
{ VIEW_NETCONNECT | IN_VIEW_BMP, SFVIDM_TOOL_CONNECT, TBSTATE_ENABLED, TBSTYLE_BUTTON, {0,0}, 0, -1 },
{ VIEW_NETDISCONNECT | IN_VIEW_BMP, SFVIDM_TOOL_DISCONNECT, TBSTATE_ENABLED, TBSTYLE_BUTTON, {0,0}, 0, -1 },
{ 0, 0, TBSTATE_ENABLED, TBSTYLE_SEP, {0,0}, 0, -1 },
#define MAX_NETBUTTON 3
{ STD_CUT | IN_STD_BMP, SFVIDM_EDIT_CUT, TBSTATE_ENABLED, TBSTYLE_BUTTON, {0,0}, 0, -1},
{ STD_COPY | IN_STD_BMP, SFVIDM_EDIT_COPY, TBSTATE_ENABLED, TBSTYLE_BUTTON, {0,0}, 0, -1},
{ STD_PASTE | IN_STD_BMP, SFVIDM_EDIT_PASTE, TBSTATE_ENABLED, TBSTYLE_BUTTON, {0,0}, 0, -1},
{ 0, 0, TBSTATE_ENABLED, TBSTYLE_SEP, {0,0}, 0, -1 },
{ STD_UNDO | IN_STD_BMP, SFVIDM_EDIT_UNDO, TBSTATE_ENABLED, TBSTYLE_BUTTON, {0,0}, 0, -1},
{ 0, 0, TBSTATE_ENABLED, TBSTYLE_SEP, {0,0}, 0, -1 },
{ STD_DELETE | IN_STD_BMP, SFVIDM_FILE_DELETE, TBSTATE_ENABLED, TBSTYLE_BUTTON, {0,0}, 0, -1},
{ STD_PROPERTIES | IN_STD_BMP, SFVIDM_FILE_PROPERTIES, 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, SFVIDM_VIEW_ICON, TBSTATE_ENABLED, TBSTYLE_BUTTON, {0,0}, 0L, -1 },
{ VIEW_SMALLICONS | IN_VIEW_BMP, SFVIDM_VIEW_SMALLICON, TBSTATE_ENABLED, TBSTYLE_BUTTON, {0,0}, 0L, -1 },
{ VIEW_LIST | IN_VIEW_BMP, SFVIDM_VIEW_LIST, TBSTATE_ENABLED, TBSTYLE_BUTTON, {0,0}, 0L, -1 },
{ VIEW_DETAILS | IN_VIEW_BMP, SFVIDM_VIEW_DETAILS, TBSTATE_ENABLED, TBSTYLE_BUTTON, {0,0}, 0L, -1 },
};
#pragma data_seg()
LRESULT DefView_TBNotify(LPDEFVIEW pdsv, NMHDR *pnm)
{
#define ptbn ((LPTBNOTIFY)pnm)
switch (pnm->code) {
case TBN_BEGINDRAG:
DefView_OnMenuSelect(pdsv, ptbn->iItem, 0, 0);
break;
#ifdef CUST_TOOLBAR
case TBN_GETBUTTONINFO:
if (ptbn->iItem < ARRAYSIZE(c_tbDefView))
{
if (ptbn->pszText)
DV_GetMenuHelpText(pdsv, c_tbDefView[ptbn->iItem].idCommand, ptbn->pszText, ptbn->cchText);
return TRUE;
}
break;
case TBN_QUERYINSERT:
return TRUE;
case TBN_QUERYDELETE:
return TRUE;
case TBN_RESET:
DefView_MergeToolBar(pdsv, FALSE);
return TRUE;
#endif // CUST_TOOLBAR
}
return 0;
}
void DefView_MergeToolBar(LPDEFVIEW this, BOOL bCanRestore)
{
TBADDBITMAP ab;
BOOL fNetButtons;
DWORD rgfAttr;
int iStartDefButtons = 0;
int iEndDefButtons = ARRAYSIZE(c_tbDefView);
ab.hInst = HINST_COMMCTRL; // hinstCommctrl
ab.nID = IDB_STD_SMALL_COLOR; // std bitmaps
this->psb->lpVtbl->SendControlMsg(this->psb, FCW_TOOLBAR, TB_ADDBITMAP, 8, (LPARAM)&ab, &this->iStdBMOffset);
ab.nID = IDB_VIEW_SMALL_COLOR; // std view bitmaps
this->psb->lpVtbl->SendControlMsg(this->psb, FCW_TOOLBAR, TB_ADDBITMAP, 8, (LPARAM)&ab, &this->iViewBMOffset);
rgfAttr = SFGAO_FILESYSTEM | SFGAO_FILESYSANCESTOR;
fNetButtons = (GetSystemMetrics(SM_NETWORK) & RNC_NETWORKS) &&
SUCCEEDED(this->pshf->lpVtbl->GetAttributesOf(this->pshf, 0, NULL, &rgfAttr)) &&
(rgfAttr & (SFGAO_FILESYSTEM | SFGAO_FILESYSANCESTOR));
if (!fNetButtons)
iStartDefButtons = MAX_NETBUTTON;
#ifdef CUST_TOOLBAR
LRESULT lres;
bCanRestore = FALSE; // disable saving for now
if (bCanRestore &&
(this->psb->lpVtbl->SendControlMsg(this->psb, FCW_TOOLBAR, TB_SAVERESTORE, FALSE, (LPARAM)&tbs, &lres), lres))
{
this->psb->lpVtbl->SetToolbarItems(this->psb, NULL, 0, FCT_CONFIGABLE);
DebugMsg(DM_TRACE, TEXT("loaded saved toolbar"));
}
else
#endif // CUST_TOOLBAR
{
LPTBBUTTON pbtn;
TBINFO tbinfo;
int i;
tbinfo.uFlags = TBIF_APPEND;
tbinfo.cbuttons = 0;
// Does the client want to prepend/append a toolbar?
this->pfnCallback(this->psvOuter, this->pshf, this->hwndMain, DVM_GETBUTTONINFO, 0, (LPARAM)&tbinfo);
pbtn = LocalAlloc(LPTR, SIZEOF(c_tbDefView) + (tbinfo.cbuttons * SIZEOF(TBBUTTON)));
if (pbtn)
{
int iStart = 0;
int cButtons = tbinfo.cbuttons + iEndDefButtons - iStartDefButtons;
// Merge the toolbars
switch (tbinfo.uFlags)
{
case TBIF_PREPEND:
this->pfnCallback(this->psvOuter, this->pshf, this->hwndMain, DVM_GETBUTTONS,
MAKEWPARAM(SFVIDM_CLIENT_FIRST, tbinfo.cbuttons),
(LPARAM)pbtn);
iStart = tbinfo.cbuttons;
break;
case TBIF_APPEND:
this->pfnCallback(this->psvOuter, this->pshf, this->hwndMain, DVM_GETBUTTONS,
MAKEWPARAM(SFVIDM_CLIENT_FIRST, tbinfo.cbuttons),
(LPARAM)&pbtn[iEndDefButtons - iStartDefButtons]);
iStart = 0;
break;
case TBIF_REPLACE:
this->pfnCallback(this->psvOuter, this->pshf, this->hwndMain, DVM_GETBUTTONS,
MAKEWPARAM(SFVIDM_CLIENT_FIRST, tbinfo.cbuttons),
(LPARAM)pbtn);
cButtons = tbinfo.cbuttons;
iEndDefButtons = iStartDefButtons;
break;
default:
Assert(0); // should never be here
break;
}
for (i = 0;iStartDefButtons < iEndDefButtons; iStartDefButtons++, i++)
{
pbtn[i + iStart] = c_tbDefView[iStartDefButtons];
if (!(c_tbDefView[iStartDefButtons].fsStyle & TBSTYLE_SEP))
{
if (c_tbDefView[iStartDefButtons].iBitmap & IN_VIEW_BMP)
pbtn[i + iStart].iBitmap = (c_tbDefView[iStartDefButtons].iBitmap & ~IN_VIEW_BMP) + this->iViewBMOffset;
else
pbtn[i + iStart].iBitmap = c_tbDefView[iStartDefButtons].iBitmap + this->iStdBMOffset;
}
}
this->psb->lpVtbl->SetToolbarItems(this->psb, pbtn, cButtons, FCT_MERGE);
LocalFree((HLOCAL)pbtn);
}
}
_DSV_CheckToolbar(this);
}
STDMETHODIMP CDefView_GetWindow(LPSHELLVIEW psv, HWND *phwnd)
{
LPDEFVIEW this = IToClass(CDefView, sv, psv);
*phwnd = this->hwndView;
return S_OK;
}
STDMETHODIMP CDefView_ContextSensitiveHelp(LPSHELLVIEW psv, BOOL fEnterMode)
{
return E_NOTIMPL;
}
STDMETHODIMP CDefView_EnableModeless(LPSHELLVIEW psv, BOOL fEnable)
{
// We have no modeless window to be enabled/disabled
return S_OK;
}
STDMETHODIMP CDefView_Refresh(LPSHELLVIEW psv)
{
LPDEFVIEW this = IToClass(CDefView, sv, psv);
ULONG rgf = SFGAO_VALIDATE;
HRESULT hres;
LV_ITEM item;
//
// HACK: We always call IsShared with fUpdateCache=FALSE for performance.
// However, we need to update the cache when the user explicitly tell
// us to "Refresh". This is not the ideal place to put this code, but
// we have no other choice.
//
// BUGBUG: this update cache thing is old and bogus I think... talk to the net guys
//
TCHAR szPathAny[MAX_PATH];
// finish any pending edits
SendMessage(this->hwndListview, LVM_EDITLABEL, (WPARAM)-1, 0L);
GetWindowsDirectory(szPathAny, ARRAYSIZE(szPathAny));
IsShared(szPathAny, TRUE);
// First we have to save all the icon positions, so they will be restored
// properly during the FillObjects
CDefView_SaveViewState(psv);
// Then we have to notify IShellFolder that we're refreshing
this->pshf->lpVtbl->GetAttributesOf(this->pshf, 0, NULL, &rgf);
//
// if a item is selected, make sure it gets nuked from the icon
// cache, this is a last resort type thing, select a item and
// hit F5 to fix all your problems.
//
item.iItem = ListView_GetNextItem(this->hwndListview, -1, LVNI_SELECTED);
if (item.iItem != -1) {
item.iSubItem = 0;
item.mask = LVIF_PARAM;
item.lParam = 0;
ListView_GetItem(this->hwndListview, &item);
Icon_FSEvent(SHCNE_UPDATEITEM, (LPCITEMIDLIST)item.lParam, NULL);
}
this->dvState.lParamSort = 0;
this->dvState.iDirection = 1;
this->dvState.iLastColumnClick = -1;
hres = DefView_FillObjectsShowHide(this, TRUE, NULL, 0, TRUE);
return hres;
}
STDMETHODIMP CDefView_CreateViewWindow(IShellView *psv, IShellView *lpPrevView,
LPCFOLDERSETTINGS lpfs, IShellBrowser *psb, RECT *prc, HWND *phWnd)
{
LPDEFVIEW this = IToClass(CDefView, sv, psv);
HRESULT hres;
UINT uLen;
BOOL fAllowDrop = TRUE;
FOLDERVIEWMODE fvmSave;
*phWnd = NULL;
if (this->hwndView || !DefView_RegisterWindow() || !psb)
return (E_UNEXPECTED);
Assert(this->pshf);
this->pshf->lpVtbl->QueryInterface(this->pshf, &IID_IShellIcon, &this->psi);
// We need to make sure to store this before doing the GetWindowRect
//
this->psb = psb;
psb->lpVtbl->AddRef(psb);
psb->lpVtbl->QueryInterface(psb, &IID_ICommDlgBrowser, &this->pcdb);
fvmSave = this->fs.ViewMode;
this->fs = *lpfs;
if (fvmSave && (lpfs->fFlags & FWF_BESTFITWINDOW))
this->fs.ViewMode = fvmSave;
// This should never fail
psb->lpVtbl->GetWindow(psb, &this->hwndMain);
Assert(IsWindow(this->hwndMain));
// We need to restore the column widths before showing the window
uLen = DefView_GetSaveHeader(this, &this->pSaveHeader);
this->uSaveHeaderLen = uLen;
// if there was a previous view that we know about, preserve the sort order
if (lpPrevView && (lpPrevView->lpVtbl == this->sv.lpVtbl)) {
LPDEFVIEW pdsvPrev;
pdsvPrev = IToClass(CDefView, sv,lpPrevView);
if (pdsvPrev) {
this->dvState = pdsvPrev->dvState;
if (this->pSaveHeader)
this->pSaveHeader->dvState = pdsvPrev->dvState;
}
}
if (!CreateWindow(c_szDefViewClass, szNULL, WS_CHILD | WS_CLIPCHILDREN | WS_VISIBLE | WS_CLIPSIBLINGS | WS_TABSTOP,
prc->left, prc->top, prc->right-prc->left, prc->bottom-prc->top,
this->hwndMain, NULL, HINST_THISDLL, this))
{
hres = (E_OUTOFMEMORY);
goto Error1;
}
// since DefView_FillObjects can take a while
// we force a paint now before any items are added so
// we don't see the grey background of the cabinet window
// for a long time.
UpdateWindow(this->hwndView);
// NB - Nasty side effect - this needs to be done
// before calling _BestFit (in DV_FillObjects) so that the
// parent can handle size changes effectively.
*phWnd = this->hwndView;
// Fill the listview with subobjects
hres = DefView_FillObjectsShowHide(this, TRUE, this->pSaveHeader, uLen, TRUE);
if (FAILED(hres))
{
//
// The fill objects failed for some reason, we should
// return an error.
//
this->sv.lpVtbl->DestroyViewWindow(&this->sv);
Assert(this->hwndView == NULL);
*phWnd = NULL;
//
// Note that we don't need to clean this->psb or this->pcdb
// because this object will be deleted anyway.
//
return hres;
}
// this needs to be done after the enumeration
if (this->pidlMonitor || this->lFSEvents) // check both so that views can register for everything
{
int fSources = (this->lFSEvents & SHCNE_DISKEVENTS) ? SHCNRF_ShellLevel | SHCNRF_InterruptLevel : SHCNRF_ShellLevel;
UINT uFSEvents = this->lFSEvents | SHCNE_UPDATEIMAGE;
SHChangeNotifyEntry fsne;
if (!this->pfnCallback ||
(this->pfnCallback && FAILED(this->pfnCallback(this->psvOuter,
this->pshf, this->hwndMain, DVM_QUERYFSNOTIFY,
0, (LPARAM)&fsne))) )
{
// Reset entry
fsne.pidl = this->pidlMonitor;
fsne.fRecursive = FALSE;
}
this->uRegister = SHChangeNotifyRegister(this->hwndView, SHCNRF_NewDelivery | fSources,
uFSEvents, WM_DSV_FSNOTIFY, 1, &fsne);
}
// We do the toolbar before the menu bar to avoid flash
DefView_MergeToolBar(this, TRUE);
// BUGBUG: commdlg should support drag drop too!
Assert(this->pdtgtBack == NULL);
// this may fail
this->pshf->lpVtbl->CreateViewObject(this->pshf, this->hwndMain, &IID_IDropTarget, &this->pdtgtBack);
SHRegisterDragDrop(this->hwndListview, &this->dvdt.dt);
hres = S_OK;
ViewWindow_BestFit(this, FALSE);
// Tell the defview client that this windows has been initialized
if (this->pfnCallback)
{
HRESULT hresT;
DVSELCHANGEINFO dvsci;
dvsci.plParam = &this->lSelChangeInfo;
this->pfnCallback(this->psvOuter, this->pshf, this->hwndMain,
DVM_WINDOWCREATED, (WPARAM)this->hwndView, (LPARAM)&dvsci);
hresT = this->pfnCallback(this->psvOuter, this->pshf, this->hwndMain,
DVM_QUERYCOPYHOOK, 0, 0);
if (SUCCEEDED(hresT)) {
DefView_AddCopyHook(this);
}
}
return hres;
Error1:
if (this->pSaveHeader)
{
LocalFree((HLOCAL)this->pSaveHeader);
this->pSaveHeader = NULL;
this->uSaveHeaderLen = 0;
}
return hres;
}
STDMETHODIMP CDefView_DestroyViewWindow(IShellView *psv)
{
LPDEFVIEW this = IToClass(CDefView, sv, psv);
if (this->pdr)
{
// The whole point of delayed release is that loading and
// unloading is slow, so put up an hourglass
// BUGBUG: We should set the priority to very low and kill
// the window first, but that won;t do a whole lot of good
// if 16-bit code is going to be called, which is the case
// for the Control Panel.
DECLAREWAITCURSOR;
SetWaitCursor();
this->pdr->lpVtbl->Release(this->pdr);
this->pdr = NULL;
ResetWaitCursor();
}
#ifdef CUST_TOOLBAR
if (!this->fToolbarSaved && this->psb)
{
TBSAVEPARAMS tbs = {HKEY_CURRENT_USER, c_szShellState, c_szDefViewToobar};
this->psb->lpVtbl->SendControlMsg(this->psb, FCW_TOOLBAR, TB_SAVERESTORE, TRUE, (LPARAM)&tbs, NULL);
this->fToolbarSaved = TRUE;
}
#endif // CUST_TOOLBAR
//
// Just in case...
//
CDefView_OnDeactivate(this);
if (this->hwndView)
{
HWND hwndTemp = this->hwndView;
//
// This is a bit lazy implementation, but minimum code.
//
DefView_RemoveCopyHook(this);
// Put NULL in hwndView indicating that we are destroying.
this->hwndView = NULL;
// Tell the defview client that this window will be destroyed
if (this->pfnCallback)
{
this->pfnCallback(this->psvOuter, this->pshf, this->hwndMain,
DVM_WINDOWDESTROY, (WPARAM)hwndTemp, 0);
}
DestroyWindow(hwndTemp);
}
return S_OK;
}
void DV_MergeViewMenu(HMENU hmenu, HMENU hmenuMerge)
{
HMENU hmenuView = _GetMenuFromID(hmenu, FCIDM_MENU_VIEW);
if (hmenuView)
{
int index;
//
// Find the "options" separator in the view menu.
//
for (index = GetMenuItemCount(hmenuView)-1; index>=0; index--)
{
MENUITEMINFO mii;
mii.cbSize = SIZEOF(MENUITEMINFO);
mii.fMask = MIIM_ID;
mii.cch = 0; // just in case
if (GetMenuItemInfo(hmenuView, (UINT)index, TRUE, &mii)
&& (mii.wID == FCIDM_MENU_VIEW_SEP_OPTIONS))
{
// merge it right above the separator.
break;
}
}
//
// Here, index is the index of he "optoins" separator if it has;
// otherwise, it is -1.
//
// Add the separator above (in addition to existing one if any).
InsertMenu(hmenuView, index, MF_BYPOSITION | MF_SEPARATOR, 0, NULL);
// Then merge our menu between two separators (or right below if only one).
if (index != -1) {
index++;
}
Shell_MergeMenus(hmenuView, hmenuMerge, (UINT)index, 0, (UINT)-1, MM_SUBMENUSHAVEIDS);
}
}
// set up the menus based on our activation state
//
BOOL CDefView_OnActivate(LPDEFVIEW this, UINT uState)
{
if (this->uState != uState)
{
HMENU hMenu;
CDefView_OnDeactivate(this);
Assert(this->hmenuCur == NULL);
hMenu = CreateMenu();
if (hMenu)
{
HMENU hMergeMenu;
OLEMENUGROUPWIDTHS mwidth = { { 0, 0, 0, 0, 0, 0 } };
this->hmenuCur = hMenu;
this->psb->lpVtbl->InsertMenusSB(this->psb, hMenu, &mwidth);
if (uState == SVUIA_ACTIVATE_FOCUS)
{
hMergeMenu = LoadMenu(HINST_THISDLL, MAKEINTRESOURCE(POPUP_SFV_MAINMERGE));
if (hMergeMenu)
{
// NOTE: hard coded references to offsets in this menu
Shell_MergeMenus(_GetMenuFromID(hMenu, FCIDM_MENU_FILE),
GetSubMenu(hMergeMenu, 0), (UINT)0, 0, (UINT)-1,
MM_ADDSEPARATOR | MM_SUBMENUSHAVEIDS);
Shell_MergeMenus(_GetMenuFromID(hMenu, FCIDM_MENU_EDIT),
GetSubMenu(hMergeMenu, 1), (UINT)-1, 0, (UINT)-1,
MM_ADDSEPARATOR | MM_SUBMENUSHAVEIDS);
DV_MergeViewMenu(hMenu, GetSubMenu(hMergeMenu, 2));
Shell_MergeMenus(_GetMenuFromID(hMenu, FCIDM_MENU_HELP),
GetSubMenu(hMergeMenu, 3), (UINT)0, 0, (UINT)-1,
MM_ADDSEPARATOR | MM_SUBMENUSHAVEIDS);
DestroyMenu(hMergeMenu);
}
}
else
{
hMergeMenu = LoadMenu(HINST_THISDLL, MAKEINTRESOURCE(POPUP_SFV_MAINMERGENF));
if (hMergeMenu)
{
// NOTE: hard coded references to offsets in this menu
// top half of edit menu
Shell_MergeMenus(_GetMenuFromID(hMenu, FCIDM_MENU_EDIT),
GetSubMenu(hMergeMenu, 0), (UINT)0, 0, (UINT)-1,
MM_ADDSEPARATOR | MM_SUBMENUSHAVEIDS);
// bottom half of edit menu
Shell_MergeMenus(_GetMenuFromID(hMenu, FCIDM_MENU_EDIT),
GetSubMenu(hMergeMenu, 1), (UINT)-1, 0, (UINT)-1,
MM_SUBMENUSHAVEIDS);
// view menu
DV_MergeViewMenu(hMenu, GetSubMenu(hMergeMenu, 2));
Shell_MergeMenus(_GetMenuFromID(hMenu, FCIDM_MENU_HELP),
GetSubMenu(hMergeMenu, 3), (UINT)0, 0, (UINT)-1,
MM_ADDSEPARATOR | MM_SUBMENUSHAVEIDS);
DestroyMenu(hMergeMenu);
}
}
if (this->pfnCallback)
{
// Allow the client to merge its own menus
UINT indexClient = GetMenuItemCount(hMenu)-1;
QCMINFO info = { hMenu, indexClient, SFVIDM_CLIENT_FIRST, SFVIDM_CLIENT_LAST };
this->pfnCallback(this->psvOuter, this->pshf, this->hwndMain, DVM_MERGEMENU, 0, (LPARAM)&info);
}
this->psb->lpVtbl->SetMenuSB(this->psb, hMenu, NULL, this->hwndView);
}
this->uState = uState;
}
return TRUE;
}
BOOL CDefView_OnDeactivate(LPDEFVIEW this)
{
if (this->uState != SVUIA_DEACTIVATE)
{
Assert(this->hmenuCur);
if (this->pfnCallback)
{
this->pfnCallback(this->psvOuter, this->pshf, this->hwndMain, DVM_UNMERGEMENU, 0, (LPARAM)this->hmenuCur);
}
this->psb->lpVtbl->SetMenuSB(this->psb, NULL, NULL, NULL);
this->psb->lpVtbl->RemoveMenusSB(this->psb, this->hmenuCur);
DestroyMenu(this->hmenuCur);
this->hmenuCur = NULL;
this->uState = SVUIA_DEACTIVATE;
}
return TRUE;
}
//
// This function activates the view window. Note that activating it
// will not change the focus (while setting the focus will activate it).
//
STDMETHODIMP CDefView_UIActivate(LPSHELLVIEW psv, UINT uState)
{
LPDEFVIEW this = IToClass(CDefView, sv, psv);
if (uState)
{
CDefView_OnActivate(this, uState);
Assert(this->hmenuCur);
}
else
{
CDefView_OnDeactivate(this);
Assert(this->hmenuCur==NULL);
}
return S_OK;
}
STDMETHODIMP CDefView_GetCurrentInfo(IShellView * psv, LPFOLDERSETTINGS lpfs)
{
LPDEFVIEW this = IToClass(CDefView, sv, psv);
*lpfs = this->fs;
return S_OK;
}
STDMETHODIMP CDefView_TranslateAccelerator(LPSHELLVIEW psv, LPMSG pmsg)
{
LPDEFVIEW this = IToClass(CDefView, sv, psv);
if (this->fInLabelEdit)
{
// DebugMsg(DM_TRACE, "in label edit mode, disabled exploeres accelerator");
// process this msg so the exploer does not get to translate
TranslateMessage(pmsg);
DispatchMessage(pmsg);
return S_OK; // we handled it
}
return TranslateAccelerator(this->hwndView, this->hAccel, pmsg) ? S_OK : ResultFromShort(S_FALSE);
}
void DefView_InitViewMenu(LPDEFVIEW this, HMENU hmInit)
{
int iCurViewMenuItem = _DSV_GetMenuIDFromViewMode(this->fs.ViewMode);
UINT uEnable;
CheckMenuRadioItem(hmInit, SFVIDM_VIEW_ICON, SFVIDM_VIEW_DETAILS,
iCurViewMenuItem, MF_BYCOMMAND | MF_CHECKED);
uEnable = ((iCurViewMenuItem == SFVIDM_VIEW_LIST) || (iCurViewMenuItem == SFVIDM_VIEW_DETAILS)) ?
(MF_GRAYED | MF_BYCOMMAND) : (MF_ENABLED | MF_BYCOMMAND);
EnableMenuItem(hmInit, SFVIDM_ARRANGE_GRID, uEnable);
EnableMenuItem(hmInit, SFVIDM_ARRANGE_AUTO, uEnable);
CheckMenuItem(hmInit, SFVIDM_ARRANGE_AUTO,
((uEnable == (MF_ENABLED | MF_BYCOMMAND)) && (this->fs.fFlags & FWF_AUTOARRANGE)) ? MF_CHECKED : MF_UNCHECKED);
}
void DV_GetMenuHelpText(LPDEFVIEW pdsv, UINT id, LPTSTR pszText, UINT cchText)
{
VDATEINPUTBUF(pszText, TCHAR, cchText);
*pszText = 0;
if (InRange(id, SFVIDM_CONTEXT_FIRST, SFVIDM_CONTEXT_LAST) && pdsv->pcmSel)
{
*pszText = '\0';
// First try to get the stardard help string
pdsv->pcmSel->lpVtbl->GetCommandString(pdsv->pcmSel,
id - SFVIDM_CONTEXT_FIRST, GCS_HELPTEXT, NULL,
(LPSTR)pszText, cchText);
if (*pszText == TEXT('\0'))
{
// If we didn't get anything, try to grab the other version of the
// (ansi for a unicode build, or unicode for an ansi build)
#ifdef UNICODE
CHAR szText[MAX_PATH];
pdsv->pcmSel->lpVtbl->GetCommandString(pdsv->pcmSel,
id - SFVIDM_CONTEXT_FIRST, GCS_HELPTEXTA, NULL,
szText, ARRAYSIZE(szText));
MultiByteToWideChar(CP_ACP, 0,
szText, -1,
pszText, cchText);
#else
WCHAR szText[MAX_PATH];
pdsv->pcmSel->lpVtbl->GetCommandString(pdsv->pcmSel,
id - SFVIDM_CONTEXT_FIRST, GCS_HELPTEXTW, NULL,
(LPSTR)szText, ARRAYSIZE(szText));
WideCharToMultiByte(CP_ACP, 0,
szText, -1,
pszText, cchText,
NULL, NULL);
#endif
}
#ifdef SN_TRACE
if (GetKeyState(VK_CONTROL)<0)
{
UINT cch;
lstrcat(pszText, TEXT(" (debug only) Canonical Verb = "));
cch=lstrlen(pszText);
pdsv->pcmSel->lpVtbl->GetCommandString(pdsv->pcmSel,
id - SFVIDM_CONTEXT_FIRST, GCS_VERB, NULL,
(LPSTR)(pszText+cch), cchText-cch);
if (*(pszText+cch)==TEXT('\0'))
{
#ifdef UNICODE
CHAR szVerb[MAX_PATH];
pdsv->pcmSel->lpVtbl->GetCommandString(pdsv->pcmSel,
id - SFVIDM_CONTEXT_FIRST, GCS_VERBA, NULL,
szVerb, ARRAYSIZE(szVerb));
MultiByteToWideChar(CP_ACP, 0,
szVerb, -1,
pszText+cch, cchText-cch);
#else
WCHAR szVerb[MAX_PATH];
pdsv->pcmSel->lpVtbl->GetCommandString(pdsv->pcmSel,
id - SFVIDM_CONTEXT_FIRST, GCS_VERBW, NULL,
(LPSTR)szVerb, ARRAYSIZE(szVerb));
WideCharToMultiByte(CP_ACP, 0,
szVerb, -1,
pszText+cch, cchText-cch);
#endif
}
}
#endif
}
else if (InRange(id, SFVIDM_CLIENT_FIRST, SFVIDM_CLIENT_LAST) && pdsv->pfnCallback)
{
pdsv->pfnCallback(pdsv->psvOuter, pdsv->pshf, NULL, DVM_GETHELPTEXT,
MAKEWPARAM(id - SFVIDM_CLIENT_FIRST, cchText), (LPARAM)pszText);
}
else if (InRange(id, SFVIDM_FIRST, SFVIDM_LAST))
{
if ((id == SFVIDM_EDIT_UNDO) && IsUndoAvailable()) {
GetUndoText(PeekUndoAtom(), pszText, UNDO_STATUSTEXT);
} else {
LoadString(HINST_THISDLL, id + SFVIDS_MH_FIRST, pszText, cchText);
}
}
}
void DV_GetToolTipText(LPDEFVIEW pdsv, UINT id, LPTSTR pszText, UINT cchText)
{
VDATEINPUTBUF(pszText, TCHAR, cchText);
*pszText = 0;
if (InRange(id, SFVIDM_CLIENT_FIRST, SFVIDM_CLIENT_LAST) && pdsv->pfnCallback)
{
pdsv->pfnCallback(pdsv->psvOuter, pdsv->pshf, NULL, DVM_GETTOOLTIPTEXT,
MAKEWPARAM(id - SFVIDM_CLIENT_FIRST, cchText), (LPARAM)pszText);
}
else
{
Assert(InRange(id, SFVIDM_FIRST, SFVIDM_LAST));
if (id == SFVIDM_EDIT_UNDO) {
if (IsUndoAvailable()) {
GetUndoText(PeekUndoAtom(), pszText, UNDO_MENUTEXT);
return;
}
}
LoadString(HINST_THISDLL, IDS_TT_SFVIDM_FIRST + id, pszText, cchText);
}
}
LRESULT DefView_OnMenuSelect(LPDEFVIEW pdsv, UINT id, UINT mf, HMENU hmenu)
{
TCHAR szHelpText[80 + 2*MAX_PATH]; // Lots of stack!
// If we dismissed the edit restore our status bar...
if (!hmenu && LOWORD(mf)==0xffff)
{
pdsv->psb->lpVtbl->SendControlMsg(pdsv->psb,
FCW_STATUS, SB_SIMPLE, 0, 0L, NULL);
return 0L;
}
if (mf & (MF_SYSMENU | MF_SEPARATOR))
return 0L;
szHelpText[0] = 0; // in case of failures below
if (mf & MF_POPUP)
{
MENUITEMINFO miiSubMenu;
miiSubMenu.cbSize = SIZEOF(MENUITEMINFO);
miiSubMenu.fMask = MIIM_ID;
miiSubMenu.cch = 0; // just in case
if (!GetMenuItemInfo(hmenu, id, TRUE, &miiSubMenu))
return 0;
// Change the parameters to simulate a "normal" menu item
id = miiSubMenu.wID;
mf &= ~MF_POPUP;
}
#ifdef WINDOWS_ME
szHelpText[0] = szHelpText[1] = TEXT('\t');
szHelpText[2] = TEXT('\0');
DV_GetMenuHelpText(pdsv, id, &szHelpText[2], ARRAYSIZE(szHelpText)-2);
pdsv->psb->lpVtbl->SendControlMsg(pdsv->psb,
FCW_STATUS, SB_SETTEXT, SBT_RTLREADING | SBT_NOBORDERS | 255, (LPARAM)szHelpText, NULL);
#else
DV_GetMenuHelpText(pdsv, id, szHelpText, ARRAYSIZE(szHelpText));
pdsv->psb->lpVtbl->SendControlMsg(pdsv->psb,
FCW_STATUS, SB_SETTEXT, SBT_NOBORDERS | 255, (LPARAM)szHelpText, NULL);
#endif
pdsv->psb->lpVtbl->SendControlMsg(pdsv->psb,
FCW_STATUS, SB_SIMPLE, 1, 0L, NULL);
return 0;
}
//
// This function dismisses the name edit mode if there is any.
//
// REVIEW: Moving the focus away from the edit window will
// dismiss the name edit mode. Should we introduce
// a LV_DISMISSEDIT instead?
//
void DefView_DismissEdit(LPDEFVIEW pdsv)
{
if (pdsv->uState == SVUIA_ACTIVATE_FOCUS)
{
HWND hwndFocus = GetFocus();
if (hwndFocus && pdsv->hwndListview && GetParent(hwndFocus)==pdsv->hwndListview)
{
SetFocus(pdsv->hwndListview);
}
}
}
void DefView_OnInitMenu(LPDEFVIEW pdsv)
{
//
// We need to dismiss the edit mode if it is any.
//
DefView_DismissEdit(pdsv);
}
LRESULT DefView_OnInitMenuPopup(LPDEFVIEW this, HMENU hmInit, int nIndex, BOOL fSystemMenu)
{
int i;
BOOL bDeleteItems;
LPCONTEXTMENU pcmSel;
MENUITEMINFO mii;
ULONG dwAttr;
mii.cbSize = SIZEOF(MENUITEMINFO);
mii.fMask = MIIM_SUBMENU|MIIM_ID;
mii.cch = 0; // just in case
if (!this->hmenuCur)
return(1);
if (!GetMenuItemInfo(this->hmenuCur, nIndex, TRUE, &mii) || mii.hSubMenu!=hmInit)
{
//
// BUGBUG: We need to get the submenu ID of hmInit ifself, but there is no
// such an API. JeffBog suggested me to file a bug, but he does not think
// he can put it in M6. For M6, we probably should live with this hack
// which get the ID of the first menuitem ID and treat it as the submenu ID.
//
#define WORKAROUND_21740
#ifdef WORKAROUND_21740
mii.fMask = MIIM_ID;
if (GetMenuItemInfo(hmInit, 0, TRUE, &mii)
&& InRange(mii.wID, SFVIDM_CLIENT_FIRST, SFVIDM_CLIENT_LAST)
&& this->pfnCallback)
{
goto NotifyClient;
}
#endif
return 1L;
}
// Is the ID within the client's range?
if (InRange(mii.wID, SFVIDM_CLIENT_FIRST, SFVIDM_CLIENT_LAST) && this->pfnCallback)
{
NotifyClient:
// Yes; pass it on to the callback
this->pfnCallback(this->psvOuter, this->pshf, this->hwndMain,
DVM_INITMENUPOPUP,
MAKEWPARAM(SFVIDM_CLIENT_FIRST, nIndex),
(LPARAM)hmInit);
return 0L;
}
switch (mii.wID)
{
case FCIDM_MENU_FILE:
//
// Don't touch the file menu unless we have the focus.
//
if (this->uState == SVUIA_ACTIVATE_FOCUS)
{
DWORD dwMenuState;
bDeleteItems = FALSE;
//
// First of all, remove all the menu items we've added.
//
for (i = GetMenuItemCount(hmInit) - 1; i >= 0; --i)
{
if (!bDeleteItems)
{
MENUITEMINFO mii;
mii.cbSize = SIZEOF(mii);
mii.fMask = MIIM_ID;
mii.cch = 0; // just in case
if (GetMenuItemInfo(hmInit, i, TRUE, &mii))
{
if (InRange(mii.wID, SFVIDM_CONTEXT_FIRST, SFVIDM_CONTEXT_LAST))
{
DebugMsg(DM_TRACE, TEXT("sh TR - DefView_OnInitMenuPopup: setting bDeleteItems at %d, %d"), i, mii.wID);
bDeleteItems = TRUE;
}
}
}
if (bDeleteItems)
{
DeleteMenu(hmInit, i, MF_BYPOSITION);
}
}
dwMenuState = GetMenuState(hmInit, 0, MF_BYPOSITION);
if ((dwMenuState & MF_SEPARATOR) &&
!(dwMenuState & MF_POPUP))
{
DeleteMenu(hmInit, 0, MF_BYPOSITION);
}
// Let the object add the separator.
//
// Now add item specific commands to the menu
// This is done by seeing if we already have a context menu
// object for our selection. If not we generate it now.
//
pcmSel = DefView_GetContextMenuFromSelection(this);
Assert(pcmSel == this->pcmSel);
if (pcmSel)
{
pcmSel->lpVtbl->QueryContextMenu(pcmSel,
hmInit, 0, SFVIDM_CONTEXT_FIRST,
SFVIDM_CONTEXT_LAST, CMF_DVFILE);
pcmSel->lpVtbl->Release(pcmSel);
}
//
// Enable/disable menuitems in the "File" pulldown.
//
dwAttr = DefView_GetAttributesFromSelection(this,
SFGAO_CANRENAME|SFGAO_CANDELETE|SFGAO_CANLINK|SFGAO_HASPROPSHEET);
Def_InitFileCommands(dwAttr, hmInit, SFVIDM_FIRST, FALSE);
goto NotifyClient;
}
break;
case FCIDM_MENU_EDIT:
//
// Enable/disable menuitems in the "Edit" pulldown.
//
dwAttr = DefView_GetAttributesFromSelection(this, SFGAO_CANCOPY|SFGAO_CANMOVE);
Def_InitEditCommands(dwAttr, hmInit, SFVIDM_FIRST, this->pdtgtBack, 0);
break;
case FCIDM_MENU_VIEW:
DefView_InitViewMenu(this, hmInit);
break;
default:
return 1L;
}
return 0L;
}
//
// Member: CDefView::AddPropertySheetPages
//
STDMETHODIMP CDefView_AddPropertySheetPages(IShellView *psv, DWORD dwReserved, LPFNADDPROPSHEETPAGE lpfn, LPARAM lparam)
{
return S_OK;
}
//
// Member: CDefView::SaveViewState
//
STDMETHODIMP CDefView_SaveViewState(IShellView *psv)
{
LPDEFVIEW this = IToClass(CDefView, sv, psv);
LPSTREAM pstm;
HRESULT hres;
LARGE_INTEGER dlibMove = {0, 0};
ULARGE_INTEGER libCurPosition;
ULARGE_INTEGER libPosPosition;
ULONG ulWrite;
DVSAVEHEADER dvSaveHeader;
BOOL bDefaultCols, bDefaultPos;
hres = this->psb->lpVtbl->GetViewStateStream(this->psb, STGM_WRITE, &pstm);
if (FAILED(hres))
{
return hres;
}
// Position the stream right after the header, and save the starting
// position at the same time
dlibMove.LowPart = SIZEOF(dvSaveHeader);
hres = pstm->lpVtbl->Seek(pstm, dlibMove, STREAM_SEEK_CUR, &libCurPosition);
if (FAILED(hres))
{
goto Error1;
}
// HACK: Avoid 2 calls to seek by just subtracting
libCurPosition.LowPart -= SIZEOF(dvSaveHeader);
bDefaultCols = DefView_SaveCols(this, pstm);
dvSaveHeader.cbSize = SIZEOF(dvSaveHeader);
// We save the view mode to determine if the scroll positions are
// still valid on restore
dvSaveHeader.ViewMode = this->fs.ViewMode;
dvSaveHeader.ptScroll.x = GetScrollPos(this->hwndListview, SB_HORZ);
dvSaveHeader.ptScroll.y = GetScrollPos(this->hwndListview, SB_VERT);
dvSaveHeader.dvState = this->dvState;
// Check whether everything is the default; clear out the stream if so
if (this->bClearItemPos && IsDefaultState(dvSaveHeader) && bDefaultCols)
{
goto SetSize;
}
if (bDefaultCols)
{
// No need to save column info
dvSaveHeader.cbColOffset = 0;
dvSaveHeader.cbPosOffset = SIZEOF(dvSaveHeader);
}
else
{
dlibMove.LowPart = 0;
pstm->lpVtbl->Seek(pstm, dlibMove, STREAM_SEEK_CUR, &libPosPosition);
dvSaveHeader.cbColOffset = SIZEOF(dvSaveHeader);
dvSaveHeader.cbPosOffset = (USHORT)(libPosPosition.LowPart - libCurPosition.LowPart);
}
dlibMove.LowPart = libCurPosition.LowPart;
pstm->lpVtbl->Seek(pstm, dlibMove, STREAM_SEEK_SET, NULL);
hres = pstm->lpVtbl->Write(pstm, &dvSaveHeader, SIZEOF(dvSaveHeader), &ulWrite);
if (FAILED(hres) || ulWrite != SIZEOF(dvSaveHeader))
{
goto SetSize;
}
// Make sure we save all information written so far
libCurPosition.LowPart += dvSaveHeader.cbPosOffset;
dlibMove.LowPart = libCurPosition.LowPart;
pstm->lpVtbl->Seek(pstm, dlibMove, STREAM_SEEK_SET, NULL);
bDefaultPos = DefView_SavePos(this, pstm);
if (!bDefaultPos)
{
dlibMove.LowPart = 0;
pstm->lpVtbl->Seek(pstm, dlibMove, STREAM_SEEK_CUR,
&libCurPosition);
}
SetSize:
pstm->lpVtbl->SetSize(pstm, libCurPosition);
Error1:;
pstm->lpVtbl->Release(pstm);
return hres;
}
HRESULT DefView_SelectAndPositionItem(IShellView *psv, LPCITEMIDLIST pidlItem, UINT uFlags, POINT *ppt)
{
LPDEFVIEW this = IToClass(CDefView, sv, psv);
int i;
// See if we should first deselect everything else
if (!pidlItem)
{
if (uFlags != SVSI_DESELECTOTHERS)
{
// I only know how to deselect everything
return(E_INVALIDARG);
}
ListView_SetItemState(this->hwndListview, -1, 0, LVIS_SELECTED);
return(S_OK);
}
i = DefView_FindItem(this, pidlItem, NULL, FALSE);
if (i != -1)
{
// set the position first so that the ensure visible scrolls to
// the new position
if (ppt)
{
ListView_SetItemPosition32(this->hwndListview, i, ppt->x, ppt->y);
this->bItemsMoved = TRUE;
this->bClearItemPos = FALSE;
}
// The SVSI_EDIT flag also contains SVSI_SELECT and as such
// a simple & wont work!
if ((uFlags & SVSI_EDIT) == SVSI_EDIT)
{
ListView_EditLabel(this->hwndListview, i);
}
else
{
UINT stateMask = LVIS_SELECTED;
UINT state = (uFlags & SVSI_SELECT) ? LVIS_SELECTED : 0;
if (uFlags & SVSI_FOCUSED)
{
state |= LVIS_FOCUSED;
stateMask |= LVIS_FOCUSED;
}
// See if we should first deselect everything else
if (uFlags & SVSI_DESELECTOTHERS)
ListView_SetItemState(this->hwndListview, -1, 0, LVIS_SELECTED);
ListView_SetItemState(this->hwndListview, i, state, stateMask);
if (uFlags & SVSI_ENSUREVISIBLE)
ListView_EnsureVisible(this->hwndListview, i, FALSE);
}
return S_OK;
}
return (E_FAIL);
}
//
// Member: CDefView::SelectItem
//
STDMETHODIMP CDefView_SelectItem(IShellView *psv, LPCITEMIDLIST pidlItem, UINT uFlags)
{
LPDEFVIEW this = IToClass(CDefView, sv, psv);
if (this->hwndStatic)
{
ENTERCRITICAL;
// We must be filling in the background, so this item may not be
// visible yet. Defer this action until later.
if (this->pidlSelect)
{
ILFree(this->pidlSelect);
}
this->pidlSelect = ILClone(pidlItem);
this->uFlagsSelect = uFlags;
LEAVECRITICAL;
return(S_OK);
}
return DefView_SelectAndPositionItem(psv, pidlItem, uFlags, NULL);
}
//
// To be called back from within CDefFolderMenu
//
// Returns:
// S_OK, if successfully processed.
// (S_FALSE), if default code should be used.
//
HRESULT CALLBACK DefView_DFMCallBackBG(LPSHELLFOLDER psf, HWND hwndOwner,
LPDATAOBJECT pdtobj, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
extern TCHAR const c_szNewFolder[];
#ifdef UNICODE
extern CHAR const c_szNewFolderAnsi[];
#endif
HRESULT hres = S_OK;
switch(uMsg) {
case DFM_VALIDATECMD:
switch (wParam)
{
case DFM_CMD_NEWFOLDER:
{
IContextMenu *pcm;
if (SUCCEEDED(psf->lpVtbl->CreateViewObject(psf, hwndOwner,
&IID_IContextMenu, &pcm)))
{
TCHAR szTemp[10];
// BUGBUG: Do I need to QueryContextMenu?
hres = pcm->lpVtbl->GetCommandString(pcm, (ULONG)c_szNewFolder,
GCS_VALIDATE, NULL,
(LPSTR)szTemp, ARRAYSIZE(szTemp));
#ifdef UNICODE
if(FAILED(hres))
{
hres = pcm->lpVtbl->GetCommandString(pcm, (ULONG)c_szNewFolder,
GCS_VALIDATEA, NULL,
(LPSTR)szTemp, ARRAYSIZE(szTemp));
}
#endif
pcm->lpVtbl->Release(pcm);
}
else
{
hres = S_FALSE;
}
}
break;
case DFM_CMD_VIEWLIST:
case DFM_CMD_VIEWDETAILS:
break;
default:
hres = S_FALSE;
}
break;
case DFM_INVOKECOMMAND:
switch(wParam)
{
case DFM_CMD_NEWFOLDER:
{
IContextMenu *pcm;
if (SUCCEEDED(psf->lpVtbl->CreateViewObject(psf, hwndOwner,
&IID_IContextMenu, &pcm)))
{
CMINVOKECOMMANDINFOEX ici;
ici.cbSize = SIZEOF(CMINVOKECOMMANDINFOEX);
ici.fMask = 0L;
ici.hwnd = hwndOwner;
ici.lpParameters = NULL;
ici.lpDirectory = NULL;
ici.nShow = SW_NORMAL;
#ifdef UNICODE
ici.lpVerb = c_szNewFolderAnsi;
ici.lpVerbW = c_szNewFolder;
#else
ici.lpVerb = c_szNewFolder;
#endif
// BUGBUG: Do I need to QueryContextMenu?
pcm->lpVtbl->InvokeCommand(pcm, (LPCMINVOKECOMMANDINFO)&ici);
pcm->lpVtbl->Release(pcm);
}
}
break;
case DFM_CMD_VIEWLIST:
{
LPDEFVIEW pdsv = DV_HwndMain2DefView(hwndOwner);
SendMessage(pdsv->hwndView, WM_COMMAND, SFVIDM_VIEW_LIST, 0);
break;
}
case DFM_CMD_VIEWDETAILS:
{
LPDEFVIEW pdsv = DV_HwndMain2DefView(hwndOwner);
SendMessage(pdsv->hwndView, WM_COMMAND, SFVIDM_VIEW_DETAILS, 0);
break;
}
default:
// This is one of view menu items, use the default code.
hres = (S_FALSE);
break;
}
break;
default:
hres = E_NOTIMPL;
break;
}
return hres;
}
//
// Member: CDefView::SelectItem
//
STDMETHODIMP CDefView_GetItemObject(IShellView * psv, UINT uItem, REFIID riid, LPVOID *ppv)
{
LPDEFVIEW this = IToClass(CDefView, sv, psv);
*ppv = NULL;
switch (uItem)
{
case SVGIO_BACKGROUND:
if (IsEqualIID(riid, &IID_IContextMenu))
{
return(CDefFolderMenu_Create(NULL,
this->hwndMain,
0, NULL,
this->pshf,
DefView_DFMCallBackBG,
NULL, NULL,
(IContextMenu **)ppv));
}
break;
case SVGIO_ALLVIEW:
if (this->hwndStatic)
{
DECLAREWAITCURSOR;
SetWaitCursor();
do
{
// If hwndStatic is around, we must be filling the
// view in a background thread, so we will peek for
// messages to it (so SendMessages will get through)
// and dispatch only hwndStatic messages so we get the
// animation effect.
// Note there is no timeout, so this could take
// a while on a slow link, but there really isn't
// much else I can do
MSG msg;
// Since hwndStatic can only be destroyed on a DESTROYSTATIC
// message, we should never get a RIP
if (PeekMessage(&msg, this->hwndView, WM_DSV_DESTROYSTATIC,
WM_DSV_DESTROYSTATIC, PM_REMOVE)
|| PeekMessage(&msg, this->hwndStatic, 0, 0,
PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
} while (this->hwndStatic);
ResetWaitCursor();
}
// Fall through
case SVGIO_SELECTION:
return(DefView_GetUIObjectFromItem(this, riid, ppv, uItem));
}
return(E_NOTIMPL);
}
//===========================================================================
// Constructor of CDefView class
//===========================================================================
#pragma data_seg(".text", "CODE")
IShellViewVtbl c_DefViewVtbl =
{
CDefView_QueryInterface,
CDefView_AddRef,
CDefView_Release,
CDefView_GetWindow,
CDefView_ContextSensitiveHelp,
CDefView_TranslateAccelerator,
CDefView_EnableModeless,
CDefView_UIActivate,
CDefView_Refresh,
CDefView_CreateViewWindow,
CDefView_DestroyViewWindow,
CDefView_GetCurrentInfo,
CDefView_AddPropertySheetPages,
CDefView_SaveViewState,
CDefView_SelectItem,
CDefView_GetItemObject,
};
//=============================================================================
// CDVDropTarget : member prototypes
//=============================================================================
STDMETHODIMP CDVDropTarget_QueryInterface(LPDROPTARGET pdt, REFIID riid, LPVOID * ppvObj);
STDMETHODIMP_(ULONG) CDVDropTarget_AddRef(LPDROPTARGET pdt);
STDMETHODIMP_(ULONG) CDVDropTarget_Release(LPDROPTARGET pdt);
STDMETHODIMP CDVDropTarget_DragEnter(LPDROPTARGET pdt, LPDATAOBJECT pdtobj, DWORD grfKeyState, POINTL pt, LPDWORD pdwEffect);
STDMETHODIMP CDVDropTarget_DragOver(LPDROPTARGET pdt, DWORD grfKeyState, POINTL pt, LPDWORD pdwEffect);
STDMETHODIMP CDVDropTarget_DragLeave(LPDROPTARGET pdt);
STDMETHODIMP CDVDropTarget_Drop(LPDROPTARGET pdt, LPDATAOBJECT pdtobj, DWORD grfKeyState, POINTL pt, LPDWORD pdwEffect);
//=============================================================================
// CDVDropTarget : VTable
//=============================================================================
IDropTargetVtbl c_CDVDropTarget = {
CDVDropTarget_QueryInterface,
CDVDropTarget_AddRef,
CDVDropTarget_Release,
CDVDropTarget_DragEnter,
CDVDropTarget_DragOver,
CDVDropTarget_DragLeave,
CDVDropTarget_Drop
};
#pragma data_seg()
HRESULT _CreateShellFolderView(LPSHELLFOLDER pshf, LPCITEMIDLIST pidl, LONG lEvents, LPSHELLVIEW * ppsv)
{
LPDEFVIEW pdsv = (void*)LocalAlloc(LPTR, SIZEOF(*pdsv));
if (!pdsv)
{
*ppsv = NULL;
return E_OUTOFMEMORY; // error
}
pdsv->sv.lpVtbl = &c_DefViewVtbl;
pdsv->dvdt.dt.lpVtbl = &c_CDVDropTarget;
pdsv->cRef = 1;
pshf->lpVtbl->AddRef(pshf);
pdsv->pshf = pshf;
pdsv->pidlMonitor = pidl;
pdsv->lFSEvents = lEvents;
pdsv->dvState.iDirection = 1;
pdsv->dvState.iLastColumnClick = -1;
pdsv->cRefForIdle = -1;
pdsv->dwAttrSel = (DWORD)-1;
//
// LPTR contains LMEM_ZEROINIT
// pdsv->psvOuter = NULL;
// pdsv->psi = NULL;
// pdsv->dvState.lParamSort = 0;
// pdsv->psd = NULL;
// pdsv->pcmSel = NULL;
// pdsv->pfnCallback = NULL;
//
*ppsv = &pdsv->sv;
++gp_dvp.cRef;
return S_OK;
}
HRESULT WINAPI SHCreateShellFolderViewEx(LPCSFV pcsfv, LPSHELLVIEW * ppsv)
{
HRESULT hres = (E_INVALIDARG);
Assert(pcsfv);
Assert(ppsv);
if (pcsfv)
{
Assert(SIZEOF(*pcsfv) == pcsfv->cbSize);
hres = _CreateShellFolderView(pcsfv->pshf, pcsfv->pidl, pcsfv->lEvents, ppsv);
if (SUCCEEDED(hres))
{
LPDEFVIEW this = IToClass(CDefView, sv, *ppsv);
this->psvOuter = pcsfv->psvOuter;
this->pfnCallback = pcsfv->pfnCallback;
this->fs.ViewMode = pcsfv->fvm;
}
}
return hres;
}
//===========================================================================
// Drag & Drop
//===========================================================================
void CDVDropTarget_LeaveAndReleaseData(LPDEFVIEW this)
{
this->dvdt.dt.lpVtbl->DragLeave(&this->dvdt.dt);
if (this->pdtobjHdrop)
{
DebugMsg(DM_TRACE, TEXT("sh TR - releasing 3.1 HDROP data object"));
this->pdtobjHdrop->lpVtbl->Release(this->pdtobjHdrop);
this->pdtobjHdrop = NULL;
}
}
void DV_DropFiles(LPDEFVIEW this, HDROP hdrop)
{
if (this->pdtobjHdrop)
{
DWORD dwEffect = DROPEFFECT_COPY;
POINT pt;
POINTL ptl;
// hdrop becomes owned by this data object, it will free it
HRESULT hres = DataObj_SetGlobal(this->pdtobjHdrop, CF_HDROP, hdrop);
// we created this data object so this should not fail
Assert(SUCCEEDED(hres));
DragQueryPoint(hdrop, &pt); // in client coords of window dropped on
ClientToScreen(this->hwndListview, &pt);
ptl.x = pt.x;
ptl.y = pt.y;
// MK_LBUTTON means treat as a default drop
this->dvdt.dt.lpVtbl->Drop(&this->dvdt.dt, this->pdtobjHdrop, MK_LBUTTON, ptl, &dwEffect);
CDVDropTarget_LeaveAndReleaseData(this);
}
else
{
DebugMsg(DM_ERROR, TEXT("sh ER - WM_DROPFILES with no pdtgt or no pdtobjHDrop"));
DragFinish(hdrop); // be sure to free this
}
}
//
// This function processes win 3.1 Drag & Drop messages
//
LRESULT DV_OldDragMsgs(LPDEFVIEW this, UINT uMsg, WPARAM wParam, const DROPSTRUCT * lpds)
{
DWORD dwEffect = DROPEFFECT_COPY;
//
// We don't need to do this hack if NT defined POINT as typedef POINTL.
//
union {
POINT ptScreen;
POINTL ptlScreen;
} drop;
Assert(SIZEOF(drop.ptScreen)==SIZEOF(drop.ptlScreen));
if (lpds) // Notes: lpds is NULL, if uMsg is WM_DROPFILES.
{
drop.ptScreen = lpds->ptDrop;
ClientToScreen(this->hwndMain, &drop.ptScreen);
}
switch (uMsg) {
case WM_DRAGSELECT:
// WM_DRAGSELECT is sent to a sink whenever an new object is dragged inside of it.
// wParam: TRUE if the sink is being entered, FALSE if it's being exited.
if (wParam)
{
DebugMsg(DM_TRACE, TEXT("3.1 drag enter"));
if (this->pdtobjHdrop)
{
// can happen if old target fails to generate drag leave properly
DebugMsg(DM_TRACE, TEXT("generating DragLeave on old drag enter"));
CDVDropTarget_LeaveAndReleaseData(this);
}
if (SUCCEEDED(CIDLData_CreateFromIDArray(NULL, 0, NULL, &this->pdtobjHdrop)))
{
// promise the CF_HDROP by setting a NULL handle
// indicating that this dataobject will have an hdrop at Drop() time
DataObj_SetGlobal(this->pdtobjHdrop, CF_HDROP, (HGLOBAL)NULL);
this->dvdt.dt.lpVtbl->DragEnter(&this->dvdt.dt, this->pdtobjHdrop, MK_LBUTTON, drop.ptlScreen, &dwEffect);
}
}
else
{
DebugMsg(DM_TRACE, TEXT("3.1 drag leave"));
CDVDropTarget_LeaveAndReleaseData(this);
}
break;
case WM_DRAGMOVE:
// WM_DRAGMOVE is sent to a sink as the object is being dragged within it.
// wParam: Unused
if (this->pdtobjHdrop)
{
this->dvdt.dt.lpVtbl->DragOver(&this->dvdt.dt, MK_LBUTTON, drop.ptlScreen, &dwEffect);
}
break;
case WM_QUERYDROPOBJECT:
switch (lpds->wFmt) {
case DOF_SHELLDATA:
case DOF_DIRECTORY:
case DOF_DOCUMENT:
case DOF_MULTIPLE:
case DOF_EXECUTABLE:
#if 0
// this works, but it flashes the cursor a lot. don't bother with this now...
if (this->pdtobjHdrop)
{
// REVIEW: we could construct MK_LEFTBUTTON
return SUCCEEDED(this->dvdt.dt.lpVtbl->DragOver(&this->dvdt.dt, MK_LBUTTON, drop.ptlScreen, &dwEffect)) && dwEffect;
}
#endif
// assume all targets can accept HDROP if we don't have the data object yet
return TRUE; // we will accept the drop
}
return FALSE; // don't accept
case WM_DROPOBJECT:
if (!this->pdtobjHdrop)
return FALSE;
// Check the format of dragged object.
switch (lpds->wFmt) {
case DOF_EXECUTABLE:
case DOF_DOCUMENT:
case DOF_DIRECTORY:
case DOF_MULTIPLE:
case DOF_PROGMAN:
case DOF_SHELLDATA:
// We need to unlock this window if this drag&drop is originated
// from the shell itself.
DAD_DragLeave();
// The source is Win 3.1 app (probably FileMan), request HDROP.
return DO_DROPFILE; // Send us a WM_DROPFILES with HDROP
}
break;
case WM_DROPFILES:
DV_DropFiles(this, (HDROP)wParam);
break;
}
return 0; // Unknown format. Don't drop any
}
//=============================================================================
// CDVDropTarget : member
//=============================================================================
STDMETHODIMP CDVDropTarget_QueryInterface(LPDROPTARGET pdt, REFIID riid, LPVOID * ppvObj)
{
CDVDropTarget * this = IToClass(CDVDropTarget, dt, pdt);
LPDEFVIEW pdv = IToClass(CDefView, dvdt, this);
Assert(pdv->sv.lpVtbl->QueryInterface == CDefView_QueryInterface);
return CDefView_QueryInterface(&pdv->sv, riid, ppvObj);
}
STDMETHODIMP_(ULONG) CDVDropTarget_AddRef(LPDROPTARGET pdt)
{
CDVDropTarget * this = IToClass(CDVDropTarget, dt, pdt);
LPDEFVIEW pdv = IToClass(CDefView, dvdt, this);
Assert(pdv->sv.lpVtbl->AddRef == CDefView_AddRef);
return CDefView_AddRef(&pdv->sv);
}
void CDVDropTarget_ReleaseDataObject(CDVDropTarget * this)
{
if (this->pdtobj)
{
//
// JUST-IN-CASE: Put NULL in this->pdtobj before we release it.
// We do this just in case because we don't know what OLE does
// from inside Release (it might call back one of our members).
//
LPDATAOBJECT pdtobj = this->pdtobj;
this->pdtobj = NULL;
pdtobj->lpVtbl->Release(pdtobj);
}
}
void CDVDropTarget_ReleaseCurrentDropTarget(CDVDropTarget * this)
{
LPDEFVIEW pdv = IToClass(CDefView, dvdt, this);
if (this->pdtgtCur)
{
this->pdtgtCur->lpVtbl->DragLeave(this->pdtgtCur);
this->pdtgtCur->lpVtbl->Release(this->pdtgtCur);
this->pdtgtCur = NULL;
}
pdv->itemCur = -2;
// WARNING: Never touch pdv->itemOver in this function.
}
STDMETHODIMP_(ULONG) CDVDropTarget_Release(LPDROPTARGET pdt)
{
CDVDropTarget * this = IToClass(CDVDropTarget, dt, pdt);
LPDEFVIEW pdv = IToClass(CDefView, dvdt, this);
UINT cRef;
Assert(pdv->sv.lpVtbl->Release == CDefView_Release);
cRef = CDefView_Release(&pdv->sv);
#ifdef DEBUG
if (cRef == 0)
{
AssertMsg(this->pdtobj == NULL, TEXT("didn't get matching DragLeave"));
}
#endif
return cRef;
}
STDMETHODIMP CDVDropTarget_DragEnter(LPDROPTARGET pdt, LPDATAOBJECT pdtobj, DWORD grfKeyState, POINTL ptl, LPDWORD pdwEffect)
{
CDVDropTarget * this = IToClass(CDVDropTarget, dt, pdt);
LPDEFVIEW pdv = IToClass(CDefView, dvdt, this);
POINT pt;
HWND hwndLock;
g_fDraggingOverSource = FALSE;
DebugMsg(DM_TRACE, TEXT("sh - TR CDVDropTarget::DragEnter with *pdwEffect=%x"), *pdwEffect);
Assert(this->pdtobj == NULL);
pdtobj->lpVtbl->AddRef(pdtobj);
this->pdtobj = pdtobj;
this->grfKeyState = grfKeyState;
Assert(this->pdtgtCur == NULL);
// don't really need to do this, but this sets the target state
CDVDropTarget_ReleaseCurrentDropTarget(this);
this->itemOver = -2;
//
// In case of Desktop, we should not lock the enter screen.
//
hwndLock = DV_ISDESKTOP(pdv) ? pdv->hwndListview : pdv->hwndMain;
GetWindowRect(hwndLock, &this->rcLockWindow);
pt.x = ptl.x-this->rcLockWindow.left;
pt.y = ptl.y-this->rcLockWindow.top;
DAD_DragEnterEx(hwndLock, pt);
DAD_InitScrollData(&this->asd);
this->ptLast.x = this->ptLast.y = 0x7fffffff; // put bogus value to force redraw
return S_OK;
}
//
// Returns: if the cursor is over a listview item, its index; otherwise, -1.
//
int DV_GetDropTarget(LPDEFVIEW pdsv, const POINT *ppt)
{
LV_HITTESTINFO info;
info.pt = *ppt;
return ListView_HitTest(pdsv->hwndListview, &info);
}
#define DVAE_BEFORE 0x01
#define DVAE_AFTER 0x02
// this MUST set pdwEffect to 0 or DROPEFFECT_MOVE if it's a default drag drop
// in the same window
void DV_AlterEffect(LPDEFVIEW pdv, DWORD grfKeyState, DWORD *pdwEffect, UINT uFlags)
{
//
// Check if we are dragginv over the drag source itself.
//
g_fDraggingOverSource = FALSE;
if (DV_IsDropOnSource(pdv, NULL))
{
// Yes.
//
// Check if we are in icon mode or not.
//
if ((pdv->fs.ViewMode != FVM_DETAILS) && (pdv->fs.ViewMode != FVM_LIST))
{
// Yes.
//
// If this is default drag&drop, enable move.
//
if (uFlags & DVAE_AFTER) {
if (grfKeyState & MK_LBUTTON)
{
if (!(grfKeyState & MK_CONTROL) &&
!(grfKeyState & MK_SHIFT))
{
*pdwEffect = DROPEFFECT_MOVE;
g_fDraggingOverSource = TRUE;
}
} else if (grfKeyState & MK_RBUTTON) {
*pdwEffect |= DROPEFFECT_MOVE;
}
}
}
else
{
if (uFlags & DVAE_BEFORE) {
// No. Disable move.
*pdwEffect &= ~DROPEFFECT_MOVE;
//
// If this is default drag&drop, disable all.
//
if (grfKeyState & MK_LBUTTON)
{
*pdwEffect = 0;
}
}
}
}
}
STDMETHODIMP CDVDropTarget_DragOver(LPDROPTARGET pdt, DWORD grfKeyState, POINTL ptl, LPDWORD pdwEffect)
{
HRESULT hres = S_OK;
CDVDropTarget * this = IToClass(CDVDropTarget, dt, pdt);
LPDEFVIEW pdv = IToClass(CDefView, dvdt, this);
int itemNew;
POINT pt = { ptl.x, ptl.y }; // in screen coords
RECT rc;
BOOL fInRect;
DWORD dwEffectScroll = 0;
DWORD dwEffectOut = 0;
BOOL fSameImage = FALSE;
if (this->pdtobj == NULL)
{
Assert(0); // DragEnter should be called before.
return (E_FAIL);
}
GetWindowRect(pdv->hwndListview, &rc);
fInRect = PtInRect(&rc, pt);
ScreenToClient(pdv->hwndListview, &pt);
// assume coords of our window match listview
if (DAD_AutoScroll(pdv->hwndListview, &this->asd, &pt))
dwEffectScroll = DROPEFFECT_SCROLL;
// hilight an item, or unhilight all items (DropTarget returns -1)
if (fInRect)
itemNew = DV_GetDropTarget(pdv, &pt);
else
itemNew = -1;
//
// If we are dragging over on a different item, get its IDropTarget
// interface or adjust itemNew to -1.
//
if (this->itemOver != itemNew)
{
LPDROPTARGET pdtgtNew = NULL;
this->itemOver = itemNew;
// Avoid dropping onto drag source objects.
if ((itemNew != -1) && pdv->bDragSource)
{
UINT uState = ListView_GetItemState(pdv->hwndListview, itemNew, LVIS_SELECTED);
if (uState & LVIS_SELECTED)
itemNew = -1;
}
// If we are dragging over an item, try to get its IDropTarget.
if (itemNew != -1)
{
// We are dragging over an item.
LPCITEMIDLIST apidl[1] = { DSV_GetPIDL(pdv, itemNew) };
UINT dwAttr = SFGAO_DROPTARGET;
hres = pdv->pshf->lpVtbl->GetAttributesOf(pdv->pshf, 1, apidl, &dwAttr);
if (SUCCEEDED(hres) && (dwAttr & SFGAO_DROPTARGET))
{
hres = pdv->pshf->lpVtbl->GetUIObjectOf(pdv->pshf,
pdv->hwndMain, 1, apidl, &IID_IDropTarget, NULL, &pdtgtNew);
Assert(itemNew != pdv->itemCur); // MUST not be the same
}
if (pdtgtNew==NULL) {
// If the item is not a drop target, don't hightlight it
// treat it as transparent.
itemNew = -1;
}
}
//
// If the new target is different from the current one, switch it.
//
if (pdv->itemCur != itemNew)
{
// Release previous drop target, if any.
CDVDropTarget_ReleaseCurrentDropTarget(this);
Assert(this->pdtgtCur==NULL);
// Update pdv->itemCur which indicates the current target.
// (Note that it might be different from this->itemOver).
pdv->itemCur = itemNew;
// If we are dragging over the background or over non-sink item,
// get the drop target for the folder.
if (itemNew == -1)
{
// We are dragging over the background, this can be NULL
Assert(pdtgtNew == NULL);
this->pdtgtCur = pdv->pdtgtBack;
if (this->pdtgtCur)
this->pdtgtCur->lpVtbl->AddRef(this->pdtgtCur);
}
else
{
Assert(pdtgtNew);
this->pdtgtCur = pdtgtNew;
}
// Hilight the sink item (itemNew != -1) or unhilight all (-1).
LVUtil_DragSelectItem(pdv->hwndListview, itemNew);
// Call IDropTarget::DragEnter of the target object.
if (this->pdtgtCur)
{
//
// Note that pdwEffect is in/out parameter.
//
dwEffectOut = *pdwEffect; // pdwEffect in
// Special case if we are dragging within a source window
DV_AlterEffect(pdv, grfKeyState, &dwEffectOut, DVAE_BEFORE);
hres = this->pdtgtCur->lpVtbl->DragEnter(this->pdtgtCur, this->pdtobj, grfKeyState, ptl, &dwEffectOut);
DV_AlterEffect(pdv, grfKeyState, &dwEffectOut, DVAE_AFTER);
}
else
{
Assert(dwEffectOut==0);
DV_AlterEffect(pdv, grfKeyState, &dwEffectOut, DVAE_BEFORE | DVAE_AFTER);
}
DebugMsg(DM_TRACE, TEXT("sh TR - CDV::DragOver dwEIn=%x, dwEOut=%x"), *pdwEffect, dwEffectOut);
}
else
{
Assert(pdtgtNew == NULL); // It must be NULL
goto NoChange;
}
}
else
{
NoChange:
//
// No change in the selection. We assume that *pdwEffect stays
// the same during the same drag-loop as long as the key state doesn't change.
//
if ((this->grfKeyState != grfKeyState) && this->pdtgtCur)
{
//
// Note that pdwEffect is in/out parameter.
//
dwEffectOut = *pdwEffect; // pdwEffect in
// Special case if we are dragging within a source window
DV_AlterEffect(pdv, grfKeyState, &dwEffectOut, DVAE_BEFORE);
hres = this->pdtgtCur->lpVtbl->DragOver(this->pdtgtCur, grfKeyState, ptl, &dwEffectOut);
DV_AlterEffect(pdv, grfKeyState, &dwEffectOut, DVAE_AFTER);
} else {
//
// Same item and same key state. Use the previous dwEffectOut.
//
dwEffectOut = this->dwEffectOut;
fSameImage = TRUE;
hres = S_OK;
}
}
this->grfKeyState = grfKeyState; // store these for the next Drop
this->dwEffectOut = dwEffectOut; // and DragOver
//
// HACK ALERT:
// OLE does not call IDropTarget::Drop if we return something
// valid. We force OLE call it by returning DROPEFFECT_SCROLL.
//
if (g_fDraggingOverSource) {
dwEffectScroll = DROPEFFECT_SCROLL;
}
*pdwEffect = dwEffectOut | dwEffectScroll; // pdwEffect out
// We need pass pt relative to the locked window (not the client).
pt.x = ptl.x-this->rcLockWindow.left;
pt.y = ptl.y-this->rcLockWindow.top;
if (!(fSameImage && pt.x==this->ptLast.x && pt.y==this->ptLast.y))
{
DAD_DragMove(pt);
this->ptLast.x = pt.x;
this->ptLast.y = pt.y;
}
// DebugMsg(DM_TRACE, "sh TR - CDVDropTarget_DragOver dwEffect:%4x hres:%d", *pdwEffect, hres);
return hres;
}
STDMETHODIMP CDVDropTarget_DragLeave(LPDROPTARGET pdt)
{
CDVDropTarget * this = IToClass(CDVDropTarget, dt, pdt);
LPDEFVIEW pdv = IToClass(CDefView, dvdt, this);
//
// Make it possible to call it more than necessary.
//
if (this->pdtobj)
{
DebugMsg(DM_TRACE, TEXT("sh - TR CDVDropTarget::DragLeave"));
CDVDropTarget_ReleaseCurrentDropTarget(this);
this->itemOver = -2;
CDVDropTarget_ReleaseDataObject(this);
DAD_DragLeave();
LVUtil_DragSelectItem(pdv->hwndListview, -1);
}
g_fDraggingOverSource = FALSE;
Assert(this->pdtgtCur == NULL);
Assert(this->pdtobj == NULL);
return S_OK;
}
STDMETHODIMP CDVDropTarget_Drop(LPDROPTARGET pdt, LPDATAOBJECT pdtobj,
DWORD grfKeyState, POINTL pt, LPDWORD pdwEffect)
{
CDVDropTarget * this = IToClass(CDVDropTarget, dt, pdt);
LPDEFVIEW pdv = IToClass(CDefView, dvdt, this);
HRESULT hres = S_OK;
BOOL bDropHandled = FALSE;
DebugMsg(DM_TRACE, TEXT("sh - TR CDVDropTarget::Drop (*pdwEffect=%x)"), *pdwEffect);
//
// According to AlexGo (OLE), this is by-design. We should make it sure
// that we use pdtobj instead of this->pdtobj.
//
#if 0
Assert(pdtobj == this->pdtobj);
#else
this->pdtobj = pdtobj;
#endif
pdv->ptDrop.x = pt.x;
pdv->ptDrop.y = pt.y;
ScreenToClient(pdv->hwndListview, &pdv->ptDrop);
//
// handle moves within the same window here.
// depend on DV_AlterEffect forcing in DROPEFFECT_MOVE and only
// dropeffect move when drag in same window
//
// Notes: We need to use this->grfKeyState instead of grfKeyState
// to see if the left mouse was used or not during dragging.
//
DV_AlterEffect(pdv, this->grfKeyState, pdwEffect, DVAE_BEFORE|DVAE_AFTER);
if ((this->grfKeyState & MK_LBUTTON) && (*pdwEffect == DROPEFFECT_MOVE) &&
(DV_IsDropOnSource(pdv, NULL)))
{
// This means we are left-dropping on ourselves, so we just move
// the icons.
pdv->bDropAnchor = TRUE;
DAD_DragLeave();
DV_MoveIcons(pdv, pdtobj);
SetForegroundWindow(pdv->hwndMain);
pdv->bDropAnchor = FALSE;
bDropHandled = TRUE;
*pdwEffect = 0; // the underlying objects didn't 'move' anywhere
}
//
// Note that we don't use the drop position intentionally,
// so that it matches to the last destination feedback.
//
if (this->pdtgtCur)
{
// use this local because if pdtgtCur::Drop does a UnlockWindow
// then hits an error and needs to put up a dialog,
// we could get re-entered and clobber the defview's pdtgtCur
LPDROPTARGET pdtgtCur = this->pdtgtCur;
this->pdtgtCur = NULL;
//
// HACK ALERT!!!!
//
// If we don't call LVUtil_DragEnd here, we'll be able to leave
// dragged icons visible when the menu is displayed. However, because
// we are calling IDropTarget::Drop() which may create some modeless
// dialog box or something, we can not ensure the locked state of
// the list view -- LockWindowUpdate() can lock only one window at
// a time. Therefore, we skip this call only if the pdtgtCur
// is a subclass of CIDLDropTarget, assuming its Drop calls
// CDefView_DragEnd (or CIDLDropTarget_DragDropMenu) appropriately.
//
if (!bDropHandled)
{
pdv->bDropAnchor = TRUE;
if (!ISIDLDROPTARGET(pdtgtCur))
{
//
// This will hide the dragged image.
//
DAD_DragLeave();
//
// We need to reset the drag image list so that the user
// can start another drag&drop while we are in this
// Drop() member function call.
//
DAD_SetDragImage(NULL, NULL);
}
// Special case if we are dragging within a source window
DV_AlterEffect(pdv, grfKeyState, pdwEffect, DVAE_BEFORE|DVAE_AFTER);
pdtgtCur->lpVtbl->Drop(pdtgtCur, pdtobj, grfKeyState, pt, pdwEffect);
DAD_DragLeave();
bDropHandled = TRUE;
pdv->bDropAnchor = FALSE;
}
else
{
// match the drag enter
pdtgtCur->lpVtbl->DragLeave(pdtgtCur);
}
pdtgtCur->lpVtbl->Release(pdtgtCur);
//
// Assert if (ISIDLDROPTARGET(this->pdtgtCur)
// && IDropTarget::Drop did not call CDefView_UnlockWindow())
//
Assert(!DAD_IsDragging());
}
else
{
//
// We come here if Drop is called without DragMove (with DragEnter).
//
DebugMsg(DM_TRACE, TEXT("CDV::Drop this->pdtgtCur == 0"));
*pdwEffect = 0;
}
//
// Clean up everything (OLE won't call DragLeave after Drop).
//
CDVDropTarget_DragLeave(pdt);
return hres;
}
//
// HACK ALERT!!! (see CDVDropTarget_Drop as well)
//
// All the subclasses of CIDLDropTarget MUST call this function from
// within its Drop() member function. Calling CIDLDropTarget_DragDropMenu()
// is sufficient because it calls CDefView_UnlockWindow.
//
// lego... make this a #define in defview.h
#ifndef CDefView_UnlockWindow
void CDefView_UnlockWindow()
{
DAD_DragLeave();
}
#endif
LPDEFVIEW WINAPI DV_HwndMain2DefView(HWND hwndMain)
{
LPDEFVIEW pdsv = NULL;
LPSHELLBROWSER psb = FileCabinet_GetIShellBrowser(hwndMain);
if (psb)
{
LPSHELLVIEW psv;
if (SUCCEEDED(psb->lpVtbl->QueryActiveShellView(psb, &psv)))
{
if (psv->lpVtbl->QueryInterface == CDefView_QueryInterface)
{
pdsv = IToClass(CDefView, sv, psv);
}
else if (psv->lpVtbl->QueryInterface)
{
// We might have the psvOuter, query for the pdsv
LPSHELLVIEW psvInner;
if (SUCCEEDED(psv->lpVtbl->QueryInterface(psv, &IID_IShellView, &psvInner)))
{
pdsv = IToClass(CDefView, sv, psvInner);
psv->lpVtbl->Release(psv);
}
}
psv->lpVtbl->Release(psv);
}
}
return pdsv;
}
HWND WINAPI DV_HwndMain2HwndView(HWND hwndMain)
{
LPDEFVIEW pdsv = DV_HwndMain2DefView(hwndMain);
return pdsv ? pdsv->hwndView : NULL;
}
BOOL DefView_IsBkDropTarget(LPDEFVIEW pdsv, LPDROPTARGET pdtg)
{
BOOL fRet = FALSE;
POINT pt;
if (pdsv->bContextMenuMode) {
if (ListView_GetSelectedCount(pdsv->hwndListview) == 0) {
fRet = TRUE;
}
}
if (!fRet && DefView_GetDropPoint(pdsv, &pt)) {
// The Drop point is returned in internal listview coordinates
// space, so we need to convert it back to client space
// before we call this function...
LVUtil_LVToClient(pdsv->hwndListview, &pt);
if (DV_GetDropTarget(pdsv, &pt) == -1) {
fRet = TRUE;
}
}
return fRet;
}
LRESULT WINAPI SHShellFolderView_Message(HWND hwndMain, UINT uMsg, LPARAM lParam)
{
LPDEFVIEW pdsv = DV_HwndMain2DefView(hwndMain);
if (!pdsv)
{
//
// DV_HwndMain2DefView fails if we are either closing the view or
// switchin the view window. In such a case, we simply need to fail.
//
DebugMsg(DM_TRACE, TEXT("sh TR - ShellFolderView_Message DV_HwndMain2DefView failes (bogus hwnd or closing/switching view)"));
return 0;
}
switch (uMsg)
{
case SFVM_REARRANGE:
{
DECLAREWAITCURSOR;
pdsv->dvState.lParamSort = lParam;
SetWaitCursor();
DV_ReArrange(pdsv);
ResetWaitCursor();
break;
}
case SFVM_ARRANGEGRID:
DefView_Command(pdsv, NULL, GET_WM_COMMAND_MPS(SFVIDM_ARRANGE_GRID, 0, 0));
break;
case SFVM_AUTOARRANGE:
DefView_Command(pdsv, NULL, GET_WM_COMMAND_MPS(SFVIDM_ARRANGE_AUTO, 0, 0));
break;
case SFVM_GETAUTOARRANGE:
return pdsv->fs.fFlags & FWF_AUTOARRANGE;
// BUGBUG: not used?
case SFVM_GETARRANGEPARAM:
return pdsv->dvState.lParamSort;
case SFVM_ADDOBJECT:
// BUGBUG: Shouldn't this make a copy of the PIDL? Maybe not for
// performance
return (LPARAM)DefView_AddObject(pdsv, (LPITEMIDLIST)lParam);
case SFVM_GETOBJECTCOUNT:
return (LPARAM)ListView_GetItemCount(pdsv->hwndListview);
case SFVM_GETOBJECT:
return (LPARAM)DSV_GetPIDL(pdsv, (int)lParam);
case SFVM_REMOVEOBJECT:
return (LPARAM)DefView_RemoveObject(pdsv, (LPITEMIDLIST)lParam, FALSE);
case SFVM_UPDATEOBJECT:
return (LPARAM)DefView_UpdateObject(pdsv, (LPITEMIDLIST *)lParam);
case SFVM_REFRESHOBJECT:
return (LPARAM)DefView_RefreshObject(pdsv, (LPITEMIDLIST *)lParam);
case SFVM_SETREDRAW:
SendMessage(pdsv->hwndListview, WM_SETREDRAW, (WPARAM)lParam, 0);
break;
case SFVM_GETSELECTEDOBJECTS:
{
HRESULT hres;
hres = DefView_GetItemObjects(pdsv, (LPCITEMIDLIST **)lParam,
SVGIO_SELECTION);
if (SUCCEEDED(hres))
{
return(ResultFromShort(hres));
}
else
{
return(-1);
}
}
case SFVM_GETSELECTEDCOUNT:
return (LPARAM)ListView_GetSelectedCount(pdsv->hwndListview);
case SFVM_ISDROPONSOURCE:
return(DV_IsDropOnSource(pdsv, (LPDROPTARGET)lParam));
case SFVM_MOVEICONS:
DV_MoveIcons(pdsv, (LPDATAOBJECT)lParam);
break;
case SFVM_GETDROPPOINT:
return DefView_GetDropPoint(pdsv, (LPPOINT)lParam);
case SFVM_GETDRAGPOINT:
return DefView_GetDragPoint(pdsv, (LPPOINT)lParam);
case SFVM_SETITEMPOS:
DefView_SetItemPos(pdsv, (LPSFV_SETITEMPOS)lParam);
break;
case SFVM_ISBKDROPTARGET:
return DefView_IsBkDropTarget(pdsv, (LPDROPTARGET)lParam);
case SFVM_SETCLIPBOARD:
return DSV_OnSetClipboard(pdsv, lParam);
case SFVM_SETPOINTS:
DefView_SetPoints(pdsv, (LPDATAOBJECT)lParam);
break;
case SFVM_GETITEMSPACING:
return DV_GetItemSpacing(pdsv, (LPITEMSPACING)lParam);
default:
// -1L is the default return value
return 0;
}
return 1;
}
//===========================================================================
// A common function that given an IShellFolder and and an Idlist that is
// contained in it, get back the index into the system image list.
//===========================================================================
HRESULT _GetILIndexGivenPXIcon(LPEXTRACTICON pxicon, UINT uFlags, LPCITEMIDLIST pidl, int *piImage, BOOL fAnsiCrossOver)
{
TCHAR szIconFile[MAX_PATH];
#ifdef UNICODE
CHAR szIconFileA[MAX_PATH];
IExtractIconA *pxiconA = (IExtractIconA *)pxicon;
#endif
int iIndex;
int iImage = -1;
UINT wFlags=0;
HRESULT hres;
Assert(himlIcons); // you must initialize the icon cache first
#ifdef UNICODE
if (fAnsiCrossOver)
{
szIconFileA[0] = '\0';
hres = pxiconA->lpVtbl->GetIconLocation(pxiconA, uFlags | GIL_FORSHELL,
szIconFileA, ARRAYSIZE(szIconFileA), &iIndex, &wFlags);
MultiByteToWideChar(CP_ACP, 0,
szIconFileA, -1,
szIconFile, ARRAYSIZE(szIconFile));
}
else
{
#endif
hres = pxicon->lpVtbl->GetIconLocation(pxicon, uFlags | GIL_FORSHELL,
szIconFile, ARRAYSIZE(szIconFile), &iIndex, &wFlags);
#ifdef UNICODE
}
#endif
//
// "*" as the file name means iIndex is already a system
// icon index, we are done.
//
// this is a hack for our own internal icon handler
//
if (SUCCEEDED(hres) && (wFlags & GIL_NOTFILENAME) &&
szIconFile[0] == TEXT('*') && szIconFile[1] == 0)
{
*piImage = iIndex;
return hres;
}
if (SUCCEEDED(hres))
{
//
// if GIL_DONTCACHE was returned by the icon handler, dont
// lookup the previous icon, assume a cache miss.
//
if (!(wFlags & GIL_DONTCACHE))
iImage = LookupIconIndex(PathFindFileName(szIconFile), iIndex, wFlags);
// if we miss our cache...
if (iImage == -1)
{
HICON hiconLarge=NULL;
HICON hiconSmall=NULL;
// try getting it from the ExtractIcon member fuction
#ifdef UNICODE
if (fAnsiCrossOver)
{
hres=pxiconA->lpVtbl->Extract(pxiconA, szIconFileA, iIndex,
&hiconLarge, &hiconSmall, MAKELONG(g_cxIcon, g_cxSmIcon));
}
else
{
#endif
hres=pxicon->lpVtbl->Extract(pxicon, szIconFile, iIndex,
&hiconLarge, &hiconSmall, MAKELONG(g_cxIcon, g_cxSmIcon));
#ifdef UNICODE
}
#endif
// S_FALSE means, can you please do it...Thanks
if (hres == S_FALSE)
{
hres = SHDefExtractIcon(szIconFile, iIndex, wFlags,
&hiconLarge, &hiconSmall, MAKELONG(g_cxIcon, g_cxSmIcon));
}
// if we extracted a icon add it to the cache.
if (hiconLarge)
{
// yes! got it, stuff it into our cache
iImage = SHAddIconsToCache(hiconLarge, hiconSmall, szIconFile, iIndex, wFlags);
}
// if we failed in any way pick a default icon
if (iImage == -1)
{
int iIcon;
if (wFlags & GIL_SIMULATEDOC)
iIcon = II_DOCUMENT;
else if ((wFlags & GIL_PERINSTANCE) && PathIsExe(szIconFile))
iIcon = II_APPLICATION;
else
iIcon = II_DOCNOASSOC;
iIndex = Shell_GetCachedImageIndex(szIconFile, iIcon, 0);
// if the handler failed dont cache this default icon.
// so we will try again later and mabey get the right icon.
//
// handlers should only fail if they cant access the file
// or something equaly bad.
if (SUCCEEDED(hres))
//
// Pretend that we were able to extract this from that file
//
AddToIconTable(szIconFile, iIndex, wFlags, iImage);
else
DebugMsg(DM_TRACE, TEXT("not caching icon for '%s' because cant access file"), szIconFile);
}
}
}
if (iImage < 0)
iImage = Shell_GetCachedImageIndex(c_szShell32Dll,
II_DOCNOASSOC, 0);
*piImage = iImage;
return hres;
}
//===========================================================================
// A common function that given an IShellFolder and and an Idlist that is
// contained in it, get back the index into the system image list.
//===========================================================================
HRESULT SHGetIconFromPIDL(IShellFolder *psf, IShellIcon *psi, LPCITEMIDLIST pidl, UINT flags, int *piImage)
{
IExtractIcon *pxi;
HRESULT hres;
Assert(himlIcons); // you must initialize the icon cache first
if (psi && psi->lpVtbl->GetIconOf(psi, pidl, flags, piImage) == S_OK)
{
return S_OK;
}
*piImage = Shell_GetCachedImageIndex(c_szShell32Dll,
II_DOCNOASSOC, 0);
hres = psf->lpVtbl->GetUIObjectOf(psf,
NULL, 1, pidl ? &pidl : NULL, &IID_IExtractIcon, NULL, &pxi);
if (SUCCEEDED(hres))
{
hres = _GetILIndexGivenPXIcon(pxi, flags, pidl, piImage, FALSE);
pxi->lpVtbl->Release(pxi);
}
#ifdef UNICODE
else
{
//
// Try the ANSI interface, see if we are dealing with an old set of code
//
IExtractIconA *pxiA;
hres = psf->lpVtbl->GetUIObjectOf(psf,
NULL, 1, pidl ? &pidl : NULL, &IID_IExtractIconA, NULL, &pxiA);
if (SUCCEEDED(hres))
{
hres = _GetILIndexGivenPXIcon(
(IExtractIcon *)pxiA, // cast to relieve grief
flags, pidl, piImage, TRUE ); // indicate Ansi ver.
pxiA->lpVtbl->Release(pxiA);
}
}
#endif
return hres;
}
//===========================================================================
// A common function that given an IShellFolder and and an Idlist that is
// contained in it, get back the index into the system image list.
//===========================================================================
int WINAPI SHMapPIDLToSystemImageListIndex(LPSHELLFOLDER psf, LPCITEMIDLIST pidl, int *piIndexSel)
{
int iIndex;
Assert(himlIcons); // you must initialize the icon cache first
if (piIndexSel)
SHGetIconFromPIDL(psf, NULL, pidl, GIL_OPENICON, piIndexSel);
SHGetIconFromPIDL(psf, NULL, pidl, 0, &iIndex);
return iIndex;
}
// -------------- auto scroll stuff --------------
BOOL _AddTimeSample(AUTO_SCROLL_DATA *pad, const POINT *ppt, DWORD dwTime)
{
pad->pts[pad->iNextSample] = *ppt;
pad->dwTimes[pad->iNextSample] = dwTime;
pad->iNextSample++;
if (pad->iNextSample == ARRAYSIZE(pad->pts))
pad->bFull = TRUE;
pad->iNextSample = pad->iNextSample % ARRAYSIZE(pad->pts);
return pad->bFull;
}
#ifdef DEBUG
// for debugging, verify we have good averages
DWORD g_time = 0;
int g_distance = 0;
#endif
int _CurrentVelocity(AUTO_SCROLL_DATA *pad)
{
int i, iStart, iNext;
int dx, dy, distance;
DWORD time;
Assert(pad->bFull);
distance = 0;
time = 1; // avoid div by zero
i = iStart = pad->iNextSample % ARRAYSIZE(pad->pts);
do {
iNext = (i + 1) % ARRAYSIZE(pad->pts);
dx = abs(pad->pts[i].x - pad->pts[iNext].x);
dy = abs(pad->pts[i].y - pad->pts[iNext].y);
distance += (dx + dy);
time += abs(pad->dwTimes[i] - pad->dwTimes[iNext]);
i = iNext;
} while (i != iStart);
#ifdef DEBUG
g_time = time;
g_distance = distance;
#endif
// scale this so we don't loose accuracy
return (distance * 1024) / time;
}
// NOTE: this is duplicated in shell32.dll
//
// checks to see if we are at the end position of a scroll bar
// to avoid scrolling when not needed (avoid flashing)
//
// in:
// code SB_VERT or SB_HORZ
// bDown FALSE is up or left
// TRUE is down or right
BOOL CanScroll(HWND hwnd, int code, BOOL bDown)
{
SCROLLINFO si;
si.cbSize = SIZEOF(SCROLLINFO);
si.fMask = SIF_ALL;
GetScrollInfo(hwnd, code, &si);
if (bDown)
{
if (si.nPage)
si.nMax -= si.nPage - 1;
return si.nPos < si.nMax;
}
else
{
return si.nPos > si.nMin;
}
}
#define DSD_NONE 0x0000
#define DSD_UP 0x0001
#define DSD_DOWN 0x0002
#define DSD_LEFT 0x0004
#define DSD_RIGHT 0x0008
//---------------------------------------------------------------------------
DWORD DAD_DragScrollDirection(HWND hwnd, const POINT *ppt)
{
RECT rcOuter, rc;
DWORD dwDSD = DSD_NONE; // 0
DWORD dwStyle = GetWindowLong(hwnd, GWL_STYLE);
// BUGBUG: do these as globals
#define g_cxVScroll GetSystemMetrics(SM_CXVSCROLL)
#define g_cyHScroll GetSystemMetrics(SM_CYHSCROLL)
GetClientRect(hwnd, &rc);
if (dwStyle & WS_HSCROLL)
rc.bottom -= g_cyHScroll;
if (dwStyle & WS_VSCROLL)
rc.right -= g_cxVScroll;
// the explorer forwards us drag/drop things outside of our client area
// so we need to explictly test for that before we do things
//
rcOuter = rc;
InflateRect(&rcOuter, g_cxSmIcon, g_cySmIcon);
InflateRect(&rc, -g_cxIcon, -g_cyIcon);
if (!PtInRect(&rc, *ppt) && PtInRect(&rcOuter, *ppt))
{
// Yep - can we scroll?
if (dwStyle & WS_HSCROLL)
{
if (ppt->x < rc.left)
{
if (CanScroll(hwnd, SB_HORZ, FALSE))
dwDSD |= DSD_LEFT;
}
else if (ppt->x > rc.right)
{
if (CanScroll(hwnd, SB_HORZ, TRUE))
dwDSD |= DSD_RIGHT;
}
}
if (dwStyle & WS_VSCROLL)
{
if (ppt->y < rc.top)
{
if (CanScroll(hwnd, SB_VERT, FALSE))
dwDSD |= DSD_UP;
}
else if (ppt->y > rc.bottom)
{
if (CanScroll(hwnd, SB_VERT, TRUE))
dwDSD |= DSD_DOWN;
}
}
}
return dwDSD;
}
#define SCROLL_FREQUENCY (GetDoubleClickTime()/2) // 1 line scroll every 1/4 second
#define MIN_SCROLL_VELOCITY 20 // scaled mouse velocity
BOOL WINAPI DAD_AutoScroll(HWND hwnd, AUTO_SCROLL_DATA *pad, const POINT *pptNow)
{
// first time we've been called, init our state
int v;
DWORD dwTimeNow = GetTickCount();
DWORD dwDSD = DAD_DragScrollDirection(hwnd, pptNow);
if (!_AddTimeSample(pad, pptNow, dwTimeNow))
return dwDSD;
v = _CurrentVelocity(pad);
if (v <= MIN_SCROLL_VELOCITY)
{
// Nope, do some scrolling.
if ((dwTimeNow - pad->dwLastScroll) < SCROLL_FREQUENCY)
dwDSD = 0;
if (dwDSD & DSD_UP)
{
DAD_ShowDragImage(FALSE);
FORWARD_WM_VSCROLL(hwnd, NULL, SB_LINEUP, 1, SendMessage);
}
else if (dwDSD & DSD_DOWN)
{
DAD_ShowDragImage(FALSE);
FORWARD_WM_VSCROLL(hwnd, NULL, SB_LINEDOWN, 1, SendMessage);
}
if (dwDSD & DSD_LEFT)
{
DAD_ShowDragImage(FALSE);
FORWARD_WM_HSCROLL(hwnd, NULL, SB_LINEUP, 1, SendMessage);
}
else if (dwDSD & DSD_RIGHT)
{
DAD_ShowDragImage(FALSE);
FORWARD_WM_HSCROLL(hwnd, NULL, SB_LINEDOWN, 1, SendMessage);
}
DAD_ShowDragImage(TRUE);
if (dwDSD)
{
DebugMsg(DM_TRACE, TEXT("v=%d"), v);
pad->dwLastScroll = dwTimeNow;
}
}
return dwDSD; // bits set if in scroll region
}
//
// We need to store this array in per-instance data because we don't
// want to marshal COPYHOOKINFO data structure across process boundary.
// However, this implementation does not support multiple instance of
// the shell.
//
#pragma data_seg(DATASEG_PERINSTANCE)
HDSA g_hdsaDefViewCopyHook = NULL;
#pragma data_seg()
typedef struct _DVCOPYHOOK {
HWND hwndView;
LPDEFVIEW pdv;
} DVCOPYHOOK, *LPDVCOPYHOOK;
void DefView_AddCopyHook(LPDEFVIEW this)
{
ENTERCRITICAL;
if (!g_hdsaDefViewCopyHook)
{
g_hdsaDefViewCopyHook = DSA_Create(SIZEOF(DVCOPYHOOK), 4);
DebugMsg(DM_TRACE, TEXT("sh TR - DefView_AddCopyHook creating the dsa"));
}
if (g_hdsaDefViewCopyHook)
{
DVCOPYHOOK dvch = { this->hwndView, this };
Assert(dvch.hwndView);
if (DSA_InsertItem(g_hdsaDefViewCopyHook, INT_MAX, &dvch)!=-1)
{
this->sv.lpVtbl->AddRef(&this->sv);
DebugMsg(DM_TRACE, TEXT("sh TR - DefView_AddCopyHook successfully added (total=%d)"),
DSA_GetItemCount(g_hdsaDefViewCopyHook));
}
}
LEAVECRITICAL;
}
int DefView_FindCopyHook(LPDEFVIEW this, BOOL fRemoveInvalid)
{
int item;
ASSERTCRITICAL;
if (g_hdsaDefViewCopyHook==NULL) {
return -1;
}
item = DSA_GetItemCount(g_hdsaDefViewCopyHook);
while(--item>=0)
{
const DVCOPYHOOK * pdvch=DSA_GetItemPtr(g_hdsaDefViewCopyHook, item);
if (pdvch)
{
if (fRemoveInvalid) {
if (!IsWindow(pdvch->hwndView))
{
DebugMsg(DM_WARNING, TEXT("sh WA - DefView_FindCopyHook: found a invalid element, removing..."));
DSA_DeleteItem(g_hdsaDefViewCopyHook, item);
continue;
}
}
if ((pdvch->hwndView==this->hwndView) && (pdvch->pdv==this)) {
return item;
}
}
else
{
Assert(0);
}
}
return -1; // not found
}
void DefView_RemoveCopyHook(LPDEFVIEW this)
{
LPSHELLVIEW psv = NULL;
ENTERCRITICAL;
if (g_hdsaDefViewCopyHook)
{
int item = DefView_FindCopyHook(this, TRUE);
if (item!=-1)
{
LPDVCOPYHOOK pdvch=DSA_GetItemPtr(g_hdsaDefViewCopyHook, item);
psv = &pdvch->pdv->sv;
DebugMsg(DM_TRACE, TEXT("sh TR - DefView_RemoveCopyHook removing an element"));
DSA_DeleteItem(g_hdsaDefViewCopyHook, item);
//
// If this is the last guy, destroy it.
//
if (DSA_GetItemCount(g_hdsaDefViewCopyHook)==0)
{
DebugMsg(DM_TRACE, TEXT("sh TR - DefView_RemoveCopyHook destroying hdsa (no element)"));
DSA_Destroy(g_hdsaDefViewCopyHook);
g_hdsaDefViewCopyHook=NULL;
}
}
}
LEAVECRITICAL;
//
// Release it outside the critical section.
//
if (psv) {
psv->lpVtbl->Release(psv);
}
}
UINT DefView_CopyHook(const COPYHOOKINFO *pchi)
{
int item;
UINT idRet = IDYES;
if (g_hdsaDefViewCopyHook==NULL) {
return idRet;
}
for (item=0;;item++)
{
DVCOPYHOOK dvch = { NULL, NULL };
//
// We should minimize this critical section (and must not
// call pfnCallBack which may popup UI!).
//
ENTERCRITICAL;
if (g_hdsaDefViewCopyHook && DSA_GetItem(g_hdsaDefViewCopyHook, item, &dvch)) {
dvch.pdv->sv.lpVtbl->AddRef(&dvch.pdv->sv);
}
LEAVECRITICAL;
if (dvch.pdv)
{
if (IsWindow(dvch.hwndView))
{
HRESULT hres;
hres = dvch.pdv->pfnCallback(dvch.pdv->psvOuter, dvch.pdv->pshf,
dvch.pdv->hwndMain,
DVM_NOTIFYCOPYHOOK, 0, (LPARAM)pchi);
dvch.pdv->sv.lpVtbl->Release(&dvch.pdv->sv);
if (SUCCEEDED(hres) && (hres != S_OK))
{
idRet = SCODE_CODE(hres);
Assert(idRet==IDYES || idRet==IDCANCEL || idRet==IDNO);
break;
}
item++;
}
else
{
DebugMsg(DM_TRACE, TEXT("sh WA - DefView_CopyHook list has an invalid element"));
dvch.pdv->sv.lpVtbl->Release(&dvch.pdv->sv);
}
}
else
{
break; // no more item.
}
}
return idRet;
}