|
|
#include "shellprv.h"
#include <regstr.h>
#include <shellp.h>
#include <htmlhelp.h>
#include "ole2dup.h"
#include "ids.h"
#include "defview.h"
#include "lvutil.h"
#include "idlcomm.h"
#include "filetbl.h"
#include "undo.h"
#include "cnctnpt.h"
#include "ovrlaymn.h"
#include "_security.h"
#include "unicpp\dutil.h"
#include "uemapp.h"
#include "unicpp\deskhtm.h"
#include "unicpp\dcomp.h"
#include "datautil.h"
#include "defvphst.h"
#include <shdispid.h>
#include <limits.h>
#include "prop.h"
#include <mshtmcid.h>
#include "dvtasks.h"
#include "category.h"
#include "ViewState.h"
#include <initguid.h>
#include <guids.h>
#include <CommonControls.h>
#include "clsobj.h"
#include <sfview.h>
#include "defviewp.h"
#include "shellp.h"
#include "duiview.h"
#include "enumidlist.h"
#include "util.h"
#include "foldertypes.h"
#include <dpa.h>
#include "views.h"
#include "defcm.h"
#include "contextmenu.h"
// a "default" view to trick the browser into letting us delay viewmode selection
// {6C6720F7-4B22-4CAA-82D6-502BB6F85A9A}
DEFINE_GUID(VID_DefaultView, 0x6C6720F7L, 0x4B22, 0x4CAA, 0x82, 0xD6, 0x50, 0x2B, 0xB6, 0xF8, 0x5A, 0x9A);
void DisableActiveDesktop(); STDAPI_(void) CFSFolder_UpdateIcon(IShellFolder *psf, LPCITEMIDLIST pidl); STDAPI_(void) SetPositionItemsPoints(IFolderView* psfv, LPCITEMIDLIST* apidl, UINT cidl, IDataObject* pdtobj, POINT* ptDrag); void UpdateGridSizes(BOOL fDesktop, HWND hwndListview, int nWorkAreas, LPRECT prcWork, BOOL fMinimizeGutterSpace);
#define ID_LISTVIEW 1
#define ID_STATIC 2
extern BOOL g_fDraggingOverSource;
#define IsDefaultState(_dvHead) ((_dvHead).dvState.lParamSort == 0 && \
(_dvHead).dvState.iDirection == 1 && \ (_dvHead).dvState.iLastColumnClick == -1 && \ (_dvHead).ptScroll.x == 0 && (_dvHead).ptScroll.y == 0)
HMODULE g_hmodNTSHRUI = NULL;
typedef struct { POINT pt; ITEMIDLIST idl; } DVITEM;
//
// Note that it returns NULL, if iItem is -1.
//
// determine if color is light or dark
#define COLORISLIGHT(clr) ((5*GetGValue((clr)) + 2*GetRValue((clr)) + GetBValue((clr))) > 8*128)
void EnableCombinedView(CDefView *pdsv, BOOL fEnable);
BOOL IsBarricadeGloballyOff(); VARIANT_BOOL GetBarricadeStatus(LPCTSTR pszValueName); BOOL GetBarricadeValueNameFromPidl(LPCITEMIDLIST pidl, LPTSTR pszValueName, UINT cch); HRESULT SetBarricadeStatus(LPCTSTR pszValueName, VARIANT_BOOL bShowBarricade);
// Command Strings
// !! warning. Some ContextMenu handlers do not do a case-insensitive
// check of the command so keep the case the same everywhere
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"); TCHAR const c_szRename[] = TEXT("rename"); TCHAR const c_szDelete[] = TEXT("delete"); TCHAR const c_szNewFolder[] = TEXT(CMDSTR_NEWFOLDERA);
char const c_szDeleteA[] = "delete"; char const c_szNewFolderA[] = CMDSTR_NEWFOLDERA; char const c_szPrintA[] = "print";
WCHAR const c_szPrintW[] = L"print";
DWORD CDefView::_Attributes(LPCITEMIDLIST pidl, DWORD dwAttribs) { return SHGetAttributes(_pshf, pidl, dwAttribs); }
// IDefViewSafety
HRESULT CDefView::IsSafePage() { HRESULT hr = E_ACCESSDENIED; WCHAR wszCurrentMoniker[MAX_PATH]; if (SUCCEEDED(_cFrame._GetCurrentWebViewMoniker(wszCurrentMoniker, ARRAYSIZE(wszCurrentMoniker)))) { // Some previous versions of the OS put a "FILE://" in front of the template name and some didn't.
// SHRegisterValidateTemplate can't handle this prefix, so skip past it.
LPWSTR pszMoniker = wszCurrentMoniker; if (!StrNCmpI(pszMoniker, L"FILE://", 7)) pszMoniker += 7;
hr = SHRegisterValidateTemplate(pszMoniker, SHRVT_VALIDATE | SHRVT_PROMPTUSER | SHRVT_REGISTERIFPROMPTOK); } return hr; }
// IDVGetEnum
HRESULT CDefView::SetEnumReadyCallback(PFDVENUMREADYBALLBACK pfn, void *pvData) { _pfnEnumReadyCallback = pfn; _pvEnumCallbackData = pvData; return S_OK; }
BOOL FilterOnAttributes(DWORD dwAttributes, DWORD grfEnumFlags) { if (dwAttributes & SFGAO_FOLDER) { if (!(grfEnumFlags & SHCONTF_FOLDERS)) return FALSE; // item is folder but client does not want folders
} else if (!(grfEnumFlags & SHCONTF_NONFOLDERS)) { return FALSE; // item is file, but client only wants folders
}
if (!(grfEnumFlags & SHCONTF_INCLUDEHIDDEN) && (dwAttributes & SFGAO_HIDDEN)) return FALSE; // item is hidden by client wants non hidden
return TRUE; }
HRESULT CDefView::CreateEnumIDListFromContents(LPCITEMIDLIST pidlFolder, DWORD grfEnumFlags, IEnumIDList **ppenum) { HRESULT hr = E_FAIL; LPITEMIDLIST pidlView = _GetViewPidl(); if (pidlView) { if (ILIsEqual(pidlFolder, pidlView) && (grfEnumFlags & _GetEnumFlags()) == grfEnumFlags) { LPCITEMIDLIST *apidl; UINT cItems; hr = _GetItemObjects(&apidl, SVGIO_ALLVIEW, &cItems); if (SUCCEEDED(hr)) { for (UINT i = 0; i < cItems; i++) { if (!FilterOnAttributes(_Attributes(apidl[i], SFGAO_FOLDER | SFGAO_HIDDEN), grfEnumFlags)) { apidl[i] = apidl[cItems - 1]; cItems--; i--; } }
hr = CreateIEnumIDListOnIDLists(apidl, cItems, ppenum); LocalFree(apidl); } } ILFree(pidlView); } return hr; }
HRESULT CDefView::_OnDefaultCommand() { return _pcdb ? _pcdb->OnDefaultCommand(_psvOuter ? _psvOuter : this) : E_NOTIMPL; }
HRESULT CDefView::_OnStateChange(UINT code) { return _pcdb ? _pcdb->OnStateChange(_psvOuter ? _psvOuter : this, code) : E_NOTIMPL; }
HRESULT CDefView::_IncludeObject(LPCITEMIDLIST pidl) { if (_pcdb) return _pcdb->IncludeObject(_psvOuter ? _psvOuter : this, pidl); else { IFolderFilter *psff = _cCallback.GetISFF(); return psff ? psff->ShouldShow(_pshf, NULL, pidl) : S_OK; } }
HRESULT CDefView::CallCB(UINT uMsg, WPARAM wParam, LPARAM lParam) { return _cCallback.CallCB(uMsg, wParam, lParam); }
// fires dispatch events to clients (address bar, webview, etc).
// this translates return values of false into "ERROR_CANCELLED"
HRESULT CDefView::_FireEvent(DISPID dispid) { HRESULT hr; VARIANT varResult = {0}; SHINVOKEPARAMS inv = {0};
inv.dispidMember = dispid; inv.piid = &IID_NULL; inv.wFlags = DISPATCH_METHOD; inv.pvarResult = &varResult;
if (SUCCEEDED(IUnknown_CPContainerInvokeIndirect(_pauto, DIID_DShellFolderViewEvents, &inv))) { if ((VT_BOOL == varResult.vt) && (VARIANT_FALSE == varResult.boolVal)) { hr = HRESULT_FROM_WIN32(ERROR_CANCELLED); } else { hr = S_OK; } VariantClear(&varResult); } else hr = S_FALSE;
return hr; }
BOOL CDefView::_IsPositionedView() { return !_fGroupView && ((_fs.ViewMode == FVM_ICON) || (_fs.ViewMode == FVM_SMALLICON) || (_fs.ViewMode == FVM_TILE) || (_fs.ViewMode == FVM_THUMBNAIL) || (_fs.ViewMode == FVM_THUMBSTRIP)); }
// reposition the selected items in a listview by dx, dy
void CDefView::_MoveSelectedItems(int dx, int dy, BOOL fAbsolute) { SendMessage(_hwndListview, WM_SETREDRAW, FALSE, 0); for (int i = ListView_GetNextItem(_hwndListview, -1, LVNI_SELECTED); i >= 0; i = ListView_GetNextItem(_hwndListview, i, LVNI_SELECTED)) { if (fAbsolute) { _SetItemPosition(i, dx, dy); } else { POINT pt; ListView_GetItemPosition(_hwndListview, i, &pt);
pt.x += dx; pt.y += dy;
_SetItemPosition(i, pt.x, pt.y); } } SendMessage(_hwndListview, WM_SETREDRAW, TRUE, 0); }
void CDefView::_SameViewMoveIcons() { POINT ptDrop; BOOL fAbsolute = FALSE;
// We'll use the insert mark rect (if available) to determine a drop point
if (_GetInsertPoint(&ptDrop)) fAbsolute = TRUE; // Move all items to this point.
else { ptDrop = _ptDrop; ptDrop.x -= _ptDragAnchor.x; ptDrop.y -= _ptDragAnchor.y; LVUtil_ClientToLV(_hwndListview, &ptDrop); }
ASSERT(_IsPositionedView());
_MoveSelectedItems(ptDrop.x, ptDrop.y, fAbsolute); }
//
// This function checks if the current HTML wallpaper is the default
// wallpaper and returns TRUE if so. If the wallpaper is the default wallpaper,
// it reads the colors from the registry. If the colors are missing, then it
// supplies the default colors.
//
BOOL CDefView::_GetColorsFromHTMLdoc(COLORREF *pclrTextBk, COLORREF *pclrHotlight) { // make sure the HTML document has reached ready-state interactive
COLORREF clrBackground; BOOL bRet = SUCCEEDED(_cFrame._GetHTMLBackgroundColor(&clrBackground)); if (bRet) { // The following are the standard colors supported on desktop
const COLORREF c_VgaColorTable[] = { 0x000000, // Black
0x000080, 0x0000FF, 0x008000, 0x008080, 0x00FF00, // Green
0x00FFFF, // Yellow
0x800000, 0x800080, 0x808000, 0x808080, 0xF0CAA6, 0xF0FBFF, 0xFF0000, // Blue
0xFF00FF, // Magenta
0xFFFF00, // cobalt
0xFFFFFF // White
};
// Check if the given background color is a standard color.
// If not, use the system background (COLOR_BACKGROUND).
*pclrTextBk = GetSysColor(COLOR_BACKGROUND); // default
for (int i = 0; i < ARRAYSIZE(c_VgaColorTable); i++) { if (c_VgaColorTable[i] == clrBackground) { *pclrTextBk = clrBackground; // standard, so use it
break; } }
if (COLORISLIGHT(*pclrTextBk)) *pclrHotlight = 0x000000; //Black as hightlight color!
else *pclrHotlight = 0xFFFFFF; //White as highlight color!
} return bRet; }
// Set the colors for the folder - taking care if it's the desktop.
void CDefView::_SetFolderColors() { COLORREF clrText, clrTextBk, clrWindow;
// Is this view for the desktop?
if (_IsDesktop()) { COLORREF clrHotlight;
Shell_SysColorChange();
// If we show HTML wallpaper, then get the appropriate colors too!
if (_fCombinedView && _GetColorsFromHTMLdoc(&clrTextBk, &clrHotlight)) { // Set the Hotlight color!
ListView_SetHotlightColor(_hwndListview, clrHotlight); } else { // Yep.
// Clear the background color of the desktop to make it
// properly handle transparency.
clrTextBk = GetSysColor(COLOR_BACKGROUND);
//Reset the Hotlight color sothat the system color can be used.
ListView_SetHotlightColor(_hwndListview, CLR_DEFAULT); } // set a text color that will show up over desktop color
if (COLORISLIGHT(clrTextBk)) clrText = 0x000000; // black
else clrText = 0xFFFFFF; // white
clrWindow = CLR_NONE; // Assume transparent
//
// 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.
//
// Don't do this optimization if USER is going to paint
// some magic text on the desktop, such as
//
// "FailSafe" (SM_CLEANBOOT)
// "Debug" (SM_DEBUG)
// "Build ####" (REGSTR_PATH_DESKTOP\PaintDesktopVersion)
// "Evaluation Version"
//
// too bad there is no SPI_GETWALLPAPER, we need to read
// from WIN.INI.
//
TCHAR szWallpaper[128], szPattern[128]; DWORD dwPaintVersion = 0; szWallpaper[0] = 0; szPattern[0] = 0;
HKEY hkey; if (RegOpenKeyEx(HKEY_CURRENT_USER, REGSTR_PATH_DESKTOP, 0, KEY_QUERY_VALUE, &hkey) == 0) { UINT cb = sizeof(szWallpaper); SHQueryValueEx(hkey, TEXT("Wallpaper"), NULL, NULL, (LPBYTE)szWallpaper, (ULONG*)&cb); cb = sizeof(szPattern); SHQueryValueEx(hkey, TEXT("Pattern"), NULL, NULL, (LPBYTE)szPattern, (ULONG*)&cb); cb = sizeof(dwPaintVersion); SHQueryValueEx(hkey, TEXT("PaintDesktopVersion"), NULL, NULL, (LPBYTE)&dwPaintVersion, (ULONG*)&cb);
// Other external criteria for painting the version
//
// - This is a beta version (has an expiration date)
// - A test certificate is installed
//
if (dwPaintVersion == 0 && IsOS(OS_WIN2000ORGREATER)) { #define REGSTR_PATH_LM_ROOTCERTIFICATES \
TEXT("SOFTWARE\\Microsoft\\SystemCertificates\\Root\\Certificates") #define REGSTR_PATH_GPO_ROOTCERTIFICATES \
TEXT("SOFTWARE\\Policies\\Microsoft\\SystemCertificates\\Root\\Certificates") #define REGSTR_KEY_TESTCERTIFICATE \
TEXT("2BD63D28D7BCD0E251195AEB519243C13142EBC3")
dwPaintVersion = (0 != USER_SHARED_DATA->SystemExpirationDate.QuadPart) || SHRegSubKeyExists(HKEY_LOCAL_MACHINE, REGSTR_PATH_LM_ROOTCERTIFICATES TEXT("\\") REGSTR_KEY_TESTCERTIFICATE) || SHRegSubKeyExists(HKEY_LOCAL_MACHINE, REGSTR_PATH_GPO_ROOTCERTIFICATES TEXT("\\") REGSTR_KEY_TESTCERTIFICATE) || SHRegSubKeyExists(HKEY_CURRENT_USER, REGSTR_PATH_GPO_ROOTCERTIFICATES TEXT("\\") REGSTR_KEY_TESTCERTIFICATE); } RegCloseKey(hkey); }
if (_fCombinedView || (GetSystemMetrics(SM_CLEANBOOT) == 0 && GetSystemMetrics(SM_DEBUG) == 0 && !dwPaintVersion && (!_fHasDeskWallPaper) && (szWallpaper[0] == 0 || szWallpaper[0] == TEXT('(')) && (szPattern[0] == 0 || szPattern[0] == TEXT('(')))) { clrWindow = GetSysColor(COLOR_BACKGROUND); } } else { // Nope.
clrWindow = GetSysColor(COLOR_WINDOW); clrTextBk = clrWindow; clrText = GetSysColor(COLOR_WINDOWTEXT);
if (_fs.fFlags & FWF_TRANSPARENT) { IWebBrowser2 *pwb; if (SUCCEEDED(IUnknown_QueryService(_psb, SID_SContainerDispatch, IID_PPV_ARG(IWebBrowser2, &pwb)))) { IDispatch *pdisp; if (SUCCEEDED(pwb->get_Parent(&pdisp))) { IUnknown_HTMLBackgroundColor(pdisp, &clrWindow); pdisp->Release(); } pwb->Release(); } } }
if (!_fClassic && ISVALIDCOLOR(_crCustomColors[CRID_CUSTOMTEXTBACKGROUND])) clrTextBk = _crCustomColors[CRID_CUSTOMTEXTBACKGROUND];
if (!_fClassic && ISVALIDCOLOR(_crCustomColors[CRID_CUSTOMTEXT])) clrText = _crCustomColors[CRID_CUSTOMTEXT];
BOOL bChange = FALSE;
if (clrWindow != ListView_GetBkColor(_hwndListview)) bChange = ListView_SetBkColor(_hwndListview, clrWindow);
if (clrTextBk != ListView_GetTextBkColor(_hwndListview)) bChange = ListView_SetTextBkColor(_hwndListview, clrTextBk);
if (clrText != ListView_GetTextColor(_hwndListview)) bChange = ListView_SetTextColor(_hwndListview, clrText);
if (bChange) InvalidateRect(_hwndListview, NULL, TRUE); }
#define ViewRequiresColumns(x) ((x) == FVM_DETAILS || (x) == FVM_TILE)
DWORD CDefView::_LVStyleFromView() { DWORD dwStyle;
if (_IsDesktop()) { dwStyle = LVS_NOSCROLL | LVS_ALIGNLEFT; } else { dwStyle = LVS_SHOWSELALWAYS; // make sure selection is visible
}
// dwStyle |= _UxGetView();
// The listview view is no longer set using the window style, so the call to the
// view mapping code has been commented out.
// APPCOMPAT: This may be an issue, if apps are depending the exstyle bits on the listview hwnd
// in defview. If so, we can set them, but we must take care to exclude any bits outside the 2bit
// "view range" in the extended style (namely, tile view)
if (_IsAutoArrange()) dwStyle |= LVS_AUTOARRANGE;
if (_fs.fFlags & FWF_SINGLESEL) dwStyle |= LVS_SINGLESEL;
if (_fs.fFlags & FWF_ALIGNLEFT) dwStyle |= LVS_ALIGNLEFT;
if (_fs.fFlags & FWF_NOSCROLL) dwStyle |= LVS_NOSCROLL;
return dwStyle; }
DWORD CDefView::_LVExStyleFromView() { DWORD dwLVExStyle = 0;
if (_fs.fFlags & FWF_SNAPTOGRID) dwLVExStyle |= LVS_EX_SNAPTOGRID;
if (_fs.fFlags & FWF_CHECKSELECT) dwLVExStyle |= LVS_EX_CHECKBOXES|LVS_EX_SIMPLESELECT;
return dwLVExStyle; }
HRESULT CDefView::_GetDetailsHelper(int i, DETAILSINFO *pdi) { HRESULT hr = E_NOTIMPL;
if (_pshf2) { hr = _pshf2->GetDetailsOf(pdi->pidl, i, (SHELLDETAILS *)&pdi->fmt); }
if (FAILED(hr)) // Don't make NSEs impl all of IShellFolder2
{ if (_psd) { // HACK: pdi->fmt is the same layout as SHELLDETAILS
hr = _psd->GetDetailsOf(pdi->pidl, i, (SHELLDETAILS *)&pdi->fmt); } else if (HasCB()) { hr = CallCB(SFVM_GETDETAILSOF, i, (LPARAM)pdi); } }
return hr; }
// Determine if the given defview state struct has valid
// state info. If is doesn't, this function massages the
// values so it does.
UINT CDefView::_GetHeaderCount() { UINT cCols = 0; HWND hwndHead = ListView_GetHeader(_hwndListview); if (hwndHead) { cCols = Header_GetItemCount(hwndHead); } return cCols; }
void CDefView::AddColumns() { // so we do this once
if (_bLoadedColumns) return;
_bLoadedColumns = TRUE;
// I also use this as a flag for whether to free pColHdr
//
// Calculate a reasonable size to initialize the column width to.
_cxChar = GetControlCharWidth(_hwndListview);
// Check whether there is any column enumerator (ShellDetails or callback)
if (_psd || _pshf2 || HasCB()) { // Some shell extensions return S_OK and NULL pstmCols.
IStream *pstmCols = NULL; if (SUCCEEDED(CallCB(SFVM_GETCOLSAVESTREAM, STGM_READ, (LPARAM)&pstmCols)) && pstmCols) { _vs.LoadColumns(this, pstmCols); pstmCols->Release(); }
// Verify that this has been initialized. This may not be if there was no state stream.
_vs.InitializeColumns(this);
for (UINT i = 0; i < _vs.GetColumnCount(); ++i) { if (_IsColumnInListView(i)) { UINT iVisible = _RealToVisibleCol(i);
LV_COLUMN col; col.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM; col.fmt = _vs.GetColumnFormat(i);
// If column width is not specified in the desktop.ini.......
col.cx = _vs.GetColumnWidth(iVisible, _vs.GetColumnCharCount(i) * _cxChar); col.pszText = _vs.GetColumnName(i); col.cchTextMax = MAX_COLUMN_NAME_LEN; col.iSubItem = i;
if (col.fmt & LVCFMT_COL_HAS_IMAGES) { ListView_SetExtendedListViewStyleEx(_hwndListview, LVS_EX_SUBITEMIMAGES, LVS_EX_SUBITEMIMAGES); col.fmt &= ~LVCFMT_COL_HAS_IMAGES; }
ListView_InsertColumn(_hwndListview, iVisible, &col); } }
// Set the header control to have zero margin around bitmaps, for the sort arrows
Header_SetBitmapMargin(ListView_GetHeader(_hwndListview), 0);
ListView_SetExtendedListViewStyleEx(_hwndListview, LVS_EX_HEADERDRAGDROP | LVS_EX_LABELTIP, LVS_EX_HEADERDRAGDROP | LVS_EX_LABELTIP);
//We added columns; so, just sync the Column order.
_vs.SyncColumnOrder(this, TRUE); }
// use real numbers, not visible
int cCols = (int)_vs.GetColumnCount(); if (_vs._iLastColumnClick >= cCols) { _vs.InitWithDefaults(this);
if (_vs._iLastColumnClick >= cCols || _vs._lParamSort >= cCols) { // our defaults won't work on this view....
// hard code these defaults
_vs._lParamSort = 0; _vs._iDirection = 1; _vs._iLastColumnClick = -1; } } }
void CDefView::InitSelectionMode() { _dwSelectionMode = 0;
if (_fs.fFlags & FWF_SINGLECLICKACTIVATE) { _dwSelectionMode = LVS_EX_TRACKSELECT | LVS_EX_ONECLICKACTIVATE; } else if (!_fClassic) { SHELLSTATE ss; SHGetSetSettings(&ss, SSF_DOUBLECLICKINWEBVIEW, FALSE);
if (!ss.fDoubleClickInWebView) _dwSelectionMode = LVS_EX_TRACKSELECT | LVS_EX_ONECLICKACTIVATE; } }
void CDefView::_UpdateSelectionMode() { InitSelectionMode(); ListView_SetExtendedListViewStyleEx(_hwndListview, LVS_EX_TRACKSELECT | LVS_EX_ONECLICKACTIVATE | LVS_EX_TWOCLICKACTIVATE, _dwSelectionMode); }
DWORD _GetUnderlineStyles() { DWORD dwUnderline = ICON_IE;
// Read the icon underline settings.
DWORD cb = sizeof(dwUnderline); SHRegGetUSValue(TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer"), TEXT("IconUnderline"), NULL, &dwUnderline, &cb, FALSE, &dwUnderline, cb);
// If it says to use the IE link settings, read them in.
if (dwUnderline == ICON_IE) { dwUnderline = ICON_YES;
TCHAR szUnderline[8]; cb = sizeof(szUnderline); SHRegGetUSValue(TEXT("Software\\Microsoft\\Internet Explorer\\Main"), TEXT("Anchor Underline"), NULL, szUnderline, &cb, FALSE, szUnderline, cb);
// Convert the string to an ICON_ value.
if (!lstrcmpi(szUnderline, TEXT("hover"))) dwUnderline = ICON_HOVER; else if (!lstrcmpi(szUnderline, TEXT("no"))) dwUnderline = ICON_NO; else dwUnderline = ICON_YES; }
// Convert the ICON_ value into an LVS_EX value.
DWORD dwExStyle;
switch (dwUnderline) { case ICON_NO: dwExStyle = 0; break;
case ICON_HOVER: dwExStyle = LVS_EX_UNDERLINEHOT; break;
case ICON_YES: dwExStyle = LVS_EX_UNDERLINEHOT | LVS_EX_UNDERLINECOLD; break; } return dwExStyle; }
void CDefView::_UpdateUnderlines() { // Set the new LVS_EX_UNDERLINE flags.
ListView_SetExtendedListViewStyleEx(_hwndListview, LVS_EX_UNDERLINEHOT | LVS_EX_UNDERLINECOLD, _GetUnderlineStyles()); }
void CDefView::_SetSysImageList() { HIMAGELIST himlLarge, himlSmall;
Shell_GetImageLists(&himlLarge, &himlSmall); ListView_SetImageList(_hwndListview, himlLarge, LVSIL_NORMAL); ListView_SetImageList(_hwndListview, himlSmall, LVSIL_SMALL); }
void CDefView::_SetTileview() { IImageList* piml; if (SUCCEEDED(SHGetImageList(SHIL_EXTRALARGE, IID_PPV_ARG(IImageList, &piml)))) { ListView_SetImageList(_hwndListview, IImageListToHIMAGELIST(piml), LVSIL_NORMAL); piml->Release(); } }
LRESULT CDefView::_OnCreate(HWND hWnd) { _hwndView = hWnd; _hmenuCur = NULL; _uState = SVUIA_DEACTIVATE; _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
// Map the ViewMode to the proper listview style
DWORD dwStyle = _LVStyleFromView() | LVS_EDITLABELS; DWORD dwExStyle = 0;
// If the parent window is mirrored then the treeview window will inheret the mirroring flag
// And we need the reading order to be Left to right, which is the right to left in the mirrored mode.
if (IS_WINDOW_RTL_MIRRORED(hWnd)) { // This means left to right reading order because this window will be mirrored.
dwExStyle |= WS_EX_RTLREADING; }
// don't set this as in webview this is normally off, having this
// set causes a 3d edge to flash on in a refresh
if (!_ShouldShowWebView() && !_IsDesktop() && !(_fs.fFlags & FWF_NOCLIENTEDGE)) { dwExStyle |= WS_EX_CLIENTEDGE; }
if (_IsOwnerData()) dwStyle |= LVS_OWNERDATA;
_hwndListview = CreateWindowEx(dwExStyle, WC_LISTVIEW, TEXT("FolderView"), // MSAA name
dwStyle | WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | LVS_SHAREIMAGELISTS, 0, 0, 0, 0, hWnd, (HMENU)ID_LISTVIEW, HINST_THISDLL, NULL); if (_hwndListview) { // Set up non-viewmode-dependant listview information here.
// Other flags are set up in _SwitchToViewFVM
DWORD dwLVExStyle = _LVExStyleFromView() | LVS_EX_INFOTIP | LVS_EX_LABELTIP;
if (_IsDesktop()) { if (GetNumberOfMonitors() > 1) dwLVExStyle |= LVS_EX_MULTIWORKAREAS; } else { dwLVExStyle |= LVS_EX_DOUBLEBUFFER; // Enable double buffering for all but desktop for affects
}
// turn on infotips -- window was just created, so all LVS_EX bits are off
ListView_SetExtendedListViewStyle(_hwndListview, dwLVExStyle);
// Get the proper RTL bits to pass on to our child windows
_fmt = 0; // Be sure that the OS is supporting the flags DATE_LTRREADING and DATE_RTLREADING
if (g_bBiDiPlatform) { // Get the date format reading order
LCID locale = GetUserDefaultLCID(); if ((PRIMARYLANGID(LANGIDFROMLCID(locale)) == LANG_ARABIC)) { // Get the real list view windows ExStyle.
// [msadek]; we shouldn't check for either WS_EX_RTLREADING OR RTL_MIRRORED_WINDOW
// on localized builds we have both of them to display dirve letters,..etc correctly
// on enabled builds we have none of them. let's check on RTL_MIRRORED_WINDOW only
if (GetWindowLong(_hwndListview, GWL_EXSTYLE) & RTL_MIRRORED_WINDOW) _fmt = LVCFMT_RIGHT_TO_LEFT; else _fmt = LVCFMT_LEFT_TO_RIGHT; } }
// Get hwndInfotip (the control for all listview infotips).
HWND hwndInfotip = ListView_GetToolTips(_hwndListview); if (hwndInfotip) { // make the tooltip window to be topmost window (set the TTS_TOPMOST style bit for the tooltip)
SetWindowPos(hwndInfotip, HWND_TOPMOST, 0,0,0,0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
// Initialize hwndInfotip.
_InitInfotipControl(hwndInfotip); }
_UpdateUnderlines();
// IShellDetails for old callers, new guys use IShellFolder2
ASSERT(_psd == NULL); _pshf->CreateViewObject(_hwndMain, IID_PPV_ARG(IShellDetails, &_psd));
// App compat - some apps need columns loaded first thing
if (SHGetAppCompatFlags(ACF_LOADCOLUMNHANDLER) & ACF_LOADCOLUMNHANDLER) { AddColumns(); }
_SetFolderColors(); }
// Create _hwndInfotip (the control for all non-listview infotips).
_hwndInfotip = _CreateInfotipControl(hWnd); if (_hwndInfotip) { // Initialize _hwndInfotip.
_InitInfotipControl(_hwndInfotip); }
return _hwndListview ? 0 : -1; // 0 is success, -1 is failure from WM_CREATE
}
HWND CDefView::_CreateInfotipControl(HWND hwndParent) { // hwndInfotip is currently expected to be destroyed by destruction of
// the parent hwnd (hwndParent). Thus, hwndParent should not be NULL.
ASSERT(hwndParent != NULL); // Sanity check.
// Create hwndInfotip.
return ::CreateWindowEx( IS_WINDOW_RTL_MIRRORED(hwndParent) || IS_BIDI_LOCALIZED_SYSTEM() ? WS_EX_LAYOUTRTL : 0, TOOLTIPS_CLASS, NULL, 0, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, hwndParent, NULL, g_hinst, NULL); }
void CDefView::_InitInfotipControl(HWND hwndInfotip) { ASSERT(hwndInfotip);
// Set the length of time the pointer must remain stationary within a tool's
// bounding rectangle before the ToolTip window appears to 2 times the default.
INT iTime = ::SendMessage(hwndInfotip, TTM_GETDELAYTIME, TTDT_INITIAL, 0); ::SendMessage(hwndInfotip, TTM_SETDELAYTIME, TTDT_INITIAL, (LPARAM)(INT)MAKELONG(iTime * 2, 0));
// Set the length of time a ToolTip window remains visible if the pointer
// is stationary within a tool's bounding rectangle to a very large value.
::SendMessage(hwndInfotip, TTM_SETDELAYTIME, TTDT_AUTOPOP, (LPARAM)(INT)MAKELONG(MAXSHORT, 0)); }
// "Auto" AutoArrange means re-position if we are in a positioned view
// and the listview is not in auto-arrange mode. we do this to re-layout
// the icons in cases where that makes sense
HRESULT CDefView::_AutoAutoArrange(DWORD dwReserved) { if (!_fUserPositionedItems && _IsPositionedView() && !(GetWindowStyle(_hwndListview) & LVS_AUTOARRANGE)) { ListView_Arrange(_hwndListview, LVA_DEFAULT); } return S_OK; }
LRESULT CDefView::WndSize(HWND hWnd) { RECT rc;
// We need to dismiss "name edit" mode, if we are in.
_DismissEdit();
// Get the client size.
GetClientRect(hWnd, &rc);
// Set the Static to be the Client size.
if (_hwndStatic) { MoveWindow(_hwndStatic, rc.left, rc.top, rc.right-rc.left, rc.bottom-rc.top, TRUE);
HWND hAnimate = ::GetWindow (_hwndStatic, GW_CHILD);
if (hAnimate) { MoveWindow(hAnimate, rc.left, rc.top, rc.right-rc.left, rc.bottom-rc.top, TRUE); }
RedrawWindow(_hwndStatic, NULL, NULL, RDW_ERASE | RDW_INVALIDATE | RDW_ALLCHILDREN); }
// Set all windows to their new rectangles.
_cFrame.SetRect(&rc);
// Don't resize _hwndListview if a DefViewOC is using it.
//
// If we're waiting for a Web View (!_fCanActivateNow), then it
// doesn't make sense to resize the _hwndListview -- just extra
// work, right? But in the non-WebView case, it's this first
// resize which sets the listview size, and then there are no
// more. Unfortunately, the first resize comes in when the
// _hwndListview is created, which is *before* _fCanActivateNow
// can possibly be set.
if (!_fGetWindowLV && !_pDUIView) { SetWindowPos(_hwndListview, NULL, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, SWP_NOZORDER | SWP_NOACTIVATE); OnResizeListView(); }
if (_pDUIView) { _pDUIView->SetSize (&rc); _AutoAutoArrange(0); }
CallCB(SFVM_SIZE, 0, 0); return 1; }
UINT _GetMenuIDFromViewMode(UINT uViewMode) { ASSERTMSG(FVM_FIRST <= uViewMode && uViewMode <= FVM_LAST, "_GetMenuIDFromViewMode received unknown uViewMode"); return SFVIDM_VIEW_FIRSTVIEW + uViewMode - FVM_FIRST; }
void CDefView::CheckToolbar() { if (SHGetAppCompatFlags(ACF_WIN95DEFVIEW) & ACF_WIN95DEFVIEW) { int idCmdCurView = _GetMenuIDFromViewMode(_fs.ViewMode);
// preserve win95 behavior for dumb corel apps
for (int idCmd = SFVIDM_VIEW_ICON; idCmd <= SFVIDM_VIEW_DETAILS; idCmd++) { _psb->SendControlMsg( FCW_TOOLBAR, TB_CHECKBUTTON, idCmd, (LPARAM)(idCmd == idCmdCurView), NULL); } } }
void CDefView::OnListViewDelete(int iItem, LPITEMIDLIST pidlToFree, BOOL fCallCB) { LPCITEMIDLIST pidlReal = _GetPIDLParam((LPARAM)pidlToFree, iItem);
if (fCallCB) { CallCB(SFVM_DELETEITEM, 0, (LPARAM)pidlReal); }
ILFree(pidlToFree); // NULL in owner data case
}
// NOTE: many keys are handled as accelerators
void CDefView::HandleKeyDown(LV_KEYDOWN *pnmhdr) { // REVIEW: these are things not handled by accelerators, see if we can
// make them all based on accelerators
switch (pnmhdr->wVKey) { case VK_ESCAPE: if (_bHaveCutStuff) OleSetClipboard(NULL); break; } }
// This function checks to see if we are in virtual mode or not. If we are in
// virtual mode, we always need to ask our folder we are viewing for the item and
// not the listview.
LPCITEMIDLIST CDefView::_GetPIDL(int i) { if (_IsOwnerData()) { LPCITEMIDLIST pidl = NULL; CallCB(SFVM_GETITEMIDLIST, i, (LPARAM)&pidl); return pidl; }
return (LPCITEMIDLIST)LVUtil_GetLParam(_hwndListview, i); }
LPCITEMIDLIST CDefView::_GetPIDLParam(LPARAM lParam, int i) { return lParam ? (LPCITEMIDLIST)lParam : _GetPIDL(i); }
// returns an array of LPCITEMIDLIST for objects in the view (selected or all)
// the "focused" item is always in array entry 0. this array contains poitners to pidls
// owned stored in the listview, so YOU SHOULD NOT FREE THEM OR MESS WITH THEM IN ANYWAY.
// this also implies the lifetime of this array must be shorter than the listview
// data it points to. that is if the view changes under you you are hosed.
//
// Notes: 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).
typedef struct { LPCITEMIDLIST pidl; POINT pt; int iItem; } POS_SORT_INFO;
// standard compare returns
// -1 1 < 2
// 0 1 = 2
// 1 1 > 2
//
// NOTE: in the RTL_MIRRORED_WINDOW case the coords are reversed for us
int _CmpTopToBottomLeftToRight(POS_SORT_INFO *psi1, POS_SORT_INFO *psi2, LPARAM lParam) { int iCmp = psi1->pt.y - psi2->pt.y; if (0 == iCmp) { iCmp = psi1->pt.x - psi2->pt.x; } return iCmp; }
int _CmpLeftToRightTopToBottom(POS_SORT_INFO *psi1, POS_SORT_INFO *psi2, LPARAM lParam) { int iCmp = psi1->pt.x - psi2->pt.x; if (0 == iCmp) { iCmp = psi1->pt.y - psi2->pt.y; } return iCmp; }
CDPA<POS_SORT_INFO>::_PFNDPACOMPARE _GetSortFunction(HWND hwndListview) { if (GetWindowStyle(hwndListview) & LVS_ALIGNLEFT) { return _CmpLeftToRightTopToBottom; // desktop LV_VIEW_ICON case
} else { UINT uViewMode = ListView_GetView(hwndListview); switch (uViewMode) { case LV_VIEW_DETAILS: case LV_VIEW_LIST: return _CmpLeftToRightTopToBottom;
case LV_VIEW_TILE: case LV_VIEW_ICON: default: return _CmpTopToBottomLeftToRight; } } }
UINT CDefView::_GetItemArray(LPCITEMIDLIST apidl[], UINT capidl, UINT uWhat) { UINT cItems = 0;
if ((uWhat & SVGIO_TYPE_MASK) == SVGIO_SELECTION) { cItems = ListView_GetSelectedCount(_hwndListview); } else if ((uWhat & SVGIO_TYPE_MASK) == SVGIO_CHECKED) { int iItem = ListView_GetItemCount(_hwndListview) - 1; for (; iItem >= 0; iItem--) { if (ListView_GetCheckState(_hwndListview, iItem)) cItems++; } } else { cItems = ListView_GetItemCount(_hwndListview); }
if (apidl) { UINT uType = (SVGIO_SELECTION == (uWhat & SVGIO_TYPE_MASK)) ? LVNI_SELECTED : LVNI_ALL; BOOL bArrayFilled = FALSE; // gets set on success of the sort code path
// optimize the 1 case, the sort below is not needed
if (!(SVGIO_FLAG_VIEWORDER & uWhat) && (capidl > 1)) { CDPA<POS_SORT_INFO> dpaItemInfo;
// pick a grow size of capidl so that we get a single alloc
// when we add the first item to the array
if (dpaItemInfo.Create(capidl)) { POS_SORT_INFO *ppsi = new POS_SORT_INFO[capidl]; if (ppsi) { UINT iDPAIndex = 0; for (int iListView = ListView_GetNextItem(_hwndListview, -1, uType); (iListView >= 0) && (iDPAIndex < capidl); iListView = ListView_GetNextItem(_hwndListview, iListView, uType)) { // if we want checked then it must be checked, otherwise just return (or skip)
if ((SVGIO_CHECKED != (uWhat & SVGIO_TYPE_MASK)) || ListView_GetCheckState(_hwndListview, iListView)) { ppsi[iDPAIndex].pidl = _GetPIDL(iListView); ppsi[iDPAIndex].iItem = iListView; ListView_GetItemPosition(_hwndListview, iListView, &ppsi[iDPAIndex].pt);
// this may fail, but we catch that case below
dpaItemInfo.SetPtr(iDPAIndex, &ppsi[iDPAIndex]); iDPAIndex++; } }
// make sure the DPA got all of the items, if not
// we fall through to the unsorted case
if (dpaItemInfo.GetPtrCount() == capidl) { dpaItemInfo.Sort(_GetSortFunction(_hwndListview), 0);
int iFirstItem = ListView_GetNextItem(_hwndListview, -1, LVNI_FOCUSED);
// compute the start index in the dpa based on iFirstItem. this is to
// rotate the array so that iFirstItem is first in the list
for (iDPAIndex = 0; iDPAIndex < (UINT)dpaItemInfo.GetPtrCount(); iDPAIndex++) { if (dpaItemInfo.FastGetPtr(iDPAIndex)->iItem == iFirstItem) { break; // iDPAIndex setup for loop below
} }
for (int i = 0; i < dpaItemInfo.GetPtrCount(); i++, iDPAIndex++) { if (iDPAIndex >= (UINT)dpaItemInfo.GetPtrCount()) iDPAIndex = 0; // wrap back to zero
apidl[i] = dpaItemInfo.FastGetPtr(iDPAIndex)->pidl; } bArrayFilled = TRUE; // we have the results we want
delete [] ppsi; } } dpaItemInfo.Destroy(); } }
if (!bArrayFilled) { UINT i = 0; for (int iListView = ListView_GetNextItem(_hwndListview, -1, uType); (iListView >= 0) && (i < capidl); iListView = ListView_GetNextItem(_hwndListview, iListView, uType)) { // if we want checked then it must be checked, otherwise just return (or skip)
if ((SVGIO_CHECKED != (uWhat & SVGIO_TYPE_MASK)) || ListView_GetCheckState(_hwndListview, iListView)) { apidl[i++] = _GetPIDL(iListView); } } } } return cItems; }
//
// get the array of IDList from the selection and calls
// IShellFolder::GetUIObjectOf member to get the specified UI object
// interface.
//
HRESULT CDefView::_GetUIObjectFromItem(REFIID riid, void **ppv, UINT uWhat, BOOL fSetPoints) { LPCITEMIDLIST *apidl; UINT cItems; HRESULT hr;
if (SVGIO_SELECTION == (uWhat & SVGIO_TYPE_MASK)) { hr = GetSelectedObjects(&apidl, &cItems); } else { hr = _GetItemObjects(&apidl, uWhat, &cItems); }
if (SUCCEEDED(hr)) { if (cItems) { hr = _pshf->GetUIObjectOf(_hwndMain, cItems, apidl, riid, 0, ppv); if (SUCCEEDED(hr) && (IID_IDataObject == riid) && fSetPoints) { _SetPoints(cItems, apidl, (IDataObject *)*ppv); } LocalFree((HLOCAL)apidl); } else hr = E_INVALIDARG; } return hr; }
// If the browser has a Tree then we want to use explore.
UINT CDefView::_GetExplorerFlag() { return IsExplorerBrowser(_psb) ? CMF_EXPLORE : 0; }
// creates a selection object out of the current selection.
IShellItemArray* CDefView::_CreateSelectionShellItemArray(void) { IShellItemArray *pSelectionObj = NULL; LPCITEMIDLIST *apidl; UINT cItems;
if (SUCCEEDED(_GetItemObjects(&apidl, SVGIO_SELECTION | SVGIO_FLAG_VIEWORDER, &cItems)) && cItems) { SHCreateShellItemArray(NULL, _pshf, cItems, apidl, &pSelectionObj); LocalFree(apidl); } return pSelectionObj; }
DWORD CDefView::_AttributesFromSel(DWORD dwAttributesNeeded) { // If this gets hit then chances are it's a performance problem...
//
if (_fSelectionChangePending) { TraceMsg(TF_WARNING, "Potential perf badness: may be asking for attributes during OnLVNUpdateItem!"); if (_pSelectionShellItemArray) ATOMICRELEASE(_pSelectionShellItemArray); _pSelectionShellItemArray = _CreateSelectionShellItemArray(); }
DWORD dwAttributes = 0; if (_pSelectionShellItemArray) { _pSelectionShellItemArray->GetAttributes(SIATTRIBFLAGS_APPCOMPAT, dwAttributesNeeded, &dwAttributes); } return dwAttributes; }
// IContextMenuSite:
// Defview's context menu implementation isn't very clean. As a temporary step towards
// cleaning it up (CONTEXT and BACK_CONTEXT are intermingled), use the new DOCONTEXTMENUPOPUP range
//
HRESULT CDefView::DoContextMenuPopup(IUnknown* punkCM, UINT fFlags, POINT pt) { return _DoContextMenuPopup(punkCM, fFlags, pt, FALSE); }
HRESULT CDefView::_DoContextMenuPopup(IUnknown* punk, UINT fFlags, POINT pt, BOOL fListviewItem) { IContextMenu* pcm; HRESULT hr = punk->QueryInterface(IID_PPV_ARG(IContextMenu, &pcm)); if (SUCCEEDED(hr)) { hr = E_OUTOFMEMORY;
HMENU hmContext = CreatePopupMenu(); if (hmContext) { fFlags |= _GetExplorerFlag();
if (0 > GetKeyState(VK_SHIFT)) fFlags |= CMF_EXTENDEDVERBS;
IContextMenu3* pcm3; if (SUCCEEDED(pcm->QueryInterface(IID_PPV_ARG(IContextMenu3, &pcm3)))) { fFlags |= CMF_ICM3; pcm3->Release(); }
// Give the context menu a site if it doesn't have one already
IUnknown* punkSite; if (SUCCEEDED(IUnknown_GetSite(pcm, IID_PPV_ARG(IUnknown, &punkSite)))) { punkSite->Release(); } else { IUnknown_SetSite(pcm, SAFECAST(this, IShellView2*)); }
hr = pcm->QueryContextMenu(hmContext, 0, SFVIDM_BACK_CONTEXT_FIRST, SFVIDM_BACK_CONTEXT_LAST, fFlags); if (SUCCEEDED(hr)) { // Must preinitialize to NULL; Adaptec Easy CD Creator 3.5 does not
// null out the pointer on failure.
ICommDlgBrowser2 *pcdb2 = NULL; _psb->QueryInterface(IID_PPV_ARG(ICommDlgBrowser2, &pcdb2));
// 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 (_IsCommonDialog()) { // make sure this is an item
if (fListviewItem) { HMENU hmSelect = SHLoadPopupMenu(HINST_THISDLL, POPUP_COMMDLG_POPUPMERGE);
// If we have a pointer to the ICommDlgBrowser2 interface
// query if this interface wants to change the text of the
// default verb. This interface is needed in the common print
// dialog to change the default text from 'Select' to 'Print'.
if (pcdb2) { WCHAR szTextW[MAX_PATH] = {0};
if (pcdb2->GetDefaultMenuText(this, szTextW, ARRAYSIZE(szTextW)) == S_OK) { MENUITEMINFO mi = {0}; mi.cbSize = sizeof(mi); mi.fMask = MIIM_TYPE; mi.fType = MFT_STRING; mi.dwTypeData = szTextW; SetMenuItemInfo(hmSelect, 0, MF_BYPOSITION, &mi); } }
// 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_BACK_CONTEXT_FIRST-1), (UINT)-1, MM_ADDSEPARATOR);
SetMenuDefaultItem(hmContext, 0, MF_BYPOSITION); DestroyMenu(hmSelect); } }
_SHPrettyMenu(hmContext);
// If this is the common dialog browser 2, we need inform it
// the context menu is has started. This notifiction is use in
// the common print dialog on NT which hosts the printers folder.
// Common dialog want to relselect the printer object if the user
// selected the context menu from the background.
if (pcdb2) { pcdb2->Notify(this, CDB2N_CONTEXTMENU_START); }
// To reduce some menu message forwarding, throw away _pcmFile if we have one
// (Since we can't have a TrackPopupMenu and a File menu open at the same time)
IUnknown_SetSite(_pcmFile, NULL); ATOMICRELEASE(_pcmFile);
// stash pcm in _pcmContextMenuPopup so we can forward menu messages
ASSERT(NULL==_pcmContextMenuPopup); _pcmContextMenuPopup = pcm; _pcmContextMenuPopup->AddRef();
int idDefault = GetMenuDefaultItem(hmContext, MF_BYCOMMAND, 0); int idCmd = TrackPopupMenu(hmContext, TPM_RETURNCMD | TPM_RIGHTBUTTON | TPM_LEFTALIGN, pt.x, pt.y, 0, _hwndView, NULL);
ATOMICRELEASE(_pcmContextMenuPopup);
if ((idCmd == idDefault) && _OnDefaultCommand() == S_OK) { // commdlg browser ate the default command
} else if (idCmd == 0) { // No item selected
} else if (idCmd >= SFVIDM_BACK_CONTEXT_FIRST && idCmd <= SFVIDM_BACK_CONTEXT_LAST) { idCmd -= SFVIDM_BACK_CONTEXT_FIRST;
// We need to special case the rename command (just in case a legacy contextmenu impl relied on this behavior)
TCHAR szCommandString[64]; ContextMenu_GetCommandStringVerb(pcm, idCmd, szCommandString, ARRAYSIZE(szCommandString)); if (lstrcmpi(szCommandString, c_szRename) == 0) { DoRename(); } else { CMINVOKECOMMANDINFOEX ici = { 0 };
ici.cbSize = sizeof(ici); ici.hwnd = _hwndMain; ici.lpVerb = IntToPtr_(LPCSTR, idCmd); ici.nShow = SW_NORMAL; ici.ptInvoke = pt; ici.fMask |= CMIC_MASK_PTINVOKE | CMIC_MASK_FLAG_LOG_USAGE;
// record if shift or control was being held down
SetICIKeyModifiers(&ici.fMask);
_InvokeContextMenu(pcm, &ici); } } else { RIPMSG(FALSE, "CDefView::DoContextMenuPopup - Some IContextMenu inserted an ID out of our range. Ignoring."); }
// If this is the common dialog browser 2, we need inform it
// the context menu is done. This notifiction is use in
// the common print dialog on NT which hosts the printers folder.
// Common dialog want to relselect the printer object if the user
// selected the context menu from the background.
if (pcdb2) { pcdb2->Notify(this, CDB2N_CONTEXTMENU_DONE); pcdb2->Release(); } }
DestroyMenu(hmContext); }
// Always remove the site even if we didn't set it -- once used, the IContextMenu is dead.
IUnknown_SetSite(pcm, NULL);
pcm->Release(); }
return hr; }
void CDefView::ContextMenu(DWORD dwPos) { int iItem; UINT fFlags = 0; POINT pt;
if (SHRestricted(REST_NOVIEWCONTEXTMENU)) { return; }
// if shell32's global copy of the stopwatch mode is not init'd yet, init it now.
if (g_dwStopWatchMode == 0xffffffff) g_dwStopWatchMode = StopWatchMode();
if (g_dwStopWatchMode) StopWatch_Start(SWID_MENU, TEXT("Defview ContextMenu Start"), SPMODE_SHELL | SPMODE_DEBUGOUT);
if (IsWindowVisible(_hwndListview) && (IsChildOrSelf(_hwndListview, GetFocus()) == S_OK)) { // Find the selected item
iItem = ListView_GetNextItem(_hwndListview, -1, LVNI_SELECTED); } else { iItem = -1; }
if (dwPos == (DWORD) -1) { if (iItem != -1) { RECT rc; int iItemFocus = ListView_GetNextItem(_hwndListview, -1, LVNI_FOCUSED|LVNI_SELECTED); if (iItemFocus == -1) iItemFocus = iItem;
//
// Note that LV_GetItemRect returns it in client coordinate!
//
ListView_GetItemRect(_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(_hwndListview, HWND_DESKTOP, &pt, 1); } else { pt.x = GET_X_LPARAM(dwPos); pt.y = GET_Y_LPARAM(dwPos); }
IContextMenu* pcm; LPARAM uemEvent; if (iItem == -1) { DECLAREWAITCURSOR; SetWaitCursor();
// use the background context menu wrapper
GetItemObject(SVGIO_BACKGROUND, IID_PPV_ARG(IContextMenu, &pcm));
ResetWaitCursor();
// set the max range for these, so that they are unaffected...
uemEvent = _IsDesktop() ? UIBL_CTXTDESKBKGND : UIBL_CTXTDEFBKGND; } else { fFlags |= CMF_CANRENAME;
// One or more items are selected, let the folder add menuitems.
_CreateSelectionContextMenu(IID_PPV_ARG(IContextMenu, &pcm));
uemEvent = _IsDesktop() ? UIBL_CTXTDESKITEM : UIBL_CTXTDEFITEM; }
UEMFireEvent(&UEMIID_SHELL, UEME_INSTRBROWSER, UEMF_INSTRUMENT, UIBW_UICONTEXT, uemEvent);
if (g_dwStopWatchMode) StopWatch_Stop(SWID_MENU, TEXT("Defview ContextMenu Stop (!SafeToDefaultVerb)"), SPMODE_SHELL | SPMODE_DEBUGOUT);
if (IsSafeToDefaultVerb() && pcm) { _DoContextMenuPopup(pcm, fFlags, pt, iItem != -1); }
ATOMICRELEASE(pcm); }
BOOL CDefView::_GetItemSpacing(ITEMSPACING *pis) { DWORD dwSize = ListView_GetItemSpacing(_hwndListview, TRUE); pis->cxSmall = GET_X_LPARAM(dwSize); pis->cySmall = GET_Y_LPARAM(dwSize); dwSize = ListView_GetItemSpacing(_hwndListview, FALSE); pis->cxLarge = GET_X_LPARAM(dwSize); pis->cyLarge = GET_Y_LPARAM(dwSize);
return _fs.ViewMode != FVM_ICON; }
BOOL _DidDropOnRecycleBin(IDataObject *pdtobj) { CLSID clsid; return SUCCEEDED(DataObj_GetDropTarget(pdtobj, &clsid)) && IsEqualCLSID(clsid, CLSID_RecycleBin); }
void CDefView::_SetPoints(UINT cidl, LPCITEMIDLIST *apidl, IDataObject *pdtobj) { POINT pt; GetDragPoint(&pt);
::SetPositionItemsPoints(SAFECAST(this, IFolderView*), apidl, cidl, pdtobj, &pt); }
LRESULT CDefView::_OnBeginDrag(NM_LISTVIEW * pnm) { POINT ptOffset = pnm->ptAction; // hwndLV client coords
// This DefView is used as a drag source so we need to see if it's
// is hosted by something that can disguise the action.
if (S_OK != _ZoneCheck(PUAF_NOUI, URLACTION_SHELL_WEBVIEW_VERB)) { // This DefView is hosted in HTML, so we need to turn off the
// ability of this defview from being a drag source.
return 0; }
_OnDelayedSelectionChange();
if (FAILED(_FireEvent(DISPID_BEGINDRAG))) // script canceles dragging
return 0;
DWORD dwEffect = _AttributesFromSel(SFGAO_CANDELETE | DROPEFFECT_LINK | DROPEFFECT_MOVE | DROPEFFECT_COPY);
// Turn on DROPEFFECT_MOVE for any deleteable item
// (this is so the item can be dragged to the recycle bin)
if (SFGAO_CANDELETE & dwEffect) { dwEffect |= DROPEFFECT_MOVE; } // Mask out all attribute bits that aren't also DROPEFFECT bits:
dwEffect &= (DROPEFFECT_LINK | DROPEFFECT_MOVE | DROPEFFECT_COPY);
// Somebody began dragging in our window, so store that fact
_bDragSource = TRUE;
// save away the anchor point
_ptDragAnchor = pnm->ptAction; LVUtil_ClientToLV(_hwndListview, &_ptDragAnchor);
ClientToScreen(_hwndListview, &ptOffset); // now in screen
// can't use _pdoSelection here since we need fSetPoints
IDataObject *pdtobj; if (SUCCEEDED(_GetUIObjectFromItem(IID_PPV_ARG(IDataObject, &pdtobj), SVGIO_SELECTION, TRUE))) { // Give the source a chance to alter the drop effect.
CallCB(SFVM_ALTERDROPEFFECT, (WPARAM)&dwEffect, (LPARAM)pdtobj);
if (DAD_SetDragImageFromWindow(_hwndListview, &ptOffset, pdtobj)) { if (DRAGDROP_S_DROP == SHDoDragDrop(_hwndMain, pdtobj, NULL, dwEffect, &dwEffect)) { if (S_OK != CallCB(SFVM_DIDDRAGDROP, (WPARAM)dwEffect, (LPARAM)pdtobj)) { // the return of DROPEFFECT_MOVE tells us we need to delete the data
// see if we need to do that now...
// NOTE: we can't trust the dwEffect return result from DoDragDrop() because
// some apps (adobe photoshop) return this when you drag a file on them that
// they intend to open. so we invented the "PreformedEffect" as a way to
// know what the real value is, that is why we test both of these.
if ((DROPEFFECT_MOVE == dwEffect) && (DROPEFFECT_MOVE == DataObj_GetDWORD(pdtobj, g_cfPerformedDropEffect, DROPEFFECT_NONE))) { // enable UI for the recycle bin case (the data will be lost
// as the recycle bin really can't recycle stuff that is not files)
UINT uFlags = _DidDropOnRecycleBin(pdtobj) ? 0 : CMIC_MASK_FLAG_NO_UI; SHInvokeCommandOnDataObject(_hwndMain, NULL, pdtobj, uFlags,c_szDeleteA); } } }
//
// We need to clear the dragged image only if we still have the drag context.
//
DAD_SetDragImage((HIMAGELIST)-1, NULL); } pdtobj->Release(); } _bDragSource = FALSE; // All done dragging
return 0; }
void CDefView::_FocusOnSomething(void) { int iFocus = ListView_GetNextItem(_hwndListview, -1, LVNI_FOCUSED); if (iFocus == -1) { if (ListView_GetItemCount(_hwndListview) > 0) { // set the focus on the first item.
ListView_SetItemState(_hwndListview, 0, LVIS_FOCUSED, LVIS_FOCUSED); } } }
HRESULT CDefView::_InvokeContextMenu(IContextMenu *pcm, CMINVOKECOMMANDINFOEX *pici) { TCHAR szWorkingDir[MAX_PATH]; CHAR szWorkingDirAnsi[MAX_PATH];
if (SUCCEEDED(CallCB(SFVM_GETWORKINGDIR, ARRAYSIZE(szWorkingDir), (LPARAM)szWorkingDir))) { // Fill in both the ansi working dir and the unicode one
// since we don't know who's gonna be processing this thing.
SHUnicodeToAnsi(szWorkingDir, szWorkingDirAnsi, ARRAYSIZE(szWorkingDirAnsi)); pici->lpDirectory = szWorkingDirAnsi; pici->lpDirectoryW = szWorkingDir; pici->fMask |= CMIC_MASK_UNICODE; }
// In case the ptInvoke field was not already set for us, guess where
// that could be. (dli) maybe should let the caller set all points
if (!(pici->fMask & CMIC_MASK_PTINVOKE)) { if (GetCursorPos(&pici->ptInvoke)) pici->fMask |= CMIC_MASK_PTINVOKE; }
pici->fMask |= CMIC_MASK_ASYNCOK;
_OnDelayedSelectionChange();
HRESULT hr = _FireEvent(DISPID_VERBINVOKED); if (SUCCEEDED(hr)) hr = pcm->InvokeCommand((LPCMINVOKECOMMANDINFO)pici); return hr; }
DWORD CDefView::_GetNeededSecurityAction(void) { DWORD dwUrlAction = 0;
// this blanks out everything that has SFGAO_FOLDER -- what about zip/cab?
// this check is used in context menu ops to see if the default verb is
// trusted -- for the zip/cab case, we DO trust the default verb since we
// expose them as folders throughout the rest of the shell.
if (!(SFGAO_FOLDER & _AttributesFromSel(SFGAO_FOLDER))) { // If we are hosted by Trident, Zone Check Action.
// this is how we detect if we are hosted in an IFRAME or OBJECT tag
IUnknown *punk; if (SUCCEEDED(_psb->QueryInterface(IID_IIsWebBrowserSB, (void **)&punk))) { dwUrlAction = URLACTION_SHELL_VERB; punk->Release(); } else if (_fGetWindowLV) { // If we are using WebView, Zone Check Action.
dwUrlAction = URLACTION_SHELL_WEBVIEW_VERB; } }
return dwUrlAction; }
HRESULT CDefView::_ZoneCheck(DWORD dwFlags, DWORD dwAllowAction) { HRESULT hr = S_OK; DWORD dwUrlAction = _GetNeededSecurityAction();
if (dwUrlAction && (dwUrlAction != dwAllowAction)) { // First check if our parent wants to generate our context (Zone/URL).
IInternetHostSecurityManager *pihsm; hr = IUnknown_QueryService(_psb, IID_IInternetHostSecurityManager, IID_PPV_ARG(IInternetHostSecurityManager, &pihsm)); if (FAILED(hr) && _cFrame._pDocView) { // Yes, so if we are in WebView mode, check the instance of Trident that is
// displaying the WebView content, because that content could discuise the DefView
// and make the user unknowingly do something bad.
hr = IUnknown_QueryService(_cFrame._pDocView, IID_IInternetHostSecurityManager, IID_PPV_ARG(IInternetHostSecurityManager, &pihsm)); }
if (SUCCEEDED(hr)) { // This is the prefered way to do the zone check.
hr = ZoneCheckHost(pihsm, dwUrlAction, dwFlags | PUAF_FORCEUI_FOREGROUND); pihsm->Release(); } else { // No, we were not able to get the interface. So fall back to zone checking the
// URL that comes from the pidl we are at.
TCHAR szPathSource[MAX_PATH]; if (_GetPath(szPathSource)) { // Try to get a IInternetSecurityMgrSite so our UI will be modal.
IInternetSecurityMgrSite *pisms; if (SUCCEEDED(IUnknown_QueryService(_psb, SID_STopLevelBrowser, IID_PPV_ARG(IInternetSecurityMgrSite, &pisms)))) { // TODO: Have this object support IInternetSecurityMgrSite in case our parent doesn't provide one.
// Make that code support ::GetWindow() and ::EnableModless() or we won't get the modal behavior
// needed for VB and AOL.
hr = ZoneCheckUrl(szPathSource, dwUrlAction, dwFlags | PUAF_ISFILE | PUAF_FORCEUI_FOREGROUND, pisms); pisms->Release(); } } } }
return hr; }
BOOL CDefView::IsSafeToDefaultVerb(void) { return S_OK == _ZoneCheck(PUAF_WARN_IF_DENIED, 0); }
HRESULT CDefView::_InvokeContextMenuVerb(IContextMenu* pcm, LPCSTR pszVerb, UINT uKeyFlags, DWORD dwCMMask) { DECLAREWAITCURSOR; SetWaitCursor();
CMINVOKECOMMANDINFOEX ici = {0}; ici.cbSize = sizeof(ici); ici.hwnd = _hwndMain; ici.nShow = SW_NORMAL; ici.fMask = dwCMMask;
// Get the point where the double click is invoked.
GetMsgPos(&ici.ptInvoke); ici.fMask |= CMIC_MASK_PTINVOKE;
// record if shift or control was being held down
SetICIKeyModifiers(&ici.fMask);
IUnknown_SetSite(pcm, SAFECAST(this, IOleCommandTarget *));
// Security note: we assume all non default verbs safe
HRESULT hr; if (pszVerb || (IsSafeToDefaultVerb() && SUCCEEDED(_FireEvent(DISPID_DEFAULTVERBINVOKED)))) { WCHAR szVerbW[128]; if (pszVerb) { ici.lpVerb = pszVerb; SHAnsiToUnicode(pszVerb, szVerbW, ARRAYSIZE(szVerbW)); ici.lpVerbW = szVerbW; ici.fMask |= CMIC_MASK_UNICODE; }
HMENU hmenu = CreatePopupMenu(); if (hmenu) { UINT fFlags = _GetExplorerFlag();
if (NULL == pszVerb) fFlags |= CMF_DEFAULTONLY; // optmization
// SHIFT + dbl click does a Explore by default
if (uKeyFlags & LVKF_SHIFT) fFlags |= CMF_EXPLORE;
pcm->QueryContextMenu(hmenu, 0, CONTEXTMENU_IDCMD_FIRST, CONTEXTMENU_IDCMD_LAST, fFlags);
if (pszVerb) hr = S_OK; else { UINT idCmd = GetMenuDefaultItem(hmenu, MF_BYCOMMAND, GMDI_GOINTOPOPUPS); if (idCmd == -1) { hr = E_FAIL; } else { ici.lpVerb = (LPSTR)MAKEINTRESOURCE(idCmd - CONTEXTMENU_IDCMD_FIRST); hr = S_OK; } }
if (SUCCEEDED(hr)) { // 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;
hr = _InvokeContextMenu(pcm, &ici); }
DestroyMenu(hmenu); } else { hr = E_OUTOFMEMORY; } } else { hr = E_ACCESSDENIED; }
IUnknown_SetSite(pcm, NULL);
if (hcursor_wait_cursor_save) ResetWaitCursor();
return hr; }
HRESULT CDefView::_InvokeContextMenuVerbOnSelection(LPCSTR pszVerb, UINT uKeyFlags, DWORD dwCMMask) { if (NULL == pszVerb) { if (_IsDesktop()) UEMFireEvent(&UEMIID_SHELL, UEME_UISCUT, UEMF_XEVENT, -1, (LPARAM)-1);
if (S_OK == _OnDefaultCommand()) { return S_FALSE; /* commdlg browser ate the message */ }
if (uKeyFlags & LVKF_ALT) pszVerb = "properties"; }
// Dealing with context menus can be slow
DECLAREWAITCURSOR; SetWaitCursor();
IContextMenu *pcmSel; HRESULT hr = _CreateSelectionContextMenu(IID_PPV_ARG(IContextMenu, &pcmSel));
if (SUCCEEDED(hr)) _LogDesktopLinksAndRegitems();
ResetWaitCursor(); // undo the cursor since the below _Invoke needs to control cursor shape
if (SUCCEEDED(hr)) { hr = _InvokeContextMenuVerb(pcmSel, pszVerb, uKeyFlags, dwCMMask);
pcmSel->Release(); }
return hr; }
//
// We want to keep track of which desktop regitems and links the user is using.
// This lets the desktop cleaner app know which ones can safely be
// cleaned up.
//
// Be careful - there are many race conditions... You have to do the
// GetSelectedObjects before any InvokeCommands are done, because the
// InvokeCommand calls might change the selection state. But you also
// have to use the result of GetSelectedObjects immediately, because
// it returns pidls that are owned by the defview, and if a filesys
// notify comes in, you might end up with pidls that have been freed.
//
// So we just do all the work up front, before actually invoking anything.
// This does mean that if the invoke fails, we still log the usage,
// but that seems like a small price to pay.
//
void CDefView::_LogDesktopLinksAndRegitems() { if (_IsDesktop()) { LPCITEMIDLIST *apidl; UINT cItems; if (SUCCEEDED(GetSelectedObjects(&apidl, &cItems)) && apidl) { for (UINT i = 0; i < cItems; i++) { TCHAR szDisplayName[GUIDSTR_MAX+2]; // +2 for leading "::"
if (SUCCEEDED(DisplayNameOf(_pshf, apidl[i], SHGDN_INFOLDER | SHGDN_FORPARSING, szDisplayName, ARRAYSIZE(szDisplayName)))) { if (_Attributes(apidl[i], SFGAO_LINK)) { // its a link
UEMFireEvent(&UEMIID_SHELL, UEME_RUNPATH, UEMF_XEVENT, -1, (LPARAM)szDisplayName); } else if (IsRegItemName(szDisplayName, NULL)) { // it's a regitem
UEMFireEvent(&UEMIID_SHELL, UEME_RUNPATH, UEMF_XEVENT, -1, (LPARAM)szDisplayName); } } } LocalFree((HLOCAL)apidl); } } }
void CDefView::_UpdateColData(CBackgroundColInfo *pbgci) { UINT iItem = ListView_MapIDToIndex(_hwndListview, pbgci->GetId()); if (iItem != -1) { UINT uiCol = pbgci->GetColumn();
if (_IsColumnInListView(uiCol)) { UINT iVisCol = _RealToVisibleCol(uiCol);
ListView_SetItemText(_hwndListview, iItem, iVisCol, (LPTSTR)pbgci->GetText()); } }
delete pbgci; }
void CDefView::_UpdateIcon(LPITEMIDLIST pidl, UINT iIcon) { int i = _FindItem(pidl, NULL, FALSE, FALSE);
if (i >= 0) { LV_ITEM item = {0};
item.mask = LVIF_IMAGE; item.iItem = i; item.iImage = iIcon;
ListView_SetItem(_hwndListview, &item); } ILFree(pidl); }
void CDefView::_UpdateOverlay(int iList, int iOverlay) { ASSERT (iList >= 0);
if (_IsOwnerData()) { // In the ownerdata case, tell the owner that the overlay changed
CallCB(SFVM_SETICONOVERLAY, iList, iOverlay); ListView_RedrawItems(_hwndListview, iList, iList); } else { ListView_SetItemState(_hwndListview, iList, INDEXTOOVERLAYMASK(iOverlay), LVIS_OVERLAYMASK); } }
HRESULT CDefView::_GetIconAsync(LPCITEMIDLIST pidl, int *piIcon, BOOL fCanWait) { HRESULT hr;
// if we are not an owner-data view then try to extract asynchronously
UINT flags = (_IsOwnerData() ? 0 : GIL_ASYNC);
if (GIL_ASYNC & flags) { hr = SHMapIDListToImageListIndexAsync(_pScheduler, _pshf, pidl, flags, _AsyncIconTaskCallback, this, NULL, piIcon, NULL);
if (SUCCEEDED(hr)) { return S_OK; // indicate that we got the real icon
} else if (hr == E_PENDING) { hr = S_FALSE; // the icon index we have is a placeholder
} } else { hr = SHGetIconFromPIDL(_pshf, _psi, pidl, flags, piIcon); }
return hr; }
void CDefView::_AsyncIconTaskCallback(LPCITEMIDLIST pidl, void *pvData, void *pvHint, INT iIconIndex, INT iOpenIconIndex) { CDefView *pdv = (CDefView *)pvData; ASSERT(pdv); if (pdv) { LPITEMIDLIST pidlClone = ILClone(pidl); if (pidlClone && !PostMessage(pdv->_hwndView, WM_DSV_UPDATEICON, (WPARAM)pidlClone, (LPARAM)iIconIndex)) ILFree(pidlClone); } }
HRESULT CDefView::_GetOverlayIndexAsync(LPCITEMIDLIST pidl, int iList) { IRunnableTask * pTask;
HRESULT hr = CIconOverlayTask_CreateInstance(this, pidl, iList, &pTask); if (SUCCEEDED(hr)) { _AddTask(pTask, TOID_DVIconOverlay, 0, TASK_PRIORITY_GET_ICON, ADDTASK_ATEND); pTask->Release(); }
return hr; }
//
// Returns: if the cursor is over a listview item, its index; otherwise, -1.
//
int CDefView::_HitTest(const POINT *ppt, BOOL fIgnoreEdge) { LV_HITTESTINFO info;
if (!_IsListviewVisible()) return -1;
info.pt = *ppt; int iRet = ListView_HitTest(_hwndListview, &info);
if (-1 != iRet && fIgnoreEdge) { // If we're in one of these large image area modes, and the caller says
// it's okay to ignore "edge" hits, then pretend the user is over nothing.
// Tile mode only ignores the left edge of the icon, since the right edge
// is all text (and usually shorter than the tile width anyway).
if (_IsTileMode() && (info.flags & LVHT_ONLEFTSIDEOFICON)) iRet = -1; else if (_IsImageMode() && (info.flags & (LVHT_ONLEFTSIDEOFICON|LVHT_ONRIGHTSIDEOFICON))) iRet = -1; }
return iRet; }
void CDefView::_OnGetInfoTip(NMLVGETINFOTIP *plvn) { if (!SHShowInfotips()) return;
LPCITEMIDLIST pidl = _GetPIDL(plvn->iItem); if (pidl) { ATOMICRELEASE(_pBackgroundInfoTip); // Release the previous value, if any
HRESULT hr = E_FAIL; _pBackgroundInfoTip = new CBackgroundInfoTip(&hr, plvn);
if (_pBackgroundInfoTip && SUCCEEDED(hr)) { LPITEMIDLIST pidlFolder = _GetViewPidl(); if (pidlFolder) { CStatusBarAndInfoTipTask *pTask; hr = CStatusBarAndInfoTipTask_CreateInstance(pidlFolder, pidl, 0, 0, _pBackgroundInfoTip, _hwndView, _pScheduler, &pTask); if (SUCCEEDED(hr)) { if (_pScheduler) { // make sure there are no other background infotip tasks going on...
_pScheduler->RemoveTasks(TOID_DVBackgroundInfoTip, ITSAT_DEFAULT_LPARAM, FALSE); }
_AddTask(pTask, TOID_DVBackgroundInfoTip, 0, TASK_PRIORITY_INFOTIP, ADDTASK_ATEND); pTask->Release(); } ILFree(pidlFolder); } } } // Do not show a tip while the processing is happening in the background
plvn->pszText[0] = 0; }
HRESULT CDefView::_OnViewWindowActive() { IShellView *psv = _psvOuter ? _psvOuter : SAFECAST(this, IShellView*);
return _psb->OnViewWindowActive(psv); }
// CLR_NONE is a special value that never matches a valid RGB
COLORREF g_crAltColor = CLR_NONE; // uninitialized magic value
COLORREF g_crAltEncryptedColor = CLR_NONE; // uninitialized magic value
DWORD GetRegColor(COLORREF clrDefault, LPCTSTR pszName, COLORREF *pValue) { // Fetch the alternate color (for compression) if supplied.
if (*pValue == CLR_NONE) // initialized yet?
{ DWORD cbData = sizeof(*pValue); if (FAILED(SKGetValue(SHELLKEY_HKCU_EXPLORER, NULL, pszName, NULL, pValue, &cbData))) { *pValue = clrDefault; // default value
} } return *pValue; }
LRESULT CDefView::_GetDisplayInfo(LV_DISPINFO *plvdi) { LPCITEMIDLIST pidl = _GetPIDLParam(plvdi->item.lParam, plvdi->item.iItem); if (pidl && (plvdi->item.mask & (DEFVIEW_LISTCALLBACK_FLAGS))) { ASSERT(IsValidPIDL(pidl)); ASSERT(plvdi->item.iSubItem != 0 ? ViewRequiresColumns(_fs.ViewMode) : TRUE);
LV_ITEM item = {0}; item.mask = plvdi->item.mask & (DEFVIEW_LISTCALLBACK_FLAGS); item.iItem = plvdi->item.iItem; item.iImage = plvdi->item.iImage = -1; // for iSubItem != 0 case
if ((plvdi->item.iSubItem == 0) && (item.mask & LVIF_IMAGE)) { // If the folder supports IShellIconOverlay then only need to ask for ghosted, else
// we need to do the old stuff...
DWORD uFlags = _Attributes(pidl, _psio ? SFGAO_GHOSTED : SFGAO_LINK | SFGAO_SHARE | SFGAO_GHOSTED);
// set the mask
item.mask |= LVIF_STATE; plvdi->item.mask |= LVIF_STATE; item.stateMask = LVIS_OVERLAYMASK;
// Pick the right overlay icon. The order is significant.
item.state = 0; if (_psio) { int iOverlayIndex = SFV_ICONOVERLAY_UNSET; if (_IsOwnerData()) { // Note: we are passing SFV_ICONOVERLAY_DEFAULT here because
// some owners do not respond to SFVM_GETICONOVERLAY might return
// iOverlayIndex unchanged and it will get
iOverlayIndex = SFV_ICONOVERLAY_DEFAULT; CallCB(SFVM_GETICONOVERLAY, plvdi->item.iItem, (LPARAM)&iOverlayIndex); if (iOverlayIndex > 0) { item.stateMask |= LVIS_OVERLAYMASK; item.state |= INDEXTOOVERLAYMASK(iOverlayIndex); } }
if (iOverlayIndex == SFV_ICONOVERLAY_UNSET) { iOverlayIndex = OI_ASYNC; HRESULT hr = _psio->GetOverlayIndex(pidl, &iOverlayIndex); if (E_PENDING == hr) _GetOverlayIndexAsync(pidl, item.iItem); else if (S_OK == hr) { ASSERT(iOverlayIndex >= 0); ASSERT(iOverlayIndex < MAX_OVERLAY_IMAGES);
// In the owner data case, tell the owner we got an Overlay index
if (_IsOwnerData()) CallCB(SFVM_SETICONOVERLAY, item.iItem, iOverlayIndex);
item.state = INDEXTOOVERLAYMASK(iOverlayIndex); } } } else { if (uFlags & SFGAO_LINK) { item.state = INDEXTOOVERLAYMASK(II_LINK - II_OVERLAYFIRST + 1); } else if (uFlags & SFGAO_SHARE) { item.state = INDEXTOOVERLAYMASK(II_SHARE - II_OVERLAYFIRST + 1); } }
if (uFlags & SFGAO_GHOSTED) { item.stateMask |= LVIS_CUT; item.state |= LVIS_CUT; } else { item.stateMask |= LVIS_CUT; item.state &= ~LVIS_CUT; }
plvdi->item.stateMask = item.stateMask; plvdi->item.state = item.state;
// Get the image
if (_IsOwnerData() && !_IsImageMode()) { CallCB(SFVM_GETITEMICONINDEX, plvdi->item.iItem, (LPARAM)&item.iImage); }
if (item.iImage == -1) { if (_IsImageMode()) { // Check if the item is visible. If it is not, then the image was
// probably asked for by the thumbnail read ahead task, in which case, we set a
// different priority.
if (ListView_IsItemVisible(_hwndListview, item.iItem)) { if (S_OK != ExtractItem((UINT*)&item.iImage, item.iItem, pidl, TRUE, FALSE, PRIORITY_P5)) { _CacheDefaultThumbnail(pidl, &item.iImage); } } else { // Likely from read ahead task.
ExtractItem((UINT*)&item.iImage, item.iItem, pidl, TRUE, FALSE, PRIORITY_READAHEAD_EXTRACT); } } else _GetIconAsync(pidl, &item.iImage, TRUE); }
plvdi->item.iImage = item.iImage; }
if (item.mask & LVIF_TEXT) { if (plvdi->item.cchTextMax) *plvdi->item.pszText = 0;
// Note that we do something different for index 0 = NAME
if (plvdi->item.iSubItem == 0) { DisplayNameOf(_pshf, pidl, SHGDN_INFOLDER, plvdi->item.pszText, plvdi->item.cchTextMax); } else { // on the first slow column complete all of the other columns (assumed to be slow)
// now so we get good caching from the col handlers
UINT iReal = _VisibleToRealCol(plvdi->item.iSubItem);
if (_vs.GetColumnState(iReal) & SHCOLSTATE_SLOW) { UINT cCols = _vs.GetColumnCount(); for (UINT iVisCol = plvdi->item.iSubItem; iReal < cCols; iReal++) { if (_IsColumnInListView(iReal)) { ASSERT(_vs.GetColumnState(iReal) & SHCOLSTATE_SLOW);
UINT uId = ListView_MapIndexToID(_hwndListview, plvdi->item.iItem);
// in the async case set the text to nothing (NULL). this will
// prevent another call to ListView_GetItemText() from invoking us
ListView_SetItemText(_hwndListview, plvdi->item.iItem, iVisCol++, NULL);
IRunnableTask *pTask; if (SUCCEEDED(CExtendedColumnTask_CreateInstance(this, pidl, uId, _fmt, iReal, &pTask))) { _AddTask(pTask, TOID_DVBackgroundEnum, 0, TASK_PRIORITY_FILE_PROPS, ADDTASK_ATEND); pTask->Release(); } } } return 0; // bail!
}
DETAILSINFO di;
di.pidl = pidl; di.fmt = _fmt; di.iImage = -1; // Assume for now no image...
if (SUCCEEDED(_GetDetailsHelper(iReal, &di))) { StrRetToBuf(&di.str, pidl, plvdi->item.pszText, plvdi->item.cchTextMax);
if ((di.iImage != -1) && (plvdi->item.mask & LVIF_IMAGE)) { plvdi->item.iImage = di.iImage; } } } }
if ((item.mask & LVIF_GROUPID) && _fGroupView) { plvdi->item.mask |= LVIF_GROUPID; plvdi->item.iGroupId = _GetGroupForItem(plvdi->item.iItem, pidl); }
if (item.mask & LVIF_COLUMNS) { if (_fScrolling) { // Ignore any column requests if we're currently scrolling. However, don't
// return zero for the number of columns, return I_COLUMNSCALLBACK instead, because
// we do still want listview to call us back to ask for them if it is every displaying
// this guy while we're not scrolling.
plvdi->item.cColumns = I_COLUMNSCALLBACK; plvdi->item.puColumns = NULL; _fRequestedTileDuringScroll = TRUE; } else { if (_IsOwnerData()) { AddColumns();
if (plvdi->item.cColumns > 1) { // hack special case for the find folder
if (_MapSCIDToColumn(&SCID_DIRECTORY, &plvdi->item.puColumns[0])) plvdi->item.cColumns = 1; } } else { BOOL fGotColumns = FALSE; // Start a task to extract the important columns for this item.
LPCITEMIDLIST pidl = _GetPIDL(plvdi->item.iItem); if (pidl) { plvdi->item.cColumns = TILEVIEWLINES; if (SUCCEEDED(_PeekColumnsCache(NULL, 0, pidl, plvdi->item.puColumns, &plvdi->item.cColumns))) { // Make sure columns are loaded
AddColumns();
_FixupColumnsForTileview(plvdi->item.puColumns, plvdi->item.cColumns); fGotColumns = TRUE; } else { IRunnableTask *pTask; UINT uId = ListView_MapIndexToID(_hwndListview, plvdi->item.iItem);
if (SUCCEEDED(CFileTypePropertiesTask_CreateInstance(this, pidl, TILEVIEWLINES, uId, &pTask))) //pidl gets cloned
{ _AddTask(pTask, TOID_DVFileTypeProperties, 0, TASK_PRIORITY_FILE_PROPS, ADDTASK_ATEND); pTask->Release(); } } }
if (!fGotColumns) { plvdi->item.cColumns = 0; plvdi->item.puColumns = NULL; } } }
}
if (plvdi->item.iSubItem == 0) plvdi->item.mask |= LVIF_DI_SETITEM; // always store the name
} return 0; }
int CALLBACK GroupCompare(int iGroup1, int iGroup2, void *pvData) { ICategorizer* pcat = (ICategorizer*)pvData;
HRESULT hr = pcat->CompareCategory(CATSORT_DEFAULT, (DWORD)iGroup1, (DWORD)iGroup2);
return ShortFromResult(hr); }
void CDefView::_OnCategoryTaskAdd() { _fInBackgroundGrouping = TRUE; }
void CDefView::_OnCategoryTaskDone() { _fInBackgroundGrouping = FALSE;
LONG cTasksCompleteLocal;
ENTERCRITICAL; { _fGroupingMsgInFlight = FALSE;
cTasksCompleteLocal = _cTasksCompleted; _cTasksCompleted = 0;
// swap the DPAs
HDPA hdpaTemp = _hdpaGroupingListBackup; _hdpaGroupingListBackup = _hdpaGroupingListActive; _hdpaGroupingListActive = hdpaTemp; } LEAVECRITICAL;
LONG cEntries = DPA_GetPtrCount(_hdpaGroupingListBackup);
// now process everything in _hdpaGroupingListEmpty
for (int i = 0; i < cEntries; i++) { CBackgroundGroupInfo* pbggi = (CBackgroundGroupInfo*)DPA_GetPtr(_hdpaGroupingListBackup, i); if (pbggi) { if (pbggi->VerifyGroupExists(_hwndListview, _pcat)) { int iItem = ListView_MapIDToIndex(_hwndListview, pbggi->GetId());
if (iItem != -1) { LVITEM lvi = {0}; lvi.mask = LVIF_GROUPID; lvi.iGroupId = pbggi->GetGroupId(); lvi.iItem = iItem; ListView_SetItem(_hwndListview, &lvi); } } delete pbggi; } }
DPA_DeleteAllPtrs(_hdpaGroupingListBackup);
if (_pidlSelectAndPosition) { POINT pt = {0}; // Don't care: Groups don't have a position
SelectAndPositionItem(_pidlSelectAndPosition, _uSelectAndPositionFlags, &pt);
Pidl_Set(&_pidlSelectAndPosition, NULL); _uSelectAndPositionFlags = 0; } for (LONG i = 0; i < cTasksCompleteLocal; i++) { _ShowSearchUI(FALSE); _GlobeAnimation(FALSE); } }
DWORD CDefView::_GetGroupForItem(int iItem, LPCITEMIDLIST pidl) { DWORD dwGroup = I_GROUPIDNONE; if (_fGroupView) { if (_fSlowGroup) { UINT uId = ListView_MapIndexToID(_hwndListview, iItem); IRunnableTask* pTask; if (SUCCEEDED(CCategoryTask_Create(this, pidl, uId, &pTask))) { // Need to get the globe/search stuff kicked off while within the CreateViewWindow2 call,
// so do it here instead of a posted message in the above constructor
_OnCategoryTaskAdd();
_AddTask(pTask, TOID_DVBackgroundGroup, 0, TASK_PRIORITY_GROUP, ADDTASK_ATEND); pTask->Release(); } } else { _pcat->GetCategory(1, (LPCITEMIDLIST*)&pidl, &dwGroup); if (!ListView_HasGroup(_hwndListview, dwGroup)) { CATEGORY_INFO ci; _pcat->GetCategoryInfo(dwGroup, &ci);
LVINSERTGROUPSORTED igrp; igrp.pfnGroupCompare = GroupCompare; igrp.pvData = (void *)_pcat; igrp.lvGroup.cbSize = sizeof(LVGROUP); igrp.lvGroup.mask = LVGF_HEADER | LVGF_GROUPID; igrp.lvGroup.pszHeader= ci.wszName; igrp.lvGroup.iGroupId = (int)dwGroup;
ListView_InsertGroupSorted(_hwndListview, &igrp); } } }
return dwGroup; }
BOOL CDefView::_EnsureSCIDCache() { BOOL bRet = FALSE; if (_hdsaSCIDCache) { bRet = TRUE; } else if (_pshf2) { _hdsaSCIDCache = DSA_Create(sizeof(SHCOLUMNID), 30); if (_hdsaSCIDCache) { SHCOLUMNID scid;
for (UINT iCol = 0; SUCCEEDED(_pshf2->MapColumnToSCID(iCol, &scid)); iCol++) { // ignore failure, just means we can't find the thing
DSA_AppendItem(_hdsaSCIDCache, &scid); } bRet = TRUE; } } return bRet; }
BOOL CDefView::_MapSCIDToColumn(const SHCOLUMNID *pscid, UINT *pnColumn) { BOOL bRet = FALSE; *pnColumn = 0; if (_EnsureSCIDCache()) { UINT cCol = DSA_GetItemCount(_hdsaSCIDCache);
for (UINT iCol = 0; iCol < cCol; iCol++) { SHCOLUMNID scid; DSA_GetItem(_hdsaSCIDCache, iCol, &scid); if (IsEqualSCID(*pscid, scid)) { *pnColumn = iCol; bRet = TRUE; break; } } } return bRet; }
HRESULT CDefView::_GetPropertyUI(IPropertyUI **pppui) { if (!_ppui) SHCoCreateInstance(NULL, &CLSID_PropertiesUI, NULL, IID_PPV_ARG(IPropertyUI, &_ppui));
return _ppui ? _ppui->QueryInterface(IID_PPV_ARG(IPropertyUI, pppui)) : E_NOTIMPL; }
HRESULT CDefView::_PeekColumnsCache(PTSTR pszPath, UINT cchPath, LPCITEMIDLIST pidl, UINT rguColumns[], UINT *pcColumns) { TCHAR szPath[MAX_PATH]; if (pszPath == NULL) { pszPath = szPath; cchPath = ARRAYSIZE(szPath); }
// NOTE - need to replace this with GetDetailsEx(SCID_CANONICALTYPE) to support
// caching properly. then we dont need to sniff attributes or the name in order to get
// a nice caching index.
HRESULT hr = DisplayNameOf(_pshf, pidl, SHGDN_INFOLDER | SHGDN_FORPARSING, pszPath, cchPath); if (SUCCEEDED(hr)) { LPCWSTR pszExt = _Attributes(pidl, SFGAO_FOLDER) ? NULL : PathFindExtension(pszPath);
hr = E_FAIL;
// Check file table cache:
ENTERCRITICAL; SHCOLUMNID *pscidCached; UINT cSCIDCached = pszExt ? LookupFileSCIDs(pszExt, &pscidCached) : 0; //Handle no extension case by not looking up in cache
LEAVECRITICAL;
if (cSCIDCached) // Found the SCIDs cache in the file table
{ UINT nFilled = 0; // Found it... we don't need to check the registry
for (UINT nSCID = 0; nSCID < cSCIDCached && nFilled < *pcColumns; nSCID++) { if (_MapSCIDToColumn(&pscidCached[nSCID], &rguColumns[nFilled])) nFilled++; } *pcColumns = nFilled; LocalFree(pscidCached);
hr = S_OK; } }
return hr;
}
// Get the important columns for this guy, based on file extension
// pidl: The pidl of the item in question
// puColumns[]: The array which will get filled with important column indicies
// pcColumns IN: specifies how big rguColumns[] is. OUT: specified how many slots got filled.
HRESULT CDefView::_GetImportantColumns(LPCITEMIDLIST pidl, UINT rguColumns[], UINT *pcColumns) { TCHAR szPath[MAX_PATH];
// We need to ensure that the columns are loaded here
if (!_bLoadedColumns) { DWORD_PTR lRes = 0; if (!SendMessageTimeout(_hwndView, WM_DSV_ENSURE_COLUMNS_LOADED, 0, 0, SMTO_NORMAL, 5000, &lRes) || lRes == 0) return E_FAIL; }
HRESULT hr = _PeekColumnsCache(szPath, ARRAYSIZE(szPath), pidl, rguColumns, pcColumns); if (FAILED(hr)) { IQueryAssociations *pqa; hr = _pshf->GetUIObjectOf(_hwndMain, 1, &pidl, IID_PPV_ARG_NULL(IQueryAssociations, &pqa)); if (SUCCEEDED(hr)) { IPropertyUI *ppui; hr = _GetPropertyUI(&ppui); if (SUCCEEDED(hr)) { TCHAR szProps[INFOTIPSIZE]; DWORD cchOut = ARRAYSIZE(szProps); hr = pqa->GetString(0, ASSOCSTR_TILEINFO, NULL, szProps, &cchOut); if (SUCCEEDED(hr)) { UINT cNumColumns = 0; // # of items in rguColumns
UINT cSCID = 0; // # of items in rgscid
SHCOLUMNID rgscid[64]; // reasonable upper bound
ULONG chEaten = 0; // loop variable ParsePropertyName updates this
while ((cSCID < ARRAYSIZE(rgscid)) && SUCCEEDED(ppui->ParsePropertyName(szProps, &rgscid[cSCID].fmtid, &rgscid[cSCID].pid, &chEaten))) { // Map SCID to a column (while there are more column slots)
if ((cNumColumns < *pcColumns) && _MapSCIDToColumn(&rgscid[cSCID], &rguColumns[cNumColumns])) { cNumColumns++; cSCID++; } } *pcColumns = cNumColumns;
LPCWSTR pszExt = _Attributes(pidl, SFGAO_FOLDER) ? NULL : PathFindExtension(szPath); if (pszExt) { // cache for future use, except if there's no extension (cache key)
ENTERCRITICAL; AddFileSCIDs(pszExt, rgscid, cSCID); LEAVECRITICAL; } } ppui->Release(); } pqa->Release(); } } return hr; }
void CDefView::_FixupColumnsForTileview(UINT *rguColumns, UINT cColumns) { // Make sure these columns are added to listview (ie. visible).
// And then map the columns in rguColumns from real columns to visible columns
for (UINT i = 0; i < cColumns; i++) { _AddTileColumn(rguColumns[i]); }
// Now, also add the sorted by column, if it hasn't been added yet.
if (!_fSetTileViewSortedCol) { _fSetTileViewSortedCol = TRUE; // It's ok if we don't actually set it. It's the thought that counts.
if (_vs._lParamSort != -1) { _AddTileColumn(_vs._lParamSort);
// And set it selected, if we're not in groupview
if (!_fGroupView) { ListView_SetSelectedColumn(_hwndListview, _RealToVisibleCol(_vs._lParamSort)); } } }
// This must be done after all the _AddTileColumns, or else the visible col #'s will be off.
for (UINT i = 0; i < cColumns; i++) { rguColumns[i] = _RealToVisibleCol(rguColumns[i]); } }
void CDefView::_SetImportantColumns(CBackgroundTileInfo *pbgTileInfo) { UINT cColumns = pbgTileInfo->GetColumnCount(); UINT *rguColumns = pbgTileInfo->GetColumns();
LVTILEINFO ti = {0}; ti.cbSize = sizeof(ti); ti.cColumns = cColumns; ti.puColumns = rguColumns; ti.iItem = ListView_MapIDToIndex(_hwndListview, pbgTileInfo->GetId()); if (ti.iItem != -1) { _FixupColumnsForTileview(rguColumns, cColumns); // have the listview store the per item tile info that we have computed
ListView_SetTileInfo(_hwndListview, &ti); }
delete pbgTileInfo; }
// Ensures if we're in tileview, that the tileviewinfo is set.
void CDefView::_SetView(UINT fvm) { // Update our internal state
_fs.ViewMode = fvm;
// Map the ViewMode into a listview mode
DWORD iView = LV_VIEW_ICON; // Now switch the listview
switch (fvm) { case FVM_ICON: case FVM_SMALLICON: case FVM_THUMBNAIL: case FVM_THUMBSTRIP: iView = LV_VIEW_ICON; break;
case FVM_LIST: iView = LV_VIEW_LIST; break;
case FVM_TILE: iView = LV_VIEW_TILE; break;
case FVM_DETAILS: iView = LV_VIEW_DETAILS; break;
default: ASSERTMSG(FALSE, "_SetView got an invalid ViewMode!"); break; }
if (iView == LV_VIEW_TILE) { RECT rcLabelMargin = {1, 1, 1, 1}; // This gives us some room around the label, so the focus rect doesn't clip part of the text
LVTILEVIEWINFO lvtvi = {0}; lvtvi.cbSize = sizeof(lvtvi); lvtvi.dwMask = LVTVIM_TILESIZE | LVTVIM_COLUMNS | LVTVIM_LABELMARGIN; lvtvi.dwFlags = LVTVIF_AUTOSIZE; lvtvi.cLines = TILEVIEWLINES; lvtvi.rcLabelMargin = rcLabelMargin; ListView_SetTileViewInfo(_hwndListview, &lvtvi); } ListView_SetView(_hwndListview, iView); _FireEvent(DISPID_VIEWMODECHANGED); }
// rename the selection based on the new name for the renamed item
// this makes it easy to rename groups of files to a common base name
HRESULT CDefView::_DoBulkRename(LPCITEMIDLIST pidlNewName) { LPCITEMIDLIST *apidl; UINT cItems; HRESULT hr = _GetItemObjects(&apidl, SVGIO_SELECTION, &cItems); if (SUCCEEDED(hr)) { if (cItems > 1) // only interesting if more than 1
{ TCHAR szBase[MAX_PATH]; // seed file name used to generate other names
hr = DisplayNameOf(_pshf, pidlNewName, SHGDN_INFOLDER | SHGDN_FORPARSING, szBase, ARRAYSIZE(szBase)); if (SUCCEEDED(hr)) { if (!SHGetAttributes(_pshf, pidlNewName, SFGAO_FOLDER)) PathRemoveExtension(szBase); // remove the extension, if it is a file
UINT cBase = 1; // one based counter, start at "File (1)"
// if input contains (#) use that as the sequence # base
LPWSTR psz = StrChr(szBase, TEXT('(')); if (psz) { cBase = StrToInt(psz + 1) + 1; // start at this in sequence
*psz = 0; // remove the (#) from the base name
}
PathRemoveBlanks(szBase); // clean away leading/trailing blanks
// start at 1, skipping the focused item, renaming all others in the array
for (UINT i = 1; (i < cItems) && SUCCEEDED(hr); i++) { TCHAR szOld[MAX_PATH];
hr = DisplayNameOf(_pshf, apidl[i], SHGDN_INFOLDER | SHGDN_FORPARSING, szOld, ARRAYSIZE(szOld)); if (SUCCEEDED(hr)) { // Clone the pidl since isf->SetNameOf can result in synchronous update item
// that can free the ListView owned apidl[i].
LPITEMIDLIST pidlOldName = ILClone(apidl[i]); if (pidlOldName) { // if the new name we produce conflicts with a name that
// already exists we will retry up to 100 times
for (UINT cRetry = 0; cRetry < 100; cRetry++) { WCHAR szName[MAX_PATH]; wnsprintf(szName, ARRAYSIZE(szName), TEXT("%s (%d)%s"), szBase, cBase, PathFindExtension(szOld));
hr = _pshf->SetNameOf(NULL, pidlOldName, szName, SHGDN_INFOLDER | SHGDN_FORPARSING, NULL); if (SUCCEEDED(hr)) { // force sync change notify update to make sure
// all renames come through (avoid UPDATEDIR)
SHChangeNotifyHandleEvents(); cBase++; break; // did this one successfully
} else if (HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS) == hr || HRESULT_FROM_WIN32(ERROR_FILE_EXISTS) == hr) { cBase++; hr = S_OK; // and keep trying
} else { break; // other error, exit
} }
ILFree(pidlOldName); } } } } } LocalFree(apidl); } return hr; }
LRESULT CDefView::_OnLVNotify(NM_LISTVIEW *plvn) { switch (plvn->hdr.code) { case NM_KILLFOCUS: // force update on inactive to not ruin save bits
_OnStateChange(CDBOSC_KILLFOCUS); if (GetForegroundWindow() != _hwndMain) UpdateWindow(_hwndListview); _fHasListViewFocus = FALSE; _EnableDisableTBButtons(); break;
case NM_SETFOCUS: { if (!_fDestroying) { if (_cFrame.IsWebView()) // Do OLE stuff
{ UIActivate(SVUIA_ACTIVATE_FOCUS); } else { // We should call IShellBrowser::OnViewWindowActive() before
// calling its InsertMenus().
_OnViewWindowActive(); _OnStateChange(CDBOSC_SETFOCUS); OnActivate(SVUIA_ACTIVATE_FOCUS); _FocusOnSomething(); _UpdateStatusBar(FALSE); } _fHasListViewFocus = TRUE; _EnableDisableTBButtons(); } break; }
case NM_RCLICK: // on the shift+right-click case we want to deselect everything and select just our item if it is
// not already selected. if we dont do this, then listview gets confused (because he thinks
// shift means extend selection, but in the right click case it dosent!) and will bring up the
// context menu for whatever is currently selected instead of what the user just right clicked on.
if ((GetKeyState(VK_SHIFT) < 0) && (plvn->iItem >= 0) && !(ListView_GetItemState(_hwndListview, plvn->iItem, LVIS_SELECTED) & LVIS_SELECTED)) { // clear any currently slected items
ListView_SetItemState(_hwndListview, -1, 0, LVIS_SELECTED);
// select the guy that was just right-clicked on
ListView_SetItemState(_hwndListview, plvn->iItem, LVIS_SELECTED, LVIS_SELECTED); } break;
case LVN_ENDSCROLL: { // This means we're scrolling. Ignore requests for LVIF_COLUMNS while we're
// scrolling to speed things up.
// We don't want to ignore requests for LVIF_COLUMNS when we're owner data, because
// owner data listviews always callback for info on what to display. (The result would
// be already-present tileinfo vanishing while scrolling, since we'd be ignoring requests
// for what to display)
if ((_fs.ViewMode == FVM_TILE) && !_IsOwnerData()) { SetTimer(_hwndView, DV_IDTIMER_SCROLL_TIMEOUT, 250, NULL);
if (!_fScrolling) { _fScrolling = TRUE;
// We don't reset this on every LVN_ENDSCROLL - only if this is the first time
// we've scrolled since a stable (non-scrolling) state
_fRequestedTileDuringScroll = FALSE; } } } break;
case LVN_GETINFOTIP: _OnGetInfoTip((NMLVGETINFOTIP *)plvn); break;
case LVN_ITEMACTIVATE: if (!_fDisabled) { //in win95 if user left clicks on one click activate icon and then right
//clicks on it (within double click time interval), the icon is launched
//and context menu appears on top of it -- it does not disappear.
//furthermore the context menu cannot be destroyed but stays on top of
//any window and items on it are not accessible. to avoid this
//send cancel mode to itself to destroy context before the icon is
//launched
if (_hwndView) SendMessage(_hwndView, WM_CANCELMODE, 0, 0);
_InvokeContextMenuVerbOnSelection(NULL, ((NMITEMACTIVATE *)plvn)->uKeyFlags, CMIC_MASK_FLAG_LOG_USAGE); } break;
case NM_CUSTOMDRAW: { LPNMLVCUSTOMDRAW pcd = (LPNMLVCUSTOMDRAW)plvn;
switch (pcd->nmcd.dwDrawStage) { case CDDS_PREPAINT: { return _fShowCompColor ? CDRF_NOTIFYITEMDRAW : CDRF_DODEFAULT; } case CDDS_ITEMPREPAINT: { LRESULT lres = CDRF_DODEFAULT; LPCITEMIDLIST pidl = _GetPIDLParam(pcd->nmcd.lItemlParam, (int)pcd->nmcd.dwItemSpec); if (pidl) { DWORD dwAttribs = _Attributes(pidl, SFGAO_COMPRESSED | SFGAO_ENCRYPTED);
// only one or the other, can never be both
if (dwAttribs & SFGAO_COMPRESSED) { // default value of Blue
pcd->clrText = GetRegColor(RGB(0, 0, 255), TEXT("AltColor"), &g_crAltColor); } else if (dwAttribs & SFGAO_ENCRYPTED) { // default value Luna Mid Green
pcd->clrText = GetRegColor(RGB(19, 146, 13), TEXT("AltEncryptionColor"), &g_crAltEncryptedColor); } } if (_IsImageMode() && pcd->nmcd.hdc && (_dwRecClrDepth <= 8)) { HPALETTE hpal = NULL; if (SUCCEEDED(_GetBrowserPalette(&hpal))) { // Since we are a child of the browser, we should always take a back seat to thier palette selection
_hpalOld = SelectPalette(pcd->nmcd.hdc, hpal, TRUE); RealizePalette(pcd->nmcd.hdc); lres |= CDRF_NOTIFYPOSTPAINT; } } return lres; }
case CDDS_ITEMPOSTPAINT: if (_IsImageMode() && _hpalOld && pcd->nmcd.hdc) { SelectPalette(pcd->nmcd.hdc, _hpalOld, TRUE); _hpalOld = NULL; } break; } } return CDRF_DODEFAULT;
case LVN_BEGINDRAG: case LVN_BEGINRDRAG: if (_fDisabled) return FALSE; /* commdlg doesn't want user dragging */ return _OnBeginDrag(plvn);
case LVN_ITEMCHANGING: if (_fDisabled) return TRUE; break;
// Something changed in the listview. Delete any data that
// we might have cached away.
case LVN_ITEMCHANGED: if (plvn->uChanged & LVIF_STATE) { if (!_fIgnoreItemChanged) { // The rest only cares about SELCHANGE messages (avoid LVIS_DRAGSELECT, etc)
if ((plvn->uNewState ^ plvn->uOldState) & (LVIS_SELECTED | LVIS_FOCUSED)) { //if we are the drag source then dont send selection change message
if (!_bDragSource) { _OnStateChange(CDBOSC_SELCHANGE); }
OnLVSelectionChange(plvn); } else if ((plvn->uNewState ^ plvn->uOldState) & (LVIS_STATEIMAGEMASK)) { if (!_bDragSource) { _OnStateChange(CDBOSC_STATECHANGE); } } } } break;
// owner data state changed: e.g. search results
case LVN_ODSTATECHANGED: { NM_ODSTATECHANGE *pnm = (NM_ODSTATECHANGE *)plvn;
// for now handle only selection changes
if ((pnm->uOldState ^ pnm->uNewState) & (LVIS_SELECTED | LVIS_FOCUSED)) { _OnLVSelectionChange(-1, pnm->uOldState, pnm->uNewState, 0); } } break;
case LVN_DELETEITEM: OnListViewDelete(plvn->iItem, (LPITEMIDLIST)plvn->lParam, TRUE); break;
case LVN_COLUMNCLICK: // allow clicking on columns to set the sort order
if (_fGroupView) { BOOL fAllowArrange = TRUE; UINT iRealColumn = _VisibleToRealCol(plvn->iSubItem); SHCOLUMNID scid; if (SUCCEEDED(_pshf2->MapColumnToSCID(iRealColumn, &scid))) { ICategoryProvider* pcp = NULL; if (SUCCEEDED(_pshf->CreateViewObject(NULL, IID_PPV_ARG(ICategoryProvider, &pcp)))) { // returns S_FALSE to remove.
if (S_FALSE == pcp->CanCategorizeOnSCID(&scid)) { fAllowArrange = FALSE; } } }
if (fAllowArrange) _ArrangeBy(iRealColumn + SFVIDM_GROUPSFIRST); } else if (_pshf2 || _psd || HasCB()) { LPARAM lParamSort = _vs._lParamSort; LONG iLastColumnClick = _vs._iLastColumnClick, iLastSortDirection = _vs._iDirection; // push sort state
// Folder doesn't know which columns are on or off, so communication with folder uses real col #s
UINT iRealColumn = _VisibleToRealCol(plvn->iSubItem);
// seeral ways to do this... each can defer to the
// ultimate default that is defview calling itself.
HRESULT hr = S_FALSE; if (_psd) hr = _psd->ColumnClick(iRealColumn);
if (hr != S_OK) hr = CallCB(SFVM_COLUMNCLICK, iRealColumn, 0);
if (hr != S_OK) hr = Rearrange(iRealColumn);
// Allows iLastColumnClick to stay valid during the above calls
if (SUCCEEDED(hr)) _vs._iLastColumnClick = iRealColumn; else { // We failed somewhere so pop the sort state.
_vs._iDirection = iLastSortDirection; _vs._iLastColumnClick = (int)_vs._lParamSort; _vs._lParamSort = lParamSort ; _SetSortFeedback(); _vs._iLastColumnClick = iLastColumnClick; } } break;
case LVN_KEYDOWN: HandleKeyDown(((LV_KEYDOWN *)plvn)); break;
#define plvdi ((LV_DISPINFO *)plvn)
case LVN_BEGINLABELEDIT: { LPCITEMIDLIST pidl = _GetPIDLParam(plvdi->item.lParam, plvdi->item.iItem);
if (!pidl || !_Attributes(pidl, SFGAO_CANRENAME)) { MessageBeep(0); return TRUE; // Don't allow label edit
}
_fInLabelEdit = TRUE;
HWND hwndEdit = ListView_GetEditControl(_hwndListview); if (hwndEdit) { int cchMax = 0;
CallCB(SFVM_GETCCHMAX, (WPARAM)pidl, (LPARAM)&cchMax);
if (cchMax) { ASSERT(cchMax < 1024); SendMessage(hwndEdit, EM_LIMITTEXT, cchMax, 0); }
TCHAR szName[MAX_PATH]; if (SUCCEEDED(DisplayNameOf(_pshf, pidl, SHGDN_INFOLDER | SHGDN_FOREDITING, szName, ARRAYSIZE(szName)))) { SetWindowText(hwndEdit, szName); }
SHLimitInputEdit(hwndEdit, _pshf); } } break;
case LVN_ENDLABELEDIT:
_fInLabelEdit = FALSE; if (plvdi->item.pszText) { LPCITEMIDLIST pidl = _GetPIDLParam(plvdi->item.lParam, plvdi->item.iItem); if (pidl) { // this set site is questionable as folder should not have any state
// associated with the view. but this is needed for FTP so it can
// do an EnableModless for it's UI
IUnknown_SetSite(_pshf, SAFECAST(this, IOleCommandTarget *));
// Clone the pidl since isf->SetNameOf can result in a synchronous update item that
// will free the listview owned pidl.
LPITEMIDLIST pidlOldName = ILClone(pidl); if (pidlOldName) { LPITEMIDLIST pidlNewName = NULL; // paranoid about bad SetNameOf() impls
if (SUCCEEDED(_pshf->SetNameOf(_hwndMain, pidlOldName, plvdi->item.pszText, SHGDN_INFOLDER, &pidlNewName))) { ASSERT(NULL != pidlNewName); // folders need to implement this
if (pidlNewName) { _DoBulkRename(pidlNewName); ILFree(pidlNewName); }
SHChangeNotifyHandleEvents(); _OnStateChange(CDBOSC_RENAME); } else { SendMessage(_hwndListview, LVM_EDITLABEL, plvdi->item.iItem, (LPARAM)plvdi->item.pszText); }
ILFree(pidlOldName); }
IUnknown_SetSite(_pshf, NULL); } } else { // The user canceled. so return TRUE to let things like the mouse
// click be processed.
return TRUE; } break;
case LVN_GETDISPINFO: return _GetDisplayInfo(plvdi);
case LVN_ODFINDITEM: // We are owner data so we need to find the item for the user...
{ int iItem = -1; if (SUCCEEDED(CallCB(SFVM_ODFINDITEM, (WPARAM)&iItem, (LPARAM)plvn))) return iItem; return -1; // Not Found
}
case LVN_ODCACHEHINT: // Just a hint we don't care about return values
CallCB(SFVM_ODCACHEHINT, 0, (LPARAM)plvn); break;
case LVN_GETEMPTYTEXT: if (HasCB()) { if ((plvdi->item.mask & LVIF_TEXT) && SUCCEEDED(CallCB(SFVM_GETEMPTYTEXT, (WPARAM)(plvdi->item.cchTextMax), (LPARAM)(plvdi->item.pszText)))) return TRUE; } break;
} #undef lpdi
#undef plvdi
return 0; }
// FEATURE -- implement enabling/disabling of other toolbar buttons. We can enable/disable
// based on the current selection, but the problem is that some of the buttons work
// for other guys when defview doesn't have focus. Specifically, cut/copy/paste work
// for the folders pane. If we're going to enable/disable these buttons based on the
// selection, then we'll need to have a mechanism that lets the active band (such as
// folders) also have a say about the button state. That is too much work right now.
static const UINT c_BtnCmds[] = { SFVIDM_EDIT_COPYTO, SFVIDM_EDIT_MOVETO, #ifdef ENABLEDISABLEBUTTONS
SFVIDM_EDIT_COPY, SFVIDM_EDIT_CUT, #endif
};
static const DWORD c_BtnAttr[] = { SFGAO_CANCOPY, SFGAO_CANMOVE, #ifdef ENABLEDISABLEBUTTONS
SFGAO_CANCOPY, SFGAO_CANMOVE, #endif
};
#define SFGAO_RELEVANT (SFGAO_CANCOPY | SFGAO_CANMOVE)
// Description:
// Called by toolbar infrastructure to determine whether to display a given
// toolbar button in the "enabled" or "disabled" state.
//
// Return:
// TRUE display toolbar button in enabled state
// FALSE display toolbar button in disabled state
//
BOOL CDefView::_ShouldEnableToolbarButton(UINT uiCmd, DWORD dwAttr, int iIndex) { COMPILETIME_ASSERT(sizeof(c_BtnCmds) == sizeof(c_BtnAttr));
BOOL bEnable;
switch (uiCmd) { case SFVIDM_VIEW_VIEWMENU: bEnable = !_fBarrierDisplayed; break;
default: { DWORD dwBtnAttr;
if (iIndex != -1) { // Caller was nice and figured out dest index for us
dwBtnAttr = c_BtnAttr[iIndex]; } else { // Look for the command ourselves
dwBtnAttr = SHSearchMapInt((int*)c_BtnCmds, (int*)c_BtnAttr, ARRAYSIZE(c_BtnCmds), uiCmd); if (dwBtnAttr == -1) { // We don't care about this button, just enable it.
return TRUE; } }
// Disable any button we care about while listview is inactive.
bEnable = BOOLIFY(dwAttr & dwBtnAttr) && _fHasListViewFocus; break; } }
return bEnable; }
// As a perf enhancement, we cache the attributes of the currently selected
// files/folders in a FS view only. This is to avoid n^2 traversals of the
// selected items as we select/unselect them. These cached attributes
// should not be used for anything other than determining toolbar button
// states and should be revisited if we add toolbar buttons that care about
// much more than the attributes used by Move to & Copy to.
BOOL CDefView::_GetCachedToolbarSelectionAttrs(ULONG *pdwAttr) { BOOL fResult = FALSE; CLSID clsid; HRESULT hr = IUnknown_GetClassID(_pshf, &clsid); if (SUCCEEDED(hr) && IsEqualGUID(CLSID_ShellFSFolder, clsid)) { UINT iCount; if (SUCCEEDED(GetSelectedCount(&iCount)) && (iCount > 0) && (_uCachedSelCount > 0)) { *pdwAttr = _uCachedSelAttrs; fResult = TRUE; } } return fResult; }
void CDefView::_SetCachedToolbarSelectionAttrs(ULONG dwAttrs) { if (SUCCEEDED(GetSelectedCount(&_uCachedSelCount))) _uCachedSelAttrs = dwAttrs; else _uCachedSelCount = 0; }
void CDefView::_EnableDisableTBButtons() { if (!IsEqualGUID(_clsid, GUID_NULL)) { IExplorerToolbar *piet; if (SUCCEEDED(IUnknown_QueryService(_psb, SID_SExplorerToolbar, IID_PPV_ARG(IExplorerToolbar, &piet)))) { ULONG dwAttr;
if (!_GetCachedToolbarSelectionAttrs(&dwAttr)) dwAttr = _AttributesFromSel(SFGAO_RELEVANT);
for (int i = 0; i < ARRAYSIZE(c_BtnCmds); i++) _EnableToolbarButton(piet, c_BtnCmds[i], _ShouldEnableToolbarButton(c_BtnCmds[i], dwAttr, i));
_SetCachedToolbarSelectionAttrs(dwAttr);
piet->Release(); } } }
// Description:
// Enables or disables a specified button on the toolbar.
//
void CDefView::EnableToolbarButton(UINT uiCmd, BOOL bEnable) { if (!IsEqualGUID(_clsid, GUID_NULL)) { IExplorerToolbar *piet;
if (SUCCEEDED(IUnknown_QueryService(_psb, SID_SExplorerToolbar, IID_PPV_ARG(IExplorerToolbar, &piet)))) { _EnableToolbarButton(piet, uiCmd, bEnable); piet->Release(); } } }
// Description:
// Enables or disables a specified button on the toolbar.
//
// Note:
// This is an _internal_ method only.
// External calls should use EnableToolbarButton().
// Caller is responsible for ensuring this object uses IExplorerToolbar mechanism.
//
void CDefView::_EnableToolbarButton(IExplorerToolbar *piet, UINT uiCmd, BOOL bEnable) { ASSERT(!IsEqualGUID(_clsid, GUID_NULL)); // Required or piet cannot be valid.
ASSERT(piet); // Required or we're not using IExplorerToolbar mechanism.
UINT uiState;
if (SUCCEEDED(piet->GetState(&_clsid, uiCmd, &uiState))) { if (bEnable) uiState |= TBSTATE_ENABLED; else uiState &= ~TBSTATE_ENABLED;
piet->SetState(&_clsid, uiCmd, uiState); } }
void CDefView::_OnContentsChanged() { // use a timer to delay sending a gazillion content change messages to automation.
// todo: see what duiview has to do with this stuff.
// only fire event if someone is listening
if (_pauto || _pDUIView) { // delay for 100ms
SetTimer(_hwndView, DV_IDTIMER_NOTIFY_AUTOMATION_CONTENTSCHANGED, 100, NULL); } if (!_pDUIView) { _fRcvdContentsChangeBeforeDuiViewCreated = TRUE; } }
void CDefView::_OnDelayedContentsChanged() { KillTimer(_hwndView, DV_IDTIMER_NOTIFY_AUTOMATION_CONTENTSCHANGED);
// update dui, would be better if there were different handlers in CDUIView
// but go through selection changed for now.
ATOMICRELEASE(_pSelectionShellItemArray);
_pSelectionShellItemArray = _CreateSelectionShellItemArray();
if (_pDUIView) { if (_fBarrierDisplayed != _QueryBarricadeState()) { //
// Yet another DUI special-case.
// If the barrier state has changed, we need to
// tell DUIView about it so that the DUI right-pane
// content is reconstructed. This is required to make
// Control Panel update it's right-pane content when
// webview is turned on/off.
//
_fBarrierDisplayed = !_fBarrierDisplayed; _pDUIView->EnableBarrier (_fBarrierDisplayed); } _pDUIView->OnContentsChange(_pSelectionShellItemArray); }
_FireEvent(DISPID_CONTENTSCHANGED); }
// WARNING: don't add any code here that is expensive in anyway!
// we get many many of these notifies and if we slow this routine down
// we mess select all and large selection perf.
//
// you can add expensive code to the WM_DSV_SENDSELECTIONCHANGED handler _OnSelectionChanged,
// that happens after all of the sel change notifies go through.
//
// or you can add really expensive code to the double-click-timeout delayed _OnDelayedSelectionChange.
//
void CDefView::OnLVSelectionChange(NM_LISTVIEW *plvn) { _OnLVSelectionChange(plvn->iItem, plvn->uOldState, plvn->uNewState, plvn->lParam); }
void CDefView::_OnLVSelectionChange(int iItem, UINT uOldState, UINT uNewState, LPARAM lParam) { // Do selection changed stuff on a selection change only
if ((uOldState ^ uNewState) & LVIS_SELECTED) { // Tell the defview client that the selection may have changed
SFVM_SELCHANGE_DATA dvsci;
dvsci.uNewState = uNewState; dvsci.uOldState = uOldState; dvsci.lParamItem = lParam;
CallCB(SFVM_SELCHANGE, MAKEWPARAM(SFVIDM_CLIENT_FIRST, iItem), (LPARAM)&dvsci); }
// Notify the dispach that the focus changed..
_PostSelectionChangedMessage(uOldState ^ uNewState); }
void CDefView::_PostSelectionChangedMessage(UINT uSelectionStateChanged) { if (!_fSelectionChangePending) { _uSelectionStateChanged = uSelectionStateChanged;
// RACE CONDITION FIX (edwardp & buzzr)
// It is imperative to set _fSelectionChangePending _before_ posting
// WM_DSV_SENDSELECTIONCHANGED. Otherwise, a race condition ensues
// whereby we could handle the message via _OnSelectionChanged()
// whose first line sets _fSelectionChangePending = FALSE before we
// have set it to TRUE here. This means _fSelectionChangePending
// will never again be set to FALSE (since the this thread will be
// rescheduled, set it to TRUE, and the action of clearing it will
// already be past). This was happening with 100% reproducability
// with our background CGetCommandStateTask for WIA devices. The
// symptom most noticeable was that the DUI pane (task lists and
// details) no longer updated with each selection change.
_fSelectionChangePending = TRUE; PostMessage(_hwndView, WM_DSV_SENDSELECTIONCHANGED, 0, 0); } else { _uSelectionStateChanged |= uSelectionStateChanged; } }
void CDefView::_OnSelectionChanged() // handles WM_DSV_SENDSELECTIONCHANGED
{ _fSelectionChangePending = FALSE; // release this first so code we call doesn't think we're "pending" any more
if (_uSelectionStateChanged & LVIS_SELECTED) { // Get and cache the data object for the current selection
ATOMICRELEASE(_pSelectionShellItemArray); _pSelectionShellItemArray = _CreateSelectionShellItemArray();
// Update DUIView
if (_pDUIView) _pDUIView->OnSelectionChange(_pSelectionShellItemArray);
_UpdateStatusBar(FALSE); _EnableDisableTBButtons(); }
// Only fire selection change events if someone is listening
// and if the selection changed event was not caused by going into Edit mode (why?)
if (_pauto && !_fInLabelEdit) { // Send out the selection changed notification to the automation after a delay.
if (!_bAutoSelChangeTimerSet) { _bAutoSelChangeTimerSet = TRUE; _uAutoSelChangeState = _uSelectionStateChanged; } else { _uAutoSelChangeState |= _uSelectionStateChanged; }
// But not too long, since parts of our UI update when they receive this event.
// (Update the timer every time to keep delaying it during rapid selection change events)
SetTimer(_hwndView, DV_IDTIMER_NOTIFY_AUTOMATION_SELCHANGE, GetDoubleClickTime()/2, NULL); } }
void CDefView::_OnDelayedSelectionChange() // handles DV_IDTIMER_NOTIFY_AUTOMATION_SELCHANGE
{ if (_bAutoSelChangeTimerSet) { KillTimer(_hwndView, DV_IDTIMER_NOTIFY_AUTOMATION_SELCHANGE);
if (_uAutoSelChangeState & LVIS_SELECTED) _FireEvent(DISPID_SELECTIONCHANGED);
if (_uAutoSelChangeState & LVIS_FOCUSED) _FireEvent(DISPID_FOCUSCHANGED);
_bAutoSelChangeTimerSet = FALSE; } }
void CDefView::_PostNoItemStateChangedMessage() { if (_pauto && !_fNoItemStateChangePending) { PostMessage(_hwndView, WM_DSV_SENDNOITEMSTATECHANGED, 0, 0); _fNoItemStateChangePending = TRUE; } }
void CDefView::_OnNoItemStateChanged() { _FireEvent(DISPID_NOITEMSTATE_CHANGED); _fNoItemStateChangePending = FALSE; }
void CDefView::_PostEnumDoneMessage() { PostMessage(_hwndView, WM_DSV_FILELISTENUMDONE, 0, 0); }
void CDefView::_PostFillDoneMessage() { _ShowSearchUI(TRUE); PostMessage(_hwndView, WM_DSV_FILELISTFILLDONE, 0, 0); }
void CDefView::_OnEnumDoneMessage() { if (_pauto) _FireEvent(DISPID_FILELISTENUMDONE);
if (_pfnEnumReadyCallback) _pfnEnumReadyCallback(_pvEnumCallbackData); }
#define IN_VIEW_BMP 0x8000
#define EXT_VIEW_GOES_HERE 0x4000
#define PRIVATE_TB_FLAGS (IN_VIEW_BMP | EXT_VIEW_GOES_HERE)
#define IN_STD_BMP 0x0000
LRESULT CDefView::_OnNotify(NMHDR *pnm) { switch (pnm->idFrom) { case ID_LISTVIEW: return _OnLVNotify((NM_LISTVIEW *)pnm);
case FCIDM_TOOLBAR: return _TBNotify(pnm);
default:
switch (pnm->code) { case TTN_NEEDTEXT: #define ptt ((LPTOOLTIPTEXT)pnm)
_GetToolTipText(ptt->hdr.idFrom, ptt->szText, ARRAYSIZE(ptt->szText)); #undef ptt
break;
case NM_RCLICK: if (GetParent(pnm->hwndFrom) == _hwndListview) { POINT p; GetMsgPos(&p); _DoColumnsMenu(p.x, p.y); return 1; // To keep normal context menu from appearing
} } }
return 0; }
// ask the folder for the default column state
DWORD CDefView::_DefaultColumnState(UINT iCol) { DWORD dwState; if (_pshf2) { if (FAILED(_pshf2->GetDefaultColumnState(iCol, &dwState))) { dwState = SHCOLSTATE_ONBYDEFAULT; // deal with E_NOTIMPL GetDefaultColumState implementations
} } else { dwState = SHCOLSTATE_ONBYDEFAULT; } return dwState; }
// SHCOLSTATE_ONBYDEFAULT
//
// columns that are turn on for this view (are displayed in the UI)
BOOL CDefView::_IsDetailsColumn(UINT iCol) { return (_vs.GetColumnState(iCol) & SHCOLSTATE_ONBYDEFAULT) ? TRUE : FALSE; }
BOOL CDefView::_IsColumnInListView(UINT iCol) { return ((_vs.GetColumnState(iCol) & SHCOLSTATE_ONBYDEFAULT) || (_vs.GetTransientColumnState(iCol) & SHTRANSCOLSTATE_TILEVIEWCOLUMN)) ? TRUE : FALSE; }
BOOL CDefView::_IsTileViewColumn(UINT iCol) { return (_vs.GetTransientColumnState(iCol) & SHTRANSCOLSTATE_TILEVIEWCOLUMN) ? TRUE : FALSE; }
// SHCOLSTATE_HIDDEN
//
// columns that should not be displayed in the UI, but are exposed from
// the psf2->GetDetailsEx(). this is a way to have programtic access to properties
// that don't show up in details view
BOOL CDefView::_IsColumnHidden(UINT uCol) { return (_vs.GetColumnState(uCol) & SHCOLSTATE_HIDDEN) ? TRUE : FALSE; }
#define COL_CM_MAXITEMS 25 // how many item show up in context menu before more ... is inserted
HRESULT CDefView::AddColumnsToMenu(HMENU hm, DWORD dwBase) { BOOL bNeedMoreMenu = FALSE; HRESULT hr = E_FAIL;
if (_vs._hdsaColumns) { AppendMenu(hm, MF_STRING | MF_CHECKED | MF_GRAYED, dwBase, _vs.GetColumnName(0)); for (UINT i = 1; i < min(COL_CM_MAXITEMS, _vs.GetColumnCount()); i++) { DWORD dwFlags = _vs.GetColumnState(i); if (!(dwFlags & SHCOLSTATE_HIDDEN)) { if (dwFlags & SHCOLSTATE_SECONDARYUI) bNeedMoreMenu = TRUE; else AppendMenu(hm, MF_STRING | (dwFlags & SHCOLSTATE_ONBYDEFAULT) ? MF_CHECKED : 0, dwBase + i, _vs.GetColumnName(i)); } }
if (bNeedMoreMenu || (_vs.GetColumnCount() > COL_CM_MAXITEMS)) { TCHAR szMore[MAX_COLUMN_NAME_LEN]; LoadString(HINST_THISDLL, IDS_COL_CM_MORE, szMore, ARRAYSIZE(szMore)); AppendMenu(hm, MF_SEPARATOR, 0, NULL); AppendMenu(hm, MF_STRING, SFVIDM_VIEW_COLSETTINGS, szMore); } hr = S_OK; }
return hr; }
UINT CDefView::_RealToVisibleCol(UINT iReal) { ASSERT(_bLoadedColumns && _vs.GetColumnCount());
int iVisible = -1; // start here to get zero based result
int cMax = min(_vs.GetColumnCount() - 1, iReal);
for (int i = 0; i <= cMax; i++) { if (_IsColumnInListView(i)) { iVisible++; } } ASSERT(-1 != iVisible); return iVisible; }
// map listview (zero based) column indexes
// indexs (zero based)
UINT CDefView::_VisibleToRealCol(UINT iVisible) { ASSERT(_bLoadedColumns && _vs.GetColumnCount());
for (UINT i = 0, cVisibleSeen = 0; i < _vs.GetColumnCount(); i++) { if (_IsColumnInListView(i)) { if (cVisibleSeen == iVisible) { return i; } cVisibleSeen++; } } ASSERT(0); // should never get a vis col not in the real
return 0; }
void CDefView::_AddTileColumn(UINT uCol) { if (_IsColumnInListView(uCol)) { // All we need to do is make sure it's marked as a tile column
_vs.SetTransientColumnState(uCol, SHTRANSCOLSTATE_TILEVIEWCOLUMN, SHTRANSCOLSTATE_TILEVIEWCOLUMN); return; }
_vs.SetTransientColumnState(uCol, SHTRANSCOLSTATE_TILEVIEWCOLUMN, SHTRANSCOLSTATE_TILEVIEWCOLUMN);
// Now that we set the transient state, we can get the new visible column index
// for this guy, and add it to the listview.
UINT uColVis = _RealToVisibleCol(uCol); _AddColumnToListView(uCol, uColVis);
// We now need to reset the tile info for each item. We can make an optimization:
// if this column was added at the end (i.e. biggest visible column), it won't affect
// any of the current tiles, so we don't need to do this. Passing -1 gives us the
// largest visible index.
if (_RealToVisibleCol(-1) != uColVis) { _ResetTileInfo(uColVis, TRUE); } }
// Remove all columns that were added because of tileview (unless they were also
// added for other reasons).
// Note: This should only be called when leaving tileview, since we do not reset the
// items' tileinfo.
void CDefView::_RemoveTileColumns() { for (UINT uCol = 0; uCol < _vs.GetColumnCount(); uCol++) { if (_IsTileViewColumn(uCol)) { // First nuke the tile bit.
UINT uColVis = _RealToVisibleCol(uCol); _vs.SetTransientColumnState(uCol, SHTRANSCOLSTATE_TILEVIEWCOLUMN, 0);
// Then go ahead and remove it from listview if it wasn't a details column
if (!_IsDetailsColumn(uCol)) { ListView_DeleteColumn(_hwndListview, uColVis); } } } }
// This method resets the tileinfo for each item in the listview, based on which
// visible column we just added or removed.
// uColVis = the visible column that was added or removed.
// Note: This must be called prior to there being any tileinfo in the listview containing
// a reference to this new column.
void CDefView::_ResetTileInfo(UINT uColVis, BOOL bAdded) { if (!_IsOwnerData()) { UINT rguColumns[TILEVIEWLINES];
for (int i = 0; i < ListView_GetItemCount(_hwndListview); i++) { UINT uColBoundary = uColVis; LVITEM lvi; lvi.mask = LVIF_COLUMNS | LVIF_NORECOMPUTE; lvi.iSubItem = 0; lvi.iItem = i; lvi.cColumns = ARRAYSIZE(rguColumns); lvi.puColumns = rguColumns;
if (!ListView_GetItem(_hwndListview, &lvi)) continue;
if ((lvi.cColumns == 0) || (lvi.cColumns == I_COLUMNSCALLBACK)) { continue; }
ASSERT(lvi.cColumns <= ARRAYSIZE(rguColumns)); // If for some reason listview has more, there's a problem
// guard just in case
if (lvi.cColumns > ARRAYSIZE(rguColumns)) { lvi.cColumns = ARRAYSIZE(rguColumns); }
UINT *puColumn = lvi.puColumns; BOOL bChange = FALSE;
// Adjust the column numbers as needed: up for added, down for removed.
int iIncDec = bAdded ? 1 : -1; if (!bAdded) { // What is this doing? If we've added a column X, we need to adjust columns
// from X on up. If we've removed a column X, we need to adjust columns from
// X+1 on up. So basically, instead of doing (*puColumn > uColBoundary), we're
// doing (*puColumn >= (uColBoundary+1)). So we can do the same ">=" expression
// whether or not bAdded, avoiding an if check in the loop.
uColBoundary++; }
for (UINT uCol = 0; uCol < lvi.cColumns; uCol++, puColumn++) { if (*puColumn >= uColBoundary) { (*puColumn) = (UINT)(iIncDec + (int)(*puColumn)); // Inc or dec.
bChange = TRUE; } }
if (bChange) // If there were any changes, set the ti back.
{ LVTILEINFO ti; ti.cbSize = sizeof(ti); ti.iItem = lvi.iItem; ti.cColumns = lvi.cColumns; ti.puColumns = lvi.puColumns; ListView_SetTileInfo(_hwndListview, &ti); } } } }
// Called when leaving tileview, this "cleans the slate" so that we reload the
// columns properly when re-entering tileview at a later time.
void CDefView::_RemoveTileInfo() { if (!_IsOwnerData()) { for (int i = 0; i < ListView_GetItemCount(_hwndListview); i++) { LVTILEINFO ti = {0}; ti.cbSize = sizeof(ti); ti.iItem = i; ti.cColumns = I_COLUMNSCALLBACK;
ListView_SetTileInfo(_hwndListview, &ti); } } }
// uCol is a real column number, not visible column number
// This method toggles the SHCOLSTATE_ONBYDEFAULT bit of the column,
// and adds or removes the column as necessary.
BOOL CDefView::_HandleColumnToggle(UINT uCol, BOOL bRefresh) { BOOL fWasOn = _IsColumnInListView(uCol); // if its off now, we are adding it
BOOL fWasDetailsColumn = _IsDetailsColumn(uCol);
UINT uColVisOld = _RealToVisibleCol(uCol);
_vs.SetColumnState(uCol, SHCOLSTATE_ONBYDEFAULT, fWasDetailsColumn ? 0 : SHCOLSTATE_ONBYDEFAULT);
BOOL fIsOn = _IsColumnInListView(uCol); // This could == fWasOn if it's a tileview column
UINT uColVis = _RealToVisibleCol(uCol);
if (fIsOn != fWasOn) { if (!fWasOn) { _AddColumnToListView(uCol, uColVis);
if (_fs.ViewMode == FVM_TILE) { _ResetTileInfo(uColVis, TRUE); } } else { _vs.RemoveColumn(uColVisOld); ListView_DeleteColumn(_hwndListview, uColVisOld);
if (_fs.ViewMode == FVM_TILE) { _ResetTileInfo(uColVisOld, FALSE); }
if (_vs._lParamSort == (int) uCol) { UINT iNewVis = _VisibleToRealCol(0); Rearrange(iNewVis); }
if (ListView_GetSelectedColumn(_hwndListview) == (UINT)uCol) ListView_SetSelectedColumn(_hwndListview, -1); } }
if (bRefresh) { ListView_RedrawItems(_hwndListview, 0, 0x7fff); InvalidateRect(_hwndListview, NULL, TRUE); UpdateWindow(_hwndListview); } return TRUE; }
// uCol = Real column number. uColVis = add it as this visible column.
void CDefView::_AddColumnToListView(UINT uCol, UINT uColVis) { LV_COLUMN col = {0};
// Adding a column
col.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM; col.fmt = _vs.GetColumnFormat(uCol); col.cx = _vs.GetColumnCharCount(uCol) * _cxChar; // Use default width
col.pszText = _vs.GetColumnName(uCol); col.cchTextMax = MAX_COLUMN_NAME_LEN; col.iSubItem = uCol; // not vis
// This is all odd... Find Files uses this, but i think it should be LVCFMT_COL_IMAGE
if (col.fmt & LVCFMT_COL_HAS_IMAGES) { ListView_SetExtendedListViewStyleEx(_hwndListview, LVS_EX_SUBITEMIMAGES, LVS_EX_SUBITEMIMAGES); col.fmt &= ~LVCFMT_COL_HAS_IMAGES; }
if (-1 != ListView_InsertColumn(_hwndListview, uColVis, &col)) { // now add it to our DSA
_vs.AppendColumn(uColVis, (USHORT) col.cx, uColVis);
if (!_fGroupView && (_vs._lParamSort == (int)uCol)) { ListView_SetSelectedColumn(_hwndListview, uColVis); } } }
void SetHeaderSort(HWND hwndHead, int iCol, UINT sortFlags) { HDITEM hdi = {HDI_FORMAT}; Header_GetItem(hwndHead, iCol, &hdi); hdi.fmt &= ~(HDF_SORTUP | HDF_SORTDOWN); hdi.fmt |= sortFlags; Header_SetItem(hwndHead, iCol, &hdi); }
void CDefView::_SetSortFeedback() { HWND hwndHead = ListView_GetHeader(_hwndListview);
// the _IsOwnerData() is bad. this keeps search from getting sort UI feedback.
// to fix this implement a mode where the sort has not been determined and thus we don't
// display any sort feedback. regular folders could use this too as after items have
// been added the view is not really sorted
if (!hwndHead || _IsOwnerData()) return;
BOOL fRemoveBitmapFromLastHeader = TRUE; int iColLast = _RealToVisibleCol(_vs._iLastColumnClick); int iCol = _RealToVisibleCol((UINT)_vs._lParamSort);
if (_fGroupView) { SetHeaderSort(hwndHead, iCol, 0); } else { ListView_SetSelectedColumn(_hwndListview, iCol);
SetHeaderSort(hwndHead, iCol, _vs._iDirection > 0 ? HDF_SORTUP : HDF_SORTDOWN);
// Only remove the bitmap if the last header is not the one we are currently sorting by
if (iColLast == iCol) fRemoveBitmapFromLastHeader = FALSE; }
if (fRemoveBitmapFromLastHeader && iColLast != -1) { SetHeaderSort(hwndHead, iColLast, 0); } }
// use the folder to compare two items, falling back if the lParam is not understood by
// that folder.
// 99/05/18 #341468 vtan: If the first comparison fails it may be because
// lParamSort is not understood by IShellFolder::CompareIDs (perhaps it's
// an extended column that might not be installed any more)
// In this case get the default comparison method
// and use that. If that fails use 0 which should hopefully not fail. If
// the 0 case fails we are toast with an assert.
HRESULT CDefView::_CompareIDsFallback(LPARAM lParam, LPCITEMIDLIST p1, LPCITEMIDLIST p2) { HRESULT hr = _pshf->CompareIDs(lParam, p1, p2); if (FAILED(hr)) { LPARAM lParamSort; _vs.GetDefaults(this, &lParamSort, NULL, NULL);
hr = _pshf->CompareIDs(lParamSort | (SHCIDS_ALLFIELDS & lParam), p1, p2); if (FAILED(hr)) { // even that did not work, fall back to zero based compare (pluse the all fields flag)
hr = _pshf->CompareIDs((SHCIDS_ALLFIELDS & lParam), p1, p2); } } return hr; }
// compare two items, taking into account the sort direction
int CDefView::_CompareIDsDirection(LPARAM lParam, LPCITEMIDLIST p1, LPCITEMIDLIST p2) { ASSERT(_vs._iDirection != 0); HRESULT hr = _CompareIDsFallback(lParam, (LPITEMIDLIST)p1, (LPITEMIDLIST)p2); return ShortFromResult(hr) * _vs._iDirection; }
// p1 and p2 are pointers to the lv_item's LPARAM, which is currently the pidl
int CALLBACK CDefView::_Compare(void *p1, void *p2, LPARAM lParam) { CDefView *pdv = (CDefView *)lParam; return pdv->_CompareIDsDirection(pdv->_vs._lParamSort, (LPITEMIDLIST)p1, (LPITEMIDLIST)p2); }
typedef struct { VARIANT var; BOOL fIsFolder; } VARIANT_AND_FOLDER;
typedef struct { VARIANT_AND_FOLDER *pvars; SHCOLUMNID scid; CDefView *pdv; } VARIANT_SORT_INFO;
int CALLBACK _CompareVariantCallback(LPARAM dw1, LPARAM dw2, LPARAM lParam) { VARIANT_SORT_INFO *psi = (VARIANT_SORT_INFO *)lParam;
int iRet = 0;
// Always put the folders first
if (psi->pvars[dw1].fIsFolder) { if (!psi->pvars[dw2].fIsFolder) iRet = -1; } else if (psi->pvars[dw2].fIsFolder) { iRet = 1; }
if (0 == iRet) { iRet = CompareVariants(psi->pvars[dw1].var, psi->pvars[dw2].var); }
return iRet * psi->pdv->_vs._iDirection; }
#define LV_NOFROZENITEM -1
HRESULT CDefView::_Sort(void) { HRESULT hr = CallCB(SFVM_ARRANGE, 0, _vs._lParamSort); if (FAILED(hr)) { hr = HRESULT_FROM_WIN32(ERROR_CAN_NOT_COMPLETE);
int iIndexRecycleBin = LV_NOFROZENITEM; POINT ptRecycleBin;
_SetSortFeedback();
// For desktop, we need to freeze the recycle bin position before we arrage other icons.
if (_fPositionRecycleBin) { iIndexRecycleBin = _FreezeRecycleBin(&ptRecycleBin); _fPositionRecycleBin = FALSE; }
// This is semi-bogus for defview to care whether the column is extended or not.
// We could have modified the ISF::CompareIDs() to handle extended columns, but
// then it would only have the pidls, and would have to re-extract any data, so
// its much faster if we separate out the extended columns, and take advantage
// of listview's caching abilities.
DWORD dwState = _DefaultColumnState((UINT)_vs._lParamSort); SHCOLUMNID scid; HRESULT hrMapColumn = E_FAIL; if (_pshf2) hrMapColumn = _pshf2->MapColumnToSCID((UINT)_vs._lParamSort, &scid);
// SHCOLSTATE_PREFER_VARCMP tells us that the folder's CompareIDs()
// produces the same result as comparing the variants. this is an optimization
// for folders who's CompareIDs() are slow (bit bucket)
if (_IsOwnerData() || (dwState & (SHCOLSTATE_EXTENDED | SHCOLSTATE_PREFER_VARCMP))) { if (_GetBackgroundTaskCount(TOID_DVBackgroundEnum) == 0) { int cItems = ListView_GetItemCount(_hwndListview); if (cItems) { VARIANT_SORT_INFO vsi; BOOL fOkToProceed = TRUE; if ((UINT)_vs._lParamSort == 0) { vsi.scid = SCID_NAME; } else if (SUCCEEDED(hrMapColumn)) { vsi.scid = scid; } else { fOkToProceed = FALSE; hr = hrMapColumn; }
if (fOkToProceed) { vsi.pvars = new VARIANT_AND_FOLDER[cItems]; if (vsi.pvars) { vsi.pdv = this;
for (int i = 0; i < cItems; i++) { LPCITEMIDLIST pidl = _GetPIDL(i); if (pidl) { DWORD dwAttrib = SHGetAttributes(_pshf, pidl, SFGAO_FOLDER); vsi.pvars[i].fIsFolder = dwAttrib & SFGAO_FOLDER; if ((UINT)_vs._lParamSort == 0) // This is the NAME column
{ WCHAR szName[MAX_PATH]; if (SUCCEEDED(DisplayNameOf(_pshf, pidl, SHGDN_INFOLDER | SHGDN_NORMAL, szName, ARRAYSIZE(szName)))) { InitVariantFromStr(&vsi.pvars[i].var, szName); } } else { _pshf2->GetDetailsEx(pidl, &vsi.scid, &vsi.pvars[i].var); } } }
hr = CallCB(SFVM_SORTLISTDATA, (LPARAM)_CompareVariantCallback, (LPARAM)&vsi);
// dont send a LVM_SORTITEMS to an ownerdraw or comctl32 will rip
if (FAILED(hr) && !_IsOwnerData() && ListView_SortItemsEx(_hwndListview, _CompareVariantCallback, (LPARAM)&vsi)) hr = S_OK;
for (int i = 0; i < cItems; i++) { VariantClear(&vsi.pvars[i].var); }
delete [] vsi.pvars; } } } } } else { ASSERT(!_IsOwnerData()) // dont send a LVM_SORTITEMS to an ownerdraw or comctl32 will rip
if (ListView_SortItems(_hwndListview, _Compare, (LPARAM)this)) hr = S_OK; }
//If we froze recycle-bin earlier, now is the time to put it in it's default position.
if (iIndexRecycleBin != LV_NOFROZENITEM) _SetRecycleBinInDefaultPosition(&ptRecycleBin); } return hr; }
// this should NOT check for whether the item is already in the listview
// if it does, we'll have some serious performance problems
int CDefView::_AddObject(LPITEMIDLIST pidl) // takes ownership of pidl.
{ int iItem = -1;
// Check the commdlg hook to see if we should include this
// object.
if ((S_OK == _IncludeObject(pidl)) && (S_FALSE != CallCB(SFVM_INSERTITEM, 0, (LPARAM)pidl))) { LV_ITEM item = {0};
item.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM | LVIF_COLUMNS; item.iItem = INT_MAX; // add at end
item.iImage = I_IMAGECALLBACK; item.pszText = LPSTR_TEXTCALLBACK; item.lParam = (LPARAM)pidl; // Takes pidl ownership.
item.cColumns = I_COLUMNSCALLBACK; // REVIEW: why not fill this in like the _UpdateObject call? That would fix the problem where GroupBy doesn't keep the "Searching UI" going...
iItem = ListView_InsertItem(_hwndListview, &item);
if (iItem < 0) { ILFree(pidl); } else if (_bBkFilling) { _pEnumTask->_AddToPending(pidl); }
_OnContentsChanged(); if (iItem == 0) { _PostNoItemStateChangedMessage(); } } else { ILFree(pidl); }
return iItem; }
// Find an item in the view
int CDefView::_FindItem(LPCITEMIDLIST pidl, LPITEMIDLIST *ppidlFound, BOOL fSamePtr, BOOL fForwards) { RIP(ILFindLastID(pidl) == pidl);
int cItems = ListView_GetItemCount(_hwndListview); if (_iLastFind >= cItems) _iLastFind = 0;
int iItem = _iLastFind; if (SUCCEEDED(CallCB(SFVM_INDEXOFITEMIDLIST, (WPARAM)&iItem, (LPARAM)pidl))) { if (ppidlFound) *ppidlFound = (LPITEMIDLIST)_GetPIDL(iItem); // cast as caller knows how to free this
} else { iItem = -1; // assume failure
for (int cCounter = 0, i = _iLastFind; cCounter < cItems; cCounter++) { LPCITEMIDLIST pidlT = _GetPIDL(i); ASSERT(pidlT); if (pidlT) { if ((pidlT == pidl) || (!fSamePtr && (0 == ResultFromShort(_pshf->CompareIDs(0, pidl, pidlT))))) { if (ppidlFound) *ppidlFound = (LPITEMIDLIST)pidlT; // cast as callers know how to free
_iLastFind = iItem = i; // success
// TraceMsg(TF_DEFVIEW, "####FIND CACHE RESULT --- %s by %d", cCounter < iItem ? TEXT("WIN") : TEXT("LOSE"), iItem - cCounter);
break; } }
if (fForwards) { i = (i+1)%cItems; } else { i = (i > 0)?(i - 1):(cItems-1); } }
if (-1 == iItem) { _iLastFind = 0; // didn't find it, reset this for next time
} } return iItem; }
int CDefView::_FindItemHint(LPCITEMIDLIST pidl, int iItem) { _iLastFind = iItem; return _FindItem(pidl, NULL, FALSE, FALSE); }
// 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 CDefView::_RemoveObject(LPCITEMIDLIST pidl, BOOL fSamePtr) { int i = 0;
// 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) { CallCB(SFVM_DELETEITEM, 0, 0); // view callback notify
ListView_DeleteAllItems(_hwndListview);
_PostNoItemStateChangedMessage(); _OnContentsChanged(); } else { // Non null go look for item.
i = _FindItem(pidl, NULL, fSamePtr); if (i >= 0) { RECT rc; UINT uState = ListView_GetItemState(_hwndListview, i, LVIS_ALL);
if (uState & LVIS_FOCUSED) ListView_GetItemRect(_hwndListview, i, &rc, LVIR_ICON);
if (_bBkFilling) _pEnumTask->_DeleteFromPending(pidl); // removes the pointer from the pending list.
ListView_DeleteItem(_hwndListview, i);
// we deleted the focused item.. replace the focus to the nearest item.
if (uState & LVIS_FOCUSED) { int iFocus = i; if (_IsPositionedView() || _fGroupView) { LV_FINDINFO lvfi = {0};
lvfi.flags = LVFI_NEARESTXY; lvfi.pt.x = rc.left; lvfi.pt.y = rc.top; iFocus = ListView_FindItem(_hwndListview, -1, &lvfi); } else { if (ListView_GetItemCount(_hwndListview) >= iFocus) iFocus--; }
if (iFocus != -1) { ListView_SetItemState(_hwndListview, iFocus, LVIS_FOCUSED, LVIS_FOCUSED); ListView_EnsureVisible(_hwndListview, iFocus, FALSE); } else { // RAID 372130
// Notify image preview control to update its image (to
// nothing). The image preview control uses focus change
// events to track when it should update the image it is
// displaying. When it receives a focus change event, it
// queries the listview to see which item has focus, then
// displays that item in the image preview window. When
// the last item in the listview is deleted, we need to
// fire a focus change event to the image preview control
// even though the focus has not changed to another item.
// This way, the image preview control realizes there is
// nothing with focus, and correctly displays as empty.
if (_fs.ViewMode == FVM_THUMBSTRIP) _ThumbstripSendImagePreviewFocusChangeEvent(); } }
// Notify automation if the listview is now empty
UINT uCount = 0; GetObjectCount(&uCount); if (!uCount) { _PostNoItemStateChangedMessage(); } _OnContentsChanged(); } } return i; }
// search the list for a match of the first pidl. If a match is found,
// the item is updated to the second pidl...
int CDefView::_UpdateObject(LPCITEMIDLIST pidlOld, LPCITEMIDLIST pidlNew) { LPITEMIDLIST pidlOldToFree; int i = _FindItem(pidlOld, &pidlOldToFree, FALSE); if (i >= 0) { if (_IsOwnerData()) { if (SUCCEEDED(CallCB(SFVM_SETITEMIDLIST, i, (LPARAM)pidlNew))) { // Invalidate the rectangle so we update the item...
RECT rc; ListView_GetItemRect(_hwndListview, i, &rc, LVIR_BOUNDS); InvalidateRect(_hwndListview, &rc, FALSE);
ListView_Update(_hwndListview, i); _OnContentsChanged(); } else { i = -1; // we failed, try to cleanup and bail.
} } else { LPITEMIDLIST pidlNewClone = ILClone(pidlNew); if (pidlNewClone) { LV_ITEM item = {0};
// 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.pszText = LPSTR_TEXTCALLBACK; item.iImage = I_IMAGECALLBACK; item.lParam = (LPARAM)pidlNewClone;
// if selected, deselect it
UINT uState = ListView_GetItemState(_hwndListview, i, LVIS_FOCUSED|LVIS_SELECTED); if (uState & (LVIS_FOCUSED|LVIS_SELECTED)) { _OnLVSelectionChange(i, uState, 0, (LPARAM)pidlOldToFree); }
// remove the item.
CallCB(SFVM_DELETEITEM, 0, (LPARAM)pidlOldToFree);
// now insert it with a new pidl
CallCB(SFVM_INSERTITEM, 0, (LPARAM)pidlNewClone);
// if it was selected, select it again
if (uState & (LVIS_FOCUSED|LVIS_SELECTED)) { _OnLVSelectionChange(i, 0, uState, (LPARAM)pidlNewClone); }
if (_fGroupView) { item.mask |= LVIF_GROUPID; item.iGroupId = (int)_GetGroupForItem(item.iItem, pidlNewClone); }
ListView_SetItem(_hwndListview, &item);
if (_bBkFilling) { _pEnumTask->_DeleteFromPending(pidlOld); _pEnumTask->_AddToPending(pidlNewClone); }
int cCols = _GetHeaderCount(); for (item.iSubItem++; item.iSubItem < cCols; item.iSubItem++) { ListView_SetItemText(_hwndListview, item.iItem, item.iSubItem, LPSTR_TEXTCALLBACK); }
//
// Warning!!! Only free pidlOldToFree *after* calling ListView_SetItem. ListView_SetItem
// can call back asking for image info on the old pidl!
//
// Now delete the item but don't call the callback since we did that already.
OnListViewDelete(i, pidlOldToFree, FALSE);
_OnContentsChanged(); } else { i = -1; } } } return i; }
//
// invalidates all items with the given image index.
//
// or update all items if iImage == -1
//
void CDefView::_UpdateImage(int iImage) { // -1 means update all
// reset the imagelists incase the size has changed, and do
// a full update.
if (iImage == -1) { if (_IsImageMode()) { _RemoveThumbviewTasks(); _pImageCache->Flush(TRUE); _SetThumbview(); } else if (_IsTileMode()) { _SetTileview(); } else { _SetSysImageList(); }
_ReloadContent(); } else { // get a dc so we can optimize for visible/not visible cases
HDC hdcLV = GetDC(_hwndListview);
// scan the listview updating any items which match
LV_ITEM item = {0}; int cItems = ListView_GetItemCount(_hwndListview); for (item.iItem = 0; item.iItem < cItems; item.iItem++) { item.mask = LVIF_IMAGE | LVIF_PARAM | LVIF_NORECOMPUTE;
ListView_GetItem(_hwndListview, &item); int iImageOld = item.iImage;
if (item.iImage == iImage) // this filters I_IMAGECALLBACK for us
{ RECT rc; LPCITEMIDLIST pidl = _GetPIDLParam(item.lParam, item.iItem);
CFSFolder_UpdateIcon(_pshf, pidl);
//
// if the item is visible then we don't want to flicker so just
// kick off an async extract. if the item is not visible then
// leave it for later by slamming in I_IMAGECALLBACK.
//
item.iImage = I_IMAGECALLBACK;
if (!_IsImageMode() && ListView_GetItemRect(_hwndListview, item.iItem, &rc, LVIR_ICON) && RectVisible(hdcLV, &rc)) { int iImageNew; HRESULT hr = _GetIconAsync(pidl, &iImageNew, FALSE);
if (hr == S_FALSE) continue;
if (SUCCEEDED(hr)) { if (iImageNew == iImageOld) { ListView_RedrawItems(_hwndListview, item.iItem, item.iItem); continue; }
item.iImage = iImageNew; } }
item.mask = LVIF_IMAGE; item.iSubItem = 0; ListView_SetItem(_hwndListview, &item); } }
ReleaseDC(_hwndListview, hdcLV); } }
// 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 CDefView::_RefreshObject(LPITEMIDLIST *ppidl) { int i = _FindItem(ppidl[0], NULL, FALSE); if (i >= 0) ListView_RedrawItems(_hwndListview, i, i); return i; }
HRESULT CDefView::_GetItemObjects(LPCITEMIDLIST **ppidl, UINT uWhat, UINT *pcItems) { *pcItems = _GetItemArray(NULL, 0, uWhat); if (ppidl) { *ppidl = NULL; if (*pcItems) { *ppidl = (LPCITEMIDLIST *)LocalAlloc(LPTR, sizeof(*ppidl) * (*pcItems)); if (*ppidl) _GetItemArray(*ppidl, *pcItems, uWhat); else return E_OUTOFMEMORY; } } return S_OK; }
void CDefView::_SetItemPosition(int i, int x, int y) { ListView_SetItemPosition32(_hwndListview, i, x, y); _fUserPositionedItems = TRUE; }
void CDefView::_SetItemPos(LPSFV_SETITEMPOS psip) { int i = _FindItem(psip->pidl, NULL, FALSE); if (i >= 0) { _SetItemPosition(i, psip->pt.x, psip->pt.y); } }
// "View State" here refers to column information and icon positions
BOOL CDefView::GetViewState() { BOOL bRet = FALSE;
IPropertyBag* ppb; if (SUCCEEDED(IUnknown_QueryServicePropertyBag(_psb, SHGVSPB_FOLDER, IID_PPV_ARG(IPropertyBag, &ppb)))) { DWORD dw; // Check if we've saved state before (first check) or if we may want to
// try upgrading some settings if we haven't saved state before (second check)
if (SUCCEEDED(SHPropertyBag_ReadDWORD(ppb, VS_PROPSTR_MODE, &dw)) || SUCCEEDED(SHPropertyBag_ReadDWORD(ppb, VS_PROPSTR_FFLAGS, &dw))) { bRet = SUCCEEDED(_vs.LoadFromPropertyBag(this, ppb)); } else { IStream* pstm; if (SUCCEEDED(_LoadGlobalViewState(&pstm))) { _vs.LoadFromStream(this, pstm); bRet = TRUE; pstm->Release(); } } ppb->Release(); } else {
// 99/02/05 #226140 vtan: Try to get the view state stream
// from ShellBrowser. If that fails then look for a global
// view state stream that is stored when the user clicks on
// the "Like Current Folder" in the View tab of folder settings.
// IShellBrowser::GetViewStateStream() match the dwDefRevCount
// of the cabinet state to make sure that it's valid.
IStream *pstm; if (SUCCEEDED(_psb->GetViewStateStream(STGM_READ, &pstm)) || SUCCEEDED(_LoadGlobalViewState(&pstm))) { _vs.LoadFromStream(this, pstm);
pstm->Release(); bRet = TRUE; } }
return bRet; }
void CDefView::_UpdateEnumerationFlags() { SHELLSTATE ss; SHGetSetSettings(&ss, SSF_SHOWALLOBJECTS | SSF_SHOWCOMPCOLOR, FALSE); _fShowAllObjects = ss.fShowAllObjects;
// Don't allow compression coloring on the desktop proper
_fShowCompColor = _IsDesktop() ? FALSE : ss.fShowCompColor; }
// starts and stops the spinning Globe animation
// indicating that we are in the process of navigating to
// a directory
void CDefView::_GlobeAnimation(BOOL fStartSpinning, BOOL fForceStop) { if (_fGlobeCanSpin) { DWORD dwCmdID = 0;
if (fStartSpinning) { if (_crefGlobeSpin++ == 0) { dwCmdID = CBRANDIDM_STARTGLOBEANIMATION; } } else { ASSERT(_crefGlobeSpin > 0);
if (fForceStop || (--_crefGlobeSpin == 0)) { dwCmdID = CBRANDIDM_STOPGLOBEANIMATION;
// our navigation is over, never spin again
_fGlobeCanSpin = FALSE; } }
if (dwCmdID) { IUnknown_QueryServiceExec(_psb, SID_SBrandBand, &CGID_BrandCmdGroup, dwCmdID, 0, NULL, NULL); } } }
LRESULT SearchingUIWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { case GET_WM_CTLCOLOR_MSG(CTLCOLOR_STATIC): SetBkColor(GET_WM_CTLCOLOR_HDC(wParam, lParam, uMsg), GetSysColor(COLOR_WINDOW)); return (LRESULT)GetSysColorBrush(COLOR_WINDOW);
default: return DefWindowProc(hWnd, uMsg, wParam, lParam); }
return 0; }
void CDefView::_ShowSearchUI(BOOL fStartSearchWindow) { if (_fAllowSearchingWindow || _crefSearchWindow) // once started, make sure our refcount finishes
{ if (fStartSearchWindow) { if (_crefSearchWindow++ == 0) { // The static window could already exist during a refresh
if (!_hwndStatic) { _hwndStatic = SHCreateWorkerWindowW((WNDPROC)SearchingUIWndProc, _hwndView, 0, WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS, NULL, NULL);
if (_hwndStatic) { HWND hAnimate = CreateWindowEx(0, ANIMATE_CLASS, c_szNULL, WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | ACS_TRANSPARENT | ACS_AUTOPLAY | ACS_CENTER, 0, 0, 0, 0, _hwndStatic, (HMENU)ID_STATIC, HINST_THISDLL, NULL); if (hAnimate) { RECT rc; GetClientRect(_hwndView, &rc); // Move this window to the top so the user sees the "looking" icon
// We are in a "normal" view. We need to do this always or the
// Flashlight doesn't appear. It tested safe with WebView on.
SetWindowPos(_hwndStatic, HWND_TOP, 0, 0, rc.right, rc.bottom, 0); SetWindowPos(hAnimate, HWND_TOP, 0, 0, rc.right, rc.bottom, SWP_NOZORDER); _OnMoveWindowToTop(_hwndStatic);
SetTimer(_hwndView, DV_IDTIMER_START_ANI, 2000, NULL); // 2 second timer
} } }
ShowHideListView(); } } else { if (0 == _crefSearchWindow) // if _ShowSearchUI(FALSE) gets called before _ShowSearchUI(TRUE)
{ _fAllowSearchingWindow = FALSE; } else if (--_crefSearchWindow == 0) { _fAllowSearchingWindow = FALSE;
ShowHideListView(); } } } }
// this is only called from within SHCNE_* don't put up ui on the enum error.
void CDefView::_FullViewUpdate(BOOL fUpdateItem) { if (fUpdateItem) _ReloadContent(); // the folder we're looking at has changed
else FillObjectsShowHide(FALSE); // our contents have changed
}
void CDefView::_ShowControl(UINT idControl, int idCmd) { IBrowserService *pbs; if (SUCCEEDED(_psb->QueryInterface(IID_PPV_ARG(IBrowserService, &pbs)))) { pbs->ShowControlWindow(idControl, idCmd); pbs->Release(); } }
// This function does three things:
// 1 - Alter the size of the parent to best fit around the items we have.
// 2 - Set the default icon view mode
// 3 - Make sure the correct toolbars are showing
//
void CDefView::_BestFit() { // Only bestfit once
if (_fs.fFlags & FWF_BESTFITWINDOW) { _fs.fFlags &= ~FWF_BESTFITWINDOW;
// Make sure the correct toolbars are showing the first time this folder is displayed
//
int iITbar = SBSC_HIDE; int iStdBar = SBSC_HIDE; switch (_uDefToolbar) { case HIWORD(TBIF_INTERNETBAR): iITbar = SBSC_SHOW; goto ShowToolbar;
case HIWORD(TBIF_STANDARDTOOLBAR): iStdBar = SBSC_SHOW; goto ShowToolbar;
case HIWORD(TBIF_NOTOOLBAR): ShowToolbar: _ShowControl(FCW_INTERNETBAR, iITbar); _ShowControl(FCW_TOOLBAR, iStdBar); break; }
} }
void CDefView::_ClearPostedMsgs(HWND hwnd) { MSG msg;
while (PeekMessage(&msg, hwnd, WM_DSV_UPDATEICON, WM_DSV_UPDATEICON, PM_REMOVE)) { // PeekMessage(hwnd) can return messages posted to CHILDREN of this hwnd...
// Verify that the message was really for us.
//
if (msg.hwnd == hwnd) { TraceMsg(TF_DEFVIEW, "DefView: WM_DSV_UPDATEICON after WM_DESTROY!!!"); LPITEMIDLIST pidl = (LPITEMIDLIST) msg.wParam; ILFree(pidl); } }
while (PeekMessage(&msg, hwnd, WM_DSV_UPDATECOLDATA, WM_DSV_UPDATECOLDATA, PM_REMOVE)) { // PeekMessage(hwnd) can return messages posted to CHILDREN of this hwnd...
// Verify that the message was really for us.
//
if (msg.hwnd == hwnd) { TraceMsg(TF_DEFVIEW, "DefView: WM_DSV_UPDATECOLDATA after WM_DESTROY!!!"); delete (CBackgroundColInfo*)msg.lParam; } } while (PeekMessage(&msg, hwnd, WM_DSV_DELAYSTATUSBARUPDATE, WM_DSV_DELAYSTATUSBARUPDATE, PM_REMOVE)) { if (msg.hwnd == hwnd) { LocalFree((void *)msg.lParam); } }
while (PeekMessage(&msg, hwnd, WM_DSV_SETIMPORTANTCOLUMNS, WM_DSV_SETIMPORTANTCOLUMNS, PM_REMOVE)) { // PeekMessage(hwnd) can return messages posted to CHILDREN of this hwnd...
// Verify that the message was really for us.
//
if (msg.hwnd == hwnd) { TraceMsg(TF_DEFVIEW, "DefView: WM_DSV_SETIMPORTANTCOLUMNS after WM_DESTROY!!!"); delete (CBackgroundTileInfo*)msg.lParam; } }
while (PeekMessage(&msg, hwnd, WM_DSV_SETITEMGROUP, WM_DSV_SETITEMGROUP, PM_REMOVE)) { // PeekMessage(hwnd) can return messages posted to CHILDREN of this hwnd...
// Verify that the message was really for us.
//
if (msg.hwnd == hwnd) { TraceMsg(TF_DEFVIEW, "DefView: WM_DSV_SETITEMGROUP after WM_DESTROY!!!"); delete (CBackgroundGroupInfo*)msg.lParam; } }
while (PeekMessage(&msg, hwnd, WM_DSV_UPDATETHUMBNAIL, WM_DSV_UPDATETHUMBNAIL, PM_REMOVE)) { // PeekMessage(hwnd) can return messages posted to CHILDREN of this hwnd...
// Verify that the message was really for us.
//
if (msg.hwnd == hwnd) { TraceMsg(TF_DEFVIEW, "DefView: WM_DSV_UPDATETHUMBNAIL after WM_DESTROY!!!"); _CleanupUpdateThumbnail((DSV_UPDATETHUMBNAIL*)msg.lParam); } }
while (PeekMessage(&msg, hwnd, WM_DSV_POSTCREATEINFOTIP, WM_DSV_POSTCREATEINFOTIP, PM_REMOVE)) { // PeekMessage(hwnd) can return messages posted to CHILDREN of this hwnd...
// Verify that the message was really for us.
//
if (msg.hwnd == hwnd) { TraceMsg(TF_DEFVIEW, "DefView: WM_DSV_POSTCREATEINFOTIP after WM_DESTROY!!!"); _OnPostCreateInfotipCleanup((TOOLINFO *)msg.wParam); } } }
void CDefView::_CallRefresh(BOOL fPreRefresh) { if (fPreRefresh) { IUnknown_Exec(_pshf, NULL, OLECMDID_REFRESH, 0, NULL, NULL); }
CallCB(SFVM_REFRESH, fPreRefresh, 0); }
void CDefView::FillDone() { SendMessage(_hwndListview, WM_SETREDRAW, (WPARAM)FALSE, 0); _fListviewRedraw = TRUE;
AddRef(); // hold a ref to ourself while in this function.
_fAllowSearchingWindow = FALSE;
_PostFillDoneMessage();
if (_bBkFilling) _OnStopBackgroundEnum();
HRESULT hr = _pEnumTask->FillObjectsDoneToView(); _pEnumTask->Release(); _pEnumTask = NULL;
if (SUCCEEDED(hr)) { // Clear our error state, if we were in one
_fEnumFailed = FALSE;
if (_fSyncOnFillDone) { _vs.Sync(this, TRUE); _fSyncOnFillDone = FALSE; }
ShowHideListView();
// set the focus on the first item.
_FocusOnSomething();
_DoThumbnailReadAhead(); } else { // The fill objects failed for some reason, go into error mode
TraceMsg(TF_WARNING, "::FillObjects failed to enumerate for some reason"); _fEnumFailed = TRUE; ShowHideListView(); }
// Tell the defview client that this window has been refreshed
_CallRefresh(FALSE); _OnContentsChanged();
_UpdateStatusBar(TRUE); _PostEnumDoneMessage();
Release();
SendMessage(_hwndListview, WM_SETREDRAW, (WPARAM)TRUE, 0); _fListviewRedraw = FALSE; }
HRESULT CDefView::_OnStartBackgroundEnum() { _GlobeAnimation(TRUE); _ShowSearchUI(TRUE); _bBkFilling = TRUE;
return S_OK; }
HRESULT CDefView::_OnStopBackgroundEnum() { _bBkFilling = FALSE; _GlobeAnimation(FALSE); _ShowSearchUI(FALSE);
return S_OK; }
HRESULT CDefView::_OnBackgroundEnumDone() { FillDone();
_UpdateStatusBar(FALSE);
CallCB(SFVM_BACKGROUNDENUMDONE, 0, 0);
return S_OK; }
HRESULT EmptyBkgrndThread(IShellTaskScheduler *pScheduler) { HRESULT hr = S_OK;
if (pScheduler) { // empty the queue and wait until it is empty.....
hr = pScheduler->RemoveTasks(TOID_NULL, ITSAT_DEFAULT_LPARAM, TRUE); } return hr; }
DWORD CDefView::_GetEnumFlags() { // Setup the enum flags.
DWORD grfEnumFlags = SHCONTF_NONFOLDERS; if (_fShowAllObjects) grfEnumFlags |= SHCONTF_INCLUDEHIDDEN;
//Is this View in Common Dialog
if (!(grfEnumFlags & SHCONTF_INCLUDEHIDDEN)) { // Ask Common dialog if its wants to show all files
ICommDlgBrowser2 *pcdb2; if (SUCCEEDED(_psb->QueryInterface(IID_PPV_ARG(ICommDlgBrowser2, &pcdb2)))) { DWORD dwFlags = 0; pcdb2->GetViewFlags(&dwFlags); if (dwFlags & CDB2GVF_SHOWALLFILES) grfEnumFlags |= SHCONTF_INCLUDEHIDDEN; pcdb2->Release(); } }
if (!(_fs.fFlags & FWF_NOSUBFOLDERS)) grfEnumFlags |= SHCONTF_FOLDERS;
return grfEnumFlags; }
HRESULT CDefView::FillObjectsShowHide(BOOL fInteractive) { HRESULT hr = S_OK;
DECLAREWAITCURSOR; SetWaitCursor(); // This is a potentially long operation
// To get here we're either not enumerating at all,
// or we are enumerating on the background thread,
// or we got re-entered
ASSERT((!_pEnumTask&&!_bBkFilling) || (_pEnumTask)); if (_pEnumTask) { if (fInteractive) { // This is in response to the user pressing F5,
// assume the current enumeration will be valid
hr = S_FALSE; } else if (!_bBkFilling) { // We're not on the background but we have a _pEnumTask, this means
// that we got re-entered during the below call to FillObjectsToDPA.
// Assume the current enumeration attempt will be valid
hr = S_FALSE; } else { if (_pScheduler) { // An UPDATEDIR or equivalent happened, anything already enumerated could be bad.
// Tell the current enumeration task to give up
_pScheduler->RemoveTasks(TOID_DVBackgroundEnum, ITSAT_DEFAULT_LPARAM, FALSE); _pScheduler->RemoveTasks(TOID_DVBackgroundGroup, ITSAT_DEFAULT_LPARAM, TRUE); }
ASSERT(_bBkFilling); _OnStopBackgroundEnum();
_pEnumTask->Release(); _pEnumTask = NULL; } }
if (S_OK==hr) { _pEnumTask = new CDefviewEnumTask(this, ++_dwEnumId); if (_pEnumTask) { // Note: It is possible for us to get re-entered during FillObjectsToDPA,
// since we pass our HWND to the enumerator.
_pEnumTask->FillObjectsToDPA(fInteractive); hr = _pEnumTask->FillObjectsDPAToDone(); } else { _fEnumFailed = TRUE; ShowHideListView();
hr = E_OUTOFMEMORY; } }
ResetWaitCursor(); return hr; }
// 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
HRESULT CDefView::_ExplorerCommand(UINT idFCIDM) { HRESULT hr = E_FAIL;
static struct { UINT idmFC; UINT fBackground; 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 }, };
for (int i = 0; i < ARRAYSIZE(c_idMap); i++) { if (c_idMap[i].idmFC == idFCIDM) { IContextMenu *pcm;
if (c_idMap[i].fBackground == EC_BACKGROUND) { hr = _pshf->CreateViewObject(_hwndMain, IID_PPV_ARG(IContextMenu, &pcm)); } else { hr = _CreateSelectionContextMenu(IID_PPV_ARG(IContextMenu, &pcm)); if (FAILED(hr) && (c_idMap[i].fBackground == EC_EITHER) && !ListView_GetSelectedCount(_hwndListview)) { hr = _pshf->CreateViewObject(_hwndMain, IID_PPV_ARG(IContextMenu, &pcm)); } }
if (SUCCEEDED(hr)) { CMINVOKECOMMANDINFOEX ici = {0};
ici.cbSize = sizeof(ici); ici.hwnd = _hwndMain; ici.nShow = SW_NORMAL;
// record if shift or control was being held down
SetICIKeyModifiers(&ici.fMask);
// 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]; SHUnicodeToAnsi(c_idMap[i].pszVerb, szVerbAnsi, ARRAYSIZE(szVerbAnsi)); ici.lpVerb = szVerbAnsi; ici.lpVerbW = c_idMap[i].pszVerb; ici.fMask |= CMIC_MASK_UNICODE;
HMENU hmenu = CreatePopupMenu(); if (hmenu) { IUnknown_SetSite(pcm, SAFECAST(this, IOleCommandTarget *));
pcm->QueryContextMenu(hmenu, 0, CONTEXTMENU_IDCMD_FIRST, CONTEXTMENU_IDCMD_LAST, 0);
_bContextMenuMode = TRUE;
hr = _InvokeContextMenu(pcm, &ici);
_bContextMenuMode = FALSE;
DestroyMenu(hmenu);
IUnknown_SetSite(pcm, NULL); }
pcm->Release(); } else { // keys are pressed when there is no selection.
MessageBeep(0); } break; } ASSERT(i < ARRAYSIZE(c_idMap)); }
return hr; }
STDAPI_(BOOL) Def_IsPasteAvailable(IDropTarget *pdtgt, DWORD *pdwEffect);
BOOL CDefView::_AllowCommand(UINT uID) { DWORD dwAttribsIn; DWORD dwEffect;
switch (uID) { case SFVIDM_EDIT_PASTE: return Def_IsPasteAvailable(_pdtgtBack, &dwEffect);
case SFVIDM_EDIT_PASTELINK: Def_IsPasteAvailable(_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 _AttributesFromSel(dwAttribsIn) & dwAttribsIn; }
// return copy of pidl of folder we're viewing
LPITEMIDLIST CDefView::_GetViewPidl() { LPITEMIDLIST pidl; if (SHGetIDListFromUnk(_pshf, &pidl) != S_OK) // S_FALSE is success by empty
{ if (SUCCEEDED(CallCB(SFVM_THISIDLIST, 0, (LPARAM)&pidl))) { ASSERT(pidl); } else if (_SetupNotifyData() && _pidlMonitor) { pidl = ILClone(_pidlMonitor); } } return pidl; }
inline BOOL CDefView::_ItemsDeferred() { return _hdsaSelect != NULL; }
BOOL CDefView::_IsListviewVisible() { return _fListViewShown; }
inline BOOL CDefView::_IsOwnerData() { return _fs.fFlags & FWF_OWNERDATA; }
inline BOOL CDefView::_IsCommonDialog() { return NULL != _pcdb; }
BOOL CDefView::_IsDesktop() { return _fs.fFlags & FWF_DESKTOP; }
BOOL CDefView::_IsViewDesktop() { BOOL bDesktop = FALSE; LPITEMIDLIST pidl = _GetViewPidl(); if (pidl) { bDesktop = ILIsEmpty(pidl); ILFree(pidl); } return bDesktop; }
// access to the current views name ala IShellFolder::GetDisplayNameOf()
HRESULT CDefView::_GetNameAndFlags(UINT gdnFlags, LPTSTR pszPath, UINT cch, DWORD *pdwFlags) { *pszPath = 0;
HRESULT hr; LPITEMIDLIST pidl = _GetViewPidl(); if (pidl) { hr = SHGetNameAndFlags(pidl, gdnFlags, pszPath, cch, pdwFlags); ILFree(pidl); } else hr = E_OUTOFMEMORY; return hr; }
// returns TRUE if the current view is a file system folder, returns the path
BOOL CDefView::_GetPath(LPTSTR pszPath) { *pszPath = 0;
LPITEMIDLIST pidl = _GetViewPidl(); if (pidl) { SHGetPathFromIDList(pidl, pszPath); ILFree(pidl); } return *pszPath != 0; }
EXTERN_C TCHAR const c_szHtmlWindowsHlp[] = TEXT("windows.chm");
// web view background colors, click mode, etc have changed
//
void CDefView::_UpdateListviewColors() { // First clear out our state
for (int i = 0; i < ARRAYSIZE(_crCustomColors); i++) _crCustomColors[i] = CLR_MYINVALID;
// Then read the registry/desktop.ini
LPCTSTR pszLegacyWatermark = NULL; SFVM_CUSTOMVIEWINFO_DATA cvi = {0}; if (SUCCEEDED(CallCB(SFVM_GETCUSTOMVIEWINFO, (WPARAM)0, (LPARAM)&cvi))) { if (!_IsCommonDialog() && !_IsDesktop()) { // Set up the listview image, if any
if (*cvi.szIconAreaImage) { pszLegacyWatermark = cvi.szIconAreaImage; }
// change the differing stuff
//
if (!_fClassic) { for (i = 0; i < ARRAYSIZE(_crCustomColors); i++) { COLORREF cr = cvi.crCustomColors[i]; if (ISVALIDCOLOR(cr)) { _crCustomColors[i] = PALETTERGB(0, 0, 0) | cr; } }
// if there was an image specified but no custom text background,
// set to CLR_NONE so the listview text is transparent
// get combined view custom colors
if (!ISVALIDCOLOR(_crCustomColors[CRID_CUSTOMTEXTBACKGROUND]) && cvi.szIconAreaImage[0]) { _crCustomColors[CRID_CUSTOMTEXTBACKGROUND] = CLR_NONE; } } } }
_SetLegacyWatermark(pszLegacyWatermark);
_SetFolderColors();
_UpdateSelectionMode(); }
BOOL CDefView::HasCurrentViewWindowFocus() { BOOL fRet = false; HWND hwndCurrentFocus = GetFocus(); if (hwndCurrentFocus) { fRet = (SHIsChildOrSelf(_hwndListview, hwndCurrentFocus) == S_OK); } return fRet; }
HWND CDefView::ViewWindowSetFocus() { SetFocus(_hwndListview); if (!_IsDesktop()) { _cFrame._uState = SVUIA_ACTIVATE_FOCUS; } return _hwndListview; }
HRESULT CDefView::_GetSFVMViewState(UINT uViewMode, SFVM_VIEW_DATA* pvi) { HRESULT hr = CallCB(SFVM_GETVIEWDATA, (WPARAM)uViewMode, (LPARAM)pvi); if (FAILED(hr)) { pvi->dwOptions = SFVMQVI_NORMAL; } return hr; } HRESULT CDefView::_GetSFVMViewInfoTemplate(UINT uViewMode, SFVM_WEBVIEW_TEMPLATE_DATA* pvit) { return CallCB(SFVM_GETWEBVIEW_TEMPLATE, (WPARAM)uViewMode, (LPARAM)pvit); }
HRESULT CDefView::_GetWebViewMoniker(LPWSTR pszMoniker, DWORD cchMoniker) { SFVM_WEBVIEW_TEMPLATE_DATA vit; if (SUCCEEDED(_GetSFVMViewInfoTemplate(_fs.ViewMode, &vit))) { StrCpyN(pszMoniker, vit.szWebView, cchMoniker); } else { pszMoniker[0] = L'\0'; } return *pszMoniker ? S_OK : E_FAIL; }
// Show or hide Web View content
//
// This does not affect the View Mode of the listview (it does tweak desktop listview for _fCombinedView stuff)
//
// fShow==TRUE -> hr is success/fail of showing web view
// fShow==FALSE -> hr is E_FAIL (nobody looks at return code of turning web view off)
//
HRESULT CDefView::_SwitchToWebView(BOOL fShow) { HRESULT hr = E_FAIL;
// Cache the focus/select state across this transition
BOOL bSetFocusRequired = HasCurrentViewWindowFocus();
if (fShow) { // For now, the desktop is always a combined view...
if (_IsDesktop()) { BOOL fCombinedViewOld = (BOOL)_fCombinedView; SHELLSTATE ss; SHGetSetSettings(&ss, SSF_HIDEICONS | SSF_DESKTOPHTML | SSF_STARTPANELON, FALSE);
// Does the user want desktop in HyperText view?
if (ss.fDesktopHTML) _fCombinedView = TRUE;
if (ss.fHideIcons) _fs.fFlags |= FWF_NOICONS; else _fs.fFlags &= ~FWF_NOICONS;
if (_fCombinedView && !fCombinedViewOld) { EnableCombinedView(this, TRUE); ListView_SetExtendedListViewStyleEx(_hwndListview, LVS_EX_REGIONAL, LVS_EX_REGIONAL); _SetFolderColors(); } }
WCHAR wszMoniker[MAX_PATH]; hr = _GetWebViewMoniker(wszMoniker, ARRAYSIZE(wszMoniker)); if (SUCCEEDED(hr)) { if (_IsDesktop()) { IActiveDesktopP *piadp; if (SUCCEEDED(SHCoCreateInstance(NULL, &CLSID_ActiveDesktop, NULL, IID_PPV_ARG(IActiveDesktopP, &piadp)))) { piadp->EnsureUpdateHTML(); piadp->Release(); } hr = _cFrame.ShowWebView(wszMoniker); } else if (SHRestricted(REST_REVERTWEBVIEWSECURITY)) { hr = _cFrame.ShowWebView(wszMoniker); } else if (!_fUserRejectedWebViewTemplate) { WCHAR szTemplate[MAX_PATH]; DWORD cchTemplate = ARRAYSIZE(szTemplate);
if (PathIsURL(wszMoniker)) { hr = PathCreateFromUrl(wszMoniker, szTemplate, &cchTemplate, 0); } else { StringCchCopy(szTemplate, ARRAYSIZE(szTemplate), wszMoniker); }
if (SUCCEEDED(hr)) { DWORD dwFlags = SHRVT_VALIDATE | SHRVT_ALLOW_INTRANET; if (SHRestricted(REST_ALLOWUNHASHEDWEBVIEW)) { dwFlags |= SHRVT_PROMPTUSER | SHRVT_REGISTERIFPROMPTOK; } hr = SHRegisterValidateTemplate(szTemplate, dwFlags); if (SUCCEEDED(hr)) { hr = _cFrame.ShowWebView(wszMoniker); } else { _fUserRejectedWebViewTemplate = TRUE; } } } }
if (FAILED(hr)) { fShow = FALSE; } else { RECT rcClient;
// Make sure the new view is the correct size
GetClientRect(_hwndView, &rcClient); _cFrame.SetRect(&rcClient);
ShowHideListView(); } }
if (!fShow) { _cFrame.HideWebView();
// If we were combined, then get the listview out of region mode and
// reset the color scheme. Also, turn off the combined bit.
if (_fCombinedView) { _fCombinedView = FALSE; ListView_SetExtendedListViewStyleEx(_hwndListview, LVS_EX_REGIONAL, 0); EnableCombinedView(this, FALSE); _SetFolderColors(); } }
// restore focus/select state -- if we switched to web view it will put much of
// this into a "pending" state until the listview is re-shown inside the web content
//
if (bSetFocusRequired) { CallCB(SFVM_SETFOCUS, 0, 0); ViewWindowSetFocus(); }
CheckToolbar(); _EnableDisableTBButtons();
// make sure that the listview settings get refreshed anyway (back image)
_UpdateListviewColors();
return hr; }
void CDefView::_RemoveThumbviewTasks() { if (_pScheduler) { _pScheduler->RemoveTasks(TOID_ExtractImageTask, ITSAT_DEFAULT_LPARAM, FALSE); _pScheduler->RemoveTasks(TOID_CheckCacheTask, ITSAT_DEFAULT_LPARAM, FALSE); _pScheduler->RemoveTasks(TOID_ReadAheadHandler, ITSAT_DEFAULT_LPARAM, FALSE); _fReadAhead = FALSE; } }
//
// This function checkes to see if the list view needs to be shown; then shows it.
// If it needs to be hidden, hides it! You must call this function every time
// you change a bit of state that could change the show/hide state of listview.
//
// Let me repeat that: call this function EVERY TIME you change state that
// affects our show/hide.
//
HRESULT CDefView::ShowHideListView() { // NOTE: this is where most of the flicker bugs come from -- showing the
// listview too early. This is touchy code, so be careful when you change it.
// And plese document all changes for future generations. Thanks.
//
// Standard "is listview shown" check
//
// If our view hasn't been UIActivate()d yet, then we are waiting until
// the IShellBrowser selects us as the active view.
//
// App compat for above UIActivate() change:
// Adaptec Easy CD Creator never calls IShellView::UIActivate.
// They got away with it because UIActivate didn't used to do much,
// but now we use UIActivate to decide when to show our icons. They forget
// to call it and the icons never show up.
// So if we are in Win95 Defview compatibility mode, then
// go ahead and show the icons now. The app gets flicker, but at least
// the icons show up at all.
//
// Don't show the listview if we're told to not show it, or we see an error during enum.
//
// If we're enumerating in the background, don't show
//
// Potential problem: We used to defer SelectPendingSelectedItems until:
// "_fListViewShown && (_cFrame._dwConnectionCookie /*&& !_cFrame._fReadyStateInteractiveProcessed*/)"
// Selecting before readystatedone may pose a problem, but I don't see how it could
// be a problem unless showing the view early is a problem as well, which this code didn't check.
//
if ((!_cFrame.IsWebView() || _fGetWindowLV || _fCombinedView) // we think icons should be visible
&& (_uState != SVUIA_DEACTIVATE || (SHGetAppCompatFlags(ACF_WIN95DEFVIEW) & ACF_WIN95DEFVIEW)) // async defview means we don't show before we transition out of DEACTIVE
&& !(BOOLIFY(_fs.fFlags & FWF_NOICONS)) // check if we've been told to not show icons
&& !_fEnumFailed // failed enumeration wants _hwndView to show through, not _hwndListview
&& !(_crefSearchWindow && _hwndStatic) // keep the listview hidden while we show the "searching" window
) { // Make sure we do each transition only once - we do more than just show the window
if (!_fListViewShown) { _fListViewShown = TRUE;
// Bring this to the top while showing it to avoid a second paint when
// _hwndStatic is destroyed (listview has optimizations when hidden,
// and it will repaint when once shown even if though it may be obscured)
//
SetWindowPos(_hwndListview, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE|SWP_SHOWWINDOW); _OnMoveWindowToTop(_hwndListview);
// Remove _hwndStatic after listview is moved to top to avoid a re-paint
if (_hwndStatic) { DestroyWindow(_hwndStatic); _hwndStatic = NULL; }
// if we need to select items, do it now that the window is shown
SelectPendingSelectedItems(); } } else { if (_fListViewShown) { _fListViewShown = FALSE; ShowWindow(_hwndListview, SW_HIDE); }
// If FWF_NOICONS is set and the enumertion went to the background thread we need
// to make sure that we turn of the searchui.
if (BOOLIFY(_fs.fFlags & FWF_NOICONS) && _hwndStatic && 0 == _crefSearchWindow) { DestroyWindow(_hwndStatic); _hwndStatic = NULL; } }
return S_OK; }
IShellItemArray* CDefView::_GetFolderAsShellItemArray() { if (!_pFolderShellItemArray && _pshfParent && _pidlRelative) { SHCreateShellItemArray(NULL, _pshfParent, 1, (LPCITEMIDLIST *)&_pidlRelative, &_pFolderShellItemArray); } return _pFolderShellItemArray; }
// if the attributes dwAttribMask for pdo exactly match dwAttribValue, this item should be enabled
HRESULT CDefView::_CheckAttribs(IShellItemArray *psiItemArray, DWORD dwAttribMask, DWORD dwAttribValue, UISTATE* puisState) { DWORD dwAttrib = 0; HRESULT hr;
if (NULL == psiItemArray) { psiItemArray = _GetFolderAsShellItemArray(); }
if (psiItemArray) { hr = psiItemArray->GetAttributes(SIATTRIBFLAGS_APPCOMPAT, dwAttribMask, &dwAttrib); if (FAILED(hr)) dwAttrib = 0; } else hr = S_OK;
*puisState = (dwAttribValue == dwAttrib) ? UIS_ENABLED : UIS_HIDDEN;
return hr; }
HRESULT CDefView::_CanWrite(IUnknown* pv, IShellItemArray *psiItemArray, BOOL fOkToBeSlow, UISTATE* puisState) { CDefView* pThis = (CDefView*)(void*)pv; return pThis->_CheckAttribs(psiItemArray, SFGAO_READONLY|SFGAO_STORAGE, SFGAO_STORAGE, puisState); } HRESULT CDefView::_CanRename(IUnknown* pv, IShellItemArray *psiItemArray, BOOL fOkToBeSlow, UISTATE* puisState) { CDefView* pThis = (CDefView*)(void*)pv; return pThis->_CheckAttribs(psiItemArray, SFGAO_CANRENAME, SFGAO_CANRENAME, puisState); } HRESULT CDefView::_CanMove(IUnknown* pv, IShellItemArray *psiItemArray, BOOL fOkToBeSlow, UISTATE* puisState) { CDefView* pThis = (CDefView*)(void*)pv; return pThis->_CheckAttribs(psiItemArray, SFGAO_CANMOVE, SFGAO_CANMOVE, puisState); } HRESULT CDefView::_CanCopy(IUnknown* pv, IShellItemArray *psiItemArray, BOOL fOkToBeSlow, UISTATE* puisState) { CDefView* pThis = (CDefView*)(void*)pv; return pThis->_CheckAttribs(psiItemArray,SFGAO_CANCOPY, SFGAO_CANCOPY, puisState); }
HRESULT CDefView::_CanPublish(IUnknown* pv, IShellItemArray *psiItemArray, BOOL fOkToBeSlow, UISTATE* puisState) { CDefView* pThis = (CDefView*)(void*)pv; *puisState = UIS_HIDDEN;
if ((pThis->_wvLayout.dwLayout & SFVMWVL_NOPUBLISH) || SHRestricted(REST_NOPUBLISHWIZARD)) { // bail out early with UIS_HIDDEN, we dont show the verb
return S_OK; }
// Iterate first 10 items because that is what old code did before
// switching to IShellItemArray. Since the attribs that are
// being requested for are already being cached in the ShellItemArray
// may as well always ask for all.
if (psiItemArray) { IEnumShellItems *pEnumShellItems; if (SUCCEEDED(psiItemArray->EnumItems(&pEnumShellItems))) { IShellItem *pShellItem; DWORD dwIterationCount = 0; BOOL fHide = FALSE, fHasStreams = FALSE, fHasStorages = FALSE;
while (!fHide && (dwIterationCount < 10) && (S_OK == pEnumShellItems->Next(1, &pShellItem, NULL))) { SFGAOF dwAttribs = SFGAO_STORAGE | SFGAO_STREAM; HRESULT hrAttribs = pShellItem->GetAttributes(dwAttribs, &dwAttribs);
pShellItem->Release(); pShellItem = NULL; // null to catch if we use it again.
if (SUCCEEDED(hrAttribs)) { if (!(dwAttribs & (SFGAO_STORAGE | SFGAO_STREAM))) { // if this item doesn't have either storage or stream, hide the task.
fHide = TRUE; } else if (dwAttribs & SFGAO_STREAM) { // if we have a folder and files, hide the task.
fHide = fHasStorages; fHasStreams = TRUE; } else if (dwAttribs & SFGAO_STORAGE) { // if we have multiple folders or a folder and files, hide the task.
fHide = fHasStorages || fHasStreams; fHasStorages = TRUE; } }
++dwIterationCount; }
if (!fHide) *puisState = UIS_ENABLED;
pEnumShellItems->Release(); } } else { // if nothing is selected, enable the task if the current folder is a storage.
LPITEMIDLIST pidl = pThis->_GetViewPidl(); if (pidl) { if (SHGetAttributes(NULL, pidl, SFGAO_STORAGE)) { *puisState = UIS_ENABLED; } ILFree(pidl); } }
return S_OK; }
// Note - _DoesStaticMenuHaveVerb only checks the first pidl in the data object for now
// So only use it for single-selections
// -DSheldon
BOOL CDefView::_DoesStaticMenuHaveVerb(IShellItemArray *psiItemArray, LPCWSTR pszVerb) { BOOL fHasVerb = FALSE; IShellItem *pshItem;
// get first shellItem in the array.
if (SUCCEEDED(psiItemArray->GetItemAt(0,&pshItem))) { IQueryAssociations* pqa; if (SUCCEEDED(pshItem->BindToHandler(NULL, BHID_SFUIObject, IID_PPV_ARG(IQueryAssociations, &pqa)))) { DWORD cch = 0; fHasVerb = SUCCEEDED(pqa->GetString(0, ASSOCSTR_COMMAND, pszVerb, NULL, &cch)); pqa->Release(); } pshItem->Release(); } return fHasVerb; }
HRESULT CDefView::_GetFullPathNameAt(IShellItemArray *psiItemArray,DWORD dwIndex,LPOLESTR *ppszPath) { HRESULT hr = E_FAIL; IShellItem *pShellItem;
if (NULL == psiItemArray || NULL == ppszPath) { ASSERT(psiItemArray); ASSERT(ppszPath);
return E_INVALIDARG; } // get the path of the first item in the ShellArray.
hr = psiItemArray->GetItemAt(dwIndex,&pShellItem);
if (SUCCEEDED(hr)) { hr = pShellItem->GetDisplayName(SIGDN_FILESYSPATH,ppszPath); pShellItem->Release(); }
return hr; }
HRESULT CDefView::_CanShare(IUnknown* pv,IShellItemArray *psiItemArray, BOOL fOkToBeSlow, UISTATE* puisState) { HRESULT hr = E_FAIL;
CDefView* pThis = (CDefView*)(void*)pv; *puisState = UIS_DISABLED;
if (!psiItemArray) { psiItemArray = pThis->_GetFolderAsShellItemArray(); }
if (psiItemArray) { #ifdef DEBUG
// Sanity check.
DWORD dwNumItems; ASSERT(S_OK == psiItemArray->GetCount(&dwNumItems)); ASSERT(1 == dwNumItems); #endif
IShellItem *psi; hr = psiItemArray->GetItemAt(0, &psi); if (SUCCEEDED(hr)) { // Retrieve pidl.
LPITEMIDLIST pidl; hr = SHGetIDListFromUnk(psi, &pidl); if (SUCCEEDED(hr)) { // Retrieve path and attributes.
WCHAR szPath[MAX_PATH]; DWORD dwAttributes = SFGAO_LINK; hr = SHGetNameAndFlags(pidl, SHGDN_FORPARSING, szPath, ARRAYSIZE(szPath), &dwAttributes); if (SUCCEEDED(hr) && !(dwAttributes & SFGAO_LINK) && !PathIsRemote(szPath)) { if (!g_hmodNTSHRUI) { g_hmodNTSHRUI = LoadLibrary(L"ntshrui.dll"); }
if (g_hmodNTSHRUI) { PFNCANSHAREFOLDERW pfnCanShareFolder = (PFNCANSHAREFOLDERW)GetProcAddress(g_hmodNTSHRUI, "CanShareFolderW"); if (pfnCanShareFolder) { *puisState = (S_OK == pfnCanShareFolder(szPath)) ? UIS_ENABLED : UIS_DISABLED; } } } ILFree(pidl); } psi->Release(); } }
return hr; }
HRESULT CDefView::_CanEmail(IUnknown* pv, IShellItemArray *psiItemArray, BOOL fOkToBeSlow, UISTATE* puisState) { DWORD dwAttributes = 0;
// Prevent people from attempting to e-mail non-filesystem objects.
// Attempting to attach such objects to an e-mail message fails.
// An example of this type of failure is attempting to e-mail
// connectoids in the "Network Connections" folder.
if (psiItemArray) { psiItemArray->GetAttributes(SIATTRIBFLAGS_APPCOMPAT, SFGAO_FILESYSTEM, &dwAttributes); }
*puisState = dwAttributes & SFGAO_FILESYSTEM ? UIS_ENABLED : UIS_DISABLED;
return S_OK; }
HRESULT CDefView::_CanPrint(IUnknown* pv, IShellItemArray *psiItemArray, BOOL fOkToBeSlow, UISTATE* puisState) { if (!(((CDefView*)(void*)pv)->_wvLayout.dwLayout & SFVMWVL_NOPRINT)) return _HasPrintVerb(pv, psiItemArray, fOkToBeSlow, puisState);
*puisState = UIS_HIDDEN; return S_OK; }
HRESULT CDefView::_HasPrintVerb(IUnknown* pv, IShellItemArray *psiItemArray, BOOL fOkToBeSlow, UISTATE* puisState) {
CDefView* pThis = (CDefView*)(void*)pv;
if (!psiItemArray) { psiItemArray = pThis->_GetFolderAsShellItemArray(); }
BOOL fHasPrint = _DoesStaticMenuHaveVerb(psiItemArray,c_szPrintW);
*puisState = (fHasPrint) ? UIS_ENABLED : UIS_HIDDEN;
return S_OK; } HRESULT CDefView::_CanDelete(IUnknown* pv, IShellItemArray *psiItemArray, BOOL fOkToBeSlow, UISTATE* puisState) { CDefView* pThis = (CDefView*)(void*)pv; return pThis->_CheckAttribs(psiItemArray, SFGAO_CANDELETE, SFGAO_CANDELETE, puisState); } // determines if defview is hosted over the system drive root or not
BOOL CDefView::_IsSystemDrive(void) { TCHAR szPath[MAX_PATH]; TCHAR szSystemDrive[4]; BOOL bResult = FALSE;
if (SUCCEEDED(_GetPath(szPath))) { SHExpandEnvironmentStrings (TEXT("%SystemDrive%\\"), szSystemDrive, ARRAYSIZE(szSystemDrive));
if (!lstrcmpi(szPath, szSystemDrive)) { bResult = TRUE; } }
return bResult; } HRESULT CDefView::_CanViewDrives(IUnknown* pv, IShellItemArray *psiItemArray, BOOL fOkToBeSlow, UISTATE* puisState) { CDefView* pThis = (CDefView*)(void*)pv;
*puisState = UIS_DISABLED;
if (pThis->_wvContent.dwFlags & SFVMWVF_BARRICADE) { if (pThis->_fBarrierDisplayed) { if (pThis->_IsSystemDrive()) { *puisState = UIS_ENABLED; } } }
return S_OK; } HRESULT CDefView::_CanHideDrives(IUnknown* pv, IShellItemArray *psiItemArray, BOOL fOkToBeSlow, UISTATE* puisState) { CDefView* pThis = (CDefView*)(void*)pv;
*puisState = UIS_DISABLED;
if (pThis->_wvContent.dwFlags & SFVMWVF_BARRICADE) { if (!pThis->_fBarrierDisplayed) { if (pThis->_IsSystemDrive()) { *puisState = UIS_ENABLED; } } }
return S_OK; } HRESULT CDefView::_CanViewFolder(IUnknown* pv, IShellItemArray *psiItemArray, BOOL fOkToBeSlow, UISTATE* puisState) { CDefView* pThis = (CDefView*)(void*)pv;
*puisState = UIS_DISABLED;
if (pThis->_wvContent.dwFlags & SFVMWVF_BARRICADE) { if (pThis->_fBarrierDisplayed) { if (!pThis->_IsSystemDrive()) { *puisState = UIS_ENABLED; } } }
return S_OK; } HRESULT CDefView::_CanHideFolder(IUnknown* pv, IShellItemArray *psiItemArray, BOOL fOkToBeSlow, UISTATE* puisState) { CDefView* pThis = (CDefView*)(void*)pv;
*puisState = UIS_DISABLED;
if (pThis->_wvContent.dwFlags & SFVMWVF_BARRICADE) { if (!pThis->_fBarrierDisplayed) { if (!pThis->_IsSystemDrive()) { *puisState = UIS_ENABLED; } } }
return S_OK; } HRESULT CDefView::_HasPreviousVersions(IUnknown* pv, IShellItemArray *psiItemArray, BOOL fOkToBeSlow, UISTATE* puisState) { HRESULT hr = S_OK;
*puisState = UIS_HIDDEN;
if (!psiItemArray) { CDefView* pThis = (CDefView*)(void*)pv; psiItemArray = pThis->_GetFolderAsShellItemArray(); } if (NULL != psiItemArray) { #ifdef DEBUG
// Sanity check.
DWORD dwNumItems; ASSERT(S_OK == psiItemArray->GetCount(&dwNumItems)); ASSERT(1 == dwNumItems); #endif
BOOL bHasShadowCopy = FALSE;
// This returns E_PENDING if the answer is unknown
// and fOkToBeSlow is FALSE
hr = HavePreviousVersionsAt(psiItemArray, 0, fOkToBeSlow, &bHasShadowCopy); if (S_OK == hr && bHasShadowCopy) { *puisState = UIS_ENABLED; } } return hr; }
HRESULT CDefView::_DoVerb(IShellItemArray *psiItemArray, LPCSTR pszVerbA) { HRESULT hr = E_FAIL;
if (NULL== psiItemArray) { IContextMenu* pcm; hr = GetItemObject(SVGIO_BACKGROUND, IID_PPV_ARG(IContextMenu, &pcm)); if (SUCCEEDED(hr)) { hr = _InvokeContextMenuVerb(pcm, pszVerbA, 0, CMIC_MASK_FLAG_LOG_USAGE); pcm->Release(); } } else { ASSERT(psiItemArray == _pSelectionShellItemArray); hr = _InvokeContextMenuVerbOnSelection(pszVerbA, 0, CMIC_MASK_FLAG_LOG_USAGE); }
return hr; }
HRESULT CDefView::_DoDropOnClsid(REFCLSID clsidDrop, IDataObject* pdo) { HRESULT hr = E_FAIL; IDataObject *pdoFree = NULL;
if (!pdo) { IShellItemArray *pFolder = _GetFolderAsShellItemArray(); if (pFolder) { hr = pFolder->BindToHandler(NULL, BHID_DataObject, IID_PPV_ARG(IDataObject, &pdoFree)); if (SUCCEEDED(hr)) { pdo = pdoFree; } else { pdoFree = NULL; } } }
if (pdo) { hr = SHSimulateDropOnClsid(clsidDrop, SAFECAST(this, IOleCommandTarget *), pdo); }
ATOMICRELEASE(pdoFree); // may be NULL
return hr; }
HRESULT CDefView::_OnNewFolder(IUnknown* pv, IShellItemArray *psiItemArray, IBindCtx *pbc) { CDefView* pThis = (CDefView*)(void*)pv; return pThis->_DoVerb(psiItemArray,c_szNewFolderA); }
HRESULT CDefView::_OnRename(IUnknown* pv, IShellItemArray *psiItemArray, IBindCtx *pbc) { CDefView* pThis = (CDefView*)(void*)pv; return pThis->DoRename(); }
HRESULT CDefView::_OnMove(IUnknown* pv, IShellItemArray *psiItemArray, IBindCtx *pbc) { CDefView* pThis = (CDefView*)(void*)pv; return pThis->_DoMoveOrCopyTo(CLSID_MoveToMenu, psiItemArray); }
HRESULT CDefView::_OnCopy(IUnknown* pv, IShellItemArray *psiItemArray, IBindCtx *pbc) { CDefView* pThis = (CDefView*)(void*)pv; return pThis->_DoMoveOrCopyTo(CLSID_CopyToMenu, psiItemArray); }
HRESULT CDefView::_OnPublish(IUnknown* pv, IShellItemArray *psiItemArray, IBindCtx *pbc) { HRESULT hr = S_OK; IDataObject *pdo = NULL;
CDefView* pThis = (CDefView*)(void*)pv;
if (psiItemArray) { hr = psiItemArray->BindToHandler(NULL, BHID_DataObject, IID_PPV_ARG(IDataObject, &pdo)); }
if (SUCCEEDED(hr)) { hr = pThis->_DoDropOnClsid(CLSID_PublishDropTarget, pdo); }
ATOMICRELEASE(pdo); // may be NULL
return hr; }
HRESULT CDefView::_OnShare(IUnknown* pv, IShellItemArray *psiItemArray, IBindCtx *pbc) { HRESULT hr = E_FAIL;
CDefView* pThis = (CDefView*)(void*)pv;
if (!psiItemArray) { psiItemArray = pThis->_GetFolderAsShellItemArray(); }
if (NULL != psiItemArray) { LPOLESTR pszPath;
hr = pThis->_GetFullPathNameAt(psiItemArray, 0, &pszPath);
if (SUCCEEDED(hr)) { if (!g_hmodNTSHRUI) { g_hmodNTSHRUI = LoadLibrary(L"ntshrui.dll"); }
if (g_hmodNTSHRUI) { PFNSHOWSHAREFOLDERUIW pfnShowShareFolderUI = (PFNSHOWSHAREFOLDERUIW) GetProcAddress(g_hmodNTSHRUI, "ShowShareFolderUIW"); if (pfnShowShareFolderUI) { pfnShowShareFolderUI(pThis->_hwndMain, pszPath); } }
CoTaskMemFree(pszPath); } }
return hr; }
HRESULT CDefView::_OnEmail(IUnknown* pv, IShellItemArray *psiItemArray, IBindCtx *pbc) { HRESULT hr = E_FAIL; IDataObject *pdo = NULL;
if (psiItemArray) { hr = psiItemArray->BindToHandler(NULL,BHID_DataObject,IID_PPV_ARG(IDataObject,&pdo)); }
if (SUCCEEDED(hr)) { CDefView* pThis = (CDefView*)(void*)pv; BOOL bNoFilesFoundToEmail = TRUE;
INamespaceWalk *pnsw; hr = CoCreateInstance(CLSID_NamespaceWalker, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(INamespaceWalk, &pnsw)); if (SUCCEEDED(hr)) { // Note:
// To mirror the behaviour of the selection context menu's "Send To->
// Mail Recipient", don't traverse links, mail the link file itself.
hr = pnsw->Walk(pdo, NSWF_DONT_TRAVERSE_LINKS, 0, NULL); if (SUCCEEDED(hr)) { UINT cItems; LPITEMIDLIST *ppidls; hr = pnsw->GetIDArrayResult(&cItems, &ppidls); if (SUCCEEDED(hr)) { if (cItems) { IDataObject* pdoWalk; hr = SHCreateFileDataObject(&c_idlDesktop, cItems, (LPCITEMIDLIST *)ppidls, NULL, (IDataObject **)&pdoWalk); if (SUCCEEDED(hr)) { hr = pThis->_DoDropOnClsid(CLSID_MailRecipient, pdoWalk); bNoFilesFoundToEmail = FALSE; pdoWalk->Release(); } } FreeIDListArray(ppidls, cItems); } } pnsw->Release(); }
if (bNoFilesFoundToEmail) { // No items found to e-mail (selected folders contained no files).
ShellMessageBox( HINST_THISDLL, pThis->_hwndMain, MAKEINTRESOURCE(IDS_NOFILESTOEMAIL), NULL, MB_OK | MB_ICONERROR); }
pdo->Release(); } return hr; }
HRESULT CDefView::_OnPrint(IUnknown* pv, IShellItemArray *psiItemArray, IBindCtx *pbc) { CDefView* pThis = (CDefView*)(void*)pv; return pThis->_DoVerb(psiItemArray,c_szPrintA); }
HRESULT CDefView::_OnDelete(IUnknown* pv, IShellItemArray *psiItemArray, IBindCtx *pbc) { CDefView* pThis = (CDefView*)(void*)pv; return pThis->_DoVerb(psiItemArray,c_szDeleteA); }
HRESULT CDefView::RemoveBarricade (void) { LPITEMIDLIST pidl = _GetViewPidl(); if (pidl) { TCHAR szValueName[MAX_PATH]; if (GetBarricadeValueNameFromPidl(pidl, szValueName, ARRAYSIZE(szValueName))) { SetBarricadeStatus (szValueName, VARIANT_FALSE); } ILFree(pidl); }
// Restore "View" menu commands which were stripped.
RecreateMenus(); // Enable "View Menu" button on the toolbar.
EnableToolbarButton(SFVIDM_VIEW_VIEWMENU, TRUE);
_fBarrierDisplayed = FALSE;
return _pDUIView->EnableBarrier(FALSE); }
HRESULT CDefView::_OnView(IUnknown* pv, IShellItemArray *psiItemArray, IBindCtx *pbc) { CDefView* pThis = (CDefView*)(void*)pv;
return pThis->RemoveBarricade(); }
HRESULT CDefView::_OnHide(IUnknown* pv, IShellItemArray *psiItemArray, IBindCtx *pbc) { CDefView* pThis = (CDefView*)(void*)pv;
LPITEMIDLIST pidl = pThis->_GetViewPidl(); if (pidl) { TCHAR szValueName[MAX_PATH]; if (GetBarricadeValueNameFromPidl(pidl, szValueName, ARRAYSIZE(szValueName))) { SetBarricadeStatus(szValueName, VARIANT_TRUE); } ILFree(pidl); }
// Disable "View Menu" button on the toolbar.
pThis->EnableToolbarButton(SFVIDM_VIEW_VIEWMENU, FALSE);
pThis->_fBarrierDisplayed = TRUE;
return pThis->_pDUIView->EnableBarrier(TRUE); }
HRESULT CDefView::_OnAddRemovePrograms(IUnknown* pv, IShellItemArray *psiItemArray, IBindCtx *pbc) { HCURSOR hcOld = SetCursor(LoadCursor(NULL, IDC_WAIT));
SHRunControlPanel(L"appwiz.cpl", NULL);
SetCursor(hcOld);
return S_OK; }
HRESULT CDefView::_OnSearchFiles(IUnknown* pv, IShellItemArray *psiItemArray, IBindCtx *pbc) { CDefView* pThis = (CDefView*)(void*)pv;
return IUnknown_ShowBrowserBar (pThis->_psb, CLSID_FileSearchBand, TRUE); }
HRESULT CDefView::_OnPreviousVersions(IUnknown* pv, IShellItemArray *psiItemArray, IBindCtx *pbc) { CDefView* pThis = (CDefView*)(void*)pv; if (!psiItemArray) { psiItemArray = pThis->_GetFolderAsShellItemArray(); } ShowPreviousVersionsAt(psiItemArray, 0, pThis->_hwndMain); return S_OK; }
const WVTASKITEM c_DefviewBlockadeTaskHeader = WVTI_HEADER_ENTRY(L"shell32.dll", IDS_HEADER_DEFVIEW_BLOCKADE, IDS_HEADER_DEFVIEW_BLOCKADE, IDS_HEADER_DEFVIEW_BLOCKADE, IDS_HEADER_DEFVIEW_BLOCKADE, IDS_HEADER_DEFVIEW_BLOCKADE_TT); const WVTASKITEM c_DefviewBlockadeTasks[] = { WVTI_ENTRY_ALL(UICID_ViewContents, L"shell32.dll", IDS_TASK_DEFVIEW_VIEWCONTENTS_DRIVE, IDS_TASK_DEFVIEW_VIEWCONTENTS_DRIVE_TT, IDI_STSPROGS, CDefView::_CanViewDrives, CDefView::_OnView), WVTI_ENTRY_ALL(UICID_HideContents, L"shell32.dll", IDS_TASK_DEFVIEW_HIDECONTENTS_DRIVE, IDS_TASK_DEFVIEW_HIDECONTENTS_DRIVE_TT, IDI_STSPROGS, CDefView::_CanHideDrives, CDefView::_OnHide), WVTI_ENTRY_ALL(UICID_ViewContents, L"shell32.dll", IDS_TASK_DEFVIEW_VIEWCONTENTS_FOLDER, IDS_TASK_DEFVIEW_VIEWCONTENTS_FOLDER_TT,IDI_STSPROGS, CDefView::_CanViewFolder, CDefView::_OnView), WVTI_ENTRY_ALL(UICID_HideContents, L"shell32.dll", IDS_TASK_DEFVIEW_HIDECONTENTS_FOLDER, IDS_TASK_DEFVIEW_HIDECONTENTS_FOLDER_TT,IDI_STSPROGS, CDefView::_CanHideFolder, CDefView::_OnHide), WVTI_ENTRY_ALL(UICID_AddRemovePrograms, L"shell32.dll", IDS_TASK_ARP, IDS_TASK_ARP_TT, IDI_CPCAT_ARP, NULL, CDefView::_OnAddRemovePrograms), WVTI_ENTRY_ALL(UICID_SearchFiles, L"shell32.dll", IDS_TASK_SEARCHFORFILES, IDS_TASK_SEARCHFORFILES_TT, IDI_STFIND, NULL, CDefView::_OnSearchFiles), };
const WVTASKITEM c_DefviewFileFolderTasksHeaders = WVTI_HEADER(L"shell32.dll", IDS_HEADER_FILEFOLDER, IDS_HEADER_FILEFOLDER_TT); const WVTASKITEM c_DefviewItemFolderTasksHeaders = WVTI_HEADER(L"shell32.dll", IDS_HEADER_ITEMFOLDER, IDS_HEADER_ITEMFOLDER_TT);
const WVTASKITEM c_DefviewFileFolderTasks[] = { WVTI_ENTRY_NOSELECTION(UICID_NewFolder, L"shell32.dll", IDS_TASK_CURFOLDER_NEWFOLDER, IDS_TASK_CURFOLDER_NEWFOLDER_TT, IDI_TASK_NEWFOLDER, CDefView::_CanWrite, CDefView::_OnNewFolder), WVTI_ENTRY_TITLE(UICID_Rename, L"shell32.dll", IDS_TASK_RENAME_FILE, IDS_TASK_RENAME_FOLDER, 0, IDS_TASK_RENAME_FILE_TT, IDI_TASK_RENAME, CDefView::_CanRename, CDefView::_OnRename), WVTI_ENTRY_TITLE(UICID_Move, L"shell32.dll", IDS_TASK_MOVE_FILE, IDS_TASK_MOVE_FOLDER, IDS_TASK_MOVE_ITEMS, IDS_TASK_MOVE_TT, IDI_TASK_MOVE, CDefView::_CanMove, CDefView::_OnMove), WVTI_ENTRY_ALL_TITLE(UICID_Copy, L"shell32.dll", 0, IDS_TASK_COPY_FILE, IDS_TASK_COPY_FOLDER, IDS_TASK_COPY_ITEMS, IDS_TASK_COPY_TT, IDI_TASK_COPY, CDefView::_CanCopy, CDefView::_OnCopy), WVTI_ENTRY_ALL_TITLE(UICID_Publish, L"shell32.dll", IDS_TASK_PUBLISH_FOLDER, IDS_TASK_PUBLISH_FILE,IDS_TASK_PUBLISH_FOLDER, IDS_TASK_PUBLISH_ITEMS, IDS_TASK_PUBLISH_TT, IDI_TASK_PUBLISH, CDefView::_CanPublish, CDefView::_OnPublish), WVTI_ENTRY_ALL_TITLE(UICID_Share, L"shell32.dll", IDS_TASK_SHARE_FOLDER, 0, IDS_TASK_SHARE_FOLDER, 0, IDS_TASK_SHARE_TT, IDI_TASK_SHARE, CDefView::_CanShare, CDefView::_OnShare), WVTI_ENTRY_TITLE(UICID_Email, L"shell32.dll", IDS_TASK_EMAIL_FILE, IDS_TASK_EMAIL_FOLDER, IDS_TASK_EMAIL_ITEMS, IDS_TASK_EMAIL_TT, IDI_TASK_EMAILFILE, CDefView::_CanEmail, CDefView::_OnEmail), WVTI_ENTRY_TITLE(UICID_Print, L"shell32.dll", IDS_TASK_PRINT_FILE, 0, 0, IDS_TASK_PRINT_TT, IDI_TASK_PRINT, CDefView::_CanPrint, CDefView::_OnPrint), WVTI_ENTRY_TITLE(UICID_Delete, L"shell32.dll", IDS_TASK_DELETE_FILE, IDS_TASK_DELETE_FOLDER, IDS_TASK_DELETE_ITEMS, IDS_TASK_DELETE_TT, IDI_TASK_DELETE, CDefView::_CanDelete, CDefView::_OnDelete), WVTI_ENTRY_ALL_TITLE(UICID_PreviousVersions, L"shell32.dll", IDS_TASK_SHADOW, IDS_TASK_SHADOW, IDS_TASK_SHADOW, 0, IDS_TASK_SHADOW_TT, IDI_TASK_SHADOW, CDefView::_HasPreviousVersions, CDefView::_OnPreviousVersions), }; const size_t c_cDefviewFileFolderTasks = ARRAYSIZE(c_DefviewFileFolderTasks);
const WVTASKITEM c_DefviewItemFolderTasks[] = { WVTI_ENTRY_NOSELECTION(UICID_NewFolder, L"shell32.dll", IDS_TASK_CURFOLDER_NEWFOLDER, IDS_TASK_CURFOLDER_NEWFOLDER_TT, IDI_TASK_NEWFOLDER, CDefView::_CanWrite, CDefView::_OnNewFolder), WVTI_ENTRY_TITLE(UICID_Rename, L"shell32.dll", IDS_TASK_RENAME_ITEM, IDS_TASK_RENAME_FOLDER, 0, IDS_TASK_RENAME_ITEM_TT, IDI_TASK_RENAME, CDefView::_CanRename, CDefView::_OnRename), WVTI_ENTRY_TITLE(UICID_Move, L"shell32.dll", IDS_TASK_MOVE_ITEM, IDS_TASK_MOVE_FOLDER, IDS_TASK_MOVE_ITEMS, IDS_TASK_MOVE_TT, IDI_TASK_MOVE, CDefView::_CanMove, CDefView::_OnMove), WVTI_ENTRY_ALL_TITLE(UICID_Copy, L"shell32.dll", 0, IDS_TASK_COPY_ITEM, IDS_TASK_COPY_FOLDER, IDS_TASK_COPY_ITEMS, IDS_TASK_COPY_TT, IDI_TASK_COPY, CDefView::_CanCopy, CDefView::_OnCopy), WVTI_ENTRY_ALL_TITLE(UICID_Share, L"shell32.dll", IDS_TASK_SHARE_FOLDER, 0, IDS_TASK_SHARE_FOLDER, 0, IDS_TASK_SHARE_TT, IDI_TASK_SHARE, CDefView::_CanShare, CDefView::_OnShare), WVTI_ENTRY_TITLE(UICID_Delete, L"shell32.dll", IDS_TASK_DELETE_ITEM, IDS_TASK_DELETE_FOLDER, IDS_TASK_DELETE_ITEMS, IDS_TASK_DELETE_TT, IDI_TASK_DELETE, CDefView::_CanDelete, CDefView::_OnDelete), }; const size_t c_cDefviewItemFolderTasks = ARRAYSIZE(c_DefviewItemFolderTasks);
const WVTASKITEM c_DefviewOtherPlaces = WVTI_HEADER_ENTRY(L"shell32.dll", IDS_HEADER_OTHER_PLACES, IDS_HEADER_OTHER_PLACES, IDS_HEADER_OTHER_PLACES, IDS_HEADER_OTHER_PLACES, IDS_HEADER_OTHER_PLACES_TT); const WVTASKITEM c_DefviewDetails = WVTI_HEADER_ENTRY(L"shell32.dll", IDS_HEADER_DETAILS, IDS_HEADER_DETAILS, IDS_HEADER_DETAILS, IDS_HEADER_DETAILS, IDS_HEADER_DETAILS_TT);
const WVTASKITEM* CDefView::_FindTaskItem(REFGUID guidCanonicalName) { const BOOL bFileFolderTasks = _wvLayout.dwLayout & SFVMWVL_FILES; const WVTASKITEM *paTasks = bFileFolderTasks ? c_DefviewFileFolderTasks : c_DefviewItemFolderTasks; const size_t cTasks = bFileFolderTasks ? c_cDefviewFileFolderTasks : c_cDefviewItemFolderTasks;
for (size_t i = 0; i < cTasks; i++) if (IsEqualGUID(*(paTasks[i].pguidCanonicalName), guidCanonicalName)) return &paTasks[i];
return NULL; }
HRESULT CDefView::get_Name(REFGUID guidCanonicalName, IShellItemArray *psiItemArray, LPWSTR *ppszName) { const WVTASKITEM* pTask = _FindTaskItem(guidCanonicalName); if (pTask) return CWVTASKITEM::get_Name(pTask, psiItemArray, ppszName); return E_FAIL; }
HRESULT CDefView::get_Icon(REFGUID guidCanonicalName, IShellItemArray *psiItemArray, LPWSTR *ppszIcon) { const WVTASKITEM* pTask = _FindTaskItem(guidCanonicalName); if (pTask) return CWVTASKITEM::get_Icon(pTask, psiItemArray, ppszIcon); return E_FAIL; }
HRESULT CDefView::get_Tooltip(REFGUID guidCanonicalName, IShellItemArray *psiItemArray, LPWSTR *ppszInfotip) { const WVTASKITEM* pTask = _FindTaskItem(guidCanonicalName); if (pTask) return CWVTASKITEM::get_Tooltip(pTask, psiItemArray, ppszInfotip); return E_FAIL; }
HRESULT CDefView::get_State(REFGUID guidCanonicalName, IShellItemArray *psiItemArray, UISTATE* puisState) { const WVTASKITEM* pTask = _FindTaskItem(guidCanonicalName); if (pTask) return CWVTASKITEM::get_State(pTask, SAFECAST(this, IShellView2*), psiItemArray, TRUE, puisState); return E_FAIL; }
HRESULT CDefView::Invoke(REFGUID guidCanonicalName, IShellItemArray *psiItemArray, IBindCtx *pbc) { const WVTASKITEM* pTask = _FindTaskItem(guidCanonicalName); if (pTask) return CWVTASKITEM::Invoke(pTask, SAFECAST(this, IShellView2*), psiItemArray, pbc); return E_FAIL; }
HRESULT CDefView::_GetDefaultWebviewContent(BOOL bFileFolderTasks) { if (!_wvTasks.penumSpecialTasks) { if (_wvContent.dwFlags & SFVMWVF_BARRICADE) { // defview provides a default penumSpecialTasks for barricaded folders
Create_IUIElement(&c_DefviewBlockadeTaskHeader, &(_wvContent.pSpecialTaskHeader));
Create_IEnumUICommand((IUnknown*)(void*)this, c_DefviewBlockadeTasks, ARRAYSIZE(c_DefviewBlockadeTasks), &(_wvTasks.penumSpecialTasks)); } }
if (!_wvTasks.penumFolderTasks) { if (_wvContent.pFolderTaskHeader) _wvContent.pFolderTaskHeader->Release(); Create_IUIElement(bFileFolderTasks ? &c_DefviewFileFolderTasksHeaders : &c_DefviewItemFolderTasksHeaders, &(_wvContent.pFolderTaskHeader));
Create_IEnumUICommand( (IUnknown*)(void*)this, bFileFolderTasks ? c_DefviewFileFolderTasks : c_DefviewItemFolderTasks, bFileFolderTasks ? c_cDefviewFileFolderTasks : c_cDefviewItemFolderTasks, &(_wvTasks.penumFolderTasks)); }
if (!_wvContent.penumOtherPlaces) { LPCTSTR rgCSIDLs[] = { MAKEINTRESOURCE(CSIDL_PERSONAL), MAKEINTRESOURCE(CSIDL_COMMON_DOCUMENTS), MAKEINTRESOURCE(CSIDL_NETWORK) };
LPITEMIDLIST pidl = _GetViewPidl();
CreateIEnumIDListOnCSIDLs(pidl, rgCSIDLs, ARRAYSIZE(rgCSIDLs), &_wvContent.penumOtherPlaces);
if (pidl) ILFree(pidl); }
ASSERT(NULL==_pOtherPlacesHeader); Create_IUIElement(&c_DefviewOtherPlaces, &_pOtherPlacesHeader);
ASSERT(NULL==_pDetailsHeader); Create_IUIElement(&c_DefviewDetails, &_pDetailsHeader);
return S_OK; }
void CDefView::_FreeWebViewContentData() { ATOMICRELEASE(_wvContent.pSpecialTaskHeader); ATOMICRELEASE(_wvContent.pFolderTaskHeader); ATOMICRELEASE(_wvContent.penumOtherPlaces); ATOMICRELEASE(_wvTasks.penumSpecialTasks); ATOMICRELEASE(_wvTasks.penumFolderTasks);
ATOMICRELEASE(_pOtherPlacesHeader); ATOMICRELEASE(_pDetailsHeader);
_fQueryWebViewData = FALSE; _wvLayout.dwLayout = -1; // an invalid value
}
BOOL CDefView::_QueryBarricadeState() { BOOL bResult = FALSE; LPITEMIDLIST pidl = _GetViewPidl(); if (pidl) { //
// Control panel is a special case.
// The barricade is used to represent 'category view' which can
// be turned on/off by the user. We must always ask control panel
// if it's barricade is on or off.
//
BOOL bIsControlPanel = FALSE; LPITEMIDLIST pidlControlPanel; if (SUCCEEDED(SHGetSpecialFolderLocation(NULL, CSIDL_CONTROLS, &pidlControlPanel))) { bIsControlPanel = ILIsEqual(pidl, pidlControlPanel); ILFree (pidlControlPanel); } if (bIsControlPanel) { SFVM_WEBVIEW_CONTENT_DATA wvc; if (SUCCEEDED(CallCB(SFVM_GETWEBVIEWCONTENT, 0, (LPARAM)&wvc))) { //
// Control Panel doesn't provide all the standard
// webview content so it's doing nothing more than setting
// the dwFlags member. Assert to ensure this doesn't
// change in the future without us knowing about it.
//
ASSERT(NULL == wvc.pIntroText); ASSERT(NULL == wvc.pSpecialTaskHeader); ASSERT(NULL == wvc.pFolderTaskHeader); ASSERT(NULL == wvc.penumOtherPlaces); bResult = (0 != (SFVMWVF_BARRICADE & wvc.dwFlags)); } } else if (_wvContent.dwFlags & SFVMWVF_BARRICADE) { if (!IsBarricadeGloballyOff()) { TCHAR szValueName[MAX_PATH]; if (GetBarricadeValueNameFromPidl(pidl, szValueName, ARRAYSIZE(szValueName))) { if (VARIANT_TRUE == GetBarricadeStatus(szValueName)) { bResult = TRUE; } } } } ILFree(pidl); } return bResult; }
void CDefView::_ShowLegacyWatermark() { BOOL fShowLegacyWatermark = TRUE; LVBKIMAGE lvbki = {0};
if (_pszLegacyWatermark) { lvbki.ulFlags = LVBKIF_SOURCE_URL | LVBKIF_STYLE_TILE; lvbki.pszImage = _pszLegacyWatermark; } else { // this code path is used to clear the watermark
lvbki.ulFlags = LVBKIF_TYPE_WATERMARK;
// if we're turning off the legacy watermark, we may have to turn on the theme one
if (_idThemeWatermark && _pDUIView) { fShowLegacyWatermark = FALSE; } }
if (fShowLegacyWatermark) ListView_SetBkImage(_hwndListview, &lvbki); else _ShowThemeWatermark(); }
void CDefView::_ShowThemeWatermark() { BOOL fShowLegacyWatermark = TRUE;
if (_idThemeWatermark && _pDUIView) { HINSTANCE hinstTheme = _pDUIView->_GetThemeHinst();
LVBKIMAGE lvbki = {0}; lvbki.ulFlags = LVBKIF_TYPE_WATERMARK; lvbki.hbm = DUILoadBitmap(hinstTheme, _idThemeWatermark, LR_DEFAULTCOLOR); if (lvbki.hbm) { // If the window color doesn't match the background color of the watermark,
// then we'll hide the watermark.
HDC hDC = CreateCompatibleDC(NULL);
if (hDC) { HBITMAP hOldBitmap;
hOldBitmap = (HBITMAP)SelectObject (hDC, lvbki.hbm);
if (GetPixel(hDC, 0, 0) != GetSysColor(COLOR_WINDOW)) { _idThemeWatermark = 0; }
SelectObject (hDC, hOldBitmap); DeleteDC (hDC); }
if (_idThemeWatermark && ListView_SetBkImage(_hwndListview, &lvbki)) { fShowLegacyWatermark = FALSE; } else { DeleteObject(lvbki.hbm); } }
if (fShowLegacyWatermark) _idThemeWatermark = 0; // something failed, pretend we don't have one
}
// usually this will just hide the previous watermark
if (fShowLegacyWatermark) { _ShowLegacyWatermark(); } }
void CDefView::_SetThemeWatermark() { UINT idThemeWatermark = 0;
if (_pDUIView) { const WVTHEME* pwvTheme = _pDUIView->GetThemeInfo(); if (pwvTheme && pwvTheme->idListviewWatermark) { HINSTANCE hinstTheme = _pDUIView->_GetThemeHinst(); if (HINST_THISDLL != hinstTheme) { // Only add the watermark if the machine is fast enough...
if (SHRegGetBoolUSValue(REGSTR_EXPLORER_ADVANCED, TEXT("ListviewWatermark"), FALSE, // Don't ignore HKCU
FALSE)) // Assume not fast enough
{ idThemeWatermark = pwvTheme->idListviewWatermark; } } } }
if (idThemeWatermark != _idThemeWatermark) { _idThemeWatermark = idThemeWatermark;
// Since DUI Document view isn't themed, legacy watermarks have precedence there.
// Might as well have them take precedence for My Pictures too...
if (!_pszLegacyWatermark) { _ShowThemeWatermark(); } } }
void CDefView::_SetLegacyWatermark(LPCTSTR pszLegacyWatermark) { Str_SetPtr(&_pszLegacyWatermark, pszLegacyWatermark);
_ShowLegacyWatermark(); }
HRESULT CDefView::_TryShowWebView(UINT fvmNew, UINT fvmOld) { SFVM_WEBVIEW_LAYOUT_DATA sfvmwvld = {0};
HRESULT hr = E_FAIL; BOOL fShowDUI = FALSE;
// The desktop IShellFolder doesn't know if it's in-frame or in the real desktop,
// so only ask it for new DUIView if we're not the actual desktop.
if (!_IsDesktop()) { // Supporting SFVM_GETWEBVIEWLAYOUT means the folder wants our new
// DUI View and they support SFVM_GETWEBVIEWCONTENT
//
hr = CallCB(SFVM_GETWEBVIEWLAYOUT, (WPARAM)fvmNew, (LPARAM)&sfvmwvld);
fShowDUI = SUCCEEDED(hr); }
// This folder doesn't specify the new DUIView, try the old WebView stuff
if (!fShowDUI) { WCHAR wszMoniker[MAX_PATH]; hr = _GetWebViewMoniker(wszMoniker, ARRAYSIZE(wszMoniker)); if (SUCCEEDED(hr)) { if(_pDUIView) //Hide it only if we are switching from DUI
_TryHideWebView(); // just in case we're switching from DUI to Web View (can happen when customizing)
if (wszMoniker[0]) { hr = _SwitchToWebView(TRUE); } }
// Okay, we don't have Web View, use the default DUI View
if (FAILED(hr)) { sfvmwvld.dwLayout = SFVMWVL_NORMAL; fShowDUI = TRUE; } }
if (fShowDUI) { hr = S_OK;
_cFrame.HideWebView(); // just in case we're switching from Web View to DUI View (can happen when customizing)
if (sfvmwvld.dwLayout != _wvLayout.dwLayout) { if (!_fQueryWebViewData) // instead of this we could allow per-layout tasks...
{ CallCB(SFVM_GETWEBVIEWTHEME, 0, (LPARAM)&_wvTheme);
// _FreeWebViewContentData(); if we have per-layout tasks...
if (FAILED(CallCB(SFVM_GETWEBVIEWCONTENT, 0, (LPARAM)&_wvContent))) { ZeroMemory(&_wvContent, sizeof(_wvContent)); }
if (0 == (SFVMWVF_ENUMTASKS & _wvContent.dwFlags)) { //
// View wants standard task sections.
// Non-standard task sections are enumerated in duiview.
//
if (FAILED(CallCB(SFVM_GETWEBVIEWTASKS, 0, (LPARAM)&_wvTasks))) { ZeroMemory(&_wvTasks, sizeof(_wvTasks)); } _GetDefaultWebviewContent(sfvmwvld.dwLayout & SFVMWVL_FILES); }
_fQueryWebViewData = TRUE; }
CopyMemory(&_wvLayout, &sfvmwvld, sizeof(_wvLayout)); _wvLayout.punkPreview = NULL;
if (_pDUIView) { _pDUIView->EnablePreview(sfvmwvld.punkPreview); } else { _pDUIView = Create_CDUIView(this); if (_pDUIView) { _fBarrierDisplayed = _QueryBarricadeState();
if (SUCCEEDED(_pDUIView->Initialize(_fBarrierDisplayed, sfvmwvld.punkPreview))) { if (((SFVMWVF_ENUMTASKS | SFVMWVF_CONTENTSCHANGE) & _wvContent.dwFlags) && _fRcvdContentsChangeBeforeDuiViewCreated) { //
// If the webview provider dynamically enumerates
// tasks or wants to be refreshed when contents change,
// (i.e. Control Panel), AND we received a 'contents change'
// before DUI View was created, initiate a 'contents change' now.
// Otherwise, such providers will not receive a 'contents change'
// and thus will not display their dynamic webview content.
//
_OnContentsChanged(); } } else { _pDUIView->Release(); _pDUIView = NULL; } } } } else { // except potentially refresh if we need to add/remove our DUI Details minipreview
if (_pDUIView && (_IsImageMode(fvmNew) != _IsImageMode(fvmOld))) _pDUIView->OnSelectionChange(_pSelectionShellItemArray); }
ATOMICRELEASE(sfvmwvld.punkPreview); }
_SetThemeWatermark();
return hr; }
HRESULT CDefView::_TryHideWebView() { if (_pDUIView) { _pDUIView->DetachListview(); // so we detach and re-parent the listview synchronously
//
// Ensure DUser has shut down and handled all DUser messages
// before we release our ref on CDUIView.
//
_pDUIView->UnInitializeDirectUI(); _pDUIView->Release(); _pDUIView = NULL; // * necessary * because this is used internally as a state (must be BEFORE WndSize() below)
_wvLayout.dwLayout = -1; // an invalid value
_fListViewShown = FALSE; // CDUIView::DetachListview() does a SW_HIDE on the listview
WndSize(_hwndView); // resize _hwndView to account for DUI disappearing (otherwise
// it will still be the smaller size expecting DUI to be drawn
// next to it)
ShowHideListView(); } else { _SwitchToWebView(FALSE); }
return S_OK; }
// we are switching the listview view mode in this function, not dorking with web view content.
//
HRESULT CDefView::_SwitchToViewFVM(UINT fvmNew, UINT uiType) { HRESULT hr = S_OK; UINT fvmOld = _fs.ViewMode;
ASSERT(_hwndListview);
HWND hwndCurrentFocus = GetFocus(); BOOL bSetFocusRequired = HasCurrentViewWindowFocus();
if (SWITCHTOVIEW_WEBVIEWONLY != uiType) { // if we haven't loaded the columns yet, do that now
// Don't pre-load the columns for TileView, we are delaying the load on purpose for perf reasons.
if (fvmNew == FVM_DETAILS) { AddColumns(); _SetSortFeedback(); } else if (fvmNew == FVM_THUMBSTRIP) { // Thumbstrip makes no sense in non-webview, fall back to thumbnail
if (!_ShouldShowWebView()) { fvmNew = FVM_THUMBNAIL; } }
// Combined view only applies to large icon view
if (_fCombinedView && fvmNew != FVM_ICON) { _fCombinedView = FALSE; ListView_SetExtendedListViewStyleEx(_hwndListview, LVS_EX_REGIONAL, 0); _SetFolderColors(); }
// First we turn OFF view specific stuff that is no longer needed
switch (fvmOld) { case FVM_THUMBSTRIP: if (FVM_THUMBSTRIP != fvmNew) { ListView_SetExtendedListViewStyleEx(_hwndListview, LVS_EX_SINGLEROW, 0);
// we may have forced thumbstrip to auto-arrange, undo that if so
if (!(_fs.fFlags & FWF_AUTOARRANGE)) { SHSetWindowBits(_hwndListview, GWL_STYLE, LVS_AUTOARRANGE, 0); } } // fall through
case FVM_THUMBNAIL: if (!_IsImageMode(fvmNew)) { _ResetThumbview();
// Since we are switching from thumbnail view, remove any thumbnail extraction tasks
_RemoveThumbviewTasks();
if (_fs.fFlags & FWF_OWNERDATA) { InvalidateRect(_hwndListview, NULL, TRUE); } else { ListView_InvalidateImageIndexes(_hwndListview); } } break;
case FVM_TILE: if (!_IsTileMode(fvmNew)) { if (_pScheduler) _pScheduler->RemoveTasks(TOID_DVFileTypeProperties, ITSAT_DEFAULT_LPARAM, TRUE);
// Remove the columns that
// were pulled in because of tileview.
_RemoveTileColumns(); } break; }
_SetView(fvmNew); // we can now switch the listview around
// Now that'we no longer in tileview, we can reset the tileinfo. If we were to do it
// prior to changing the view, then listview would start asking us for the tileinformation
// for each item again, and we'd pull in the tile columns again.
if (fvmOld == FVM_TILE) { _RemoveTileInfo(); }
// Third, turn ON view specific stuff
//
switch (fvmNew) { case FVM_THUMBSTRIP: if (FVM_THUMBSTRIP!=fvmOld) { ListView_SetExtendedListViewStyleEx(_hwndListview, LVS_EX_SINGLEROW, LVS_EX_SINGLEROW);
// thumbstrip can not be in group view
if (_fGroupView) _ToggleGrouping();
// thumbstrip is always in auto-arrange
if (!(_fs.fFlags & FWF_AUTOARRANGE)) { _ClearItemPositions(); SHSetWindowBits(_hwndListview, GWL_STYLE, LVS_AUTOARRANGE, LVS_AUTOARRANGE); } } // fall through
case FVM_THUMBNAIL: if (!_IsImageMode(fvmOld)) { if (GetKeyState(VK_SHIFT) < 0) { _fs.fFlags ^= FWF_HIDEFILENAMES; // toggle
} _SetThumbview(); _DoThumbnailReadAhead();
RECT rc = {1, 3, 4, 4}; ListView_SetViewMargins(_hwndListview, &rc); } break;
case FVM_TILE: if (!_IsTileMode(fvmOld)) { _SetTileview();
RECT rc = {3, 4, 4, 1}; ListView_SetViewMargins(_hwndListview, &rc); } break;
default: _SetSysImageList();
{ RECT rc = {1, 3, 4, 0}; ListView_SetViewMargins(_hwndListview, &rc); } break; } }
if (SWITCHTOVIEW_NOWEBVIEW != uiType) { // New to Whistler: a view mode transition may also entail a web view template change
if (_ShouldShowWebView()) { _TryShowWebView(fvmNew, fvmOld); _AutoAutoArrange(0);
hr = S_OK; // we don't care about failure since we still get icons
} else { _TryHideWebView(); } }
if (SWITCHTOVIEW_WEBVIEWONLY != uiType) { ShowHideListView(); _AutoAutoArrange(0); if (bSetFocusRequired) { // _hwndListview is the current view window. Let's set focus to it.
CallCB(SFVM_SETFOCUS, 0, 0); ViewWindowSetFocus();
// notify image preview control to update its image
if (fvmNew == FVM_THUMBSTRIP) _ThumbstripSendImagePreviewFocusChangeEvent(); } else { SetFocus(hwndCurrentFocus); } CheckToolbar(); // update menus, i.e. add Choose Columns to the view menu if Details view is selected
// or remove it otherwise
RecreateMenus(); _EnableDisableTBButtons(); } return hr; }
// Description:
// Notify image preview control to update its image. The image preview
// control uses focus change events to track when it should update the image
// it is displaying. When it receives a focus change event, it queries the
// listview to see which item has focus, then displays that item in the
// image preview window. When nothing in the listview has focus (such as
// when it has no items), the image preview window displays as empty.
//
// This method fires the "focus changed" event which is picked up by the
// image preview control, and causes it to update the image it's displaying.
//
void CDefView::_ThumbstripSendImagePreviewFocusChangeEvent() { ASSERT(_fs.ViewMode == FVM_THUMBSTRIP); _FireEvent(DISPID_FOCUSCHANGED); }
int CDefView::CheckCurrentViewMenuItem(HMENU hmenu) { int iCurViewMenuItem = _GetMenuIDFromViewMode(_fs.ViewMode);
CheckMenuRadioItem(hmenu, SFVIDM_VIEW_FIRSTVIEW, SFVIDM_VIEW_LASTVIEW, iCurViewMenuItem, MF_BYCOMMAND | MF_CHECKED);
return iCurViewMenuItem; }
const UINT c_aiNonCustomizableFolders[] = { CSIDL_WINDOWS, CSIDL_SYSTEM, CSIDL_SYSTEMX86, CSIDL_PROGRAM_FILES, CSIDL_PROGRAM_FILESX86, CSIDL_PERSONAL, CSIDL_MYDOCUMENTS, CSIDL_MYMUSIC, CSIDL_MYPICTURES, CSIDL_MYVIDEO, CSIDL_COMMON_DOCUMENTS, CSIDL_COMMON_MUSIC, CSIDL_COMMON_PICTURES, CSIDL_COMMON_VIDEO };
// since we moved to the property bag this check is fast; we don't probe to see if we can create desktop.ini
// or anything.
BOOL IsCustomizable(LPCITEMIDLIST pidlFolder) { BOOL fCustomizable = FALSE;
if (!SHRestricted(REST_NOCUSTOMIZETHISFOLDER) && !SHRestricted(REST_CLASSICSHELL)) { // Check if this is a file system folder.
// customization requires the folder being a regular file system
// folder. FILESYSTEMANCESTOR is the key bit here
#define SFGAO_CUST_BITS (SFGAO_FILESYSTEM | SFGAO_FOLDER | SFGAO_FILESYSANCESTOR)
ULONG rgfFolderAttr = SFGAO_CUST_BITS; TCHAR szPath[MAX_PATH]; if (SUCCEEDED(SHGetNameAndFlags(pidlFolder, SHGDN_FORPARSING, szPath, ARRAYSIZE(szPath), &rgfFolderAttr)) && (SFGAO_CUST_BITS == (rgfFolderAttr & SFGAO_CUST_BITS))) { if (!PathIsOneOf(szPath, c_aiNonCustomizableFolders, ARRAYSIZE(c_aiNonCustomizableFolders)) && (!PathIsRoot(szPath) || PathIsUNCServerShare(szPath)) && !SHRestricted(REST_NOCUSTOMIZEWEBVIEW)) { IPropertyBag *ppb; if (SUCCEEDED(SHGetViewStatePropertyBag(pidlFolder, VS_BAGSTR_EXPLORER, SHGVSPB_PERUSER | SHGVSPB_PERFOLDER, IID_PPV_ARG(IPropertyBag, &ppb)))) { fCustomizable = TRUE; ppb->Release(); } } } }
return fCustomizable; }
// wrapper around IsCustomizable to save some state, plus some defview-specific logic.
BOOL CDefView::_CachedIsCustomizable() { if (_IsDesktop() || _IsViewDesktop() || _IsCommonDialog()) { _iCustomizable = NOT_CUSTOMIZABLE; }
if (_iCustomizable == DONTKNOW_IF_CUSTOMIZABLE) { LPITEMIDLIST pidl = _GetViewPidl(); if (pidl) { _iCustomizable = IsCustomizable(pidl) ? YES_CUSTOMIZABLE : NOT_CUSTOMIZABLE; ILFree(pidl); } }
return (_iCustomizable != NOT_CUSTOMIZABLE); }
BOOL CDefView::_InvokeCustomization() { BOOL fRet = FALSE;
if (!_CachedIsCustomizable()) { //If not customizable, put up this error message!
ShellMessageBox(HINST_THISDLL, _hwndMain, MAKEINTRESOURCE(IDS_NOTCUSTOMIZABLE), NULL, MB_OK | MB_ICONEXCLAMATION | MB_SETFOREGROUND); return FALSE; // ...and bail out!
}
//Save the view state first.
SaveViewState();
LPITEMIDLIST pidl = _GetViewPidl(); if (pidl) { TCHAR szSheetName[25]; LoadString(HINST_THISDLL, IDS_CUSTOMIZE, szSheetName, ARRAYSIZE(szSheetName)); SHELLEXECUTEINFO sei = { SIZEOF(sei), SEE_MASK_INVOKEIDLIST, // fMask
_hwndMain, // hwnd
c_szProperties, // lpVerb
NULL, // lpFile
szSheetName, // lpParameters
NULL, // lpDirectory
SW_SHOWNORMAL, // nShow
NULL, // hInstApp
pidl, // lpIDList
NULL, // lpClass
0, // hkeyClass
0, // dwHotKey
NULL // hIcon
};
// only invoking properties verb
fRet = ShellExecuteEx(&sei);
ILFree(pidl); } return fRet; }
struct { UINT uiSfvidm; DWORD dwOlecmdid; } const c_CmdTable[] = { { SFVIDM_EDIT_CUT, OLECMDID_CUT }, { SFVIDM_EDIT_COPY, OLECMDID_COPY }, { SFVIDM_EDIT_PASTE, OLECMDID_PASTE }, { SFVIDM_FILE_DELETE, OLECMDID_DELETE }, { SFVIDM_FILE_PROPERTIES, OLECMDID_PROPERTIES }, };
DWORD OlecmdidFromSfvidm(UINT uiSfvidm) { DWORD dwOlecmdid = 0;
for (int i = 0; i < ARRAYSIZE(c_CmdTable); i++) { if (c_CmdTable[i].uiSfvidm == uiSfvidm) { dwOlecmdid = c_CmdTable[i].dwOlecmdid; break; } }
return dwOlecmdid; }
void HideIE4DesktopChannelBar() { HWND hwndChannelBar; //Check if the channel bar is currently running. If so, turn it off!
if ((hwndChannelBar = FindWindowEx(GetShellWindow(), NULL, TEXT("BaseBar"), TEXT("ChanApp"))) || (hwndChannelBar = FindWindowEx(NULL, NULL, TEXT("BaseBar"), TEXT("ChanApp")))) // can be a toplevel window
{ //Close the channel bar.
PostMessage(hwndChannelBar, WM_CLOSE, 0, 0); } }
// Wrapper around _SwitchToWebView to do desktop-specific stuff
LRESULT CDefView::_SwitchDesktopHTML(BOOL fShow) { LRESULT lRes;
if (fShow) { // Do this early to give the desktop a chance to regenerate it's webview template
_CallRefresh(TRUE);
lRes = SUCCEEDED(_SwitchToWebView(TRUE));
if (lRes) { HideIE4DesktopChannelBar(); } } else { _SwitchToWebView(FALSE); CoFreeUnusedLibraries(); lRes = TRUE; }
return lRes; } void CDefView::_DoColumnsMenu(int x, int y) // X and Y are screen coordinates
{ HMENU hmenu = CreatePopupMenu(); if (hmenu) { AddColumnsToMenu(hmenu, SFVIDM_COLUMN_FIRST);
int item = TrackPopupMenu(hmenu, TPM_TOPALIGN | TPM_LEFTALIGN | TPM_RIGHTBUTTON | TPM_NONOTIFY | TPM_RETURNCMD, x, y, 0, _hwndListview, NULL); DestroyMenu(hmenu);
// validate item first
if (item == SFVIDM_VIEW_COLSETTINGS) { CColumnDlg ccd(this);
AddColumns();
ccd.ShowDialog(_hwndMain); } else if (item > SFVIDM_COLUMN_FIRST) { _HandleColumnToggle(item - SFVIDM_COLUMN_FIRST, TRUE); } } }
BOOL CDefView::_ArrangeBy(UINT idCmd) { int iColumn = idCmd - SFVIDM_GROUPSFIRST; BOOL fAllowToggle = TRUE;
// We want to enter group by if We already have a group, or if this is an extended grouping
if ((_fGroupView || InRange(idCmd, SFVIDM_GROUPSEXTENDEDFIRST, SFVIDM_GROUPSEXTENDEDLAST)) && !(_fs.ViewMode == FVM_LIST)) { _GroupBy(idCmd); iColumn = 0; // Arrange by name, when grouping
fAllowToggle = FALSE; // Always arrange in ascending order
} return S_OK == _OnRearrange(iColumn, fAllowToggle); }
BOOL CDefView::_InitArrangeMenu(HMENU hmInit) { MENUITEMINFO mii = {0};
mii.cbSize = sizeof(mii); mii.fMask = MIIM_SUBMENU; GetMenuItemInfo(hmInit, SFVIDM_MENU_ARRANGE, MF_BYCOMMAND, &mii); HMENU hmenuCtx = mii.hSubMenu;
if (hmenuCtx) { int idToCheck = -1; AddColumns(); UINT cVisible = _RealToVisibleCol(-1) + 1; // count
ICategoryProvider* pcp = NULL; _pshf->CreateViewObject(NULL, IID_PPV_ARG(ICategoryProvider, &pcp));
while (1) { MENUITEMINFO miiSep = {0}; miiSep.cbSize = sizeof(mii); miiSep.fMask = MIIM_ID | MIIM_TYPE; miiSep.wID = -1;
if (!GetMenuItemInfo(hmenuCtx, 0, MF_BYPOSITION, &miiSep) || miiSep.wID == SFVIDM_GROUPSEP) { break; }
DeleteMenu(hmenuCtx, 0, MF_BYPOSITION); }
UINT iInsert = 0; for (UINT i = 0; i < cVisible; i++) { BOOL fAddItem = TRUE; UINT iReal = _VisibleToRealCol(i); if (_IsDetailsColumn(iReal)) {
// See if the category Provider wants to exclude this column when groupview is enabled
if (pcp && _fGroupView) { SHCOLUMNID scid; if (SUCCEEDED(_pshf2->MapColumnToSCID(iReal, &scid))) { // returns S_FALSE to remove.
fAddItem = (S_OK == pcp->CanCategorizeOnSCID(&scid)); } }
if (fAddItem) { WCHAR wszName[MAX_COLUMN_NAME_LEN]; BOOL bpuiName = FALSE; IPropertyUI *ppui;
// Attempt to retrieve mnemonic name from IPropertyUI interface.
if (_pshf2 && SUCCEEDED(_GetPropertyUI(&ppui))) { SHCOLUMNID scid;
if (SUCCEEDED(_pshf2->MapColumnToSCID(iReal, &scid))) { bpuiName = SUCCEEDED(ppui->GetDisplayName(scid.fmtid, scid.pid, PUIFNF_MNEMONIC, wszName, ARRAYSIZE(wszName))); }
ppui->Release(); }
MENUITEMINFO miiItem = {0}; miiItem.cbSize = sizeof(mii); miiItem.fMask = MIIM_ID | MIIM_TYPE; miiItem.fType = MFT_STRING; miiItem.wID = iReal + SFVIDM_GROUPSFIRST; miiItem.dwTypeData = bpuiName ? wszName : _vs.GetColumnName(iReal); InsertMenuItem(hmenuCtx, iInsert++, TRUE, &miiItem); } } }
_InitExtendedGroups(pcp, hmenuCtx, iInsert, &idToCheck);
// Only do the Bullets if we're in auto arrange mode or if we are in details.
if (_IsAutoArrange() || _fGroupView || _fs.ViewMode == FVM_DETAILS) { if (idToCheck == -1) { // Since we're not going to have more than 4million columns, this case should suffice
idToCheck = (int)_vs._lParamSort + SFVIDM_GROUPSFIRST; if (_fGroupView && !(_fs.ViewMode == FVM_LIST)) { idToCheck = MapSCIDToColumn(_pshf2, &_vs._scidDetails) + SFVIDM_GROUPSFIRST; } }
CheckMenuRadioItem(hmenuCtx, SFVIDM_GROUPSFIRST, SFVIDM_GROUPSEXTENDEDLAST, idToCheck, MF_BYCOMMAND | MF_CHECKED); }
if (pcp) pcp->Release(); }
DWORD dwGroupEnableFlags = MF_GRAYED; if (_pshf2 && // Needs to implement IShellFolder2
!_IsViewDesktop() && // Doesn't work on the desktop
!(_fs.ViewMode == FVM_LIST) && // Doesn't work in 'List' View
!(_fs.ViewMode == FVM_THUMBSTRIP) &&// Doesn't work in 'ThumbStrip' View
!(_fs.fFlags & FWF_OWNERDATA)) // Doesn't work for ownerdata lists (search)
{ dwGroupEnableFlags = MF_ENABLED; CheckMenuItem(hmenuCtx, SFVIDM_GROUPBY, MF_BYCOMMAND | (_fGroupView?MF_CHECKED:0)); }
EnableMenuItem(hmenuCtx, SFVIDM_GROUPBY, MF_BYCOMMAND | dwGroupEnableFlags);
_SHPrettyMenu(hmenuCtx);
return TRUE; }
BOOL CDefView::_InitExtendedGroups(ICategoryProvider* pcp, HMENU hmenuCtx, int iIndex, int* piIdToCheck) { if (!pcp) return FALSE;
*piIdToCheck = -1; if (_hdaCategories == NULL) { _hdaCategories = DSA_Create(sizeof(GUID), 5); if (_hdaCategories) { IEnumGUID* penum; if (SUCCEEDED(pcp->EnumCategories(&penum))) { GUID guidCat; while (S_OK == penum->Next(1, &guidCat, NULL)) { DSA_AppendItem(_hdaCategories, (void*)&guidCat); }
penum->Release(); } } }
if (_hdaCategories) { int id = SFVIDM_GROUPSEXTENDEDFIRST; TCHAR szName[MAX_PATH]; TCHAR szCurrentName[MAX_PATH]; WCHAR wszName[MAX_PATH]; GUID* pguidCat;
szCurrentName[0] = 0;
if (_pcat) { _pcat->GetDescription(szCurrentName, ARRAYSIZE(szCurrentName)); }
MENUITEMINFO mii = {0}; mii.cbSize = sizeof(MENUITEMINFO); mii.fMask = MIIM_ID | MIIM_TYPE; mii.fType = MFT_SEPARATOR; mii.wID = -1;
InsertMenuItem(hmenuCtx, iIndex, TRUE, &mii);
iIndex++;
int cCategories = DSA_GetItemCount(_hdaCategories); for (int i = 0; i < cCategories; i++) { pguidCat = (GUID*)DSA_GetItemPtr(_hdaCategories, i);
if (SUCCEEDED(pcp->GetCategoryName(pguidCat, wszName, ARRAYSIZE(wszName)))) { SHUnicodeToTChar(wszName, szName, ARRAYSIZE(szName));
MENUITEMINFO mii = {0}; mii.cbSize = sizeof(MENUITEMINFO); mii.fMask = MIIM_DATA | MIIM_TYPE | MIIM_ID; mii.fType = MFT_STRING; mii.dwItemData = (DWORD_PTR)pguidCat; mii.wID = id; mii.dwTypeData = szName; mii.cch = ARRAYSIZE(szName);
InsertMenuItem(hmenuCtx, iIndex, TRUE, &mii);
if (lstrcmpi(szCurrentName, szName) == 0) { *piIdToCheck = id; }
id++; iIndex++; } } }
return TRUE; }
BOOL CDefView::_CategorizeOnSCID(const SHCOLUMNID* pscid) { BOOL fRet = FALSE;
_fSlowGroup = FALSE; if (IsEqualSCID(*pscid, SCID_NAME)) { if (SUCCEEDED(CAlphaCategorizer_Create(_pshf2, IID_PPV_ARG(ICategorizer, &_pcat)))) { _vs._guidGroupID = CLSID_AlphabeticalCategorizer; fRet = TRUE; } } else if (IsEqualSCID(*pscid, SCID_SIZE)) { if (SUCCEEDED(CSizeCategorizer_Create(_pshf2, IID_PPV_ARG(ICategorizer, &_pcat)))) { _vs._guidGroupID = CLSID_SizeCategorizer; fRet = TRUE; } } else if (IsEqualSCID(*pscid, SCID_WRITETIME) || IsEqualSCID(*pscid, SCID_CREATETIME) || IsEqualSCID(*pscid, SCID_ACCESSTIME) || IsEqualSCID(*pscid, SCID_DATEDELETED)) { if (SUCCEEDED(CTimeCategorizer_Create(_pshf2, pscid, IID_PPV_ARG(ICategorizer, &_pcat)))) { _vs._guidGroupID = CLSID_TimeCategorizer; fRet = TRUE; } } else { _fSlowGroup = TRUE; if (SUCCEEDED(CDetailCategorizer_Create(*pscid, _pshf2, IID_PPV_ARG(ICategorizer, &_pcat)))) { _vs._guidGroupID = CLSID_DetailCategorizer; fRet = TRUE; } }
if (fRet) { _vs._scidDetails = *pscid; }
return fRet; }
// slow groups have an architecture problem, after 5000 items in the view
// the message queue overflows from groupdone messages and its all bad.
// this ends up hanging the static flashlight window around because of resulting
// refcount issues.
// the only view that both defaults to a slow group and could have 5000 items is the
// cd burning folder. lou says its too late to change the interface now to let the
// categorizer decide if its slow or not, so just special case it here.
// everything works if its a fast group (and its actually fast anyway).
BOOL CDefView::_IsSlowGroup(const GUID *pguid) { BOOL fSlow = TRUE; if (IsEqualGUID(*pguid, CLSID_MergedCategorizer)) { fSlow = FALSE; } // room to grow if we need to special case others
return fSlow; }
BOOL CDefView::_CategorizeOnGUID(const GUID* pguid, const SHCOLUMNID* pscid) { BOOL fRet = FALSE; if (_pshf2) { _fGroupView = FALSE; // Just in case the create fails
if (_pScheduler) _pScheduler->RemoveTasks(TOID_DVBackgroundGroup, ITSAT_DEFAULT_LPARAM, TRUE);
ATOMICRELEASE(_pcat);
ListView_RemoveAllGroups(_hwndListview);
ICategoryProvider* pcp; if (SUCCEEDED(_pshf->CreateViewObject(NULL, IID_PPV_ARG(ICategoryProvider, &pcp)))) { GUID guidGroup = *pguid; if (pscid && S_OK != pcp->GetCategoryForSCID(const_cast<SHCOLUMNID*>(pscid), &guidGroup)) { fRet = _CategorizeOnSCID(pscid); } else { _fSlowGroup = _IsSlowGroup(&guidGroup); if (SUCCEEDED(pcp->CreateCategory(&guidGroup, IID_PPV_ARG(ICategorizer, &_pcat)))) { _vs._guidGroupID = guidGroup; if (pscid) { _vs._scidDetails = *pscid; } else { ZeroMemory(&_vs._scidDetails, sizeof(_vs._scidDetails)); }
fRet = TRUE; } } pcp->Release(); } else { if (pscid) fRet = _CategorizeOnSCID(pscid); } }
if (fRet) { _ClearItemPositions(); _fGroupView = TRUE; SHSetWindowBits(_hwndListview, GWL_STYLE, LVS_AUTOARRANGE, LVS_AUTOARRANGE);
// We're enabling groupview, so turn off the selected column
// (this will make it so tiles do not show the selected column as their first column)
ListView_SetSelectedColumn(_hwndListview, -1);
if (_fSlowGroup) _fAllowSearchingWindow = TRUE;
ListView_EnableGroupView(_hwndListview, TRUE); }
return fRet; }
void CDefView::_GroupBy(int iColumn) { _fGroupView = FALSE; // Just in case the create fails
if (_pshf2) { if (InRange(iColumn, SFVIDM_GROUPSEXTENDEDFIRST, SFVIDM_GROUPSEXTENDEDLAST)) { int iIndex = iColumn - SFVIDM_GROUPSEXTENDEDFIRST; GUID* pguid = (GUID*)DSA_GetItemPtr(_hdaCategories, iIndex); if (pguid) { _CategorizeOnGUID(pguid, NULL); } } else { SHCOLUMNID scid; iColumn -= SFVIDM_GROUPSFIRST;
if (SUCCEEDED(_pshf2->MapColumnToSCID(iColumn, &scid))) { _CategorizeOnGUID(&CLSID_DetailCategorizer, &scid); } }
// Make sure the arrows on details view look right...
_SetSortFeedback(); } }
void CDefView::_ToggleGrouping() { if (_fGroupView) { _fGroupView = FALSE; if (_pScheduler) _pScheduler->RemoveTasks(TOID_DVBackgroundGroup, ITSAT_DEFAULT_LPARAM, TRUE);
ListView_EnableGroupView(_hwndListview, FALSE); ListView_RemoveAllGroups(_hwndListview); ListView_SetSelectedColumn(_hwndListview, _vs._lParamSort); _SetSortFeedback(); _OnRearrange(_vs._lParamSort, FALSE); } else if (FVM_THUMBSTRIP != _fs.ViewMode) // Thumbstrip can never go into groupby mode
{ // If we have a categorizer, then we can just reenable grouping.
if (_pcat) { _fGroupView = TRUE;
ListView_EnableGroupView(_hwndListview, TRUE); ListView_SetSelectedColumn(_hwndListview, -1); _SetSortFeedback(); } else { // If we don't, then we need to go get one.
_GroupBy((int)_vs._lParamSort + SFVIDM_GROUPSFIRST); } } }
LRESULT CDefView::_OnDefviewEditCommand(UINT uID) { // if we are in label edit mode, don't allowany of the buttons......
if (_fInLabelEdit) { MessageBeep(0); return 1; }
if (_AllowCommand(uID)) { HRESULT hr = _ExplorerCommand(uID); if (FAILED(hr) && (hr != HRESULT_FROM_WIN32(ERROR_CANCELLED))) { MessageBeep(0); } return 1; } else { return 0; } }
HRESULT CDefView::_DoMoveOrCopyTo(REFCLSID clsid, IShellItemArray *psiItemArray) {
IDataObject *pdo = NULL; IContextMenu *pcm; HRESULT hr = E_FAIL;
if (!psiItemArray) { psiItemArray = _GetFolderAsShellItemArray(); }
if (psiItemArray) { hr = psiItemArray->BindToHandler(NULL,BHID_DataObject,IID_PPV_ARG(IDataObject, &pdo)); }
if (SUCCEEDED(hr)) {
hr = SHCoCreateInstance(NULL, &clsid, NULL, IID_PPV_ARG(IContextMenu, &pcm)); if (SUCCEEDED(hr)) { IUnknown_SetSite(pcm, SAFECAST(this, IDropTarget *)); // Needed to go modal during UI
IShellExtInit* psei; hr = pcm->QueryInterface(IID_PPV_ARG(IShellExtInit, &psei)); if (SUCCEEDED(hr)) { LPITEMIDLIST pidlFolder = _GetViewPidl(); if (pidlFolder) { psei->Initialize(pidlFolder, pdo, NULL); ILFree(pidlFolder); }
CMINVOKECOMMANDINFO ici = {0};
ici.hwnd = _hwndMain; hr = pcm->InvokeCommand(&ici);
psei->Release(); }
IUnknown_SetSite(pcm, NULL); pcm->Release(); }
pdo->Release(); }
return hr; }
LRESULT CDefView::_OnCommand(IContextMenu *pcmToInvoke, WPARAM wParam, LPARAM lParam) { UINT uID = GET_WM_COMMAND_ID(wParam, lParam);
if (InRange(uID, SFVIDM_GROUPSFIRST, SFVIDM_GROUPSEXTENDEDLAST)) { _ArrangeBy(uID); return 1; } else if (InRange(uID, SFVIDM_CONTEXT_FIRST, SFVIDM_CONTEXT_LAST)) { UINT uCMBias = SFVIDM_CONTEXT_FIRST;
if (_pcmFile) { IContextMenu* pcmToInvoke = _pcmFile; pcmToInvoke->AddRef();
// We need to special case the rename command
TCHAR szCommandString[64]; ContextMenu_GetCommandStringVerb(pcmToInvoke, uID - SFVIDM_CONTEXT_FIRST, szCommandString, ARRAYSIZE(szCommandString)); if (lstrcmpi(szCommandString, c_szRename) == 0) { DoRename(); } else { CMINVOKECOMMANDINFOEX ici = { 0 };
ici.cbSize = sizeof(CMINVOKECOMMANDINFOEX); ici.hwnd = _hwndMain; ici.lpVerb = (LPSTR)MAKEINTRESOURCE(uID - SFVIDM_CONTEXT_FIRST); ici.nShow = SW_NORMAL; ici.fMask = CMIC_MASK_FLAG_LOG_USAGE;
int iItemSelect = ListView_GetNextItem(_hwndListview, -1, LVNI_SELECTED); if (iItemSelect != -1) { RECT rcItem; ListView_GetItemRect(_hwndListview, iItemSelect, &rcItem, LVIR_BOUNDS); MapWindowPoints(_hwndListview, HWND_DESKTOP, (POINT *)&rcItem, 2); ici.ptInvoke.x = (rcItem.left + rcItem.right) / 2; ici.ptInvoke.y = (rcItem.top + rcItem.bottom) / 2; ici.fMask |= CMIC_MASK_PTINVOKE; }
// record if shift or control was being held down
SetICIKeyModifiers(&ici.fMask);
_InvokeContextMenu(pcmToInvoke, &ici); }
//Since we are releaseing our only hold on the context menu, release the site.
IUnknown_SetSite(pcmToInvoke, NULL);
pcmToInvoke->Release(); // undo our gaurd ref
ATOMICRELEASE(_pcmFile); // once used, it can't be used again
}
return 0; } #ifdef DEBUG
else if (InRange(uID, SFVIDM_BACK_CONTEXT_FIRST, SFVIDM_BACK_CONTEXT_LAST)) { RIPMSG(FALSE, "_OnCommand should not get this context menu invoke..."); } #endif
else if (InRange(uID, SFVIDM_CLIENT_FIRST, SFVIDM_CLIENT_LAST) && HasCB()) { // view callback range
CallCB(SFVM_INVOKECOMMAND, uID - SFVIDM_CLIENT_FIRST, 0); return 0; }
// First check for commands that always go to this defview
switch (uID) { case SFVIDM_GROUPBY: _ToggleGrouping(); break;
case SFVIDM_EDIT_UNDO: // if we are in label edit mode, don't allowany of the buttons......
if (_fInLabelEdit) { MessageBeep(0); return 0; }
Undo(_hwndMain); break;
case SFVIDM_VIEW_COLSETTINGS: { CColumnDlg ccd(this);
AddColumns();
ccd.ShowDialog(_hwndMain); break; }
case SFVIDM_VIEW_VIEWMENU: { // if we are in label edit mode, don't allow any of the buttons......
if (_fInLabelEdit) { MessageBeep(0); return 0; }
LPCDFVCMDDATA pcd = (LPCDFVCMDDATA)lParam; if (pcd && pcd->pva && pcd->pva->byref) { LPRECT prect = (LPRECT)pcd->pva->byref;
IContextMenu* pcm; if (SUCCEEDED(_Create_BackgrndHMENU(TRUE, IID_PPV_ARG(IContextMenu, &pcm)))) { POINT pt = { prect->left, prect->bottom};
DoContextMenuPopup(pcm, 0, pt);
pcm->Release(); } } } break;
case SFVIDM_VIEW_TILE: //
// AppCompat: Pre WinXP 0x702E used to be SFVIDM_VIEW_VIEWMENU, now it's SFVIDM_VIEW_TILE.
// Corel apps send 0x702E to get the ViewMenu on the SaveAs dialogs. Of course that no
// longer works since 0x702E switches them to TileMode. Luckily SFVIDM_VIEW_VIEWMENU has
// a non-NULL lParam while SFVIDM_VIEW_TILE always has a NULL lParam so we can tell the
// two apart. So when Corel sends a 0x702E with a non-NULL lParam they mean SFVIDM_VIEW_VIEWMENU
// and when they send a 0x702E with a NULL lParam they mean SFVIDM_VIEW_TILE.
//
COMPILETIME_ASSERT(SFVIDM_VIEW_TILE == 0x702E); //see above app compat comments.
if (lParam && (SHGetAppCompatFlags(ACF_WIN95DEFVIEW) & ACF_WIN95DEFVIEW)) { return _OnCommand(pcmToInvoke, SFVIDM_VIEW_VIEWMENU, lParam); // change this into a SFVIDM_VIEW_VIEWMENU
} // Fall through ...
case SFVIDM_VIEW_ICON: case SFVIDM_VIEW_SMALLICON: case SFVIDM_VIEW_THUMBNAIL: case SFVIDM_VIEW_THUMBSTRIP: case SFVIDM_VIEW_LIST: case SFVIDM_VIEW_DETAILS: COMPILETIME_ASSERT(FVM_ICON == (SFVIDM_VIEW_ICON-SFVIDM_VIEW_FIRST)); COMPILETIME_ASSERT(FVM_SMALLICON == (SFVIDM_VIEW_SMALLICON-SFVIDM_VIEW_FIRST)); COMPILETIME_ASSERT(FVM_THUMBNAIL == (SFVIDM_VIEW_THUMBNAIL-SFVIDM_VIEW_FIRST)); COMPILETIME_ASSERT(FVM_THUMBSTRIP == (SFVIDM_VIEW_THUMBSTRIP-SFVIDM_VIEW_FIRST)); COMPILETIME_ASSERT(FVM_LIST == (SFVIDM_VIEW_LIST-SFVIDM_VIEW_FIRST)); COMPILETIME_ASSERT(FVM_TILE == (SFVIDM_VIEW_TILE-SFVIDM_VIEW_FIRST)); COMPILETIME_ASSERT(FVM_DETAILS == (SFVIDM_VIEW_DETAILS-SFVIDM_VIEW_FIRST));
SetCurrentViewMode(uID - SFVIDM_VIEW_FIRST); break;
case SFVIDM_DESKTOPHTML_WEBCONTENT: { // we have removed this button, but we need to keep this for message for other things
BOOL bHasVisibleNonLocalPicture = FALSE; SHELLSTATE ss;
SHGetSetSettings(&ss, SSF_DESKTOPHTML, FALSE); // Get the setting
ss.fDesktopHTML = !ss.fDesktopHTML; // Toggle the state
if (ss.fDesktopHTML && !IsICWCompleted()) { IActiveDesktop *pIAD; if (SUCCEEDED(SHCoCreateInstance(NULL, &CLSID_ActiveDesktop, NULL, IID_PPV_ARG(IActiveDesktop, &pIAD)))) { bHasVisibleNonLocalPicture = (DisableUndisplayableComponents(pIAD) != 0); pIAD->Release(); } } if (!bHasVisibleNonLocalPicture) { SHELLSTATE ss2;
SHGetSetSettings(&ss, SSF_DESKTOPHTML, TRUE); // Write back the new
// Now read back the current setting - only call _SwitchDesktopHTML if the current
// setting and the one we just set agree. If they don't that means someone changed
// the setting during the above call and we shouldn't do any more work or our state
// will get messed up.
SHGetSetSettings(&ss2, SSF_DESKTOPHTML, FALSE); if (ss.fDesktopHTML == ss2.fDesktopHTML) { _SwitchDesktopHTML(BOOLIFY(ss.fDesktopHTML)); } } } break;
case SFVIDM_DESKTOPHTML_ICONS: case SFVIDM_ARRANGE_DISPLAYICONS: // (buzzr) I'm leaving SFVIDM_ARRANGE_DISPLAYICONS
{ // for backwards compat. It used to be a
SHELLSTATE ss; // menu entry on POPUP_SFV_BACKGROUND.
DWORD dwValue;
// Toggle the cached state
_fs.fFlags ^= FWF_NOICONS;
ss.fHideIcons = ((_fs.fFlags & FWF_NOICONS) != 0); dwValue = ss.fHideIcons ? 1 : 0;
// Since this value is currrently stored under the "advanced" reg tree we need
// to explicitly write to the registry or the value won't persist properly via
// SHGetSetSettings.
SHSetValue(HKEY_CURRENT_USER, TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Advanced"), TEXT("HideIcons"), REG_DWORD, &dwValue, sizeof(dwValue));
// Finally set the ShellState and perform the action!
SHGetSetSettings(&ss, SSF_HIDEICONS, TRUE); // Since this SFVIDM_ comes from the menu, we better already be active (or
// this SW_SHOW could make us visible before we want to be seen).
ASSERT(_uState != SVUIA_DEACTIVATE); ActiveDesktop_ApplyChanges(); ShowHideListView(); } break;
case SFVIDM_DESKTOPHTML_LOCK: { DWORD dwFlags = GetDesktopFlags(); dwFlags ^= COMPONENTS_LOCKED; SetDesktopFlags(COMPONENTS_LOCKED, dwFlags); ActiveDesktop_ApplyChanges(); } break;
case SFVIDM_DESKTOPHTML_WIZARD: { // launch desktop cleanup wizard
SHRunDLLThread(NULL, TEXT("fldrclnr.dll,Wizard_RunDLL all"), SW_SHOWNORMAL); } break;
case SFVIDM_EDIT_COPYTO: case SFVIDM_EDIT_MOVETO: { // if we are in label edit mode, don't allowany of the buttons......
if (_fInLabelEdit) { MessageBeep(0); return 0; }
if (_pSelectionShellItemArray) { _DoMoveOrCopyTo(((uID == SFVIDM_EDIT_COPYTO) ? CLSID_CopyToMenu : CLSID_MoveToMenu), _pSelectionShellItemArray); } } break;
case SFVIDM_FILE_PROPERTIES:
if (SHRestricted(REST_NOVIEWCONTEXTMENU)) break;
// else fall through...
case SFVIDM_EDIT_PASTE: case SFVIDM_EDIT_PASTELINK: case SFVIDM_EDIT_COPY: case SFVIDM_EDIT_CUT: case SFVIDM_FILE_LINK: case SFVIDM_FILE_DELETE: if (!_OnDefviewEditCommand(uID)) { // REVIEW: this looks like a hack.
// there's got to be a cleaner way of doing this...
//
LPDFVCMDDATA pcd = (LPDFVCMDDATA)lParam; // Try translating the SFVIDM value into a standard
// OLECMDID value, so that the caller can try applying
// it to a different object.
// doh
if (!IsBadWritePtr(pcd, sizeof(*pcd))) { pcd->nCmdIDTranslated = OlecmdidFromSfvidm(uID); } } break;
case SFVIDM_TOOL_OPTIONS: if (!SHRestricted(REST_NOFOLDEROPTIONS)) { IUnknown_Exec(_psb, &CGID_Explorer, SBCMDID_OPTIONS, 0, NULL, NULL); } break;
#ifdef DEBUG
case SFVIDM_DEBUG_WEBVIEW: _cFrame._ShowWebViewContent(); break; #endif // DEBUG
case SFVIDM_HELP_TOPIC: // Don't call WinHelp when we are in the common dialog.
if (!_IsCommonDialog()) { // Use a callback to see if the namespace has requested a different help file name and/or topic
SFVM_HELPTOPIC_DATA htd; HWND hwndDesktop = GetDesktopWindow(); SHTCharToUnicode(c_szHtmlWindowsHlp, htd.wszHelpFile, ARRAYSIZE(htd.wszHelpFile)); htd.wszHelpTopic[0] = 0; if (SUCCEEDED(CallCB(SFVM_GETHELPTOPIC, 0, (LPARAM)&htd))) { if (URL_SCHEME_MSHELP == GetUrlSchemeW(htd.wszHelpTopic)) { //
// Callback specified an HSS help URL.
//
SHELLEXECUTEINFOW sei = {0}; sei.cbSize = sizeof(sei); sei.lpFile = htd.wszHelpTopic; sei.hwnd = hwndDesktop; sei.nShow = SW_NORMAL; // executing help topic
ShellExecuteExW(&sei); } else { HtmlHelp(hwndDesktop, htd.wszHelpFile, HH_HELP_FINDER, htd.wszHelpTopic[0] ? (DWORD_PTR)htd.wszHelpTopic : 0); } } else { // ask the shell dispatch object to display Help for us
IShellDispatch *psd; if (SUCCEEDED(SHCoCreateInstance(NULL, &CLSID_Shell, NULL, IID_PPV_ARG(IShellDispatch, &psd)))) { psd->Help(); psd->Release(); } } } break;
case SFVIDM_VIEW_CUSTOMWIZARD: _InvokeCustomization(); break;
case SFVIDM_MISC_HARDREFRESH: _fAllowSearchingWindow = TRUE; _FreeWebViewContentData();
_ReloadContent(TRUE); // have to enumerate before _GetDefaultViewMode() will be accurate
SetCurrentViewMode(_GetDefaultViewMode()); // even if fvm is the same, it will update webview if it changed
Refresh(); break;
case SFVIDM_MISC_SETWEBVIEW: SetCurrentViewMode(_fs.ViewMode); // re-setting the fvm updates everything (turning web view off can switch from Thumbstrip to Thumbnail!)
Refresh(); // we want to refresh when we switch turn webview on/off, since some icons appear/disappear on the transition
break;
case SFVIDM_MISC_REFRESH: _fAllowSearchingWindow = TRUE; Refresh(); break;
default: // check for commands that need to be sent to the active object
switch (uID) { case SFVIDM_ARRANGE_AUTO: _fs.fFlags ^= FWF_AUTOARRANGE; // toggle
_ClearItemPositions(); SHSetWindowBits(_hwndListview, GWL_STYLE, LVS_AUTOARRANGE, _IsAutoArrange() ? LVS_AUTOARRANGE : 0); break;
case SFVIDM_ARRANGE_GRID: ListView_Arrange(_hwndListview, LVA_SNAPTOGRID); break;
case SFVIDM_ARRANGE_AUTOGRID: { _fs.fFlags ^= FWF_SNAPTOGRID; DWORD dwLVFlags = ListView_GetExtendedListViewStyle(_hwndListview); dwLVFlags ^= LVS_EX_SNAPTOGRID; ListView_SetExtendedListViewStyle(_hwndListview, dwLVFlags);
//if this is desktop, we need to change the icon spacing.
UpdateGridSizes(_IsDesktop(), _hwndListview, 0, NULL, BOOLIFY(dwLVFlags & LVS_EX_SNAPTOGRID));
// if ActiveDesktop on, need to refresh, otherwise, can just arrange
SHELLSTATE ss = {0}; SHGetSetSettings( &ss, SSF_DESKTOPHTML, FALSE); if (ss.fDesktopHTML) { Refresh(); } else { if ((dwLVFlags & LVS_EX_SNAPTOGRID)) { ListView_Arrange(_hwndListview, LVA_SNAPTOGRID); } } } break;
default: // Normal view, we know what to do
switch (uID) { case SFVIDM_SELECT_ALL: { DECLAREWAITCURSOR;
if (CallCB(SFVM_SELECTALL, 0, 0) != S_FALSE) { SetWaitCursor(); SetFocus(_hwndListview); ListView_SetItemState(_hwndListview, -1, LVIS_SELECTED, LVIS_SELECTED); // make the first item in the view the focused guy
ListView_SetItemState(_hwndListview, 0, LVIS_FOCUSED, LVIS_FOCUSED); ResetWaitCursor(); } break; }
case SFVIDM_DESELECT_ALL: ListView_SetItemState(_hwndListview, -1, 0, LVIS_SELECTED); break;
case SFVIDM_SELECT_INVERT: { DECLAREWAITCURSOR; SetWaitCursor(); SetFocus(_hwndListview); int iItem = -1; while ((iItem = ListView_GetNextItem(_hwndListview, iItem, 0)) != -1) { // flip the selection bit on each item
UINT flag = ListView_GetItemState(_hwndListview, iItem, LVIS_SELECTED); flag ^= LVNI_SELECTED; ListView_SetItemState(_hwndListview, iItem, flag, LVIS_SELECTED); } ResetWaitCursor(); break; }
case SFVIDM_FILE_RENAME: DoRename(); break;
default: return 1; } } }
return 0; }
LPITEMIDLIST CDefView::_ObjectExists(LPCITEMIDLIST pidl, BOOL fGlobal) { LPITEMIDLIST pidlReal = NULL; // 365069 - global events also come through here - ZekeL - 16-APR-2001
// this means that that the pidl may not be one level. if its deeper
// then for us this item doesnt exist. this enforces our assert
if (pidl && !ILIsEmpty(pidl) && (!fGlobal || ILIsEmpty(_ILNext(pidl)))) { ASSERTMSG(ILFindLastID(pidl) == pidl, "defview doesnt expect recursive notification"); SHGetRealIDL(_pshf, pidl, &pidlReal); } return pidlReal; }
void CDefView::_OnRename(LPCITEMIDLIST* ppidl) { if (_pidlMonitor) { if (!ILIsParent(_pidlMonitor, ppidl[0], TRUE)) { // move to this folder
_OnFSNotify(SHCNE_CREATE, &ppidl[1]); } else if (!ILIsParent(_pidlMonitor, ppidl[1], TRUE)) { // move from this folder
_OnFSNotify(SHCNE_DELETE, &ppidl[0]); } else { // rename within this folder
// _pidlMonitor is guaranteed to be immediate parent of both pidls so ILFindLastID is okay.
LPCITEMIDLIST pidlOld = ILFindLastID(ppidl[0]); LPITEMIDLIST pidlNew = _ObjectExists(ILFindLastID(ppidl[1]), FALSE); if (pidlNew) { _UpdateObject(pidlOld, pidlNew); ILFree(pidlNew); } } } }
//
// SFVM_UPDATESTATUSBAR return values:
//
// failure code = Callback did not do anything, we must do it all
//
// Otherwise, the GetScode(hr) is a bitmask describing what the app
// wants us to do.
//
// 0 - App wants us to do nothing (S_OK) - message handled completely
// 1 - App wants us to set the default text (but not initialize)
//
// <other bits reserved for future use>
void CDefView::_UpdateStatusBar(BOOL fInitialize) { HRESULT hr;
// We have to clear the contents here since some clients (like the ftp client) return S_OK from
// the callback but do not set the text of the bar
HWND hwndStatus; if (_psb && SUCCEEDED(_psb->GetControlWindow(FCW_STATUS, &hwndStatus)) && hwndStatus) { _fBackgroundStatusTextValid = FALSE; SendMessage(hwndStatus, SB_SETTEXT, (WPARAM)0, (LPARAM)_TEXT("")); }
if (_bBkFilling || FAILED(hr = CallCB(SFVM_UPDATESTATUSBAR, fInitialize, 0))) { // Client wants us to do everything
_DoStatusBar(fInitialize); } else if (hr & SFVUSB_INITED) { // Client wants us to do text but not initialize
_DoStatusBar(FALSE); } }
// Returns TRUE iff we are supposed to show Web View content on this view.
// For the most part it follows SSF_WEBVIEW for normal folders and SSF_DESKTOPHTML for the desktop
//
BOOL CDefView::_ShouldShowWebView() { // No webview for common dialogs
if (_IsCommonDialog()) { return FALSE; }
// No webview in cleanboot mode
if (GetSystemMetrics(SM_CLEANBOOT)) return FALSE;
BOOL bForceWebViewOn; if (SUCCEEDED(CallCB(SFVM_FORCEWEBVIEW, (WPARAM)&bForceWebViewOn, 0))) { return bForceWebViewOn; }
// Quattro Pro (QPW) doesn't know how SHChangeNotify works,
// so when they want to refresh My Computer, they create an IShellView,
// invoke its CreateViewWindow(), invoke its Refresh(), then DestroyWindow
// the window and release the view. The IShellBrowser they pass
// to CreateViewWindow is allocated on the stack (!), and they expect
// that their Release() be the last one. Creating an async view keeps
// the object alive, so when the view is complete, we try to talk to the
// IShellBrowser and fault because it's already gone.
//
// The Zip Archives (from Aeco Systems) is another messed up App.
// They neither implement IPersistFolder2 (so we can't get their pidl) nor
// set the pidl to the shellfolderviewcb object. They don't implement
// IShellFolder2 either. Webview is practically useless for them.
//
// Adaptec Easy CD Creator 3.5 is in the same boat.
//
SHELLSTATE ss; SHGetSetSettings(&ss, SSF_WIN95CLASSIC | SSF_DESKTOPHTML | SSF_WEBVIEW, FALSE);
// If the "no web view" flag is set (potential WebOC case) then return false;
if (_fs.fFlags & FWF_NOWEBVIEW) return FALSE;
if (_IsDesktop()) { return ss.fDesktopHTML; } else { return ss.fWebView && !(SHGetAppCompatFlags(ACF_OLDCREATEVIEWWND) & ACF_OLDCREATEVIEWWND) && !(SHGetObjectCompatFlags(_pshf, NULL) & OBJCOMPATF_NO_WEBVIEW); } }
// takes ownership of pidlNew since _AddObject takes ownership.
void CDefView::_AddOrUpdateItem(LPCITEMIDLIST pidlOld, LPITEMIDLIST pidlNew) { if (_FindItem(pidlOld, NULL, FALSE) != -1) { _UpdateObject(pidlOld, pidlNew); ILFree(pidlNew); } else { // check if the shellfolder says this new guy shouldn't be enumerated.
if (!_Attributes(pidlNew, SFGAO_NONENUMERATED)) { _AddObject(pidlNew); // takes pidl ownership.
} else { ILFree(pidlNew); } } }
#define FSNDEBUG
// WM_DSV_FSNOTIFY message
LRESULT CDefView::_OnFSNotify(LONG lNotification, LPCITEMIDLIST* ppidl) { LPITEMIDLIST pidl; LPCITEMIDLIST pidlItem;
//
// Note that renames between directories are changed to
// create/delete pairs by SHChangeNotify.
//
#ifdef DEBUG
#ifdef FSNDEBUG
TCHAR szPath[MAX_PATH]; TraceMsg(TF_DEFVIEW, "CDefView::_OnFSNotify, hwnd = %d lEvent = %d", _hwndView, lNotification);
switch (lNotification) { case SHCNE_RENAMEITEM: case SHCNE_RENAMEFOLDER: // two pidls
SHGetPathFromIDList(ppidl[0], szPath); TraceMsg(TF_DEFVIEW, "CDefView::_OnFSNotify: hwnd %d, %s", _hwndView, szPath); SHGetPathFromIDList(ppidl[1], szPath); TraceMsg(TF_DEFVIEW, "CDefView::_OnFSNotify: hwnd %d, %s", _hwndView, szPath); break;
case SHCNE_CREATE: case SHCNE_DELETE: case SHCNE_MKDIR: case SHCNE_RMDIR: case SHCNE_MEDIAINSERTED: case SHCNE_MEDIAREMOVED: case SHCNE_DRIVEREMOVED: case SHCNE_DRIVEADD: case SHCNE_NETSHARE: case SHCNE_NETUNSHARE: case SHCNE_ATTRIBUTES: case SHCNE_UPDATEDIR: case SHCNE_UPDATEITEM: case SHCNE_SERVERDISCONNECT: case SHCNE_DRIVEADDGUI: case SHCNE_EXTENDED_EVENT: // one pidl
SHGetPathFromIDList(ppidl[0], szPath); TraceMsg(TF_DEFVIEW, "CDefView::_OnFSNotify: hwnd %d, %s", _hwndView, szPath); break;
case SHCNE_UPDATEIMAGE: // DWORD wrapped inside a pidl
TraceMsg(TF_DEFVIEW, "CDefView::_OnFSNotify: hwnd %d, %08x", _hwndView, ((LPSHChangeDWORDAsIDList)ppidl[0])->dwItem1); break;
case SHCNE_ASSOCCHANGED: // No parameters
break; } #endif
#endif
// we may be registered for notifications on pidls that are different from
// the one returned by _GetViewPidl (ftp folder).
switch (lNotification) { case SHCNE_DRIVEADD: case SHCNE_CREATE: case SHCNE_MKDIR: pidlItem = _pidlMonitor ? ILFindChild(_pidlMonitor, ppidl[0]) : NULL; pidl = _ObjectExists(pidlItem, FALSE); if (pidl) { _AddOrUpdateItem(pidlItem, pidl); } break;
case SHCNE_DRIVEREMOVED: case SHCNE_DELETE: case SHCNE_RMDIR: pidlItem = _pidlMonitor ? ILFindChild(_pidlMonitor, ppidl[0]) : NULL; if (pidlItem) { ASSERTMSG(ILFindLastID(pidlItem) == pidlItem, "defview doesnt expect recursive notification"); _RemoveObject((LPITEMIDLIST)pidlItem, FALSE); } break;
case SHCNE_RENAMEITEM: case SHCNE_RENAMEFOLDER: _OnRename(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[1]) { // this event is generated instead of a normal UPDATEIMAGE so that we can handle the
// cross process case....
// handle the notification
int iImage = SHHandleUpdateImage(ppidl[1]); if (iImage != -1) { _UpdateImage(iImage); } } else if (ppidl && ppidl[0]) { int iImage = *(int UNALIGNED *)((BYTE *)ppidl[0] + 2); _UpdateImage(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.
_ReloadContent(); break;
case SHCNE_ATTRIBUTES: // these all mean the same thing
case SHCNE_MEDIAINSERTED: case SHCNE_MEDIAREMOVED: case SHCNE_NETUNSHARE: case SHCNE_NETSHARE: case SHCNE_UPDATEITEM: if (ppidl) { LPCITEMIDLIST pidlOld = _pidlMonitor ? ILFindChild(_pidlMonitor, ppidl[0]) : NULL; LPITEMIDLIST pidlNew = _ObjectExists(pidlOld, SHCNE_GLOBALEVENTS & lNotification); if (pidlNew) { _AddOrUpdateItem(pidlOld, pidlNew); } else { // If we do not have any subobjects and the passed in pidl is the same as
// this views pidl then refresh all the items.
LPITEMIDLIST pidlView = _GetViewPidl(); if (pidlView) { if (ILIsEqual(ppidl[0], pidlView)) { _FullViewUpdate(SHCNE_UPDATEITEM == lNotification); } ILFree(pidlView); } } } else // ppidl == NULL means update all items (re-enum them)
{ _FullViewUpdate(SHCNE_UPDATEITEM == lNotification); } break;
case SHCNE_FREESPACE: TCHAR szPath[MAX_PATH]; if (_GetPath(szPath)) { int idDrive = PathGetDriveNumber(szPath); if (idDrive != -1) { DWORD dwChangedDrives = *(DWORD UNALIGNED *)((BYTE *)ppidl[0] + 2); if (((1 << idDrive) & dwChangedDrives)) { _UpdateStatusBar(TRUE); } } } break;
default: TraceMsg(TF_DEFVIEW, "DefView: unknown FSNotify %08lX, doing full update", lNotification); _FullViewUpdate(FALSE); break; }
_UpdateStatusBar(FALSE); return 0; }
// called when some of our objects get put on the clipboard
LRESULT CDefView::_OnSetClipboard(BOOL bMove) { if (bMove) // move
{ // mark all selected items as being "cut"
int i = -1; while ((i = ListView_GetNextItem(_hwndListview, i, LVIS_SELECTED)) != -1) { ListView_SetItemState(_hwndListview, i, LVIS_CUT, LVIS_CUT); _bHaveCutStuff = TRUE; }
// join the clipboard viewer chain so we will know when to
// "uncut" our selected items.
if (_bHaveCutStuff) { ASSERT(!_bClipViewer); ASSERT(_hwndNextViewer == NULL);
_hwndNextViewer = SetClipboardViewer(_hwndView); _bClipViewer = TRUE; } } return 0; }
// called when the clipboard get changed, clear any items in the "cut" state
//
LRESULT CDefView::_OnClipboardChange() { //
// if we dont have any cut stuff we dont care.
//
if (!_bHaveCutStuff) return 0;
ASSERT(_bClipViewer);
_RestoreAllGhostedFileView(); _bHaveCutStuff = FALSE;
//
// unhook from the clipboard viewer chain.
//
ChangeClipboardChain(_hwndView, _hwndNextViewer); _bClipViewer = FALSE; _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 CDefView::_GetDropPoint(POINT *ppt) { // Check whether we already have gotten the drop anchor (before any
// menu processing)
if (_bDropAnchor) { // We'll use the insert mark rect (if available) to determine a drop point
if (!_GetInsertPoint(ppt)) { *ppt = _ptDrop; // Otherwise use _ptDrop
LVUtil_ClientToLV(_hwndListview, ppt); } } else if (_bMouseMenu) { *ppt = _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(ppt); LVUtil_ScreenToLV(_hwndListview, ppt); }
return _bDropAnchor; }
// This uses the listview's insertmark to determinie an insert point
// Returns FALSE if a point could not be determined, TRUE otherwise
// The coordinates returned are in listview coordinate space.
BOOL CDefView::_GetInsertPoint(POINT *ppt) { if (_IsAutoArrange() || (_fs.fFlags & FWF_SNAPTOGRID)) { RECT rcInsert; if (ListView_GetInsertMarkRect(_hwndListview, &rcInsert)) { LONG dwStyle = GetWindowLong(_hwndListview, GWL_STYLE); BOOL fHorizontal = (_fs.fFlags & FWF_ALIGNLEFT); if (fHorizontal) { ppt->x = (rcInsert.right + rcInsert.left) / 2; // Drop in middle of insertmark rect
ppt->y = rcInsert.top; } else { ppt->x = rcInsert.left; ppt->y = (rcInsert.bottom + rcInsert.top) / 2; // Drop in middle of insertmark rect
} return TRUE; } }
return FALSE; }
BOOL CDefView::_GetDragPoint(POINT *ppt) { BOOL fSource = _bDragSource || _bMouseMenu; if (fSource) { // if anchor from mouse activity
*ppt = _ptDragAnchor; } else { // if anchor from keyboard activity... use the focused item
int i = ListView_GetNextItem(_hwndListview, -1, LVNI_FOCUSED); if (i != -1) { ListView_GetItemPosition(_hwndListview, i, ppt); } else { ppt->x = ppt->y = 0; } } return fSource; }
void CDefView::_PaintErrMsg(HWND hWnd) { PAINTSTRUCT ps; HDC hdc = BeginPaint(hWnd, &ps);
// if we're in an error state, make sure we're not in webview
if (_cFrame.IsWebView()) { _SwitchToWebView(FALSE); }
RECT rc; GetClientRect(hWnd, &rc);
DrawEdge(hdc, &rc, EDGE_SUNKEN, BF_RECT | BF_SOFT | BF_ADJUST | BF_MIDDLE);
EndPaint(hWnd, &ps); }
//
// The default status bar looks like this:
//
// No items selected: "nn object(s)" nn = total objects in folder
// One item selected: <InfoTip for selected item> if item supports InfoTip
// Else: "nn object(s) selected" nn = num selected objects
//
//
void CDefView::_DoStatusBar(BOOL fInitialize) { HWND hwndStatus; if (_psb && SUCCEEDED(_psb->GetControlWindow(FCW_STATUS, &hwndStatus)) && hwndStatus) { // Some of the failure cases do not null hwnd...
UINT uMsg = IDS_FSSTATUSSELECTED;
if (fInitialize) { int ciParts[] = {-1}; SendMessage(hwndStatus, SB_SETPARTS, ARRAYSIZE(ciParts), (LPARAM)ciParts); }
if (_bBkFilling && ListView_GetSelectedCount(_hwndListview) == 0) { _fBackgroundStatusTextValid = FALSE; LPWSTR pszStatus = ShellConstructMessageString(HINST_THISDLL, MAKEINTRESOURCE(IDS_FSSTATUSSEARCHING)); // We are not checking if the alloc succeeded in ShellConstructMessageString since both
// SendMessage and LocalFree can take NULL as inputs.
SendMessage(hwndStatus, SB_SETTEXT, (WPARAM)0, (LPARAM)pszStatus); LocalFree((void *)pszStatus); } else {
LPCITEMIDLIST *apidl = NULL;
int nMsgParam = ListView_GetSelectedCount(_hwndListview); switch (nMsgParam) { case 0: // No objects selected; show total item count
nMsgParam = ListView_GetItemCount(_hwndListview); uMsg = IDS_FSSTATUSBASE; break;
case 1: UINT cItems; GetSelectedObjects(&apidl, &cItems); break; }
LPITEMIDLIST pidlFolder = _GetViewPidl(); if (pidlFolder) { CStatusBarAndInfoTipTask *pTask; if (SUCCEEDED(CStatusBarAndInfoTipTask_CreateInstance(pidlFolder, apidl ? *apidl : NULL, uMsg, nMsgParam, NULL, _hwndView, _pScheduler, &pTask))) { if (_pScheduler) { // make sure there are no other status bar background tasks going on...
_pScheduler->RemoveTasks(TOID_DVBackgroundStatusBar, ITSAT_DEFAULT_LPARAM, FALSE); }
_fBackgroundStatusTextValid = TRUE; _AddTask(pTask, TOID_DVBackgroundStatusBar, 0, TASK_PRIORITY_INFOTIP, ADDTASK_ATEND); pTask->Release(); }
ILFree(pidlFolder); }
if (apidl) LocalFree(apidl); } } }
void CDefView::_OnWinIniChangeDesktop(WPARAM wParam, LPCTSTR pszSection) { if (pszSection) { if (!lstrcmpi(pszSection, TEXT("ToggleDesktop"))) { _OnCommand(NULL, SFVIDM_DESKTOPHTML_WEBCONTENT, 0); } else if (!lstrcmpi(pszSection, TEXT("RefreshDesktop"))) { if (FAILED(Refresh())) { SHELLSTATE ss;
//Refresh failed because the new template didn't exist
//Toggle the Registry settings back to Icons-only mode!
ss.fDesktopHTML = FALSE; SHGetSetSettings(&ss, SSF_DESKTOPHTML, TRUE); // Write back the new
} } else if (!lstrcmpi(pszSection, TEXT("BufferedRefresh"))) { //See if we have already started a timer to refresh
if (!_fRefreshBuffered) { TraceMsg(TF_DEFVIEW, "A Buffered refresh starts the timer"); SetTimer(_hwndView, DV_IDTIMER_BUFFERED_REFRESH, 5000, NULL); // 5 sec
_fRefreshBuffered = TRUE; } else //If refresh is already buffered, don't do anything!
{ TraceMsg(TF_DEFVIEW, "A buffered refresh occured while another is pending"); } } else { if (wParam == SPI_SETDESKWALLPAPER || wParam == SPI_SETDESKPATTERN) { _SetFolderColors(); } } } else { switch(wParam) { case SPI_SETDESKWALLPAPER: case SPI_SETDESKPATTERN:
_SetFolderColors(); break;
case SPI_ICONHORIZONTALSPACING: case SPI_ICONVERTICALSPACING:
if (_IsDesktop()) { DWORD dwLVExStyle = ListView_GetExtendedListViewStyle(_hwndListview); UpdateGridSizes(TRUE, _hwndListview, 0, NULL, BOOLIFY(dwLVExStyle & LVS_EX_SNAPTOGRID)); } break; } } }
void CDefView::_OnWinIniChange(WPARAM wParam, LPCTSTR pszSection) { if ((wParam == SPI_GETICONTITLELOGFONT) || ((wParam == 0) && pszSection && !lstrcmpi(pszSection, TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\IconUnderline")))) { _UpdateUnderlines(); }
if (pszSection && !lstrcmpi(pszSection, TEXT("VisualEffects"))) { Refresh(); }
// Why all this code? It's a rare event -- just kick off a refresh...
if (!wParam || (pszSection && !lstrcmpi(pszSection, TEXT("intl")))) { // has the time format changed while we're in details mode?
if (ViewRequiresColumns(_fs.ViewMode) && !_IsOwnerData()) { InvalidateRect(_hwndListview, NULL, TRUE);
// 99/04/13 #320903 vtan: If the date format has changed then iterate
// the entire list looking for extended columns of type date and
// resetting them to LPSTR_TEXTCALLBACK effectively dumping the cache.
// For performance improvement it's possible to collect an array of
// visible columns and reset that array. It will still involve TWO
// for loops.
int iItemCount = ListView_GetItemCount(_hwndListview); for (int iItem = 0; iItem < iItemCount; ++iItem) { for (UINT uiRealColumn = 0; uiRealColumn < _vs.GetColumnCount(); ++uiRealColumn) { DWORD dwFlags = _vs.GetColumnState(uiRealColumn); if (((dwFlags & SHCOLSTATE_EXTENDED) != 0) && ((dwFlags & SHCOLSTATE_TYPEMASK) == SHCOLSTATE_TYPE_DATE)) { UINT uiVisibleColumn = _RealToVisibleCol(uiRealColumn);
ListView_SetItemText(_hwndListview, iItem, uiVisibleColumn, LPSTR_TEXTCALLBACK); } } } } }
//
// we may need to rebuild the icon cache.
//
if (wParam == SPI_SETICONMETRICS || wParam == SPI_SETNONCLIENTMETRICS) { if (_IsImageMode()) { _SetThumbview(); } else if (_IsTileMode()) { _SetTileview(); } else { _SetSysImageList(); } }
//
// we need to invalidate the cursor cache
//
if (wParam == SPI_SETCURSORS) { DAD_InvalidateCursors(); }
if ((wParam == SPI_SETMENUANIMATION) && _pDUIView) { _pDUIView->ManageAnimations(FALSE); }
if (!wParam && !pszSection && _pDUIView) { if (_fBarrierDisplayed != _QueryBarricadeState()) { _fBarrierDisplayed = !_fBarrierDisplayed; _pDUIView->EnableBarrier (_fBarrierDisplayed); } }
if (_IsDesktop()) { _OnWinIniChangeDesktop(wParam, pszSection); } }
void CDefView::_SetDefaultViewSettings() { // only do this if we've actually shown the view...
// (ie, there's no _hwndStatic)
// and we're not the desktop
// and we're not an exstended view
// and we are not in an explorer (tree pane on)
if (!_hwndStatic && !_IsDesktop() && !IsExplorerBrowser(_psb)) { SHELLSTATE ss;
ss.lParamSort = (LONG)_vs._lParamSort; ss.iSortDirection = _vs._iDirection; SHGetSetSettings(&ss, SSF_SORTCOLUMNS, TRUE); } }
HWND CDefView::GetChildViewWindow() { if (_cFrame.IsWebView()) return _cFrame.GetExtendedViewWindow();
return _hwndListview; }
void CDefView::_SetFocus() { // if it's a combined view then we need to give focus to listview
if (!_fCombinedView && _cFrame.IsWebView() && !_fActivateLV) { _OnViewWindowActive();
if (_cFrame._pOleObj) { MSG msg = {_hwndView, WM_KEYDOWN, VK_TAB, 0xf0001};
// HACKHACK!!! MUST set state here! idealy shbrowse should call
// UIActivate on the view but that breaks dochost stuff.
// if we did not set the state here, trident would call
// CSFVSite::ActivateMe that would not forward the call to obj::UIActivate
// and therefore nothing would get focus (actually trident would have it
// but it would not be visible). Note that this behavior happens only
// second time around, i.e. on init UIActivate is called and everything
// works fine, but if we tab from address bar onto the view, that's when
// the stuff gets broken.
OnActivate(SVUIA_ACTIVATE_FOCUS); _cFrame._UIActivateIO(TRUE, &msg); } } else { CallCB(SFVM_SETFOCUS, 0, 0); if (_hwndListview) SetFocus(_hwndListview); if (!_IsDesktop()) { _cFrame._uState = SVUIA_ACTIVATE_FOCUS; } } }
LRESULT CALLBACK CDefView::s_WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { LRESULT lres = 0; CDefView * pThis; ULONG_PTR cookie = 0;
if (WM_NCCREATE == uMsg) { pThis = (CDefView*)((LPCREATESTRUCT)lParam)->lpCreateParams; if (pThis) { pThis->AddRef(); SetWindowLongPtr(hWnd, 0, (LONG_PTR)pThis); } } else { pThis = (CDefView*)GetWindowLongPtr(hWnd, 0); }
// FUSION: When defview calls out to 3rd party code we want it to use
// the process default context. This means that the 3rd party code will get
// v5 in the explorer process. However, if shell32 is hosted in a v6 process,
// then the 3rd party code will still get v6.
// Future enhancements to this codepath may include using the fusion manifest
// tab <noinherit> which basically surplants the activat(null) in the following
// codepath. This disables the automatic activation from user32 for the duration
// of this wndproc, essentially doing this null push.
ActivateActCtx(NULL, &cookie);
// we need to use a __try{}__finally{} block here to make sure that we de-activate
// the activation context. Fusion pushes and pops these off a stack and if we leave one
// around we will basically infect this thread with the wrong context.
__try { if (pThis) { lres = pThis->WndProc(hWnd, uMsg, wParam, lParam); } else { lres = DefWindowProc(hWnd, uMsg, wParam, lParam); } } __finally { if (cookie != 0) { DeactivateActCtx(0, cookie); } } return lres; }
BOOL CDefView::_OnAppCommand(UINT cmd, UINT uDevice, DWORD dwKeys) { BOOL bHandled = FALSE; switch (cmd) { case APPCOMMAND_MEDIA_PLAY_PAUSE: if (S_OK == _InvokeContextMenuVerbOnSelection("play", 0, 0)) bHandled = TRUE; break;
} return bHandled; }
HRESULT CDefView::_ForwardMenuMessages(DWORD dwID, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT* plResult, BOOL* pfHandled) { if (InRange(dwID, SFVIDM_BACK_CONTEXT_FIRST, SFVIDM_BACK_CONTEXT_LAST)) { if (pfHandled) *pfHandled = TRUE;
return SHForwardContextMenuMsg(_pcmContextMenuPopup, uMsg, wParam, lParam, plResult, TRUE); } else if (InRange(dwID, SFVIDM_CONTEXT_FIRST, SFVIDM_CONTEXT_LAST)) { if (pfHandled) *pfHandled = TRUE;
return SHForwardContextMenuMsg(_pcmFile, uMsg, wParam, lParam, plResult, TRUE); }
if (pfHandled) *pfHandled = FALSE;
return E_FAIL; }
LRESULT CDefView::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { LRESULT l; DWORD dwID;
switch (uMsg) { // IShellBrowser forwards these to the IShellView.
// Dochost also forwards them down to the IOleObject, so we should do it too...
case WM_SYSCOLORCHANGE: { HDITEM hdi = {HDI_FORMAT, 0, NULL, NULL, 0, 0, 0, 0, 0}; HWND hwndHead = ListView_GetHeader(_hwndListview);
// We only want to update the sort arrows if they are already present.
if (hwndHead) { Header_GetItem(hwndHead, _vs._lParamSort, &hdi); if (hdi.fmt & HDF_BITMAP) _SetSortFeedback(); }
// fall through
}
case WM_WININICHANGE: _sizeThumbnail.cx = -1;
// fall through
case WM_ENTERSIZEMOVE: case WM_EXITSIZEMOVE: case WM_FONTCHANGE: if (_cFrame.IsWebView()) { HWND hwndExt = _cFrame.GetExtendedViewWindow(); if (hwndExt) { SendMessage(hwndExt, uMsg, wParam, lParam); } } break; }
switch (uMsg) { case WM_DESTROY: if (GetKeyState(VK_CONTROL) < 0) _SetDefaultViewSettings();
// Dont need our web view data any more
_FreeWebViewContentData();
// We don't flush these on WM_EXITMENULOOP any more, so do it here
IUnknown_SetSite(_pcmFile, NULL); ATOMICRELEASE(_pcmFile);
EmptyBkgrndThread(_pScheduler); ATOMICRELEASE(_pScheduler);
// do this after our task scheduler is gone, since one of it's
// items may be on the background task scheduler (or DUI may be
// talking to it on the background) and it may need it's site chain.
IUnknown_SetSite(_cCallback.GetSFVCB(), NULL);
if (_pDiskCache) { // at this point we assume that we have no lock,
_pDiskCache->Close(NULL); ATOMICRELEASE(_pDiskCache); }
// 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...
// logically hWnd == _hwndView, but we already zeroed
// _hwndView so use hWnd
_ClearPostedMsgs(hWnd);
//
// remove ourself as a clipboard viewer
//
if (_bClipViewer) { ChangeClipboardChain(hWnd, _hwndNextViewer); _bClipViewer = FALSE; _hwndNextViewer = NULL; }
if (_uRegister) { ULONG uRegister = _uRegister; _uRegister = 0; SHChangeNotifyDeregister(uRegister); }
ATOMICRELEASE(_psd); ATOMICRELEASE(_pdtgtBack);
if (_hwndListview) { if (_IsDesktop()) // only the desktop can have a combined view (e.g. Active Desktop)
{ EnableCombinedView(this, FALSE); }
if (_bRegisteredDragDrop) RevokeDragDrop(_hwndListview); }
SetAutomationObject(NULL); // cleanup refs we may be holding
if (IsWindow(_hwndInfotip)) { DestroyWindow(_hwndInfotip); _hwndInfotip = NULL; }
break;
case WM_CREATE: return _OnCreate(hWnd);
case WM_DSV_DELAYED_DESTROYWND: DestroyWindow(hWnd); break;
case WM_NCDESTROY: _hwndView = NULL;
SetWindowLongPtr(hWnd, 0, 0);
// get rid of extra junk in the icon cache
IconCacheFlush(FALSE);
if (_pDUIView) { //
// We must uninitialize DUser prior to releasing
// _pDUIView so that all DUser gadgets are properly destroyed.
// We used to call DirectUI::UnInitThread() in the CDUIView dtor.
// However, since both CDefView and the various 'task' DUI
// elements maintain a ref to CDUIView, we got into scenarios where
// one of the 'task' elements held the final ref to CDUIView. That
// resulted in the destruction of that 'task' element causing
// uninitialization of DUser in the middle of a DUser call stack.
// That's bad.
// Uninitializing DUser here causes DUser to handle all pending
// messages and destroy all it's gadgets on it's own terms.
//
_pDUIView->UnInitializeDirectUI(); _pDUIView->Release(); _pDUIView = NULL; }
// release our reference generated during WM_NCCREATE in static wndproc
Release();
break;
case WM_ENABLE: _fDisabled = !wParam; break;
case WM_ERASEBKGND: { COLORREF cr = ListView_GetBkColor(_hwndListview); if (cr == CLR_NONE) return SendMessage(_hwndMain, uMsg, wParam, lParam);
//Turning On EraseBkgnd. This is required so as to avoid the
//painting issue - when the listview is not visible and
//invalidation occurs.
HBRUSH hbr = CreateSolidBrush(cr); RECT rc; GetClientRect(hWnd, &rc); FillRect((HDC)wParam, &rc, hbr); DeleteObject(hbr); } // We want to reduce flash
return 1;
case WM_PAINT: if (_fEnumFailed) _PaintErrMsg(hWnd); else goto DoDefWndProc; break;
case WM_LBUTTONUP: if (_fEnumFailed) PostMessage(hWnd, WM_KEYDOWN, (WPARAM)VK_F5, 0); else goto DoDefWndProc; break;
case WM_SETFOCUS: if (!_fDestroying) // Ignore if we are destroying _hwndView.
{ _SetFocus(); } break;
case WM_MOUSEACTIVATE: //
// this keeps our window from coming to the front on button down
// instead, we activate the window on the up click
//
if (LOWORD(lParam) != HTCLIENT) goto DoDefWndProc; LV_HITTESTINFO lvhti;
GetCursorPos(&lvhti.pt); ScreenToClient(_hwndListview, &lvhti.pt); ListView_HitTest(_hwndListview, &lvhti); if (lvhti.iItem != -1 && lvhti.flags & LVHT_ONITEM) return MA_NOACTIVATE; else return MA_ACTIVATE;
case WM_ACTIVATE: // force update on inactive to not ruin save bits
if (wParam == WA_INACTIVE) UpdateWindow(_hwndListview); // if active view created, call active object to allow it to visualize activation.
if (_cFrame._pActive) _cFrame._pActive->OnFrameWindowActivate((BOOL)wParam); break;
case WM_SIZE: return WndSize(hWnd);
case WM_NOTIFY: { #ifdef DEBUG
// DefView_OnNotify sometimes destroys the pnm, so we need to save
// the code while we can. (E.g., common dialog single-click activate.
// LVN_ITEMACTIVATE causes us to dismiss the common dialog, which
// does a DestroyViewWindow, which destroys the ListView
// which destroys the NMHDR!)
UINT code = ((NMHDR *)lParam)->code; #endif
AddRef(); // just in case
l = _OnNotify((NMHDR *)lParam); Release(); // release
return l; }
case WM_CONTEXTMENU: if (!_fDisabled) { if (lParam != (LPARAM) -1) { _bMouseMenu = TRUE; _ptDragAnchor.x = GET_X_LPARAM(lParam); _ptDragAnchor.y = GET_Y_LPARAM(lParam); LVUtil_ScreenToLV(_hwndListview, &_ptDragAnchor); } // Note: in deview inside a defview we can have problems of the
// parent destroying us when we change views, so we better addref/release
// around this...
AddRef(); _bContextMenuMode = TRUE;
ContextMenu((DWORD) lParam);
_bContextMenuMode = FALSE; _bMouseMenu = FALSE; Release(); } break;
case WM_COMMAND: return _OnCommand(NULL, wParam, lParam);
case WM_APPCOMMAND: if (!_OnAppCommand(GET_APPCOMMAND_LPARAM(lParam), GET_DEVICE_LPARAM(lParam), GET_KEYSTATE_LPARAM(lParam))) goto DoDefWndProc; break;
case WM_DSV_DISABLEACTIVEDESKTOP: DisableActiveDesktop(); break;
case WM_DSV_DELAYWINDOWCREATE: CallCB(SFVM_DELAYWINDOWCREATE, (WPARAM)_hwndView, 0); break;
case WM_DSV_BACKGROUNDENUMDONE: // Make sure this notify is from our enumeration task (it could be from a previous one)
if (lParam == _dwEnumId) _OnBackgroundEnumDone(); break;
case WM_DSV_GROUPINGDONE: _OnCategoryTaskDone(); break;
case WM_DSV_FILELISTENUMDONE: _OnEnumDoneMessage(); break;
case WM_DSV_FILELISTFILLDONE: _ShowSearchUI(FALSE); break;
case WM_DSV_UPDATETHUMBNAIL: { DSV_UPDATETHUMBNAIL* putn = (DSV_UPDATETHUMBNAIL*)lParam; if (_IsImageMode()) // some messages may come in after the view mode is changed.
{ _UpdateThumbnail(putn->iItem, putn->iImage, putn->pidl); } _CleanupUpdateThumbnail(putn); } break; case WM_DSV_POSTCREATEINFOTIP: _OnPostCreateInfotip((TOOLINFO *)wParam, lParam); break;
case WM_DSV_FSNOTIFY: { LPITEMIDLIST *ppidl; LONG lEvent;
LPSHChangeNotificationLock pshcnl = SHChangeNotification_Lock((HANDLE)wParam, (DWORD)lParam, &ppidl, &lEvent); if (pshcnl) { if (_fDisabled || (CallCB(SFVM_FSNOTIFY, (WPARAM)ppidl, (LPARAM)lEvent) == S_FALSE)) { lParam = 0; } else { lParam = _OnFSNotify(lEvent, (LPCITEMIDLIST*)ppidl); } SHChangeNotification_Unlock(pshcnl); } } return lParam;
// the background thread's callback will post this message to us
// when it has finished extracting a icon in the background.
//
// wParam is PIDL
// lParam is iIconIndex
case WM_DSV_UPDATEICON: _UpdateIcon((LPITEMIDLIST)wParam, (UINT)lParam); break;
case WM_DSV_UPDATECOLDATA: _UpdateColData((CBackgroundColInfo*)lParam); break;
case WM_DSV_UPDATEOVERLAY: _UpdateOverlay((int)wParam, (int)lParam); break;
case WM_DSV_SETIMPORTANTCOLUMNS: _SetImportantColumns((CBackgroundTileInfo*)lParam); break;
case WM_DSV_SHOWDRAGIMAGE: return DAD_ShowDragImage((BOOL)lParam);
case WM_DSV_DELAYSTATUSBARUPDATE: { HWND hwndStatus; LPWSTR pszStatus = (LPWSTR)lParam; if (_fBackgroundStatusTextValid) { _fBackgroundStatusTextValid = FALSE; // Now prepare the text and post it to the status bar window.
_psb->GetControlWindow(FCW_STATUS, &hwndStatus); if (hwndStatus) { SendMessage(hwndStatus, SB_SETTEXT, (WPARAM)0, (LPARAM)pszStatus); } } LocalFree((void *)pszStatus); } break;
case WM_DSV_DELAYINFOTIP: if ((CBackgroundInfoTip *)wParam == _pBackgroundInfoTip && _pBackgroundInfoTip->_fReady) { LRESULT lRet = SendMessage(_hwndListview, LVM_SETINFOTIP, NULL, (LPARAM)&_pBackgroundInfoTip->_lvSetInfoTip); ATOMICRELEASE(_pBackgroundInfoTip); return lRet; } break;
case WM_DSV_ENSURE_COLUMNS_LOADED: if (!_fDestroying) { AddColumns(); return 1; } break;
case GET_WM_CTLCOLOR_MSG(CTLCOLOR_STATIC): SetBkColor(GET_WM_CTLCOLOR_HDC(wParam, lParam, uMsg), GetSysColor(COLOR_WINDOW)); return (LRESULT)GetSysColorBrush(COLOR_WINDOW);
case WM_DRAWCLIPBOARD: if (_hwndNextViewer != NULL) SendMessage(_hwndNextViewer, uMsg, wParam, lParam);
if (_bClipViewer) return _OnClipboardChange();
break;
case WM_CHANGECBCHAIN: if ((HWND)wParam == _hwndNextViewer) { _hwndNextViewer = (HWND)lParam; return TRUE; }
if (_hwndNextViewer != NULL) return SendMessage(_hwndNextViewer, uMsg, wParam, lParam); break;
case WM_WININICHANGE: _OnWinIniChange(wParam, (LPCTSTR)lParam); SendMessage(_hwndListview, uMsg, wParam, lParam); break;
case WM_THEMECHANGED: PostMessage(_hwndView, WM_COMMAND, (WPARAM)SFVIDM_MISC_REFRESH, 0); break;
case WM_SHELLNOTIFY: #define SHELLNOTIFY_SETDESKWALLPAPER 0x0004
if (wParam == SHELLNOTIFY_SETDESKWALLPAPER) { if (_IsDesktop()) { _fHasDeskWallPaper = (lParam != 0); _SetFolderColors(); } } break;
// What we would like out of these menu messages:
// WM_ENTERMENULOOP
// WM_INITMENUPOPUP
// for File.Edit.View...: handle ourselves (merge in _pcmFile etc) and forward to IShellFolderViewCB for init
// for submenus or context menus: forward to whatever IContextMenu owns the popup
// WM_INITMENUPOPUP for next menu, etc
// WM_EXITMENULOOP
// PostMessage(WM_DSV_MENUTERM)
// WM_COMMAND comes in, if a menu item was selected
// Forward to the correct object to handle
// WM_DSV_MENUTERM
// clean up File.Edit.View... (release _pcmFile etc), and forward to IShellFolderViewCB for cleanup
//
// From previous comments here, it sounds like we don't get proper WM_ENTERMENULOOP / WM_EXITMENULOOP.
// I suspect this is a behavior change since Win95. (This probably happened when we changed
// the browser's HMENU to our own custom menu bar implementation way back in IE4...)
//
// Previous code also posted WM_DSV_MENUTERM *twice* -- another relic from the Edit menu days...
//
// If we try to clean up on WM_EXITMENULOOP, then we'll free _pcmFile etc when
// the File menu closes. This caused us problems when we tried to merge _pcmFile
// into the Edit menu. (We should have used _pcmEdit and cleaned up on WM_UNINITMENUPOPUP.)
// This is no longer a problem for defview, but it is a problem for the IShellFolderViewCB
// which can merge into any of File.Edit.View... menus. (In fact, no code in the source tree
// does anything on SFVM_EXITMENULOOP.)
//
// We could free up _pcmFile early (when the File menu goes away) if we want,
// but there doesn't seem to be any harm in letting it sit around.
// So rip out this unused WM_EXITMENULOOP/WM_DSVMENUTERM/_OnMenuTermination code.
//
case WM_INITMENU: _OnInitMenu(); break;
case WM_INITMENUPOPUP: _OnInitMenuPopup((HMENU)wParam, LOWORD(lParam), HIWORD(lParam)); break;
case WM_TIMER: KillTimer(hWnd, (UINT) wParam);
// Ignore if we're in the middle of destroying the window
if (_fDestroying) break;
if (DV_IDTIMER_START_ANI == wParam) { if (_hwndStatic) { WCHAR szName[128]; HINSTANCE hinst;
// used only by camera namespace; they have a const string < 128 ch
if (S_OK != CallCB(SFVM_GETANIMATION, (WPARAM)&hinst, (LPARAM)szName)) { hinst = g_hinst; StrCpyNW(szName, L"#150", ARRAYSIZE(szName)); }
HWND hAnimate = ::GetWindow (_hwndStatic, GW_CHILD);
if (hAnimate) { // Animate_OpenEx() except we want the W version always
SendMessage(hAnimate, ACM_OPENW, (WPARAM)hinst, (LPARAM)szName); } } } else if (DV_IDTIMER_BUFFERED_REFRESH == wParam) { if (_fRefreshBuffered) { _fRefreshBuffered = FALSE; PostMessage(_hwndView, WM_KEYDOWN, (WPARAM)VK_F5, 0); TraceMsg(TF_DEFVIEW, "Buffered Refresh timer causes actual refresh"); } } else if (DV_IDTIMER_NOTIFY_AUTOMATION_SELCHANGE == wParam) { _OnDelayedSelectionChange(); } else if (DV_IDTIMER_NOTIFY_AUTOMATION_CONTENTSCHANGED == wParam) { _OnDelayedContentsChanged(); } else if (DV_IDTIMER_DISKCACHE == wParam) { DWORD dwMode; if (_pDiskCache->GetMode(&dwMode) == S_OK && _pDiskCache->IsLocked() == S_FALSE) { // two seconds since last access, close the cache.
_pDiskCache->Close(NULL); }
if (_GetBackgroundTaskCount(TOID_NULL) == 0) { // there is nothing in the queue pending, so quit listening...
KillTimer(hWnd, DV_IDTIMER_DISKCACHE); } break;
} else if (DV_IDTIMER_SCROLL_TIMEOUT == wParam) { // Scroll timer expired.
TraceMsg(TF_DEFVIEW, "SCROLL TIMEOUT");
_fScrolling = FALSE;
// Now we send a paint to listview, so it will send us more requests for tileinformation
// that we ignored during scrolling.
if (_fRequestedTileDuringScroll) { InvalidateRect(_hwndListview, NULL, FALSE); } } else { ASSERT(FALSE); // nobody is handling this timer id!
} break;
case WM_SETCURSOR: if (_hwndStatic) { SetCursor(LoadCursor(NULL, IDC_WAIT)); return TRUE; } goto DoDefWndProc;
case WM_DRAWITEM: #define lpdis ((LPDRAWITEMSTRUCT)lParam)
dwID = lpdis->itemID;
if (lpdis->CtlType != ODT_MENU) return 0; if (InRange(lpdis->itemID, SFVIDM_CLIENT_FIRST, SFVIDM_CLIENT_LAST) && HasCB()) { CallCB(SFVM_DRAWITEM, SFVIDM_CLIENT_FIRST, lParam); return 1; } else { LRESULT lResult = 0; _ForwardMenuMessages(dwID, uMsg, wParam, lParam, &lResult, NULL); return lResult; } #undef lpdis
case WM_MEASUREITEM: #define lpmis ((LPMEASUREITEMSTRUCT)lParam)
dwID = lpmis->itemID;
if (lpmis->CtlType != ODT_MENU) return 0;
if (InRange(lpmis->itemID, SFVIDM_CLIENT_FIRST, SFVIDM_CLIENT_LAST) && HasCB()) { CallCB(SFVM_MEASUREITEM, SFVIDM_CLIENT_FIRST, lParam); return 1; } else { LRESULT lResult = 0; _ForwardMenuMessages(dwID, uMsg, wParam, lParam, &lResult, NULL); return lResult; }
case WM_MENUCHAR:
if (_pcmFile) { LRESULT lResult; HRESULT hr = SHForwardContextMenuMsg(_pcmFile, uMsg, wParam, lParam, &lResult, FALSE); if (hr == S_OK) return lResult; }
if (_pcmContextMenuPopup) { LRESULT lResult; HRESULT hr = SHForwardContextMenuMsg(_pcmContextMenuPopup, uMsg, wParam, lParam, &lResult, FALSE); if (hr == S_OK) return lResult; }
return MAKELONG(0, MNC_IGNORE);
// 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: _OnMenuSelect(GET_WM_MENUSELECT_CMD(wParam, lParam), GET_WM_MENUSELECT_FLAGS(wParam, lParam), GET_WM_MENUSELECT_HMENU(wParam, lParam)); break;
case WM_SYSCOLORCHANGE: _SetFolderColors(); SendMessage(_hwndListview, uMsg, wParam, lParam); _rgbBackColor = CLR_INVALID; break;
case SVM_SELECTITEM: SelectItem((LPCITEMIDLIST)lParam, (int) wParam); break;
case SVM_SELECTANDPOSITIONITEM: { SFM_SAP * psap = (SFM_SAP*)lParam; for (UINT i = 0; i < wParam; psap++, i++) SelectAndPositionItem(psap->pidl, psap->uSelectFlags, psap->fMove ? &psap->pt : NULL); break; }
case WM_PALETTECHANGED: if (_IsImageMode()) { InvalidateRect(_hwndListview, NULL, FALSE); return TRUE; } // else Fall Through
case WM_QUERYNEWPALETTE: if (_IsImageMode()) { return FALSE; // Let Browser handle palette management
} else { HWND hwndT = GetChildViewWindow(); if (!hwndT) goto DoDefWndProc;
return SendMessage(hwndT, uMsg, wParam, lParam); }
case WM_DSV_REARRANGELISTVIEW: _ShowAndActivate(); break;
case WM_DSV_SENDSELECTIONCHANGED: _OnSelectionChanged(); break;
case WM_DSV_SENDNOITEMSTATECHANGED: _OnNoItemStateChanged(); break;
case WM_DSV_DESKHTML_CHANGES: if (_IsDesktop()) { IADesktopP2 *piadp2; if (SUCCEEDED(SHCoCreateInstance(NULL, &CLSID_ActiveDesktop, NULL, IID_PPV_ARG(IADesktopP2, &piadp2)))) { IActiveDesktopP *piadpp;
// 98/11/23 #254482 vtan: When making changes using dynamic
// HTML don't forget to update the "desktop.htt" file so
// that it's in sync with the registry BEFORE using DHTML.
if (SUCCEEDED(piadp2->QueryInterface(IID_PPV_ARG(IActiveDesktopP, &piadpp)))) { piadpp->EnsureUpdateHTML(); // ignore result
piadpp->Release(); } piadp2->MakeDynamicChanges(_cFrame._pOleObj); piadp2->Release(); } } break;
// Toggling the New Start Menu on/off causes My Computer, etc.
// desktop icons to dynamically hide/show themselves.
case WM_DSV_STARTPAGE_TURNONOFF: _ReloadContent(FALSE); break;
case WM_DSV_ADJUSTRECYCLEBINPOSITION: { // We need to move the recycle bin to it's default position.
POINT ptRecycleBin; int iIndexRecycleBin = _FreezeRecycleBin(&ptRecycleBin); if (iIndexRecycleBin != LV_NOFROZENITEM) _SetRecycleBinInDefaultPosition(&ptRecycleBin); } break;
default: DoDefWndProc: return DefWindowProc(hWnd, uMsg, wParam, lParam); }
return 0; }
// don't test the result as this will fail on the second call
void CDefView::_RegisterWindow(void) { WNDCLASS wc = {0};
// don't want vredraw and hredraw because that causes horrible
// flicker expecially with full drag
wc.style = CS_PARENTDC; wc.lpfnWndProc = CDefView::s_WndProc; wc.cbWndExtra = sizeof(CDefView *); wc.hInstance = HINST_THISDLL; wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); wc.lpszClassName = TEXT("SHELLDLL_DefView");
RegisterClass(&wc); }
CDefView::~CDefView() { _uState = SVUIA_DEACTIVATE;
// Sanity check.
ASSERT(_tlistPendingInfotips.GetHeadPosition() == NULL);
DebugMsg(TF_LIFE, TEXT("dtor CDefView %x"), this);
//
// Just in case, there is a left over.
//
_dvdt.LeaveAndReleaseData();
//
// We need to give it a chance to clean up.
//
CallCB(SFVM_PRERELEASE, 0, 0);
DestroyViewWindow();
ATOMICRELEASE(_pSelectionShellItemArray); ATOMICRELEASE(_pFolderShellItemArray);
ATOMICRELEASE(_pScheduler); //
// We should release _psb after _pshf (for docfindx)
//
ATOMICRELEASE(_pshf); ATOMICRELEASE(_pshf2); ATOMICRELEASE(_pshfParent); ATOMICRELEASE(_pshf2Parent); ILFree(_pidlRelative); ATOMICRELEASE(_psi); ATOMICRELEASE(_psio); ATOMICRELEASE(_pcdb); ATOMICRELEASE(_psb); ATOMICRELEASE(_psd);
IUnknown_SetSite(_pcmFile, NULL); ATOMICRELEASE(_pcmFile);
ATOMICRELEASE(_pcat); ATOMICRELEASE(_pImageCache); ATOMICRELEASE(_pDiskCache);
DSA_Destroy(_hdaCategories); DSA_Destroy(_hdsaSCIDCache);
// NOTE we dont release psvOuter
// it has a ref on us
if (_pbtn) LocalFree(_pbtn);
//
// Cleanup _dvdt
//
_dvdt.ReleaseDataObject(); _dvdt.ReleaseCurrentDropTarget();
_ClearPendingSelectedItems();
ATOMICRELEASE(_pauto); ATOMICRELEASE(_padvise);
if (_hmenuCur) { DestroyMenu(_hmenuCur); }
ATOMICRELEASE(_pBackgroundInfoTip); ATOMICRELEASE(_ppui);
if (_pidlSelectAndPosition) ILFree(_pidlSelectAndPosition);
Str_SetPtr(&_pszLegacyWatermark, NULL);
if (_hdpaGroupingListActive) DPA_Destroy(_hdpaGroupingListActive);
if (_hdpaGroupingListBackup) DPA_Destroy(_hdpaGroupingListBackup); }
HRESULT CDefView::_AddTask(IRunnableTask *pTask, REFTASKOWNERID rTID, DWORD_PTR lParam, DWORD dwPriority, DWORD grfFlags) { HRESULT hr = E_FAIL;
if (_pScheduler) { if (grfFlags & ADDTASK_ONLYONCE) { hr = _pScheduler->MoveTask(rTID, lParam, dwPriority, (grfFlags & ADDTASK_ATFRONT ? ITSSFLAG_TASK_PLACEINFRONT : ITSSFLAG_TASK_PLACEINBACK)); }
if (hr != S_OK) // If we didn't move it, add it
{ hr = _pScheduler->AddTask2(pTask, rTID, lParam, dwPriority, (grfFlags & ADDTASK_ATFRONT ? ITSSFLAG_TASK_PLACEINFRONT : ITSSFLAG_TASK_PLACEINBACK)); } }
return hr; }
// Get the number of running tasks of the indicated task ID.
UINT CDefView::_GetBackgroundTaskCount(REFTASKOWNERID rtid) { return _pScheduler ? _pScheduler->CountTasks(rtid) : 0; }
const TBBUTTON c_tbDefView[] = { { VIEW_MOVETO | IN_VIEW_BMP, SFVIDM_EDIT_MOVETO, TBSTATE_ENABLED, BTNS_BUTTON, {0,0}, 0, -1}, { VIEW_COPYTO | IN_VIEW_BMP, SFVIDM_EDIT_COPYTO, TBSTATE_ENABLED, BTNS_BUTTON, {0,0}, 0, -1}, { STD_DELETE | IN_STD_BMP, SFVIDM_FILE_DELETE, TBSTATE_ENABLED, BTNS_BUTTON, {0,0}, 0, -1}, { STD_UNDO | IN_STD_BMP, SFVIDM_EDIT_UNDO, TBSTATE_ENABLED, BTNS_BUTTON, {0,0}, 0, -1}, { 0, 0, TBSTATE_ENABLED, BTNS_SEP, {0,0}, 0, -1 }, { VIEW_VIEWMENU | IN_VIEW_BMP, SFVIDM_VIEW_VIEWMENU, TBSTATE_ENABLED, BTNS_WHOLEDROPDOWN, {0,0}, 0, -1}, // hidden buttons (off by default, available only via customize dialog)
{ STD_PROPERTIES | IN_STD_BMP, SFVIDM_FILE_PROPERTIES, TBSTATE_HIDDEN | TBSTATE_ENABLED, BTNS_BUTTON, {0,0}, 0, -1}, { STD_CUT | IN_STD_BMP, SFVIDM_EDIT_CUT, TBSTATE_HIDDEN | TBSTATE_ENABLED, BTNS_BUTTON, {0,0}, 0, -1}, { STD_COPY | IN_STD_BMP, SFVIDM_EDIT_COPY, TBSTATE_HIDDEN | TBSTATE_ENABLED, BTNS_BUTTON, {0,0}, 0, -1}, { STD_PASTE | IN_STD_BMP, SFVIDM_EDIT_PASTE, TBSTATE_HIDDEN | TBSTATE_ENABLED, BTNS_BUTTON, {0,0}, 0, -1}, { VIEW_OPTIONS | IN_VIEW_BMP, SFVIDM_TOOL_OPTIONS, TBSTATE_HIDDEN | TBSTATE_ENABLED, BTNS_BUTTON, {0,0}, 0, -1}, };
const TBBUTTON c_tbDefViewWebView[] = { //{ 0, 0, TBSTATE_ENABLED, BTNS_SEP, {0,0}, 0, -1 },
{ VIEW_VIEWMENU | IN_VIEW_BMP, SFVIDM_VIEW_VIEWMENU, TBSTATE_ENABLED, BTNS_WHOLEDROPDOWN, {0,0}, 0, -1}, // hidden buttons (off by default, available only via customize dialog)
{ VIEW_MOVETO | IN_VIEW_BMP, SFVIDM_EDIT_MOVETO, TBSTATE_HIDDEN | TBSTATE_ENABLED, BTNS_BUTTON, {0,0}, 0, -1}, { VIEW_COPYTO | IN_VIEW_BMP, SFVIDM_EDIT_COPYTO, TBSTATE_HIDDEN | TBSTATE_ENABLED, BTNS_BUTTON, {0,0}, 0, -1}, { STD_DELETE | IN_STD_BMP, SFVIDM_FILE_DELETE, TBSTATE_HIDDEN | TBSTATE_ENABLED, BTNS_BUTTON, {0,0}, 0, -1}, { STD_UNDO | IN_STD_BMP, SFVIDM_EDIT_UNDO, TBSTATE_HIDDEN | TBSTATE_ENABLED, BTNS_BUTTON, {0,0}, 0, -1}, { STD_PROPERTIES | IN_STD_BMP, SFVIDM_FILE_PROPERTIES, TBSTATE_HIDDEN | TBSTATE_ENABLED, BTNS_BUTTON, {0,0}, 0, -1}, { STD_CUT | IN_STD_BMP, SFVIDM_EDIT_CUT, TBSTATE_HIDDEN | TBSTATE_ENABLED, BTNS_BUTTON, {0,0}, 0, -1}, { STD_COPY | IN_STD_BMP, SFVIDM_EDIT_COPY, TBSTATE_HIDDEN | TBSTATE_ENABLED, BTNS_BUTTON, {0,0}, 0, -1}, { STD_PASTE | IN_STD_BMP, SFVIDM_EDIT_PASTE, TBSTATE_HIDDEN | TBSTATE_ENABLED, BTNS_BUTTON, {0,0}, 0, -1}, { VIEW_OPTIONS | IN_VIEW_BMP, SFVIDM_TOOL_OPTIONS, TBSTATE_HIDDEN | TBSTATE_ENABLED, BTNS_BUTTON, {0,0}, 0, -1}, };
// win95 defview toolbar, used for corel apphack
const TBBUTTON c_tbDefView95[] = { { STD_CUT | IN_STD_BMP, SFVIDM_EDIT_CUT, TBSTATE_ENABLED, BTNS_BUTTON, {0,0}, 0, -1}, { STD_COPY | IN_STD_BMP, SFVIDM_EDIT_COPY, TBSTATE_ENABLED, BTNS_BUTTON, {0,0}, 0, -1}, { STD_PASTE | IN_STD_BMP, SFVIDM_EDIT_PASTE, TBSTATE_ENABLED, BTNS_BUTTON, {0,0}, 0, -1}, { 0, 0, TBSTATE_ENABLED, BTNS_SEP, {0,0}, 0, -1 }, { STD_UNDO | IN_STD_BMP, SFVIDM_EDIT_UNDO, TBSTATE_ENABLED, BTNS_BUTTON, {0,0}, 0, -1}, { 0, 0, TBSTATE_ENABLED, BTNS_SEP, {0,0}, 0, -1 }, { STD_DELETE | IN_STD_BMP, SFVIDM_FILE_DELETE, TBSTATE_ENABLED, BTNS_BUTTON, {0,0}, 0, -1}, { STD_PROPERTIES | IN_STD_BMP, SFVIDM_FILE_PROPERTIES, TBSTATE_ENABLED, BTNS_BUTTON, {0,0}, 0, -1}, { 0, 0, TBSTATE_ENABLED, BTNS_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, BTNS_BUTTON, {0,0}, 0, -1 }, { VIEW_SMALLICONS | IN_VIEW_BMP, SFVIDM_VIEW_SMALLICON, TBSTATE_ENABLED, BTNS_BUTTON, {0,0}, 0, -1 }, { VIEW_LIST | IN_VIEW_BMP, SFVIDM_VIEW_LIST, TBSTATE_ENABLED, BTNS_BUTTON, {0,0}, 0, -1 }, { VIEW_DETAILS | IN_VIEW_BMP, SFVIDM_VIEW_DETAILS, TBSTATE_ENABLED, BTNS_BUTTON, {0,0}, 0, -1 }, };
LRESULT CDefView::_TBNotify(NMHDR *pnm) { LPTBNOTIFY ptbn = (LPTBNOTIFY)pnm;
switch (pnm->code) { case TBN_BEGINDRAG: _OnMenuSelect(ptbn->iItem, 0, 0); break; } return 0; }
BOOL CDefView::_MergeIExplorerToolbar(UINT cExtButtons) { BOOL fRet = FALSE; IExplorerToolbar *piet; if (SUCCEEDED(IUnknown_QueryService(_psb, SID_SExplorerToolbar, IID_PPV_ARG(IExplorerToolbar, &piet)))) { BOOL fGotClsid = TRUE;
DWORD dwFlags = 0;
if (cExtButtons == 0) { // This shf has no buttons to merge in; use the standard defview
// clsid so that the shf shares standard toolbar customization.
_clsid = CGID_DefViewFrame;
} else if (SUCCEEDED(IUnknown_GetClassID(_pshf, &_clsid))) { // This shf has buttons to merge in; use its clsid
// so that this shf gets separate customization persistence.
// The shf might expect us to provide room for two lines of
// text (since that was the default in IE4).
dwFlags |= VBF_TWOLINESTEXT; } else { // This shf has buttons to merge in but doesn't implement
// IPersist::GetClassID; so we can't use IExplorerToolbar mechanism.
fGotClsid = FALSE; }
if (fGotClsid) { HRESULT hr = piet->SetCommandTarget((IUnknown *)SAFECAST(this, IOleCommandTarget *), &_clsid, dwFlags); if (SUCCEEDED(hr)) { // If hr == S_FALSE, another defview merged in its buttons under the
// same clsid, and they're still there. So no need to call AddButtons.
if (hr != S_FALSE) hr = piet->AddButtons(&_clsid, _cButtons, _pbtn);
if (SUCCEEDED(hr)) { fRet = TRUE; } } } piet->Release(); } return fRet; }
int _FirstHiddenButton(TBBUTTON* ptbn, int cButtons) { for (int i = 0; i < cButtons; i++) { if (ptbn[i].fsState & TBSTATE_HIDDEN) break; }
return i; }
void CDefView::_CopyDefViewButton(PTBBUTTON ptbbDest, PTBBUTTON ptbbSrc) { *ptbbDest = *ptbbSrc;
if (!(ptbbDest->fsStyle & BTNS_SEP)) { // Fix up bitmap offset depending on whether this is a "view" bitmap or a "standard" bitmap
if (ptbbDest->iBitmap & IN_VIEW_BMP) ptbbDest->iBitmap = (int)((ptbbDest->iBitmap & ~PRIVATE_TB_FLAGS) + _iViewBMOffset); else ptbbDest->iBitmap = (int)(ptbbDest->iBitmap + _iStdBMOffset); } }
//
// Here's the deal with _GetButtons
//
// DefView has some buttons, and its callback client may have some buttons.
//
// Some of defview's buttons are visible on the toolbar by default, and some only show
// up if you customize the toolbar.
//
// We specify which buttons are hidden by default by marking them with TBSTATE_HIDDEN in
// the declaration of c_tbDefView. We assume all such buttons are in a continuous block at
// the end of c_tbDefView.
//
// We return in ppbtn a pointer to an array of all the buttons, including those not shown
// by default. We put the buttons not shown by default at the end of this array. We pass
// back in pcButtons the count of visible buttons, and in pcTotalButtons the count of visible
// and hidden buttons.
//
// The int return value is the number of client buttons in the array.
//
int CDefView::_GetButtons(PTBBUTTON* ppbtn, LPINT pcButtons, LPINT pcTotalButtons) { int cVisibleBtns = 0; // count of visible defview + client buttons
TBINFO tbinfo; tbinfo.uFlags = TBIF_APPEND; tbinfo.cbuttons = 0;
// Does the client want to prepend/append a toolbar?
CallCB(SFVM_GETBUTTONINFO, 0, (LPARAM)&tbinfo);
_uDefToolbar = HIWORD(tbinfo.uFlags); tbinfo.uFlags &= 0xffff;
// tbDefView needs to be big enough to hold either c_tbDefView or c_tbDefView95
COMPILETIME_ASSERT(ARRAYSIZE(c_tbDefView95) >= ARRAYSIZE(c_tbDefView)); COMPILETIME_ASSERT(ARRAYSIZE(c_tbDefView95) >= ARRAYSIZE(c_tbDefViewWebView));
TBBUTTON tbDefView[ARRAYSIZE(c_tbDefView95)]; int cDefViewBtns; // total count of defview buttons
if (SHGetAppCompatFlags(ACF_WIN95DEFVIEW) & ACF_WIN95DEFVIEW) { memcpy(tbDefView, c_tbDefView95, sizeof(TBBUTTON) * ARRAYSIZE(c_tbDefView95)); cDefViewBtns = ARRAYSIZE(c_tbDefView95); } else if (_cFrame.IsWebView() || _pDUIView) { memcpy(tbDefView, c_tbDefViewWebView, sizeof(TBBUTTON) * ARRAYSIZE(c_tbDefViewWebView)); cDefViewBtns = ARRAYSIZE(c_tbDefViewWebView); } else { memcpy(tbDefView, c_tbDefView, sizeof(TBBUTTON) * ARRAYSIZE(c_tbDefView)); cDefViewBtns = ARRAYSIZE(c_tbDefView); }
int cVisibleDefViewBtns = _FirstHiddenButton(tbDefView, cDefViewBtns); // count of visible defview buttons
TBBUTTON *pbtn = (TBBUTTON *)LocalAlloc(LPTR, (cDefViewBtns + tbinfo.cbuttons) * sizeof(*pbtn)); if (pbtn) { int iStart = 0; cVisibleBtns = tbinfo.cbuttons + cVisibleDefViewBtns;
// Have the client fill in its buttons
switch (tbinfo.uFlags) { case TBIF_PREPEND: CallCB(SFVM_GETBUTTONS, MAKEWPARAM(SFVIDM_CLIENT_FIRST, tbinfo.cbuttons), (LPARAM)pbtn); iStart = tbinfo.cbuttons; break;
case TBIF_APPEND: CallCB(SFVM_GETBUTTONS, MAKEWPARAM(SFVIDM_CLIENT_FIRST, tbinfo.cbuttons), (LPARAM)&pbtn[cVisibleDefViewBtns]); iStart = 0; break;
case TBIF_REPLACE: CallCB(SFVM_GETBUTTONS, MAKEWPARAM(SFVIDM_CLIENT_FIRST, tbinfo.cbuttons), (LPARAM)pbtn);
cVisibleBtns = tbinfo.cbuttons; cVisibleDefViewBtns = 0; break;
default: RIPMSG(0, "View callback passed an invalid TBINFO flag"); break; }
// Fill in visible defview buttons
for (int i = 0; i < cVisibleDefViewBtns; i++) { // Visible defview button block gets added at iStart
_CopyDefViewButton(&pbtn[i + iStart], &tbDefView[i]); }
// Fill in hidden defview buttons
for (i = cVisibleDefViewBtns; i < cDefViewBtns; i++) { // Hidden defview button block gets added after visible & client buttons
_CopyDefViewButton(&pbtn[i + tbinfo.cbuttons], &tbDefView[i]);
// If this rips a visible button got mixed in with the hidden block
ASSERT(pbtn[i + tbinfo.cbuttons].fsState & TBSTATE_HIDDEN);
// Rip off the hidden bit
pbtn[i + tbinfo.cbuttons].fsState &= ~TBSTATE_HIDDEN; } }
ASSERT(ppbtn); ASSERT(pcButtons); ASSERT(pcTotalButtons);
*ppbtn = pbtn; *pcButtons = cVisibleBtns; *pcTotalButtons = tbinfo.cbuttons + cDefViewBtns;
return tbinfo.cbuttons; }
void CDefView::MergeToolBar(BOOL bCanRestore) { TBADDBITMAP ab;
ab.hInst = HINST_COMMCTRL; // hinstCommctrl
ab.nID = IDB_STD_SMALL_COLOR; // std bitmaps
_psb->SendControlMsg(FCW_TOOLBAR, TB_ADDBITMAP, 8, (LPARAM)&ab, &_iStdBMOffset);
ab.nID = IDB_VIEW_SMALL_COLOR; // std view bitmaps
_psb->SendControlMsg(FCW_TOOLBAR, TB_ADDBITMAP, 8, (LPARAM)&ab, &_iViewBMOffset);
if (_pbtn) LocalFree(_pbtn);
int cExtButtons = _GetButtons(&_pbtn, &_cButtons, &_cTotalButtons);
if (_pbtn && !_MergeIExplorerToolbar(cExtButtons)) { // if we're able to do the new IExplorerToolbar merge method, great...
// if not, we use the old style
_psb->SetToolbarItems(_pbtn, _cButtons, FCT_MERGE); CDefView::CheckToolbar(); } }
STDMETHODIMP CDefView::GetWindow(HWND *phwnd) { *phwnd = _hwndView; return S_OK; }
STDMETHODIMP CDefView::ContextSensitiveHelp(BOOL fEnterMode) { return E_NOTIMPL; }
STDMETHODIMP CDefView::EnableModeless(BOOL fEnable) { // We have no modeless window to be enabled/disabled
return S_OK; }
HRESULT CDefView::_ReloadListviewContent() { // 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.
TCHAR szPathAny[MAX_PATH];
_UpdateSelectionMode();
// finish any pending edits
SendMessage(_hwndListview, LVM_EDITLABEL, (WPARAM)-1, 0);
GetWindowsDirectory(szPathAny, ARRAYSIZE(szPathAny)); IsShared(szPathAny, TRUE);
// HACK: strange way to notify folder that we're refreshing
ULONG rgf = SFGAO_VALIDATE; _pshf->GetAttributesOf(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.
//
int iItem = ListView_GetNextItem(_hwndListview, -1, LVNI_SELECTED); if (iItem != -1) CFSFolder_UpdateIcon(_pshf, _GetPIDL(iItem));
// We should not save the selection if doing refresh.
_ClearPendingSelectedItems();
// 01/05/21 #399284: Don't save/restore the state and nuke objects if there's a background process using them
if(!_bBkFilling) { // First we have to save all the icon positions, so they will be restored
// properly during the FillObjectsShowHide
SaveViewState();
// 99/04/07 #309965 vtan: Persist the view state (above). Make sure
// our internal representation is the same as the one on the disk
// by dumping our cache and reloading the information.
GetViewState();
// To make it look like the refesh is doing something, clear
// all the icons from the view before we start enumerating.
_RemoveObject(NULL, FALSE); _fSyncOnFillDone = TRUE; // apply the just-saved view state when we finish enumeration
}
return FillObjectsShowHide(TRUE); }
HRESULT CDefView::_ReloadContent(BOOL fForce) { if (_bReEntrantReload) { return S_FALSE; } _bReEntrantReload = TRUE;
HRESULT hrExtView = S_OK; HRESULT hrNormalView = S_OK; SHELLSTATE ss;
// Tell the defview client that this window is about to be refreshed
_CallRefresh(TRUE);
// make sure that the CommandIds and the Uids match by recreating the menus
RecreateMenus();
// If the global SSF_WIN95CLASSIC state changed, we need to muck with the UI.
SHGetSetSettings(&ss, SSF_WIN95CLASSIC, FALSE); // Show webview and pane again if we are forced OR the view has changed.
if (fForce || (BOOLIFY(ss.fWin95Classic) != BOOLIFY(_fClassic))) { _fClassic = ss.fWin95Classic; _UpdateListviewColors(); }
if (_ShouldShowWebView()) { // We need to save the icon positions before we refresh the view.
SaveViewState();
if (_pDUIView) { hrExtView = _pDUIView->Refresh(); } else { _TryShowWebView(_fs.ViewMode, _fs.ViewMode); } } else { _TryHideWebView(); // make sure it's off
}
// We want to preserve the earlier error if any
hrNormalView = _ReloadListviewContent();
_bReEntrantReload = FALSE; return FAILED(hrExtView) ? hrExtView : hrNormalView; }
STDMETHODIMP CDefView::Refresh() { // See if some refreshes were buffered
if (_fRefreshBuffered) { //Since we are refreshing it right now. Kill the timer.
TraceMsg(TF_DEFVIEW, "Buffered Refresh Timer Killed by regular Refresh"); KillTimer(_hwndView, DV_IDTIMER_BUFFERED_REFRESH); _fRefreshBuffered = FALSE; }
// If desktop is in modal state, do not attempt to refresh.
// If we do, we endup destroying Trident object when it is in modal state.
if (_IsDesktop() && _fDesktopModal) { // Remember that we could not refresh the desktop because it was in
// a modal state.
_fDesktopRefreshPending = TRUE; return S_OK; }
// make sure we have the latest
SHRefreshSettings();
_UpdateRegFlags();
if (_IsDesktop()) { SHELLSTATE ss = {0}; SHGetSetSettings(&ss, SSF_DESKTOPHTML, FALSE);
// The following code is not needed because _ReloadContent() takes care of switching to
// web-view.
// _SwitchDesktopHTML(BOOLIFY(ss.fDesktopHTML));
if (ss.fDesktopHTML) { // For backward compatibility, hide the desktop channel bar.
HideIE4DesktopChannelBar(); // ActiveDesktop is not part of shdocvw's browser session count
// so when we refresh, we must tell wininet to reset the session
// count otherwise we will not hit the net.
MyInternetSetOption(NULL, INTERNET_OPTION_RESET_URLCACHE_SESSION, NULL, 0); } }
return _ReloadContent(TRUE); }
STDMETHODIMP CDefView::CreateViewWindow(IShellView *psvPrevious, LPCFOLDERSETTINGS pfs, IShellBrowser *psb, RECT *prc, HWND *phWnd) { SV2CVW2_PARAMS cParams = {0};
cParams.cbSize = sizeof(SV2CVW2_PARAMS); cParams.psvPrev = psvPrevious; cParams.pfs = pfs; cParams.psbOwner = psb; cParams.prcView = prc;
HRESULT hr = CreateViewWindow2(&cParams);
*phWnd = cParams.hwndView;
if (SUCCEEDED(hr) && (SHGetAppCompatFlags(ACF_OLDCREATEVIEWWND) & ACF_OLDCREATEVIEWWND)) { //
// CreateViewWindow was documented as returning S_OK on success,
// but IE4 changed the function to return S_FALSE if the defview
// was created async.
//
// PowerDesk relies on the old behavior.
// So does Quattro Pro.
//
hr = S_OK; }
return hr; }
STDMETHODIMP CDefView::HandleRename(LPCITEMIDLIST pidl) { HRESULT hr = E_FAIL;
// Gross, but if no PIDL passed in use the GetObject(-2) hack to get the selected object...
// Don't need to free as it wsa not cloned...
if (!pidl) { GetObject((LPITEMIDLIST*)&pidl, (UINT)-2); } else { RIP(ILFindLastID(pidl) == pidl); if (ILFindLastID(pidl) != pidl) { return E_INVALIDARG; } }
hr = SelectAndPositionItem(pidl, SVSI_SELECT, NULL); if (SUCCEEDED(hr)) hr = SelectAndPositionItem(pidl, SVSI_EDIT, NULL);
return hr; }
// IViewObject
HRESULT CDefView::GetColorSet(DWORD dwAspect, LONG lindex, void *pvAspect, DVTARGETDEVICE *ptd, HDC hicTargetDev, LOGPALETTE **ppColorSet) { if (_cFrame.IsWebView() && _cFrame._pvoActive) { return _cFrame._pvoActive->GetColorSet(dwAspect, lindex, pvAspect, ptd, hicTargetDev, ppColorSet); }
if (ppColorSet) *ppColorSet = NULL;
return E_FAIL; }
HRESULT CDefView::Freeze(DWORD, LONG, void *, DWORD *pdwFreeze) { return E_NOTIMPL; }
HRESULT CDefView::Unfreeze(DWORD) { return E_NOTIMPL; }
HRESULT CDefView::SetAdvise(DWORD dwAspect, DWORD advf, IAdviseSink *pSink) { if (dwAspect != DVASPECT_CONTENT) return DV_E_DVASPECT;
if (advf & ~(ADVF_PRIMEFIRST | ADVF_ONLYONCE)) return E_INVALIDARG;
if (pSink != _padvise) { ATOMICRELEASE(_padvise);
_padvise = pSink;
if (_padvise) _padvise->AddRef(); }
if (_padvise) { _advise_aspect = dwAspect; _advise_advf = advf;
if (advf & ADVF_PRIMEFIRST) PropagateOnViewChange(dwAspect, -1); } else _advise_aspect = _advise_advf = 0;
return S_OK; }
HRESULT CDefView::GetAdvise(DWORD *pdwAspect, DWORD *padvf, IAdviseSink **ppSink) { if (pdwAspect) *pdwAspect = _advise_aspect;
if (padvf) *padvf = _advise_advf;
if (ppSink) { if (_padvise) _padvise->AddRef();
*ppSink = _padvise; }
return S_OK; }
HRESULT CDefView::Draw(DWORD, LONG, void *, DVTARGETDEVICE *, HDC, HDC, const RECTL *, const RECTL *, BOOL (*)(ULONG_PTR), ULONG_PTR) { return E_NOTIMPL; }
void CDefView::PropagateOnViewChange(DWORD dwAspect, LONG lindex) { dwAspect &= _advise_aspect;
if (dwAspect && _padvise) { IAdviseSink *pSink = _padvise; IUnknown *punkRelease;
if (_advise_advf & ADVF_ONLYONCE) { punkRelease = pSink; _padvise = NULL; _advise_aspect = _advise_advf = 0; } else punkRelease = NULL;
pSink->OnViewChange(dwAspect, lindex);
ATOMICRELEASE(punkRelease); } }
void CDefView::PropagateOnClose() { //
// we aren't closing ourselves, just somebody under us...
// ...reflect this up the chain as a view change.
//
if (_padvise) PropagateOnViewChange(_advise_aspect, -1); }
UINT CDefView::_ValidateViewMode(UINT uViewMode) { UINT uViewModeDefault = FVM_ICON;
if (uViewMode >= FVM_FIRST && uViewMode <= FVM_LAST) { uViewModeDefault = uViewMode; #ifdef DEBUG
if (!_ViewSupported(uViewMode)) { // Whoa! the default is excluded? Ignore it.
TraceMsg(TF_WARNING, "Bug in IShellFolderViewCB client: returned a default viewmode that is excluded"); } #endif
} else { TraceMsg(TF_WARNING, "Bug in IShellFolderViewCB client: returned invalid viewmode"); }
return uViewModeDefault; }
UINT CDefView::_GetDefaultViewMode() { UINT uViewMode = (_IsDesktop() || !IsOS(OS_SERVERADMINUI)) ? FVM_ICON : FVM_DETAILS; CallCB(SFVM_DEFVIEWMODE, 0, (LPARAM)&uViewMode);
return _ValidateViewMode(uViewMode); }
void CDefView::_GetDeferredViewSettings(UINT* puViewMode) { SFVM_DEFERRED_VIEW_SETTINGS sdvsSettings;
ZeroMemory(&sdvsSettings, sizeof(sdvsSettings));
if (SUCCEEDED(CallCB(SFVM_GETDEFERREDVIEWSETTINGS, 0, (LPARAM)&sdvsSettings))) { _vs._lParamSort = sdvsSettings.uSortCol; _vs._iDirection = sdvsSettings.iSortDirection >= 0 ? 1 : -1; *puViewMode = _ValidateViewMode(sdvsSettings.fvm);
_fs.fFlags = (_fs.fFlags & ~FWF_AUTOARRANGE) | (sdvsSettings.fFlags & FWF_AUTOARRANGE); SHSetWindowBits(_hwndListview, GWL_STYLE, LVS_AUTOARRANGE, _IsAutoArrange() ? LVS_AUTOARRANGE : 0);
if (sdvsSettings.fGroupView && (*puViewMode != FVM_THUMBSTRIP)) { SHCOLUMNID scid; if SUCCEEDED(_pshf2->MapColumnToSCID(sdvsSettings.uSortCol, &scid)) { _CategorizeOnGUID(&CLSID_DetailCategorizer, &scid); } } } else { *puViewMode = _GetDefaultViewMode(); } }
BOOL CDefView::_ViewSupported(UINT uView) { SFVM_VIEW_DATA vi; _GetSFVMViewState(uView, &vi);
BOOL fIncludeView; if (vi.dwOptions == SFVMQVI_INCLUDE) fIncludeView = TRUE; else if (vi.dwOptions == SFVMQVI_EXCLUDE) fIncludeView = FALSE; else fIncludeView = uView != FVM_THUMBSTRIP; // by default, everything is included except FVM_THUMBSTRIP
return fIncludeView; }
STDMETHODIMP CDefView::GetView(SHELLVIEWID* pvid, ULONG uView) { HRESULT hr;
if ((int)uView >= 0) { // start with the first supported view
UINT fvm = FVM_FIRST; while (fvm <= FVM_LAST && !_ViewSupported(fvm)) fvm++;
// find fvm associated with index uView
for (ULONG i = 0; fvm <= FVM_LAST && i < uView; fvm++, i++) { // skip unsupported views
while (fvm <= FVM_LAST && !_ViewSupported(fvm)) fvm++; }
if (fvm <= FVM_LAST) { hr = SVIDFromViewMode((FOLDERVIEWMODE)fvm, pvid); } else if (i == uView) { // enumerate the "default view" so the browser doesn't throw it out later
*pvid = VID_DefaultView; hr = S_OK; } else { hr = E_INVALIDARG; } } else { // We're being asked about specific view info:
switch (uView) { case SV2GV_CURRENTVIEW: hr = SVIDFromViewMode((FOLDERVIEWMODE)_fs.ViewMode, pvid); break;
case SV2GV_DEFAULTVIEW: // tell the browser "default" so we can pick the right one later on
*pvid = VID_DefaultView; hr = S_OK; break;
default: hr = E_INVALIDARG; break; } }
return hr; }
// For Folder Advanced Options flags that we check often, it's better
// to cache the values as flags. Update them here.
void CDefView::_UpdateRegFlags() { DWORD dwValue, cbSize = sizeof(dwValue); if (ERROR_SUCCESS == SHGetValue(HKEY_CURRENT_USER, TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Advanced"), TEXT("ClassicViewState"), NULL, &dwValue, &cbSize) && dwValue) { _fWin95ViewState = TRUE; } else { _fWin95ViewState = FALSE; } }
BOOL CDefView::_SetupNotifyData() { if (!_pidlMonitor && !_lFSEvents) { LPCITEMIDLIST pidl = NULL; LONG lEvents = 0;
if (SUCCEEDED(CallCB(SFVM_GETNOTIFY, (WPARAM)&pidl, (LPARAM)&lEvents))) { _pidlMonitor = pidl; _lFSEvents = lEvents; } } return _pidlMonitor || _lFSEvents; }
void CDefView::_ShowViewEarly() { // Show the window early (what old code did)
SetWindowPos(_hwndView, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE|SWP_SHOWWINDOW); _OnMoveWindowToTop(_hwndView); UpdateWindow(_hwndView); }
BOOL LV_FindWorkArea(RECT rcWorkAreas[], int nWorkAreas, POINT *ppt, int *piWorkArea) { for (int iWork = 0; iWork < nWorkAreas; iWork++) { if (PtInRect(&rcWorkAreas[iWork], *ppt)) { *piWorkArea = iWork; return TRUE; } }
*piWorkArea = 0; // default case is the primary work area
return FALSE; }
void CDefView::_ClearItemPositions() { _fUserPositionedItems = FALSE; _vs.ClearPositionData(); }
//
// This function finds the Recycle bin icon and freezes it. It also freezes the bottom right corner
// slot sothat no icon can occupy it.
//
int CDefView::_FreezeRecycleBin(POINT *ppt) { int iIndexRecycleBin = -1;
if (_IsDesktop()) { LPITEMIDLIST pidlRecycleBin; if (SUCCEEDED(SHGetFolderLocation(NULL, CSIDL_BITBUCKET, NULL, 0, &pidlRecycleBin))) { //Find the index of the recycle bin in the listview.
iIndexRecycleBin = _FindItem(pidlRecycleBin, NULL, FALSE); if (iIndexRecycleBin >= 0) //If we don't find recycle bin, we don't have anything to do!
{ //Freeze the recycle item (prevent it from moving)
ListView_SetFrozenItem(_hwndListview, TRUE, iIndexRecycleBin);
RECT rcItem; ListView_GetItemRect(_hwndListview, iIndexRecycleBin, &rcItem, LVIR_SELECTBOUNDS);
//Get the ViewRect.
RECT rcViewRect; int nWorkAreas = 0; //Get the number of work-areas
ListView_GetNumberOfWorkAreas(_hwndListview, &nWorkAreas); if (nWorkAreas > 1) { ASSERT(nWorkAreas <= LV_MAX_WORKAREAS); if (nWorkAreas <= LV_MAX_WORKAREAS) // just make sure in retail
{ RECT rcWorkAreas[LV_MAX_WORKAREAS]; int iCurWorkArea = 0; //Get all the work areas!
ListView_GetWorkAreas(_hwndListview, nWorkAreas, rcWorkAreas); //Find which work area the Recycle-bin currently lies.
LV_FindWorkArea(rcWorkAreas, nWorkAreas, (LPPOINT)(&rcItem.left), &iCurWorkArea); CopyRect(&rcViewRect, &rcWorkAreas[iCurWorkArea]); } } else { ListView_GetViewRect(_hwndListview, &rcViewRect); }
//Calculate the bottom-right corner of this slot
POINT ptRecycleBin; ptRecycleBin.x = rcViewRect.right; ptRecycleBin.y = rcViewRect.bottom;
//Freeze this slot sothat no other icon can occupy this.
ListView_SetFrozenSlot(_hwndListview, TRUE, &ptRecycleBin);
RECT rcIcon; ListView_GetItemRect(_hwndListview, iIndexRecycleBin, &rcIcon, LVIR_ICON);
ppt->x = rcViewRect.right - RECTWIDTH(rcIcon) - (RECTWIDTH(rcItem) - RECTWIDTH(rcIcon))/2; ppt->y = rcViewRect.bottom - RECTHEIGHT(rcItem); } ILFree(pidlRecycleBin); } }
return iIndexRecycleBin; }
//
// This function moves the RecycleBin item to the given location and then unfreezes the item and
// the frozen slot.
//
void CDefView::_SetRecycleBinInDefaultPosition(POINT *ppt) { // If a sorting has happened since an item was frozen, the index of that item would have changed.
// So, get the index of the recycle bin here.
int iIndexRecycleBin = ListView_GetFrozenItem(_hwndListview);
if (iIndexRecycleBin != LV_NOFROZENITEM) { //Move the recycle-bin icon to it's default position
_SetItemPosition(iIndexRecycleBin, ppt->x, ppt->y); //Unfreeze the slot
ListView_SetFrozenSlot(_hwndListview, FALSE, NULL); //FALSE ==> Unfreeze!
//Unfreeze the recycle bin
ListView_SetFrozenItem(_hwndListview, FALSE, 0); //FALSE ==> Unfreeze!
//Since we repositioned recyclebin earlier, we need to save it in the registry.
//Do we need this?
// SaveViewState();
} }
STDMETHODIMP CDefView::CreateViewWindow2(LPSV2CVW2_PARAMS pParams) { if (g_dwProfileCAP & 0x00000001) StopCAP();
if (pParams->cbSize < sizeof(SV2CVW2_PARAMS)) return E_INVALIDARG;
pParams->hwndView = NULL;
_RegisterWindow();
if (_hwndView || !pParams->psbOwner) return E_UNEXPECTED;
DECLAREWAITCURSOR; SetWaitCursor();
// Need to leave this code as is. Previously, we had changed it to
// pParams->psbOwner->QueryInterface(IID_PPV_ARG(IShellBrowser, &_psb));
// However, this breaks Corel Quattro Pro 8 in their filesave dialog.
// They pass in some sort of dummy "stub" IShellBrowser. QI'ing it for IShellBrowser
// will do nothing, and thus _psb will remain null, and we crash. Restoring it to
// the old way, _psb will be their "stub", but still valid, IShellBrowser.
// Look for other comments for "Quattro Pro" in this file to see why they pass
// in this stub.
// (do this before doing the GetWindowRect)
_psb = pParams->psbOwner; _psb->AddRef(); ASSERT(_psb); // much of our code assumes this to be valid w/o checking
#ifdef _X86_
// Verify that the CHijaakObjectWithSite is properly laid out
COMPILETIME_ASSERT(FIELD_OFFSET(CDefView, _psfHijaak) + sizeof(_psfHijaak) == FIELD_OFFSET(CDefView, _psb)); #endif
_fGlobeCanSpin = TRUE; _GlobeAnimation(TRUE);
HRESULT hr;
SHELLSTATE ss; // we will need these bits later on
SHGetSetSettings(&ss, SSF_WIN95CLASSIC | SSF_DESKTOPHTML | SSF_WEBVIEW | SSF_STARTPANELON, FALSE);
_pshf->QueryInterface(IID_PPV_ARG(IShellIcon, &_psi)); _pshf->QueryInterface(IID_PPV_ARG(IShellIconOverlay, &_psio));
pParams->psbOwner->QueryInterface(IID_PPV_ARG(ICommDlgBrowser, &_pcdb));
// listview starts out in large icon mode, we will switch to the proper view shortly
_fs.ViewMode = FVM_ICON;
// refetch FWF_ after browser supplied versions stomped our copy
_fs.fFlags = pParams->pfs->fFlags & ~FWF_OWNERDATA; CallCB(SFVM_FOLDERSETTINGSFLAGS, 0, (LPARAM)&_fs.fFlags);
// pvid takes precedence over pfs->ViewMode
UINT fvm = pParams->pfs->ViewMode; if (pParams->pvid) { if (IsEqualIID(*pParams->pvid, VID_DefaultView)) fvm = FVM_LAST + 1; // not a real view -- we will pick after enumeration
else ViewModeFromSVID(pParams->pvid, (FOLDERVIEWMODE *)&fvm); }
// This should never fail
_psb->GetWindow(&_hwndMain); ASSERT(IsWindow(_hwndMain)); CallCB(SFVM_HWNDMAIN, 0, (LPARAM)_hwndMain);
// We need to restore the column widths and icon positions before showing the window
if (!GetViewState()) { // Icon positions are not available; Therefore, it is a clean install
// and we need to position recycle bin if this is Desktop.
_fPositionRecycleBin = BOOLIFY(_IsDesktop()); } _fSyncOnFillDone = TRUE; // apply the just-loaded view state when we finish enumeration
// if there was a previous view that we know about, update our column state
if (_fWin95ViewState && pParams->psvPrev) { _vs.InitFromPreviousView(pParams->psvPrev); }
_pEnumTask = new CDefviewEnumTask(this, ++_dwEnumId); if (_pEnumTask && CreateWindowEx(IS_WINDOW_RTL_MIRRORED(_hwndMain) ? dwExStyleRTLMirrorWnd : 0, TEXT("SHELLDLL_DefView"), NULL, WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_TABSTOP, pParams->prcView->left, pParams->prcView->top, pParams->prcView->right - pParams->prcView->left, pParams->prcView->bottom - pParams->prcView->top, _hwndMain, NULL, HINST_THISDLL, this)) { // See if they want to overwrite the selection object
if (_fs.fFlags & FWF_OWNERDATA) { // Only used in owner data.
ILVRange *plvr = NULL; CallCB(SFVM_GETODRANGEOBJECT, LVSR_SELECTION, (LPARAM)&plvr); if (plvr) { ListView_SetLVRangeObject(_hwndListview, LVSR_SELECTION, plvr); plvr->Release(); // We assume the lv will hold onto it...
}
plvr = NULL; CallCB(SFVM_GETODRANGEOBJECT, LVSR_CUT, (LPARAM)&plvr); if (plvr) { ListView_SetLVRangeObject(_hwndListview, LVSR_CUT, plvr); plvr->Release(); // We assume the lv will hold onto it...
} }
// This needs to be done before calling _BestFit (used to be in _FillObjects)
// so that the parent can handle size changes effectively.
pParams->hwndView = _hwndView;
// Since ::FillObjects can take a while we force a paint now
// before any items are added so we don't see the gray background of
// the explorer window for a long time.
//
// We used to do this after determining "async-ness" of the view, which
// required us to pick the webview template. We want to postpone that
// decision so force the repaint in the same scenarios that we otherwise
// would have (non-webview or desktop).
//
// Make an educated guess here, if we get it wrong, we fix it up below.
//
if (!_ShouldShowWebView() || _IsDesktop()) { _ShowViewEarly(); }
// Try and fill the listview synchronously with view creation.
//
_fAllowSearchingWindow = TRUE; hr = _pEnumTask->FillObjectsToDPA(TRUE); if (SUCCEEDED(hr)) { // Setting the view mode has to happen after SFVM_ENUMERATEDITEMS
// NOTE: this also AddColumns() if the new view requires them
if (FVM_LAST + 1 == fvm) _GetDeferredViewSettings(&fvm);
// Don't call SetCurrentViewMode since it clears position data and we may have read in
// position data via GetViewState but haven't used it yet. Call _SwitchToViewFVM directly.
hr = _SwitchToViewFVM(fvm, SWITCHTOVIEW_NOWEBVIEW);
// The following bits depend on the result of _SwitchToViewFVM.
// It returns the value from turning on web view,
// this is used to determine async defview behavior (so we have
// an answer to the SHDVID_CANACTIVATENOW question the browser
// will soon ask us).
//
// Note: Desktop synchronous, even for web view
//
if (SUCCEEDED(hr) && _IsDesktop()) hr = S_OK; _fCanActivateNow = (S_OK == hr); // S_FALSE implies async waiting for ReadyStateInteractive
_fIsAsyncDefView = !BOOLIFY(_fCanActivateNow); // needed in a separate bit since _fCanActivateNow changes
// This has to happen after _SwitchToViewFVM so it can calculate
// the correct size of the window
_BestFit();
// Tell the defview client that this windows has been initialized
// Note that this must come before _pEnumTask->FillObjectsDPAToDone() so that the status bar displays
// (Disk Free space xxGB) correctly in explorer view.
CallCB(SFVM_WINDOWCREATED, (WPARAM)_hwndView, 0);
//
// If this is desktop, we need to calc and upgrade the grid sizes.
// (This is needed because the SnapToGrid may be ON and the default grid size
// will result in large gutter space on the edges).
//
if (_IsDesktop()) { DWORD dwLVExStyle = ListView_GetExtendedListViewStyle(_hwndListview); //
//Since the work areas are NOT yet set for the desktop's listview (because this is too early
//in it's creation, we pass just one work area and the view rect as work area here.)
//
UpdateGridSizes(TRUE, _hwndListview, 1, pParams->prcView, BOOLIFY(dwLVExStyle & LVS_EX_SNAPTOGRID)); } // Doing this after _BestFit means we dont need to auto-auto arrange
_pEnumTask->FillObjectsDPAToDone();
// splitting this function call in half means that we won't call WebView with contents changed for initial population
_SwitchToViewFVM(fvm, SWITCHTOVIEW_WEBVIEWONLY);
// If we're activating now, make sure we did the synchronous thing up above...
// (If not, do it now -- otherwise defview may never be shown)
if (_fCanActivateNow && !(!_ShouldShowWebView() || _IsDesktop())) { _ShowViewEarly(); } if (_IsDesktop()) { HideIE4DesktopChannelBar(); }
// turn on proper background and colors
_fClassic = ss.fWin95Classic; _UpdateListviewColors();
// this needs to be done after the enumeration
if (_SetupNotifyData()) { SHChangeNotifyEntry fsne = {0};
if (FAILED(CallCB(SFVM_QUERYFSNOTIFY, 0, (LPARAM)&fsne))) { // Reset entry
fsne.pidl = _pidlMonitor; fsne.fRecursive = FALSE; }
int iSources = (_lFSEvents & SHCNE_DISKEVENTS) ? SHCNRF_ShellLevel | SHCNRF_InterruptLevel : SHCNRF_ShellLevel; LONG lEvents = _lFSEvents | SHCNE_UPDATEIMAGE | SHCNE_UPDATEDIR; _uRegister = SHChangeNotifyRegister(_hwndView, SHCNRF_NewDelivery | iSources, lEvents, WM_DSV_FSNOTIFY, 1, &fsne); }
// We do the toolbar before the menu bar to avoid flash
if (!_IsDesktop()) MergeToolBar(TRUE);
// Note: it's okay for the CreateViewObject(&_pdtgtBack) to fail
ASSERT(_pdtgtBack == NULL); _pshf->CreateViewObject(_hwndMain, IID_PPV_ARG(IDropTarget, &_pdtgtBack));
// we don't really need to register drag drop when in the shell because
// our frame does it for us. we still need it here for comdlg and other
// hosts.. but for the desktop, let the desktpo frame take care of this
// so that they can do webbar d/d creation
if (!_IsDesktop()) { THR(RegisterDragDrop(_hwndListview, SAFECAST(this, IDropTarget*))); _bRegisteredDragDrop = TRUE; }
ASSERT(SUCCEEDED(hr))
PostMessage(_hwndView, WM_DSV_DELAYWINDOWCREATE, 0, 0);
if (SUCCEEDED(CallCB(SFVM_QUERYCOPYHOOK, 0, 0))) AddCopyHook();
if (SUCCEEDED(_GetIPersistHistoryObject(NULL))) { IBrowserService *pbs; if (SUCCEEDED(_psb->QueryInterface(IID_PPV_ARG(IBrowserService, &pbs)))) { IOleObject *pole; IStream *pstm; IBindCtx *pbc; pbs->GetHistoryObject(&pole, &pstm, &pbc); if (pole) { IUnknown_SetSite(pole, SAFECAST(this, IShellView2*)); // Set the back pointer.
if (pstm) { IPersistHistory *pph; if (SUCCEEDED(pole->QueryInterface(IID_PPV_ARG(IPersistHistory, &pph)))) { pph->LoadHistory(pstm, pbc); pph->Release(); } pstm->Release(); } IUnknown_SetSite(pole, NULL); // just to be safe...
if (pbc) pbc->Release(); pole->Release(); } pbs->Release(); } }
if (_psb && !_dwProffered) { // Proffer DVGetEnum service: this connects CDefView with the tree control for
// optimized navigation.
IUnknown_ProfferService(_psb, SID_SFolderView, SAFECAST(this, IServiceProvider *), &_dwProffered); // Failure here does not require special handling
} } else { // Cleanup - enum failed.
DestroyViewWindow(); } } else { hr = E_OUTOFMEMORY; }
_GlobeAnimation(FALSE);
ResetWaitCursor();
return hr; }
struct SCHEDULER_AND_HWND { IShellTaskScheduler *pScheduler; HWND hwnd; };
STDMETHODIMP CDefView::DestroyViewWindow() { if (_fDestroying) return S_OK;
if (_psb && _dwProffered) { // Revoke DVGetEnum service
IUnknown_ProfferService(_psb, SID_SFolderView, NULL, &_dwProffered); // Failure here does not require special handling
}
// Make sure that we stop the spinning globe before going away.
_GlobeAnimation(FALSE, TRUE);
_fDestroying = TRUE;
// 99/04/16 #326158 vtan: Loop thru the headers looking for
// stray HBITMAPs which need to be DeleteObject'd. Don't bother
// setting it back the header is about to be dumped.
// NOTE: Make sure this gets executed BEFORE the view gets
// dumped below in DestoryViewWindow().
if (IsWindow(_hwndListview)) { HWND hwndHeader = ListView_GetHeader(_hwndListview); if (IsWindow(hwndHeader)) { int iHeaderCount = Header_GetItemCount(hwndHeader); for (int i = 0; i < iHeaderCount; ++i) { HDITEM hdi = {0}; hdi.mask = HDI_BITMAP; Header_GetItem(hwndHeader, i, &hdi); if (hdi.hbm != NULL) TBOOL(DeleteObject(hdi.hbm)); } } }
_cFrame.HideWebView();
//
// Just in case...
//
OnDeactivate();
if (IsWindow(_hwndView)) { //
// This is a bit lazy implementation, but minimum code.
//
RemoveCopyHook();
// Tell the defview client that this window will be destroyed
CallCB(SFVM_WINDOWDESTROY, (WPARAM)_hwndView, 0); }
if (IsWindow(_hwndView)) { if (_pScheduler) { // empty the queue but do NOT wait until it is empty.....
_pScheduler->RemoveTasks(TOID_NULL, ITSAT_DEFAULT_LPARAM, FALSE);
// If there is still a task going, then kill our window later, as to not
// block the UI thread.
#ifdef DEBUG
// Stress the feature in debug mode
if (1) #else
if (_GetBackgroundTaskCount(TOID_NULL) > 0) #endif
{ ShowWindow(_hwndView, SW_HIDE);
// We are NOT passing 'this' defview pointer to the background thread
// because we do not want the destructor of defview to be called on any
// thread other than the one it was created on.
SCHEDULER_AND_HWND *pData = (SCHEDULER_AND_HWND *)LocalAlloc(LPTR, sizeof(*pData)); if (pData) { _pScheduler->AddRef(); pData->pScheduler = _pScheduler; pData->hwnd = _hwndView; // We need to keep Browseui loaded because we depend on the CShellTaskScheduler
// to be still around when our background task executes. Browseui can be unloaded by COM when
// we CoUninit from this thread.
if (SHQueueUserWorkItem(CDefView::BackgroundDestroyWindow, pData, 0, NULL, NULL, "browseui.dll", 0)) goto exit; else { LocalFree(pData); _pScheduler->Release(); } } } } DestroyWindow(_hwndView); }
exit: return S_OK; }
DWORD CDefView::BackgroundDestroyWindow(void *pvData) { SCHEDULER_AND_HWND *pData = (SCHEDULER_AND_HWND *)pvData;
// Note: the window coud have been already destroyed before we get here
// in the case where the frame gets closed down.
if (IsWindow(pData->hwnd)) { // Remove all tasks
EmptyBkgrndThread(pData->pScheduler);
// We need to release before we post to ensure that browseui doesn't get unloaded from under us (pScheduler is
// in browseui.dll). Browseui can get unloaded when we uninitialize OLE's MTA, even if there are still refs on the DLL.
pData->pScheduler->Release(); PostMessage(pData->hwnd, WM_DSV_DELAYED_DESTROYWND, 0, 0); } else { pData->pScheduler->Release(); }
LocalFree(pData); return 0; }
void CDefView::_MergeViewMenu(HMENU hmenuViewParent, HMENU hmenuMerge) { HMENU hmenuView = _GetMenuFromID(hmenuViewParent, FCIDM_MENU_VIEW); if (hmenuView) { #ifdef DEBUG
DWORD dwValue; DWORD cbSize = sizeof(dwValue); if (ERROR_SUCCESS == SHGetValue(HKEY_CURRENT_USER, TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Advanced"), TEXT("DebugWebView"), NULL, &dwValue, &cbSize) && dwValue) { MENUITEMINFO mi = {0}; mi.cbSize = sizeof(mi); mi.fMask = MIIM_TYPE|MIIM_ID; mi.fType = MFT_STRING; mi.dwTypeData = TEXT("Show WebView Content"); mi.wID = SFVIDM_DEBUG_WEBVIEW; InsertMenuItem(hmenuMerge, -1, MF_BYPOSITION, &mi); } #endif
//
// Find the "options" separator in the view menu.
//
int index = MenuIndexFromID(hmenuView, FCIDM_MENU_VIEW_SEP_OPTIONS);
//
// 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); } }
void CDefView::_SetUpMenus(UINT uState) { //
// If this is desktop, don't bother creating menu
//
if (!_IsDesktop()) { OnDeactivate();
ASSERT(_hmenuCur == NULL);
HMENU hMenu = CreateMenu(); if (hMenu) { HMENU hMergeMenu; OLEMENUGROUPWIDTHS mwidth = { { 0, 0, 0, 0, 0, 0 } };
_hmenuCur = hMenu; _psb->InsertMenusSB(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 | MM_DONTREMOVESEPS);
Shell_MergeMenus(_GetMenuFromID(hMenu, FCIDM_MENU_EDIT), GetSubMenu(hMergeMenu, 1), (UINT)-1, 0, (UINT)-1, MM_ADDSEPARATOR | MM_SUBMENUSHAVEIDS | MM_DONTREMOVESEPS);
_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
_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); } }
// Allow the client to merge its own menus
UINT indexClient = GetMenuItemCount(hMenu)-1; QCMINFO info = { hMenu, indexClient, SFVIDM_CLIENT_FIRST, SFVIDM_CLIENT_LAST }; CallCB(SFVM_MERGEMENU, 0, (LPARAM)&info);
_psb->SetMenuSB(hMenu, NULL, _hwndView); } } }
// set up the menus based on our activation state
//
BOOL CDefView::OnActivate(UINT uState) { if (_uState != uState) { _SetUpMenus(uState); _uState = uState; }
return TRUE; }
BOOL CDefView::OnDeactivate() { if (_hmenuCur || (_uState != SVUIA_DEACTIVATE)) { if (!_IsDesktop()) { ASSERT(_hmenuCur);
CallCB(SFVM_UNMERGEMENU, 0, (LPARAM)_hmenuCur);
_psb->SetMenuSB(NULL, NULL, NULL); _psb->RemoveMenusSB(_hmenuCur); DestroyMenu(_hmenuCur); _hmenuCur = NULL; } _uState = SVUIA_DEACTIVATE; } return TRUE; }
void CDefView::_OnMoveWindowToTop(HWND hwnd) { //
// Let the browser know that this has happened
//
VARIANT var; var.vt = VT_INT_PTR; var.byref = hwnd;
IUnknown_Exec(_psb, &CGID_Explorer, SBCMDID_ONVIEWMOVETOTOP, 0, &var, NULL); }
//
// 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(UINT uState) { if (SVUIA_DEACTIVATE == uState) { OnDeactivate(); ASSERT(_hmenuCur==NULL); } else { if (_fIsAsyncDefView) { // Need to show the defview window for the Async Case only. Showing
// it earlier causes repaint problems(Bug 275266). Showing the window
// here for the Sync case also causes problems - when the client
// creates a Synchronous Defview and then hides it later which gets
// lost with this SetWindowPos (Bug 355392).
SetWindowPos(_hwndView, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE|SWP_SHOWWINDOW); UpdateWindow(_hwndView); _OnMoveWindowToTop(_hwndView); }
if (uState == SVUIA_ACTIVATE_NOFOCUS) { // we lost focus
// if in web view and we have valid ole obj (just being paranoid)
if (!_fCombinedView && _cFrame.IsWebView() && _cFrame._pOleObj) { _cFrame._UIActivateIO(FALSE, NULL); } }
// We may be waiting for ReadyState_Interactive. If requested,
// we should activate before then...
//
// When we boot, the desktop paints ugly white screen for several
// seconds before it shows the HTML content. This is because: the
// following code switches the oleobj even before it reaches readystate
// interactive. For desktop, we skip this here. When the new object
// reaches readystate interactive, we will show it!
if (!_IsDesktop()) { _cFrame._SwitchToNewOleObj();
// NOTE: The browser IP/UI-activates us when we become the
// current active view! We want to resize and show windows
// at that time. But if we're still waiting for _fCanActivateNow
// (ie, ReadyStateInteractive), then we need to cache this request
// and do it later. NOTE: if Trident caches the focus (done w/ TAB)
// then we don't need to do anything here...
//
if (uState == SVUIA_ACTIVATE_FOCUS) { _SetFocus(); // _SetFocus can set _uState without causing our menu to
// get created and merged. Clear it here so that OnActivate does the
// right thing.
if (!_hmenuCur) _uState = SVUIA_DEACTIVATE; }
} // else we are the desktop; do we also need to steal focus?
else if (uState == SVUIA_ACTIVATE_FOCUS) { HWND hwnd = GetFocus(); if (SHIsChildOrSelf(_hwndView, hwnd) != S_OK) _SetFocus(); }
// OnActivate must follow _SetFocus
OnActivate(uState);
ShowHideListView();
ASSERT(_IsDesktop() || _hmenuCur);
_cFrame._UpdateZonesStatusPane(NULL); }
return S_OK; }
STDMETHODIMP CDefView::GetCurrentInfo(LPFOLDERSETTINGS pfs) { *pfs = _fs; return S_OK; }
BOOL IsBackSpace(const MSG *pMsg) { return pMsg && (pMsg->message == WM_KEYDOWN) && (pMsg->wParam == VK_BACK); }
extern int IsVK_TABCycler(MSG *pMsg);
//***
// NOTES
// try ListView->TA first
// then if that fails try WebView->TA iff it has focus.
// then if that fails and it's a TAB we do WebView->UIAct
STDMETHODIMP CDefView::TranslateAccelerator(LPMSG pmsg) { // 1st, try ListView
if (_fInLabelEdit) { // the second clause stops us passing mouse key clicks to the toolbar if we are in label edit mode...
if (WM_KEYDOWN == pmsg->message || WM_KEYUP == pmsg->message) { // process this msg so the exploer does not get to translate
TranslateMessage(pmsg); DispatchMessage(pmsg); return S_OK; // we handled it
} else return S_FALSE; } // If we are in classic mode and if it's a tab and the listview doesn't have focus already, receive the tab.
else if (IsVK_TABCycler(pmsg) && !(_cFrame.IsWebView() || _pDUIView) && (GetFocus() != _hwndListview)) { _SetFocus(); return S_OK; }
if (GetFocus() == _hwndListview) { if (::TranslateAccelerator(_hwndView, _hAccel, pmsg)) { // we know we have a normal view, therefore this is
// the right translate accelerator to use, otherwise the
// common dialogs will fail to get any accelerated keys.
return S_OK; } else if (WM_KEYDOWN == pmsg->message || WM_SYSKEYDOWN == pmsg->message) { // MSHTML eats these keys for frameset scrolling, but we
// want to get them to our wndproc . . . translate 'em ourself
//
switch (pmsg->wParam) { case VK_LEFT: case VK_RIGHT: // only go through here if alt is not down.
// don't intercept all alt combinations because
// alt-enter means something
// this is for alt-left/right compat with IE
if (GetAsyncKeyState(VK_MENU) < 0) break; // fall through
case VK_UP: case VK_DOWN: case VK_HOME: case VK_END: case VK_PRIOR: case VK_NEXT: case VK_RETURN: case VK_F10: TranslateMessage(pmsg); DispatchMessage(pmsg); return S_OK; } } }
// 1.5th, before we pass it down, see whether shell browser handles it.
// we do this to make sure that webview has the same accelerator semantics
// no matter what view(s) are active.
// note that this is arguably inconsistent w/ the 'pass it to whoever has
// focus'.
//
// however *don't* do this if:
// - we're in a dialog (in which case the buttons should come 1st)
// (comdlg's shellbrowser xxx::TA impl is broken it always does S_OK)
// - it's a TAB (which is always checked last)
// - it's a BACKSPACE (we should give the currently active object the first chance).
// However, in this case, we should call TranslateAcceleratorSB() AFTER we've tried
// calling TranslateAccelerator() on the currently active control (_pActive) in
// _cFrame->OnTranslateAccelerator().
//
// note: if you muck w/ this code careful not to regress the following:
// - ie41:62140: mnemonics broken after folder selected in organize favs
// - ie41:62419: TAB activates addr and menu if folder selected in explorer
if (!_IsCommonDialog() && !IsVK_TABCycler(pmsg) && !IsBackSpace(pmsg)) if (S_OK == _psb->TranslateAcceleratorSB(pmsg, 0)) return S_OK;
BOOL bTabOffLastTridentStop = FALSE; BOOL bHadIOFocus = (_cFrame._HasFocusIO() == S_OK); // Cache this here before the _cFrame.OnTA() call below
// 2nd, try WebView if it's active
if (IsVK_TABCycler(pmsg) && _pDUIView) { if (_pDUIView->Navigate(GetAsyncKeyState(VK_SHIFT) >= 0)) return S_OK; }
if (_cFrame.IsWebView() && (S_OK == _cFrame.OnTranslateAccelerator(pmsg, &bTabOffLastTridentStop))) { return S_OK; }
// We've given _pActive->TranslateAccelerator() the first shot in
// _cFrame.OnTranslateAccelerator, but it failed. Let's try the shell browser.
if (IsBackSpace(pmsg) && (S_OK == _psb->TranslateAcceleratorSB(pmsg, 0))) return S_OK;
// 3rd, ???
if (::TranslateAccelerator(_hwndView, _hAccel, pmsg)) return S_OK;
// 4th, if it's a TAB, cycle to next guy
// hack: we fake a bunch of the TAB-activation handshaking
if (IsVK_TABCycler(pmsg) && _cFrame.IsWebView()) { HRESULT hr; BOOL fBack = (GetAsyncKeyState(VK_SHIFT) < 0);
if (!bHadIOFocus && bTabOffLastTridentStop) { // We were at the last tab stop in trident when the browser called defview->TA().
// When we called TA() on trident above, it must've told us that we are tabbing
// off the last tab stop (bTabOffLastTridentStop). This will leave us not setting focus
// on anything. But, we have to set focus to something. We can do this by calling TA()
// on trident again, which will set focus on the first tab stop again.
return _cFrame.OnTranslateAccelerator(pmsg, &bTabOffLastTridentStop); } else if (_cFrame._HasFocusIO() == S_OK) { // ExtView has focus, and doesn't want the TAB.
// this means we're TABing off of it.
// no matter what, deactivate it (since we're TABing off).
// if the view is next in the TAB order, (pseudo-)activate it,
// and return S_OK since we've handled it.
// o.w. return S_OK so our parent will activate whoever's next
// in the TAB order.
hr = _cFrame._UIActivateIO(FALSE, NULL); ASSERT(hr == S_OK);
// in web view listview already has focus so don't give it again
// that's not the case with desktop
if (fBack && _IsDesktop()) { SetFocus(_hwndListview); return S_OK; }
return S_FALSE; } else { if (!fBack) { hr = _cFrame._UIActivateIO(TRUE, pmsg); ASSERT(hr == S_OK || hr == S_FALSE); return hr; } } }
return S_FALSE; }
// Description:
// Regenerates the CDefView's menus. Used for regaining any menu items
// which may have been stripped via DeleteMenu(), as occurs for various
// particular view states.
//
// Example: Transitioning to a barricaded view automatically strips out
// a number of commands from the "View" menu which are not appropriate
// for the barricaded view. Thus, on the transition back out of the
// barricaded view, the menus must be recreated in order to regain
// any/all the menu items stripped (this is not to say a number of
// them may not be stripped again if we're just transitioning to
// another view which doesn't want them in there!).
//
void CDefView::RecreateMenus() { UINT uState = _uState; _SetUpMenus(uState); // Note _SetupMenus() calls OnDeactivate()
_uState = uState; // which sets _uState to SVUIA_DEACTIVATE.
}
void CDefView::InitViewMenu(HMENU hmInit) { // Initialize view menu accordingly...
if (_fBarrierDisplayed) _InitViewMenuWhenBarrierDisplayed(hmInit); else _InitViewMenuWhenBarrierNotDisplayed(hmInit);
// Remove any extraneous menu separators arising from initialization.
_SHPrettyMenu(hmInit); }
// Description:
// Used to initialize the entries of the "View" menu and its associated
// submenus whenever a soft barrier is being displayed.
//
// Note:
// This method is also employed when "Category View" is being used in
// browsing the Control Panel.
//
void CDefView::_InitViewMenuWhenBarrierDisplayed(HMENU hmenuView) { // If "list view" is not visible (i.e. we're in Category View in the
// Control Panel, or we're looking at a barricaded folder), we strip
// out the following stuff from the View menu:
//
// Filmstrip
// Thumbnails
// Tiles
// Icons
// List
// Details
// -------------------
// Arrange Icons By ->
// -------------------
// Choose Details...
// Customize This Folder...
// Remove menu entries.
DeleteMenu(hmenuView, SFVIDM_VIEW_THUMBSTRIP, MF_BYCOMMAND); DeleteMenu(hmenuView, SFVIDM_VIEW_THUMBNAIL, MF_BYCOMMAND); DeleteMenu(hmenuView, SFVIDM_VIEW_TILE, MF_BYCOMMAND); DeleteMenu(hmenuView, SFVIDM_VIEW_ICON, MF_BYCOMMAND); DeleteMenu(hmenuView, SFVIDM_VIEW_LIST, MF_BYCOMMAND); DeleteMenu(hmenuView, SFVIDM_VIEW_DETAILS, MF_BYCOMMAND); DeleteMenu(hmenuView, SFVIDM_MENU_ARRANGE, MF_BYCOMMAND); DeleteMenu(hmenuView, SFVIDM_VIEW_COLSETTINGS, MF_BYCOMMAND); DeleteMenu(hmenuView, SFVIDM_VIEW_CUSTOMWIZARD, MF_BYCOMMAND); }
// Description:
// Used to initialize the entries of the "View" menu and its associated
// submenus whenever a soft barrier is not being displayed.
//
void CDefView::_InitViewMenuWhenBarrierNotDisplayed(HMENU hmenuView) { DWORD dwListViewFlags = ListView_GetExtendedListViewStyle(_hwndListview);
UINT uEnabled = (MF_ENABLED | MF_BYCOMMAND); UINT uDisabled = (MF_GRAYED | MF_BYCOMMAND); UINT uChecked = (MF_CHECKED | MF_BYCOMMAND); UINT uUnchecked = (MF_UNCHECKED | MF_BYCOMMAND);
UINT uAAEnable; // Auto Arrange
UINT uAACheck; UINT uAGrEnable; // Align to Grid
UINT uAGrCheck;
// Initialize "view" menu entries.
_InitViewMenuViewsWhenBarrierNotDisplayed(hmenuView);
// Initialize "Arrange Icons By ->" submenu.
_InitArrangeMenu(hmenuView);
// Determine and set appropriate enable state for "Auto Arrange" and "Align to Grid".
if (_IsPositionedView() && _IsListviewVisible() && !(_fs.ViewMode == FVM_THUMBSTRIP)) uAAEnable = uAGrEnable = uEnabled; else uAAEnable = uAGrEnable = uDisabled; EnableMenuItem(hmenuView, SFVIDM_ARRANGE_AUTO, uAAEnable); EnableMenuItem(hmenuView, SFVIDM_ARRANGE_AUTOGRID, uAGrEnable);
// Determine and set appropriate check state for "Auto Arrange" and "Align to Grid".
uAACheck = (((uAAEnable == uEnabled) || _fGroupView || (_fs.ViewMode == FVM_THUMBSTRIP)) && _IsAutoArrange()) ? uChecked : uUnchecked; uAGrCheck = (((uAGrEnable == uEnabled) || _fGroupView) && (dwListViewFlags & LVS_EX_SNAPTOGRID)) ? uChecked : uUnchecked; CheckMenuItem(hmenuView, SFVIDM_ARRANGE_AUTO, uAACheck); CheckMenuItem(hmenuView, SFVIDM_ARRANGE_AUTOGRID, uAGrCheck);
// If icons are not being shown (such as can be set on the
// desktop), disable ALL icon-arrangement related commands.
if (!_IsListviewVisible()) { HMENU hArrangeSubMenu; UINT uID; int i = 0;
// Retrieve "Arrange Icons By ->" submenu.
hArrangeSubMenu = GetSubMenu(hmenuView, 2);
// Iterate and disable until we get to "Show Icons".
while (1) { uID = GetMenuItemID(hArrangeSubMenu, i);
if ((uID == SFVIDM_DESKTOPHTML_ICONS) || (uID == (UINT)-1)) break; else EnableMenuItem(hArrangeSubMenu, i, MF_GRAYED | MF_BYPOSITION);
i++; } } else if (!_ShouldShowWebView()) { // If Web View is off, then thumbstrip will never work...
DeleteMenu(hmenuView, SFVIDM_VIEW_THUMBSTRIP, MF_BYCOMMAND); }
// Remove "Customize This Folder..." if folder is not customizable.
if (!_CachedIsCustomizable()) { // The Folder Option "Classic style" and the shell restriction WIN95CLASSIC
// should be the same. (Per ChristoB, otherwise admin's never understand what
// the restriction means.) Since we want this to change DEFAULTs, and still
// allow the user to turn on Web View, we don't remove the customize wizard here.
int iIndex = MenuIndexFromID(hmenuView, SFVIDM_VIEW_CUSTOMWIZARD); if (iIndex != -1) { DeleteMenu(hmenuView, iIndex + 1, MF_BYPOSITION); // Remove Menu seperator
DeleteMenu(hmenuView, iIndex, MF_BYPOSITION); // Remove Customize
} } }
// Description:
// Initializes the "view" entries on a view menu. This involves stripping
// out any "view" entries for unsupported views, and additionally checking
// of the appropriate "view" entry for the current view.
//
// Note:
// This method should not be called if a soft barrier is being displayed.
// Remember that in this case there is no concept of a view, so why
// would someone be attempting to initialize "view" menu entries.
//
void CDefView::_InitViewMenuViewsWhenBarrierNotDisplayed(HMENU hmenuView) { ASSERT(!_fBarrierDisplayed);
// Remove menu entries for unsupported views.
for (UINT fvm = FVM_FIRST; fvm <= FVM_LAST; fvm++) if (!_ViewSupported(fvm)) DeleteMenu(hmenuView, SFVIDM_VIEW_FIRSTVIEW + fvm - FVM_FIRST, MF_BYCOMMAND);
// "Check" menu entry for current view.
CheckCurrentViewMenuItem(hmenuView); }
void CDefView::_GetCBText(UINT_PTR id, UINT uMsgT, UINT uMsgA, UINT uMsgW, LPTSTR psz, UINT cch) { *psz = 0;
WCHAR szW[MAX_PATH]; if (SUCCEEDED(CallCB(uMsgW, MAKEWPARAM(id - SFVIDM_CLIENT_FIRST, ARRAYSIZE(szW)), (LPARAM)szW))) SHUnicodeToTChar(szW, psz, cch); else { char szA[MAX_PATH]; if (SUCCEEDED(CallCB(uMsgA, MAKEWPARAM(id - SFVIDM_CLIENT_FIRST, ARRAYSIZE(szA)), (LPARAM)szA))) SHAnsiToTChar(szA, psz, cch); else CallCB(uMsgT, MAKEWPARAM(id - SFVIDM_CLIENT_FIRST, cch), (LPARAM)psz); } }
void CDefView::_GetMenuHelpText(UINT_PTR id, LPTSTR pszText, UINT cchText) { *pszText = 0;
if ((InRange(id, SFVIDM_CONTEXT_FIRST, SFVIDM_CONTEXT_LAST) && _pcmFile) || (InRange(id, SFVIDM_BACK_CONTEXT_FIRST, SFVIDM_BACK_CONTEXT_LAST) && _pcmContextMenuPopup)) { UINT uCMBias = SFVIDM_CONTEXT_FIRST; IContextMenu *pcmSel = NULL;
if (InRange(id, SFVIDM_CONTEXT_FIRST, SFVIDM_CONTEXT_LAST)) { pcmSel = _pcmFile; uCMBias = SFVIDM_CONTEXT_FIRST; } else if (InRange(id, SFVIDM_BACK_CONTEXT_FIRST, SFVIDM_BACK_CONTEXT_LAST)) { pcmSel = _pcmContextMenuPopup; uCMBias = SFVIDM_BACK_CONTEXT_FIRST; }
// First try to get the stardard help string
pcmSel->GetCommandString(id - uCMBias, GCS_HELPTEXT, NULL, (LPSTR)pszText, cchText); if (*pszText == 0) { // If we didn't get anything, try to grab the ansi version
CHAR szText[MAX_PATH]; szText[0] = 0; // Don't start with garbage in case of failure...
pcmSel->GetCommandString(id - uCMBias, GCS_HELPTEXTA, NULL, szText, ARRAYSIZE(szText)); SHAnsiToUnicode(szText, pszText, cchText); } } else if (InRange(id, SFVIDM_CLIENT_FIRST, SFVIDM_CLIENT_LAST) && HasCB()) { _GetCBText(id, SFVM_GETHELPTEXT, SFVM_GETHELPTEXTA, SFVM_GETHELPTEXTW, pszText, cchText); } else if (InRange(id, SFVIDM_GROUPSFIRST, SFVIDM_GROUPSLAST)) { TCHAR sz[MAX_PATH]; int idHelp = _fGroupView?IDS_GROUPBY_HELPTEXT:IDS_ARRANGEBY_HELPTEXT;
LoadString(HINST_THISDLL, idHelp, sz, ARRAYSIZE(sz)); wnsprintf(pszText, cchText, sz, _vs.GetColumnName((UINT)id - SFVIDM_GROUPSFIRST)); } else if (InRange(id, SFVIDM_GROUPSEXTENDEDFIRST, SFVIDM_GROUPSEXTENDEDLAST)) { // Can't think of anything descriptive
} else if (InRange(id, SFVIDM_FIRST, SFVIDM_LAST)) { if ((id == SFVIDM_EDIT_UNDO) && IsUndoAvailable()) { GetUndoText(pszText, cchText, UNDO_STATUSTEXT); } else { UINT idHelp = (UINT)id + SFVIDS_MH_FIRST; // Unfortunatly, this starts to hit other ranges, so I'm just hard coding this one instead of
// using the table. If you add more, we need another table method of associating ids and help strings
if (id == SFVIDM_GROUPBY) idHelp = IDS_GROUPBYITEM_HELPTEXT; LoadString(HINST_THISDLL, idHelp, pszText, cchText); } } }
void CDefView::_GetToolTipText(UINT_PTR id, LPTSTR pszText, UINT cchText) { *pszText = 0;
if (InRange(id, SFVIDM_CLIENT_FIRST, SFVIDM_CLIENT_LAST) && HasCB()) { _GetCBText(id, SFVM_GETTOOLTIPTEXT, SFVM_GETTOOLTIPTEXTA, SFVM_GETTOOLTIPTEXTW, pszText, cchText); } else if (InRange(id, SFVIDM_FIRST, SFVIDM_LAST)) { if (id == SFVIDM_EDIT_UNDO) { if (IsUndoAvailable()) { GetUndoText(pszText, cchText, UNDO_MENUTEXT); return; } } LoadString(HINST_THISDLL, (UINT)(IDS_TT_SFVIDM_FIRST + id), pszText, cchText); } else { // REVIEW: This might be an assert situation: missing tooltip info...
TraceMsg(TF_WARNING, "_GetToolTipText: tip request for unknown object"); } }
LRESULT CDefView::_OnMenuSelect(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) { _psb->SendControlMsg(FCW_STATUS, SB_SIMPLE, 0, 0, NULL); return 0; }
if (mf & (MF_SYSMENU | MF_SEPARATOR)) return 0;
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; }
_GetMenuHelpText(id, szHelpText, ARRAYSIZE(szHelpText)); _fBackgroundStatusTextValid = FALSE; _psb->SendControlMsg(FCW_STATUS, SB_SETTEXT, SBT_NOBORDERS | 255, (LPARAM)szHelpText, NULL); _psb->SendControlMsg(FCW_STATUS, SB_SIMPLE, 1, 0, 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 CDefView::_DismissEdit() { if (_uState == SVUIA_ACTIVATE_FOCUS) { ListView_CancelEditLabel(_hwndListview); } }
void CDefView::_OnInitMenu() { // We need to dismiss the edit mode if it is any.
_DismissEdit(); }
void _RemoveContextMenuItems(HMENU hmInit) { int i;
for (i = GetMenuItemCount(hmInit) - 1; i >= 0; --i) { MENUITEMINFO mii; mii.cbSize = sizeof(mii); mii.fMask = MIIM_ID | MIIM_ID; mii.cch = 0; // just in case
if (GetMenuItemInfo(hmInit, i, TRUE, &mii)) { if (InRange(mii.wID, SFVIDM_CONTEXT_FIRST, SFVIDM_CONTEXT_LAST)) { TraceMsg(TF_DEFVIEW, "_RemoveContextMenuItems: setting bDeleteItems at %d, %d", i, mii.wID); //bDeleteItems = TRUE;
DeleteMenu(hmInit, i, MF_BYPOSITION); } } } }
BOOL HasClientItems(HMENU hmenu) { int cItems = GetMenuItemCount(hmenu); for (int i = 0; i < cItems; i++) { UINT id = GetMenuItemID(hmenu, i);
if (InRange(id, SFVIDM_CLIENT_FIRST, SFVIDM_CLIENT_LAST)) return TRUE; } return FALSE; }
LRESULT CDefView::_OnInitMenuPopup(HMENU hmInit, int nIndex, BOOL fSystemMenu) { if (_hmenuCur) { // This old code makes sure we only switch on the wID for one of our top-level windows
// The id shouldn't be re-used, so this probably isn't needed. But it doesn't hurt...
//
MENUITEMINFO mii = {0}; mii.cbSize = sizeof(mii); mii.fMask = MIIM_SUBMENU | MIIM_ID; if (GetMenuItemInfo(_hmenuCur, nIndex, TRUE, &mii) && mii.hSubMenu == hmInit) { switch (mii.wID) { case FCIDM_MENU_FILE: // PERF note: we could avoid the rip-down-and-re-build our File menu
// if we have a _pcmFile and the _uState is the same as last
// time and the selection is identical to last time.
// First of all, clean up our last _pcmFile usage:
// remove all the menu items we've added
// remove the named separators for defcm
_RemoveContextMenuItems(hmInit); SHUnprepareMenuForDefcm(hmInit, SFVIDM_CONTEXT_FIRST, SFVIDM_CONTEXT_LAST); IUnknown_SetSite(_pcmFile, NULL); ATOMICRELEASE(_pcmFile);
// Second, handle the focus/nofocus menus
if (_uState == SVUIA_ACTIVATE_FOCUS) { // Enable/disable our menuitems in the "File" pulldown.
Def_InitFileCommands(_AttributesFromSel(SFGAO_CANRENAME | SFGAO_CANDELETE | SFGAO_CANLINK | SFGAO_HASPROPSHEET), hmInit, SFVIDM_FIRST, FALSE);
// Collect our new _pcmFile context menu
IContextMenu* pcmSel = NULL; _CreateSelectionContextMenu(IID_PPV_ARG(IContextMenu, &pcmSel));
IContextMenu* pcmBack = NULL; _pshf->CreateViewObject(_hwndMain, IID_PPV_ARG(IContextMenu, &pcmBack));
IContextMenu* rgpcm[] = { pcmSel, pcmBack }; Create_ContextMenuOnContextMenuArray(rgpcm, ARRAYSIZE(rgpcm), IID_PPV_ARG(IContextMenu, &_pcmFile));
if (pcmSel) pcmSel->Release();
if (pcmBack) pcmBack->Release(); } else if (_uState == SVUIA_ACTIVATE_NOFOCUS) { _pshf->CreateViewObject(_hwndMain, IID_PPV_ARG(IContextMenu, &_pcmFile)); }
// Third, merge in the context menu items
{ HRESULT hrPrepare = SHPrepareMenuForDefcm(hmInit, 0, 0, SFVIDM_CONTEXT_FIRST, SFVIDM_CONTEXT_LAST); if (_pcmFile) { IUnknown_SetSite(_pcmFile, SAFECAST(this, IShellView2*)); _pcmFile->QueryContextMenu(hmInit, 0, SFVIDM_CONTEXT_FIRST, SFVIDM_CONTEXT_LAST, CMF_DVFILE | CMF_NODEFAULT); } SHPrettyMenuForDefcm(hmInit, 0, SFVIDM_CONTEXT_FIRST, SFVIDM_CONTEXT_LAST, hrPrepare); } break;
case FCIDM_MENU_EDIT: // Enable/disable menuitems in the "Edit" pulldown.
Def_InitEditCommands(_AttributesFromSel(SFGAO_CANCOPY | SFGAO_CANMOVE), hmInit, SFVIDM_FIRST, _pdtgtBack, 0); _SHPrettyMenu(hmInit); break;
case FCIDM_MENU_VIEW: InitViewMenu(hmInit); break; } } }
// Check for a context menu's popup:
// assume the first item in the menu identifies the range
BOOL fHandled; _ForwardMenuMessages(GetMenuItemID(hmInit, 0), WM_INITMENUPOPUP, (WPARAM)hmInit, MAKELPARAM(nIndex, fSystemMenu), NULL, &fHandled);
// Maybe this is the callback's menu then?
if (!fHandled && _hmenuCur && HasCB() && HasClientItems(hmInit)) { CallCB(SFVM_INITMENUPOPUP, MAKEWPARAM(SFVIDM_CLIENT_FIRST, nIndex), (LPARAM)hmInit); }
return 0; }
// IShellView::AddPropertySheetPages
STDMETHODIMP CDefView::AddPropertySheetPages(DWORD dwRes, LPFNADDPROPSHEETPAGE lpfn, LPARAM lParam) { SFVM_PROPPAGE_DATA data;
ASSERT(IS_VALID_CODE_PTR(lpfn, FNADDPROPSHEETPAGE));
data.dwReserved = dwRes; data.pfn = lpfn; data.lParam = lParam;
// Call the callback to add pages
CallCB(SFVM_ADDPROPERTYPAGES, 0, (LPARAM)&data);
return S_OK; }
STDMETHODIMP CDefView::SaveViewState() { HRESULT hr;
IPropertyBag* ppb; hr = IUnknown_QueryServicePropertyBag(_psb, SHGVSPB_FOLDER, IID_PPV_ARG(IPropertyBag, &ppb));
if (SUCCEEDED(hr)) { hr = _vs.SaveToPropertyBag(this, ppb); ppb->Release(); } else { IStream *pstm; hr = _psb->GetViewStateStream(STGM_WRITE, &pstm); if (SUCCEEDED(hr)) { hr = _vs.SaveToStream(this, pstm); pstm->Release(); } else { // There are cases where we may not save out the complete view state
// but we do want to save out the column information (like Docfind...)
if (SUCCEEDED(CallCB(SFVM_GETCOLSAVESTREAM, STGM_READ, (LPARAM)&pstm))) { hr = _vs.SaveColumns(this, pstm); pstm->Release(); } } } return hr; }
// 99/02/05 #226140 vtan: Function used to get the storage
// stream for the default view state of the current DefView.
// Typically this will be CLSID_ShellFSFolder but can be
// others.
HRESULT CDefView::_GetStorageStream (DWORD grfMode, IStream* *ppIStream) { *ppIStream = NULL;
CLSID clsid; HRESULT hr = IUnknown_GetClassID(_pshf, &clsid); if (SUCCEEDED(hr)) { TCHAR szCLSID[64]; // enough for the CLSID
if (IsEqualGUID(CLSID_MyDocuments, clsid)) clsid = CLSID_ShellFSFolder;
TINT(SHStringFromGUID(clsid, szCLSID, ARRAYSIZE(szCLSID))); *ppIStream = OpenRegStream(HKEY_CURRENT_USER, REGSTR_PATH_EXPLORER TEXT("\\Streams\\Defaults"), szCLSID, grfMode); if (*ppIStream == NULL) hr = E_FAIL; } return hr; }
// 99/02/05 #226140 vtan: Function called from DefView's
// implementation of IOleCommandTarget::Exec() which is
// invoked from CShellBrowser2::SetAsDefFolderSettings().
HRESULT CDefView::_SaveGlobalViewState(void) { IStream *pstm; HRESULT hr = _GetStorageStream(STGM_WRITE, &pstm); if (SUCCEEDED(hr)) { hr = _vs.SaveToStream(this, pstm); if (SUCCEEDED(hr)) { hr = (ERROR_SUCCESS == SHDeleteKey(HKEY_CURRENT_USER, TEXT("Software\\Microsoft\\Windows\\ShellNoRoam\\Bags"))) ? S_OK : E_FAIL; } pstm->Release(); } return hr; }
// 99/02/05 #226140 vtan: Function called from
// GetViewState to get the default view state
// for this class.
HRESULT CDefView::_LoadGlobalViewState(IStream* *ppIStream) { return _GetStorageStream(STGM_READ, ppIStream); }
// 99/02/09 #226140 vtan: Function used to reset the
// global view states stored by deleting the key
// that stores all of them.
HRESULT CDefView::_ResetGlobalViewState(void) { SHDeleteKey(HKEY_CURRENT_USER, REGSTR_PATH_EXPLORER TEXT("\\Streams\\Defaults"));
LONG lRetVal = SHDeleteKey(HKEY_CURRENT_USER, TEXT("Software\\Microsoft\\Windows\\ShellNoRoam\\Bags")); return (ERROR_SUCCESS == lRetVal) ? S_OK : E_FAIL; }
void CDefView::_RestoreAllGhostedFileView() { ListView_SetItemState(_hwndListview, -1, 0, LVIS_CUT);
UINT c = ListView_GetItemCount(_hwndListview); for (UINT i = 0; i < c; i++) { if (_Attributes(_GetPIDL(i), SFGAO_GHOSTED)) ListView_SetItemState(_hwndListview, i, LVIS_CUT, LVIS_CUT); } }
HRESULT CDefView::SelectAndPositionItem(LPCITEMIDLIST pidlItem, UINT uFlags, POINT *ppt) { HRESULT hr;
if (NULL == pidlItem) hr = _SelectAndPosition(-1, uFlags, ppt); else if (ILFindLastID(pidlItem) == pidlItem) { if (_fInBackgroundGrouping) { Pidl_Set(&_pidlSelectAndPosition, pidlItem); _uSelectAndPositionFlags = uFlags; hr = S_OK; } else { int iItem = _FindItem(pidlItem, NULL, FALSE); if (iItem != -1) hr = _SelectAndPosition(iItem, uFlags, ppt); else hr = S_OK; } } else { RIP(ILFindLastID(pidlItem) == pidlItem); hr = E_INVALIDARG; } return hr; }
HRESULT CDefView::_SelectAndPosition(int iItem, UINT uFlags, POINT *ppt) { HRESULT hr = S_OK; // assume all is good
// See if we should first deselect everything else
if (-1 == iItem) { if (uFlags == SVSI_DESELECTOTHERS) { ListView_SetItemState(_hwndListview, -1, 0, LVIS_SELECTED); _RestoreAllGhostedFileView(); } else { hr = E_INVALIDARG; // I only know how to deselect everything
} } else { if (_pDUIView) { _fBarrierDisplayed = FALSE; _pDUIView->EnableBarrier(FALSE); }
if (uFlags & SVSI_TRANSLATEPT) { //The caller is asking us to take this point and convert it from screen Coords
// to the Client of the Listview.
LVUtil_ScreenToLV(_hwndListview, ppt); }
// set the position first so that the ensure visible scrolls to
// the new position
if (ppt) { _SetItemPosition(iItem, ppt->x, ppt->y); } else if ((SVSI_POSITIONITEM & uFlags) && _bMouseMenu && _IsPositionedView()) { _SetItemPosition(iItem, _ptDragAnchor.x, _ptDragAnchor.y); }
if ((uFlags & SVSI_EDIT) == SVSI_EDIT) { // Grab focus if the listview (or any of it's children) don't already have focus
HWND hwndFocus = GetFocus(); if (SHIsChildOrSelf(_hwndListview, hwndFocus) != S_OK) SetFocus(_hwndListview);
ListView_EditLabel(_hwndListview, iItem); } else { // change the item state
if (!(uFlags & SVSI_NOSTATECHANGE)) { 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(_hwndListview, -1, 0, LVIS_SELECTED); _RestoreAllGhostedFileView(); }
ListView_SetItemState(_hwndListview, iItem, state, stateMask); }
if (uFlags & SVSI_ENSUREVISIBLE) ListView_EnsureVisible(_hwndListview, iItem, FALSE);
// we should only set focus when SVUIA_ACTIVATE_FOCUS
// bug fixing that might break find target code
if (uFlags & SVSI_FOCUSED) SetFocus(_hwndListview);
if (uFlags & SVSI_SELECTIONMARK) ListView_SetSelectionMark(_hwndListview, iItem);
// if this is a check select view then set the state of that item accordingly
if (_fs.fFlags & FWF_CHECKSELECT) ListView_SetCheckState(_hwndListview, iItem, (uFlags & SVSI_CHECK)); } } return hr; }
STDMETHODIMP CDefView::SelectItem(int iItem, DWORD uFlags) { return _SelectAndPosition(iItem, uFlags, NULL); }
typedef struct { LPITEMIDLIST pidl; UINT uFlagsSelect; } DELAY_SEL_ITEM;
STDMETHODIMP CDefView::SelectItem(LPCITEMIDLIST pidlItem, UINT uFlags) { // if the listview isn't shown, there's nothing to select yet.
// Likewise if we are in the process of being created we should defer.
if (!_IsListviewVisible()) { if (!_hdsaSelect) { _hdsaSelect = DSA_Create(sizeof(DELAY_SEL_ITEM), 4); if (!_hdsaSelect) return E_OUTOFMEMORY; }
HRESULT hr = E_OUTOFMEMORY; DELAY_SEL_ITEM dvdsi; dvdsi.pidl = ILClone(pidlItem); if (dvdsi.pidl) { dvdsi.uFlagsSelect = uFlags; if (DSA_AppendItem(_hdsaSelect, &dvdsi) == DSA_ERR) ILFree(dvdsi.pidl); else hr = S_OK; } return hr; }
return SelectAndPositionItem(pidlItem, uFlags, NULL); }
// IFolderView
STDMETHODIMP CDefView::GetCurrentViewMode(UINT *pViewMode) { *pViewMode = _fs.ViewMode; return S_OK; }
STDMETHODIMP CDefView::SetCurrentViewMode(UINT uViewMode) { ASSERT(FVM_FIRST <= uViewMode && uViewMode <= FVM_LAST);
if (uViewMode != _vs._ViewMode) _ClearItemPositions();
return _SwitchToViewFVM(uViewMode); }
STDMETHODIMP CDefView::GetFolder(REFIID riid, void **ppv) { if (_pshf) return _pshf->QueryInterface(riid, ppv);
*ppv = NULL; return E_NOINTERFACE; }
STDMETHODIMP CDefView::Item(int iItemIndex, LPITEMIDLIST *ppidl) { HRESULT hr = E_FAIL; LPCITEMIDLIST pidl = _GetPIDL(iItemIndex); if (pidl) { hr = SHILClone(pidl, ppidl); } return hr; }
STDMETHODIMP CDefView::ItemCount(UINT uFlags, int *pcItems) { *pcItems = _GetItemArray(NULL, NULL, uFlags); return S_OK; }
HRESULT CDefView::_EnumThings(UINT uWhat, IEnumIDList **ppenum) { *ppenum = NULL;
LPCITEMIDLIST *apidl; UINT cItems; HRESULT hr = _GetItemObjects(&apidl, uWhat, &cItems); if (SUCCEEDED(hr)) { hr = CreateIEnumIDListOnIDLists(apidl, cItems, ppenum); LocalFree(apidl); } return hr; }
STDMETHODIMP CDefView::Items(UINT uWhat, REFIID riid, void **ppv) { HRESULT hr = E_NOINTERFACE; if (IID_IEnumIDList == riid) { hr = _EnumThings(uWhat, (IEnumIDList**)ppv); } else if (IID_IDataObject == riid) { if ((uWhat & SVGIO_TYPE_MASK) == SVGIO_SELECTION) { if (_pSelectionShellItemArray) { hr = _pSelectionShellItemArray->BindToHandler(NULL, BHID_DataObject, riid, ppv); } } else { hr = _GetUIObjectFromItem(riid, ppv, uWhat, FALSE); } } return hr; }
// inverse of ::SelectItem(..., SVSI_SELECTIONMARK)
STDMETHODIMP CDefView::GetSelectionMarkedItem(int *piItem) { *piItem = ListView_GetSelectionMark(_hwndListview); return (-1 == *piItem) ? S_FALSE : S_OK; }
STDMETHODIMP CDefView::GetFocusedItem(int *piItem) { *piItem = ListView_GetNextItem(_hwndListview, -1, LVNI_FOCUSED); return (-1 == *piItem) ? S_FALSE : S_OK; }
BOOL CDefView::_GetItemPosition(LPCITEMIDLIST pidl, POINT *ppt) { int i = _FindItem(pidl, NULL, FALSE); if (i != -1) return ListView_GetItemPosition(_hwndListview, i, ppt); return FALSE; }
STDMETHODIMP CDefView::GetItemPosition(LPCITEMIDLIST pidl, POINT *ppt) { return _GetItemPosition(pidl, ppt) ? S_OK : E_FAIL; }
STDMETHODIMP CDefView::GetSpacing(POINT* ppt) { if (ppt) { if (_fs.ViewMode != FVM_TILE) { BOOL fSmall;
switch (_fs.ViewMode) { case FVM_SMALLICON: case FVM_LIST: case FVM_DETAILS: fSmall = TRUE; break;
case FVM_ICON: case FVM_THUMBNAIL: case FVM_THUMBSTRIP: default: fSmall = FALSE; break; }
DWORD dwSize = ListView_GetItemSpacing(_hwndListview, fSmall); ppt->x = GET_X_LPARAM(dwSize); ppt->y = GET_Y_LPARAM(dwSize); } else { LVTILEVIEWINFO tvi; tvi.cbSize = sizeof(tvi); tvi.dwMask = LVTVIM_TILESIZE;
if (ListView_GetTileViewInfo(_hwndListview, &tvi)) { ppt->x = tvi.sizeTile.cx; ppt->y = tvi.sizeTile.cy; } else { // guess.
ppt->x = 216; ppt->y = 56; } } }
return _IsPositionedView() ? S_OK : S_FALSE; }
STDMETHODIMP CDefView::GetDefaultSpacing(POINT* ppt) { ASSERT(ppt);
if (_fs.ViewMode != FVM_THUMBNAIL && _fs.ViewMode != FVM_THUMBSTRIP && _fs.ViewMode != FVM_TILE) { DWORD dwSize = ListView_GetItemSpacing(_hwndListview, FALSE); ppt->x = GET_X_LPARAM(dwSize); ppt->y = GET_Y_LPARAM(dwSize); } else { // Bug #163528 (edwardp 8/15/00) Should get this data from comctl.
ppt->x = GetSystemMetrics(SM_CXICONSPACING); ppt->y = GetSystemMetrics(SM_CYICONSPACING); }
return S_OK; }
// IShellFolderView
STDMETHODIMP CDefView::GetAutoArrange() { return _IsAutoArrange() ? S_OK : S_FALSE; }
void CDefView::_ClearPendingSelectedItems() { if (_hdsaSelect) { HDSA hdsa = _hdsaSelect; _hdsaSelect = NULL; int cItems = DSA_GetItemCount(hdsa); for (int i = 0; i < cItems; i++) { DELAY_SEL_ITEM *pdvdsi = (DELAY_SEL_ITEM*)DSA_GetItemPtr(hdsa, i); if (pdvdsi) ILFree(pdvdsi->pidl); } DSA_Destroy(hdsa); } }
// Call this whenever the state changes such that SelectItem (above)
void CDefView::SelectPendingSelectedItems() { ASSERT(_IsListviewVisible()); if (_hdsaSelect) {
//
// Listview quirk: If the following conditions are met..
//
// 1. WM_SETREDRAW(FALSE) or ShowWindow(SW_HIDE)
// 2. Listview has never painted yet
// 3. LVS_LIST
//
// then ListView_LGetRects doesn't work. And consequently,
// everything that relies on known item rectangles (e.g.,
// LVM_ENSUREVISIBLE, sent by below SelectItem call) doesn't work.
//
// (1) ShowHideListView did a ShowWindow(SW_SHOW), but
// FillDone does a WM_SETREDRAW(FALSE).
// check _fListviewRedraw to see if condition (1) is met
//
// (2) We just showed the listview, if it's the first time,
// then Condition (2) has been met
//
// But wait, there's also a listview bug where SetWindowPos
// doesn't trigger it into thinking that the window is visible.
// So you have to send a manual WM_SHOWWINDOW, too.
//
// So if we detect that condition (3) is also met, we temporarily
// enable redraw (thereby cancelling condition 1), tell listview
// "No really, you're visible" -- this tickles it into computing
// column stuff -- then turn redraw back off.
//
if (_fListviewRedraw && (GetWindowStyle(_hwndListview) & LVS_TYPEMASK) == LVS_LIST) { // Evil hack (fix comctl32.dll v6.0 someday NTRAID#182448)
SendMessage(_hwndListview, WM_SETREDRAW, (WPARAM)TRUE, 0); SendMessage(_hwndListview, WM_SHOWWINDOW, TRUE, 0); SendMessage(_hwndListview, WM_SETREDRAW, (WPARAM)FALSE, 0); }
// End of listview hack workaround
HDSA hdsa = _hdsaSelect; _hdsaSelect = NULL; int cItems = DSA_GetItemCount(hdsa); for (int i = 0; i < cItems; i++) { DELAY_SEL_ITEM *pdvdsi = (DELAY_SEL_ITEM*)DSA_GetItemPtr(hdsa, i); if (pdvdsi) { SelectItem(pdvdsi->pidl, pdvdsi->uFlagsSelect); ILFree(pdvdsi->pidl); } } DSA_Destroy(hdsa); } }
HRESULT CDefView::_GetIPersistHistoryObject(IPersistHistory **ppph) { // See to see if specific folder wants to handle it...
HRESULT hr = CallCB(SFVM_GETIPERSISTHISTORY, 0, (LPARAM)ppph); if (FAILED(hr)) { // Here we can decide if we want to default should be to always save
// the default defview stuff or not. For now we will assume that we do
if (ppph) { CDefViewPersistHistory *pdvph = new CDefViewPersistHistory(); if (pdvph) { hr = pdvph->QueryInterface(IID_PPV_ARG(IPersistHistory, ppph)); pdvph->Release(); } else { *ppph = NULL; hr = E_OUTOFMEMORY; } } else hr = S_FALSE; // still succeeds but can detect on other side if desired...
} return hr; }
STDMETHODIMP CDefView::GetItemObject(UINT uWhat, REFIID riid, void **ppv) { HRESULT hr = E_NOINTERFACE; *ppv = NULL;
switch (uWhat & SVGIO_TYPE_MASK) { case SVGIO_BACKGROUND: if (IsEqualIID(riid, IID_IContextMenu) || IsEqualIID(riid, IID_IContextMenu2) || IsEqualIID(riid, IID_IContextMenu3)) { hr = _CBackgrndMenu_CreateInstance(riid, ppv); } else if (IsEqualIID(riid, IID_IDispatch) || IsEqualIID(riid, IID_IDefViewScript)) { if (!_pauto) { // try to create an Instance of the Shell dispatch for folder views...
IDispatch *pdisp; if (SUCCEEDED(SHExtCoCreateInstance(NULL, &CLSID_ShellFolderView, NULL, IID_PPV_ARG(IDispatch, &pdisp)))) { SetAutomationObject(pdisp); // we hold a ref here
ASSERT(_pauto); // the above grabbed this
pdisp->Release(); } }
// return the IDispath interface.
if (_pauto) hr = _pauto->QueryInterface(riid, ppv); } else if (IsEqualIID(riid, IID_IPersistHistory)) { // See if the folder wants a chance at this. The main
// case for this is the search results windows.
hr = _GetIPersistHistoryObject((IPersistHistory**)ppv); if (SUCCEEDED(hr)) { IUnknown_SetSite((IUnknown*)*ppv, SAFECAST(this, IShellView2*)); } } else if (_cFrame.IsWebView() && _cFrame._pOleObj) { hr = _cFrame._pOleObj->QueryInterface(riid, ppv); } break;
case SVGIO_ALLVIEW: if (_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 WM_DSV_BACKGROUNDENUMDONE
// message, we should never get a RIP
// We also need to allow WM_DSV_FILELISTFILLDONE since it can destroy _hwndStatic
if (PeekMessage(&msg, _hwndView, WM_DSV_BACKGROUNDENUMDONE, WM_DSV_BACKGROUNDENUMDONE, PM_REMOVE) || PeekMessage(&msg, _hwndView, WM_DSV_FILELISTFILLDONE, WM_DSV_FILELISTFILLDONE, PM_REMOVE) || PeekMessage(&msg, _hwndView, WM_DSV_GROUPINGDONE, WM_DSV_GROUPINGDONE, PM_REMOVE) || PeekMessage(&msg, _hwndStatic, 0, 0, PM_REMOVE)) { TranslateMessage(&msg); DispatchMessage(&msg); } } while (_hwndStatic);
ResetWaitCursor(); }
// Fall through
case SVGIO_SELECTION: hr = _GetUIObjectFromItem(riid, ppv, uWhat, TRUE); break; }
return hr; }
HRESULT CDefView::PreCreateInfotip(HWND hwndContaining, UINT_PTR uToolID, LPRECT prectTool) { ASSERT(hwndContaining != NULL); ASSERT(_FindPendingInfotip(hwndContaining, uToolID, NULL, FALSE) == S_FALSE);
PENDING_INFOTIP *ppi = new PENDING_INFOTIP; HRESULT hr; if (ppi) { ppi->hwndContaining = hwndContaining; ppi->uToolID = uToolID; ppi->rectTool = *prectTool;
if (_tlistPendingInfotips.AddTail(ppi)) { hr = S_OK; } else { hr = E_OUTOFMEMORY; delete ppi; } } else { hr = E_OUTOFMEMORY; }
return hr; }
HRESULT CDefView::PostCreateInfotip(HWND hwndContaining, UINT_PTR uToolID, HINSTANCE hinst, UINT_PTR uInfotipID, LPARAM lParam) { ASSERT(hwndContaining != NULL);
TOOLINFO *pti = new TOOLINFO; HRESULT hr; if (pti) { pti->cbSize = sizeof(TOOLINFO); pti->uFlags = 0; pti->hwnd = hwndContaining; pti->uId = uToolID; //pti->rect = initialized in _OnPostCreateInfotip()
pti->hinst = hinst; pti->lpszText = (LPWSTR)uInfotipID; pti->lParam = lParam;
hr = PostMessage(_hwndView, WM_DSV_POSTCREATEINFOTIP, (WPARAM)pti, lParam) ? S_OK : E_FAIL;
if (FAILED(hr)) { delete pti; } } else { hr = E_OUTOFMEMORY; }
return hr; }
HRESULT CDefView::PostCreateInfotip(HWND hwndContaining, UINT_PTR uToolID, LPCWSTR pwszInfotip, LPARAM lParam) { HRESULT hr = SHStrDup(pwszInfotip, (LPWSTR *)&pwszInfotip); if (SUCCEEDED(hr)) { hr = PostCreateInfotip(hwndContaining, uToolID, NULL, (UINT_PTR)pwszInfotip, lParam); if (FAILED(hr)) { CoTaskMemFree((LPVOID)pwszInfotip); } } return hr; }
HRESULT CDefView::_OnPostCreateInfotip(TOOLINFO *pti, LPARAM lParam) { HRESULT hr = _FindPendingInfotip(pti->hwnd, pti->uId, &pti->rect, TRUE); if (hr == S_OK) { hr = SendMessage(_hwndInfotip, TTM_ADDTOOL, 0, (LPARAM)pti) ? S_OK : E_FAIL; } _OnPostCreateInfotipCleanup(pti); return hr; }
HRESULT CDefView::_OnPostCreateInfotipCleanup(TOOLINFO *pti) { if (!pti->hinst) CoTaskMemFree(pti->lpszText); delete pti; return S_OK; }
HRESULT CDefView::_FindPendingInfotip(HWND hwndContaining, UINT_PTR uToolID, LPRECT prectTool, BOOL bRemoveAndDestroy) { CLISTPOS posNext = _tlistPendingInfotips.GetHeadPosition(); CLISTPOS posCurrent; PENDING_INFOTIP *ppi; HRESULT hr = S_FALSE;
while (posNext) { posCurrent = posNext; ppi = _tlistPendingInfotips.GetNext(posNext); if (ppi->hwndContaining == hwndContaining && ppi->uToolID == uToolID) { if (bRemoveAndDestroy) { if (prectTool) { // Use prectTool as out param.
*prectTool = ppi->rectTool; } _tlistPendingInfotips.RemoveAt(posCurrent); delete ppi; } else { if (prectTool) { // Use prectTool as in param.
ppi->rectTool = *prectTool; } } hr = S_OK; break; } }
// Post Contition -- callers expect only S_OK or S_FALSE.
ASSERT(hr == S_OK || hr == S_FALSE);
return hr; }
HRESULT CDefView::CreateInfotip(HWND hwndContaining, UINT_PTR uToolID, LPRECT prectTool, HINSTANCE hinst, UINT_PTR uInfotipID, LPARAM lParam) { ASSERT(hwndContaining != NULL);
// CreateInfotip() is not for use with PreCreateInfotip()/PostCreateInfotip().
ASSERT(_FindPendingInfotip(hwndContaining, uToolID, NULL, FALSE) == S_FALSE);
TOOLINFO ti;
ti.cbSize = sizeof(TOOLINFO); ti.uFlags = 0; ti.hwnd = hwndContaining; ti.uId = uToolID; ti.rect = *prectTool; ti.hinst = hinst; ti.lpszText = (LPWSTR)uInfotipID; ti.lParam = lParam;
return SendMessage(_hwndInfotip, TTM_ADDTOOL, 0, (LPARAM)&ti) ? S_OK : E_FAIL; }
HRESULT CDefView::CreateInfotip(HWND hwndContaining, UINT_PTR uToolID, LPRECT prectTool, LPCWSTR pwszInfotip, LPARAM lParam) { return CreateInfotip(hwndContaining, uToolID, prectTool, NULL, (UINT_PTR)pwszInfotip, lParam); }
HRESULT CDefView::DestroyInfotip(HWND hwndContaining, UINT_PTR uToolID) { ASSERT(hwndContaining != NULL);
if (_FindPendingInfotip(hwndContaining, uToolID, NULL, TRUE) == S_FALSE) { TOOLINFO ti = { 0 };
ti.cbSize = sizeof(ti); ti.hwnd = hwndContaining; ti.uId = uToolID;
SendMessage(_hwndInfotip, TTM_DELTOOL, 0, (LPARAM)&ti); }
return S_OK; }
// Note:
// Coordinates in prectTool must be relative to the hwnd in hwndContaining.
//
HRESULT CDefView::RepositionInfotip(HWND hwndContaining, UINT_PTR uToolID, LPRECT prectTool) { if (_FindPendingInfotip(hwndContaining, uToolID, prectTool, FALSE) == S_FALSE) { TOOLINFO ti = { 0 };
ti.cbSize = sizeof(ti); ti.hwnd = hwndContaining; ti.uId = uToolID; ti.rect = *prectTool;
SendMessage(_hwndInfotip, TTM_NEWTOOLRECT, 0, (LPARAM)&ti); }
return S_OK; }
HRESULT CDefView::RelayInfotipMessage(HWND hwndFrom, UINT uMsg, WPARAM wParam, LPARAM lParam) { HRESULT hr;
if (_hwndInfotip) { MSG msg; msg.hwnd = hwndFrom; msg.message = uMsg; msg.wParam = wParam; msg.lParam = lParam; SendMessage(_hwndInfotip, TTM_RELAYEVENT, 0, (LPARAM)&msg); hr = S_OK; } else { hr = E_FAIL; }
return hr; }
STDMETHODIMP CDefView::QueryInterface(REFIID riid, void **ppvObj) { static const QITAB qit[] = { QITABENT(CDefView, IShellView2), // IID_IShellView2
QITABENTMULTI(CDefView, IShellView, IShellView2), // IID_IShellView
QITABENT(CDefView, IViewObject), // IID_IViewObject
QITABENT(CDefView, IDropTarget), // IID_IDropTarget
QITABENT(CDefView, IShellFolderView), // IID_IShellFolderView
QITABENT(CDefView, IFolderView), // IID_IFolderView
QITABENT(CDefView, IOleCommandTarget), // IID_IOleCommandTarget
QITABENT(CDefView, IServiceProvider), // IID_IServiceProvider
QITABENT(CDefView, IDefViewFrame3), // IID_IDefViewFrame
QITABENT(CDefView, IDefViewFrame), // IID_IDefViewFrame
QITABENT(CDefView, IDocViewSite), // IID_IDocViewSite
QITABENT(CDefView, IInternetSecurityMgrSite), // IID_IInternetSecurityMgrSite
QITABENT(CDefView, IObjectWithSite), // IID_IObjectWithSite
QITABENT(CDefView, IPersistIDList), // IID_IPersistIDList
QITABENT(CDefView, IDVGetEnum), // IID_IDVGetEnum
QITABENT(CDefView, IContextMenuSite), // IID_IContextMenuSite
QITABENT(CDefView, IDefViewSafety), // IID_IDefViewSafety
QITABENT(CDefView, IUICommandTarget), // IID_IUICommandTarget
{ 0 } };
HRESULT hr = QISearch(this, qit, riid, ppvObj); if (FAILED(hr)) { // special case this one as it simply casts this...
if (IsEqualIID(riid, IID_CDefView)) { *ppvObj = (void *)this; AddRef(); hr = S_OK; } } return hr; }
STDMETHODIMP_(ULONG) CDefView::AddRef() { return InterlockedIncrement(&_cRef); }
STDMETHODIMP_(ULONG) CDefView::Release() { ASSERT( 0 != _cRef ); ULONG cRef = InterlockedDecrement(&_cRef); if ( 0 == cRef ) { delete this; } return cRef; }
//===========================================================================
// Constructor of CDefView class
//===========================================================================
CDefView::CDefView(IShellFolder *psf, IShellFolderViewCB *psfvcb, IShellView *psvOuter) : _cRef(1), _cCallback(psfvcb) { psf->QueryInterface(IID_PPV_ARG(IShellFolder, &_pshf)); psf->QueryInterface(IID_PPV_ARG(IShellFolder2, &_pshf2));
LPITEMIDLIST pidlFull = _GetViewPidl(); if (pidlFull) { LPCITEMIDLIST pidlRelative; if (SUCCEEDED(SHBindToFolderIDListParent(NULL, pidlFull, IID_PPV_ARG(IShellFolder, &_pshfParent), &pidlRelative))) { _pidlRelative = ILClone(pidlRelative); _pshfParent->QueryInterface(IID_PPV_ARG(IShellFolder2, &_pshf2Parent)); } ILFree(pidlFull); }
CallCB(SFVM_FOLDERSETTINGSFLAGS, 0, (LPARAM)&_fs.fFlags);
_vs.InitWithDefaults(this);
_rgbBackColor = CLR_INVALID;
_sizeThumbnail.cx = -1; // non init state
_iIncrementCat = 1;
_wvLayout.dwLayout = -1; // an invalid value
// NOTE we dont AddRef() psvOuter
// it has a ref on us
_psvOuter = psvOuter;
// the client needs this info to be able to do anything with us,
// so set it REALLY early on in the creation process
IUnknown_SetSite(_cCallback.GetSFVCB(), SAFECAST(this, IShellFolderView*));
for (int i = 0; i < ARRAYSIZE(_crCustomColors); i++) _crCustomColors[i] = CLR_MYINVALID;
_UpdateRegFlags();
IDLData_InitializeClipboardFormats();
if (SUCCEEDED(CoCreateInstance(CLSID_ShellTaskScheduler, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IShellTaskScheduler2, &_pScheduler)))) { // init a set a 60 second timeout
_pScheduler->Status(ITSSFLAG_KILL_ON_DESTROY, DEFVIEW_THREAD_IDLE_TIMEOUT); }
// Catch unexpected STACK allocations which would break us.
ASSERT(_hwndInfotip == NULL); }
STDMETHODIMP CDefView::Init() { HRESULT hr;
_hdpaGroupingListActive = DPA_Create(16); _hdpaGroupingListBackup = DPA_Create(16);
if (_hdpaGroupingListActive && _hdpaGroupingListBackup) { hr = S_OK; } else { hr = E_OUTOFMEMORY; }
return hr; }
STDAPI SHCreateShellFolderView(const SFV_CREATE* pcsfv, IShellView ** ppsv) { *ppsv = NULL; HRESULT hr = E_INVALIDARG;
if (pcsfv && sizeof(*pcsfv) == pcsfv->cbSize) { CDefView *pdsv = new CDefView(pcsfv->pshf, pcsfv->psfvcb, pcsfv->psvOuter); if (pdsv) { hr = pdsv->Init(); if (SUCCEEDED(hr)) { *ppsv = pdsv; } else { pdsv->Release(); } } } return hr; }
void CDVDropTarget::LeaveAndReleaseData() { DragLeave(); }
void CDVDropTarget::ReleaseDataObject() { ATOMICRELEASE(_pdtobj); }
void CDVDropTarget::ReleaseCurrentDropTarget() { CDefView *pdv = IToClass(CDefView, _dvdt, this); if (_pdtgtCur) { _pdtgtCur->DragLeave(); ATOMICRELEASE(_pdtgtCur); } pdv->_itemCur = -2; // WARNING: Never touch pdv->itemOver in this function.
}
HRESULT CDVDropTarget::DragEnter(IDataObject *pdtobj, DWORD grfKeyState, POINTL ptl, DWORD *pdwEffect) { CDefView *pdv = IToClass(CDefView, _dvdt, this);
IUnknown_Set((IUnknown **)&_pdtobj, pdtobj);
// Don't allow a drop from our webview content to ourself!
_fIgnoreSource = FALSE; IOleCommandTarget* pct; if (pdv->_cFrame.IsWebView() && SUCCEEDED(pdv->_cFrame.GetCommandTarget(&pct))) { VARIANTARG v = {0};
if (SUCCEEDED(pct->Exec(&CGID_ShellDocView, SHDVID_ISDRAGSOURCE, 0, NULL, &v))) { pct->Release(); if (v.lVal) { *pdwEffect = DROPEFFECT_NONE; _fIgnoreSource = TRUE; return S_OK; } } }
g_fDraggingOverSource = FALSE;
_grfKeyState = grfKeyState;
ASSERT(_pdtgtCur == NULL); // don't really need to do this, but this sets the target state
ReleaseCurrentDropTarget(); _itemOver = -2;
//
// In case of Desktop, we should not lock the enter screen.
//
HWND hwndLock = pdv->_IsDesktop() ? pdv->_hwndView : pdv->_hwndMain; GetWindowRect(hwndLock, &_rcLockWindow);
DAD_DragEnterEx3(hwndLock, ptl, pdtobj);
DAD_InitScrollData(&_asd);
_ptLast.x = _ptLast.y = 0x7fffffff; // put bogus value to force redraw
return S_OK; }
#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 CDefView::_AlterEffect(DWORD grfKeyState, DWORD *pdwEffect, UINT uFlags) { g_fDraggingOverSource = FALSE;
if (_IsDropOnSource(NULL)) { if (_IsPositionedView()) { // If this is default drag & drop, enable move.
if (uFlags & DVAE_AFTER) { if ((grfKeyState & (MK_LBUTTON | MK_CONTROL | MK_SHIFT | MK_ALT)) == MK_LBUTTON) { *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;
// default drag & drop, disable all.
if ((grfKeyState & (MK_LBUTTON | MK_CONTROL | MK_SHIFT | MK_ALT)) == MK_LBUTTON) { *pdwEffect = 0; } } } } }
HRESULT CDVDropTarget::DragOver(DWORD grfKeyState, POINTL ptl, DWORD *pdwEffect) { CDefView *pdv = IToClass(CDefView, _dvdt, this); HRESULT hr = S_OK; DWORD dwEffectScroll = 0; DWORD dwEffectOut = 0; DWORD dwEffectOutToCache; BOOL fSameImage = FALSE;
if (_fIgnoreSource) { // for parity with win2k behavior, we need to bail out from DragOver
// if we hit the SHDVID_ISDRAGSOURCE in DragEnter.
// this is so when you have a stretched background in active desktop and
// show desktop icons is off, when you drag the background image around
// you'll get DROPEFFECT_NONE instead of a bad DROPEFFECT_COPY.
*pdwEffect = DROPEFFECT_NONE; return S_OK; }
POINT pt = {ptl.x, ptl.y}; // in screen coords
RECT rc; GetWindowRect(pdv->_hwndListview, &rc); BOOL fInRect = PtInRect(&rc, pt);
ScreenToClient(pdv->_hwndListview, &pt); // now in client
// assume coords of our window match listview
if (DAD_AutoScroll(pdv->_hwndListview, &_asd, &pt)) dwEffectScroll = DROPEFFECT_SCROLL;
// hilight an item, or unhilight all items (DropTarget returns -1)
int itemNew = fInRect ? pdv->_HitTest(&pt, TRUE) : -1;
// If we are dragging over on a different item, get its IDropTarget
// interface or adjust itemNew to -1.
if (_itemOver != itemNew) { IDropTarget *pdtgtNew = NULL;
_dwLastTime = GetTickCount(); // keep track for auto-expanding the tree
_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] = { pdv->_GetPIDL(itemNew) }; if (apidl[0]) { pdv->_pshf->GetUIObjectOf(pdv->_hwndMain, 1, apidl, IID_PPV_ARG_NULL(IDropTarget, &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.
ReleaseCurrentDropTarget(); ASSERT(_pdtgtCur==NULL);
// Update pdv->_itemCur which indicates the current target.
// (Note that it might be different from _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); _pdtgtCur = pdv->_pdtgtBack; if (_pdtgtCur) _pdtgtCur->AddRef(); } else { ASSERT(pdtgtNew); _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 (_pdtgtCur) { // pdwEffect is in/out parameter.
dwEffectOut = *pdwEffect; // pdwEffect in
// Special case if we are dragging within a source window
pdv->_AlterEffect(grfKeyState, &dwEffectOut, DVAE_BEFORE); hr = _pdtgtCur->DragEnter(_pdtobj, grfKeyState, ptl, &dwEffectOut); pdv->_AlterEffect(grfKeyState, &dwEffectOut, DVAE_AFTER); } else { ASSERT(dwEffectOut==0); pdv->_AlterEffect(grfKeyState, &dwEffectOut, DVAE_BEFORE | DVAE_AFTER); }
TraceMsg(TF_DEFVIEW, "CDV::DragOver dwEIn=%x, dwEOut=%x", *pdwEffect, dwEffectOut); } else { ASSERT(pdtgtNew == NULL); // It must be NULL
goto NoChange; }
// Every time we're over a new item, record this information so we can handle the insertmark.
_fItemOverNotADropTarget = (itemNew == -1); } else { NoChange: if (_itemOver != -1) { DWORD dwNow = GetTickCount();
if ((dwNow - _dwLastTime) >= 1000) { _dwLastTime = dwNow; // DAD_ShowDragImage(FALSE);
// OpenItem(pdv, _itemOver);
// DAD_ShowDragImage(TRUE);
} }
//
// 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 ((_grfKeyState != grfKeyState) && _pdtgtCur) { // Note that pdwEffect is in/out parameter.
dwEffectOut = *pdwEffect; // pdwEffect in
// Special case if we are dragging within a source window
pdv->_AlterEffect(grfKeyState, &dwEffectOut, DVAE_BEFORE); hr = _pdtgtCur->DragOver(grfKeyState, ptl, &dwEffectOut); pdv->_AlterEffect(grfKeyState, &dwEffectOut, DVAE_AFTER); } else { // Same item and same key state. Use the previous dwEffectOut.
dwEffectOut = _dwEffectOut; fSameImage = TRUE; hr = S_OK; } }
// Cache the calculated dwEffectOut (BEFORE making local modifications below).
dwEffectOutToCache = dwEffectOut;
// Activate/deactivate insertmark, if appropriate.
LVINSERTMARK lvim = { sizeof(LVINSERTMARK), 0, -1, 0 }; if (_fItemOverNotADropTarget) { // Only do the insertion mark stuff if we're in a view mode that makes sense for these:
if (pdv->_IsAutoArrange() || (pdv->_fs.fFlags & FWF_SNAPTOGRID)) { ListView_InsertMarkHitTest(pdv->_hwndListview, &pt, &lvim);
if (pdv->_bDragSource && pdv->_IsAutoArrange() && (lvim.iItem == -1)) { // a "move" drop here won't do anything so set the effect appropriately
if (dwEffectOut & DROPEFFECT_MOVE) { // fall back to "copy" drop effect (if supported)
if (*pdwEffect & DROPEFFECT_COPY) { dwEffectOut |= DROPEFFECT_COPY; } // fall back to "link" drop effect (if supported)
else if (*pdwEffect & DROPEFFECT_LINK) { dwEffectOut |= DROPEFFECT_LINK; } // fall back to no drop effect
dwEffectOut &= ~DROPEFFECT_MOVE; }
// NOTE: a DROPEFFECT_MOVE still comes through the ::Drop for a left-drop...
// we might want to remember that we're exclududing move (_bDragSourceDropOnDragItem)
} } } ListView_SetInsertMark(pdv->_hwndListview, &lvim);
_grfKeyState = grfKeyState; // store these for the next Drop
_dwEffectOut = dwEffectOutToCache; // and DragOver
// 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
if (!(fSameImage && pt.x == _ptLast.x && pt.y == _ptLast.y)) { HWND hwndLock = pdv->_IsDesktop() ? pdv->_hwndView : pdv->_hwndMain; DAD_DragMoveEx(hwndLock, ptl); _ptLast.x = ptl.x; _ptLast.y = ptl.y; }
return hr; }
HRESULT CDVDropTarget::DragLeave() { CDefView *pdv = IToClass(CDefView, _dvdt, this);
//
// Make it possible to call it more than necessary.
//
if (_pdtobj) { TraceMsg(TF_DEFVIEW, "CDVDropTarget::DragLeave");
ReleaseCurrentDropTarget(); _itemOver = -2; ReleaseDataObject();
DAD_DragLeave(); LVUtil_DragSelectItem(pdv->_hwndListview, -1); }
g_fDraggingOverSource = FALSE;
ASSERT(_pdtgtCur == NULL); ASSERT(_pdtobj == NULL);
LVINSERTMARK lvim = { sizeof(LVINSERTMARK), 0, -1, 0 }; // clear insert mark (-1)
ListView_SetInsertMark(pdv->_hwndListview, &lvim);
return S_OK; }
HRESULT CDVDropTarget::Drop(IDataObject *pdtobj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect) { CDefView *pdv = IToClass(CDefView, _dvdt, this);
IUnknown_Set((IUnknown **)&_pdtobj, pdtobj);
pdv->_ptDrop.x = pt.x; pdv->_ptDrop.y = pt.y;
ScreenToClient(pdv->_hwndListview, &pdv->_ptDrop);
//
// handle moves within the same window here.
// depend on _AlterEffect forcing in DROPEFFECT_MOVE and only
// dropeffect move when drag in same window
//
// Notes: We need to use _grfKeyState instead of grfKeyState
// to see if the left mouse was used or not during dragging.
//
pdv->_AlterEffect(_grfKeyState, pdwEffect, DVAE_BEFORE | DVAE_AFTER);
if ((_grfKeyState & MK_LBUTTON) && (*pdwEffect == DROPEFFECT_MOVE) && (pdv->_IsDropOnSource(NULL))) { // This means we are left-dropping on ourselves, so we just move
// the icons.
DAD_DragLeave();
pdv->_SameViewMoveIcons();
SetForegroundWindow(pdv->_hwndMain);
ASSERT(pdv->_bDropAnchor == FALSE);
*pdwEffect = 0; // the underlying objects didn't 'move' anywhere
ReleaseCurrentDropTarget(); } else if (_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
IDropTarget *pdtgtCur = _pdtgtCur; _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.
//
pdv->_bDropAnchor = TRUE;
if (!DoesDropTargetSupportDAD(pdtgtCur)) { // This will hide the dragged image.
DAD_DragLeave();
// 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
pdv->_AlterEffect(grfKeyState, pdwEffect, DVAE_BEFORE | DVAE_AFTER);
IUnknown_SetSite(pdtgtCur, SAFECAST(pdv, IShellView2*));
pdtgtCur->Drop(pdtobj, grfKeyState, pt, pdwEffect);
IUnknown_SetSite(pdtgtCur, NULL);
pdtgtCur->Release();
DAD_DragLeave();
pdv->_bDropAnchor = FALSE; } else { // We come here if Drop is called without DragMove (with DragEnter).
*pdwEffect = 0; }
DragLeave(); // DoDragDrop does not call DragLeave() after Drop()
return S_OK; }
BOOL CDefView::_IsBkDropTarget(IDropTarget *pdtg) { BOOL fRet = FALSE;
if (_bContextMenuMode) { if (ListView_GetSelectedCount(_hwndListview) == 0) { fRet = TRUE; } }
POINT pt; if (!fRet) { if (_GetInsertPoint(&pt)) // If there is an insert point, then the background is the drop target.
return TRUE;
if (_GetDropPoint(&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(_hwndListview, &pt); if (_HitTest(&pt) == -1) { fRet = TRUE; } } } return fRet; }
// IShellFolderView::Rearrange
STDMETHODIMP CDefView::Rearrange(LPARAM lParamSort) { return _OnRearrange(lParamSort, TRUE); }
// end user initiated arrange (click on col header, etc)
HRESULT CDefView::_OnRearrange(LPARAM lParamSort, BOOL fAllowToggle) { DECLAREWAITCURSOR;
_vs._iLastColumnClick = (int) _vs._lParamSort; _vs._lParamSort = lParamSort;
// toggle the direction of the sort if on the same column
if (fAllowToggle && !_IsPositionedView() && _vs._iLastColumnClick == (int) lParamSort) _vs._iDirection = -_vs._iDirection; else _vs._iDirection = 1;
SetWaitCursor();
HRESULT hr = _Sort();
// reset to the state that no items have been moved if currently in a positioned mode
// so auto-arraning works.
if (_IsPositionedView()) { _ClearItemPositions(); }
ResetWaitCursor();
return hr; }
STDMETHODIMP CDefView::ArrangeGrid() { _OnCommand(NULL, GET_WM_COMMAND_MPS(SFVIDM_ARRANGE_GRID, 0, 0)); return S_OK; }
STDMETHODIMP CDefView::AutoArrange() { _OnCommand(NULL, GET_WM_COMMAND_MPS(SFVIDM_ARRANGE_AUTO, 0, 0)); return S_OK; }
STDMETHODIMP CDefView::GetArrangeParam(LPARAM *plParamSort) { *plParamSort = _vs._lParamSort; return S_OK; }
STDMETHODIMP CDefView::AddObject(LPITEMIDLIST pidl, UINT *puItem) { LPITEMIDLIST pidlCopy = ILClone(pidl);
if (pidlCopy) { *puItem = _AddObject(pidlCopy); // takes pidl ownership.
} else { *puItem = (UINT)-1; } // must cast to "int" because UINTs are never negative so we would
// otherwise never be able to detect failure
return (int)*puItem >= 0 ? S_OK : E_OUTOFMEMORY; }
STDMETHODIMP CDefView::GetObjectCount(UINT *puCount) { *puCount = ListView_GetItemCount(_hwndListview); return S_OK; }
STDMETHODIMP CDefView::SetObjectCount(UINT uCount, UINT dwFlags) { // Mask over to the flags that map directly accross
DWORD dw = dwFlags & SFVSOC_NOSCROLL; UINT uCountOld = 0;
GetObjectCount(&uCountOld);
if ((dwFlags & SFVSOC_INVALIDATE_ALL) == 0) dw |= LVSICF_NOINVALIDATEALL; // gross transform
HRESULT hr = (HRESULT)SendMessage(_hwndListview, LVM_SETITEMCOUNT, (WPARAM)uCount, (LPARAM)dw);
// Notify automation if we're going from 0 to 1 or more items
if (!uCountOld && uCount) { _PostNoItemStateChangedMessage(); }
return hr; }
STDMETHODIMP CDefView::GetObject(LPITEMIDLIST *ppidl, UINT uItem) { // Worse hack, if -42 then return our own pidl...
if (uItem == (UINT)-42) { *ppidl = (LPITEMIDLIST)_pidlMonitor; return *ppidl ? S_OK : E_UNEXPECTED; }
// Hack, if item is -2, this implies return the focused item
if (uItem == (UINT)-2) uItem = ListView_GetNextItem(_hwndListview, -1, LVNI_FOCUSED);
*ppidl = (LPITEMIDLIST)_GetPIDL(uItem); // cast due to bad interface def
return *ppidl ? S_OK : E_UNEXPECTED; }
STDMETHODIMP CDefView::RemoveObject(LPITEMIDLIST pidl, UINT *puItem) { *puItem = _RemoveObject(pidl, FALSE);
// must cast to "int" because UINTs are never negative so we would
// otherwise never be able to detect failure
return (int)*puItem >= 0 ? S_OK : E_INVALIDARG; }
STDMETHODIMP CDefView::UpdateObject(LPITEMIDLIST pidlOld, LPITEMIDLIST pidlNew, UINT *puItem) { *puItem = _UpdateObject(pidlOld, pidlNew); return (int)(*puItem) >= 0 ? S_OK : E_INVALIDARG; }
STDMETHODIMP CDefView::RefreshObject(LPITEMIDLIST pidl, UINT *puItem) { *puItem = _RefreshObject(&pidl); // must cast to "int" because UINTs are never negative so we would
// otherwise never be able to detect failure
return (int)*puItem >= 0 ? S_OK : E_INVALIDARG; }
STDMETHODIMP CDefView::SetRedraw(BOOL bRedraw) { SendMessage(_hwndListview, WM_SETREDRAW, (WPARAM)bRedraw, 0); return S_OK; }
STDMETHODIMP CDefView::GetSelectedObjects(LPCITEMIDLIST **pppidl, UINT *puItems) { return _GetItemObjects(pppidl, SVGIO_SELECTION, puItems); }
STDMETHODIMP CDefView::GetSelectedCount(UINT *puSelected) { *puSelected = ListView_GetSelectedCount(_hwndListview); return S_OK; }
BOOL CDefView::_IsDropOnSource(IDropTarget *pdtgt) { // context menu paste (_bMouseMenu shows context menu, cut stuff shows source)
if (_bMouseMenu && _bHaveCutStuff) { int iItem = ListView_GetNextItem(_hwndListview, -1, LVNI_SELECTED); if (iItem == -1) return TRUE; }
if (_itemCur != -1 || !_bDragSource) { // We did not drag onto the background of the source
return FALSE; }
return TRUE; }
STDMETHODIMP CDefView::IsDropOnSource(IDropTarget *pDropTarget) { return _IsDropOnSource(pDropTarget) ? S_OK : S_FALSE; }
STDMETHODIMP CDefView::MoveIcons(IDataObject *pdtobj) { return E_NOTIMPL; }
STDMETHODIMP CDefView::GetDropPoint(POINT *ppt) { return _GetDropPoint(ppt) ? S_OK : S_FALSE; }
STDMETHODIMP CDefView::GetDragPoint(POINT *ppt) { return _GetDragPoint(ppt) ? S_OK : S_FALSE; }
STDMETHODIMP CDefView::SetItemPos(LPCITEMIDLIST pidl, POINT *ppt) { SFV_SETITEMPOS sip; sip.pidl = pidl; sip.pt = *ppt;
_SetItemPos(&sip); return S_OK; }
STDMETHODIMP CDefView::IsBkDropTarget(IDropTarget *pDropTarget) { return _IsBkDropTarget(pDropTarget) ? S_OK : S_FALSE; }
STDMETHODIMP CDefView::SetClipboard(BOOL bMove) { _OnSetClipboard(bMove); // do this always, even if not current active view
return S_OK; }
// defcm.cpp asks us to setup the points of the currently selected objects
// into the data object on Copy/Cut commands
STDMETHODIMP CDefView::SetPoints(IDataObject *pdtobj) { LPCITEMIDLIST *apidl; UINT cItems; HRESULT hr = GetSelectedObjects(&apidl, &cItems); if (SUCCEEDED(hr) && cItems) { _SetPoints(cItems, apidl, pdtobj); LocalFree((HLOCAL)apidl); } return hr; }
STDMETHODIMP CDefView::GetItemSpacing(ITEMSPACING *pSpacing) { return _GetItemSpacing(pSpacing) ? S_OK : S_FALSE; }
STDMETHODIMP CDefView::SetCallback(IShellFolderViewCB* pNewCB, IShellFolderViewCB** ppOldCB) { *ppOldCB = NULL;
return _cCallback.SetCallback(pNewCB, ppOldCB); }
const UINT c_rgiSelectFlags[][2] = { { SFVS_SELECT_ALLITEMS, SFVIDM_SELECT_ALL }, { SFVS_SELECT_NONE, SFVIDM_DESELECT_ALL }, { SFVS_SELECT_INVERT, SFVIDM_SELECT_INVERT } };
STDMETHODIMP CDefView::Select(UINT dwFlags) { // translate the flag into the menu ID
for (int i = 0; i < ARRAYSIZE(c_rgiSelectFlags); i++) { if (c_rgiSelectFlags[i][0] == dwFlags) { return (HRESULT)_OnCommand(NULL, c_rgiSelectFlags[i][1], 0); } }
return E_INVALIDARG; }
STDMETHODIMP CDefView::QuerySupport(UINT * pdwSupport) { // *pdwSupport is an in/out param, we leave the out == in
return S_OK; // DefView supports all the operations...
}
STDMETHODIMP CDefView::SetAutomationObject(IDispatch *pdisp) { // release back pointers
IUnknown_SetOwner(_pauto, NULL); IUnknown_SetSite(_pauto, NULL);
IUnknown_Set((IUnknown **)&_pauto, pdisp); // hold or free _pauto
// this connects the automation object to our view, so it can implement
// stuff like "SelectedItems"
IUnknown_SetOwner(_pauto, SAFECAST(this, IShellFolderView *));
// use the browser as the site so OM related QueryService calls will find
// the browser above us as the place to do security checks instead of defivew
// this is stuff that depends on the zone of the caller as the security check
IUnknown_SetSite(_pauto, _psb);
return S_OK; }
STDMETHODIMP CDefView::SelectAndPositionItems(UINT cidl, LPCITEMIDLIST* apidl, POINT* apt, DWORD dwFlags) { for (UINT i = 0; i < cidl; i++) SelectAndPositionItem(apidl[i], dwFlags, apt ? &apt[i] : NULL);
return S_OK; }
// -------------- 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(si); si.fMask = (SIF_RANGE | SIF_PAGE | SIF_POS); 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; DWORD dwStyle = GetWindowLong(hwnd, GWL_STYLE);
#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) { TraceMsg(TF_DEFVIEW, "v=%d", v); pad->dwLastScroll = dwTimeNow; } } return dwDSD; // bits set if in scroll region
}
// warning: global data holding COM objects that may span apartment boundaries
// be very careful
HDSA g_hdsaDefViewCopyHook = NULL;
typedef struct { HWND hwndView; CDefView *pdv; } DVCOPYHOOK;
void CDefView::AddCopyHook() { ENTERCRITICAL; if (!g_hdsaDefViewCopyHook) { g_hdsaDefViewCopyHook = DSA_Create(sizeof(DVCOPYHOOK), 4); TraceMsg(TF_DEFVIEW, "AddCopyHook creating the dsa"); }
if (g_hdsaDefViewCopyHook) { DVCOPYHOOK dvch = { _hwndView, this }; ASSERT(dvch.hwndView); if (DSA_AppendItem(g_hdsaDefViewCopyHook, &dvch)!=-1) { AddRef(); TraceMsg(TF_DEFVIEW, "AddCopyHook successfully added (total=%d)", DSA_GetItemCount(g_hdsaDefViewCopyHook)); } } LEAVECRITICAL; }
int CDefView::FindCopyHook(BOOL fRemoveInvalid) { ASSERTCRITICAL;
if (g_hdsaDefViewCopyHook) { int item = DSA_GetItemCount(g_hdsaDefViewCopyHook); while (--item >= 0) { const DVCOPYHOOK *pdvch = (const DVCOPYHOOK *)DSA_GetItemPtr(g_hdsaDefViewCopyHook, item); if (pdvch) { if (fRemoveInvalid) { if (!IsWindow(pdvch->hwndView)) { TraceMsg(TF_WARNING, "FindCopyHook: found a invalid element, removing..."); DSA_DeleteItem(g_hdsaDefViewCopyHook, item); continue; } }
if ((pdvch->hwndView == _hwndView) && (pdvch->pdv == this)) { return item; } } else { ASSERT(0); } }
} return -1; // not found
}
void CDefView::RemoveCopyHook() { IShellView *psv = NULL; ENTERCRITICAL; if (g_hdsaDefViewCopyHook) { int item = FindCopyHook(TRUE); if (item != -1) { DVCOPYHOOK *pdvch = (DVCOPYHOOK *)DSA_GetItemPtr(g_hdsaDefViewCopyHook, item); psv = pdvch->pdv; TraceMsg(TF_DEFVIEW, "RemoveCopyHook removing an element"); DSA_DeleteItem(g_hdsaDefViewCopyHook, item);
//
// If this is the last guy, destroy it.
//
if (DSA_GetItemCount(g_hdsaDefViewCopyHook) == 0) { TraceMsg(TF_DEFVIEW, "RemoveCopyHook destroying hdsa (no element)"); DSA_Destroy(g_hdsaDefViewCopyHook); g_hdsaDefViewCopyHook = NULL; } } } LEAVECRITICAL;
//
// Release it outside the critical section.
//
ATOMICRELEASE(psv); }
STDAPI_(UINT) DefView_CopyHook(const COPYHOOKINFO *pchi) { UINT idRet = IDYES;
if (g_hdsaDefViewCopyHook==NULL) { return idRet; }
for (int 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->AddRef(); } LEAVECRITICAL;
if (dvch.pdv) { if (IsWindow(dvch.hwndView)) { HRESULT hr = dvch.pdv->CallCB(SFVM_NOTIFYCOPYHOOK, 0, (LPARAM)pchi);
ATOMICRELEASE(dvch.pdv); if (SUCCEEDED(hr) && (hr != S_OK)) { idRet = HRESULT_CODE(hr); ASSERT(idRet==IDYES || idRet==IDCANCEL || idRet==IDNO); break; } item++; } else { TraceMsg(TF_DEFVIEW, "DefView_CopyHook list has an invalid element"); ATOMICRELEASE(dvch.pdv); } } else { break; // no more item.
} }
return idRet; }
// IOleCommandTarget stuff - just forward to the webview
STDMETHODIMP CDefView::QueryStatus(const GUID *pguidCmdGroup, ULONG cCmds, OLECMD rgCmds[], OLECMDTEXT *pcmdtext) { HRESULT hr = OLECMDERR_E_UNKNOWNGROUP; BOOL fQSCalled = FALSE;
if (_cFrame.IsWebView()) { IOleCommandTarget* pct;
if (SUCCEEDED(_cFrame.GetCommandTarget(&pct))) { hr = pct->QueryStatus(pguidCmdGroup, cCmds, rgCmds, pcmdtext); fQSCalled = SUCCEEDED(hr); pct->Release(); } }
if (pguidCmdGroup == NULL) { if (rgCmds == NULL) return E_INVALIDARG;
for (UINT i = 0; i < cCmds; i++) { // ONLY say that we support the stuff we support in ::OnExec
switch (rgCmds[i].cmdID) { case OLECMDID_REFRESH: rgCmds[i].cmdf = OLECMDF_ENABLED; break;
default: // don't disable if the webview has already answered
if (!fQSCalled) { rgCmds[i].cmdf = 0; } break; } } } else if (IsEqualGUID(_clsid, *pguidCmdGroup)) { if (pcmdtext) { switch (pcmdtext->cmdtextf) { case OLECMDTEXTF_NAME: // It's a query for the button tooltip text.
ASSERT(cCmds == 1); _GetToolTipText(rgCmds[0].cmdID, pcmdtext->rgwz, pcmdtext->cwBuf);
// ensure NULL termination
pcmdtext->rgwz[pcmdtext->cwBuf - 1] = 0; pcmdtext->cwActual = lstrlenW(pcmdtext->rgwz);
hr = S_OK; break;
default: hr = E_FAIL; break; } } else { DWORD dwAttr = _AttributesFromSel(SFGAO_RELEVANT);
for (UINT i = 0; i < cCmds; i++) { if (_ShouldEnableToolbarButton(rgCmds[i].cmdID, dwAttr, -1)) rgCmds[i].cmdf = OLECMDF_ENABLED; else rgCmds[i].cmdf = 0; }
hr = S_OK; } }
return hr; } STDMETHODIMP CDefView::Exec(const GUID *pguidCmdGroup, DWORD nCmdID, DWORD nCmdexecopt, VARIANTARG *pvarargIn, VARIANTARG *pvarargOut) { HRESULT hr;
// Hold a ref to ourselves on Exec. In the camera name space if the view context menu is up when the camera
// is unplugged, explorer faults because the view is torn down and the context menu exec tries to unwind
// after defview is gone. This holds a ref on defview while in exec so defview doesn't dissappear.
//
AddRef(); hr = _Exec(pguidCmdGroup, nCmdID, nCmdexecopt, pvarargIn, pvarargOut); Release();
return hr; }
HRESULT CDefView::_Exec(const GUID *pguidCmdGroup, DWORD nCmdID, DWORD nCmdexecopt, VARIANTARG *pvarargIn, VARIANTARG *pvarargOut) { HRESULT hr = OLECMDERR_E_UNKNOWNGROUP;
if (pguidCmdGroup == NULL) { switch (nCmdID) { case OLECMDID_REFRESH: _fAllowSearchingWindow = TRUE; // this exec typically comes from a user action (F5, Refresh)
if (FAILED(_ReloadContent())) { //This invalidation deletes the WebView and also avoid
//unpainted areas in ListView areas whose paint messages
//are eaten by the visible WebView
InvalidateRect(_hwndView, NULL, TRUE); } hr = S_OK; break; } } else if (IsEqualGUID(CGID_DefView, *pguidCmdGroup)) { switch (nCmdID) { case DVID_SETASDEFAULT:
// 99/02/05 #226140 vtan: Exec command issued from
// CShellBrowser2::_SaveDefViewDefaultFolderSettings()
// when user clicks "Like Current Folder" in folder
// options "View" tab.
ASSERTMSG(nCmdexecopt == OLECMDEXECOPT_DODEFAULT, "nCmdexecopt must be OLECMDEXECOPT_DODEFAULT"); ASSERTMSG(pvarargIn == NULL, "pvarargIn must be NULL"); ASSERTMSG(pvarargOut == NULL, "pvarargOut must be NULL"); hr = _SaveGlobalViewState(); break; case DVID_RESETDEFAULT:
// 99/02/05 #226140 vtan: Exec command issued from
// CShellBrowser2::_ResetDefViewDefaultFolderSettings()
// when user clicks "Reset All Folders" in folder
// options "View" tab.
ASSERTMSG(nCmdexecopt == OLECMDEXECOPT_DODEFAULT, "nCmdexecopt must be OLECMDEXECOPT_DODEFAULT"); ASSERTMSG(pvarargIn == NULL, "pvarargIn must be NULL"); ASSERTMSG(pvarargOut == NULL, "pvarargOut must be NULL"); hr = _ResetGlobalViewState(); break; default: break; } } else if (IsEqualGUID(CGID_ShellDocView, *pguidCmdGroup)) { switch (nCmdID) { case SHDVID_CANACTIVATENOW: return _fCanActivateNow ? S_OK : S_FALSE;
// NOTE: for a long time IOleCommandTarget was implemented
// BUT it wasn't in the QI! At this late stage of the game
// I'll be paranoid and not forward everything down to the
// webview. We'll just pick off CANACTIVATENOW...
//
default: return OLECMDERR_E_UNKNOWNGROUP; } } else if (IsEqualGUID(CGID_Explorer, *pguidCmdGroup)) { switch (nCmdID) { case SBCMDID_GETPANE: V_I4(pvarargOut) = PANE_NONE; CallCB(SFVM_GETPANE, nCmdexecopt, (LPARAM)&V_I4(pvarargOut)); return S_OK;
case SBCMDID_MIXEDZONE: if (pvarargOut) return _cFrame._GetCurrentZone(NULL, pvarargOut); break;
default: break; } } else if (IsEqualGUID(IID_IExplorerToolbar, *pguidCmdGroup)) { // handle the ones coming FROM itbar:
switch (nCmdID) { case ETCMDID_GETBUTTONS: pvarargOut->vt = VT_BYREF; pvarargOut->byref = (void *)_pbtn; *pvarargIn->plVal = _cTotalButtons; return S_OK;
case ETCMDID_RELOADBUTTONS: MergeToolBar(TRUE); return S_OK; } } else if (IsEqualGUID(_clsid, *pguidCmdGroup)) { UEMFireEvent(&UEMIID_BROWSER, UEME_UITOOLBAR, UEMF_XEVENT, UIG_OTHER, nCmdID);
DFVCMDDATA cd; cd.pva = pvarargIn; cd.hwnd = _hwndMain; cd.nCmdIDTranslated = 0; _OnCommand(NULL, nCmdID, (LPARAM)&cd); }
// no need to pass OLECMDID_REFRESH on to the webview, as we
// just nuked and replaced the webview above -- a super refresh of sorts.
if (_cFrame.IsWebView() && hr != S_OK) { // Do not pass IDM_PARSECOMPLETE back to MSHTML. This will cause them to load mshtmled.dll
// unecessarily for webview which is a significant performance hit.
if (!(pguidCmdGroup && IsEqualGUID(CGID_MSHTML, *pguidCmdGroup) && (nCmdID == IDM_PARSECOMPLETE))) { IOleCommandTarget* pct; if (SUCCEEDED(_cFrame.GetCommandTarget(&pct))) { hr = pct->Exec(pguidCmdGroup, nCmdID, nCmdexecopt, pvarargIn, pvarargOut); pct->Release(); } } }
return hr; }
void CDefView::_ShowAndActivate() { // Can't call SetFocus because it rips focus away from such nice
// UI elements like the TREE pane...
// UIActivate will steal focus only if _uState is SVUIA_ACTIVATE_FOCUS
UIActivate(_uState); }
// IDefViewFrame (available only through QueryService from sfvext!)
//
HRESULT CDefView::GetShellFolder(IShellFolder **ppsf) { *ppsf = _pshf; if (*ppsf) _pshf->AddRef();
return *ppsf ? S_OK : E_FAIL; }
// IDefViewFrame3
//
HRESULT CDefView::GetWindowLV(HWND * phwnd) { if (!_IsDesktop()) { if (!_fGetWindowLV) { _fGetWindowLV = TRUE; // Caller will call ShowHideListView for us
*phwnd = _hwndListview; } TraceMsg(TF_DEFVIEW, "GetWindowLV - TAKEN"); return S_OK; } else { *phwnd = NULL; return E_FAIL; } }
HRESULT CDefView::OnResizeListView() { _AutoAutoArrange(0); return S_OK; }
HRESULT CDefView::ReleaseWindowLV() { _fGetWindowLV = FALSE; WndSize(_hwndView); // Make sure we resize _hwndListview
ShowHideListView(); return S_OK; }
HRESULT CDefView::DoRename() { return HandleRename(NULL); }
// IServiceProvider
STDMETHODIMP CDefView::QueryService(REFGUID guidService, REFIID riid, void **ppv) { HRESULT hr = E_FAIL; *ppv = NULL;
if (guidService == SID_DefView) // private service ID
{ // DefViewOCs request this interface
if (riid != IID_IDefViewFrame || !_IsDesktop()) hr = QueryInterface(riid, ppv); } else if (guidService == SID_ShellTaskScheduler) { if (_pScheduler) { hr = _pScheduler->QueryInterface(riid, ppv); } } else if ((guidService == SID_SContextMenuSite) || (guidService == SID_SFolderView)) // documented service ID
{ hr = QueryInterface(riid, ppv); } else if (guidService == SID_ShellFolderViewCB) // access to the view callback object
{ IShellFolderViewCB * psfvcb = _cCallback.GetSFVCB(); if (psfvcb) hr = psfvcb->QueryInterface(riid, ppv); } else if (guidService == SID_WebViewObject) { if (_cFrame.IsWebView()) { if (_cFrame._pOleObj) { //
// We hit this codepath while navigating away (while saving history),
// so there should not be any pending _cFrame._pOleObjNew as this
// view is going to be destroyed.
//
ASSERTMSG(!_cFrame._pOleObjNew, "Ambiguous Oleobj while peristing trident history in webview"); hr = _cFrame._pOleObj->QueryInterface(riid, ppv); } else if (_cFrame._pOleObjNew) { //
// We hit this codepath if we are navigating to the view (while loading history),
// we have not yet called _cFrame._SwitchToNewOleObj(), so we'll use
// the pending oleobj as CDefViewPersistHistory::LoadHistory()
// expects to get the right IPersistHistory interface from it.
//
hr = _cFrame._pOleObjNew->QueryInterface(riid, ppv); } } } else if (guidService == SID_SProgressUI) { // return a new instance of the progress dialog to the caller
hr = CoCreateInstance(CLSID_ProgressDialog, NULL, CLSCTX_INPROC_SERVER, riid, ppv); } else if (_psb) { hr = IUnknown_QueryService(_psb, guidService, riid, ppv); // send up the to the browser
} else { hr = IUnknown_QueryService(_punkSite, guidService, riid, ppv); // or our site
} return hr; }
STDMETHODIMP CDefView::OnSetTitle(VARIANTARG *pvTitle) { return E_NOTIMPL; }
BOOL CDefView::_LoadCategory(GUID *pguidGroupID) { BOOL fRet = FALSE; LPITEMIDLIST pidl = _GetViewPidl(); if (pidl) { IPropertyBag *ppb; if (SUCCEEDED(IUnknown_QueryServicePropertyBag(_psb, SHGVSPB_FOLDER, IID_PPV_ARG(IPropertyBag, &ppb)))) { fRet = SUCCEEDED(SHPropertyBag_ReadGUID(ppb, TEXT("Categorize"), pguidGroupID)); ppb->Release(); } ILFree(pidl); } return fRet; }
void SHGetThumbnailSize(SIZE *psize) { psize->cx = psize->cy = 96;
DWORD dw = 0, cb = sizeof(dw); SHRegGetUSValue(TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer"), TEXT("ThumbnailSize"), NULL, &dw, &cb, FALSE, NULL, 0);
if (dw >= 32 && dw <= 256) // constrain to reason
{ psize->cx = psize->cy = (int)dw; } }
void SHGetThumbnailSizeForThumbsDB(SIZE *psize) { SHGetThumbnailSize(psize);
// Due to tnail.cpp restriction buffer sizes, we can only go to 120 (since that's all we've tested at)
if (psize->cx > 120) psize->cx = psize->cy = 120; }
void CDefView::_GetThumbnailSize(SIZE *psize) { if (-1 == _sizeThumbnail.cx) { SHGetThumbnailSize(&_sizeThumbnail); } *psize = _sizeThumbnail; }
#ifdef _X86_
//************
//
// More of the Hijaak Hack
//
// We return no attributes (specifically, Hijaak looks for
// SFGAO_FILESYSTEM) and Hijaak will say, "Whoa, I don't know
// how to patch this guy; I'll leave it alone."
STDAPI FakeHijaak_GetAttributesOf(void *_this, UINT cidl, LPCITEMIDLIST *apidl, ULONG *rgfInOut) { *rgfInOut = 0; // Move along, nothing to see here
return S_OK; }
const struct FakeHijaakFolderVtbl { FARPROC Dummy[9]; FARPROC GetAttributesOf; } c_FakeHijaakFolderVtbl = { { 0 }, (FARPROC)FakeHijaak_GetAttributesOf };
const LPVOID c_FakeHijaakFolder = (const LPVOID)&c_FakeHijaakFolderVtbl;
//
// End of the Hijaak Hack
//
//************
#endif // _X86_
CBackgroundDefviewInfo::CBackgroundDefviewInfo(LPCITEMIDLIST pidl, UINT uId) : _pidl(pidl), _uId(uId) {
} CBackgroundDefviewInfo::~CBackgroundDefviewInfo (void) { ILFree(const_cast<LPITEMIDLIST>(_pidl)); }
CBackgroundColInfo::CBackgroundColInfo(LPCITEMIDLIST pidl, UINT uId, UINT uiCol, STRRET& strRet) : CBackgroundDefviewInfo(pidl, uId), _uiCol(uiCol) { StrRetToBuf(&strRet, NULL, const_cast<TCHAR*>(_szText), ARRAYSIZE(_szText)); }
CBackgroundColInfo::~CBackgroundColInfo(void) { }
// Takes ownership of pidl, copies rguColumns.
CBackgroundTileInfo::CBackgroundTileInfo(LPCITEMIDLIST pidl, UINT uId, UINT rguColumns[], UINT cColumns) : CBackgroundDefviewInfo(pidl, uId), _cColumns(cColumns) { ASSERT(cColumns <= ARRAYSIZE(_rguColumns));
for (UINT i = 0; (i < cColumns) && (i < ARRAYSIZE(_rguColumns)); i++) _rguColumns[i] = rguColumns[i]; }
CBackgroundTileInfo::~CBackgroundTileInfo(void) { }
// Helper function that scales the given size by some percentage where the percentage
// is defined in the resources for the localizers to adjust as approp. Range is 0 to 30% larger
INT ScaleSizeBasedUponLocalization (INT iSize) { TCHAR szPercentageIncrease[3]; INT iReturnValue = iSize; INT iPercentageIncrease;
if (iSize > 0) { if (LoadString(HINST_THISDLL, IDS_SIZE_INCREASE_PERCENTAGE, szPercentageIncrease, ARRAYSIZE(szPercentageIncrease))) { iPercentageIncrease = StrToInt(szPercentageIncrease);
if (iPercentageIncrease > 0) { if (iPercentageIncrease > 30) { iPercentageIncrease = 30; }
iReturnValue += ((iPercentageIncrease * iSize) / 100); } } }
return iReturnValue; }
CBackgroundGroupInfo::CBackgroundGroupInfo (LPCITEMIDLIST pidl, UINT uId, DWORD dwGroupId): CBackgroundDefviewInfo(pidl, uId), _dwGroupId(dwGroupId) {
}
BOOL CBackgroundGroupInfo::VerifyGroupExists(HWND hwnd, ICategorizer* pcat) { if (!pcat) return FALSE;
if (!ListView_HasGroup(hwnd, _dwGroupId)) { CATEGORY_INFO ci; pcat->GetCategoryInfo(_dwGroupId, &ci);
LVINSERTGROUPSORTED igrp; igrp.pfnGroupCompare = GroupCompare; igrp.pvData = (void *)pcat; igrp.lvGroup.cbSize = sizeof(LVGROUP); igrp.lvGroup.mask = LVGF_HEADER | LVGF_GROUPID; igrp.lvGroup.pszHeader= ci.wszName; igrp.lvGroup.iGroupId = (int)_dwGroupId;
ListView_InsertGroupSorted(hwnd, &igrp); }
return TRUE; }
|