13936 lines
463 KiB
C++
13936 lines
463 KiB
C++
#include "shellprv.h"
|
|
extern "C" {
|
|
#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 "vdate.h"
|
|
};
|
|
#include "cnctnpt.h"
|
|
#include "mmhelper.h"
|
|
#include "ovrlaymn.h"
|
|
#include "sfvext.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>
|
|
|
|
// docfindx.cpp
|
|
UINT GetControlCharWidth(HWND hwnd);
|
|
|
|
// An old version of Web View had the notion of customizing the listview
|
|
// background image and icon text colors, but we're not using that any
|
|
// more. We still allow that to be changed via desktop.ini, but there's
|
|
// no global inherited version of this support. The code is under ifdef:
|
|
|
|
//#define CUSTOM_BACKGROUND
|
|
|
|
#define TF_FOCUS TF_WARNING
|
|
|
|
#define DEFAULT_NUMCHARS 20
|
|
|
|
void DisableActiveDesktop();
|
|
extern "C" HMENU CDesktop_GetActiveDesktopMenu(void);
|
|
|
|
STDAPI_(void) DPA_FreeIDArray(HDPA hdpa);
|
|
|
|
BOOL StringFromCustomViewData(SFVVIEWSDATA* pItem, LPTSTR pszString, UINT cb, UINT idString);
|
|
BOOL ColorFromCustomViewData(SFVVIEWSDATA* pItem, COLORREF* pcr, UINT idCR);
|
|
BOOL ShowInfoTip();
|
|
|
|
// define MAX_ICON_WAIT to be the most (in ms) we will ever wait for a icon to be extracted.
|
|
// BUGBUG we should read this from the registry
|
|
|
|
// define MIN_ICON_WAIT to be amount of time that has to go by
|
|
// before we start waiting again.
|
|
// BUGBUG we should read this from the registry
|
|
|
|
// define TF_ICON to be the trace flags you want all the icon
|
|
// related trace out in this file to use.
|
|
#define MAX_ICON_WAIT 500
|
|
#define MIN_ICON_WAIT 2500
|
|
|
|
#define TF_ICON TF_DEFVIEW
|
|
//#define TF_ICON TF_ALWAYS
|
|
|
|
#include <sfview.h>
|
|
#include "sfviewp.h"
|
|
#include "shellp.h"
|
|
|
|
// PRIORITIES for tasks added to the DefView background task scheduler
|
|
#define TASK_PRIORITY_BKGRND_FILL ITSAT_DEFAULT_PRIORITY
|
|
#define TASK_PRIORITY_GET_ICON ITSAT_DEFAULT_PRIORITY
|
|
|
|
#define DEFVIEW_THREAD_IDLE_TIMEOUT (1000 * 60 * 2)
|
|
|
|
BOOL DefView_IdleDoStuff(CDefView* pdsv, LPRUNNABLETASK pTask, REFTASKOWNERID rTID, DWORD_PTR lParam, DWORD dwPriority);
|
|
|
|
// Whenever we enter an "update pending" state, set this timer
|
|
// instead. Let child DefViewOCs have a window to get our
|
|
// listview without us invalidating it's state
|
|
#define DV_IDTIMER_UPDATEPENDING 2
|
|
#define UPDATEPENDINGTIME (5*1000)
|
|
|
|
#define DV_IDTIMER_NOTIFY_AUTOMATION_SELCHANGE 4
|
|
#define NOTIFY_AUTOMATION_SELCHANGE_TIMEOUT (GetDoubleClickTime())
|
|
|
|
// Uncomment following line to turn on timing of view enumeration
|
|
// #define TIMING 1
|
|
#ifdef TIMING
|
|
DWORD dwFinish, dwStart;
|
|
#endif
|
|
|
|
STDAPI SHGetIconFromPIDL(IShellFolder* psf, IShellIcon* psi, LPCITEMIDLIST pidl, UINT flags, int* piImage);
|
|
|
|
#ifndef SIF_ALL
|
|
#define SIF_ALL (SIF_RANGE | SIF_PAGE | SIF_POS)
|
|
#endif
|
|
|
|
#define ID_LISTVIEW 1
|
|
#define ID_STATIC 2
|
|
|
|
#define WM_DVI_FILLSTUFF (WM_USER + 0x100)
|
|
#define WM_DVI_ENDIDLE (WM_USER + 0x101)
|
|
#define WM_DVI_GETICON (WM_USER + 0x102)
|
|
|
|
#define INVALID_THREAD_ID ((DWORD)-1)
|
|
|
|
#define DV_CDB_IsCommonDialog(_pdsv) (_pdsv->_pcdb)
|
|
|
|
#define DV_CDB_OnDefaultCommand(_pdsv) \
|
|
(_pdsv->_pcdb ? _pdsv->_pcdb->OnDefaultCommand( \
|
|
_pdsv->_psvOuter ? _pdsv->_psvOuter : _pdsv) : E_NOTIMPL)
|
|
|
|
#define DV_CDB_OnStateChange(_pdsv, _code) \
|
|
(_pdsv->_pcdb ? _pdsv->_pcdb->OnStateChange( \
|
|
_pdsv->_psvOuter ? _pdsv->_psvOuter : _pdsv, _code) : E_NOTIMPL)
|
|
|
|
#define DV_CDB_IncludeObject(_pdsv, _pidl) \
|
|
(_pdsv->_pcdb ? _pdsv->_pcdb->IncludeObject( \
|
|
_pdsv->_psvOuter ? _pdsv->_psvOuter : _pdsv, _pidl) : S_OK)
|
|
|
|
extern BOOL g_fDraggingOverSource;
|
|
|
|
UINT g_msgMSWheel = 0;
|
|
|
|
extern "C" const TCHAR c_szDefViewClass[] = TEXT("SHELLDLL_DefView");
|
|
|
|
#define COMBINED_CX 244
|
|
#define COMBINED_CY 0
|
|
|
|
#define IsDefaultState(_dvHead) ((_dvHead).dvState.lParamSort == 0 && \
|
|
(_dvHead).dvState.iDirection == 1 && \
|
|
(_dvHead).dvState.iLastColumnClick == -1 && \
|
|
(_dvHead).ptScroll.x == 0 && (_dvHead).ptScroll.y == 0)
|
|
|
|
typedef struct
|
|
{
|
|
POINT pt;
|
|
ITEMIDLIST idl;
|
|
} DVITEM;
|
|
// The following are the standard colors supported on desktop
|
|
const COLORREF g_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
|
|
};
|
|
|
|
// Note that it returns NULL, if iItem is -1.
|
|
|
|
HRESULT DefView_ExplorerCommand(CDefView* pdsv, UINT idFCIDM);
|
|
void DefView_DismissEdit(CDefView* pdsv);
|
|
void DefView_OnInitMenu(CDefView* pdsv);
|
|
LRESULT DefView_OnMenuSelect(CDefView* pdsv, UINT id, UINT mf, HMENU hmenu);
|
|
void DV_GetMenuHelpText(CDefView* pdsv, UINT_PTR id, LPTSTR pszText, UINT cchText);
|
|
void DV_GetToolTipText(CDefView* pdsv, UINT_PTR id, LPTSTR pszText, UINT cchText);
|
|
void DV_DoDefaultStatusBar(CDefView* pdsv, BOOL fInitialize);
|
|
|
|
void DefView_MoveSelectedItems(CDefView* pdsv, int dx, int dy, BOOL fAll);
|
|
BOOL DefView_GetDropPoint(CDefView* pdv, POINT* ppt);
|
|
BOOL DefView_GetDragPoint(CDefView* pdv, POINT* ppt);
|
|
DWORD LVStyleFromView(CDefView* lpdv);
|
|
HRESULT DefView_GetItemObjects(CDefView* pdsv, LPCITEMIDLIST** ppidl, UINT uItem, UINT* pcItems);
|
|
|
|
HRESULT DV_AllocRestOfStream(IStream* pstm, void** ppData, UINT* puLen);
|
|
|
|
#define SHOWICONS 1
|
|
#ifdef SHOWICONS
|
|
#define DV_SHOWICONS(pdsv) (!(pdsv->_fCombinedView) || !(pdsv->_fs.fFlags & FWF_NOICONS))
|
|
#else
|
|
#define DV_SHOWICONS(pdsv) (TRUE)
|
|
#endif
|
|
|
|
// determine if color is light or dark
|
|
#define COLORISLIGHT(clr) ((5*GetGValue((clr)) + 2*GetRValue((clr)) + GetBValue((clr))) > 8*128)
|
|
|
|
#define MINVIEWWIDTH 170
|
|
#define MINVIEWHEIGHT 132
|
|
|
|
// REVIEW UNDONE - calculate these, don't guess!
|
|
#define PARENTGAPWIDTH (40 - 8) // 40 is win95, -8 is nGapWidth (below)
|
|
#define PARENTGAPHEIGHT (32 - 93) // 32 is win95, -93 is nGepHeight (below)
|
|
|
|
#define EXTVIEWEXTRAWIDTH 100
|
|
#define EXTVIEWEXTRAHEIGHT 110
|
|
|
|
void EnableCombinedView(CDefView* pdsv, BOOL fEnable);
|
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
void DefView_StartNotify(CDefView* pdsv, UINT code)
|
|
{
|
|
if (pdsv == NULL)
|
|
return;
|
|
|
|
TIMESTART(pdsv->_WMNotify);
|
|
|
|
switch (code) {
|
|
case LVN_ITEMCHANGING:
|
|
TIMESTART(pdsv->_LVChanging);
|
|
break;
|
|
case LVN_ITEMCHANGED:
|
|
TIMESTART(pdsv->_LVChanged);
|
|
break;
|
|
case LVN_DELETEITEM:
|
|
TIMESTART(pdsv->_LVDelete);
|
|
break;
|
|
case LVN_GETDISPINFO:
|
|
TIMESTART(pdsv->_LVGetDispInfo);
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
void DefView_StopNotify(CDefView* pdsv, UINT code)
|
|
{
|
|
if (pdsv == NULL)
|
|
return;
|
|
|
|
TIMESTOP(pdsv->_WMNotify);
|
|
|
|
switch (code) {
|
|
case LVN_ITEMCHANGING:
|
|
TIMESTOP(pdsv->_LVChanging);
|
|
break;
|
|
case LVN_ITEMCHANGED:
|
|
TIMESTOP(pdsv->_LVChanged);
|
|
break;
|
|
case LVN_DELETEITEM:
|
|
TIMESTOP(pdsv->_LVDelete);
|
|
break;
|
|
case LVN_GETDISPINFO:
|
|
TIMESTOP(pdsv->_LVGetDispInfo);
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
HRESULT DV_Next(CDefView* pdsv, IEnumIDList* peunk, int cnt, LPITEMIDLIST* ppidl, ULONG* pcelt)
|
|
{
|
|
HRESULT hres;
|
|
|
|
TIMESTART(pdsv->_EnumNext);
|
|
hres = peunk->Next(cnt, ppidl, pcelt);
|
|
TIMESTOP(pdsv->_EnumNext);
|
|
return hres;
|
|
}
|
|
|
|
|
|
TCHAR* DV_Name(CDefView* pdsv)
|
|
{
|
|
static TCHAR ach[128];
|
|
if (pdsv->_hwndMain) {
|
|
GetWindowText(pdsv->_hwndMain, ach, ARRAYSIZE(ach));
|
|
} else {
|
|
lstrcpy(ach, TEXT("<NULL _hwndMain>"));
|
|
}
|
|
|
|
return ach;
|
|
}
|
|
|
|
#else
|
|
#define DefView_StartNotify(pdsv, code)
|
|
#define DefView_StopNotify(pdsv, code)
|
|
#define DV_Next(pdsv,peunk,cnt,ppidl,pcelt) peunk->Next(cnt, ppidl, pcelt)
|
|
#define DV_Name(pdsv) NULL
|
|
#endif
|
|
|
|
|
|
HRESULT CDefView::CallCB(UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
return m_cCallback.CallCB(uMsg, wParam, lParam);
|
|
}
|
|
|
|
|
|
void CDefView::RegisterSFVEvents(IUnknown* pTarget, BOOL fConnect)
|
|
{
|
|
ConnectToConnectionPoint(SAFECAST(this, IShellView2*), DIID_DShellFolderViewEvents, fConnect, pTarget, &m_dwConnectionCookie, NULL);
|
|
}
|
|
|
|
|
|
// There is almost always an automation client. Usually the Address Bar
|
|
// and Explorer Band are watching us. Sometimes MSHTML is watching, too.
|
|
|
|
HRESULT CDefView::NotifyAutomation(DISPID dispid)
|
|
{
|
|
return IUnknown_CPContainerInvokeParam(m_pauto, DIID_DShellFolderViewEvents, dispid, NULL, 0);
|
|
}
|
|
|
|
|
|
// CDefView::CheckIfSelectedAndNotifyAutomation
|
|
|
|
// This happens rarely, so it doesn't matter that we go through a lot of work, only to find that nobody is listening.
|
|
void CDefView::CheckIfSelectedAndNotifyAutomation(LPCITEMIDLIST pidl, int iItem)
|
|
{
|
|
// Don't bother notifying if there is a pending selection change notify as this will
|
|
// also update the user...
|
|
if (_fSelectionChangePending)
|
|
return;
|
|
|
|
if (m_pauto) {
|
|
// See if we need to get the Index number for the item
|
|
if (iItem < 0)
|
|
iItem = _FindItem(pidl, NULL, FALSE);
|
|
if (iItem >= 0) {
|
|
// Ok we have an index see if the item is selected or not...
|
|
if (ListView_GetItemState(_hwndListview, iItem, LVIS_SELECTED) & LVIS_SELECTED) {
|
|
// Ok we will tell them about it, for now don't pass the pidl...
|
|
// Trying to keep perf OK, don't do invoke directly but instead post the
|
|
// same message we do for selection changed.
|
|
_PostSelChangedMessage();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
BOOL DV_IsDropOnSource(CDefView* pdsv, IDropTarget* pdtgt)
|
|
{
|
|
// context menu paste (_bMouseMenu shows context menu, cut stuff shows source)
|
|
if (pdsv->_bMouseMenu && pdsv->_bHaveCutStuff) {
|
|
int iItem = ListView_GetNextItem(pdsv->_hwndListview, -1, LVNI_SELECTED);
|
|
if (iItem == -1) {
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
// If pdtgt is specified, it should match.
|
|
if (pdtgt && (pdtgt != pdsv->_pdtgtBack)) {
|
|
return FALSE;
|
|
}
|
|
|
|
if (pdsv->_itemCur != -1 || !pdsv->_bDragSource) {
|
|
// We did not drag onto the background of the source
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
void CDefView::_SameViewMoveIcons()
|
|
{
|
|
POINT ptDrop = _ptDrop;
|
|
|
|
ASSERT(DV_ISANYICONMODE(_fs.ViewMode));
|
|
|
|
LVUtil_ClientToLV(_hwndListview, &ptDrop);
|
|
|
|
DefView_MoveSelectedItems(this, ptDrop.x - _ptDragAnchor.x, ptDrop.y - _ptDragAnchor.y, FALSE);
|
|
}
|
|
|
|
|
|
#ifdef WINNT
|
|
BOOL _DoesRegkeyExist(HKEY hkRoot, LPCTSTR pszSubkey)
|
|
{
|
|
LONG l = 0;
|
|
return RegQueryValue(hkRoot, pszSubkey, NULL, &l) == ERROR_SUCCESS;
|
|
}
|
|
#endif
|
|
|
|
|
|
// 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 GetColorsFromHTMLdoc(CDefView* pdsv, COLORREF* clrTextBk, COLORREF* clrHotlight)
|
|
{
|
|
int i;
|
|
|
|
// If the HTML document hasn't reached ready-state interactive, then
|
|
// the background color is NOT yet set!
|
|
if (pdsv->m_cFrame.m_bgColor == CLR_INVALID)
|
|
return FALSE;
|
|
|
|
// 98/11/19 #250276 vtan: Use the previously private but now
|
|
// public function _SetDesktopListViewIconTextColors to
|
|
// update the public member variable m_bgColor from Trident.
|
|
(HRESULT)pdsv->m_cFrame._SetDesktopListViewIconTextColors(FALSE); // no update required - prevents recursion
|
|
|
|
*clrTextBk = pdsv->m_cFrame.m_bgColor;
|
|
|
|
// Check if the given background color is a standard color. If not, use
|
|
// the system background color itself.
|
|
BOOL fStandardColor = FALSE;
|
|
for (i = 0; i < ARRAYSIZE(g_VgaColorTable); i++) {
|
|
if (g_VgaColorTable[i] == *clrTextBk) {
|
|
fStandardColor = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!fStandardColor)
|
|
*clrTextBk = GetSysColor(COLOR_BACKGROUND);
|
|
|
|
if (COLORISLIGHT(*clrTextBk))
|
|
*clrHotlight = 0x000000; //Black as hightlight color!
|
|
else
|
|
*clrHotlight = 0xFFFFFF; //White as highlight color!
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
|
|
// Set the colors for the folder - taking care if it's the desktop.
|
|
void DSV_SetFolderColors(CDefView* pdsv)
|
|
{
|
|
COLORREF clrText, clrTextBk, clrWindow;
|
|
|
|
// Is this view for the desktop?
|
|
if (pdsv->_IsDesktop()) {
|
|
TCHAR szWallpaper[128];
|
|
TCHAR szPattern[128];
|
|
HKEY hkey;
|
|
UINT cb;
|
|
DWORD dwPaintVersion;
|
|
COLORREF clrHotlight;
|
|
|
|
Shell_SysColorChange();
|
|
|
|
//If we show HTML wallpaper, then get the appropriate colors too!
|
|
if ((pdsv->_fCombinedView) && GetColorsFromHTMLdoc(pdsv, &clrTextBk, &clrHotlight)) {
|
|
//Set the Hotlight color!
|
|
ListView_SetHotlightColor(pdsv->_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(pdsv->_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.
|
|
|
|
// BUGBUG we assume the string for none starts with a '('
|
|
// BUGBUG we dont know if a random app has subclassed
|
|
// BUGBUG ..the desktop, we should check this case too.
|
|
|
|
szWallpaper[0] = 0;
|
|
szPattern[0] = 0;
|
|
dwPaintVersion = 0;
|
|
|
|
if (RegOpenKey(HKEY_CURRENT_USER, REGSTR_PATH_DESKTOP, &hkey) == 0) {
|
|
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);
|
|
RegCloseKey(hkey);
|
|
|
|
#ifdef WINNT
|
|
// 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_WIN2000)) {
|
|
#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) ||
|
|
_DoesRegkeyExist(HKEY_LOCAL_MACHINE, REGSTR_PATH_LM_ROOTCERTIFICATES TEXT("\\") REGSTR_KEY_TESTCERTIFICATE) ||
|
|
_DoesRegkeyExist(HKEY_LOCAL_MACHINE, REGSTR_PATH_GPO_ROOTCERTIFICATES TEXT("\\") REGSTR_KEY_TESTCERTIFICATE);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
#ifndef CRAZY_CODE
|
|
if (pdsv->_fCombinedView ||
|
|
#else
|
|
if (
|
|
#endif
|
|
(GetSystemMetrics(SM_CLEANBOOT) == 0 && GetSystemMetrics(SM_DEBUG) == 0 && !dwPaintVersion && (!pdsv->_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 ((pdsv->_fs.fFlags & FWF_TRANSPARENT) && !pdsv->_fCombinedView) {
|
|
clrWindow = CLR_NONE;
|
|
clrTextBk = CLR_NONE;
|
|
}
|
|
}
|
|
|
|
if (!pdsv->_fClassic && ISVALIDCOLOR(pdsv->_crCustomColors[CRID_CUSTOMTEXTBACKGROUND]))
|
|
clrTextBk = pdsv->_crCustomColors[CRID_CUSTOMTEXTBACKGROUND];
|
|
|
|
if (!pdsv->_fClassic && ISVALIDCOLOR(pdsv->_crCustomColors[CRID_CUSTOMTEXT]))
|
|
clrText = pdsv->_crCustomColors[CRID_CUSTOMTEXT];
|
|
|
|
// if its a thumbvw, do the thumbvw thing, always do standard listvw thing.
|
|
if (pdsv->m_cFrame.IsSFVExtension()) {
|
|
IShellFolderView* pSFV = pdsv->m_cFrame.GetExtendedISFV();
|
|
|
|
if (pSFV) {
|
|
HRESULT hres;
|
|
IDefViewExtInit2* pShellView = NULL;
|
|
|
|
hres = pSFV->QueryInterface(IID_IDefViewExtInit2, (void**)&pShellView);
|
|
if (SUCCEEDED(hres)) {
|
|
pShellView->SetViewWindowColors(clrText, clrTextBk, clrWindow);
|
|
ATOMICRELEASE(pShellView);
|
|
}
|
|
}
|
|
|
|
}
|
|
ListView_SetBkColor(pdsv->_hwndListview, clrWindow);
|
|
ListView_SetTextBkColor(pdsv->_hwndListview, clrTextBk);
|
|
ListView_SetTextColor(pdsv->_hwndListview, clrText);
|
|
}
|
|
|
|
|
|
DWORD LVStyleFromView(CDefView* pdsv)
|
|
{
|
|
DWORD dwStyle;
|
|
|
|
if (pdsv->_IsDesktop()) {
|
|
dwStyle = LVS_ICON | LVS_NOSCROLL | LVS_ALIGNLEFT;
|
|
} else {
|
|
switch (pdsv->_fs.ViewMode) {
|
|
case FVM_LIST:
|
|
dwStyle = LVS_LIST;
|
|
break;
|
|
case FVM_DETAILS:
|
|
dwStyle = LVS_REPORT;
|
|
break;
|
|
case FVM_SMALLICON:
|
|
dwStyle = LVS_SMALLICON;
|
|
break;
|
|
default:
|
|
TraceMsg(TF_WARNING, "Unknown ViewMode value");
|
|
|
|
// fall through...
|
|
case FVM_ICON:
|
|
dwStyle = LVS_ICON;
|
|
break;
|
|
}
|
|
dwStyle |= LVS_SHOWSELALWAYS; // make sure selection is visible
|
|
}
|
|
|
|
if (pdsv->_fs.fFlags & FWF_AUTOARRANGE)
|
|
dwStyle |= LVS_AUTOARRANGE;
|
|
|
|
if (pdsv->_fs.fFlags & FWF_SINGLESEL)
|
|
dwStyle |= LVS_SINGLESEL;
|
|
|
|
if (pdsv->_fs.fFlags & FWF_ALIGNLEFT)
|
|
dwStyle |= LVS_ALIGNLEFT;
|
|
|
|
if (pdsv->_fs.fFlags & FWF_NOSCROLL)
|
|
dwStyle |= LVS_NOSCROLL;
|
|
|
|
return dwStyle;
|
|
}
|
|
|
|
|
|
void CDefView::_GetSortDefaults(DVSAVESTATE* pSaveState)
|
|
{
|
|
SHELLSTATE ss;
|
|
SHGetSetSettings(&ss, SSF_SORTCOLUMNS, FALSE);
|
|
pSaveState->lParamSort = ss.lParamSort;
|
|
pSaveState->iDirection = ss.iSortDirection ? ss.iSortDirection : 1;
|
|
pSaveState->iLastColumnClick = -1;
|
|
CallCB(SFVM_GETSORTDEFAULTS, (LPARAM) & (pSaveState->iDirection), (WPARAM) & (pSaveState->lParamSort));
|
|
|
|
TraceMsg(TF_DEFVIEW, "_GetSortDefaults returning lParamSort==%d, iDir==%d", pSaveState->lParamSort, pSaveState->iDirection);
|
|
}
|
|
|
|
|
|
HRESULT CDefView::_GetDetailsHelper(int i, DETAILSINFO* pdi)
|
|
{
|
|
HRESULT hres = E_NOTIMPL;
|
|
|
|
if (_pshf2) {
|
|
hres = _pshf2->GetDetailsOf(pdi->pidl, i, (SHELLDETAILS*)&pdi->fmt);
|
|
}
|
|
|
|
if (FAILED(hres)) // Don't make NSEs impl all of IShellFolder2
|
|
{
|
|
if (_psd) {
|
|
// HACK: pdi->fmt is the same layout as SHELLDETAILS
|
|
hres = _psd->GetDetailsOf(pdi->pidl, i, (SHELLDETAILS*)&pdi->fmt);
|
|
} else if (HasCB()) {
|
|
hres = CallCB(SFVM_GETDETAILSOF, i, (LPARAM)pdi);
|
|
} else {
|
|
TraceMsg(TF_ERROR, "cdv._gdh: neither ISD or callback found");
|
|
}
|
|
}
|
|
|
|
return hres;
|
|
}
|
|
|
|
|
|
/*
|
|
Purpose: Determine if the given defview state struct has valid
|
|
state info. If is doesn't, this function massages the values so it does.
|
|
*/
|
|
void ValidateDVState(IN OUT PDVSAVESTATE pdvState)
|
|
{
|
|
ASSERT(IS_VALID_WRITE_PTR(pdvState, DVSAVESTATE));
|
|
|
|
if (0 == pdvState->iDirection)
|
|
pdvState->iDirection = 1;
|
|
}
|
|
|
|
|
|
#define CCOLSHEADER_SIGNATURE 0xfddfdffd
|
|
#define CCOLSHEADER_VERSION_IE4 0x0C
|
|
#define CCOLSHEADER_VERSION_IE5 0x0E
|
|
|
|
|
|
// Use this for actualy dumping to the stream
|
|
typedef struct
|
|
{
|
|
DWORD dwSignature;
|
|
USHORT uVersion; // 0x0c == IE4, 0x0e == IE5
|
|
USHORT uCols;
|
|
USHORT uOffsetWidths;
|
|
USHORT uOffsetColOrder;
|
|
USHORT uOffsetColStates;
|
|
} COLSFILEHEADER, * LPCOLSFILEHEADER;
|
|
|
|
|
|
class CColumnPointer
|
|
{
|
|
public:
|
|
// Note: we assume that pdsv is used _only_ for calling IVC. If this changes, fix callers
|
|
CColumnPointer(BYTE* pCols, CDefView* pdsv)
|
|
{
|
|
LPCOLSFILEHEADER lpcfh = (LPCOLSFILEHEADER)pCols;
|
|
DWORD* pColList = NULL;
|
|
int u;
|
|
|
|
// set these up, as they shouldn't change
|
|
m_cfh.dwSignature = CCOLSHEADER_SIGNATURE;
|
|
m_cfh.uVersion = CCOLSHEADER_VERSION_IE5;
|
|
|
|
m_dsaWidths = DSA_Create(sizeof(USHORT), 5); // Standard defview has 5 columns
|
|
m_dsaColOrder = DSA_Create(sizeof(INT), 5);
|
|
|
|
// Here we need to loop through pCols (which points to a ColsFileHeader,
|
|
// followed by the two arrays), and set up the DPAs, and our m_cfh
|
|
|
|
// if we think there are zero columns, then bail...
|
|
if (lpcfh && lpcfh->dwSignature == CCOLSHEADER_SIGNATURE && lpcfh->uCols) {
|
|
|
|
for (u = 0; u < lpcfh->uCols; u++) {
|
|
TraceMsg(TF_DEFVIEW, "CColumns: %d initialized to %d wide", u, *((USHORT*)(pCols + lpcfh->uOffsetWidths + u * sizeof(USHORT))));
|
|
DSA_AppendItem(m_dsaColOrder, (pCols + lpcfh->uOffsetColOrder + u * sizeof(INT)));
|
|
DSA_AppendItem(m_dsaWidths, (pCols + lpcfh->uOffsetWidths + u * sizeof(USHORT)));
|
|
}
|
|
|
|
if (lpcfh->uVersion >= CCOLSHEADER_VERSION_IE5) {
|
|
// the col list is at the very end
|
|
pColList = (DWORD*)(pCols + lpcfh->uOffsetColStates);
|
|
TraceMsg(TF_DEFVIEW, "Initializing with column info, pCols=%x");
|
|
}
|
|
} else {
|
|
if (pCols != NULL)
|
|
TraceMsg(TF_ERROR, "CColumnPointer Not able to set column info (w95 upgrade???) (pCols = 0x%X)", pCols);
|
|
else
|
|
TraceMsg(TF_DEFVIEW, "CColumnPointer ctor: not passed any data");
|
|
}
|
|
if (pdsv)
|
|
pdsv->InitializeVariableColumns(pColList);
|
|
// Set any defaults
|
|
}
|
|
|
|
~CColumnPointer()
|
|
{
|
|
if (m_dsaWidths)
|
|
DSA_Destroy(m_dsaWidths);
|
|
if (m_dsaColOrder)
|
|
DSA_Destroy(m_dsaColOrder);
|
|
}
|
|
|
|
// uOrder should normally be uCol
|
|
BOOL AppendColumn(UINT uCol, USHORT uWidth, INT uOrder)
|
|
{
|
|
UINT* p;
|
|
// Slide every index above this one up
|
|
for (INT u = 0; u < DSA_GetItemCount(m_dsaColOrder); u++) {
|
|
p = (UINT*)DSA_GetItemPtr(m_dsaColOrder, u);
|
|
if (!p)
|
|
break; // safety...
|
|
if (*p >= uCol)
|
|
(*p)++;
|
|
}
|
|
|
|
DSA_AppendItem(m_dsaWidths, &uWidth);
|
|
DSA_AppendItem(m_dsaColOrder, &uOrder);
|
|
// maybe we should store column ordering as absolute numbers
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL RemoveColumn(UINT uCol)
|
|
{
|
|
UINT* p;
|
|
|
|
if ((int)uCol >= DSA_GetItemCount(m_dsaWidths))
|
|
return FALSE;
|
|
// Slide every index above this one down
|
|
for (INT u = 0; u < DSA_GetItemCount(m_dsaColOrder); u++) {
|
|
p = (UINT*)DSA_GetItemPtr(m_dsaColOrder, u);
|
|
if (!p)
|
|
break; // safety...
|
|
if (*p > uCol)
|
|
(*p)--;
|
|
}
|
|
|
|
DSA_DeleteItem(m_dsaWidths, uCol);
|
|
DSA_DeleteItem(m_dsaColOrder, uCol);
|
|
return TRUE;
|
|
}
|
|
|
|
UINT RestoreColumnWidth(UINT uCol, UINT uDefWid)
|
|
{
|
|
USHORT uWidth = 0;
|
|
if (uCol < (UINT)DSA_GetItemCount(m_dsaWidths)) {
|
|
DSA_GetItem(m_dsaWidths, uCol, &uWidth);
|
|
}
|
|
return(uWidth ? uWidth : uDefWid); // disallow zero width columns
|
|
}
|
|
|
|
// return true if default widths
|
|
BOOL SaveColumnWidths(HWND hwndList)
|
|
{
|
|
UINT cCols;
|
|
USHORT us;
|
|
LV_COLUMN lvc;
|
|
BOOL bOk = TRUE;
|
|
HDSA dsaNewWidths;
|
|
HWND hwndHead = ListView_GetHeader(hwndList);
|
|
|
|
if (!hwndHead || !(cCols = Header_GetItemCount(hwndHead)))
|
|
return TRUE;
|
|
|
|
dsaNewWidths = DSA_Create(sizeof(USHORT), cCols);
|
|
if (!dsaNewWidths)
|
|
return TRUE;
|
|
|
|
TraceMsg(TF_DEFVIEW, "SaveColumnWidths storing %d columns", cCols);
|
|
for (UINT u = 0; u < cCols && bOk; ++u) {
|
|
lvc.mask = LVCF_WIDTH;
|
|
bOk = ListView_GetColumn(hwndList, u, &lvc);
|
|
us = (USHORT)lvc.cx; // make sure its a short
|
|
DSA_AppendItem(dsaNewWidths, &us);
|
|
// TraceMsg(TF_DEFVIEW, " saving col %d width of %d", u, us);
|
|
}
|
|
if (bOk) {
|
|
if (m_dsaWidths)
|
|
DSA_Destroy(m_dsaWidths);
|
|
m_dsaWidths = dsaNewWidths;
|
|
} else
|
|
DSA_Destroy(dsaNewWidths);
|
|
return !bOk;
|
|
}
|
|
|
|
void RestoreColumnOrder(HWND hwndList)
|
|
{
|
|
UINT cCols;
|
|
UINT* pCols;
|
|
HWND hwndHead = ListView_GetHeader(hwndList);
|
|
|
|
if (!hwndHead)
|
|
return;
|
|
|
|
cCols = Header_GetItemCount(hwndHead);
|
|
|
|
if (cCols != (UINT)DSA_GetItemCount(m_dsaColOrder)) {
|
|
// this is a normal case if a folder is opened and there is no saved state. no need to spew.
|
|
if (DSA_GetItemCount(m_dsaColOrder))
|
|
TraceMsg(TF_DEFVIEW, "RestoreColumnOrder: cCols (%d) mismatched DPA count (%d), NOT RESTORING", cCols, DSA_GetItemCount(m_dsaColOrder));
|
|
return;
|
|
}
|
|
|
|
pCols = (LPUINT)LocalAlloc(LPTR, cCols * sizeof(UINT));
|
|
for (UINT u = 0; u < cCols; u++)
|
|
DSA_GetItem(m_dsaColOrder, u, pCols + u);
|
|
|
|
ListView_SetColumnOrderArray(hwndList, cCols, pCols);
|
|
LocalFree(pCols);
|
|
}
|
|
|
|
// return TRUE if default order
|
|
BOOL SaveColumnOrder(HWND hwndList)
|
|
{
|
|
BOOL bDefaultOrder = TRUE;
|
|
UINT cCols;
|
|
HWND hwndHead = ListView_GetHeader(hwndList);
|
|
|
|
if (!hwndHead) {
|
|
TraceMsg(TF_DEFVIEW, "returning prev col order, not reading from listview");
|
|
return TRUE;
|
|
}
|
|
|
|
cCols = Header_GetItemCount(hwndHead);
|
|
|
|
if (!cCols) {
|
|
TraceMsg(TF_DEFVIEW, "returning prev col order, not reading from listview");
|
|
return TRUE;
|
|
}
|
|
|
|
UINT* pCols = (LPUINT)LocalAlloc(LPTR, cCols * sizeof(UINT));
|
|
|
|
ListView_GetColumnOrderArray(hwndList, cCols, pCols);
|
|
|
|
DSA_DeleteAllItems(m_dsaColOrder);
|
|
for (UINT u = 0; u < cCols; ++u) {
|
|
DSA_AppendItem(m_dsaColOrder, &pCols[u]);
|
|
if (pCols[u] != u)
|
|
bDefaultOrder = FALSE;
|
|
}
|
|
|
|
LocalFree(pCols);
|
|
return(bDefaultOrder);
|
|
}
|
|
|
|
HRESULT Write(IStream* pstm, CDefView* pvc)
|
|
{
|
|
DWORD i;
|
|
|
|
if (!pstm) {
|
|
return (E_INVALIDARG);
|
|
}
|
|
|
|
ASSERT(DSA_GetItemCount(m_dsaColOrder) == DSA_GetItemCount(m_dsaWidths));
|
|
|
|
// dwSignature and cbSize were set above
|
|
m_cfh.uCols = (UINT)DSA_GetItemCount(m_dsaColOrder);
|
|
m_cfh.uOffsetColOrder = sizeof(COLSFILEHEADER);
|
|
m_cfh.uOffsetWidths = m_cfh.uOffsetColOrder + sizeof(UINT) * m_cfh.uCols;
|
|
m_cfh.uOffsetColStates = m_cfh.uOffsetWidths + sizeof(USHORT) * m_cfh.uCols;
|
|
|
|
// No point in persisting, if there aren't any columns around.
|
|
// this is true for folders that are just opened and closed
|
|
if (!m_cfh.uCols)
|
|
return E_FAIL;
|
|
|
|
// Note- dependent on DSA storing data internally as byte-packed.
|
|
pstm->Write(&m_cfh, sizeof(m_cfh), NULL);
|
|
pstm->Write(DSA_GetItemPtr(m_dsaColOrder, 0), sizeof(UINT) * DSA_GetItemCount(m_dsaColOrder), NULL);
|
|
pstm->Write(DSA_GetItemPtr(m_dsaWidths, 0), sizeof(USHORT) * DSA_GetItemCount(m_dsaWidths), NULL);
|
|
|
|
for (i = 0; i < pvc->GetMaxColumns(); i++) {
|
|
if (pvc->IsColumnOn(i))
|
|
pstm->Write(&i, sizeof(DWORD), NULL);
|
|
}
|
|
i = 0xFFFFFFFF;
|
|
pstm->Write(&i, sizeof(DWORD), NULL);
|
|
|
|
TraceMsg(TF_DEFVIEW, "CColumnPointer persisting %d columns", m_cfh.uCols);
|
|
return S_OK; // duhhh, pass down errors from stream writes...
|
|
}
|
|
|
|
private:
|
|
COLSFILEHEADER m_cfh; // old header
|
|
HDSA m_dsaWidths;
|
|
HDSA m_dsaColOrder;
|
|
};
|
|
|
|
|
|
void CDefView::AddColumns()
|
|
{
|
|
int i;
|
|
UINT iVisible;
|
|
LPBYTE pColHdr;
|
|
LV_COLUMN col;
|
|
UINT uLen;
|
|
|
|
// so we do this once
|
|
ASSERT(!_bLoadedColumns);
|
|
_bLoadedColumns = TRUE;
|
|
|
|
// I also use this as a flag for whether to free pColHdr
|
|
IStream* pstmCols = NULL;
|
|
|
|
// 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 && !this->HasCB()) {
|
|
goto Error1;
|
|
}
|
|
|
|
pColHdr = NULL;
|
|
if (SUCCEEDED(CallCB(SFVM_GETCOLSAVESTREAM, STGM_READ, (LPARAM)&pstmCols)) && pstmCols) {
|
|
uLen = 0;
|
|
|
|
HRESULT hres = DV_AllocRestOfStream(pstmCols, (void**)&pColHdr, &uLen);
|
|
|
|
ATOMICRELEASE(pstmCols);
|
|
|
|
if (FAILED(hres)) {
|
|
// Make sure we do not try to Free below
|
|
pstmCols = NULL;
|
|
uLen = 0;
|
|
}
|
|
} else if (_pSaveHeader) {
|
|
uLen = _pSaveHeader->GetColumnsInfo(&pColHdr);
|
|
} else {
|
|
uLen = 0;
|
|
}
|
|
|
|
TraceMsg(TF_DEFVIEW, "AddColumns: about to create CColumnPointer with uLen == %d, pColHdr = %x", uLen, pColHdr);
|
|
if (_pcp)
|
|
delete _pcp;
|
|
_pcp = new CColumnPointer(pColHdr, this);
|
|
|
|
for (i = 0; i < (int)GetMaxColumns(); ++i) {
|
|
if (this->IsColumnOn(i)) {
|
|
MapRealToVisibleColumn((UINT)i, &iVisible);
|
|
|
|
col.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;
|
|
col.fmt = m_pColItems[i].fmt;
|
|
col.cx = _pcp->RestoreColumnWidth(iVisible, m_pColItems[i].cChars * _cxChar); // Note iVisible
|
|
col.pszText = m_pColItems[i].szName;
|
|
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);
|
|
}
|
|
|
|
}
|
|
// i is now the number of columns
|
|
|
|
MapRealToVisibleColumn((UINT)i, &iVisible);
|
|
iVisible++; // don't want zero based -- should make GetVisibleColumnCount?
|
|
TraceMsg(TF_DEFVIEW, "AddColumns: settting CDefView's m_uCols = %d", iVisible);
|
|
m_uCols = iVisible;
|
|
|
|
// 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);
|
|
|
|
_pcp->RestoreColumnOrder(_hwndListview);
|
|
|
|
if (pstmCols) {
|
|
LocalFree((HLOCAL)pColHdr);
|
|
}
|
|
|
|
Error1:;
|
|
|
|
// use real numbers, not visible
|
|
if (_dvState.iLastColumnClick >= i) {
|
|
_GetSortDefaults(&_dvState);
|
|
|
|
if (_dvState.iLastColumnClick >= i || _dvState.lParamSort >= i) {
|
|
// our defaults won't work on this view....
|
|
// hard code these defaults
|
|
_dvState.lParamSort = 0;
|
|
_dvState.iDirection = 1;
|
|
_dvState.iLastColumnClick = -1;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void CDefView::InitSelectionMode()
|
|
{
|
|
SHELLSTATE ss;
|
|
|
|
SHGetSetSettings(&ss, SSF_DOUBLECLICKINWEBVIEW, FALSE);
|
|
|
|
_dwSelectionMode = 0;
|
|
|
|
if (!_fClassic || (_fs.fFlags & FWF_SINGLECLICKACTIVATE)) {
|
|
if (!ss.fDoubleClickInWebView)
|
|
_dwSelectionMode = LVS_EX_TRACKSELECT | LVS_EX_ONECLICKACTIVATE;
|
|
}
|
|
}
|
|
|
|
|
|
void CDefView::UpdateSelectionMode()
|
|
{
|
|
this->InitSelectionMode();
|
|
ListView_SetExtendedListViewStyleEx(_hwndListview, LVS_EX_TRACKSELECT | LVS_EX_ONECLICKACTIVATE | LVS_EX_TWOCLICKACTIVATE, _dwSelectionMode);
|
|
}
|
|
|
|
|
|
void UpdateUnderlines(CDefView* pdsv)
|
|
{
|
|
DWORD cb;
|
|
DWORD dwUnderline = ICON_IE;
|
|
DWORD dwExStyle;
|
|
|
|
// Read the icon underline settings.
|
|
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.
|
|
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;
|
|
}
|
|
|
|
// Set the new LVS_EX_UNDERLINE flags.
|
|
ListView_SetExtendedListViewStyleEx(pdsv->_hwndListview, LVS_EX_UNDERLINEHOT | LVS_EX_UNDERLINECOLD, dwExStyle);
|
|
}
|
|
|
|
|
|
LRESULT CDefView::_OnCreate(HWND hWnd)
|
|
{
|
|
DWORD dwStyle;
|
|
DWORD dwExStyle;
|
|
HIMAGELIST himlLarge, himlSmall;
|
|
ULONG rgfAttr;
|
|
|
|
SetWindowLongPtr(hWnd, 0, (LONG_PTR)this);
|
|
this->AddRef(); // hwnd -> this
|
|
_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
|
|
dwStyle = LVStyleFromView(this);
|
|
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;
|
|
}
|
|
|
|
rgfAttr = SFGAO_CANRENAME;
|
|
if (SUCCEEDED(_pshf->GetAttributesOf(0, NULL, &rgfAttr)) && (rgfAttr & SFGAO_CANRENAME)) {
|
|
dwStyle |= LVS_EDITLABELS;
|
|
}
|
|
|
|
if (!_IsDesktop() && !(_fs.fFlags & FWF_NOCLIENTEDGE)) {
|
|
dwExStyle |= WS_EX_CLIENTEDGE;
|
|
}
|
|
|
|
if (_IsOwnerData())
|
|
dwStyle |= LVS_OWNERDATA;
|
|
|
|
_hwndListview = CreateWindowEx(dwExStyle, WC_LISTVIEW, NULL, WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | dwStyle | LVS_SHAREIMAGELISTS, 0, 0, 0, 0, hWnd, (HMENU)ID_LISTVIEW, HINST_THISDLL, NULL);
|
|
if (!_hwndListview) {
|
|
TraceMsg(TF_ERROR, "Failed to create view window");
|
|
return -1; // failure
|
|
}
|
|
|
|
DWORD dwLVExStyle = LVS_EX_INFOTIP | LVS_EX_LABELTIP;
|
|
if (_IsDesktop() && (GetNumberOfMonitors() > 1))
|
|
dwLVExStyle |= LVS_EX_MULTIWORKAREAS;
|
|
|
|
// turn on infotips -- window was just created, so all LVS_EX bits are off
|
|
ListView_SetExtendedListViewStyle(_hwndListview, dwLVExStyle);
|
|
|
|
_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) || (PRIMARYLANGID(LANGIDFROMLCID(locale)) == LANG_HEBREW)) {
|
|
//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
|
|
dwLVExStyle = GetWindowLong(_hwndListview, GWL_EXSTYLE);
|
|
if (dwLVExStyle & RTL_MIRRORED_WINDOW)
|
|
_fmt = LVCFMT_RIGHT_TO_LEFT;
|
|
else
|
|
_fmt = LVCFMT_LEFT_TO_RIGHT;
|
|
}
|
|
}
|
|
|
|
// set the TTS_TOPMOST style bit for the tooltip
|
|
HWND hwndInfoTip = ListView_GetToolTips(_hwndListview);
|
|
if (EVAL(hwndInfoTip)) {
|
|
//make the tooltip window to be topmost window
|
|
SetWindowPos(hwndInfoTip, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
|
|
|
|
// set the AutoPopTime (the duration of showing the tooltip) to a large value
|
|
SendMessage(hwndInfoTip, TTM_SETDELAYTIME, TTDT_AUTOPOP, (LPARAM)MAXSHORT);
|
|
|
|
// increase the ShowTime (the delay before we show the tooltip) to 2 times the default value
|
|
LRESULT uiShowTime = SendMessage(hwndInfoTip, TTM_GETDELAYTIME, TTDT_INITIAL, 0);
|
|
SendMessage(hwndInfoTip, TTM_SETDELAYTIME, TTDT_INITIAL, uiShowTime * 2);
|
|
}
|
|
|
|
UpdateUnderlines(this);
|
|
|
|
Shell_GetImageLists(&himlLarge, &himlSmall);
|
|
ListView_SetImageList(_hwndListview, himlLarge, LVSIL_NORMAL);
|
|
ListView_SetImageList(_hwndListview, himlSmall, LVSIL_SMALL);
|
|
|
|
ASSERT(_psd == NULL); // this should not be set yet...
|
|
|
|
// IShellDetails for old callers, new guys use IShellFolder2
|
|
_pshf->CreateViewObject(_hwndMain, IID_IShellDetails, (void**)&_psd);
|
|
|
|
if ((_fs.ViewMode == FVM_DETAILS) || (SHGetAppCompatFlags(ACF_LOADCOLUMNHANDLER) & ACF_LOADCOLUMNHANDLER)) {
|
|
if (!_bLoadedColumns)
|
|
this->AddColumns();
|
|
}
|
|
|
|
DSV_SetFolderColors(this);
|
|
|
|
if (!g_msgMSWheel)
|
|
g_msgMSWheel = RegisterWindowMessage(TEXT("MSWHEEL_ROLLMSG"));
|
|
|
|
return 0; // success
|
|
}
|
|
|
|
|
|
// "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
|
|
STDMETHODIMP CDefView::AutoAutoArrange(DWORD dwReserved)
|
|
{
|
|
if (this->m_cFrame.IsSFVExtension()) {
|
|
// delegate to the view extension....
|
|
return this->m_cFrame.SFVAutoAutoArrange(dwReserved);
|
|
}
|
|
|
|
if (!_bItemsMoved && DV_ISANYICONMODE(_fs.ViewMode) && !(GetWindowStyle(_hwndListview) & LVS_AUTOARRANGE)) {
|
|
ListView_Arrange(_hwndListview, LVA_DEFAULT);
|
|
}
|
|
|
|
return NOERROR;
|
|
}
|
|
|
|
|
|
LRESULT CDefView::WndSize(HWND hWnd)
|
|
{
|
|
RECT rc;
|
|
|
|
// We need to dismiss "name edit" mode, if we are in.
|
|
DefView_DismissEdit(this);
|
|
|
|
// 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);
|
|
RedrawWindow(_hwndStatic, NULL, NULL, RDW_ERASE | RDW_INVALIDATE);
|
|
}
|
|
|
|
// Set all windows to their new rectangles.
|
|
|
|
// BUGBUG: if this is an SFV extension and _fGetWindowLV has happened,
|
|
// we'll resize it incorrectly. Should be an impossible situation to
|
|
// be in, but we should be more careful...
|
|
m_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) {
|
|
SetWindowPos(_hwndListview, NULL, rc.left, rc.top,
|
|
rc.right - rc.left, rc.bottom - rc.top, SWP_NOZORDER | SWP_NOACTIVATE);
|
|
AutoAutoArrange(0); // defview OC does this in the other case
|
|
}
|
|
|
|
CallCB(SFVM_SIZE, 0, 0);
|
|
return 1;
|
|
}
|
|
|
|
|
|
UINT _DSV_GetMenuIDFromViewMode(UINT ViewMode)
|
|
{
|
|
switch (ViewMode) {
|
|
case FVM_SMALLICON:
|
|
return SFVIDM_VIEW_SMALLICON;
|
|
case FVM_LIST:
|
|
return SFVIDM_VIEW_LIST;
|
|
case FVM_DETAILS:
|
|
return SFVIDM_VIEW_DETAILS;
|
|
case FVM_ICON:
|
|
default:
|
|
return SFVIDM_VIEW_ICON;
|
|
}
|
|
}
|
|
|
|
void CDefView::CheckToolbar()
|
|
{
|
|
int idCmd;
|
|
int idCmdCurView = m_cFrame.CurExtViewId();
|
|
|
|
if (idCmdCurView < 0) {
|
|
idCmdCurView = _DSV_GetMenuIDFromViewMode(_fs.ViewMode);
|
|
}
|
|
|
|
if (SHGetAppCompatFlags(ACF_WIN95DEFVIEW) & ACF_WIN95DEFVIEW) {
|
|
// preserve win95 behavior for dumb corel apps
|
|
for (idCmd = SFVIDM_VIEW_ICON; idCmd <= SFVIDM_VIEW_DETAILS; idCmd++) {
|
|
_psb->SendControlMsg(FCW_TOOLBAR, TB_CHECKBUTTON, idCmd, (LPARAM)(idCmd == idCmdCurView), NULL);
|
|
}
|
|
|
|
for (idCmd = SFVIDM_VIEW_EXTFIRST; idCmd <= SFVIDM_VIEW_EXTLAST; idCmd++) {
|
|
_psb->SendControlMsg(FCW_TOOLBAR, TB_CHECKBUTTON, idCmd, (LPARAM)(idCmd == idCmdCurView), NULL);
|
|
}
|
|
} else if (_pbtn) {
|
|
// loop through our button array and check/uncheck any SFVIDM_VIEW_EXT
|
|
// buttons. normally, we won't have any of these.
|
|
for (int i = 0; i < _cButtons; i++) {
|
|
if (InRange(_pbtn[i].idCommand, SFVIDM_VIEW_EXTFIRST, SFVIDM_VIEW_EXTLAST)) {
|
|
_psb->SendControlMsg(FCW_TOOLBAR, TB_CHECKBUTTON, _pbtn[i].idCommand, (LPARAM)(_pbtn[i].idCommand == idCmdCurView), NULL);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void CDefView::OnListViewDelete(int iItem, LPITEMIDLIST pidl)
|
|
{
|
|
LPITEMIDLIST pidlReal = pidl;
|
|
|
|
if (!pidlReal && _IsOwnerData()) {
|
|
CallCB(SFVM_GETITEMIDLIST, iItem, (LPARAM)&pidlReal);
|
|
}
|
|
|
|
if (pidlReal) {
|
|
CallCB(SFVM_DELETEITEM, 0, (LPARAM)pidlReal);
|
|
}
|
|
|
|
if (pidl)
|
|
ILFree(pidl);
|
|
}
|
|
|
|
|
|
// NOTE: many keys are handled as accelerators
|
|
|
|
void CDefView::HandleKeyDown(LV_KEYDOWN* lpnmhdr)
|
|
{
|
|
// REVIEW: these are things not handled by accelerators, see if we can
|
|
// make them all based on accelerators
|
|
switch (lpnmhdr->wVKey) {
|
|
case VK_ESCAPE:
|
|
if (_bHaveCutStuff)
|
|
OleSetClipboard(NULL);
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
// DV_GetPidl
|
|
|
|
// 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.
|
|
|
|
// BUGBUG: this really should return a const poitner since clients are not supposed to free this.
|
|
LPITEMIDLIST CDefView::_GetPIDL(int i)
|
|
{
|
|
if (_IsOwnerData()) {
|
|
LPITEMIDLIST pidl = NULL;
|
|
CallCB(SFVM_GETITEMIDLIST, i, (LPARAM)&pidl);
|
|
return pidl;
|
|
}
|
|
|
|
return (LPITEMIDLIST)LVUtil_GetLParam(_hwndListview, i);
|
|
}
|
|
|
|
|
|
LPCITEMIDLIST DV_GetPIDLParam(CDefView* pdsv, LPARAM lParam, int i)
|
|
{
|
|
if (lParam)
|
|
return (LPCITEMIDLIST)lParam;
|
|
|
|
return (LPCITEMIDLIST)pdsv->_GetPIDL(i);
|
|
}
|
|
|
|
|
|
// DefView_GetItemPIDLS
|
|
|
|
// This function returns an array of LPCITEMIDLIST for "selected" objects in the
|
|
// listview. It always returns the number of selected objects. Typically, the
|
|
// client (1) calls this function with cItemMax==0 to know the required size for the
|
|
// array, (2) allocates a block of memory for the array, and (3) calls this function
|
|
// again to get the list of LPCITEMIDLIST.
|
|
|
|
// Notes: Note that this function returns LP*C*ITEMIDLIST. The caller is not
|
|
// supposed alter or delete them. Their lifetime are very short (until the list view is modified).
|
|
UINT DefView_GetItemPIDLS(CDefView* pdsv, LPCITEMIDLIST apidl[], UINT cItemMax, UINT uItem)
|
|
{
|
|
// REVIEW: We should put the focused one at the top of the list.
|
|
int iItem = -1;
|
|
int iItemFocus = -1;
|
|
UINT cItem = 0;
|
|
UINT uType;
|
|
|
|
switch (uItem) {
|
|
case SVGIO_SELECTION:
|
|
// special case for faster search
|
|
if (!cItemMax) {
|
|
return ListView_GetSelectedCount(pdsv->_hwndListview);
|
|
}
|
|
iItemFocus = ListView_GetNextItem(pdsv->_hwndListview, -1, LVNI_FOCUSED);
|
|
uType = LVNI_SELECTED;
|
|
break;
|
|
case SVGIO_ALLVIEW:
|
|
// special case for faster search
|
|
if (!cItemMax)
|
|
return ListView_GetItemCount(pdsv->_hwndListview);
|
|
uType = LVNI_ALL;
|
|
break;
|
|
}
|
|
|
|
while ((iItem = ListView_GetNextItem(pdsv->_hwndListview, iItem, uType)) != -1) {
|
|
if (cItem < cItemMax) {
|
|
// Check if the item is the focused one or not.
|
|
if (iItem == iItemFocus) {
|
|
// Yes, put it at the top.
|
|
apidl[cItem] = apidl[0];
|
|
apidl[0] = pdsv->_GetPIDL(iItem);
|
|
} else {
|
|
// No, put it at the end of the list.
|
|
apidl[cItem] = pdsv->_GetPIDL(iItem);
|
|
}
|
|
}
|
|
cItem++;
|
|
}
|
|
|
|
return cItem;
|
|
}
|
|
|
|
|
|
// This function get the array of IDList from the selection and calls
|
|
// IShellFolder::GetUIObjectOf member to get the specified UI object interface.
|
|
HRESULT DefView_GetUIObjectFromItem(CDefView* pdsv, REFIID riid, void** ppv, UINT uItem)
|
|
{
|
|
LPCITEMIDLIST* apidl = NULL;
|
|
UINT cItems;
|
|
HRESULT hres;
|
|
|
|
if (pdsv->m_cFrame.IsSFVExtension()) // IShellView extension
|
|
{
|
|
hres = E_INVALIDARG;
|
|
|
|
if (SVGIO_SELECTION == uItem) {
|
|
// delegate to the IShellView2 extension
|
|
hres = pdsv->m_cFrame.GetExtendedISFV()->GetSelectedObjects(&apidl, &cItems);
|
|
}
|
|
#if 0 // THIS IS UNTESTED: it's here in case someone needs it later. make sure it works
|
|
else if (SVGIO_ALLVIEW == uItem) {
|
|
hres = pdsv->m_cFrame.GetExtendedISFV()->GetObjectCount(&cItems);
|
|
if (SUCCEEDED(hres)) {
|
|
apidl = (LPCITEMIDLIST*)LocalAlloc(LPTR, SIZEOF(LPCITEMIDLIST) * cItems);
|
|
if (apidl) {
|
|
UINT count = 0;
|
|
|
|
for (UINT i = 0; i < cItems; i++) {
|
|
if (SUCCEEDED(pdsv->m_cFrame.GetExtendedISFV()->GetObject((LPITEMIDLIST*)&apidl[count], count)))
|
|
count++;
|
|
}
|
|
cItems = count;
|
|
if (0 == cItems) {
|
|
LocalFree(apidl);
|
|
hres = E_UNEXPECTED;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (SUCCEEDED(hres)) {
|
|
if (cItems) {
|
|
hres = pdsv->_pshf->GetUIObjectOf(pdsv->_hwndMain, cItems, apidl, riid, 0, ppv);
|
|
ASSERT(apidl);
|
|
LocalFree((HLOCAL)apidl);
|
|
} else
|
|
hres = E_INVALIDARG;
|
|
}
|
|
} else {
|
|
hres = DefView_GetItemObjects(pdsv, &apidl, uItem, &cItems);
|
|
if (SUCCEEDED(hres)) {
|
|
if (cItems) {
|
|
hres = pdsv->_pshf->GetUIObjectOf(pdsv->_hwndMain, cItems, apidl, riid, 0, ppv);
|
|
} else {
|
|
hres = E_INVALIDARG;
|
|
}
|
|
|
|
if (apidl)
|
|
LocalFree(apidl);
|
|
}
|
|
}
|
|
|
|
return hres;
|
|
}
|
|
|
|
|
|
// This function creates the cached context menu for the current selection and update the cache, if it is not created yet. Then, returns a copied
|
|
// pointer to it. The caller should Release() it.
|
|
|
|
// NOTE: if we're in an extended view, don't cache the context menu
|
|
// because the top defview isn't notified of selection changes, so the cache will get out of date.
|
|
IContextMenu* CDefView::_GetContextMenuFromSelection()
|
|
{
|
|
IContextMenu* pcm = NULL;
|
|
|
|
if (_HasNormalView()) {
|
|
if (m_cFrame.IsSFVExtension()) // IShellView Extended View
|
|
{
|
|
m_cFrame.GetExtendedISV()->GetItemObject(SVGIO_SELECTION, IID_IContextMenu, (void**)&pcm);
|
|
} else // normal large, small, list, details . . .
|
|
{
|
|
if (_pcmSel == NULL)
|
|
DefView_GetUIObjectFromItem(this, IID_IContextMenu, (void**)&_pcmSel, SVGIO_SELECTION);
|
|
|
|
if (_pcmSel) {
|
|
pcm = _pcmSel;
|
|
pcm->AddRef();
|
|
}
|
|
}
|
|
} else // DocObject extended view
|
|
{
|
|
TraceMsg(TF_ERROR, "Extended View: GetContextMenuFromSelection has no active site!");
|
|
}
|
|
|
|
return pcm;
|
|
}
|
|
|
|
|
|
// If the browser has a Tree then we want to use explore.
|
|
UINT CDefView::_GetExplorerFlag()
|
|
{
|
|
HWND hwnd;
|
|
if (_psb && SUCCEEDED(_psb->GetControlWindow(FCW_TREE, &hwnd)) && hwnd)
|
|
return CMF_EXPLORE;
|
|
return 0;
|
|
}
|
|
|
|
|
|
#define DEFAULT_ATTRIBUTES (DROPEFFECT_LINK | DROPEFFECT_MOVE | DROPEFFECT_COPY | SFGAO_CANDELETE | SFGAO_CANRENAME | SFGAO_HASPROPSHEET)
|
|
DWORD DefView_GetAttributesFromSelection(CDefView* pdsv, DWORD dwAttrMask)
|
|
{
|
|
if (pdsv->_HasNormalView()) // normal views are non docobj extended views.
|
|
{
|
|
if (pdsv->m_cFrame.IsSFVExtension()) // IShellView extension
|
|
{
|
|
LPCITEMIDLIST* apidl;
|
|
HRESULT hres;
|
|
UINT cItems;
|
|
|
|
// delegate to the IShellView2 extension
|
|
hres = pdsv->m_cFrame.GetExtendedISFV()->GetSelectedObjects(&apidl, &cItems);
|
|
if (FAILED(hres) || cItems == 0) {
|
|
// BUGBUG REVIEW: we may want to mimic the BUGBUG else case above
|
|
dwAttrMask = 0;
|
|
} else {
|
|
if (FAILED(pdsv->_pshf->GetAttributesOf(cItems, apidl, &dwAttrMask))) {
|
|
dwAttrMask = 0;
|
|
}
|
|
LocalFree((HLOCAL)apidl);
|
|
}
|
|
|
|
return dwAttrMask;
|
|
} else // large, small, list, details . . .
|
|
{
|
|
DWORD dwAttrQuery = DEFAULT_ATTRIBUTES | dwAttrMask;
|
|
|
|
if ((pdsv->_dwAttrSel == (DWORD)-1) || (dwAttrQuery != DEFAULT_ATTRIBUTES)) {
|
|
LPCITEMIDLIST* apidl;
|
|
UINT cItems;
|
|
|
|
HRESULT hres = DefView_GetItemObjects(pdsv, &apidl, SVGIO_SELECTION, &cItems);
|
|
|
|
// this cache was written right before RC1 if you hit this after fix it
|
|
ASSERT(dwAttrQuery == DEFAULT_ATTRIBUTES);
|
|
|
|
if (SUCCEEDED(hres)) {
|
|
if (cItems) {
|
|
if (SUCCEEDED(pdsv->_pshf->GetAttributesOf(cItems, apidl, &dwAttrQuery))) {
|
|
pdsv->_dwAttrSel = dwAttrQuery;
|
|
}
|
|
|
|
LocalFree((HLOCAL)apidl);
|
|
} else {
|
|
// mask out attrib bits here... if there's no selection, we can't
|
|
// rename, delete, link, prop...
|
|
|
|
// BUGBUG: maybe this should be set to 0, but I'm afraid of what
|
|
// yanking SFGAO_FILESYS and other random stuff will do to us..
|
|
pdsv->_dwAttrSel &= ~(DROPEFFECT_LINK | DROPEFFECT_MOVE | DROPEFFECT_COPY | SFGAO_CANDELETE | SFGAO_CANRENAME | SFGAO_HASPROPSHEET);
|
|
}
|
|
}
|
|
}
|
|
return (pdsv->_dwAttrSel & dwAttrMask);
|
|
}
|
|
} else // Extended View
|
|
{
|
|
TraceMsg(TF_WARNING, "Extended View: GetAttributesFromSelection has no active site!");
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
// DV_FlushCachedMenu flags
|
|
#define DVFCMF_SEL 0x00000001
|
|
#define DVFCMF_BACKGROUND 0x00000002
|
|
#define DVFCMF_ALL 0xFFFFFFFF
|
|
|
|
|
|
void DV_FlushCachedMenu(CDefView* pdsv, ULONG dwFlushFlags = DVFCMF_ALL)
|
|
{
|
|
if (dwFlushFlags & DVFCMF_SEL) {
|
|
// THIS FAULTS if you create two context menus in a row
|
|
//IUnknown_SetSite(SAFECAST(pdsv->_pcmSel,IContextMenu*),NULL);
|
|
ATOMICRELEASE(pdsv->_pcmSel);
|
|
}
|
|
|
|
if (pdsv->_uState != SVUIA_ACTIVATE_NOFOCUS) {
|
|
if (dwFlushFlags & DVFCMF_BACKGROUND) {
|
|
// We only need to collapse when the background is selected.
|
|
// Collapse any menus before flushing the cache. This is for NT Bug#303716
|
|
SendMessage(pdsv->_hwndView, WM_CANCELMODE, 0, 0);
|
|
|
|
// THIS FAULTS if you create two context menus in a row
|
|
//IUnknown_SetSite(SAFECAST(pdsv->_pcmBackground,IContextMenu*),NULL);
|
|
ATOMICRELEASE(pdsv->_pcmBackground);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void CDefView::ContextMenu(DWORD dwPos)
|
|
{
|
|
int iItem;
|
|
int idCmd;
|
|
int idDefault = -1;
|
|
int nInsert;
|
|
HMENU hmContext;
|
|
UINT fFlags = 0;
|
|
POINT pt;
|
|
UINT iIDStart = SFVIDM_CONTEXT_FIRST;
|
|
UINT iIDLast = SFVIDM_CONTEXT_LAST;
|
|
|
|
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);
|
|
}
|
|
|
|
hmContext = CreatePopupMenu();
|
|
if (!hmContext) {
|
|
if (g_dwStopWatchMode)
|
|
StopWatch_Stop(SWID_MENU, TEXT("Defview ContextMenu Stop (ERROR)"), SPMODE_SHELL | SPMODE_DEBUGOUT);
|
|
// BUGBUG: There should be an error message here
|
|
return;
|
|
}
|
|
|
|
LPARAM i;
|
|
if (iItem == -1) {
|
|
DECLAREWAITCURSOR;
|
|
SetWaitCursor();
|
|
|
|
// use the background context menu wrapper
|
|
HRESULT hr = this->GetItemObject(SVGIO_BACKGROUND, IID_IContextMenu, (void**)&_pcmBackground);
|
|
|
|
ResetWaitCursor();
|
|
|
|
// set the max range for these, so that they are unaffected...
|
|
iIDStart = 0;
|
|
iIDLast = 0xffff;
|
|
i = _IsDesktop() ? UIBL_CTXTDESKBKGND : UIBL_CTXTDEFBKGND;
|
|
} else {
|
|
fFlags |= CMF_CANRENAME;
|
|
|
|
nInsert = 0;
|
|
|
|
// One or more items are selected, let the folder add menuitems.
|
|
_pcmBackground = _GetContextMenuFromSelection();
|
|
i = _IsDesktop() ? UIBL_CTXTDESKITEM : UIBL_CTXTDEFITEM;
|
|
}
|
|
|
|
UEMFireEvent(&UEMIID_SHELL, UEME_INSTRBROWSER, UEMF_INSTRUMENT, UIBW_UICONTEXT, i);
|
|
|
|
if (IsSafeToDefaultVerb()) {
|
|
ICommDlgBrowser2* pcdb2;
|
|
|
|
// Get the ICommDlgBrowser2 interface.
|
|
_psb->QueryInterface(IID_ICommDlgBrowser2, (void**)&pcdb2);
|
|
|
|
if (_pcmBackground) {
|
|
fFlags |= _GetExplorerFlag();
|
|
|
|
IUnknown_SetSite(_pcmBackground, SAFECAST(this, IShellView2*));
|
|
|
|
if (0 > GetKeyState(VK_SHIFT))
|
|
fFlags |= CMF_EXTENDEDVERBS;
|
|
|
|
_pcmBackground->QueryContextMenu(hmContext, nInsert, iIDStart, iIDLast, fFlags);
|
|
|
|
// If this is the common dialog browser, we need to make the
|
|
// default command "Select" so that double-clicking (which is open in common dialog) makes sense.
|
|
if (DV_CDB_IsCommonDialog(this)) {
|
|
// make sure this is an item
|
|
if (iItem != -1) {
|
|
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) {
|
|
INT iRetval = TRUE;
|
|
LPTSTR pszText = NULL;
|
|
#ifdef UNICODE
|
|
pszText = szTextW;
|
|
#else
|
|
CHAR szText[MAX_PATH] = {0};
|
|
iRetval = SHUnicodeToAnsi(szTextW, szText, ARRAYSIZE(szText));
|
|
pszText = szText;
|
|
#endif
|
|
if (iRetval) {
|
|
MENUITEMINFO mi = {0};
|
|
mi.cbSize = sizeof(mi);
|
|
mi.fMask = MIIM_TYPE;
|
|
mi.fType = MFT_STRING;
|
|
mi.dwTypeData = pszText;
|
|
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);
|
|
}
|
|
}
|
|
|
|
idDefault = GetMenuDefaultItem(hmContext, MF_BYCOMMAND, 0);
|
|
}
|
|
|
|
_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);
|
|
}
|
|
|
|
// Keep this code just before the TPM call
|
|
if (g_dwStopWatchMode)
|
|
StopWatch_Stop(SWID_MENU, TEXT("Defview ContextMenu Stop"), SPMODE_SHELL | SPMODE_DEBUGOUT);
|
|
|
|
idCmd = TrackPopupMenu(hmContext, TPM_RETURNCMD | TPM_RIGHTBUTTON | TPM_LEFTALIGN, pt.x, pt.y, 0, _hwndView, NULL);
|
|
INSTRUMENT_TRACKPOPUPMENU(SHCNFI_DEFVIEWX_TPM, _hwndView, idCmd);
|
|
|
|
if ((idCmd == idDefault) && DV_CDB_OnDefaultCommand(this) == S_OK) {
|
|
// commdlg browser ate the default command
|
|
} else if (idCmd == 0) {
|
|
// No item selected
|
|
} else if (iItem == -1) {
|
|
// we are using the background context menu wrapper, so let it do the right thing by calling Invoke command
|
|
if (_pcmBackground) {
|
|
CMINVOKECOMMANDINFO rgCmd;
|
|
|
|
ZeroMemory(&rgCmd, sizeof(rgCmd));
|
|
rgCmd.cbSize = sizeof(rgCmd);
|
|
rgCmd.lpVerb = (LPCSTR)UIntToPtr(idCmd);
|
|
|
|
_pcmBackground->InvokeCommand(&rgCmd);
|
|
}
|
|
} else {
|
|
// A default menu item was selected; let everybody
|
|
// process it normally
|
|
// Note that this must be called before clearing out _pcmSel,
|
|
// so we cannot do this with a PostMessage
|
|
this->Command(_pcmBackground, GET_WM_COMMAND_MPS(idCmd, 0, 0));
|
|
}
|
|
|
|
// 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();
|
|
}
|
|
|
|
IUnknown_SetSite(_pcmBackground, NULL);
|
|
} else {
|
|
// not IsSafeToDefaultVerb(), so we didn't show the cm, so we didn't stop the timer
|
|
if (g_dwStopWatchMode)
|
|
StopWatch_Stop(SWID_MENU, TEXT("Defview ContextMenu Stop (!SafeToDefaultVerb)"), SPMODE_SHELL | SPMODE_DEBUGOUT);
|
|
}
|
|
|
|
ATOMICRELEASE(_pcmBackground);
|
|
|
|
MSG msg;
|
|
if (PeekMessage(&msg, _hwndMain, WM_DSV_MENUTERM, WM_DSV_MENUTERM, PM_REMOVE)) {
|
|
// _OnMenuTermination no longer calls DV_FlushCachedMenu so
|
|
// call it manually
|
|
DV_FlushCachedMenu(this);
|
|
_OnMenuTermination();
|
|
}
|
|
|
|
DestroyMenu(hmContext);
|
|
}
|
|
|
|
|
|
void CALLBACK DefView_GetDataPoint(LPCITEMIDLIST pidl, LPPOINT ppt, LPARAM lParam)
|
|
{
|
|
CDefView* pdsv = (CDefView*)lParam;
|
|
|
|
if (pidl)
|
|
pdsv->_GetItemPosition(pidl, ppt);
|
|
else
|
|
DefView_GetDragPoint(pdsv, ppt);
|
|
}
|
|
|
|
|
|
void DefView_SetViewMode(CDefView* pdsv, UINT fvmNew, DWORD dwStyle)
|
|
{
|
|
pdsv->_fs.ViewMode = fvmNew;
|
|
pdsv->_dvState.iDirection = 1;
|
|
SetWindowBits(pdsv->_hwndListview, GWL_STYLE, LVS_TYPEMASK, dwStyle);
|
|
}
|
|
|
|
|
|
BOOL DV_GetItemSpacing(CDefView* pdsv, LPITEMSPACING lpis)
|
|
{
|
|
DWORD dwSize;
|
|
|
|
dwSize = ListView_GetItemSpacing(pdsv->_hwndListview, TRUE);
|
|
lpis->cxSmall = GET_X_LPARAM(dwSize);
|
|
lpis->cySmall = GET_Y_LPARAM(dwSize);
|
|
dwSize = ListView_GetItemSpacing(pdsv->_hwndListview, FALSE);
|
|
lpis->cxLarge = GET_X_LPARAM(dwSize);
|
|
lpis->cyLarge = GET_Y_LPARAM(dwSize);
|
|
|
|
return (pdsv->_fs.ViewMode != FVM_ICON);
|
|
}
|
|
|
|
|
|
void DefView_SetPoints(CDefView* pdsv, IDataObject* pdtobj)
|
|
{
|
|
SCALEINFO si;
|
|
|
|
// convert coordinates to large icon view spacing
|
|
if (pdsv->_fs.ViewMode == FVM_ICON) {
|
|
si.xMul = si.yMul = si.xDiv = si.yDiv = 1;
|
|
} else {
|
|
ITEMSPACING is;
|
|
|
|
DV_GetItemSpacing(pdsv, &is);
|
|
|
|
si.xDiv = is.cxSmall;
|
|
si.yDiv = is.cySmall;
|
|
si.xMul = is.cxLarge;
|
|
si.yMul = is.cyLarge;
|
|
}
|
|
|
|
// Assuming this is a CIDLData thing, poke the icon
|
|
// locations into it
|
|
DataObj_SetPoints(pdtobj, DefView_GetDataPoint, (LPARAM)pdsv, &si);
|
|
}
|
|
|
|
|
|
BOOL _DidDropOnRecycleBin(IDataObject* pdtobj)
|
|
{
|
|
CLSID clsid;
|
|
return SUCCEEDED(DataObj_GetDropTarget(pdtobj, &clsid)) && IsEqualCLSID(clsid, CLSID_RecycleBin);
|
|
}
|
|
|
|
|
|
// REVIEW: Currently, we are not doing any serialization assuming that only one GUI thread can come here at a time.
|
|
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;
|
|
}
|
|
|
|
if (_bAutoSelChangeTimerSet) { // Hurry up the sel change notification to the automation object
|
|
KillTimer(_hwndView, DV_IDTIMER_NOTIFY_AUTOMATION_SELCHANGE);
|
|
_bAutoSelChangeTimerSet = FALSE;
|
|
NotifyAutomation(DISPID_SELECTIONCHANGED);
|
|
}
|
|
NotifyAutomation(DISPID_VERBINVOKED);
|
|
|
|
// Get the dwEffect from the selection.
|
|
DWORD dwEffect = DefView_GetAttributesFromSelection(this, 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
|
|
|
|
IDataObject* pdtobj;
|
|
if (SUCCEEDED(DefView_GetUIObjectFromItem(this, IID_IDataObject, (void**)&pdtobj, SVGIO_SELECTION))) {
|
|
// Give the source a chance to alter the drop effect.
|
|
CallCB(SFVM_ALTERDROPEFFECT, (WPARAM)&dwEffect, (LPARAM)pdtobj);
|
|
|
|
if (DAD_SetDragImageFromWindow(_hwndListview, &ptOffset, pdtobj)) {
|
|
DefView_SetPoints(this, 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;
|
|
InvokeVerbOnDataObj(_hwndMain, c_szDelete, uFlags, pdtobj);
|
|
}
|
|
}
|
|
}
|
|
|
|
// 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);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
#define DV_IDTIMER_NOTIFY_AUTOMATION_SELCHANGE 4
|
|
#define NOTIFY_AUTOMATION_SELCHANGE_TIMEOUT (GetDoubleClickTime())
|
|
|
|
|
|
HRESULT CDefView::_InvokeCommand(IContextMenu* pcm, CMINVOKECOMMANDINFOEX* pici)
|
|
{
|
|
TCHAR szWorkingDir[MAX_PATH];
|
|
#ifdef UNICODE
|
|
CHAR szWorkingDirAnsi[MAX_PATH];
|
|
#endif
|
|
|
|
if (SUCCEEDED(CallCB(SFVM_GETWORKINGDIR, ARRAYSIZE(szWorkingDir), (LPARAM)szWorkingDir))) {
|
|
#ifdef UNICODE
|
|
// Fill in both the ansi working dir and the unicode one
|
|
// since we don't know who's gonna be processing this thing.
|
|
SHUnicodeToAnsi(szWorkingDir, szWorkingDirAnsi, ARRAYSIZE(szWorkingDirAnsi));
|
|
pici->lpDirectory = szWorkingDirAnsi;
|
|
pici->lpDirectoryW = szWorkingDir;
|
|
pici->fMask |= CMIC_MASK_UNICODE;
|
|
#else
|
|
pici->lpDirectory = szWorkingDir;
|
|
#endif
|
|
}
|
|
|
|
// In case the ptInvoke field was not already set for us, guess where
|
|
// that could be. BUGBUG: (dli) maybe should let the caller set all points
|
|
if (!(pici->fMask & CMIC_MASK_PTINVOKE)) {
|
|
POINT ptFocus;
|
|
if (GetCursorPos(&ptFocus)) {
|
|
pici->ptInvoke = ptFocus;
|
|
pici->fMask |= CMIC_MASK_PTINVOKE;
|
|
}
|
|
}
|
|
|
|
if (_bAutoSelChangeTimerSet) { // Hurry up the sel change notification to the automation object
|
|
KillTimer(_hwndView, DV_IDTIMER_NOTIFY_AUTOMATION_SELCHANGE);
|
|
_bAutoSelChangeTimerSet = FALSE;
|
|
NotifyAutomation(DISPID_SELECTIONCHANGED);
|
|
}
|
|
NotifyAutomation(DISPID_VERBINVOKED);
|
|
return pcm->InvokeCommand((LPCMINVOKECOMMANDINFO)pici);
|
|
}
|
|
|
|
|
|
DWORD CDefView::_GetNeededSecurityAction(void)
|
|
{
|
|
DWORD dwUrlAction = 0;
|
|
IUnknown* punk;
|
|
|
|
ASSERT(_psb);
|
|
|
|
// If we are hosted by Trident, Zone Check Action.
|
|
if (SUCCEEDED(_psb->QueryInterface(IID_IIsWebBrowserSB, (void**)&punk))) {
|
|
dwUrlAction = URLACTION_SHELL_VERB;
|
|
ATOMICRELEASE(punk);
|
|
} 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();
|
|
|
|
ASSERT(_psb);
|
|
|
|
if (dwUrlAction && (dwUrlAction != dwAllowAction)) {
|
|
IInternetHostSecurityManager* pihsm;
|
|
|
|
// First check if our parent wants to generate our context (Zone/URL).
|
|
hr = IUnknown_QueryService(_psb, IID_IInternetHostSecurityManager, IID_IInternetHostSecurityManager, (void**)&pihsm);
|
|
|
|
// Did that fail?
|
|
if (FAILED(hr) && m_cFrame.m_pDocView) {
|
|
if (!_fGetWindowLV)
|
|
TraceMsg(TF_DEFVIEW, "We will fall back to Zone Checking the PIDL instead of the zone our host generates."); // We should only get here in the plain WebView case.
|
|
|
|
// 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(m_cFrame.m_pDocView, IID_IInternetHostSecurityManager, IID_IInternetHostSecurityManager, (void**)&pihsm);
|
|
}
|
|
|
|
// Were we able to get an IInternetHostSecurityManager interface?
|
|
if (EVAL(SUCCEEDED(hr))) {
|
|
// This is the prefered way to do the zone check.
|
|
hr = ZoneCheckHost(pihsm, dwUrlAction, dwFlags | PUAF_FORCEUI_FOREGROUND);
|
|
ATOMICRELEASE(pihsm);
|
|
} else {
|
|
TCHAR szPathSource[MAX_PATH];
|
|
// 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.
|
|
|
|
if (_GetPath(szPathSource)) {
|
|
IInternetSecurityMgrSite* pisms;
|
|
|
|
// Try to get a IInternetSecurityMgrSite so our UI will be modal.
|
|
if (SUCCEEDED(IUnknown_QueryService(_psb, SID_STopLevelBrowser, IID_IInternetSecurityMgrSite, (void**)&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)
|
|
{
|
|
ASSERT(_psb);
|
|
return S_OK == _ZoneCheck(PUAF_WARN_IF_DENIED, 0);
|
|
}
|
|
|
|
|
|
#define CMD_ID_FIRST 1
|
|
#define CMD_ID_LAST 0x7fff
|
|
|
|
|
|
void CDefView::_ProcessDblClick(LPNMITEMACTIVATE pnmia)
|
|
{
|
|
// Use the cached context menu object if there is one, else
|
|
// make it.
|
|
IContextMenu* pcmSel;
|
|
// SHIFT invokes the "alternate" command (usually Print)
|
|
int iShowCmd = SW_NORMAL;
|
|
DECLAREWAITCURSOR;
|
|
|
|
if (_IsDesktop())
|
|
UEMFireEvent(&UEMIID_SHELL, UEME_UISCUT, UEMF_XEVENT, -1, (LPARAM)-1);
|
|
|
|
if (DV_CDB_OnDefaultCommand(this) == S_OK) {
|
|
return; /* commdlg browser ate the message */
|
|
}
|
|
SetWaitCursor();
|
|
pcmSel = _GetContextMenuFromSelection();
|
|
|
|
if (pcmSel) {
|
|
CMINVOKECOMMANDINFOEX ici;
|
|
|
|
ZeroMemory(&ici, SIZEOF(ici));
|
|
ici.cbSize = SIZEOF(ici);
|
|
ici.hwnd = _hwndMain;
|
|
ici.nShow = iShowCmd;
|
|
|
|
// 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);
|
|
|
|
// Security note: we assume "properties" is a safe action.
|
|
if (pnmia->uKeyFlags & LVKF_ALT) {
|
|
#ifdef UNICODE
|
|
// Fill in both the ansi verb and the unicode verb since we
|
|
// don't know who is going to be processing this thing.
|
|
ici.lpVerb = "properties";
|
|
ici.lpVerbW = c_szProperties;
|
|
ici.fMask |= CMIC_MASK_UNICODE;
|
|
#else
|
|
ici.lpVerb = c_szProperties;
|
|
#endif
|
|
// If ALT double click, accelerator for "Properties..."
|
|
|
|
// need to reset it so that user won't blow off the app starting cursor
|
|
// also so that if we won't leave the wait cursor up when we're not waiting
|
|
// (like in a prop sheet or something that has a message loop
|
|
ResetWaitCursor();
|
|
hcursor_wait_cursor_save = NULL;
|
|
|
|
INSTRUMENT_STATECHANGE(SHCNFI_STATE_DEFVIEWX_ALT_DBLCLK);
|
|
|
|
_InvokeCommand(pcmSel, &ici);
|
|
} else if (IsSafeToDefaultVerb()) {
|
|
HMENU hmenu = CreatePopupMenu();
|
|
if (hmenu) {
|
|
UINT idCmd;
|
|
UINT fFlags = CMF_DEFAULTONLY;
|
|
|
|
fFlags |= _GetExplorerFlag();
|
|
|
|
// SHIFT+dblclick does a Explore by default
|
|
if (pnmia->uKeyFlags & LVKF_SHIFT) {
|
|
fFlags |= CMF_EXPLORE;
|
|
}
|
|
|
|
pcmSel->QueryContextMenu(hmenu, 0, CMD_ID_FIRST, CMD_ID_LAST, fFlags);
|
|
|
|
idCmd = GetMenuDefaultItem(hmenu, MF_BYCOMMAND, GMDI_GOINTOPOPUPS);
|
|
|
|
// need to reset it so that user won't blow off the app starting cursor
|
|
// also so that if we won't leave the wait cursor up when we're not waiting
|
|
// (like in a prop sheet or something that has a message loop
|
|
ResetWaitCursor();
|
|
hcursor_wait_cursor_save = NULL;
|
|
if (idCmd != -1) {
|
|
ici.lpVerb = (LPSTR)MAKEINTRESOURCE(idCmd - CMD_ID_FIRST);
|
|
_InvokeCommand(pcmSel, &ici);
|
|
}
|
|
DestroyMenu(hmenu);
|
|
}
|
|
}
|
|
|
|
// Release our use of the context menu
|
|
if (pcmSel == _pcmSel) {
|
|
DV_FlushCachedMenu(this);
|
|
}
|
|
pcmSel->Release();
|
|
}
|
|
|
|
if (hcursor_wait_cursor_save)
|
|
ResetWaitCursor();
|
|
}
|
|
|
|
|
|
void CDefView::_UpdateColData(CBackgroundColInfo* pbgci)
|
|
{
|
|
UINT iItem = _FindItem(pbgci->GetPIDL(), NULL, FALSE);
|
|
if (iItem != -1) {
|
|
UINT uiCol = pbgci->GetColumn();
|
|
|
|
if (IsColumnOn(uiCol)) {
|
|
UINT iVisCol;
|
|
MapRealToVisibleColumn(uiCol, &iVisCol);
|
|
|
|
ListView_SetItemText(_hwndListview, iItem, iVisCol, const_cast<TCHAR*>(pbgci->GetText()));
|
|
}
|
|
}
|
|
|
|
delete pbgci;
|
|
}
|
|
|
|
|
|
void CDefView::_UpdateIcon(CDVGetIconTask* pTask)
|
|
{
|
|
ASSERT(pTask);
|
|
ASSERT(pTask->_pidl);
|
|
|
|
if (!pTask->_fUsed) {
|
|
int i = _FindItem(pTask->_pidl, NULL, FALSE);
|
|
|
|
InterlockedDecrement(&_AsyncIconCount);
|
|
|
|
DebugMsg(TF_ICON, TEXT("async icon done: pidl=%08X i=%d icon=%d count=%d"), pTask->_pidl, i, pTask->_iIcon, _AsyncIconCount);
|
|
|
|
if (i >= 0) {
|
|
LV_ITEM item;
|
|
|
|
item.mask = LVIF_IMAGE;
|
|
item.iItem = i;
|
|
item.iImage = pTask->_iIcon;
|
|
item.iSubItem = 0;
|
|
|
|
ListView_SetItem(_hwndListview, &item);
|
|
}
|
|
}
|
|
|
|
ATOMICRELEASE(pTask);
|
|
}
|
|
|
|
|
|
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 hres;
|
|
|
|
#ifdef MAX_ICON_WAIT
|
|
// get the time here so we include the time it took to load handlers etc.
|
|
LONG time = GetTickCount();
|
|
#endif
|
|
|
|
// if we are not an owner-data view then try to extract asynchronously
|
|
UINT flags = (_IsOwnerData() ? 0 : GIL_ASYNC);
|
|
|
|
again:
|
|
hres = SHGetIconFromPIDL(_pshf, _psi, pidl, flags, piIcon);
|
|
|
|
if (SUCCEEDED(hres))
|
|
return S_OK; // indicate that we got the real icon
|
|
|
|
if (hres == E_PENDING && (flags & GIL_ASYNC)) {
|
|
hres = S_FALSE; // the icon index we have is a placeholder
|
|
|
|
DebugMsg(TF_ICON, TEXT("SHGetIconFromPIDL returns E_PENDING for pidl=%08X count=%d."), pidl, _AsyncIconCount);
|
|
|
|
#ifdef MAX_ICON_WAIT
|
|
LONG wait;
|
|
|
|
// if this is the first async icon, setup to wait for the
|
|
// icon to finish extracting.
|
|
if (fCanWait && (_AsyncIconCount == 0)) {
|
|
if ((time - _AsyncIconTime) >= MIN_ICON_WAIT) {
|
|
_AsyncIconTime = time;
|
|
}
|
|
|
|
// query the time again in case SHGetIconFromPIDL went out to lunch
|
|
// in an icon handler.
|
|
time = GetTickCount();
|
|
|
|
wait = MAX_ICON_WAIT - (time - _AsyncIconTime);
|
|
|
|
if (_AsyncIconEvent == 0) {
|
|
_AsyncIconEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
|
|
if (_AsyncIconEvent == 0)
|
|
wait = 0;
|
|
} else if (wait > 0) {
|
|
ResetEvent(_AsyncIconEvent);
|
|
}
|
|
} else {
|
|
wait = 0;
|
|
}
|
|
#endif
|
|
|
|
IRunnableTask* pTask = NULL;
|
|
CDVGetIconTask* pIconTask;
|
|
hres = CDVGetIconTask_CreateInstance(this, pidl, &pTask, &pIconTask);
|
|
if (SUCCEEDED(hres)) {
|
|
InterlockedIncrement(&_AsyncIconCount);
|
|
|
|
BOOL fRes = DefView_IdleDoStuff(this, pTask, TOID_DVIconExtract, 0, TASK_PRIORITY_GET_ICON);
|
|
if (!fRes) {
|
|
InterlockedDecrement(&_AsyncIconCount);
|
|
ATOMICRELEASE(pTask);
|
|
flags &= ~GIL_ASYNC;
|
|
goto again;
|
|
}
|
|
} else {
|
|
DebugMsg(TF_ICON, TEXT("Failed to create CDVGetIconTask!"));
|
|
|
|
flags &= ~GIL_ASYNC;
|
|
goto again;
|
|
}
|
|
|
|
// currently we think we are Async...
|
|
hres = S_FALSE;
|
|
|
|
#ifdef MAX_ICON_WAIT
|
|
if (wait > 0) {
|
|
DebugMsg(TF_ICON, TEXT("Waiting for icon... base=%d time=%d wait=%d"), _AsyncIconTime, time, wait);
|
|
|
|
DWORD err = WaitForSingleObject(_AsyncIconEvent, wait);
|
|
if (err == WAIT_TIMEOUT) {
|
|
DebugMsg(TF_ICON, TEXT("**** timeout waiting for icon."));
|
|
} else if (err == WAIT_OBJECT_0) {
|
|
// The data in "paid" is valid, and we have a
|
|
// WM_DSV_UPDATEICON message in our queue.
|
|
|
|
// We can't remove the message from our queue,
|
|
// because that would allow SendNotifyEvents through
|
|
// and potentially destroy the very item we are
|
|
// processing. Instead we mark the "paid" strucure as "used", so when we get the message we can ignore it.
|
|
pIconTask->_fUsed = TRUE;
|
|
InterlockedDecrement(&_AsyncIconCount);
|
|
*piIcon = pIconTask->_iIcon;
|
|
|
|
hres = S_OK;
|
|
} else {
|
|
DebugMsg(TF_ICON, TEXT("error %d waiting for icon."), err);
|
|
}
|
|
}
|
|
#endif
|
|
ASSERT(pTask);
|
|
ATOMICRELEASE(pTask);
|
|
}
|
|
|
|
return hres;
|
|
}
|
|
|
|
|
|
HRESULT CDefView::_GetOverlayIndexAsync(LPCITEMIDLIST pidl, int iList)
|
|
{
|
|
IRunnableTask* pTask = NULL;
|
|
|
|
HRESULT hres = CDVIconOverlayTask_CreateInstance(this, pidl, iList, &pTask);
|
|
if (SUCCEEDED(hres)) {
|
|
BOOL fRes = DefView_IdleDoStuff(this, pTask, TOID_DVIconOverlay, 0, TASK_PRIORITY_GET_ICON);
|
|
ATOMICRELEASE(pTask);
|
|
}
|
|
|
|
return hres;
|
|
}
|
|
|
|
|
|
// Returns: if the cursor is over a listview item, its index; otherwise, -1.
|
|
int DV_HitTest(CDefView* pdsv, const POINT* ppt)
|
|
{
|
|
LV_HITTESTINFO info;
|
|
|
|
if (!DV_SHOWICONS(pdsv))
|
|
return -1;
|
|
|
|
info.pt = *ppt;
|
|
return ListView_HitTest(pdsv->_hwndListview, &info);
|
|
}
|
|
|
|
|
|
typedef void(*PFNGETINFOTIP)(LPVOID, LPTSTR, int);
|
|
|
|
|
|
void _AppendInfoTip(PFNGETINFOTIP pfn, LPVOID pv, NMLVGETINFOTIP* plvn)
|
|
{
|
|
BOOL fSuccess = FALSE;
|
|
|
|
// If the string is unfolded, then we display just the tip
|
|
// If the string is folded, append the tip to the name
|
|
int cchName = (plvn->dwFlags & LVGIT_UNFOLDED) ? 0 : lstrlen(plvn->pszText);
|
|
if (cchName) {
|
|
lstrcatn(plvn->pszText, TEXT("\r\n"), plvn->cchTextMax);
|
|
cchName = lstrlen(plvn->pszText);
|
|
}
|
|
|
|
// If there is room in the buffer after we added CRLF, append the
|
|
// infotip text. We succeeded if there was nontrivial infotip text.
|
|
if (cchName < plvn->cchTextMax) {
|
|
pfn(pv, plvn->pszText + cchName, plvn->cchTextMax - cchName);
|
|
fSuccess = plvn->pszText[cchName];
|
|
}
|
|
|
|
// If we didn't get an infotip, then null out the buffer so listview
|
|
// will fall back to the default "inplace tooltip" method.
|
|
if (!fSuccess)
|
|
plvn->pszText[0] = TEXT('\0');
|
|
}
|
|
|
|
|
|
void _AppendInfoTipFromString(LPVOID pv, LPTSTR pszBuf, int cch)
|
|
{
|
|
LPWSTR pwsz = (LPWSTR)pv;
|
|
SHUnicodeToTChar(pwsz, pszBuf, cch);
|
|
}
|
|
|
|
|
|
void _AppendInfoTipFromVariant(LPVOID pv, LPTSTR pszBuf, int cch)
|
|
{
|
|
VARIANT* pvar = (VARIANT*)pv;
|
|
VariantToStr(pvar, pszBuf, cch);
|
|
}
|
|
|
|
|
|
void CDefView::_OnGetInfoTip(NMLVGETINFOTIP* plvn)
|
|
{
|
|
if (!ShowInfoTip())
|
|
return;
|
|
|
|
LPITEMIDLIST pidl = _GetPIDL(plvn->iItem);
|
|
if (pidl) {
|
|
IQueryInfo* pqi;
|
|
LPCITEMIDLIST apidl[1] = {pidl};
|
|
|
|
if (SUCCEEDED(_pshf->GetUIObjectOf(NULL, 1, apidl, IID_IQueryInfo, NULL, (void**)&pqi))) {
|
|
WCHAR* pwszTip;
|
|
|
|
if (SUCCEEDED(pqi->GetInfoTip(0, &pwszTip)) && pwszTip) {
|
|
_AppendInfoTip(_AppendInfoTipFromString, pwszTip, plvn);
|
|
SHFree(pwszTip);
|
|
}
|
|
pqi->Release();
|
|
} else if (_pshf2) {
|
|
VARIANT var;
|
|
VariantInit(&var);
|
|
if (SUCCEEDED(_pshf2->GetDetailsEx(pidl, &SCID_Comment, &var))) {
|
|
_AppendInfoTip(_AppendInfoTipFromVariant, &var, plvn);
|
|
VariantClear(&var);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
HRESULT CDefView::_OnViewWindowActive()
|
|
{
|
|
IShellView* psv = _psvOuter ? _psvOuter : SAFECAST(this, IShellView*);
|
|
|
|
return _psb->OnViewWindowActive(psv);
|
|
}
|
|
|
|
|
|
#ifdef WINNT
|
|
|
|
// CLR_NONE is a special value that never matches a valid RGB
|
|
COLORREF g_crAltColor = CLR_NONE; // uninitialized magic value
|
|
|
|
DWORD GetAltColor()
|
|
{
|
|
// Fetch the alternate color (for compression) if supplied.
|
|
if (g_crAltColor == CLR_NONE) // initialized yet?
|
|
{
|
|
DWORD cbData = sizeof(COLORREF);
|
|
DWORD dwType;
|
|
HKEY hkey;
|
|
|
|
hkey = SHGetExplorerHkey(HKEY_CURRENT_USER, TRUE);
|
|
if (hkey) {
|
|
if (SHQueryValueEx(hkey, TEXT("AltColor"), NULL, &dwType, (LPBYTE)&g_crAltColor, &cbData) != ERROR_SUCCESS)
|
|
g_crAltColor = RGB(0, 0, 255); // default value
|
|
RegCloseKey(hkey);
|
|
}
|
|
}
|
|
|
|
return g_crAltColor;
|
|
}
|
|
#endif
|
|
|
|
|
|
LRESULT CDefView::_OnLVNotify(NM_LISTVIEW* plvn)
|
|
{
|
|
switch (plvn->hdr.code) {
|
|
case NM_KILLFOCUS:
|
|
// force update on inactive to not ruin save bits
|
|
DV_CDB_OnStateChange(this, CDBOSC_KILLFOCUS);
|
|
if (GetForegroundWindow() != _hwndMain)
|
|
UpdateWindow(_hwndListview);
|
|
_fHasListViewFocus = FALSE;
|
|
_EnableDisableTBButtons();
|
|
break;
|
|
case NM_SETFOCUS:
|
|
{
|
|
if (m_cFrame.IsWebView()) // Do OLE stuff
|
|
{
|
|
UIActivate(SVUIA_ACTIVATE_FOCUS);
|
|
} else {
|
|
// We should call IShellBrowser::OnViewWindowActive() before
|
|
// calling its InsertMenus().
|
|
_OnViewWindowActive();
|
|
DV_CDB_OnStateChange(this, CDBOSC_SETFOCUS);
|
|
OnActivate(SVUIA_ACTIVATE_FOCUS);
|
|
_FocusOnSomething();
|
|
DV_UpdateStatusBar(this, 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_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);
|
|
_ProcessDblClick((LPNMITEMACTIVATE)plvn);
|
|
}
|
|
break;
|
|
#ifdef WINNT
|
|
case NM_CUSTOMDRAW:
|
|
{
|
|
LPNMLVCUSTOMDRAW lpCD = (LPNMLVCUSTOMDRAW)plvn;
|
|
|
|
switch (lpCD->nmcd.dwDrawStage) {
|
|
case CDDS_PREPAINT:
|
|
if (_fShowCompColor) {
|
|
return CDRF_NOTIFYITEMDRAW;
|
|
} else {
|
|
return CDRF_DODEFAULT;
|
|
}
|
|
break;
|
|
case CDDS_ITEMPREPAINT:
|
|
{
|
|
LPCITEMIDLIST pidl = DV_GetPIDLParam(this, lpCD->nmcd.lItemlParam, (int)lpCD->nmcd.dwItemSpec);
|
|
if (pidl) {
|
|
DWORD uFlags = SFGAO_COMPRESSED;
|
|
HRESULT hres = _pshf->GetAttributesOf(1, &pidl, &uFlags);
|
|
|
|
if (SUCCEEDED(hres) && (uFlags & SFGAO_COMPRESSED)) {
|
|
lpCD->clrText = GetAltColor();
|
|
}
|
|
}
|
|
|
|
return CDRF_DODEFAULT;
|
|
}
|
|
}
|
|
}
|
|
return CDRF_DODEFAULT;
|
|
#endif
|
|
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:
|
|
// We only care about STATECHANGE messages
|
|
if (!(plvn->uChanged & LVIF_STATE)) {
|
|
// If the text is changed, we need to flush the cached
|
|
// selected item context menu. We'll leave the background menu
|
|
// around only if it is currently being displayed.
|
|
if (plvn->uChanged & LVIF_TEXT) {
|
|
ULONG dwFlush = DVFCMF_SEL; // always flush selitem cm.
|
|
|
|
// conditional fixes bug 157347; flushing
|
|
// _pcmBackground if the menu was being displayed would
|
|
// hose up submenus and create other side effects.
|
|
if (!_bContextMenuMode)
|
|
dwFlush |= DVFCMF_BACKGROUND;
|
|
|
|
DV_FlushCachedMenu(this, dwFlush);
|
|
}
|
|
break;
|
|
}
|
|
|
|
// 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) {
|
|
DV_CDB_OnStateChange(this, CDBOSC_SELCHANGE);
|
|
}
|
|
|
|
if ((plvn->uNewState ^ plvn->uOldState) & LVIS_SELECTED) {
|
|
this->OnLVSelectionChange(plvn);
|
|
}
|
|
}
|
|
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) {
|
|
// create NM_LISTVIEW so we can reuse OnLVSelectionChange
|
|
NM_LISTVIEW lvn = {0};
|
|
|
|
lvn.hdr = pnm->hdr;
|
|
lvn.iItem = -1; // say the change affected all items
|
|
lvn.uNewState = pnm->uNewState;
|
|
lvn.uOldState = pnm->uOldState;
|
|
this->OnLVSelectionChange(&lvn);
|
|
}
|
|
}
|
|
break;
|
|
case LVN_DELETEITEM:
|
|
this->OnListViewDelete(plvn->iItem, (LPITEMIDLIST)plvn->lParam);
|
|
DV_FlushCachedMenu(this);
|
|
break;
|
|
case LVN_COLUMNCLICK:
|
|
// allow clicking on columns to set the sort order
|
|
if (_pshf2 || _psd || this->HasCB()) {
|
|
UINT iRealColumn;
|
|
LPARAM lParamSort = _dvState.lParamSort;
|
|
LONG iLastColumnClick = _dvState.iLastColumnClick, iLastSortDirection = _dvState.iDirection; // push sort state
|
|
|
|
// Folder doesn't know which columns are on or off, so communication with folder uses real col #s
|
|
this->MapVisibleToRealColumn(plvn->iSubItem, &iRealColumn);
|
|
|
|
// toggle the direction of the sort if on the same column
|
|
if (_dvState.iLastColumnClick == (int)iRealColumn)
|
|
_dvState.iDirection = -_dvState.iDirection;
|
|
else
|
|
_dvState.iDirection = 1;
|
|
|
|
// 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 = this->Rearrange(iRealColumn);
|
|
|
|
// Allows iLastColumnClick to stay valid during the above calls
|
|
if (SUCCEEDED(hr))
|
|
_dvState.iLastColumnClick = iRealColumn;
|
|
else {
|
|
// We failed somewhere so pop the sort state.
|
|
_dvState.iDirection = iLastSortDirection;
|
|
_dvState.iLastColumnClick = (int)_dvState.lParamSort;
|
|
_dvState.lParamSort = lParamSort;
|
|
_SetSortArrows();
|
|
_dvState.iLastColumnClick = iLastColumnClick;
|
|
}
|
|
}
|
|
break;
|
|
case LVN_KEYDOWN:
|
|
this->HandleKeyDown(((LV_KEYDOWN*)plvn));
|
|
break;
|
|
#define plvdi ((LV_DISPINFO *)plvn)
|
|
|
|
case LVN_BEGINLABELEDIT:
|
|
{
|
|
LPCITEMIDLIST pidl = DV_GetPIDLParam(this, plvdi->item.lParam, plvdi->item.iItem);
|
|
ULONG rgfAttr = SFGAO_CANRENAME;
|
|
|
|
if (!pidl || FAILED(_pshf->GetAttributesOf(1, &pidl, &rgfAttr)) || !(rgfAttr & 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];
|
|
STRRET str;
|
|
if (SUCCEEDED(_pshf->GetDisplayNameOf(pidl, SHGDN_INFOLDER | SHGDN_FOREDITING, &str)) && SUCCEEDED(StrRetToBuf(&str, pidl, szName, ARRAYSIZE(szName)))) {
|
|
SetWindowText(hwndEdit, szName);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case LVN_ENDLABELEDIT:
|
|
_fInLabelEdit = FALSE;
|
|
if (plvdi->item.pszText) {
|
|
LPCITEMIDLIST pidl = DV_GetPIDLParam(this, plvdi->item.lParam, plvdi->item.iItem);
|
|
if (pidl) {
|
|
WCHAR wszName[MAX_PATH];
|
|
|
|
SHTCharToUnicode(plvdi->item.pszText, wszName, ARRAYSIZE(wszName));
|
|
|
|
// _pshf may need to talk back to us or use us to EnableModless for UI.
|
|
// FTP Needs this.
|
|
IUnknown_SetSite(_pshf, SAFECAST(this, IOleCommandTarget*));
|
|
if (SUCCEEDED(_pshf->SetNameOf(_hwndMain, pidl, wszName, SHGDN_INFOLDER, NULL))) {
|
|
SHChangeNotifyHandleEvents();
|
|
DV_CDB_OnStateChange(this, CDBOSC_RENAME);
|
|
} else
|
|
SendMessage(_hwndListview, LVM_EDITLABEL, plvdi->item.iItem, (LPARAM)plvdi->item.pszText);
|
|
|
|
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:
|
|
{
|
|
LV_ITEM item;
|
|
TCHAR szIconFile[MAX_PATH];
|
|
UINT uReal;
|
|
|
|
if (!(plvdi->item.mask & (LVIF_TEXT | LVIF_IMAGE)))
|
|
break;
|
|
|
|
LPCITEMIDLIST pidl = DV_GetPIDLParam(this, plvdi->item.lParam, plvdi->item.iItem);
|
|
if (!pidl)
|
|
break;
|
|
|
|
ASSERT(IsValidPIDL(pidl));
|
|
|
|
if (plvdi->item.iSubItem != 0) {
|
|
ASSERT(_fs.ViewMode == FVM_DETAILS);
|
|
this->MapVisibleToRealColumn(plvdi->item.iSubItem, &uReal);
|
|
} else
|
|
uReal = 0;
|
|
|
|
item.mask = plvdi->item.mask & (LVIF_TEXT | LVIF_IMAGE);
|
|
item.iItem = plvdi->item.iItem;
|
|
item.iSubItem = uReal; // We don't store the "visible" col#, but no-one needs it
|
|
item.iImage = plvdi->item.iImage = -1; // for iSubItem!=0 case
|
|
|
|
if (item.iSubItem == 0 && (item.mask & LVIF_IMAGE)) {
|
|
if (!_psio)
|
|
//try again
|
|
_pshf->QueryInterface(IID_IShellIconOverlay, (void**)&_psio);
|
|
|
|
// If the location supports the IShellIconOverlay than only need to ask for ghosted, else
|
|
// we need to do the old stuff...
|
|
DWORD uFlags = SFGAO_GHOSTED;
|
|
if (!_psio)
|
|
uFlags |= SFGAO_LINK | SFGAO_SHARE;
|
|
|
|
if (FAILED(_pshf->GetAttributesOf(1, &pidl, &uFlags))) {
|
|
uFlags = 0;
|
|
}
|
|
|
|
// 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 hres = _psio->GetOverlayIndex(pidl, &iOverlayIndex);
|
|
if (hres == E_PENDING)
|
|
_GetOverlayIndexAsync(pidl, item.iItem);
|
|
else if (SUCCEEDED(hres)) {
|
|
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
|
|
TIMESTART(_GetIcon);
|
|
|
|
if (_IsOwnerData()) {
|
|
CallCB(SFVM_GETITEMICONINDEX, plvdi->item.iItem, (LPARAM)&item.iImage);
|
|
}
|
|
|
|
if (item.iImage == -1)
|
|
_GetIconAsync(pidl, &item.iImage, TRUE);
|
|
TIMESTOP(_GetIcon);
|
|
|
|
plvdi->item.iImage = item.iImage;
|
|
}
|
|
|
|
// Note that TEXT must come after IMAGE, since we reuse
|
|
// szIconFile
|
|
if (item.mask & LVIF_TEXT) {
|
|
DETAILSINFO di;
|
|
|
|
if (plvdi->item.cchTextMax)
|
|
*plvdi->item.pszText = TEXT('\0');
|
|
|
|
di.str.uType = STRRET_CSTR;
|
|
di.str.cStr[0] = 0;
|
|
di.iImage = -1; // Assume for now no image...
|
|
|
|
// Note that we do something different for index 0 = NAME
|
|
if (item.iSubItem == 0) {
|
|
item.pszText = szIconFile;
|
|
item.cchTextMax = ARRAYSIZE(szIconFile);
|
|
|
|
TIMESTART(_GetName);
|
|
if (SUCCEEDED(_pshf->GetDisplayNameOf(
|
|
pidl, SHGDN_INFOLDER, &di.str))) {
|
|
#ifndef UNICODE // We can't do this on unicode (listview is expecting a ptr to a unicode string)
|
|
if (di.str.uType == STRRET_OFFSET) {
|
|
// If an offset, we just pass the name back
|
|
// but don't store it to save on space
|
|
|
|
// BUGBUG (DavePl) Note that this is someone's notify
|
|
// message, and we are placing a text pointer in it...
|
|
// a unicode text pointer out of a pidl, which is therefore
|
|
// not even aligned!
|
|
|
|
plvdi->item.pszText = (LPTSTR)(((LPBYTE)pidl) + di.str.uOffset);
|
|
item.mask &= ~LVIF_TEXT;
|
|
} else {
|
|
#endif
|
|
StrRetToBuf(&di.str, pidl, item.pszText, item.cchTextMax);
|
|
lstrcpyn(plvdi->item.pszText, item.pszText, plvdi->item.cchTextMax);
|
|
#ifndef UNICODE
|
|
}
|
|
#endif
|
|
}
|
|
TIMESTOP(_GetName);
|
|
} else {
|
|
DWORD dwState;
|
|
|
|
if (_pshf2 && SUCCEEDED(_pshf2->GetDefaultColumnState(item.iSubItem, &dwState))) {
|
|
if (dwState & SHCOLSTATE_SLOW) {
|
|
if (_bInSortCallBack)
|
|
plvdi->item.mask |= LVIF_DI_SETITEM;
|
|
else {
|
|
IRunnableTask* pTask;
|
|
|
|
if (SUCCEEDED(CDVExtendedColumnTask_CreateInstance(this, pidl, _fmt, item.iSubItem, &pTask))) {
|
|
if (DefView_IdleDoStuff(this, pTask, TOID_DVBackgroundEnum, 0, TASK_PRIORITY_BKGRND_FILL)) {
|
|
// 99/03/26 vtan: If the task was successfully created
|
|
// and scheduled then tell the list to set the text to
|
|
// nothing. This will prevent another call to
|
|
// ListView_GetItemText() from invoking us with
|
|
// LVN_GETDISPINFO while the the task is waiting to be
|
|
// completed in another thread.
|
|
|
|
ListView_SetItemText(_hwndListview, plvdi->item.iItem, plvdi->item.iSubItem, NULL);
|
|
ATOMICRELEASE(pTask);
|
|
// we set the plvdi->item.pszText to empty str already
|
|
break;
|
|
} else
|
|
TraceMsg(TF_WARNING, "dv_onlvn: couldn't add extended column task");
|
|
// else we couldn't add the task, so fall through
|
|
} else
|
|
TraceMsg(TF_WARNING, "dv_onlvn: couldn't create extended column task, falling through");
|
|
}
|
|
}
|
|
}
|
|
|
|
di.pidl = pidl;
|
|
di.fmt = _fmt;
|
|
|
|
if (SUCCEEDED(_GetDetailsHelper(item.iSubItem, &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;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// We only store the name; all other info comes on demand
|
|
if (item.iSubItem == 0) {
|
|
plvdi->item.mask |= LVIF_DI_SETITEM;
|
|
}
|
|
|
|
break;
|
|
} // LVN_GETDISPINFO
|
|
|
|
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 (this->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;
|
|
}
|
|
|
|
|
|
void CDefView::_PostEnumDoneMessage()
|
|
{
|
|
_fFileListEnumDone = TRUE;
|
|
if (m_cFrame.m_fReadyStateComplete) {
|
|
PostMessage(_hwndView, WM_DSV_FILELISTENUMDONE, 0, 0);
|
|
}
|
|
}
|
|
|
|
|
|
void CDefView::_PostSelChangedMessage()
|
|
{
|
|
if (!_fSelectionChangePending) {
|
|
PostMessage(_hwndView, WM_DSV_SENDSELECTIONCHANGED, 0, 0);
|
|
_fSelectionChangePending = TRUE;
|
|
}
|
|
}
|
|
|
|
|
|
// BUGBUG: Todo -- 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)
|
|
|
|
|
|
BOOL CDefView::_ShouldEnableButton(UINT uiCmd, DWORD dwAttr, int iIndex)
|
|
{
|
|
COMPILETIME_ASSERT(SIZEOF(c_BtnCmds) == SIZEOF(c_BtnAttr));
|
|
|
|
if (m_cFrame.IsSFVExtension()) {
|
|
// for sfv extensions we don't receive NM_SETFOCUS/NM_KILLFOCUS so
|
|
// we cannot correctly enable/disable buttons
|
|
// for now we just always leave them enabled
|
|
return TRUE;
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
if (!_fHasListViewFocus) {
|
|
// Disable any button we care about while listview is inactive.
|
|
return FALSE;
|
|
}
|
|
|
|
return BOOLIFY(dwAttr & dwBtnAttr);
|
|
}
|
|
|
|
|
|
/*
|
|
_GetCachedToolbarSelectionAttrs
|
|
|
|
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 hres = IUnknown_GetClassID(_pshf, &clsid);
|
|
if (SUCCEEDED(hres)) {
|
|
UINT iCount;
|
|
if (IsEqualGUID(CLSID_ShellFSFolder, clsid) && SUCCEEDED(GetSelectedCount(&iCount))) {
|
|
if (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_IExplorerToolbar, (void**)&piet))) {
|
|
ULONG dwAttr;
|
|
|
|
if (!_GetCachedToolbarSelectionAttrs(&dwAttr))
|
|
dwAttr = DefView_GetAttributesFromSelection(this, SFGAO_RELEVANT);
|
|
|
|
for (int i = 0; i < ARRAYSIZE(c_BtnCmds); i++) {
|
|
UINT uiState;
|
|
|
|
if (SUCCEEDED(piet->GetState(&_clsid, c_BtnCmds[i], &uiState))) {
|
|
if (_ShouldEnableButton(c_BtnCmds[i], dwAttr, i))
|
|
uiState |= TBSTATE_ENABLED;
|
|
else
|
|
uiState &= ~TBSTATE_ENABLED;
|
|
|
|
piet->SetState(&_clsid, c_BtnCmds[i], uiState);
|
|
}
|
|
}
|
|
|
|
_SetCachedToolbarSelectionAttrs(dwAttr);
|
|
|
|
piet->Release();
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// 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 screw select all and large selection perf.
|
|
|
|
// you can add expensive code to the WM_DSV_SENDSELECTIONCHANGED handler,
|
|
// that happens after all of the sel change notifies go through.
|
|
void CDefView::OnLVSelectionChange(NM_LISTVIEW* plvn)
|
|
{
|
|
// toss the cached context menu
|
|
DV_FlushCachedMenu(this, DVFCMF_SEL);
|
|
|
|
// Notify the dispach that the focus changed..
|
|
_PostSelChangedMessage();
|
|
|
|
// throw away cached attribute bits
|
|
_dwAttrSel = (DWORD)-1;
|
|
|
|
// Tell the defview client that the selection may have changed
|
|
SFVM_SELCHANGE_DATA dvsci;
|
|
|
|
dvsci.uNewState = plvn->uNewState;
|
|
dvsci.uOldState = plvn->uOldState;
|
|
dvsci.lParamItem = plvn->lParam;
|
|
|
|
CallCB(SFVM_SELCHANGE, MAKEWPARAM(SFVIDM_CLIENT_FIRST, plvn->iItem), (LPARAM)&dvsci);
|
|
}
|
|
|
|
|
|
#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)
|
|
DV_GetToolTipText(this, 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;
|
|
}
|
|
|
|
|
|
HRESULT CDefView::InitializeVariableColumns(DWORD* pdwColList)
|
|
{
|
|
#define MI_INITIAL_SIZE 6
|
|
DETAILSINFO di;
|
|
TCHAR szText[MAX_PATH];
|
|
UINT iReal;
|
|
#ifdef DEBUG
|
|
DWORD* pdwColOrig = pdwColList;
|
|
#endif
|
|
|
|
m_cColItems = 0;
|
|
|
|
if (!m_pColItems)
|
|
m_pColItems = (COL_INFO*)LocalAlloc(LPTR, MI_INITIAL_SIZE * sizeof(*m_pColItems));
|
|
|
|
if (!m_pColItems)
|
|
return E_OUTOFMEMORY;
|
|
|
|
for (iReal = 0;; iReal++) {
|
|
di.fmt = LVCFMT_LEFT;
|
|
di.cxChar = DEFAULT_NUMCHARS;
|
|
di.str.uType = (UINT)-1;
|
|
di.pidl = NULL;
|
|
|
|
if (FAILED(_GetDetailsHelper(iReal, &di)))
|
|
break;
|
|
|
|
ASSERT(iReal < 100);
|
|
|
|
StrRetToBuf(&di.str, NULL, szText, ARRAYSIZE(szText));
|
|
|
|
if (iReal >= MI_INITIAL_SIZE) {
|
|
COL_INFO* pmi = (COL_INFO*)LocalReAlloc(m_pColItems, (iReal + 1) * SIZEOF(m_pColItems[0]), LMEM_MOVEABLE | LMEM_ZEROINIT);
|
|
ASSERT(pmi);
|
|
if (!pmi) {
|
|
// Realloc failed. We need to bail out.
|
|
// Note: m_pColItems will be freed in the CDefView's destructor!
|
|
m_cColItems = iReal;
|
|
return E_OUTOFMEMORY; //No mem! Bail out!
|
|
}
|
|
m_pColItems = pmi;
|
|
}
|
|
lstrcpyn(m_pColItems[iReal].szName, szText, ARRAYSIZE(m_pColItems[iReal].szName));
|
|
m_pColItems[iReal].fmt = di.fmt;
|
|
m_pColItems[iReal].cChars = di.cxChar;
|
|
m_pColItems[iReal].csFlags = SHCOLSTATE_ONBYDEFAULT;
|
|
}
|
|
m_cColItems = iReal;
|
|
ASSERT(m_cColItems);
|
|
|
|
// first init default state from folder
|
|
if (_pshf2) {
|
|
for (iReal = 0; iReal < m_cColItems; iReal++) {
|
|
if (FAILED(_pshf2->GetDefaultColumnState(iReal, &m_pColItems[iReal].csFlags)))
|
|
m_pColItems[iReal].csFlags = SHCOLSTATE_ONBYDEFAULT;
|
|
}
|
|
}
|
|
// Set up saved column state only if the saved state
|
|
// contains information other than "nothing".
|
|
if ((pdwColList != NULL) && (*pdwColList != 0xFFFFFFFF)) {
|
|
|
|
// 99/02/05 vtan: If there is a saved column state then
|
|
// clear all the column "on" states to "off" and only
|
|
// display what columns are specified. Start at 1 so
|
|
// that name is always on.
|
|
|
|
for (iReal = 1; iReal < m_cColItems; iReal++)
|
|
m_pColItems[iReal].csFlags &= ~SHCOLSTATE_ONBYDEFAULT;
|
|
|
|
while ((*pdwColList != 0xFFFFFFFF) && SUCCEEDED(this->SetColumnState(*pdwColList, SHCOLSTATE_ONBYDEFAULT, SHCOLSTATE_ONBYDEFAULT))) {
|
|
pdwColList++;
|
|
}
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
BOOL CDefView::IsColumnHidden(UINT uCol)
|
|
{
|
|
ASSERT(_bLoadedColumns);
|
|
BOOL bRet = FALSE;
|
|
if (m_pColItems) {
|
|
bRet = (uCol < m_cColItems) && (m_pColItems[uCol].csFlags & SHCOLSTATE_HIDDEN) ? TRUE : FALSE;
|
|
}
|
|
|
|
return bRet;
|
|
}
|
|
|
|
|
|
BOOL CDefView::IsColumnOn(UINT uCol)
|
|
{
|
|
ASSERT(_bLoadedColumns);
|
|
BOOL bRet = FALSE;
|
|
if (m_pColItems) {
|
|
bRet = (uCol < m_cColItems) && (m_pColItems[uCol].csFlags & SHCOLSTATE_ONBYDEFAULT) ? TRUE : FALSE;
|
|
}
|
|
|
|
return bRet;
|
|
}
|
|
|
|
|
|
#define COL_CM_MAXITEMS 10 // how many item show up in context menu before more ... is inserted
|
|
|
|
|
|
HRESULT CDefView::AddColumnsToMenu(HMENU hm, DWORD dwBase)
|
|
{
|
|
BOOL bNeedMoreMenu = FALSE;
|
|
HRESULT hres = E_FAIL;
|
|
|
|
if (m_pColItems) {
|
|
AppendMenu(hm, MF_STRING | MF_CHECKED | MF_GRAYED, dwBase, m_pColItems[0].szName);
|
|
for (UINT i = 1; i < min(COL_CM_MAXITEMS, m_cColItems); i++) {
|
|
if (!(m_pColItems[i].csFlags & SHCOLSTATE_HIDDEN)) {
|
|
if (m_pColItems[i].csFlags & SHCOLSTATE_SECONDARYUI)
|
|
bNeedMoreMenu = TRUE;
|
|
else
|
|
AppendMenu(hm, MF_STRING | (m_pColItems[i].csFlags & SHCOLSTATE_ONBYDEFAULT) ? MF_CHECKED : 0, dwBase + i, m_pColItems[i].szName);
|
|
}
|
|
}
|
|
|
|
if (bNeedMoreMenu || (m_cColItems > COL_CM_MAXITEMS)) {
|
|
TCHAR szMore[MAX_PATH];
|
|
LoadString(HINST_THISDLL, IDS_COL_CM_MORE, szMore, SIZEOF(szMore));
|
|
AppendMenu(hm, MF_SEPARATOR, 0, NULL);
|
|
AppendMenu(hm, MF_STRING, SFVIDM_VIEW_COLSETTINGS, szMore);
|
|
}
|
|
hres = S_OK;
|
|
}
|
|
|
|
return hres;
|
|
}
|
|
|
|
|
|
HRESULT CDefView::SetColumnState(UINT uCol, DWORD dwMask, DWORD dwNewBits)
|
|
{
|
|
HRESULT hres = E_FAIL;
|
|
if (uCol >= m_cColItems)
|
|
return E_INVALIDARG;
|
|
|
|
if (m_pColItems) {
|
|
m_pColItems[uCol].csFlags = (m_pColItems[uCol].csFlags & ~dwMask) | (dwNewBits & dwMask);
|
|
hres = S_OK;
|
|
}
|
|
|
|
return hres;
|
|
}
|
|
|
|
|
|
HRESULT CDefView::MapRealToVisibleColumn(UINT uRealCol, UINT* puVisCol)
|
|
{
|
|
ASSERT(_bLoadedColumns);
|
|
HRESULT hres = E_FAIL;
|
|
*puVisCol = 0;
|
|
if (m_pColItems) {
|
|
if (m_cColItems > 0) {
|
|
if (uRealCol >= m_cColItems)
|
|
uRealCol = m_cColItems - 1;
|
|
|
|
for (UINT i = 0; i <= uRealCol; i++) {
|
|
if (m_pColItems[i].csFlags & SHCOLSTATE_ONBYDEFAULT)
|
|
(*puVisCol)++;
|
|
}
|
|
|
|
if (*puVisCol > 0)
|
|
(*puVisCol)--; // 0 based
|
|
}
|
|
hres = S_OK;
|
|
// should probably return S_FALSE if the real column is not currently visible (we return next greater visible)
|
|
}
|
|
|
|
return hres;
|
|
}
|
|
|
|
|
|
HRESULT CDefView::MapVisibleToRealColumn(UINT uVisCol, UINT* puReal)
|
|
{
|
|
ASSERT(_bLoadedColumns);
|
|
HRESULT hres = E_FAIL;
|
|
*puReal = 0;
|
|
if (m_pColItems) {
|
|
for (UINT i = 0;;) {
|
|
if (*puReal >= m_cColItems)
|
|
return E_FAIL;
|
|
if (m_pColItems[*puReal].csFlags & SHCOLSTATE_ONBYDEFAULT)
|
|
i++;
|
|
if (i > uVisCol)
|
|
break;
|
|
(*puReal)++;
|
|
}
|
|
|
|
hres = S_OK;
|
|
}
|
|
|
|
return hres;
|
|
}
|
|
|
|
|
|
BOOL CDefView::_IsExtendedColumn(INT_PTR iReal, DWORD* pdwState)
|
|
{
|
|
DWORD dwState = 0;
|
|
BOOL bRet = (_pshf2 && SUCCEEDED(_pshf2->GetDefaultColumnState((int)iReal, &dwState)) && (dwState & SHCOLSTATE_EXTENDED));
|
|
if (pdwState)
|
|
*pdwState = dwState;
|
|
|
|
return bRet;
|
|
}
|
|
|
|
|
|
UINT CDefView::GetMaxColumns()
|
|
{
|
|
return m_cColItems;
|
|
}
|
|
|
|
|
|
// uCol is a real column number, not visible column number
|
|
BOOL CDefView::_HandleColumnToggle(UINT uCol, BOOL bRefresh)
|
|
{
|
|
UINT uColVis, uColVisOld;
|
|
BOOL fWasOn = IsColumnOn(uCol); // if its off now, we are adding it
|
|
|
|
MapRealToVisibleColumn(uCol, &uColVisOld);
|
|
|
|
SetColumnState(uCol, SHCOLSTATE_ONBYDEFAULT, fWasOn ? 0 : SHCOLSTATE_ONBYDEFAULT);
|
|
|
|
MapRealToVisibleColumn(uCol, &uColVis);
|
|
if (!fWasOn) {
|
|
LV_COLUMN col;
|
|
|
|
// Adding a column
|
|
|
|
col.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;
|
|
col.fmt = m_pColItems[uCol].fmt;
|
|
col.cx = m_pColItems[uCol].cChars * _cxChar; // Use default width
|
|
col.pszText = m_pColItems[uCol].szName;
|
|
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;
|
|
}
|
|
|
|
ListView_InsertColumn(_hwndListview, uColVis, &col);
|
|
|
|
// now add it to our DSA
|
|
_pcp->AppendColumn(uColVis, (USHORT)col.cx, uColVis);
|
|
|
|
m_uCols++;
|
|
} else {
|
|
_pcp->RemoveColumn(uColVisOld);
|
|
ListView_DeleteColumn(_hwndListview, uColVisOld);
|
|
m_uCols--;
|
|
|
|
if (_dvState.lParamSort == (int)uCol) {
|
|
UINT iNewVis;
|
|
MapVisibleToRealColumn(uColVisOld, &iNewVis);
|
|
Rearrange(iNewVis);
|
|
}
|
|
}
|
|
|
|
if (bRefresh) {
|
|
ListView_RedrawItems(_hwndListview, 0, 0x7fff);
|
|
InvalidateRect(_hwndListview, NULL, TRUE);
|
|
UpdateWindow(_hwndListview);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
void CDefView::_SetSortArrows(void)
|
|
{
|
|
int iCol;
|
|
int iColLast;
|
|
HWND hwndHead = ListView_GetHeader(_hwndListview);
|
|
|
|
// 99/06/09 #340624 vtan: Only put the sort arrows for listviews which are not owner data.
|
|
if (!hwndHead || _IsOwnerData())
|
|
return;
|
|
|
|
MapRealToVisibleColumn((UINT)_dvState.lParamSort, (UINT*)&iCol);
|
|
MapRealToVisibleColumn(_dvState.iLastColumnClick, (UINT*)&iColLast);
|
|
|
|
HBITMAP hbm = (HBITMAP)LoadImage(HINST_THISDLL, MAKEINTRESOURCE((_dvState.iDirection > 0) ? IDB_SORT_UP : IDB_SORT_DN), IMAGE_BITMAP, 0, 0, LR_LOADMAP3DCOLORS);
|
|
HDITEM hdi1 = {HDI_BITMAP | HDI_FORMAT, 0, NULL, NULL, 0, 0, 0, 0, 0};
|
|
HDITEM hdi2 = {HDI_BITMAP | HDI_FORMAT, 0, NULL, hbm, 0, HDF_BITMAP | HDF_BITMAP_ON_RIGHT, 0, 0, 0};
|
|
|
|
// 99/04/16 #326158 vtan: Dump each HBITMAP that already exists in the
|
|
// header. This prevents the GDI Handle leak as the Header control does not take care of this for us.
|
|
Header_GetItem(hwndHead, iCol, &hdi1);
|
|
hdi2.fmt |= hdi1.fmt;
|
|
if (hdi1.hbm != NULL)
|
|
TBOOL(DeleteObject(hdi1.hbm));
|
|
Header_SetItem(hwndHead, iCol, &hdi2);
|
|
|
|
if (iColLast != iCol && iColLast != -1) {
|
|
HDITEM hdi1 = {HDI_BITMAP | HDI_FORMAT, 0, NULL, NULL, 0, 0, 0, 0, 0};
|
|
Header_GetItem(hwndHead, iColLast, &hdi1);
|
|
hdi1.fmt &= ~(HDF_BITMAP | HDF_BITMAP_ON_RIGHT);
|
|
if (hdi1.hbm != NULL) {
|
|
TBOOL(DeleteObject(hdi1.hbm));
|
|
hdi1.hbm = NULL;
|
|
}
|
|
Header_SetItem(hwndHead, iColLast, &hdi1);
|
|
}
|
|
}
|
|
|
|
|
|
PFNDPACOMPARE CDefView::_GetCompareFunction(void)
|
|
{
|
|
if (_pshf2 == NULL)
|
|
return(_Compare);
|
|
else
|
|
return(_CompareExact);
|
|
}
|
|
|
|
|
|
// 99/05/13 vtan: Only use CDefView::_CompareExact if you know that
|
|
// IShellFolder2 is implemented. SHCIDS_ALLFIELDS is IShellFolder2
|
|
// specific. Use CDefView::_GetCompareFunction() to get the function
|
|
// to pass to DPA_Sort() if you don't want to make this determination.
|
|
|
|
// p1 and p2 are pointers to the lv_item's LPARAM, which is currently the pidl
|
|
int CALLBACK CDefView::_CompareExact(void* p1, void* p2, LPARAM lParam)
|
|
{
|
|
CDefView* thisObject = (CDefView*)lParam;
|
|
HRESULT hres = thisObject->_pshf2->CompareIDs(thisObject->_dvState.lParamSort | SHCIDS_ALLFIELDS, (LPITEMIDLIST)p1, (LPITEMIDLIST)p2);
|
|
|
|
// 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?) 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.
|
|
if (FAILED(hres)) {
|
|
DVSAVESTATE saveState;
|
|
|
|
thisObject->_GetSortDefaults(&saveState);
|
|
hres = thisObject->_pshf2->CompareIDs(saveState.lParamSort | SHCIDS_ALLFIELDS, (LPITEMIDLIST)p1, (LPITEMIDLIST)p2);
|
|
if (FAILED(hres)) {
|
|
hres = thisObject->_pshf2->CompareIDs(SHCIDS_ALLFIELDS, (LPITEMIDLIST)p1, (LPITEMIDLIST)p2);
|
|
}
|
|
}
|
|
|
|
ASSERT(SUCCEEDED(hres))
|
|
ASSERT(thisObject->_dvState.iDirection != 0);
|
|
return (short)HRESULT_CODE(hres) * thisObject->_dvState.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* thisObject = (CDefView*)lParam;
|
|
HRESULT hres = thisObject->_pshf->CompareIDs(thisObject->_dvState.lParamSort, (LPITEMIDLIST)p1, (LPITEMIDLIST)p2);
|
|
if (FAILED(hres)) {
|
|
DVSAVESTATE saveState;
|
|
|
|
thisObject->_GetSortDefaults(&saveState);
|
|
hres = thisObject->_pshf->CompareIDs(saveState.lParamSort, (LPITEMIDLIST)p1, (LPITEMIDLIST)p2);
|
|
if (FAILED(hres)) {
|
|
hres = thisObject->_pshf->CompareIDs(0, (LPITEMIDLIST)p1, (LPITEMIDLIST)p2);
|
|
}
|
|
}
|
|
|
|
ASSERT(SUCCEEDED(hres))
|
|
ASSERT(thisObject->_dvState.iDirection != 0);
|
|
return (short)HRESULT_CODE(hres) * thisObject->_dvState.iDirection;
|
|
}
|
|
|
|
|
|
// dw1 and dw2 are the actual item index. thisObject index is only valid during the sort
|
|
int CALLBACK CDefView::_CompareExtended(LPARAM dw1, LPARAM dw2, LPARAM lParam)
|
|
{
|
|
CDefView* thisObject = (CDefView*)lParam;
|
|
|
|
// First, match filesystem and put folders in front of files:
|
|
LPCITEMIDLIST pidl1 = thisObject->_GetPIDL((int)dw1);
|
|
LPCITEMIDLIST pidl2 = thisObject->_GetPIDL((int)dw2);
|
|
|
|
// 99/03/25 #295631 vtan: Do an IShellFolder2::CompareIDs.
|
|
// Make sure it's IShellFolder2 and not IShellFolder.
|
|
if (thisObject->_pshf2 != NULL) {
|
|
HRESULT hres;
|
|
|
|
hres = thisObject->_pshf2->CompareIDs(thisObject->_dvState.lParamSort, pidl1, pidl2);
|
|
if (SUCCEEDED(hres))
|
|
return(static_cast<short>(HRESULT_CODE(hres))* thisObject->_dvState.iDirection);
|
|
}
|
|
|
|
DWORD uFlags1 = SFGAO_FOLDER;
|
|
DWORD uFlags2 = SFGAO_FOLDER;
|
|
thisObject->_pshf->GetAttributesOf(1, &pidl1, &uFlags1);
|
|
thisObject->_pshf->GetAttributesOf(1, &pidl2, &uFlags2);
|
|
// if they are different
|
|
if ((uFlags1 & SFGAO_FOLDER) && !(uFlags2 & SFGAO_FOLDER))
|
|
return thisObject->_dvState.iDirection;
|
|
else if (!(uFlags1 & SFGAO_FOLDER) && (uFlags2 & SFGAO_FOLDER))
|
|
return -(thisObject->_dvState.iDirection);
|
|
|
|
// Now that we're dealing with the same thing (folder-folder or file-file),
|
|
// we can check what the extended column has to say about our order:
|
|
TCHAR szText1[MAX_PATH], szText2[MAX_PATH];
|
|
UINT iVisCol;
|
|
|
|
thisObject->MapRealToVisibleColumn((UINT)thisObject->_dvState.lParamSort, &iVisCol);
|
|
|
|
ListView_GetItemText(thisObject->_hwndListview, dw1, iVisCol, szText1, ARRAYSIZE(szText1));
|
|
ListView_GetItemText(thisObject->_hwndListview, dw2, iVisCol, szText2, ARRAYSIZE(szText2));
|
|
|
|
ASSERT(thisObject->_dvState.iDirection != 0);
|
|
if (thisObject->_LastSortColType == SHCOLSTATE_TYPE_STR)
|
|
return lstrcmpi(szText1, szText2) * thisObject->_dvState.iDirection;
|
|
else {
|
|
int i1 = StrToInt(szText1);
|
|
int i2 = StrToInt(szText2);
|
|
|
|
return (i1 - i2) * thisObject->_dvState.iDirection;
|
|
}
|
|
}
|
|
|
|
|
|
BOOL CDefView::_InternalRearrange(void)
|
|
{
|
|
DWORD dwState = 0;
|
|
BOOL bRet = FALSE;
|
|
|
|
TIMEVAR(_DV_ReArrange);
|
|
TIMEIN(_DV_ReArrange);
|
|
|
|
TIMESTART(_DV_ReArrange);
|
|
|
|
// 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.
|
|
if (_IsExtendedColumn(_dvState.lParamSort, &dwState)) {
|
|
if (_GetBackgroundTaskCount(TOID_DVBackgroundEnum) > 0)
|
|
MessageBeep(MB_OK);
|
|
else {
|
|
// 99/04/09 #287528 vtan: If browse in same window is on then it's
|
|
// possible that the same window is being reused and the sort by will
|
|
// be on an extended column but the column will not be loaded. To
|
|
// prevent this use the default state to do the sort for the initial time only.
|
|
if (!_bLoadedColumns) {
|
|
int iOldLastColumnClick, iOldDirection;
|
|
LPARAM lOldParamSort;
|
|
SHELLSTATE ss;
|
|
|
|
lOldParamSort = _dvState.lParamSort;
|
|
iOldLastColumnClick = _dvState.iLastColumnClick;
|
|
iOldDirection = _dvState.iDirection;
|
|
SHGetSetSettings(&ss, SSF_SORTCOLUMNS, FALSE); // get default sort column.
|
|
_dvState.lParamSort = _dvState.iLastColumnClick = (int)ss.lParamSort;
|
|
_dvState.iDirection = ss.iSortDirection ? ss.iSortDirection : 1;
|
|
bRet = ListView_SortItems(_hwndListview, _Compare, (LPARAM)this);
|
|
_dvState.lParamSort = lOldParamSort;
|
|
_dvState.iLastColumnClick = iOldLastColumnClick;
|
|
_dvState.iDirection = iOldDirection;
|
|
} else {
|
|
// Sort on the extended column
|
|
_LastSortColType = dwState & SHCOLSTATE_TYPEMASK;
|
|
// The _bInSortCallBack flag tells the LVN_GETDISPINFO routine to return the data
|
|
// (as opposed to kicking off a thread), and tell listview to store it so it won't ask again.
|
|
_bInSortCallBack = TRUE;
|
|
bRet = ListView_SortItemsEx(_hwndListview, _CompareExtended, (LPARAM)this);
|
|
_bInSortCallBack = FALSE;
|
|
}
|
|
}
|
|
} else {
|
|
// dont send a LVM_SORTITEMS to an ownerdraw or comctl32 will rip
|
|
if (!_IsOwnerData()) {
|
|
bRet = ListView_SortItems(_hwndListview, _Compare, (LPARAM)this);
|
|
}
|
|
}
|
|
|
|
// reset to the state that no items have been moved if currently in a positioned mode
|
|
// so auto-arraning works.
|
|
if (DV_ISANYICONMODE(_fs.ViewMode))
|
|
_bItemsMoved = FALSE;
|
|
|
|
TIMESTOP(_DV_ReArrange);
|
|
TIMEOUT(_DV_ReArrange);
|
|
return bRet;
|
|
}
|
|
|
|
|
|
int CALLBACK CDefView::_DVITEM_Compare(void* p1, void* p2, LPARAM lParam)
|
|
{
|
|
CDefView* thisObject = reinterpret_cast<CDefView*>(lParam);
|
|
|
|
UNALIGNED DVITEM* pdvi1 = (UNALIGNED DVITEM*)p1;
|
|
UNALIGNED DVITEM* pdvi2 = (UNALIGNED DVITEM*)p2;
|
|
LPITEMIDLIST pFakeEnd1, pFakeEnd2;
|
|
USHORT uSave1, uSave2;
|
|
int nCmp;
|
|
|
|
// BUGBUG: Note that all this would be unnecessary if
|
|
// IShellFolder::CompareIDs took a bRecurse flag
|
|
pFakeEnd1 = _ILNext(&pdvi1->idl);
|
|
uSave1 = pFakeEnd1->mkid.cb;
|
|
pFakeEnd1->mkid.cb = 0;
|
|
|
|
pFakeEnd2 = _ILNext(&pdvi2->idl);
|
|
uSave2 = pFakeEnd2->mkid.cb;
|
|
pFakeEnd2->mkid.cb = 0;
|
|
|
|
nCmp = _Compare(&pdvi1->idl, &pdvi2->idl, reinterpret_cast<LPARAM>(thisObject));
|
|
|
|
pFakeEnd2->mkid.cb = uSave2;
|
|
pFakeEnd1->mkid.cb = uSave1;
|
|
|
|
return(nCmp);
|
|
}
|
|
|
|
|
|
// This returns TRUE if everything went OK, FALSE otherwise
|
|
// Side effect: the listview items are always arranged after this call
|
|
BOOL CDefView::_RestorePos(PDVSAVEHEADER pSaveHeader, UINT uLen)
|
|
{
|
|
UNALIGNED DVITEM* pDVItem, * pDVEnd;
|
|
HDPA dpaItems;
|
|
UNALIGNED DVITEM* UNALIGNED* ppDVItem, * UNALIGNED* ppEndDVItems;
|
|
BOOL bOK = FALSE;
|
|
int iCount, i;
|
|
DWORD dwStyle = GetWindowStyle(_hwndListview);
|
|
|
|
// Do the specified sorting for both the ListView and the DPA,
|
|
// so we can traverse them both in order (like the merge step of a
|
|
// merge sort), which should be pretty quick
|
|
|
|
// 99/04/07 #287528 vtan: unless it's an extended column which by
|
|
// definition is slow. The text for this is gathered using a DefView
|
|
// task. There was a check for this here which is now removed.
|
|
_SetSortArrows();
|
|
_InternalRearrange();
|
|
|
|
#if 0
|
|
// could hook this up if you think it is worth skipping this expensive code when its not necessary
|
|
// If you do, investigate how long pSaveHeader stays around for
|
|
if (((dwStyle & LVS_TYPEMASK) == LVS_REPORT) || ((dwStyle & LVS_TYPEMASK) == LVS_LIST)) {
|
|
// no point in arranging everything...
|
|
// we need to set a flag to do this later..
|
|
return TRUE;
|
|
}
|
|
#endif
|
|
pDVItem = (UNALIGNED DVITEM*)(((LPBYTE)pSaveHeader) + pSaveHeader->cbPosOffset);
|
|
|
|
// BUGBUG more runtime size checking, should be init in case you don't get
|
|
// here the day you happen to break its validity (DavePl)
|
|
ASSERT(SIZEOF(DVSAVEHEADER) >= SIZEOF(DVITEM));
|
|
|
|
pDVEnd = (UNALIGNED DVITEM*)(((LPBYTE)pSaveHeader) + uLen - SIZEOF(DVITEM));
|
|
|
|
// Grow every 16 items
|
|
dpaItems = DPA_Create(16);
|
|
if (!dpaItems) {
|
|
return bOK;
|
|
}
|
|
|
|
for (;; pDVItem = (UNALIGNED DVITEM*)_ILNext(&pDVItem->idl)) {
|
|
if (pDVItem > pDVEnd) {
|
|
// Invalid list
|
|
break;
|
|
}
|
|
|
|
// End normally when we reach a NULL IDList
|
|
if (pDVItem->idl.mkid.cb == 0) {
|
|
break;
|
|
}
|
|
|
|
if (DPA_AppendPtr(dpaItems, pDVItem) < 0) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!DPA_Sort(dpaItems, _DVITEM_Compare, (LPARAM)this)) {
|
|
goto Error1;
|
|
}
|
|
|
|
ppDVItem = (UNALIGNED DVITEM * UNALIGNED*)DPA_GetPtrPtr(dpaItems);
|
|
ppEndDVItems = ppDVItem + DPA_GetPtrCount(dpaItems);
|
|
|
|
// Turn off auto-arrange if it's on at the mo.
|
|
if (dwStyle & LVS_AUTOARRANGE)
|
|
SetWindowLong(_hwndListview, GWL_STYLE, dwStyle & ~LVS_AUTOARRANGE);
|
|
|
|
iCount = ListView_GetItemCount(_hwndListview);
|
|
for (i = 0; i < iCount; ++i) {
|
|
LPITEMIDLIST pidl = _GetPIDL(i);
|
|
|
|
// need to check for pidl because this could be on a background
|
|
// thread and an fsnotify could be coming through to blow it away
|
|
for (; pidl;) {
|
|
int nCmp;
|
|
LPITEMIDLIST pFakeEnd;
|
|
USHORT uSave;
|
|
|
|
if (ppDVItem < ppEndDVItems) {
|
|
// We terminate the IDList manually after saving
|
|
// the needed information. Note we will not GP fault
|
|
// since we added sizeof(ITEMIDLIST) onto the Alloc
|
|
pFakeEnd = _ILNext(&(*ppDVItem)->idl);
|
|
uSave = pFakeEnd->mkid.cb;
|
|
pFakeEnd->mkid.cb = 0;
|
|
|
|
nCmp = _Compare(&((*ppDVItem)->idl), pidl, (LPARAM)this);
|
|
|
|
pFakeEnd->mkid.cb = uSave;
|
|
} else {
|
|
// do this by default. this prevents overlap of icons
|
|
|
|
// i.e. if we've run out of saved positions information,
|
|
// we need to just loop through and set all remaining items
|
|
// to position 0x7FFFFFFFF so that when it's really shown,
|
|
// the listview will pick a new (unoccupied) spot.
|
|
// breaking out now would leave it were the _InternalRearrange
|
|
// put it, but another item with saved state info could
|
|
// have come and be placed on top of it.
|
|
nCmp = 1;
|
|
}
|
|
|
|
if (nCmp > 0) {
|
|
// We did not find the item
|
|
// reset it's position to be recomputed
|
|
ListView_SetItemPosition32(_hwndListview, i, 0x7FFFFFFF, 0x7FFFFFFF);
|
|
break;
|
|
} else if (nCmp == 0) {
|
|
UNALIGNED DVITEM* pDVItem = *ppDVItem;
|
|
|
|
// They are equal
|
|
ListView_SetItemPosition32(_hwndListview, i, pDVItem->pt.x, pDVItem->pt.y);
|
|
// Don't check this one again
|
|
++ppDVItem;
|
|
break;
|
|
}
|
|
|
|
// It's less than the current item, so try the next one
|
|
++ppDVItem;
|
|
}
|
|
}
|
|
|
|
// Turn auto-arrange back on if needed...
|
|
if (dwStyle & LVS_AUTOARRANGE)
|
|
SetWindowLong(_hwndListview, GWL_STYLE, dwStyle);
|
|
|
|
bOK = TRUE;
|
|
|
|
if (DPA_GetPtrCount(dpaItems) > 0) {
|
|
// If we read in any icon positions, we should save them later
|
|
// unless the user does something to cause us to go back to
|
|
// the default state
|
|
_bItemsMoved = TRUE;
|
|
}
|
|
|
|
Error1:;
|
|
DPA_Destroy(dpaItems);
|
|
return(bOK);
|
|
}
|
|
|
|
|
|
// Save (and check) column header information
|
|
// Returns TRUE if the columns are the default width, FALSE otherwise
|
|
// Side effect: the stream pointer is left right after the last column
|
|
// EVEN when on default width!
|
|
BOOL CDefView::SaveCols(IStream* pstm)
|
|
{
|
|
BOOL bDefaultCols = FALSE;
|
|
IStream* pstmCols = NULL;
|
|
|
|
if (!_psd && !_pshf2 && !this->HasCB())
|
|
return TRUE;
|
|
|
|
if (SUCCEEDED(CallCB(SFVM_GETCOLSAVESTREAM, STGM_WRITE, (LPARAM)&pstmCols))) {
|
|
// note: this case typically happens on non-FS folders
|
|
TraceMsg(TF_DEFVIEW, "dv::SaveCols got view-specific save stream");
|
|
// kinda hard to get the columns in a non-default state without loading them :)
|
|
if (!_bLoadedColumns) {
|
|
pstmCols->Release();
|
|
return TRUE;
|
|
}
|
|
pstm = pstmCols;
|
|
} else {
|
|
if (!pstm)
|
|
return TRUE; // Bail out say default...
|
|
}
|
|
|
|
// should we set _bLoadedColumns?
|
|
if (!_pcp && _pSaveHeader) {
|
|
LPBYTE pColHdr;
|
|
_pSaveHeader->GetColumnsInfo(&pColHdr);
|
|
|
|
_pcp = new CColumnPointer(pColHdr, NULL); // pass NULL instead of this, to avoid calling IVC
|
|
}
|
|
|
|
if (!_pcp) {
|
|
if (pstmCols)
|
|
pstmCols->Release();
|
|
return TRUE;
|
|
}
|
|
|
|
// Make sure we have stored the current widths
|
|
if (!_pcp->SaveColumnWidths(_hwndListview))
|
|
bDefaultCols = FALSE;
|
|
|
|
if (!_pcp->SaveColumnOrder(_hwndListview)) {
|
|
// Make sure to save state if the column order changed
|
|
bDefaultCols = FALSE;
|
|
}
|
|
|
|
if (FAILED(_pcp->Write(pstm, this))) {
|
|
// There is some problem, so just assume
|
|
// default column widths
|
|
bDefaultCols = TRUE;
|
|
}
|
|
|
|
if (pstmCols) {
|
|
ATOMICRELEASE(pstmCols);
|
|
|
|
// Always pretend we got default columns
|
|
return TRUE;
|
|
}
|
|
|
|
TraceMsg(TF_DEFVIEW, "dv::SaveCols returning %s", bDefaultCols ? TEXT("TRUE") : TEXT("FALSE"));
|
|
|
|
return bDefaultCols;
|
|
}
|
|
|
|
|
|
// Save (and check) icon position information
|
|
// Returns S_OK if the positions are saved, S_FALSE if we don't need to save,
|
|
// E_FAIL on error.
|
|
// Side effect: the stream pointer is left right after the last icon
|
|
HRESULT CDefView::SavePos(IStream* pstm)
|
|
{
|
|
int iCount, i;
|
|
DVITEM dvitem;
|
|
HRESULT hres;
|
|
|
|
if (!_bItemsMoved || !DV_ISANYICONMODE(_fs.ViewMode) || !_HasNormalView())
|
|
return S_FALSE;
|
|
|
|
iCount = ListView_GetItemCount(_hwndListview);
|
|
|
|
for (i = 0;; i++) {
|
|
if (i >= iCount) {
|
|
hres = S_OK;
|
|
break;
|
|
}
|
|
|
|
ListView_GetItemPosition(_hwndListview, i, &dvitem.pt);
|
|
|
|
hres = pstm->Write(&dvitem.pt, SIZEOF(dvitem.pt), NULL);
|
|
if (FAILED(hres))
|
|
break;
|
|
|
|
LPITEMIDLIST pidl = _GetPIDL(i);
|
|
if (pidl)
|
|
hres = pstm->Write(pidl, pidl->mkid.cb, NULL);
|
|
else
|
|
hres = E_FAIL;
|
|
|
|
if (FAILED(hres))
|
|
break;
|
|
}
|
|
|
|
if (SUCCEEDED(hres)) {
|
|
// Terminate the list with a NULL IDList
|
|
dvitem.idl.mkid.cb = 0;
|
|
hres = pstm->Write(&dvitem, SIZEOF(dvitem), NULL);
|
|
}
|
|
|
|
return hres;
|
|
}
|
|
|
|
|
|
// this should NOT check for whether the item is already in the listview
|
|
// if it does, we'll have some serious performance problems
|
|
int DefView_AddObject(CDefView* pdsv, LPITEMIDLIST pidl, BOOL bCopy = FALSE)
|
|
{
|
|
int i;
|
|
LV_ITEM item;
|
|
|
|
TIMESTART(pdsv->_AddObject);
|
|
|
|
// Check the commdlg hook to see if we should include this
|
|
// object.
|
|
if (DV_CDB_IncludeObject(pdsv, pidl) != S_OK) {
|
|
TIMESTOP(pdsv->_AddObject);
|
|
return -1;
|
|
}
|
|
|
|
if (S_FALSE == pdsv->CallCB(SFVM_INSERTITEM, 0, (LPARAM)pidl)) {
|
|
// Don't add this object
|
|
TIMESTOP(pdsv->_AddObject);
|
|
return -1;
|
|
}
|
|
|
|
item.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM;
|
|
item.iItem = INT_MAX; // add at end
|
|
item.iSubItem = 0;
|
|
|
|
item.iImage = I_IMAGECALLBACK;
|
|
item.pszText = LPSTR_TEXTCALLBACK;
|
|
if (bCopy) {
|
|
pidl = ILClone(pidl);
|
|
if (!pidl) {
|
|
TIMESTOP(pdsv->_AddObject);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
item.lParam = (LPARAM)pidl;
|
|
|
|
i = ListView_InsertItem(pdsv->_hwndListview, &item);
|
|
|
|
if (bCopy && i < 0) {
|
|
ILFree(pidl);
|
|
}
|
|
|
|
// If this is the first item added, notify automation
|
|
if (i == 0)
|
|
pdsv->_PostSelChangedMessage();
|
|
|
|
TIMESTOP(pdsv->_AddObject);
|
|
return i;
|
|
}
|
|
|
|
|
|
// Find the relative pidl, if it exists
|
|
int CDefView::_FindItem(LPCITEMIDLIST pidl, LPITEMIDLIST* ppidlFound, BOOL fSamePtr)
|
|
{
|
|
int iItem;
|
|
int cItems;
|
|
int cCounter;
|
|
|
|
// this f-n can be called during drag, to get the points for the items being dragged
|
|
// (for drag image). in the case of docfind folder the pidls packed in dataobject
|
|
// are full pidls because the source of the items in not necessarily one folder.
|
|
// in that case all the calls bellow are on docfind folder which knows how to handle
|
|
// full pidls, so in ownerdata cases the pidls may not be relative
|
|
RIP(_IsOwnerData() || ILFindLastID(pidl) == pidl);
|
|
|
|
cItems = ListView_GetItemCount(_hwndListview);
|
|
|
|
if (_iLastFind >= cItems)
|
|
_iLastFind = 0;
|
|
|
|
iItem = _iLastFind;
|
|
if (SUCCEEDED(CallCB(SFVM_INDEXOFITEMIDLIST, (WPARAM)&iItem, (LPARAM)pidl))) {
|
|
if (ppidlFound)
|
|
*ppidlFound = _GetPIDL(iItem);
|
|
return iItem;
|
|
}
|
|
|
|
for (cCounter = 0, iItem = _iLastFind; cCounter < cItems; iItem = (iItem + 1) % cItems, cCounter++) {
|
|
HRESULT hres = ResultFromShort(-1);
|
|
LPITEMIDLIST pidlT = _GetPIDL(iItem);
|
|
if (!pidlT)
|
|
return -1;
|
|
|
|
if (pidlT == pidl) {
|
|
hres = ResultFromShort(0);
|
|
} else if (!fSamePtr) {
|
|
// if we don't insist on being the same pointer, do the ole style compare
|
|
// BUGBUG: this passes 0 for the lParam
|
|
hres = _pshf->CompareIDs(0, pidl, pidlT);
|
|
}
|
|
|
|
ASSERT(SUCCEEDED(hres));
|
|
if (FAILED(hres))
|
|
return -1;
|
|
|
|
if (ShortFromResult(hres) == 0) {
|
|
if (ppidlFound)
|
|
*ppidlFound = pidlT;
|
|
_iLastFind = iItem;
|
|
#ifdef FINDCACHE_DEBUG
|
|
TraceMsg(TF_DEFVIEW, "####FIND CACHE RESULT --- %s by %d", cCounter < iItem ? TEXT("WIN") : TEXT("LOSE"), iItem - cCounter);
|
|
#endif
|
|
return iItem;
|
|
}
|
|
}
|
|
|
|
_iLastFind = 0;
|
|
return -1; // not found
|
|
}
|
|
|
|
|
|
// Function to process the SFVM_REMOVEOBJECT message, by searching
|
|
// through the list for a match of the pidl. If a match is found, the
|
|
// item is removed from the list and the index number is returned, else -1 is returned.
|
|
int CDefView::_RemoveObject(LPCITEMIDLIST pidl, BOOL fSamePtr)
|
|
{
|
|
int i;
|
|
|
|
// Docfind will pass in a null pointer to tell us that it wants
|
|
// to refresh the window by deleting all of the items from it.
|
|
if (pidl == NULL) {
|
|
// notify the iSHellFolder
|
|
CallCB(SFVM_DELETEITEM, 0, 0);
|
|
ListView_DeleteAllItems(_hwndListview);
|
|
_dwAttrSel = (DWORD)-1; // Throw away cached information about sel attributes.
|
|
_PostSelChangedMessage(); // and make sure a selection changed notify goes out.
|
|
return 0;
|
|
}
|
|
|
|
// Non null go look for item.
|
|
i = _FindItem(pidl, NULL, fSamePtr);
|
|
if (i >= 0) {
|
|
RECT rc;
|
|
UINT uState = ListView_GetItemState(_hwndListview, i, LVIS_ALL);
|
|
UINT uCount = 0;
|
|
|
|
if (uState & LVIS_FOCUSED)
|
|
ListView_GetItemRect(_hwndListview, i, &rc, LVIR_ICON);
|
|
|
|
// do the actual delete
|
|
ListView_DeleteItem(_hwndListview, i);
|
|
|
|
// we deleted the focused item.. replace the focus to the nearest item.
|
|
if (uState & LVIS_FOCUSED) {
|
|
int iFocus = i;
|
|
if (DV_ISANYICONMODE(_fs.ViewMode)) {
|
|
LV_FINDINFO lvfi;
|
|
|
|
lvfi.flags = LVFI_NEARESTXY;
|
|
lvfi.pt.x = rc.left;
|
|
lvfi.pt.y = rc.top;
|
|
lvfi.vkDirection = 0;
|
|
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);
|
|
}
|
|
}
|
|
|
|
// Notify automation if the listview is now empty
|
|
GetObjectCount(&uCount);
|
|
if (!uCount)
|
|
_PostSelChangedMessage();
|
|
}
|
|
return i;
|
|
}
|
|
|
|
|
|
// Function to process the SFVM_UPDATEOBJECT message, by searching
|
|
// through the list for a match of the first pidl. If a match is found,
|
|
// the item is updated to the second pidl...
|
|
|
|
// Win95's defview supported SFVM_UPDATEOBJECT by swapping the old pidl
|
|
// for the new pidl. It did *not* release the old pidl (causing a leak)
|
|
// and it did *not* release the new pidl (until the view went away).
|
|
// So buggy callers could still reference their pidl, even though they
|
|
// technically handed the pidl off. IE4 fixed the memory leaks by treating
|
|
// everything correctly, including releasing the passed in pidl right away.
|
|
// This exposed at least one caller (scheduled tasks, see nt5 bug 123780)
|
|
// which referenced their pidl after passing it in. We could fix this in
|
|
// NT5 via an apphack, but since IE4 already shipped, I don't see
|
|
// much reason to.
|
|
|
|
// Note: bCopy controls copying only of ppidl[1]. ppidl[0] is never copied.
|
|
int CDefView::_UpdateObject(LPITEMIDLIST* ppidl, BOOL bCopy /* = FALSE */)
|
|
{
|
|
LPITEMIDLIST pidlOld;
|
|
int i = _FindItem(ppidl[0], &pidlOld, FALSE);
|
|
|
|
if (i >= 0) {
|
|
LPITEMIDLIST pidlNew;
|
|
if (bCopy) {
|
|
pidlNew = ILClone(ppidl[1]);
|
|
if (!pidlNew)
|
|
return -1;
|
|
} else
|
|
pidlNew = ppidl[1]; // update the second item.
|
|
|
|
if (_IsOwnerData()) {
|
|
if (SUCCEEDED(CallCB(SFVM_SETITEMIDLIST, i, (LPARAM)pidlNew))) {
|
|
ListView_Update(_hwndListview, i);
|
|
if (bCopy)
|
|
ILFree(pidlNew);
|
|
} else {
|
|
// we failed, try to cleanup and bail.
|
|
if (bCopy)
|
|
ILFree(pidlNew);
|
|
return -1;
|
|
}
|
|
} else {
|
|
LV_ITEM item;
|
|
BOOL bSelected;
|
|
SFVM_SELCHANGE_DATA dvsci;
|
|
|
|
// 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.iSubItem = 0; // REVIEW: bug in listview?
|
|
item.lParam = (LPARAM)pidlNew;
|
|
|
|
//since directory is cashing the size of both the selected and all
|
|
//items we need to let it know that one/both changed
|
|
bSelected = (ListView_GetItemState(_hwndListview, i, LVIS_SELECTED) & LVIS_SELECTED);
|
|
|
|
// if selected, deselect it
|
|
if (bSelected) {
|
|
dvsci.uNewState = 0;
|
|
dvsci.uOldState = LVIS_SELECTED;
|
|
dvsci.lParamItem = (LPARAM)pidlOld;
|
|
|
|
CallCB(SFVM_SELCHANGE,
|
|
MAKEWPARAM(SFVIDM_CLIENT_FIRST, i),
|
|
(LPARAM)&dvsci);
|
|
}
|
|
// now remove it (to force its old size to be subtracted from the cached size)
|
|
CallCB(SFVM_DELETEITEM, 0, (LPARAM)pidlOld);
|
|
// now insert it with a new pidl
|
|
CallCB(SFVM_INSERTITEM, 0, (LPARAM)pidlNew);
|
|
// if it was selected, select it again
|
|
if (bSelected) {
|
|
dvsci.uNewState = LVIS_SELECTED;
|
|
dvsci.uOldState = 0;
|
|
dvsci.lParamItem = (LPARAM)pidlNew;
|
|
|
|
CallCB(SFVM_SELCHANGE, MAKEWPARAM(SFVIDM_CLIENT_FIRST, i), (LPARAM)&dvsci);
|
|
}
|
|
|
|
ListView_SetItem(_hwndListview, &item);
|
|
|
|
// Force update of all remaining columns.
|
|
HWND hwndHeader = ListView_GetHeader(_hwndListview);
|
|
int cCols = Header_GetItemCount(hwndHeader);
|
|
for (item.iSubItem++; item.iSubItem < cCols; item.iSubItem++) {
|
|
ListView_SetItemText(_hwndListview, item.iItem, item.iSubItem, LPSTR_TEXTCALLBACK);
|
|
}
|
|
}
|
|
|
|
// Free the old pidl after we've added the new one
|
|
// Don't do this if owner data as we don't know how they allocated this info...
|
|
if (!_IsOwnerData())
|
|
ILFree(pidlOld);
|
|
|
|
// Automation may want to know about the change
|
|
CheckIfSelectedAndNotifyAutomation(ppidl[0], i);
|
|
}
|
|
|
|
return i;
|
|
}
|
|
|
|
|
|
// invalidates all items with the given image index.
|
|
|
|
// or update all items if iImage == -1
|
|
void DefView_UpdateImage(CDefView* pdsv, int iImage)
|
|
{
|
|
LV_ITEM item;
|
|
int cItems;
|
|
|
|
TraceMsg(TF_DEFVIEW, "DefView_UpdateImage: %d", iImage);
|
|
|
|
// -1 means update all
|
|
// reset the imagelists incase the size has changed, and do a full update.
|
|
if (iImage == -1) {
|
|
HIMAGELIST himlLarge, himlSmall;
|
|
|
|
Shell_GetImageLists(&himlLarge, &himlSmall);
|
|
|
|
ListView_SetImageList(pdsv->_hwndListview, himlLarge, LVSIL_NORMAL);
|
|
ListView_SetImageList(pdsv->_hwndListview, himlSmall, LVSIL_SMALL);
|
|
|
|
pdsv->ReloadContent();
|
|
return;
|
|
}
|
|
|
|
// get a dc so we can optimize for visible/not visible cases
|
|
HDC hdcLV = GetDC(pdsv->_hwndListview);
|
|
|
|
// scan the listview updating any items which match
|
|
item.iSubItem = 0;
|
|
cItems = ListView_GetItemCount(pdsv->_hwndListview);
|
|
for (item.iItem = 0; item.iItem < cItems; item.iItem++) {
|
|
int iImageOld;
|
|
|
|
item.mask = LVIF_IMAGE | LVIF_PARAM | LVIF_NORECOMPUTE;
|
|
|
|
ListView_GetItem(pdsv->_hwndListview, &item);
|
|
iImageOld = item.iImage;
|
|
|
|
if (item.iImage == iImage) // this filters I_IMAGECALLBACK for us
|
|
{
|
|
RECT rc;
|
|
LPCITEMIDLIST pidl = DV_GetPIDLParam(pdsv, item.lParam, item.iItem);
|
|
|
|
Icon_FSEvent(SHCNE_UPDATEITEM, pidl, NULL);
|
|
|
|
// 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 (ListView_GetItemRect(pdsv->_hwndListview, item.iItem, &rc, LVIR_ICON) && RectVisible(hdcLV, &rc)) {
|
|
int iImageNew;
|
|
HRESULT hres = pdsv->_GetIconAsync(pidl, &iImageNew, FALSE);
|
|
if (hres == S_FALSE)
|
|
continue;
|
|
|
|
if (SUCCEEDED(hres)) {
|
|
if (iImageNew == iImageOld) {
|
|
ListView_RedrawItems(pdsv->_hwndListview, item.iItem, item.iItem);
|
|
continue;
|
|
}
|
|
|
|
item.iImage = iImageNew;
|
|
}
|
|
}
|
|
|
|
item.mask = LVIF_IMAGE;
|
|
item.iSubItem = 0;
|
|
ListView_SetItem(pdsv->_hwndListview, &item);
|
|
}
|
|
}
|
|
|
|
ReleaseDC(pdsv->_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)
|
|
{
|
|
// BUGBUG: should support refreshing a range of pidls
|
|
int i = _FindItem(ppidl[0], NULL, FALSE);
|
|
if (i >= 0)
|
|
ListView_RedrawItems(_hwndListview, i, i);
|
|
return i;
|
|
}
|
|
|
|
|
|
// Function to process the SFVM_GETSELECTEDOBJECTS message
|
|
HRESULT DefView_GetItemObjects(CDefView* pdsv, LPCITEMIDLIST** ppidl, UINT uItem, UINT* pcItems)
|
|
{
|
|
UINT cItems = DefView_GetItemPIDLS(pdsv, NULL, 0, uItem);
|
|
*pcItems = cItems;
|
|
if (ppidl) {
|
|
*ppidl = NULL;
|
|
|
|
if (cItems == 0)
|
|
return S_OK; // nothing allocated...
|
|
|
|
*ppidl = (LPCITEMIDLIST*)LocalAlloc(LPTR, SIZEOF(LPITEMIDLIST) * cItems);
|
|
if (!*ppidl)
|
|
return E_OUTOFMEMORY;
|
|
|
|
cItems = DefView_GetItemPIDLS(pdsv, *ppidl, cItems, uItem);
|
|
}
|
|
|
|
*pcItems = cItems;
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
void DefView_SetItemPos(CDefView* pdsv, LPSFV_SETITEMPOS psip)
|
|
{
|
|
int i = pdsv->_FindItem(psip->pidl, NULL, FALSE);
|
|
if (i >= 0) {
|
|
ListView_SetItemPosition32(pdsv->_hwndListview, i, psip->pt.x, psip->pt.y);
|
|
|
|
pdsv->_bItemsMoved = TRUE;
|
|
pdsv->_bClearItemPos = FALSE;
|
|
}
|
|
}
|
|
|
|
|
|
#define DV_IDTIMER 1
|
|
#define ENUMTIMEOUT 3000 // 3 seconds
|
|
#define SHORTENUMTIMEOUT 500 // 1/2 second
|
|
|
|
|
|
HRESULT DV_AllocRestOfStream(IStream* pstm, void** ppData, UINT* puLen)
|
|
{
|
|
UINT uLen;
|
|
ULARGE_INTEGER libCurPos;
|
|
ULARGE_INTEGER libEndPos;
|
|
|
|
pstm->Seek(g_li0, STREAM_SEEK_CUR, &libCurPos);
|
|
pstm->Seek(g_li0, STREAM_SEEK_END, &libEndPos);
|
|
|
|
uLen = libEndPos.LowPart - libCurPos.LowPart;
|
|
// Note that we add room for an extra ITEMIDLIST so we don't GP fault
|
|
// when we manually terminate the last ID list
|
|
if (uLen == 0) {
|
|
return(E_UNEXPECTED);
|
|
}
|
|
|
|
// Allow the caller to have some extra room
|
|
uLen += *puLen;
|
|
if ((*ppData = (void*)LocalAlloc(LPTR, uLen)) == NULL) {
|
|
return(E_OUTOFMEMORY);
|
|
}
|
|
*puLen = uLen;
|
|
|
|
pstm->Seek(*(LARGE_INTEGER*)&libCurPos, STREAM_SEEK_SET, NULL);
|
|
// This really should not fail
|
|
pstm->Read(*ppData, uLen, NULL);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
HRESULT DV_AllocNewStream(IStream* pstm, void** ppData, UINT* puLen)
|
|
{
|
|
struct
|
|
{
|
|
DVSAVEHEADER dvSaveHeader;
|
|
DVSAVEHEADEREX dvSaveHeaderEx;
|
|
} dv;
|
|
ULARGE_INTEGER libStartPos;
|
|
LARGE_INTEGER dlibMove;
|
|
ULONG cbRead;
|
|
int uLen;
|
|
HRESULT hres;
|
|
|
|
// assume failure
|
|
*ppData = NULL;
|
|
|
|
// remember the starting point in the stream
|
|
dlibMove.LowPart = dlibMove.HighPart = 0;
|
|
hres = pstm->Seek(dlibMove, STREAM_SEEK_CUR, &libStartPos);
|
|
if (FAILED(hres)) {
|
|
return hres;
|
|
}
|
|
|
|
// read the headers
|
|
hres = pstm->Read(&dv, SIZEOF(dv), &cbRead);
|
|
if (FAILED(hres)) {
|
|
return hres;
|
|
}
|
|
|
|
// if it's our extended header, we're golden
|
|
if (SIZEOF(dv) == cbRead &&
|
|
dv.dvSaveHeader.cbSize == SIZEOF(DVSAVEHEADER) &&
|
|
dv.dvSaveHeader.cbColOffset == 0 &&
|
|
dv.dvSaveHeaderEx.dwSignature == DVSAVEHEADEREX_SIGNATURE &&
|
|
dv.dvSaveHeaderEx.cbSize >= SIZEOF(DVSAVEHEADEREX)) {
|
|
if (dv.dvSaveHeaderEx.wVersion < DVSAVEHEADEREX_VERSION) {
|
|
// We used to store szExtended in here -- not any more
|
|
dv.dvSaveHeaderEx.dwUnused = 0;
|
|
}
|
|
|
|
// Allocate a buffer for the entire stream
|
|
uLen = dv.dvSaveHeaderEx.cbStreamSize;
|
|
*ppData = (PDVSAVEHEADER)LocalAlloc(LPTR, uLen + *puLen);
|
|
if (NULL != *ppData) {
|
|
// patch up cbColOffset
|
|
dv.dvSaveHeader.cbColOffset = dv.dvSaveHeaderEx.cbColOffset;
|
|
|
|
// copy what we've already read
|
|
ASSERT(dv.dvSaveHeaderEx.cbStreamSize >= SIZEOF(dv));
|
|
memcpy(*ppData, &dv, SIZEOF(dv));
|
|
|
|
// read the rest of the data
|
|
hres = pstm->Read((LPBYTE)*ppData + SIZEOF(dv), uLen - SIZEOF(dv), &cbRead);
|
|
if (FAILED(hres) || cbRead != uLen - SIZEOF(dv)) {
|
|
LocalFree(*ppData);
|
|
*ppData = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (NULL != *ppData) {
|
|
*puLen += uLen;
|
|
hres = S_OK;
|
|
} else {
|
|
// we failed, make sure we don't change the stream position
|
|
dlibMove.LowPart = libStartPos.LowPart;
|
|
pstm->Seek(dlibMove, STREAM_SEEK_SET, NULL);
|
|
|
|
hres = E_FAIL;
|
|
}
|
|
|
|
return hres;
|
|
}
|
|
|
|
|
|
UINT CDefView::_GetSaveHeader(PDVSAVEHEADER* ppSaveHeader)
|
|
{
|
|
IStream* pstm;
|
|
UINT uLen = SIZEOF(ITEMIDLIST);
|
|
|
|
*ppSaveHeader = NULL;
|
|
|
|
// 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.
|
|
if (FAILED(_psb->GetViewStateStream(STGM_READ, &pstm)) && FAILED(_LoadGlobalViewState(&pstm)))
|
|
return 0;
|
|
|
|
// first try new stream format
|
|
if (FAILED(DV_AllocNewStream(pstm, (void**)ppSaveHeader, &uLen))) {
|
|
ASSERT(uLen == SIZEOF(ITEMIDLIST));
|
|
|
|
// then try old format
|
|
if (FAILED(DV_AllocRestOfStream(pstm, (void**)ppSaveHeader, &uLen))) {
|
|
uLen = 0;
|
|
} else {
|
|
if (uLen < SIZEOF(DVSAVEHEADER) + SIZEOF(ITEMIDLIST) || (*ppSaveHeader)->cbSize != SIZEOF(DVSAVEHEADER)) {
|
|
LocalFree(*ppSaveHeader);
|
|
*ppSaveHeader = 0;
|
|
uLen = 0;
|
|
} else {
|
|
// ppSaveHeader points to a valied pre-IE4 DVSAVEHEADER.
|
|
// Put upgrade code here.
|
|
|
|
}
|
|
// BUGBUG: dan, is this what you wanted?
|
|
// else
|
|
// (*ppSaveHeader)->dvState = _dvState;
|
|
}
|
|
}
|
|
|
|
// Massage values if necessary
|
|
if (*ppSaveHeader)
|
|
ValidateDVState(&(*ppSaveHeader)->dvState);
|
|
|
|
ATOMICRELEASE(pstm);
|
|
|
|
return uLen;
|
|
}
|
|
|
|
|
|
#if 0 // we currently don't store anything we care about
|
|
PDVSAVEHEADEREX DefView_GetSaveHeaderEx(PDVSAVEHEADER pSaveHeader)
|
|
{
|
|
// Do we have room for an extended header?
|
|
if (pSaveHeader && pSaveHeader->cbPosOffset >= SIZEOF(DVSAVEHEADER) + SIZEOF(DVSAVEHEADEREX)) {
|
|
PDVSAVEHEADEREX pSaveHeaderEx = (PDVSAVEHEADEREX)(pSaveHeader + 1);
|
|
// Verify the extended header
|
|
if (pSaveHeaderEx->dwSignature == DVSAVEHEADEREX_SIGNATURE && pSaveHeaderEx->cbSize >= SIZEOF(DVSAVEHEADEREX)) {
|
|
// A random place to verify this, but we should do it somewhere
|
|
ASSERT(pSaveHeader->cbColOffset == 0 || pSaveHeader->cbColOffset >= SIZEOF(DVSAVEHEADER) + SIZEOF(DVSAVEHEADEREX));
|
|
|
|
return(pSaveHeaderEx);
|
|
}
|
|
}
|
|
|
|
return(NULL);
|
|
}
|
|
#endif
|
|
|
|
|
|
// restore the window state
|
|
|
|
// icon positions
|
|
// window scroll position
|
|
void CDefView::_RestoreState(PDVSAVEHEADER pInSaveHeader, UINT uLen)
|
|
{
|
|
PDVSAVEHEADER pSaveHeader;
|
|
|
|
if (pInSaveHeader) {
|
|
pSaveHeader = pInSaveHeader;
|
|
} else {
|
|
uLen = _GetSaveHeader(&pSaveHeader);
|
|
if (uLen == 0) {
|
|
_InternalRearrange();
|
|
return;
|
|
}
|
|
}
|
|
|
|
_dvState = pSaveHeader->dvState;
|
|
|
|
// Columns get restored during window creation
|
|
|
|
// make sure the view modes of the saved state match what we have now
|
|
if (pSaveHeader->ViewMode == _fs.ViewMode) {
|
|
// If we restored all the icon positions restore the scroll position too
|
|
if (_RestorePos(pSaveHeader, uLen))
|
|
ListView_Scroll(_hwndListview, pSaveHeader->ptScroll.x, pSaveHeader->ptScroll.y);
|
|
} else {
|
|
TraceMsg(TF_WARNING, "restore state view modes don't match (%d != %d)", pSaveHeader->ViewMode, _fs.ViewMode);
|
|
}
|
|
|
|
if (!pInSaveHeader)
|
|
LocalFree((HLOCAL)pSaveHeader);
|
|
}
|
|
|
|
|
|
// This function can be called only when we are filling listview items
|
|
// (from within ::FillObjects. It is very important to pass consistent
|
|
// dwFlags to GetDisplayNameOf and SetNameOf.
|
|
void DefView_UpdateGlobalFlags(CDefView* pdsv)
|
|
{
|
|
SHELLSTATE ss;
|
|
SHGetSetSettings(&ss, SSF_SHOWALLOBJECTS | SSF_SHOWCOMPCOLOR, FALSE);
|
|
pdsv->_fShowAllObjects = ss.fShowAllObjects;
|
|
|
|
#ifdef WINNT
|
|
// Don't allow compression coloring on the desktop proper
|
|
pdsv->_fShowCompColor = pdsv->_IsDesktop() ? FALSE : ss.fShowCompColor;
|
|
#endif
|
|
}
|
|
|
|
|
|
// We refreshed the view. Take the old pidls and new pidls and compare
|
|
// them, doing a DefView_AddObject for all the new pidls, DefView_RemoveObject
|
|
// for the deleted pidls, and _UpdateObject for the inplace modifies.
|
|
void CDefView::_FilterDPAs(HDPA hdpa, HDPA hdpaOld)
|
|
{
|
|
int i, j;
|
|
LPITEMIDLIST pidl, pidlOld;
|
|
LPARAM lSupportsIdentity = 0;
|
|
|
|
// See if the folder supports comparisons on column -1 (test for
|
|
// pidl complete comparisions. If so, then we can support inplace
|
|
// pidl modification as well as just add and remove.
|
|
if (HasCB() && SUCCEEDED(CallCB(SFVM_SUPPORTSIDENTITY, 0, 0)))
|
|
lSupportsIdentity = SHCIDS_ALLFIELDS;
|
|
|
|
#ifdef DPA_FILTER_TEST
|
|
for (i = 0; i < DPA_GetPtrCount(hdpaOld); i++) {
|
|
pidl = DPA_FastGetPtr(hdpaOld, i);
|
|
TraceMsg(TF_DEFVIEW, "pidl = %x, %x, %x", pidl, *(DWORD*)pidl, i);
|
|
}
|
|
#endif
|
|
|
|
// do the compares
|
|
for (;;) {
|
|
int iCompare;
|
|
i = DPA_GetPtrCount(hdpaOld);
|
|
j = DPA_GetPtrCount(hdpa);
|
|
|
|
if (!i && !j)
|
|
break;
|
|
|
|
if (!i) {
|
|
// only new ones left. Insert all of them.
|
|
iCompare = -1;
|
|
pidl = (LPITEMIDLIST)DPA_FastGetPtr(hdpa, 0);
|
|
|
|
} else if (!j) {
|
|
// only old ones left. remove them all.
|
|
iCompare = 1;
|
|
pidlOld = (LPITEMIDLIST)DPA_FastGetPtr(hdpaOld, 0);
|
|
} else {
|
|
HRESULT hres;
|
|
|
|
pidlOld = (LPITEMIDLIST)DPA_FastGetPtr(hdpaOld, 0);
|
|
pidl = (LPITEMIDLIST)DPA_FastGetPtr(hdpa, 0);
|
|
|
|
LPARAM lParam = (LPARAM)(((ULONG_PTR)(_dvState.lParamSort)) | lSupportsIdentity);
|
|
hres = _pshf->CompareIDs(lParam, pidl, pidlOld);
|
|
if (FAILED(hres)) {
|
|
DVSAVESTATE saveState;
|
|
|
|
_GetSortDefaults(&saveState);
|
|
hres = _pshf->CompareIDs(saveState.lParamSort | lSupportsIdentity, pidl, pidlOld);
|
|
if (FAILED(hres)) {
|
|
hres = _pshf->CompareIDs(lSupportsIdentity, pidl, pidlOld);
|
|
}
|
|
}
|
|
|
|
ASSERT(SUCCEEDED(hres));
|
|
ASSERT(_dvState.iDirection != 0);
|
|
iCompare = (short)HRESULT_CODE(hres) * _dvState.iDirection;
|
|
}
|
|
|
|
if (iCompare == 0) {
|
|
// they're the same ,remove one of each.
|
|
ILFree(pidl);
|
|
DPA_DeletePtr(hdpa, 0);
|
|
DPA_DeletePtr(hdpaOld, 0);
|
|
} else {
|
|
// Not identical. See if it's just a modify.
|
|
if (lSupportsIdentity && i && j) {
|
|
HRESULT hres = _pshf->CompareIDs(_dvState.lParamSort, pidl, pidlOld);
|
|
if (FAILED(hres)) {
|
|
DVSAVESTATE saveState;
|
|
|
|
_GetSortDefaults(&saveState);
|
|
hres = _pshf->CompareIDs(saveState.lParamSort, pidl, pidlOld);
|
|
if (FAILED(hres)) {
|
|
hres = _pshf->CompareIDs(0, pidl, pidlOld);
|
|
}
|
|
}
|
|
ASSERT(SUCCEEDED(hres));
|
|
iCompare = (short)HRESULT_CODE(hres) * _dvState.iDirection;
|
|
}
|
|
if (iCompare == 0) {
|
|
LPITEMIDLIST pp[2] = {pidlOld, pidl};
|
|
if (_UpdateObject(pp) < 0)
|
|
ILFree(pidl);
|
|
DPA_DeletePtr(hdpa, 0);
|
|
DPA_DeletePtr(hdpaOld, 0);
|
|
} else if (iCompare < 0) {
|
|
// the new item!
|
|
if (DefView_AddObject(this, pidl) == -1)
|
|
ILFree(pidl);
|
|
DPA_DeletePtr(hdpa, 0);
|
|
} else {
|
|
#ifdef DPA_FILTER_TEST
|
|
// old item, delete it.
|
|
TraceMsg(TF_DEFVIEW, "remove pidl = %x, %x", pidlOld, *(DWORD*)pidlOld);
|
|
#endif
|
|
_RemoveObject(pidlOld, TRUE);
|
|
DPA_DeletePtr(hdpaOld, 0);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// this is only called from within SHCNE_* don't put up ui on the enum error.
|
|
void DefView_Update(CDefView* pdsv)
|
|
{
|
|
if (pdsv->_bBkFilling) {
|
|
// 99/05/11 #301779 vtan: If there is a background fill and another update
|
|
// is requested then the background fill should dump what's it done and
|
|
// DefView should start all over again. This manifests in the following
|
|
// scenario.
|
|
// 1. 500+ items in a folder.
|
|
// 2. select all items.
|
|
// 3. type "delete" key.
|
|
// 4. 10 SHCNE_UPDATEITEM items get sent.
|
|
// 5. one of these causes a DefView_Update(this).
|
|
// 6. there are still 500+ items so a background thread gets fired to
|
|
// enumerate the directory contents.
|
|
// 7. the final SHCNE_UPDATEDIR gets sent.
|
|
// 8. DefView_Update(this) is invoked in the default: case.
|
|
// 9. this->_bBkFilling is true so a new enumeration object is not
|
|
// created and only the current enumeration object is enumerated
|
|
// to completion which isn't accurate to the current state.
|
|
THR(pdsv->_pScheduler->RemoveTasks(TOID_DVBackgroundEnum, ITSAT_DEFAULT_LPARAM, FALSE));
|
|
pdsv->_bBkFilling = FALSE;
|
|
}
|
|
|
|
pdsv->FillObjectsShowHide(FALSE, NULL, 0, FALSE);
|
|
}
|
|
|
|
|
|
#define HM_OK 0
|
|
#define HM_ABORT 1
|
|
#define HM_DESTROY 2
|
|
|
|
|
|
#ifdef DEADCODE
|
|
UINT HandleMessages(HWND hwnd, HWND _hwndMain)
|
|
{
|
|
MSG msg;
|
|
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
|
|
// intercept these messages
|
|
if ((msg.message == WM_SYSKEYDOWN || msg.message == WM_KEYDOWN) && msg.wParam == VK_ESCAPE)
|
|
return HM_ABORT;
|
|
|
|
if (((msg.hwnd == hwnd) ||
|
|
(msg.hwnd == _hwndMain)) && (((msg.message == WM_SYSCOMMAND) && (msg.wParam == SC_CLOSE)) ||
|
|
(msg.message == WM_DESTROY) ||
|
|
(msg.message == WM_CLOSE) ||
|
|
(msg.message == WM_QUIT))) {
|
|
PostMessage(msg.hwnd, msg.message, msg.wParam, msg.lParam);
|
|
TraceMsg(TF_DEFVIEW, "Got WM_SYSCOMMAND SC_CLOSE!");
|
|
return HM_DESTROY;
|
|
}
|
|
|
|
TranslateMessage(&msg);
|
|
DispatchMessage(&msg);
|
|
}
|
|
|
|
return HM_OK;
|
|
}
|
|
#endif
|
|
|
|
|
|
void CDefView::_ShowControl(UINT idControl, int idCmd)
|
|
{
|
|
IBrowserService* pbs;
|
|
|
|
if (SUCCEEDED(_psb->QueryInterface(IID_IBrowserService, (void**)&pbs))) {
|
|
pbs->ShowControlWindow(idControl, idCmd);
|
|
ATOMICRELEASE(pbs);
|
|
}
|
|
}
|
|
|
|
|
|
bool IsSingleWindowBrowsing(void)
|
|
{
|
|
CABINETSTATE cabinetState;
|
|
|
|
TBOOL(ReadCabinetState(&cabinetState, sizeof(cabinetState)));
|
|
return(!BOOLIFY(cabinetState.fNewWindowMode));
|
|
}
|
|
|
|
|
|
// Alter the size of the parent to best fit around the items we have.
|
|
void ViewWindow_BestFit(CDefView* pdsv, BOOL bTimeout)
|
|
{
|
|
const int cxMin = MINVIEWWIDTH, cyMin = MINVIEWHEIGHT;
|
|
const int cxSpacing = GetSystemMetrics(SM_CXICONSPACING);
|
|
const int cySpacing = GetSystemMetrics(SM_CYICONSPACING);
|
|
DWORD dwStyle = GetWindowStyle(pdsv->_hwndListview);
|
|
RECT rc, rcWork;
|
|
WINDOWPLACEMENT wp;
|
|
int cItems, cxScreen, cyScreen;
|
|
int cxMax, cyMax;
|
|
int cxWorkArea;
|
|
int cyWorkArea;
|
|
int iAdjustFactor;
|
|
|
|
// have we already best fit this window? don't do it twice
|
|
|
|
if (!(pdsv->_fs.fFlags & FWF_BESTFITWINDOW))
|
|
return;
|
|
|
|
// Don't try to do it again.
|
|
pdsv->_fs.fFlags &= ~FWF_BESTFITWINDOW;
|
|
|
|
|
|
SystemParametersInfo(SPI_GETWORKAREA, 0, &rcWork, 0);
|
|
cxWorkArea = (rcWork.right - rcWork.left);
|
|
cyWorkArea = (rcWork.bottom - rcWork.top);
|
|
|
|
// 99/02/06 #286723 vtan: Adjust best fit size to 4/5 if using
|
|
// less than 1024x768.
|
|
|
|
iAdjustFactor = 3;
|
|
if ((cxWorkArea < 1000) && (cyWorkArea < 700))
|
|
++iAdjustFactor;
|
|
cxMax = (cxWorkArea * iAdjustFactor) / 5;
|
|
cyMax = (cyWorkArea * iAdjustFactor) / 5;
|
|
|
|
int iITbar = SBSC_HIDE;
|
|
int iStdBar = SBSC_HIDE;
|
|
|
|
switch (pdsv->_uDefToolbar) {
|
|
case HIWORD(TBIF_INTERNETBAR):
|
|
iITbar = SBSC_SHOW;
|
|
goto ShowToolbar;
|
|
case HIWORD(TBIF_STANDARDTOOLBAR):
|
|
iStdBar = SBSC_SHOW;
|
|
goto ShowToolbar;
|
|
case HIWORD(TBIF_NOTOOLBAR):
|
|
ShowToolbar:
|
|
pdsv->_ShowControl(FCW_INTERNETBAR, iITbar);
|
|
pdsv->_ShowControl(FCW_TOOLBAR, iStdBar);
|
|
break;
|
|
}
|
|
// Normal views use the same code as before
|
|
|
|
// Find the largest square.
|
|
// 4000 should be close enough to infinite
|
|
if (bTimeout) {
|
|
cItems = 4000;
|
|
// Give Control Panel a chance to tell us it will
|
|
// probably only have a few items, even though it timed
|
|
// out.
|
|
pdsv->CallCB(SFVM_DEFITEMCOUNT, 0, (LPARAM)&cItems);
|
|
} else {
|
|
cItems = ListView_GetItemCount(pdsv->_hwndListview);
|
|
}
|
|
|
|
// Give docfind a chance to tell us it will eventually
|
|
// have a lot of items, even if it doesn't have any now...
|
|
pdsv->CallCB(SFVM_OVERRIDEITEMCOUNT, 0, (LPARAM)&cItems);
|
|
|
|
rc.left = rc.top = rc.right = rc.bottom = 0;
|
|
if (cItems) {
|
|
int cy, i1, i2, i3;
|
|
|
|
i1 = 1;
|
|
i2 = 0;
|
|
i3 = cItems;
|
|
while (i3 > 0) {
|
|
i3 -= i1;
|
|
i1 += 2;
|
|
i2++;
|
|
}
|
|
|
|
// Convert this into an equivalent rect for the effective
|
|
// client area.
|
|
// The width.
|
|
rc.right = (i2 * cxSpacing);
|
|
if (rc.right > cxMax) {
|
|
rc.right = cxMax;
|
|
// Now recalculate the number of rows
|
|
i2 = rc.right / cxSpacing;
|
|
}
|
|
|
|
if (!i2)
|
|
i2 = 1; // Don't divide by zero below...
|
|
// The height.
|
|
cy = (cItems + i2 - 1) / i2;
|
|
rc.bottom = (cy * cySpacing);
|
|
}
|
|
|
|
// Make sure we are in the "default" view
|
|
if ((dwStyle & LVS_TYPEMASK) == LVS_ICON) {
|
|
// If it's going to be too big then flip into listview.
|
|
pdsv->_fs.ViewMode = (rc.bottom > (cyMax * 3)) ? FVM_LIST : FVM_ICON;
|
|
|
|
// Give Briefcase a chance to tell us it likes details
|
|
pdsv->CallCB(SFVM_DEFVIEWMODE, 0, (LPARAM)&pdsv->_fs.ViewMode);
|
|
|
|
SetWindowBits(pdsv->_hwndListview, GWL_STYLE, LVS_TYPEMASK, LVStyleFromView(pdsv));
|
|
|
|
if (pdsv->_fs.ViewMode == FVM_DETAILS) {
|
|
if (!pdsv->_bLoadedColumns) {
|
|
pdsv->AddColumns();
|
|
}
|
|
// Need to special-case details because it could be any width
|
|
// I need to add an item in case there were none to start with
|
|
|
|
LV_ITEM item;
|
|
int i;
|
|
|
|
item.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM;
|
|
item.iItem = INT_MAX; // add at end
|
|
item.iSubItem = 0;
|
|
|
|
item.iImage = 0;
|
|
item.pszText = (LPTSTR)c_szNULL;
|
|
item.lParam = 0;
|
|
|
|
i = ListView_InsertItem(pdsv->_hwndListview, &item);
|
|
|
|
if (i >= 0) {
|
|
ListView_GetItemRect(pdsv->_hwndListview, i, &rc, LVIR_BOUNDS);
|
|
ListView_DeleteItem(pdsv->_hwndListview, i);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Make sure things aren't too small, or too big
|
|
rc.left = rc.top = 0;
|
|
rc.right = max(min(rc.right, cxMax), cxMin);
|
|
rc.bottom = max(min(rc.bottom, cyMax), cyMin);
|
|
|
|
// 99/02/09 #286723 vtan: Requested by gsierra to change best fit
|
|
// algorithm to use the 3/5 or 4/5 algorithm if browse in same
|
|
// or webview on.
|
|
|
|
// 99/03/18 #307124 vtan: Add IsExplorerWindow() so that initial
|
|
// explorer will be sized so that the list view is visible.
|
|
|
|
if (IsSingleWindowBrowsing() || pdsv->m_cFrame.IsWebView() || IsExplorerWindow(pdsv->_hwndMain)) {
|
|
// Extended views or normal view with the web pane on just
|
|
// get most of the screen space
|
|
rc.right = cxMax;
|
|
rc.bottom = cyMax;
|
|
}
|
|
|
|
// Allow room for the parent, toolbars, deskbands, etc.
|
|
RECT rcParent, rcListview;
|
|
GetWindowRect(pdsv->_hwndMain, &rcParent);
|
|
GetWindowRect(pdsv->_hwndListview, &rcListview);
|
|
int nGapHeight = (rcParent.bottom - rcParent.top) - (rcListview.bottom - rcListview.top);
|
|
int nGapWidth = (rcParent.right - rcParent.left) - (rcListview.right - rcListview.left);
|
|
// Also allow room for some text.
|
|
InflateRect(&rc, nGapWidth + PARENTGAPWIDTH, nGapHeight + PARENTGAPHEIGHT + (cySpacing / 2));
|
|
|
|
// we have to set the size of the listview now because
|
|
// in nashvile, we're not the active shellview, we're the psvPending
|
|
// so sizing hwndmain won't size us.
|
|
|
|
// BUGBUG: the above comment implies that if we're thumbnail view, it's size
|
|
// will be wrong. I don't believe that, so I suspect this code is not needed.
|
|
// And besides, if we're WebView, this is the wrong size anyway!
|
|
SetWindowPos(pdsv->_hwndListview, NULL, 0, 0, rc.right - rc.left, rc.bottom - rc.top, SWP_NOMOVE | SWP_NOZORDER);
|
|
|
|
// Make sure it will still fit on the screen.
|
|
wp.length = SIZEOF(wp);
|
|
GetWindowPlacement(pdsv->_hwndMain, &wp);
|
|
|
|
// Make sure the window fits in the work area.
|
|
cxScreen = cxWorkArea - GetSystemMetrics(SM_CXFRAME);
|
|
cyScreen = cyWorkArea - GetSystemMetrics(SM_CYFRAME);
|
|
if (wp.rcNormalPosition.left + rc.right > cxScreen)
|
|
rc.right = cxScreen - wp.rcNormalPosition.left;
|
|
if (wp.rcNormalPosition.top + rc.bottom > cyScreen)
|
|
rc.bottom = cyScreen - wp.rcNormalPosition.top;
|
|
|
|
// Resize the parent.
|
|
SetWindowPos(pdsv->_hwndMain, NULL, 0, 0, rc.right, rc.bottom, SWP_NOMOVE | SWP_NOZORDER);
|
|
|
|
// If we're still in icon mode but AutoArrange is off -
|
|
// things will look weird so fix it now.
|
|
if (pdsv->_HasNormalView()) {
|
|
ASSERT(!pdsv->_bItemsMoved); // no items should be positioned in this case
|
|
pdsv->AutoAutoArrange(0);
|
|
}
|
|
}
|
|
|
|
|
|
BOOL CDefView::EnumerationTimeout(BOOL bRefresh)
|
|
{
|
|
TraceMsg(TF_DEFVIEW, "Enumeration is taking too long.");
|
|
|
|
// The static window could already exist during a refresh
|
|
if (!_hwndStatic && bRefresh) {
|
|
RECT rc;
|
|
|
|
// Note that new windows go to the bottom of the Z order
|
|
_hwndStatic = CreateWindowEx(WS_EX_CLIENTEDGE, ANIMATE_CLASS, c_szNULL,
|
|
WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | ACS_TRANSPARENT | ACS_AUTOPLAY | ACS_CENTER,
|
|
0, 0, 0, 0, _hwndView, (HMENU)ID_STATIC, HINST_THISDLL, NULL);
|
|
if (!_hwndStatic) {
|
|
// We need this window to exist so that we do not get strange
|
|
// painting and clicking results
|
|
return(FALSE);
|
|
}
|
|
|
|
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);
|
|
_OnMoveWindowToTop(_hwndStatic);
|
|
|
|
// BUGBUG: _hwndListview is created hidden, this is probably not needed these days.
|
|
ShowWindow(_hwndListview, SW_HIDE);
|
|
}
|
|
|
|
ViewWindow_BestFit(this, TRUE);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
void DefView_CheckForFillDoneOnDestroy(HWND _hwndView)
|
|
{
|
|
MSG msg;
|
|
|
|
if (PeekMessage(&msg, _hwndView, WM_DSV_DESTROYSTATIC, WM_DSV_DESTROYSTATIC, PM_NOREMOVE)) {
|
|
// PeekMessage(hwnd) can return messages posted to CHILDREN of this hwnd...
|
|
// Verify that the message was really for us.
|
|
if (msg.hwnd == _hwndView) {
|
|
TraceMsg(TF_DEFVIEW, "DefView: Fill_Completed after WM_DESTROY!!!");
|
|
DPA_FreeIDArray((HDPA)msg.lParam);
|
|
}
|
|
}
|
|
|
|
while (PeekMessage(&msg, _hwndView, 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 == _hwndView) {
|
|
TraceMsg(TF_DEFVIEW, "DefView: WM_DSV_UPDATEICON after WM_DESTROY!!!");
|
|
CDVGetIconTask* pTask = (CDVGetIconTask*)msg.lParam;
|
|
ATOMICRELEASE(pTask);
|
|
}
|
|
}
|
|
|
|
while (PeekMessage(&msg, _hwndView, 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 == _hwndView) {
|
|
TraceMsg(TF_DEFVIEW, "DefView: WM_DSV_UPDATECOLDATA after WM_DESTROY!!!");
|
|
delete (CBackgroundColInfo*)msg.lParam;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void CDefView::_ShowListviewIcons()
|
|
{
|
|
// 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.
|
|
|
|
// If our view hasn't been UIActivate()d yet, then we are waiting until
|
|
// the IShellBrowser selects us as the active view.
|
|
if (_uState != SVUIA_DEACTIVATE) {
|
|
// Gotta show only under the correct conditions:
|
|
if ( // We're supposed to show icons
|
|
((!m_cFrame.IsWebView() || _IsDesktop()) && DV_SHOWICONS(this))
|
|
// and ISFV extensions manage their own icon display
|
|
&& !m_cFrame.IsSFVExtension()) {
|
|
// 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);
|
|
}
|
|
} else {
|
|
// Since all _ShowListviewIcons calls may be done before we go UIActive, we need to keep track of this show and do it later.
|
|
_fShowListviewIconsOnActivate = TRUE;
|
|
}
|
|
}
|
|
|
|
|
|
void CDefView::FillDone(HDPA hdpaNew, PDVSAVEHEADER pSaveHeader, UINT uLen, BOOL bRefresh, BOOL fInteractive)
|
|
{
|
|
HDPA hdpaOld;
|
|
int i, j;
|
|
|
|
SendMessage(_hwndListview, WM_SETREDRAW, (WPARAM)FALSE, 0);
|
|
|
|
// Make it sure that we have this around while in this function.
|
|
this->AddRef();
|
|
|
|
hdpaOld = DPA_Create(16);
|
|
if (!hdpaOld) {
|
|
goto Error1;
|
|
}
|
|
|
|
// now get the dpa of what's currently being viewed.
|
|
i = ListView_GetItemCount(_hwndListview);
|
|
|
|
for (j = 0; j < i; j++) {
|
|
LPITEMIDLIST pidl = _GetPIDL(j);
|
|
if (pidl) {
|
|
ASSERT(IsValidPIDL(pidl));
|
|
DPA_AppendPtr(hdpaOld, pidl);
|
|
}
|
|
}
|
|
|
|
// sort the two for easy comparisions
|
|
// Note the new list should already be sorted
|
|
// I wish we could do this sort in the background, but we just can't
|
|
DPA_Sort(hdpaOld, _GetCompareFunction(), (LPARAM)this);
|
|
|
|
_FilterDPAs(hdpaNew, hdpaOld);
|
|
|
|
DPA_Destroy(hdpaOld);
|
|
|
|
Error1:;
|
|
DPA_Destroy(hdpaNew);
|
|
|
|
if (bRefresh) {
|
|
TIMESTART(_RestoreState);
|
|
_RestoreState(pSaveHeader, uLen);
|
|
TIMESTOP(_RestoreState);
|
|
}
|
|
|
|
_ShowListviewIcons();
|
|
|
|
if (_hwndStatic) {
|
|
DestroyWindow(_hwndStatic);
|
|
_hwndStatic = NULL;
|
|
|
|
// 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) doesn't work.
|
|
|
|
// (1) _ShowListviewIcons does a ShowWindow(SW_SHOW), but
|
|
// at the top of this function, we did a WM_SETREDRAW(FALSE),
|
|
// so condition (1) is met.
|
|
|
|
// (2) Condition (2) is met because this function is called
|
|
// precisely to prepare the listview for its first paint.
|
|
|
|
// 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 (_fItemsDeferred() && (GetWindowStyle(_hwndListview) & LVS_TYPEMASK) == LVS_LIST) {
|
|
// Evil hack (fix comctl32.dll v6.0 someday)
|
|
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
|
|
this->SelectSelectedItems();
|
|
}
|
|
|
|
// set the focus on the first item.
|
|
_FocusOnSomething();
|
|
|
|
// Tell the defview client that this window has been refreshed
|
|
CallCB(SFVM_REFRESH, FALSE, 0);
|
|
|
|
DV_UpdateStatusBar(this, TRUE);
|
|
|
|
if (_bUpdatePending) {
|
|
this->FillObjectsShowHide(FALSE, NULL, 0, fInteractive);
|
|
}
|
|
|
|
// Decrement the reference count
|
|
this->Release();
|
|
SendMessage(_hwndListview, WM_SETREDRAW, (WPARAM)TRUE, 0);
|
|
|
|
#ifdef TIMING
|
|
dwFinish = GetTickCount();
|
|
{
|
|
TCHAR sz[40];
|
|
wsprintf(sz, TEXT("%d\n"), dwFinish - dwStart);
|
|
OutputDebugString(sz);
|
|
}
|
|
|
|
TIMESTOP(_Fill);
|
|
TIMEOUT(_Fill);
|
|
TIMEOUT(_EnumNext);
|
|
TIMEOUT(_AddObject);
|
|
TIMEOUT(_GetIcon);
|
|
TIMEOUT(_GetName);
|
|
TIMEOUT(_RestoreState);
|
|
TIMEOUT(_FSNotify);
|
|
TIMEOUT(this-> - WMNotify);
|
|
TIMEOUT(_LVChanging);
|
|
TIMEOUT(_LVChanged);
|
|
TIMEOUT(_LVDelete);
|
|
TIMEOUT(_LVGetDispInfo);
|
|
|
|
TraceMsg(TF_DEFVIEW, "::FillObjects(%s) done! ******", DV_Name(this));
|
|
#endif
|
|
}
|
|
|
|
|
|
void ChangeRefForIdle(CDefView* pdsv, BOOL bAdd)
|
|
{
|
|
if (bAdd) {
|
|
if (InterlockedIncrement(&pdsv->_cRefForIdle) == 0) {
|
|
pdsv->AddRef();
|
|
}
|
|
} else {
|
|
#ifdef DEBUG
|
|
ENTERCRITICAL;
|
|
if (pdsv->_cRefForIdle < 0) {
|
|
TraceMsg(TF_ERROR, "ChangeRefForIdle released too many times!");
|
|
ASSERT(0);
|
|
}
|
|
LEAVECRITICAL;
|
|
#endif
|
|
InterlockedDecrement(&pdsv->_cRefForIdle);
|
|
|
|
// win95 doesn't return the value from the above call...
|
|
if (pdsv->_cRefForIdle == -1) {
|
|
pdsv->Release();
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// Returns: TRUE, if we successfully create the idle thread.
|
|
|
|
// On success, *phdpaNew will be NULL. On failure it may or may not be NULL.
|
|
BOOL DV_StartIdle(CDefView* pdsv, IEnumIDList* peunk, HDPA* phdpaNew, BOOL bRefresh)
|
|
{
|
|
if (pdsv->EnumerationTimeout(bRefresh)) {
|
|
IRunnableTask* pTask;
|
|
|
|
HRESULT hr = CDVBkgrndEnumTask_CreateInstance(pdsv, peunk, *phdpaNew, bRefresh, &pTask);
|
|
if (SUCCEEDED(hr)) {
|
|
if (pdsv->_bBkFilling) {
|
|
ASSERT(pdsv->_pScheduler);
|
|
|
|
// make sure there are no other enum tasks going on...
|
|
hr = pdsv->_pScheduler->RemoveTasks(TOID_DVBackgroundEnum, ITSAT_DEFAULT_LPARAM, TRUE);
|
|
}
|
|
|
|
if (!DefView_IdleDoStuff(pdsv, pTask, TOID_DVBackgroundEnum, 0, TASK_PRIORITY_BKGRND_FILL)) {
|
|
pdsv->_bBkFilling = FALSE;
|
|
hr = E_FAIL;
|
|
}
|
|
|
|
ATOMICRELEASE(pTask);
|
|
*phdpaNew = NULL; // This has been freed by pTask->Release()
|
|
}
|
|
|
|
if (SUCCEEDED(hr)) {
|
|
pdsv->_bBkFilling = TRUE;
|
|
SetTimer(pdsv->_hwndView, DV_IDTIMER, ENUMTIMEOUT, NULL);
|
|
}
|
|
}
|
|
|
|
return pdsv->_bBkFilling;
|
|
}
|
|
|
|
|
|
HRESULT CDefView::EmptyBkgrndThread(BOOL fTerminate)
|
|
{
|
|
HRESULT hr = S_FALSE;
|
|
|
|
if (_pScheduler) {
|
|
if (fTerminate) {
|
|
// set the thread DIE timeout to 60 seconds (previously 4).
|
|
// The thread might be stuck in the middle of an RPC call,
|
|
// so our timeout needs to be at least as long as the network
|
|
// timeout. (An RPC call? That's right, because CDrives needs
|
|
// to call WNetGetWkstaInfo, which uses RPC.)
|
|
|
|
// Spinning up a drive can take a long time, too. Screw it.
|
|
// We now just wait forever.
|
|
|
|
// _pScheduler->Status( ITSSFLAG_THREAD_TERMINATE_TIMEOUT, 60*1000 );
|
|
|
|
// this blocks until the scheduler is dead.
|
|
ATOMICRELEASE(_pScheduler);
|
|
|
|
// CDTURNER
|
|
// as we don't terminate the thread anymore, changing the RefForIdle count would causes
|
|
// us to have an incorrect count and fault (there may be some objects left in the message
|
|
// queue waiting to be retrieved......
|
|
|
|
// if (_cRefForIdle != -1)
|
|
// {
|
|
// ASSERT(0); // this is a wierd case that we
|
|
// should check out when it happens.
|
|
// Note that we will err on the side of not
|
|
// releasing enough rather than too often,
|
|
// which should never happen.
|
|
// There is only a tiny window when the forgotme
|
|
// flag is not set and the release has happened,
|
|
// so the chance of a memory leak is nearly 0.
|
|
// _cRefForIdle = 0;
|
|
// ChangeRefForIdle(this, FALSE);
|
|
// }
|
|
|
|
hr = NOERROR;
|
|
} else {
|
|
// tell it to wait 10 seconds while emptying....
|
|
// _pScheduler->Status( ITSSFLAG_THREAD_TERMINATE_TIMEOUT, 10*1000 );
|
|
|
|
if (_bEmptyingScheduler) {
|
|
// we are already in the removetasks call and got caught with an interthread
|
|
// sendmessage...
|
|
return S_FALSE;
|
|
}
|
|
_bEmptyingScheduler = TRUE;
|
|
|
|
// empty the queue and wait until it is empty.....
|
|
hr = _pScheduler->RemoveTasks(TOID_NULL, ITSAT_DEFAULT_LPARAM, TRUE);
|
|
|
|
_bEmptyingScheduler = FALSE;
|
|
|
|
// NOTE: as we are not terminating this thread, we will
|
|
// NOTE: assume that anything left in the queue after 10 seconds is
|
|
// NOTE: pretty harmless...
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
// Enumeration loop for the main GUI thread.
|
|
HRESULT CDefView::FillObjects(BOOL bRefresh, PDVSAVEHEADER pSaveHeader, UINT uLen, BOOL fInteractive)
|
|
{
|
|
IEnumIDList* peunk;
|
|
LPITEMIDLIST pidl;
|
|
ULONG celt;
|
|
DWORD dwTime;
|
|
BOOL fTimeOut;
|
|
HRESULT hres;
|
|
DWORD dwEnumFlags;
|
|
DWORD dwTimeout = _fs.fFlags & FWF_BESTFITWINDOW ? ENUMTIMEOUT : SHORTENUMTIMEOUT;
|
|
HDPA hdpaNew;
|
|
DECLAREWAITCURSOR;
|
|
|
|
#ifdef TIMING
|
|
dwStart = GetTickCount();
|
|
#endif
|
|
|
|
_fFileListEnumDone = FALSE;
|
|
|
|
SetWaitCursor();// This is a potentially long operation
|
|
EmptyBkgrndThread(FALSE);// make sure we are not doing anything in the background anymore...
|
|
_ClearSelectList();// We should not save the selection if doing refresh.
|
|
|
|
if (bRefresh) {
|
|
DefView_UpdateGlobalFlags(this);
|
|
_RemoveObject(NULL, FALSE);
|
|
}
|
|
|
|
// Setup the enum flags.
|
|
dwEnumFlags = SHCONTF_NONFOLDERS;
|
|
if (_fShowAllObjects) {
|
|
dwEnumFlags |= SHCONTF_INCLUDEHIDDEN;
|
|
}
|
|
|
|
//Is this View in Common Dialog
|
|
if (DV_CDB_IsCommonDialog(this) && !(dwEnumFlags & SHCONTF_INCLUDEHIDDEN)) {
|
|
//Ask Common dialog if its wants to show all files
|
|
ICommDlgBrowser2* pcdb2;
|
|
if (SUCCEEDED(_psb->QueryInterface(IID_ICommDlgBrowser2, (void**)&pcdb2))) {
|
|
DWORD dwFlags = 0;
|
|
pcdb2->GetViewFlags(&dwFlags);
|
|
if (dwFlags & CDB2GVF_SHOWALLFILES)
|
|
dwEnumFlags |= SHCONTF_INCLUDEHIDDEN;
|
|
pcdb2->Release();
|
|
}
|
|
}
|
|
|
|
if (!(_fs.fFlags & FWF_NOSUBFOLDERS))
|
|
dwEnumFlags |= SHCONTF_FOLDERS;
|
|
|
|
peunk = NULL; // in case callers dont do this
|
|
hres = _pshf->EnumObjects(fInteractive ? _hwndMain : NULL, dwEnumFlags, &peunk);
|
|
if (hres != S_OK) {
|
|
// S_FALSE is success but empty (for doc-find)
|
|
if (hres == S_FALSE) {
|
|
CallCB(SFVM_REFRESH, FALSE, 0);
|
|
_ShowListviewIcons();
|
|
}
|
|
if (peunk == NULL)
|
|
goto Done;
|
|
}
|
|
|
|
IUnknown_SetSite(peunk, SAFECAST(this, IOleCommandTarget*)); // give enum a ref to us
|
|
|
|
hdpaNew = DPA_Create(16);
|
|
if (!hdpaNew)
|
|
goto Error1;
|
|
|
|
// 99/05/11 #301779 vtan: IShellFolder::EnumObjects() returns an
|
|
// IEnumIDList for the shell folder. To do this it uses FindFirstFile
|
|
// on Win9x and FindFirstFileEx on WinNT. Either way for a removable
|
|
// disk at the root directory, the "." and ".." FindFirstFile results
|
|
// do not exist and the HANDLE returned is invalid. The file system
|
|
// IShellFolder accounts for this and returns S_FALSE meaning the
|
|
// call didn't result in an error but really contains no valid
|
|
// enumeration. In this case use an empty HDPA (created above) as the
|
|
// PIDL list that's current and invoke CDefView::FillDone() on this.
|
|
if ((hres == S_FALSE) && (peunk != NULL)) {
|
|
goto EmptyFillDone;
|
|
}
|
|
|
|
TIMEIN(_Fill);
|
|
TIMEIN(_AddObject);
|
|
TIMEIN(_GetIcon);
|
|
TIMEIN(_GetName);
|
|
TIMEIN(_FSNotify);
|
|
TIMEIN(_EnumNext);
|
|
TIMEIN(_RestoreState);
|
|
|
|
TIMEIN(_WMNotify);
|
|
TIMEIN(_LVChanging);
|
|
TIMEIN(_LVChanged);
|
|
TIMEIN(_LVDelete);
|
|
TIMEIN(_LVGetDispInfo);
|
|
|
|
TIMESTART(_Fill);
|
|
TraceMsg(TF_DEFVIEW, "::FillObjects(%s) **", DV_Name(this));
|
|
|
|
// If the callback returns S_OK to SFVM_BACKGROUNDENUM, start the idle thread immediately.
|
|
if (CallCB(SFVM_BACKGROUNDENUM, 0, 0) == S_OK) {
|
|
TraceMsg(TF_DEFVIEW, "::FillObjects : Start idle thread immediately");
|
|
if (DV_StartIdle(this, peunk, &hdpaNew, bRefresh)) {
|
|
goto Done;
|
|
}
|
|
if (!hdpaNew)
|
|
hdpaNew = DPA_Create(16);
|
|
if (!hdpaNew)
|
|
goto Error1;
|
|
|
|
fTimeOut = TRUE; // Failed to start, don't try it again.
|
|
} else {
|
|
fTimeOut = FALSE;
|
|
}
|
|
|
|
dwTime = GetTickCount();
|
|
while (DV_Next(this, peunk, 1, &pidl, &celt) == S_OK) {
|
|
if (DPA_AppendPtr(hdpaNew, pidl) == -1) {
|
|
SHFree(pidl);
|
|
}
|
|
pidl = NULL;
|
|
|
|
// Are we taking too long?
|
|
if (!fTimeOut && ((GetTickCount() - dwTime) > dwTimeout)) {
|
|
fTimeOut = TRUE;
|
|
|
|
if (!_IsDesktop()) {
|
|
if (DV_StartIdle(this, peunk, &hdpaNew, bRefresh)) {
|
|
goto Done;
|
|
}
|
|
if (!hdpaNew)
|
|
hdpaNew = DPA_Create(16);
|
|
if (!hdpaNew)
|
|
goto Error1;
|
|
}
|
|
}
|
|
}
|
|
|
|
DPA_Sort(hdpaNew, _GetCompareFunction(), (LPARAM)this);
|
|
|
|
EmptyFillDone:
|
|
this->FillDone(hdpaNew, pSaveHeader, uLen, bRefresh, fInteractive);
|
|
|
|
Error1:;
|
|
IUnknown_SetSite(peunk, NULL); // Break the site back pointer.
|
|
Done:
|
|
ATOMICRELEASE(peunk);
|
|
ResetWaitCursor();
|
|
|
|
return hres;
|
|
}
|
|
|
|
|
|
HRESULT CDefView::FillObjectsShowHide(BOOL bRefresh, PDVSAVEHEADER pSaveHeader, UINT uLen, BOOL fInteractive)
|
|
{
|
|
HRESULT hres;
|
|
|
|
_bUpdatePending = FALSE;
|
|
_bUpdatePendingPending = FALSE; // just in case we have a WM_TIMER message around
|
|
hres = this->FillObjects(bRefresh, pSaveHeader, uLen, fInteractive);
|
|
if (!_hwndListview) {
|
|
return hres;
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
// cache this for error reporting
|
|
_hres = hres;
|
|
#endif
|
|
|
|
if (SUCCEEDED(hres)) {
|
|
// If previous enumeration failed, we need to make it visible.
|
|
if (_fEnumFailed) {
|
|
_fEnumFailed = FALSE;
|
|
_ShowListviewIcons();
|
|
}
|
|
} else {
|
|
// The fill objects failed for some reason, we should
|
|
// display an appropriate error message.
|
|
if (!_fEnumFailed) {
|
|
ShowWindow(_hwndListview, SW_HIDE);
|
|
}
|
|
|
|
TraceMsg(TF_WARNING, "::FillObjects failed (%x)", hres);
|
|
|
|
_fEnumFailed = TRUE;
|
|
}
|
|
|
|
return hres;
|
|
}
|
|
|
|
|
|
void DefView_MoveSelectedItems(CDefView* pdsv, int dx, int dy, BOOL fAll)
|
|
{
|
|
LVUtil_MoveSelectedItems(pdsv->_hwndListview, dx, dy, fAll);
|
|
pdsv->_bItemsMoved = TRUE;
|
|
pdsv->_bClearItemPos = FALSE;
|
|
}
|
|
|
|
|
|
TCHAR const c_szDelete[] = TEXT("delete");
|
|
TCHAR const c_szCut[] = TEXT("cut");
|
|
TCHAR const c_szCopy[] = TEXT("copy");
|
|
TCHAR const c_szLink[] = TEXT("link");
|
|
TCHAR const c_szProperties[] = TEXT("properties");
|
|
TCHAR const c_szPaste[] = TEXT("paste");
|
|
TCHAR const c_szPasteLink[] = TEXT("pastelink");
|
|
// char const c_szPasteSpecial[] = "pastespecial";
|
|
TCHAR const c_szRename[] = TEXT("rename");
|
|
|
|
|
|
// This function processes command from explorer menu (FCIDM_*)
|
|
|
|
// HACK ALERT:
|
|
// This implementation uses following assumptions.
|
|
// (1) The IShellFolder uses CDefFolderMenu.
|
|
// (2) The CDefFolderMenu always add the folder at the top.
|
|
|
|
|
|
#define EC_SELECTION 0
|
|
#define EC_BACKGROUND 1
|
|
#define EC_EITHER 3
|
|
|
|
|
|
HRESULT DefView_ExplorerCommand(CDefView* pdsv, UINT idFCIDM)
|
|
{
|
|
HRESULT hres = E_FAIL;
|
|
|
|
// s_idMap[i][0] = Defview menuitem ID
|
|
// s_idMap[i][1] = FALSE, context menu; TRUE; background menu
|
|
// s_idMap[i][2] = Folder menuitem ID
|
|
static struct
|
|
{
|
|
UINT idmFC;
|
|
UINT f_Background;
|
|
LPCTSTR pszVerb;
|
|
} const c_idMap[] = {
|
|
{ SFVIDM_FILE_RENAME, EC_SELECTION, c_szRename },
|
|
{ SFVIDM_FILE_DELETE, EC_SELECTION, c_szDelete },
|
|
{ SFVIDM_FILE_PROPERTIES, EC_EITHER, c_szProperties },
|
|
{ SFVIDM_EDIT_COPY, EC_SELECTION, c_szCopy },
|
|
{ SFVIDM_EDIT_CUT, EC_SELECTION, c_szCut },
|
|
{ SFVIDM_FILE_LINK, EC_SELECTION, c_szLink },
|
|
{ SFVIDM_EDIT_PASTE, EC_BACKGROUND, c_szPaste },
|
|
{ SFVIDM_EDIT_PASTELINK, EC_BACKGROUND, c_szPasteLink },
|
|
// { SFVIDM_EDIT_PASTESPECIAL,TRUE, c_szPasteSpecial },
|
|
};
|
|
int i;
|
|
|
|
for (i = 0; i < ARRAYSIZE(c_idMap); i++) {
|
|
if (c_idMap[i].idmFC == idFCIDM) {
|
|
IContextMenu* pcm = NULL;
|
|
|
|
if (c_idMap[i].f_Background == EC_BACKGROUND) {
|
|
TryBackground:
|
|
pdsv->_pshf->CreateViewObject(pdsv->_hwndMain, IID_IContextMenu, (void**)&pcm);
|
|
} else {
|
|
DECLAREWAITCURSOR;
|
|
SetWaitCursor();
|
|
pcm = pdsv->_GetContextMenuFromSelection();
|
|
ResetWaitCursor();
|
|
|
|
if (!pcm && c_idMap[i].f_Background == EC_EITHER && !ListView_GetSelectedCount(pdsv->_hwndListview)) {
|
|
goto TryBackground;
|
|
}
|
|
}
|
|
|
|
if (pcm) {
|
|
CMINVOKECOMMANDINFOEX ici;
|
|
|
|
ZeroMemory(&ici, SIZEOF(ici));
|
|
ici.cbSize = SIZEOF(ici);
|
|
ici.hwnd = pdsv->_hwndMain;
|
|
ici.nShow = SW_NORMAL;
|
|
|
|
// record if shift or control was being held down
|
|
SetICIKeyModifiers(&ici.fMask);
|
|
|
|
// We need to call QueryContextMenu() so that CDefFolderMenu
|
|
// can initialize its dispatch table correctly.
|
|
HMENU hmenu = CreatePopupMenu();
|
|
#ifdef UNICODE
|
|
// Fill in both the ansi verb and the unicode verb since we
|
|
// don't know who is going to be processing this thing.
|
|
CHAR szVerbAnsi[40];
|
|
SHUnicodeToAnsi(c_idMap[i].pszVerb, szVerbAnsi, ARRAYSIZE(szVerbAnsi));
|
|
ici.lpVerb = szVerbAnsi;
|
|
ici.lpVerbW = c_idMap[i].pszVerb;
|
|
ici.fMask |= CMIC_MASK_UNICODE;
|
|
#else
|
|
ici.lpVerb = c_idMap[i].pszVerb;
|
|
#endif
|
|
|
|
if (hmenu) {
|
|
IUnknown_SetSite(pcm, SAFECAST(pdsv, IOleCommandTarget*));
|
|
pcm->QueryContextMenu(hmenu, 0, SFVIDM_CONTEXT_FIRST, SFVIDM_CONTEXT_LAST, 0);
|
|
pdsv->_bContextMenuMode = TRUE;
|
|
hres = pdsv->_InvokeCommand(pcm, &ici);
|
|
pdsv->_bContextMenuMode = FALSE;
|
|
DestroyMenu(hmenu);
|
|
IUnknown_SetSite(pcm, NULL);
|
|
}
|
|
|
|
ATOMICRELEASE(pcm);
|
|
if (pdsv->_pcmSel == pcm) {
|
|
DV_FlushCachedMenu(pdsv);
|
|
}
|
|
} else {
|
|
// We should beep if when one of those object keys are
|
|
// pressed when there is no selection.
|
|
MessageBeep(0);
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
ASSERT(i < ARRAYSIZE(c_idMap));
|
|
|
|
return hres;
|
|
}
|
|
|
|
|
|
STDAPI_(BOOL) Def_IsPasteAvailable(IDropTarget* pdtgt, DWORD* pdwEffect);
|
|
|
|
|
|
BOOL DefView_AllowCommand(CDefView* pdsv, UINT uID, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
DWORD dwAttribsIn;
|
|
DWORD dwEffect;
|
|
|
|
switch (uID) {
|
|
case SFVIDM_EDIT_PASTE:
|
|
return Def_IsPasteAvailable(pdsv->_pdtgtBack, &dwEffect);
|
|
case SFVIDM_EDIT_PASTELINK:
|
|
Def_IsPasteAvailable(pdsv->_pdtgtBack, &dwEffect);
|
|
return dwEffect & DROPEFFECT_LINK;
|
|
case SFVIDM_EDIT_COPY:
|
|
dwAttribsIn = SFGAO_CANCOPY;
|
|
break;
|
|
case SFVIDM_EDIT_CUT:
|
|
dwAttribsIn = SFGAO_CANMOVE;
|
|
break;
|
|
case SFVIDM_FILE_DELETE:
|
|
dwAttribsIn = SFGAO_CANDELETE;
|
|
break;
|
|
case SFVIDM_FILE_LINK:
|
|
dwAttribsIn = SFGAO_CANLINK;
|
|
break;
|
|
case SFVIDM_FILE_PROPERTIES:
|
|
dwAttribsIn = SFGAO_HASPROPSHEET;
|
|
break;
|
|
default:
|
|
ASSERT(FALSE);
|
|
return FALSE;
|
|
}
|
|
|
|
return (DefView_GetAttributesFromSelection(pdsv, dwAttribsIn) & dwAttribsIn);
|
|
}
|
|
|
|
|
|
// 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 (_pidlMonitor) {
|
|
pidl = ILClone(_pidlMonitor);
|
|
}
|
|
}
|
|
|
|
return pidl;
|
|
}
|
|
|
|
|
|
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_szWindowsHlp[] = TEXT("windows.hlp");
|
|
EXTERN_C TCHAR const c_szHtmlWindowsHlp[] = TEXT("windows.chm");
|
|
|
|
|
|
#ifdef CUSTOM_BACKGROUND
|
|
// A helper function for grovelling the registry
|
|
BOOL GetRegThing(HKEY ahk[], int nhk, LPCTSTR pszValue, LPBYTE lpThing, DWORD dwThingType, DWORD cbBytes, void* lpDefault)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < nhk; i++) {
|
|
DWORD dwType;
|
|
|
|
ASSERT(NULL != ahk[i]);
|
|
|
|
if (ERROR_SUCCESS == SHQueryValueEx(ahk[i], pszValue, NULL, &dwType, lpThing, &cbBytes) && dwType == dwThingType) {
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
if (lpDefault) {
|
|
switch (dwThingType) {
|
|
case REG_SZ:
|
|
StrCpyN((LPTSTR)lpThing, (LPTSTR)lpDefault, cbBytes);
|
|
return TRUE;
|
|
case REG_BINARY:
|
|
case REG_DWORD:
|
|
MoveMemory(lpThing, lpDefault, cbBytes);
|
|
return TRUE;
|
|
default:
|
|
ASSERT(0);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
#endif // CUSTOM_BACKGROUND
|
|
|
|
|
|
// web view background colors, click mode, etc have changed
|
|
void CDefView::_UpdateListviewColors(BOOL fClassic)
|
|
{
|
|
LVBKIMAGE lvbki;
|
|
|
|
_fClassic = fClassic;
|
|
|
|
// some common stuff up front
|
|
ZeroMemory(&lvbki, SIZEOF(lvbki));
|
|
|
|
// First read the registry/desktop.ini
|
|
TCHAR szImage[INTERNET_MAX_URL_LENGTH];
|
|
int i;
|
|
|
|
szImage[0] = 0;
|
|
for (i = 0; i < ARRAYSIZE(_crCustomColors); i++)
|
|
_crCustomColors[i] = CLR_MYINVALID;
|
|
|
|
if (!DV_CDB_IsCommonDialog(this) && !_IsDesktop()) {
|
|
// BUGBUG kenwic 052599 #340912 Background needs to change even in classic mode FIXED kenwic 052599
|
|
// get the background bitmap
|
|
if (!m_cFrame._StringFromViewID(&VID_FolderState, szImage, ARRAYSIZE(szImage), ID_EXTVIEWICONAREAIMAGE)) {
|
|
m_cFrame._StringFromView(m_cFrame.m_uView, szImage, ARRAYSIZE(szImage), ID_EXTVIEWICONAREAIMAGE);
|
|
}
|
|
|
|
// Set up the listview image, if any
|
|
if (szImage[0]) {
|
|
lvbki.ulFlags = LVBKIF_SOURCE_URL | LVBKIF_STYLE_TILE;
|
|
lvbki.pszImage = szImage;
|
|
}
|
|
}
|
|
|
|
// change the differing stuff
|
|
if (!_fClassic && !DV_CDB_IsCommonDialog(this) && !_IsDesktop()) {
|
|
// VID_FolderState is what the customize wizard uses to set background
|
|
// image and text colors. These should override any view defined settings
|
|
// so check it first.
|
|
|
|
// 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
|
|
for (i = 0; i < ARRAYSIZE(_crCustomColors); i++) {
|
|
COLORREF cr;
|
|
if (m_cFrame._ColorFromViewID(&VID_FolderState, &cr, i) || m_cFrame._ColorFromView(m_cFrame.m_uView, &cr, i)) {
|
|
_crCustomColors[i] = PALETTERGB(0, 0, 0) | cr;
|
|
}
|
|
}
|
|
|
|
if (!ISVALIDCOLOR(_crCustomColors[CRID_CUSTOMTEXTBACKGROUND]) && szImage[0]) {
|
|
_crCustomColors[CRID_CUSTOMTEXTBACKGROUND] = CLR_NONE;
|
|
}
|
|
|
|
#ifdef FLAT_LISTVIEW
|
|
// Make the listview flat...
|
|
SetWindowBits(_hwndListview, GWL_EXSTYLE, WS_EX_CLIENTEDGE, 0);
|
|
#endif
|
|
|
|
#ifdef FLAT_SCROLLBAR
|
|
// ...and its scrollbar too.
|
|
ListView_SetExtendedListViewStyleEx(_hwndListview, LVS_EX_FLATSB, LVS_EX_FLATSB);
|
|
FlatSB_SetScrollProp(_hwndListview, WSB_PROP_HSTYLE, FSB_FLAT_MODE, TRUE);
|
|
FlatSB_SetScrollProp(_hwndListview, WSB_PROP_VSTYLE, FSB_FLAT_MODE, TRUE);
|
|
#endif
|
|
} else {
|
|
#ifdef FLAT_LISTVIEW
|
|
// Make the listview 3d
|
|
SetWindowBits(_hwndListview, GWL_EXSTYLE, WS_EX_CLIENTEDGE, WS_EX_CLIENTEDGE);
|
|
#endif
|
|
|
|
#ifdef FLAT_SCROLLBAR
|
|
// ...and its scrollbar too.
|
|
LONG lExStyle = ListView_GetExtendedListViewStyle(_hwndListview);
|
|
if (lExStyle & LVS_EX_FLATSB) {
|
|
// NOTE: the below comment is no longer true
|
|
// there's no way to remove the FLATSB style, so simulate normal mode
|
|
FlatSB_SetScrollProp(_hwndListview, WSB_PROP_HSTYLE, FSB_REGULAR_MODE, TRUE);
|
|
FlatSB_SetScrollProp(_hwndListview, WSB_PROP_VSTYLE, FSB_REGULAR_MODE, TRUE);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
// wrap up the common stuff
|
|
|
|
// if its a thumbvw, do the thumbvw thing, always do standard listvw thing.
|
|
if (m_cFrame.IsSFVExtension()) {
|
|
IShellFolderView* pSFV = m_cFrame.GetExtendedISFV();
|
|
|
|
if (pSFV) {
|
|
HRESULT hres;
|
|
IDefViewExtInit2* pShellView = NULL;
|
|
|
|
hres = pSFV->QueryInterface(IID_IDefViewExtInit2, (void**)&pShellView);
|
|
if (SUCCEEDED(hres)) {
|
|
WCHAR wszImage[INTERNET_MAX_URL_LENGTH];
|
|
SHTCharToUnicode(szImage, wszImage, ARRAYSIZE(wszImage));
|
|
pShellView->SetViewWindowBkImage(wszImage);
|
|
ATOMICRELEASE(pShellView);
|
|
}
|
|
}
|
|
}
|
|
|
|
ListView_SetBkImage(_hwndListview, &lvbki);
|
|
DSV_SetFolderColors(this);
|
|
this->UpdateSelectionMode();
|
|
}
|
|
|
|
|
|
BOOL CDefView::_IsReportView()
|
|
{
|
|
return !m_cFrame.IsSFVExtension() && ((LVStyleFromView(this) & LVS_TYPEMASK) == LVS_REPORT);
|
|
}
|
|
|
|
|
|
class CCurrentSelectionTransfer
|
|
{
|
|
public:
|
|
CCurrentSelectionTransfer(void);
|
|
~CCurrentSelectionTransfer(void);
|
|
|
|
HRESULT GetSelection(IShellFolderView* pISFV);
|
|
HRESULT SetSelection(IShellFolderView* pISFV, IShellView* pISV);
|
|
private:
|
|
UINT m_uiItemCount;
|
|
LPITEMIDLIST* m_PIDLs;
|
|
};
|
|
|
|
CCurrentSelectionTransfer::CCurrentSelectionTransfer(void) :
|
|
m_uiItemCount(0),
|
|
m_PIDLs(NULL)
|
|
{
|
|
|
|
}
|
|
|
|
CCurrentSelectionTransfer::~CCurrentSelectionTransfer(void)
|
|
{
|
|
if (m_PIDLs != NULL) {
|
|
UINT i;
|
|
|
|
for (i = 0; i < m_uiItemCount; ++i)
|
|
ILFree(m_PIDLs[i]);
|
|
SHFree(m_PIDLs);
|
|
m_PIDLs = NULL;
|
|
m_uiItemCount = 0;
|
|
}
|
|
}
|
|
|
|
|
|
HRESULT CCurrentSelectionTransfer::GetSelection(IShellFolderView* pISFV)
|
|
{
|
|
HRESULT hres;
|
|
LPCITEMIDLIST* pidls;
|
|
|
|
hres = pISFV->GetSelectedObjects(&pidls, &m_uiItemCount);
|
|
if (SUCCEEDED(hres)) {
|
|
if (m_uiItemCount > 0) {
|
|
m_PIDLs = reinterpret_cast<LPITEMIDLIST*>(SHAlloc(m_uiItemCount * sizeof(LPCITEMIDLIST)));
|
|
if (m_PIDLs != NULL) {
|
|
UINT i;
|
|
|
|
for (i = 0; i < m_uiItemCount; ++i)
|
|
m_PIDLs[i] = ILClone(pidls[i]);
|
|
} else {
|
|
// 99/08/16 vtan #386924: STRESS: m_uiItemCount can be
|
|
// > 0 but m_PIDLs failed to get allocated. SetSelection()
|
|
// will blindly dereference this assuming that count
|
|
// > 0 means successfully allocated.
|
|
m_uiItemCount = 0;
|
|
hres = E_OUTOFMEMORY;
|
|
}
|
|
}
|
|
|
|
if (pidls != NULL)
|
|
LocalFree(pidls);
|
|
}
|
|
|
|
return(hres);
|
|
}
|
|
|
|
|
|
HRESULT CCurrentSelectionTransfer::SetSelection(IShellFolderView* pISFV, IShellView* pISV)
|
|
{
|
|
HRESULT hres = S_OK;
|
|
|
|
if ((m_PIDLs != NULL) && (m_uiItemCount > 0)) {
|
|
UINT i, uiFlags;
|
|
|
|
THR(pISFV->SetRedraw(FALSE));
|
|
uiFlags = SVSI_SELECT | SVSI_DESELECTOTHERS | SVSI_ENSUREVISIBLE | SVSI_FOCUSED;
|
|
for (i = 0; SUCCEEDED(hres) && (i < m_uiItemCount); ++i) {
|
|
LPCITEMIDLIST pidl;
|
|
|
|
pidl = m_PIDLs[i];
|
|
if (pidl != NULL) {
|
|
hres = pISV->SelectItem(pidl, uiFlags);
|
|
}
|
|
uiFlags &= ~(SVSI_DESELECTOTHERS | SVSI_ENSUREVISIBLE | SVSI_FOCUSED);
|
|
}
|
|
THR(pISFV->SetRedraw(TRUE));
|
|
}
|
|
|
|
return(hres);
|
|
}
|
|
|
|
|
|
BOOL CDefView::HasCurrentViewWindowFocus()
|
|
{
|
|
BOOL fRet = false;
|
|
HWND hwndCurrentFocus = GetFocus();
|
|
if (hwndCurrentFocus) {
|
|
HWND hwndView;
|
|
if (m_cFrame.IsSFVExtension()) {
|
|
hwndView = m_cFrame.GetExtendedViewWindow();
|
|
} else {
|
|
hwndView = _hwndListview;
|
|
}
|
|
fRet = (SHIsChildOrSelf(hwndView, hwndCurrentFocus) == S_OK);
|
|
}
|
|
|
|
return fRet;
|
|
}
|
|
|
|
|
|
HWND CDefView::ViewWindowSetFocus()
|
|
{
|
|
HWND hwndView;
|
|
if (m_cFrame.IsSFVExtension()) {
|
|
hwndView = m_cFrame.GetExtendedViewWindow();
|
|
} else {
|
|
hwndView = _hwndListview;
|
|
}
|
|
|
|
SetFocus(hwndView);
|
|
|
|
if (!_IsDesktop()) {
|
|
m_cFrame.m_uState = SVUIA_ACTIVATE_FOCUS;
|
|
}
|
|
|
|
return hwndView;
|
|
}
|
|
|
|
|
|
// we are switching from the current view to extended view uID
|
|
// =>switch to webview type view from webview typ view or non webview type view.
|
|
HRESULT CDefView::_SwitchToViewIDPVID(UINT uID, SHELLVIEWID const* pvid, BOOL bForce)
|
|
{
|
|
HRESULT hres;
|
|
UINT fvmOld;
|
|
DWORD dwStyleOld;
|
|
SHELLSTATE ss;
|
|
BOOL fCombinedViewOld = (BOOL)_fCombinedView;
|
|
BOOL bSetFocusRequired = HasCurrentViewWindowFocus();
|
|
|
|
CCurrentSelectionTransfer selectionTransfer;
|
|
|
|
selectionTransfer.GetSelection(this);
|
|
|
|
// VID_FolderState is what's used to specify background images
|
|
// on the listview -- it's not a real view.
|
|
if (pvid && IsEqualIID(*pvid, VID_FolderState)) {
|
|
ASSERT(!IsEqualIID(*pvid, VID_FolderState));
|
|
return E_FAIL;
|
|
}
|
|
|
|
// we shouln't be coming here if for details
|
|
ASSERT(_bLoadedColumns || !(pvid && IsEqualIID(*pvid, VID_Details)));
|
|
|
|
// remember current view just in case the new view fails
|
|
fvmOld = _fs.ViewMode;
|
|
dwStyleOld = GetWindowStyle(_hwndListview) & LVS_TYPEMASK;
|
|
|
|
// For now, the desktop is always a combined view...
|
|
if (_IsDesktop()) {
|
|
SHGetSetSettings(&ss, SSF_HIDEICONS | SSF_DESKTOPHTML, 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);
|
|
DSV_SetFolderColors(this);
|
|
}
|
|
|
|
// We don't need to OnDeactivate();OnActivate() to
|
|
// remerge the menus because that is really dealing
|
|
// with focus changes
|
|
|
|
// Show the extended view
|
|
if (pvid) {
|
|
hres = m_cFrame.ShowExtView(pvid, bForce);
|
|
} else {
|
|
hres = m_cFrame.ShowExtView(uID, bForce);
|
|
}
|
|
|
|
if (SUCCEEDED(hres)) {
|
|
RECT rcClient;
|
|
|
|
// Make sure the new view is the correct size
|
|
GetClientRect(_hwndView, &rcClient);
|
|
m_cFrame.SetRect(&rcClient);
|
|
|
|
if (_fCombinedView && DV_SHOWICONS(this)) {
|
|
_ShowListviewIcons();
|
|
} else {
|
|
ShowWindow(_hwndListview, SW_HIDE);
|
|
|
|
// We want to process FSNotify messages to keep the
|
|
// listview up to date even though it may not be
|
|
// shown for a while -- there may be a DefViewOC
|
|
// on this page which will grab the listview and use it.
|
|
_bUpdatePendingPending = TRUE;
|
|
SetTimer(_hwndView, DV_IDTIMER_UPDATEPENDING, UPDATEPENDINGTIME, NULL);
|
|
}
|
|
} else {
|
|
// If the previous view was an extended view, the above failed
|
|
// ShowExtView just nuked the view and nothing is showing.
|
|
// Go back to a listview view.
|
|
TraceMsg(TF_DEFVIEW, "_SwitchToViewIDPVID failed ShowExtView");
|
|
_SwitchToViewFVM(fvmOld, dwStyleOld, TRUE);
|
|
}
|
|
|
|
selectionTransfer.SetSelection(this, this);
|
|
// If we had focus, make sure we continue to have the focus, especially if we switched to extended views
|
|
if (bSetFocusRequired) {
|
|
CallCB(SFVM_SETFOCUS, 0, 0);
|
|
ViewWindowSetFocus();
|
|
}
|
|
|
|
// if we're switching from details to thumbnail view we'll have Choose Column item
|
|
// in the view menu that we don't want -- so remove it
|
|
if (_hmenuCur && !_IsReportView())
|
|
DeleteMenu(_hmenuCur, SFVIDM_VIEW_COLSETTINGS, MF_BYCOMMAND);
|
|
|
|
CheckToolbar();
|
|
#if 0
|
|
// update menus, i.e. add Choose Columns to the view menu if Details view is selected
|
|
// or remove it otherwise
|
|
UINT uState = _uState;
|
|
_SetUpMenus(uState); // calls OnDeactivate which sets _uState to SVUIA_DEACTIVATE
|
|
_uState = uState;
|
|
#endif
|
|
_EnableDisableTBButtons();
|
|
|
|
// make sure that the listview settings get refreshed anyway (back image)
|
|
_UpdateListviewColors(_fClassic);
|
|
|
|
return hres;
|
|
}
|
|
|
|
|
|
// we are switching from the current view to a normal listview view
|
|
// => in wv or nonwv mode
|
|
HRESULT CDefView::_SwitchToViewFVM(UINT fvmNew, DWORD dwStyle, BOOL fForce)
|
|
{
|
|
HWND hwndCurrentFocus;
|
|
BOOL bSetFocusRequired, bHasNormalView;
|
|
CCurrentSelectionTransfer selectionTransfer;
|
|
BOOL fPrevViewExtended = m_cFrame.IsWebView();
|
|
|
|
ASSERT(_hwndListview);
|
|
|
|
hwndCurrentFocus = GetFocus();
|
|
bSetFocusRequired = HasCurrentViewWindowFocus();
|
|
selectionTransfer.GetSelection(this);
|
|
bHasNormalView = _HasNormalView();
|
|
|
|
// if we haven't loaded the columns yet, do that now
|
|
if (fvmNew == FVM_DETAILS && !_bLoadedColumns) {
|
|
// It's okay if (NULL == _pSaveHeader) because that case will be handled
|
|
// in CColumnPointer::CColumnPointer(NULL, ...), which is called in AddColumns().
|
|
this->AddColumns();
|
|
_SetSortArrows();
|
|
}
|
|
|
|
if (fPrevViewExtended) // webview mode?
|
|
{
|
|
// If the DefViewOC is up, then the listview selection is correct,
|
|
// so we don't need to clear the selection state.
|
|
if (!_fGetWindowLV) {
|
|
// I don't want to deal with finding the current selection
|
|
// of the extended view, so deselect everything after
|
|
// switching away from one. This is good enough.
|
|
ListView_SetItemState(_hwndListview, -1, 0, LVIS_SELECTED);
|
|
}
|
|
}
|
|
|
|
// 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);
|
|
DSV_SetFolderColors(this);
|
|
}
|
|
|
|
// extended shell view (thumbview) hosted in OC and not killing the OC,
|
|
// so must be swapping to listview with currently active OC. swap and kill
|
|
// extended shell view.
|
|
if (m_cFrame.IsSFVExtension()) {
|
|
RECT rcClient;
|
|
|
|
if (m_cFrame.IsWebView() && !fForce && _pocWinMan) // oc hosting remains but swap holsted view??
|
|
{
|
|
_pocWinMan->SwapWindow(_hwndListview, &_pocWinMan);
|
|
_fGetWindowLV = TRUE;
|
|
}
|
|
// kill TV either if is extended view ad not killing it, or not extended view.
|
|
if (!m_cFrame.IsWebView() || !fForce) {
|
|
m_cFrame.KillActiveSFV();
|
|
// Make sure the new view is the correct size
|
|
GetClientRect(_hwndView, &rcClient);
|
|
m_cFrame.SetRect(&rcClient);
|
|
}
|
|
}
|
|
// change the listview's view before we hide the extended view
|
|
// on sfv, let wfv handle this.
|
|
DefView_SetViewMode(this, fvmNew, dwStyle);
|
|
if (bHasNormalView) {
|
|
ShowWindow(_hwndListview, SW_SHOW);
|
|
}
|
|
if (fForce) {
|
|
// now remove the extended view
|
|
if (m_cFrame.IsSFVExtension())
|
|
m_cFrame.SetViewWindowStyle(WS_EX_CLIENTEDGE, WS_EX_CLIENTEDGE);
|
|
m_cFrame.ShowExtView(CSFVFrame::HIDEEXTVIEW, FALSE);
|
|
HWND hwndXV = m_cFrame.GetExtendedViewWindow();
|
|
if (hwndXV)
|
|
SetWindowPos(hwndXV, NULL, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW | SWP_NOZORDER);
|
|
}
|
|
// make sure we're showing valid content
|
|
if (_bUpdatePending)
|
|
_ReloadListviewContent();
|
|
|
|
AutoAutoArrange(0);
|
|
|
|
selectionTransfer.SetSelection(this, this);
|
|
|
|
if (bSetFocusRequired) { // _hwndListview is the current view window. Let's set focus to it.
|
|
CallCB(SFVM_SETFOCUS, 0, 0);
|
|
ViewWindowSetFocus();
|
|
} else {
|
|
SetFocus(hwndCurrentFocus);
|
|
}
|
|
|
|
CheckToolbar();
|
|
// update menus, i.e. add Choose Columns to the view menu if Details view is selected
|
|
// or remove it otherwise
|
|
UINT uState = _uState;
|
|
_SetUpMenus(uState); // calls OnDeactivate which sets _uState to SVUIA_DEACTIVATE
|
|
_uState = uState;
|
|
|
|
_EnableDisableTBButtons();
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
int CDefView::CheckCurrentViewMenuItem(HMENU hmenu)
|
|
{
|
|
int iCurViewMenuItem = m_cFrame.CurExtViewId();
|
|
|
|
// If an extended view is showing our listview, then make the extended
|
|
// view selection range a normal CHECKBOX that can be turned off and
|
|
// make the large/small/list/details a selectable mode.
|
|
CheckMenuRadioItem(hmenu, SFVIDM_VIEW_EXTENDEDFIRST, SFVIDM_VIEW_EXTENDEDLAST, iCurViewMenuItem, MF_BYCOMMAND | MF_CHECKED);
|
|
if (m_cFrame.IsWebView()) {
|
|
MENUITEMINFO mii;
|
|
TCHAR szMenuName[50];
|
|
|
|
mii.cbSize = SIZEOF(mii);
|
|
mii.fMask = MIIM_TYPE;
|
|
mii.dwTypeData = szMenuName;
|
|
mii.cch = ARRAYSIZE(szMenuName);
|
|
if (GetMenuItemInfo(hmenu, iCurViewMenuItem, MF_BYCOMMAND, &mii)) {
|
|
mii.fType &= ~MFT_RADIOCHECK;
|
|
SetMenuItemInfo(hmenu, iCurViewMenuItem, MF_BYCOMMAND, &mii);
|
|
}
|
|
}
|
|
|
|
iCurViewMenuItem = (m_cFrame.m_uView == CSFVFrame::NOEXTVIEW) ? _DSV_GetMenuIDFromViewMode(_fs.ViewMode) : m_cFrame.CmdIdFromUid(m_cFrame.m_uView);
|
|
CheckMenuRadioItem(hmenu, SFVIDM_VIEW_FIRSTVIEW, SFVIDM_VIEW_SVEXTLAST, iCurViewMenuItem, MF_BYCOMMAND | MF_CHECKED);
|
|
return iCurViewMenuItem;
|
|
}
|
|
|
|
|
|
int CDefView::_CheckIfCustomizable()
|
|
{
|
|
int iCustomizable;
|
|
TCHAR szPath[MAX_PATH];
|
|
if (!SHRestricted(REST_NOCUSTOMIZEWEBVIEW) && _GetPath(szPath)) {
|
|
PathAppend(szPath, TEXT("Desktop.ini"));
|
|
|
|
DWORD dwAttributes;
|
|
BOOL bFileExistsAlready = PathFileExistsAndAttributes(szPath, &dwAttributes);
|
|
if (bFileExistsAlready) {
|
|
//If it is a UNC path, then the attributes will be -1.
|
|
if (dwAttributes == -1)
|
|
iCustomizable = DONTKNOW_IF_CUSTOMIZABLE;
|
|
else {
|
|
//It is a local path; We know the attributes for sure!
|
|
if (dwAttributes & FILE_ATTRIBUTE_READONLY)
|
|
iCustomizable = NOT_CUSTOMIZABLE;
|
|
else
|
|
iCustomizable = YES_CUSTOMIZABLE;
|
|
}
|
|
} else
|
|
iCustomizable = DONTKNOW_IF_CUSTOMIZABLE;
|
|
|
|
//If we still don't know...
|
|
if (iCustomizable == DONTKNOW_IF_CUSTOMIZABLE) {
|
|
// The file desktop.ini doesn't exist or it exists in a UNC path.
|
|
// So, try the hard method to see if the media is writeable.
|
|
|
|
//CreateFile is a costly process that is why we do it only rarely.
|
|
HANDLE hFile = CreateFile(szPath, GENERIC_WRITE, FILE_SHARE_READ, NULL, (bFileExistsAlready ? OPEN_EXISTING : CREATE_NEW), FILE_ATTRIBUTE_NORMAL, NULL);
|
|
if (hFile != INVALID_HANDLE_VALUE) {
|
|
iCustomizable = YES_CUSTOMIZABLE;
|
|
CloseHandle(hFile);
|
|
|
|
//Delete only if it didn't exist already
|
|
if (!bFileExistsAlready)
|
|
DeleteFile(szPath); //Delete the file that we just created.
|
|
} else
|
|
iCustomizable = NOT_CUSTOMIZABLE;
|
|
}
|
|
} else
|
|
iCustomizable = NOT_CUSTOMIZABLE;
|
|
|
|
return iCustomizable;
|
|
}
|
|
|
|
|
|
BOOL CDefView::_InvokeCustomWizard()
|
|
{
|
|
BOOL fRet = FALSE;
|
|
TCHAR szPath[MAX_PATH]; // To hold Current directory path
|
|
|
|
//Check if we already know if this folder is customizable!
|
|
if ((m_iCustomizable == DONTKNOW_IF_CUSTOMIZABLE) || (m_iCustomizable == MAYBE_CUSTOMIZABLE)) {
|
|
//Ok! This is a filesystem folder. See if it is customizable and remember that in this view.
|
|
m_iCustomizable = _CheckIfCustomizable();
|
|
}
|
|
|
|
if (m_iCustomizable == NOT_CUSTOMIZABLE) {
|
|
//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();
|
|
|
|
//BUGBUG! We may need to read this name from the registry. Check with ChristoB!
|
|
if (_GetPath(szPath)) {
|
|
SHELLEXECUTEINFO ExecInfo = {0};
|
|
TCHAR szParam[MAX_PATH + 20]; // To hold "<dirname> <(int)Folder HWND>"
|
|
// The Command line is of the form <dirname> <(int)Folder HWND>
|
|
|
|
#ifdef _WIN64
|
|
wnsprintf(szParam, sizeof(szParam), TEXT("%s;%d"), szPath, (INT_PTR)_hwndMain);
|
|
#else
|
|
wnsprintf(szParam, sizeof(szParam), TEXT("%s;%d"), szPath, (int)_hwndMain);
|
|
#endif
|
|
|
|
//We need to set SEE_MASK_NOCLOSEPROCESS to getback the process handle.
|
|
//FillExecInfo(ExecInfo, _hwndMain, NULL, TEXT("IESHWIZ.EXE"), szParam, NULL, SW_SHOWNORMAL);
|
|
|
|
ExecInfo.hwnd = _hwndMain;
|
|
ExecInfo.lpVerb = NULL;
|
|
ExecInfo.lpFile = TEXT("IESHWIZ.EXE");
|
|
ExecInfo.lpParameters = szParam;
|
|
ExecInfo.lpDirectory = szPath;
|
|
ExecInfo.nShow = SW_SHOWNORMAL;
|
|
ExecInfo.fMask = SEE_MASK_NOCLOSEPROCESS;
|
|
ExecInfo.cbSize = SIZEOF(SHELLEXECUTEINFO);
|
|
|
|
//The wizard makes _hwndMain the owner of the property sheet window. So, we don't have to disable
|
|
// this here.
|
|
|
|
fRet = ShellExecuteEx(&ExecInfo);
|
|
|
|
BOOL fNeedToRefresh = FALSE;
|
|
|
|
//Let's wait for the wizard to be terminated.
|
|
while (fRet) {
|
|
DWORD dwRet = MsgWaitForMultipleObjects(1, &ExecInfo.hProcess, FALSE, INFINITE, QS_ALLINPUT);
|
|
|
|
// MsgWaitForMultipleObjects can fail with -1 being returned!
|
|
if ((dwRet == WAIT_OBJECT_0) || (dwRet == -1)) {
|
|
fNeedToRefresh = TRUE;
|
|
break;
|
|
} else {
|
|
MSG msg;
|
|
|
|
//Get and process the paint messages!
|
|
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
|
|
TranslateMessage(&msg);
|
|
DispatchMessage(&msg);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
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;
|
|
}
|
|
|
|
|
|
LRESULT CDefView::SwitchToHyperText(UINT uID, BOOL fForce)
|
|
{
|
|
LRESULT lRes = 0;
|
|
|
|
// if current togglable extended view on, turn it off.
|
|
if (!fForce && (uID == (UINT)m_cFrame.CurExtViewId()))
|
|
lRes = SUCCEEDED(_SwitchToViewFVM(_fs.ViewMode, GetWindowStyle(_hwndListview) & LVS_TYPEMASK, TRUE));
|
|
else {
|
|
lRes = SUCCEEDED(_SwitchToViewID(m_cFrame.UidFromCmdId(uID), fForce));
|
|
if (lRes) {
|
|
// free the cached contextmenu of the current selection
|
|
// so that it can be used by the WM_INITMENUPOPUP for viewextensions
|
|
ATOMICRELEASE(_pcmSel);
|
|
}
|
|
}
|
|
return lRes;
|
|
}
|
|
|
|
|
|
void CDefView::_OnMenuTermination()
|
|
{
|
|
//we no longer flush the context menu here because we use it in both file and edit menus
|
|
//so if we flush it after we're done with file menu (and edit menu gets selected)
|
|
//since this message is posted it actually flushes the newly created context menu
|
|
//file and edit now flush before they create their versions and the left over
|
|
//context menu is flushed when the window is destroyed -- longer life time but it works
|
|
|
|
//DV_FlushCachedMenu(this);
|
|
CallCB(SFVM_EXITMENULOOP, 0, 0);
|
|
}
|
|
|
|
|
|
LRESULT CDefView::_SwitchDesktopHTML(BOOL fOn, BOOL fForce)
|
|
{
|
|
LRESULT lRes;
|
|
if (fOn) {
|
|
int iView;
|
|
UINT uID;
|
|
|
|
//Reload the Templates even if we are not in extended view.
|
|
m_cFrame.GetExtViews(TRUE); //Reload the template names from registry again.
|
|
|
|
//We want to switch to HyperText View.
|
|
if ((iView = m_cFrame.GetViewIdFromGUID(&VID_WebView)) < 0)
|
|
uID = 0;
|
|
else
|
|
uID = (UINT)iView;
|
|
uID = m_cFrame.CmdIdFromUid(uID); // only works for docobj extended view.
|
|
//BUGBUG: Can CmdIdFromUid() fail here (return -1) ?
|
|
lRes = SwitchToHyperText(uID, fForce);
|
|
|
|
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, (WPARAM)0L, (LPARAM)0L);
|
|
}
|
|
} else {
|
|
//Switch to LargeIconView.
|
|
_bClearItemPos = FALSE;
|
|
_SwitchToViewFVM(FVM_ICON, LVS_ICON, _fGetWindowLV ? FALSE : TRUE);
|
|
CoFreeUnusedLibraries();
|
|
lRes = TRUE;
|
|
}
|
|
return lRes;
|
|
}
|
|
|
|
|
|
class CColumnDlg
|
|
{
|
|
public:
|
|
HRESULT ShowDialog();
|
|
CColumnDlg(CDefView* pdsv);
|
|
~CColumnDlg();
|
|
|
|
private:
|
|
void OnInit(HWND hwndLVAll);
|
|
BOOL SaveState();
|
|
void MoveItem(int iDelta);
|
|
void UpdateDlgButtons(NMLISTVIEW* pnmlv, HWND hwndDlg);
|
|
|
|
private:
|
|
CDefView* _pdsv;
|
|
HWND _hwndLVAll;
|
|
UINT* _pdwOrder;
|
|
int* _pWidths;
|
|
BOOL _bChanged;
|
|
BOOL _bLoaded;
|
|
BOOL _bUpdating; // used to block notification processing while we're updating
|
|
|
|
static BOOL_PTR CALLBACK DlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
|
|
};
|
|
|
|
|
|
CColumnDlg::CColumnDlg(CDefView* pdsv)
|
|
{
|
|
_pdsv = pdsv;
|
|
_pdwOrder = NULL;
|
|
_pWidths = FALSE;
|
|
_bLoaded = FALSE;
|
|
_bUpdating = FALSE;
|
|
}
|
|
|
|
|
|
CColumnDlg::~CColumnDlg()
|
|
{
|
|
if (_pdwOrder)
|
|
LocalFree(_pdwOrder);
|
|
if (_pWidths)
|
|
LocalFree(_pWidths);
|
|
}
|
|
|
|
|
|
HRESULT CColumnDlg::ShowDialog()
|
|
{
|
|
ASSERT(_pdsv);
|
|
|
|
_bChanged = FALSE; // We are on the stack, so no zero allocator
|
|
|
|
_pdwOrder = (UINT*)LocalAlloc(NONZEROLPTR, sizeof(UINT) * _pdsv->m_cColItems); // total columns
|
|
_pWidths = (int*)LocalAlloc(NONZEROLPTR, sizeof(int) * _pdsv->m_cColItems); // total columns
|
|
if (!_pdwOrder || !_pWidths)
|
|
return E_OUTOFMEMORY;
|
|
|
|
DialogBoxParam(HINST_THISDLL, MAKEINTRESOURCE(DLG_COLUMN_SETTINGS), _pdsv->_hwndMain, DlgProc, (LPARAM)this);
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
// Remember, each column is identified in 3 ways...
|
|
// 1. A 'real' column number, the ordinal out of all possible columns
|
|
// 2. A 'visible' column number, the index to this column in the listview
|
|
// 3. A 'column order #', the position in the header's columnorderarray
|
|
void CColumnDlg::OnInit(HWND hwndLVAll)
|
|
{
|
|
UINT i, iItem, iVisible;
|
|
UINT* pOrderInverse;
|
|
|
|
LV_ITEM lvi;
|
|
LV_COLUMN lvc;
|
|
|
|
// Fill in order array with visible columns, and set up inverse table
|
|
_pdsv->MapRealToVisibleColumn(-1, &iVisible);
|
|
iVisible++;
|
|
|
|
ListView_GetColumnOrderArray(_pdsv->_hwndListview, iVisible, _pdwOrder);
|
|
pOrderInverse = (UINT*)LocalAlloc(NONZEROLPTR, sizeof(DWORD) * iVisible);
|
|
if (!pOrderInverse)
|
|
return;
|
|
for (i = 0; i < iVisible; i++)
|
|
pOrderInverse[_pdwOrder[i]] = i;
|
|
|
|
_hwndLVAll = hwndLVAll;
|
|
ListView_SetExtendedListViewStyle(hwndLVAll, LVS_EX_CHECKBOXES);
|
|
|
|
lvc.mask = (LVCF_FMT | LVCF_SUBITEM);
|
|
lvc.fmt = LVCFMT_LEFT;
|
|
|
|
lvc.iSubItem = 0;
|
|
ListView_InsertColumn(hwndLVAll, 0, &lvc);
|
|
|
|
lvi.mask = LVIF_TEXT;
|
|
lvi.iSubItem = 0;
|
|
for (i = 0; i < (int)_pdsv->m_cColItems; i++) {
|
|
if (!_pdsv->IsColumnHidden(i)) // Don't put in entries for hidden columns
|
|
{
|
|
lvi.iItem = i;
|
|
lvi.pszText = LPSTR_TEXTCALLBACK;
|
|
ListView_InsertItem(hwndLVAll, &lvi);
|
|
}
|
|
}
|
|
|
|
lvi.mask = LVIF_TEXT | LVIF_STATE | LVIF_PARAM;
|
|
// set the visible columns
|
|
for (i = 0; i < (int)iVisible; i++) {
|
|
UINT iReal;
|
|
|
|
_pdsv->MapVisibleToRealColumn(i, &iReal);
|
|
lvi.iItem = pOrderInverse[i];
|
|
lvi.pszText = _pdsv->m_pColItems[iReal].szName;
|
|
lvi.lParam = iReal; // store the real col index in the lParam
|
|
lvi.state = INDEXTOSTATEIMAGEMASK(2);
|
|
lvi.stateMask = LVIS_STATEIMAGEMASK;
|
|
ListView_SetItem(hwndLVAll, &lvi);
|
|
|
|
// Get the column width from the view's listview
|
|
_pWidths[iReal] = ListView_GetColumnWidth(_pdsv->_hwndListview, i);
|
|
}
|
|
|
|
iItem = iVisible;
|
|
for (i = 0; i < (int)_pdsv->m_cColItems; i++) {
|
|
if (!_pdsv->IsColumnOn(i) && !_pdsv->IsColumnHidden(i)) {
|
|
lvi.pszText = _pdsv->m_pColItems[i].szName;
|
|
lvi.state = INDEXTOSTATEIMAGEMASK(1);
|
|
lvi.stateMask = LVIS_STATEIMAGEMASK;
|
|
lvi.lParam = i;
|
|
lvi.iItem = iItem;
|
|
ListView_SetItem(hwndLVAll, &lvi);
|
|
|
|
iItem++;
|
|
|
|
// get the default width we've got saved away
|
|
_pWidths[i] = _pdsv->m_pColItems[i].cChars * _pdsv->_cxChar;
|
|
}
|
|
}
|
|
|
|
// set the size properly
|
|
ListView_SetColumnWidth(hwndLVAll, 0, LVSCW_AUTOSIZE);
|
|
|
|
LocalFree(pOrderInverse);
|
|
_bLoaded = TRUE;
|
|
ListView_SetItemState(hwndLVAll, 0, LVIS_FOCUSED | LVIS_SELECTED, LVIS_FOCUSED | LVIS_SELECTED);
|
|
}
|
|
|
|
|
|
#define SWAP(x,y) {(x) ^= (y); (y) ^= (x); (x) ^= (y);}
|
|
|
|
|
|
void CColumnDlg::MoveItem(int iDelta)
|
|
{
|
|
int i, iNew;
|
|
|
|
i = ListView_GetSelectionMark(_hwndLVAll);
|
|
if (i != -1) {
|
|
iNew = i + iDelta;
|
|
if (iNew >= 0 && iNew <= (ListView_GetItemCount(_hwndLVAll) - 1)) {
|
|
LV_ITEM lvi, lvi2;
|
|
TCHAR szTmp1[MAX_COLUMN_NAME_LEN], szTmp2[MAX_COLUMN_NAME_LEN];
|
|
|
|
_bChanged = TRUE;
|
|
_bUpdating = TRUE;
|
|
|
|
lvi.iItem = i;
|
|
lvi.iSubItem = 0;
|
|
lvi.pszText = szTmp1;
|
|
lvi.cchTextMax = ARRAYSIZE(szTmp1);
|
|
lvi.stateMask = LVIS_STATEIMAGEMASK;
|
|
lvi.mask = LVIF_TEXT | LVIF_STATE | LVIF_PARAM;
|
|
|
|
lvi2.iItem = iNew;
|
|
lvi2.iSubItem = 0;
|
|
lvi2.pszText = szTmp2;
|
|
lvi2.cchTextMax = ARRAYSIZE(szTmp2);
|
|
lvi2.stateMask = LVIS_STATEIMAGEMASK;
|
|
lvi2.mask = LVIF_TEXT | LVIF_STATE | LVIF_PARAM;
|
|
|
|
ListView_GetItem(_hwndLVAll, &lvi);
|
|
ListView_GetItem(_hwndLVAll, &lvi2);
|
|
|
|
SWAP(lvi.iItem, lvi2.iItem);
|
|
|
|
ListView_SetItem(_hwndLVAll, &lvi);
|
|
ListView_SetItem(_hwndLVAll, &lvi2);
|
|
|
|
_bUpdating = FALSE;
|
|
|
|
// update selection
|
|
ListView_SetSelectionMark(_hwndLVAll, iNew);
|
|
ListView_SetItemState(_hwndLVAll, iNew, LVIS_FOCUSED | LVIS_SELECTED, LVIS_FOCUSED | LVIS_SELECTED);
|
|
// HACK: SetItemState sends notifications for i, iNew, then i again.
|
|
// we need to call it twice in a row, so UpdateDlgButtons will get the right item
|
|
ListView_SetItemState(_hwndLVAll, iNew, LVIS_FOCUSED | LVIS_SELECTED, LVIS_FOCUSED | LVIS_SELECTED);
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
TraceMsg(TF_WARNING, "ccd.mi couldn't move %d to %d", i, i + iDelta);
|
|
MessageBeep(MB_ICONEXCLAMATION);
|
|
}
|
|
|
|
|
|
BOOL CColumnDlg::SaveState()
|
|
{
|
|
int i, cItems = ListView_GetItemCount(_hwndLVAll);
|
|
LV_ITEM lvi = {0};
|
|
int iOrderIndex;
|
|
|
|
// Check order
|
|
if (!_bChanged)
|
|
return FALSE;
|
|
|
|
iOrderIndex = 0;
|
|
lvi.stateMask = LVIS_STATEIMAGEMASK;
|
|
lvi.mask = LVIF_PARAM | LVIF_STATE;
|
|
|
|
for (i = 0; i < cItems; i++) {
|
|
lvi.iItem = i;
|
|
ListView_GetItem(_hwndLVAll, &lvi);
|
|
|
|
// toggle it, if the state in the dialog doesn't match the listview state
|
|
// if (lvi.state != (UINT)INDEXTOSTATEIMAGEMASK(_pdsv->IsColumnOn(lvi.lParam)?2:1)) // BUGBUG? lvi.state is coming back as zero always
|
|
if (BOOLIFY(ListView_GetCheckState(_hwndLVAll, i)) != BOOLIFY(_pdsv->IsColumnOn((UINT)lvi.lParam))) {
|
|
_pdsv->_HandleColumnToggle((UINT)lvi.lParam, FALSE);
|
|
}
|
|
|
|
if (_pdsv->IsColumnOn((UINT)lvi.lParam))
|
|
_pdwOrder[iOrderIndex++] = (UINT)lvi.lParam; // incorrectly store real (not vis) col #, fix up below
|
|
}
|
|
|
|
// must be in a separate loop. (can't map real to visible, if we aren't done setting visible)
|
|
for (i = 0; i < iOrderIndex; i++) {
|
|
UINT iReal = _pdwOrder[i];
|
|
_pdsv->MapRealToVisibleColumn(iReal, &_pdwOrder[i]);
|
|
|
|
if (_pWidths[iReal] < 0) // negative width means they edited it
|
|
ListView_SetColumnWidth(_pdsv->_hwndListview, _pdwOrder[i], -_pWidths[iReal]);
|
|
}
|
|
ListView_SetColumnOrderArray(_pdsv->_hwndListview, iOrderIndex, _pdwOrder);
|
|
|
|
// kick the listview into repainting everything
|
|
InvalidateRect(_pdsv->_hwndListview, NULL, TRUE);
|
|
|
|
_bChanged = FALSE;
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
BOOL EnableDlgItem(HWND hdlg, UINT idc, BOOL f)
|
|
{
|
|
return EnableWindow(GetDlgItem(hdlg, idc), f);
|
|
}
|
|
|
|
|
|
void CColumnDlg::UpdateDlgButtons(NMLISTVIEW* pnmlv, HWND hwndDlg)
|
|
{
|
|
BOOL bChecked, bOldUpdateState = _bUpdating;
|
|
int iWidth;
|
|
int iItem = ListView_GetSelectionMark(_hwndLVAll);
|
|
|
|
// to disable checking
|
|
_bUpdating = TRUE;
|
|
if (pnmlv->uNewState & LVIS_STATEIMAGEMASK)
|
|
bChecked = (pnmlv->uNewState & LVIS_STATEIMAGEMASK) == (UINT)INDEXTOSTATEIMAGEMASK(2);
|
|
else
|
|
bChecked = ListView_GetCheckState(_hwndLVAll, pnmlv->iItem);
|
|
|
|
EnableDlgItem(hwndDlg, IDC_COL_UP, pnmlv->iItem > 0);
|
|
EnableDlgItem(hwndDlg, IDC_COL_DOWN, pnmlv->iItem < (int)_pdsv->m_cColItems - 1);
|
|
EnableDlgItem(hwndDlg, IDC_COL_SHOW, !bChecked && (pnmlv->lParam != 0));
|
|
EnableDlgItem(hwndDlg, IDC_COL_HIDE, bChecked && (pnmlv->lParam != 0));
|
|
|
|
// update the width edit box
|
|
iWidth = _pWidths[pnmlv->lParam];
|
|
if (iWidth < 0) iWidth = -iWidth; // we store negative values to track if it changed or not
|
|
SetDlgItemInt(hwndDlg, IDC_COL_WIDTH, iWidth, TRUE);
|
|
|
|
_bUpdating = bOldUpdateState;
|
|
}
|
|
|
|
|
|
BOOL_PTR CALLBACK CColumnDlg::DlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
CColumnDlg* pcd;
|
|
|
|
if (uMsg == WM_INITDIALOG) {
|
|
pcd = (CColumnDlg*)lParam;
|
|
if (!pcd)
|
|
EndDialog(hwndDlg, FALSE);
|
|
SetWindowLongPtr(hwndDlg, DWLP_USER, (LONG_PTR)pcd);
|
|
} else
|
|
pcd = (CColumnDlg*)GetWindowLongPtr(hwndDlg, DWLP_USER);
|
|
|
|
switch (uMsg) {
|
|
case WM_INITDIALOG:
|
|
pcd->OnInit(GetDlgItem(hwndDlg, IDC_COL_LVALL));
|
|
SendDlgItemMessage(hwndDlg, IDC_COL_WIDTH, EM_LIMITTEXT, 3, 0); // 3 digits
|
|
break;
|
|
case WM_DESTROY:
|
|
break;
|
|
case WM_COMMAND:
|
|
switch (LOWORD(wParam)) {
|
|
case IDC_COL_UP:
|
|
pcd->MoveItem(-1);
|
|
goto Common;
|
|
case IDC_COL_DOWN:
|
|
pcd->MoveItem(+1);
|
|
goto Common;
|
|
Common:
|
|
SetFocus(pcd->_hwndLVAll);
|
|
break;
|
|
case IDC_COL_SHOW:
|
|
case IDC_COL_HIDE:
|
|
{
|
|
UINT iItem = ListView_GetSelectionMark(pcd->_hwndLVAll);
|
|
ListView_SetCheckState(pcd->_hwndLVAll, iItem, LOWORD(wParam) == IDC_COL_SHOW);
|
|
SetFocus(pcd->_hwndLVAll);
|
|
break;
|
|
}
|
|
case IDC_COL_WIDTH:
|
|
if (HIWORD(wParam) == EN_CHANGE && !pcd->_bUpdating) {
|
|
LV_ITEM lvi;
|
|
lvi.iItem = ListView_GetSelectionMark(pcd->_hwndLVAll);
|
|
lvi.iSubItem = 0;
|
|
lvi.mask = LVIF_PARAM;
|
|
ListView_GetItem(pcd->_hwndLVAll, &lvi);
|
|
|
|
pcd->_pWidths[lvi.lParam] = -(int)GetDlgItemInt(hwndDlg, IDC_COL_WIDTH, NULL, FALSE);
|
|
pcd->_bChanged = TRUE;
|
|
}
|
|
break;
|
|
case IDOK:
|
|
pcd->SaveState(); // fall through
|
|
case IDCANCEL:
|
|
return EndDialog(hwndDlg, TRUE);
|
|
}
|
|
break;
|
|
|
|
case WM_NOTIFY:
|
|
if (pcd->_bLoaded && !pcd->_bUpdating) {
|
|
NMLISTVIEW* pnmlv = (NMLISTVIEW*)lParam;
|
|
switch (((LPNMHDR)lParam)->code) {
|
|
case LVN_ITEMCHANGING:
|
|
// fix up the buttons & such here
|
|
if (pnmlv->uChanged & LVIF_STATE)
|
|
pcd->UpdateDlgButtons(pnmlv, hwndDlg);
|
|
|
|
// We want to reject turning off the name column
|
|
// it both doesn't make sense to have no name column, and defview assumes there will be one
|
|
if (pnmlv->lParam == 0 && (pnmlv->uNewState & LVIS_STATEIMAGEMASK) == INDEXTOSTATEIMAGEMASK(1)) {
|
|
MessageBeep(MB_ICONEXCLAMATION);
|
|
SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, TRUE);
|
|
return TRUE;
|
|
} else {
|
|
// if something besides focus changed
|
|
if ((pnmlv->uChanged & ~LVIF_STATE) || ((pnmlv->uNewState & LVIS_STATEIMAGEMASK) != (pnmlv->uOldState & LVIS_STATEIMAGEMASK)))
|
|
pcd->_bChanged = TRUE;
|
|
}
|
|
break;
|
|
case NM_DBLCLK:
|
|
{
|
|
BOOL bCheck = ListView_GetCheckState(pcd->_hwndLVAll, pnmlv->iItem);
|
|
ListView_SetCheckState(pcd->_hwndLVAll, pnmlv->iItem, !bCheck);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
case WM_SYSCOLORCHANGE:
|
|
SendMessage(pcd->_hwndLVAll, uMsg, wParam, lParam);
|
|
break;
|
|
default:
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
LRESULT ToggleComponent(UINT uID)
|
|
{
|
|
IActiveDesktop* piad;
|
|
if (SUCCEEDED(CoCreateInstance(CLSID_ActiveDesktop, NULL, CLSCTX_INPROC_SERVER, IID_IActiveDesktop, (void**)&piad))) {
|
|
COMPONENT comp;
|
|
TCHAR componentSource[INTERNET_MAX_URL_LENGTH];
|
|
|
|
comp.dwSize = sizeof(comp);
|
|
if (SUCCEEDED(piad->GetDesktopItem(uID - SFVIDM_DESKTOPHTML_ADDSEPARATOR - 1, &comp, 0))) {
|
|
SHUnicodeToTChar(comp.wszSource, componentSource, ARRAYSIZE(componentSource));
|
|
comp.fChecked = !comp.fChecked;
|
|
|
|
// 98/12/16 vtan #250938: If ICW has not been run to completion then only
|
|
// allow the user to toggle components that are local pictures of some sort.
|
|
// If not then notify the user of the problem and launch ICW.
|
|
if (comp.fChecked && !IsICWCompleted() && !IsLocalPicture(componentSource)) {
|
|
ShellMessageBox(HINST_THISDLL, NULL, MAKEINTRESOURCE(IDS_COMP_ICW_TOGGLE), MAKEINTRESOURCE(IDS_COMP_ICW_TITLE), MB_OK);
|
|
LaunchICW();
|
|
} else {
|
|
piad->ModifyDesktopItem(&comp, COMP_ELEM_CHECKED);
|
|
// Note: Do not use AD_APPLY_FORCE here; It will force saving Wallpaper and pattern
|
|
// which will cause a complete refresh of desktop causing a flicker.
|
|
// What we really want to save is just the component that toggled.
|
|
piad->ApplyChanges(AD_APPLY_ALL | AD_APPLY_DYNAMICREFRESH);
|
|
}
|
|
}
|
|
piad->Release();
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
DWORD NewDesktopItem(LPVOID lpParameter)
|
|
{
|
|
IActiveDesktop* piad;
|
|
|
|
CoInitialize(0);
|
|
|
|
if (SUCCEEDED(SHCoCreateInstance(NULL, &CLSID_ActiveDesktop, NULL, IID_IActiveDesktop, (void**)&piad))) {
|
|
if (NewComponent(*(HWND*)lpParameter, piad, FALSE, NULL) >= 0) {
|
|
// If we succeeded in adding an item then ensure AD is on and then apply
|
|
// the changes.
|
|
COMPONENTSOPT co;
|
|
IADesktopP2* piadp2;
|
|
|
|
co.dwSize = sizeof(COMPONENTSOPT);
|
|
piad->GetDesktopItemOptions(&co, 0);
|
|
co.fActiveDesktop = TRUE;
|
|
piad->SetDesktopItemOptions(&co, 0);
|
|
// If we need to turn AD on at this point we need to refresh the wallpaper state
|
|
// in our piad object, use IADesktopP2 to do so. Otherwise if there was an html
|
|
// wallpaper it won't show up when we apply changes.
|
|
if (SUCCEEDED(piad->QueryInterface(IID_IADesktopP2, (void**)&piadp2))) {
|
|
piadp2->ReReadWallpaper();
|
|
piadp2->Release();
|
|
}
|
|
piad->ApplyChanges(AD_APPLY_ALL | AD_APPLY_DYNAMICREFRESH);
|
|
}
|
|
piad->Release();
|
|
}
|
|
|
|
CoUninitialize();
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
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_LEFTBUTTON | TPM_NONOTIFY | TPM_RETURNCMD, x, y, 0, _hwndListview, NULL);
|
|
DestroyMenu(hmenu);
|
|
|
|
// validate item first
|
|
if (item == SFVIDM_VIEW_COLSETTINGS) {
|
|
CColumnDlg ccd(this);
|
|
|
|
if (!_bLoadedColumns)
|
|
AddColumns();
|
|
|
|
ccd.ShowDialog();
|
|
} else if (item > SFVIDM_COLUMN_FIRST)
|
|
_HandleColumnToggle(item - SFVIDM_COLUMN_FIRST, TRUE);
|
|
}
|
|
}
|
|
|
|
|
|
LRESULT CDefView::Command(IContextMenu* pcmSel, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
TCHAR szSettings[MAX_PATH];
|
|
DWORD dwStyle;
|
|
UINT fvmNew;
|
|
UINT uCMBias = SFVIDM_CONTEXT_FIRST;
|
|
int iItem;
|
|
UINT uID = GET_WM_COMMAND_ID(wParam, lParam);
|
|
|
|
if (InRange(uID, SFVIDM_VIEW_EXTFIRST, SFVIDM_VIEW_EXTLAST)) {
|
|
return SwitchToHyperText(uID, FALSE);
|
|
}
|
|
|
|
if (InRange(uID, SFVIDM_CONTEXT_FIRST, SFVIDM_CONTEXT_LAST) || InRange(uID, SFVIDM_BACK_CONTEXT_FIRST, SFVIDM_BACK_CONTEXT_LAST)) {
|
|
BOOL bRenameIt = FALSE;
|
|
IContextMenu* pcmFree = NULL;
|
|
|
|
if (pcmSel == NULL) {
|
|
if (InRange(uID, SFVIDM_BACK_CONTEXT_FIRST, SFVIDM_BACK_CONTEXT_LAST)) {
|
|
pcmSel = _pcmBackground;
|
|
uCMBias = SFVIDM_BACK_CONTEXT_FIRST;
|
|
if (pcmSel) {
|
|
pcmSel->AddRef();
|
|
}
|
|
} else {
|
|
// We hopefully have a pcmSel object cached away for the current
|
|
// selection. If not this wont work anyway so blow out.
|
|
pcmSel = _pcmSel;
|
|
uCMBias = SFVIDM_CONTEXT_FIRST;
|
|
|
|
if (pcmSel) {
|
|
pcmSel->AddRef();
|
|
}
|
|
// Extended views don't cache the context menu because the
|
|
// top defview isn't notified of selection changes, so the
|
|
// cache would be wrong.
|
|
else if (!_HasNormalView()) {
|
|
pcmSel = _GetContextMenuFromSelection();
|
|
}
|
|
}
|
|
|
|
pcmFree = pcmSel;
|
|
}
|
|
|
|
if (pcmSel) {
|
|
CHAR szCmdA[MAX_PATH];
|
|
TCHAR szCommandString[MAX_PATH];
|
|
// make sure this object exists throughout the operation.
|
|
// ie we dont want a change attributes callback delete this
|
|
// object while we are using it...
|
|
pcmSel->AddRef();
|
|
|
|
szCommandString[0] = 0;
|
|
szCmdA[0] = 0;
|
|
|
|
HRESULT hres = pcmSel->GetCommandString(uID - uCMBias, GCS_VERBA, NULL, szCmdA, ARRAYSIZE(szCmdA));
|
|
if (SUCCEEDED(hres))
|
|
SHAnsiToTChar(szCmdA, szCommandString, ARRAYSIZE(szCommandString));
|
|
|
|
if (szCommandString[0] == 0) {
|
|
WCHAR szCmdW[60];
|
|
szCmdW[0] = 0;
|
|
hres = pcmSel->GetCommandString(uID - uCMBias, GCS_VERBW, NULL, (LPSTR)szCmdW, ARRAYSIZE(szCmdW));
|
|
SHUnicodeToTChar(szCmdW, szCommandString, ARRAYSIZE(szCommandString));
|
|
}
|
|
|
|
// We need to special case the rename command
|
|
if (lstrcmpi(szCommandString, c_szRename) == 0) {
|
|
bRenameIt = TRUE;
|
|
} else {
|
|
CMINVOKECOMMANDINFOEX ici = {0};
|
|
|
|
ici.cbSize = SIZEOF(CMINVOKECOMMANDINFOEX);
|
|
ici.hwnd = _hwndMain;
|
|
ici.lpVerb = (LPSTR)MAKEINTRESOURCE(uID - uCMBias);
|
|
ici.nShow = SW_NORMAL;
|
|
|
|
// Set the invoke point for this command
|
|
{
|
|
POINT ptSelect;
|
|
int iItemSelect = ListView_GetNextItem(_hwndListview, -1, LVNI_SELECTED);
|
|
if (iItemSelect != -1) {
|
|
RECT rcItem;
|
|
ListView_GetItemRect(_hwndListview, iItemSelect, &rcItem, LVIR_BOUNDS);
|
|
MapWindowPoints(_hwndListview, HWND_DESKTOP, (LPPOINT)&rcItem, 2);
|
|
ptSelect.x = (rcItem.left + rcItem.right) / 2;
|
|
ptSelect.y = (rcItem.top + rcItem.bottom) / 2;
|
|
ici.ptInvoke = ptSelect;
|
|
ici.fMask |= CMIC_MASK_PTINVOKE;
|
|
}
|
|
}
|
|
|
|
// record if shift or control was being held down
|
|
SetICIKeyModifiers(&ici.fMask);
|
|
|
|
_InvokeCommand(pcmSel, &ici);
|
|
}
|
|
|
|
//Since we are releaseing our only hold on the context menu, release the site.
|
|
IUnknown_SetSite(pcmSel, NULL);
|
|
|
|
// And release our use of it.
|
|
ATOMICRELEASE(pcmSel);
|
|
ATOMICRELEASE(pcmFree);
|
|
|
|
if (bRenameIt) {
|
|
if (_HasNormalView()) {
|
|
goto RenameIt_NormalView;
|
|
} else {
|
|
TraceMsg(TF_ERROR, "Extended View: Command can't rename");
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
// Is the ID within the client's range?
|
|
if (InRange(uID, SFVIDM_CLIENT_FIRST, SFVIDM_CLIENT_LAST) && this->HasCB()) {
|
|
// Yes; pass it on to the callback
|
|
CallCB(SFVM_INVOKECOMMAND, uID - SFVIDM_CLIENT_FIRST, 0);
|
|
return 0;
|
|
}
|
|
|
|
// Special commands for the desktop menu
|
|
if (InRange(uID, SFVIDM_DESKTOPHTML_ADDSEPARATOR + 1, SFVIDM_DESKTOP_LAST)) {
|
|
return ToggleComponent(uID);
|
|
}
|
|
|
|
// First check for commands that always go to this defview
|
|
switch (uID) {
|
|
case SFVIDM_EDIT_UNDO:
|
|
// if we are in label edit mode, don't allowany of the buttons......
|
|
if (_fInLabelEdit || (m_cFrame.IsSFVExtension() && m_cFrame.IsExtendedSFVModal())) {
|
|
MessageBeep(0);
|
|
return 0;
|
|
}
|
|
|
|
Undo(_hwndMain);
|
|
break;
|
|
case SFVIDM_VIEW_COLSETTINGS:
|
|
{
|
|
CColumnDlg ccd(this);
|
|
|
|
if (!_bLoadedColumns)
|
|
this->AddColumns();
|
|
|
|
ccd.ShowDialog();
|
|
break;
|
|
}
|
|
case SFVIDM_VIEW_VIEWMENU:
|
|
{
|
|
// if we are in label edit mode, don't allowany of the buttons......
|
|
if (_fInLabelEdit || (m_cFrame.IsSFVExtension() && m_cFrame.IsExtendedSFVModal())) {
|
|
MessageBeep(0);
|
|
return 0;
|
|
}
|
|
|
|
LPCDFVCMDDATA pcd = (LPCDFVCMDDATA)lParam;
|
|
if (pcd) {
|
|
|
|
// First, let's get that view menu.
|
|
|
|
HMENU hmenuCtx = SHLoadPopupMenu(HINST_THISDLL, POPUP_SFV_BACKGROUND);
|
|
if (hmenuCtx) {
|
|
MENUITEMINFO mii = {0};
|
|
|
|
m_cFrame.MergeExtViewsMenu(hmenuCtx, this);
|
|
CheckCurrentViewMenuItem(hmenuCtx);
|
|
|
|
mii.cbSize = SIZEOF(mii);
|
|
mii.fMask = MIIM_SUBMENU;
|
|
GetMenuItemInfo(hmenuCtx, SFVIDM_MENU_VIEW, MF_BYCOMMAND, &mii);
|
|
HMENU hmenuView = mii.hSubMenu;
|
|
|
|
if (pcd->pva && pcd->pva->byref) {
|
|
|
|
// We have X,Y coordinates, let's bring up a context
|
|
// menu at that location.
|
|
|
|
LPRECT prect = (LPRECT)pcd->pva->byref;
|
|
int idCmd = TrackPopupMenu(hmenuView, TPM_RETURNCMD, prect->left, prect->bottom, 0, pcd->hwnd, NULL);
|
|
if (idCmd)
|
|
Command(NULL, GET_WM_COMMAND_MPS(idCmd, 0, 0));
|
|
}
|
|
|
|
// 226474: view button changing from BTNS_DROPDOWN (split dropdown) to BTNS_WHOLEDROPDOWN
|
|
// (unsplit dropdown) so we just need to handle the dropdown behavior.
|
|
|
|
// i'm leaving the old code in for now under #if 0 in case we change our minds. remove by nt5 beta3.
|
|
#if 0
|
|
else {
|
|
// No X,Y coordinates, user clicked on button portion
|
|
// of the view combo. Let's cycle to the next view.
|
|
BOOL fSeparator = FALSE;
|
|
int iChecked = -1;
|
|
|
|
for (int i = 0;; i++) {
|
|
// Find the currently selected view.
|
|
ZeroMemory(&mii, SIZEOF(mii));
|
|
mii.cbSize = SIZEOF(mii);
|
|
mii.fMask = MIIM_STATE | MIIM_TYPE;
|
|
if (GetMenuItemInfo(hmenuView, i, MF_BYPOSITION, &mii) == FALSE) {
|
|
break;
|
|
}
|
|
|
|
if (mii.fType & MFT_SEPARATOR) {
|
|
fSeparator = TRUE;
|
|
}
|
|
|
|
if ((mii.fState & MFS_CHECKED) && (mii.fType & MFT_RADIOCHECK)) {
|
|
iChecked = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (iChecked != -1) {
|
|
int wNextID = -1;
|
|
|
|
// We found the radio chekced item, now
|
|
// find the next view.
|
|
ZeroMemory(&mii, SIZEOF(mii));
|
|
mii.cbSize = SIZEOF(mii);
|
|
mii.fMask = MIIM_ID;
|
|
if (GetMenuItemInfo(hmenuView, iChecked + 1, MF_BYPOSITION, &mii)) {
|
|
wNextID = mii.wID;
|
|
} else {
|
|
// We fell off the end of the menu, so
|
|
// find the first selectable view.
|
|
TCHAR szText[MAX_PATH];
|
|
ZeroMemory(&mii, SIZEOF(mii));
|
|
mii.cbSize = SIZEOF(mii);
|
|
mii.fMask = MIIM_ID | MIIM_TYPE;
|
|
mii.dwTypeData = szText;
|
|
mii.cch = ARRAYSIZE(szText);
|
|
for (i = 0;; i++) {
|
|
if (GetMenuItemInfo(hmenuView, i, MF_BYPOSITION, &mii) == FALSE) {
|
|
break;
|
|
}
|
|
|
|
if (fSeparator) {
|
|
// Need to skip past the separator and all of the toggle items before it.
|
|
if (mii.fType & MFT_SEPARATOR) {
|
|
fSeparator = FALSE;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
// Found the first item.
|
|
wNextID = mii.wID;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (wNextID != -1) {
|
|
Command(pcmSel, MAKELONG(wNextID, 0), 0);
|
|
}
|
|
}
|
|
}
|
|
#endif // 0
|
|
|
|
DestroyMenu(hmenuCtx);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case SFVIDM_VIEW_ICON:
|
|
dwStyle = LVS_ICON;
|
|
_bClearItemPos = FALSE;
|
|
fvmNew = FVM_ICON;
|
|
goto SetStyle;
|
|
case SFVIDM_VIEW_SMALLICON:
|
|
dwStyle = LVS_SMALLICON;
|
|
_bClearItemPos = FALSE;
|
|
fvmNew = FVM_SMALLICON;
|
|
goto SetStyle;
|
|
case SFVIDM_VIEW_LIST:
|
|
dwStyle = LVS_LIST;
|
|
_bClearItemPos = TRUE;
|
|
fvmNew = FVM_LIST;
|
|
goto SetStyle;
|
|
case SFVIDM_VIEW_DETAILS:
|
|
dwStyle = LVS_REPORT;
|
|
_bClearItemPos = TRUE;
|
|
fvmNew = FVM_DETAILS;
|
|
SetStyle:
|
|
_SwitchToViewFVM(fvmNew, dwStyle, m_cFrame.IsWebView() ? FALSE : TRUE);
|
|
break;
|
|
case SFVIDM_DESKTOPHTML_WEBCONTENT:
|
|
{
|
|
bool bHasVisibleNonLocalPicture;
|
|
SHELLSTATE ss;
|
|
|
|
SHGetSetSettings(&ss, SSF_DESKTOPHTML, FALSE); // Get the setting
|
|
ss.fDesktopHTML = !ss.fDesktopHTML; // Toggle the state
|
|
bHasVisibleNonLocalPicture = false;
|
|
if (ss.fDesktopHTML && !IsICWCompleted()) {
|
|
IActiveDesktop* pIAD;
|
|
|
|
if (SUCCEEDED(SHCoCreateInstance(NULL, &CLSID_ActiveDesktop, NULL, IID_IActiveDesktop, reinterpret_cast<void**>(&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(ss.fDesktopHTML, FALSE);
|
|
}
|
|
}
|
|
break;
|
|
case SFVIDM_DESKTOPHTML_SYNCHRONIZE:
|
|
{
|
|
IOleCommandTarget* pct;
|
|
// Call the command target to Update and refresh even if the desktop frame
|
|
// is in offline mode.
|
|
ASSERT(m_cFrame.IsWebView()); //This command is valid only for extended views.
|
|
if (SUCCEEDED(_psb->QueryInterface(IID_IOleCommandTarget, (void**)&pct))) {
|
|
pct->Exec(&CGID_ShellDocView, SHDVID_UPDATEOFFLINEDESKTOP, 0, NULL, NULL);
|
|
pct->Release();
|
|
}
|
|
}
|
|
break;
|
|
case SFVIDM_DESKTOPHTML_CUSTOMIZE:
|
|
LoadString(HINST_THISDLL, IDS_COMPSETTINGS, szSettings, ARRAYSIZE(szSettings));
|
|
SHRunControlPanel(szSettings, NULL);
|
|
break;
|
|
case SFVIDM_DESKTOPHTML_NEWITEM:
|
|
CloseHandle(CreateThread(NULL, 0, NewDesktopItem, &_hwndMain, 0, &dwStyle));
|
|
break;
|
|
case SFVIDM_DESKTOPHTML_ICONS:
|
|
case SFVIDM_ARRANGE_DISPLAYICONS:
|
|
{
|
|
SHELLSTATE ss;
|
|
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);
|
|
ShowWindow(_hwndListview, (DV_SHOWICONS(this) ? SW_SHOW : SW_HIDE));
|
|
}
|
|
break;
|
|
case SFVIDM_DESKTOPHTML_LOCK:
|
|
{
|
|
DWORD dwFlags = GetDesktopFlags();
|
|
dwFlags ^= COMPONENTS_LOCKED;
|
|
SetDesktopFlags(COMPONENTS_LOCKED, dwFlags);
|
|
}
|
|
break;
|
|
case SFVIDM_EDIT_COPYTO:
|
|
case SFVIDM_EDIT_MOVETO:
|
|
{
|
|
// if we are in label edit mode, don't allowany of the buttons......
|
|
if (_fInLabelEdit || (m_cFrame.IsSFVExtension() && m_cFrame.IsExtendedSFVModal())) {
|
|
MessageBeep(0);
|
|
return 0;
|
|
}
|
|
|
|
CLSID clsid;
|
|
IContextMenu* pcm;
|
|
|
|
if (uID == SFVIDM_EDIT_COPYTO)
|
|
clsid = CLSID_CopyToMenu;
|
|
else
|
|
clsid = CLSID_MoveToMenu;
|
|
|
|
if (SUCCEEDED(SHCoCreateInstance(NULL, &clsid, NULL, IID_IContextMenu, (void**)&pcm))) {
|
|
IShellExtInit* psei;
|
|
|
|
IUnknown_SetSite(pcm, SAFECAST(this, IDropTarget*)); // Needed to go modal during UI
|
|
|
|
if (SUCCEEDED(pcm->QueryInterface(IID_IShellExtInit, (void**)&psei))) {
|
|
IDataObject* pdobj;
|
|
|
|
if (SUCCEEDED(DefView_GetUIObjectFromItem(this, IID_IDataObject, (void**)&pdobj, SVGIO_SELECTION))) {
|
|
LPITEMIDLIST pidlFolder = _GetViewPidl();
|
|
CMINVOKECOMMANDINFO ici = {0};
|
|
|
|
psei->Initialize(pidlFolder, pdobj, NULL);
|
|
|
|
ici.hwnd = _hwndMain;
|
|
pcm->InvokeCommand(&ici);
|
|
|
|
ILFree(pidlFolder); // OK if NULL
|
|
pdobj->Release();
|
|
}
|
|
psei->Release();
|
|
}
|
|
|
|
IUnknown_SetSite(pcm, NULL);
|
|
pcm->Release();
|
|
}
|
|
}
|
|
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 we are in label edit mode, don't allowany of the buttons......
|
|
if (_fInLabelEdit || (m_cFrame.IsSFVExtension() && m_cFrame.IsExtendedSFVModal())) {
|
|
MessageBeep(0);
|
|
return 0;
|
|
}
|
|
|
|
if (DefView_AllowCommand(this, uID, wParam, lParam)) {
|
|
if (FAILED(DefView_ExplorerCommand(this, GET_WM_COMMAND_ID(wParam, lParam))))
|
|
MessageBeep(0);
|
|
} else {
|
|
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.
|
|
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;
|
|
case SFVIDM_HELP_TOPIC:
|
|
// HACK: Don't call WinHelp when we are in the common dialog.
|
|
if (!DV_CDB_IsCommonDialog(this)) {
|
|
// 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] = L'\0';
|
|
if (SUCCEEDED(CallCB(SFVM_GETHELPTOPIC, 0, (LPARAM)&htd))) {
|
|
TCHAR szHelpFile[MAX_PATH];
|
|
TCHAR szHelpTopic[MAX_PATH];
|
|
SHUnicodeToTChar(htd.wszHelpFile, szHelpFile, ARRAYSIZE(szHelpFile));
|
|
SHUnicodeToTChar(htd.wszHelpTopic, szHelpTopic, ARRAYSIZE(szHelpTopic));
|
|
HtmlHelp(hwndDesktop, szHelpFile, HH_HELP_FINDER, szHelpTopic[0] ? (DWORD_PTR)szHelpTopic : 0);
|
|
} else {
|
|
HtmlHelp(hwndDesktop, c_szHtmlWindowsHlp, HH_HELP_FINDER, 0);
|
|
}
|
|
}
|
|
break;
|
|
case SFVIDM_VIEW_CUSTOMWIZARD:
|
|
//ShellExec the Wizard to customize this folder.
|
|
_InvokeCustomWizard();
|
|
break;
|
|
case SFVIDM_MISC_SETWEBVIEW:
|
|
if ((BOOL_PTR)lParam) // We have to set WebView
|
|
{
|
|
SHELLVIEWID vid = VID_WebView;
|
|
_SwitchToViewPVID(&vid, TRUE);
|
|
} else {
|
|
if (!m_cFrame.IsSFVExtension()) {
|
|
_SwitchToViewFVM(_fs.ViewMode, GetWindowStyle(_hwndListview) & LVS_TYPEMASK, TRUE);
|
|
} else {
|
|
// We don't have a function to switch off only webview without switching off the extended view.
|
|
// So, let's first switch off all extended views then switch back to the current extended view.
|
|
int uView = m_cFrame.m_uView;
|
|
_SwitchToViewID((UINT)CSFVFrame::HIDEEXTVIEW, FALSE);
|
|
_SwitchToViewID(uView, FALSE);
|
|
}
|
|
}
|
|
break;
|
|
case SFVIDM_MISC_REFRESH:
|
|
Refresh();
|
|
break;
|
|
default:
|
|
if (this->m_cFrame.IsSFVExtension()) {
|
|
IShellFolderView* pSFV = this->m_cFrame.GetExtendedISFV();
|
|
IShellView2* pView = this->m_cFrame.GetExtendedISV();
|
|
|
|
HRESULT hr = E_NOTIMPL;
|
|
switch (uID) {
|
|
case SFVIDM_SELECT_ALL:
|
|
hr = pSFV->Select(SFVS_SELECT_ALLITEMS);
|
|
break;
|
|
case SFVIDM_DESELECT_ALL:
|
|
hr = pSFV->Select(SFVS_SELECT_NONE);
|
|
break;
|
|
case SFVIDM_SELECT_INVERT:
|
|
hr = pSFV->Select(SFVS_SELECT_INVERT);
|
|
break;
|
|
case SFVIDM_FILE_RENAME:
|
|
{
|
|
// get the selected items..
|
|
LPCITEMIDLIST* apidl = NULL;
|
|
UINT cidl = 0;
|
|
hr = pSFV->GetSelectedObjects(&apidl, &cidl);
|
|
if (hr != S_OK || cidl <= 0) {
|
|
MessageBeep(0);
|
|
} else {
|
|
ASSERT(NULL != apidl);
|
|
ASSERT(NULL != apidl[cidl - 1]);
|
|
|
|
hr = pView->HandleRename(apidl[cidl - 1]);
|
|
}
|
|
|
|
if (apidl != NULL)
|
|
LocalFree((void*)apidl);
|
|
break;
|
|
}
|
|
case SFVIDM_ARRANGE_AUTO:
|
|
hr = pSFV->AutoArrange();
|
|
break;
|
|
case SFVIDM_ARRANGE_GRID:
|
|
hr = pSFV->ArrangeGrid();
|
|
break;
|
|
}
|
|
} else
|
|
// Second check for commands that need to be sent to the active object
|
|
|
|
// An overload of the fDontForward flag allows us to always tell it to do the
|
|
// right thing for the Def-view regardless of whether we are looking at
|
|
// an extension view or not..
|
|
{
|
|
//Commands that can never go to an extended view!
|
|
switch (uID) {
|
|
case SFVIDM_ARRANGE_AUTO:
|
|
_fs.fFlags ^= FWF_AUTOARRANGE; // toggle
|
|
_bItemsMoved = FALSE; // positions are all gone
|
|
SetWindowLongPtr(_hwndListview, GWL_STYLE
|
|
, GetWindowStyle(_hwndListview) ^ LVS_AUTOARRANGE);
|
|
break;
|
|
case SFVIDM_ARRANGE_GRID:
|
|
#ifdef TEST_LVDISABLE
|
|
{
|
|
static BOOL fFlag = 0;
|
|
EnableWindow(_hwndListview, fFlag);
|
|
fFlag = !fFlag;
|
|
}
|
|
#endif
|
|
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();
|
|
|
|
// BUGBUG: this should cause the DVOC to call "SetActiveObject" on
|
|
// trident, and it won't do this since we just set focus to ourself...
|
|
SetFocus(_hwndListview);
|
|
ListView_SetItemState(_hwndListview, -1, LVIS_SELECTED, LVIS_SELECTED);
|
|
ResetWaitCursor();
|
|
}
|
|
break;
|
|
}
|
|
case SFVIDM_DESELECT_ALL:
|
|
ListView_SetItemState(_hwndListview, -1, 0, LVIS_SELECTED);
|
|
break;
|
|
case SFVIDM_SELECT_INVERT:
|
|
{
|
|
DECLAREWAITCURSOR;
|
|
SetWaitCursor();
|
|
// BUGBUG: this should cause the DVOC to call "SetActiveObject" on
|
|
// trident, and it won't do this since we just set focus to ourself...
|
|
SetFocus(_hwndListview);
|
|
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;
|
|
}
|
|
#if 0
|
|
case SFVIDM_SELECT_FILELIST:
|
|
{
|
|
int nItem = -1;
|
|
WIN32_FIND_DATA fd;
|
|
|
|
DECLAREWAITCURSOR;
|
|
SetWaitCursor();
|
|
|
|
// BUGBUG: this should cause the DVOC to call "SetActiveObject" on
|
|
// trident, and it won't do this since we just set focus to ourself...
|
|
SetFocus(_hwndListview);
|
|
|
|
// First, deselect all...
|
|
ListView_SetItemState(_hwndListview, -1, 0, LVIS_SELECTED);
|
|
|
|
// Second, select all files that match the criteria.
|
|
if (lParam) {
|
|
HANDLE hfind = FindFirstFile((LPCSTR)lParam, &fd);
|
|
if (hfind != INVALID_HANDLE_VALUE) {
|
|
do {
|
|
LPITEMIDLIST pidl = ILCreateFromPath(fd.cFileName);
|
|
if (pidl) {
|
|
int nItem = _FindItem(pidl, NULL, FALSE);
|
|
|
|
// flip the selection bit on each item
|
|
UINT flag = ListView_GetItemState(_hwndListview, nItem, LVIS_SELECTED);
|
|
SetFlag(flag, LVNI_SELECTED);
|
|
ListView_SetItemState(_hwndListview, nItem, flag, LVIS_SELECTED);
|
|
|
|
ILFree(pidl);
|
|
}
|
|
} while (FindNextFile(hfind, &fd));
|
|
FindClose(hfind);
|
|
}
|
|
}
|
|
|
|
ResetWaitCursor();
|
|
break;
|
|
}
|
|
#endif // 0
|
|
case SFVIDM_FILE_RENAME:
|
|
// May need to add some more support here later, but...
|
|
{
|
|
int iItemFocus;
|
|
|
|
RenameIt_NormalView:
|
|
iItemFocus = ListView_GetNextItem(_hwndListview, -1, LVNI_FOCUSED);
|
|
if (iItemFocus >= 0) {
|
|
// Deselect all items...
|
|
UINT uState = ListView_GetItemState(_hwndListview, iItemFocus, LVIS_SELECTED);
|
|
int iNextItemSelect = ListView_GetNextItem(_hwndListview, iItemFocus, LVIS_SELECTED);
|
|
int iPrevItemSelect = ListView_GetNextItem(_hwndListview, -1, LVIS_SELECTED);
|
|
if (((iNextItemSelect >= 0) && (iNextItemSelect != iItemFocus)) || ((iPrevItemSelect >= 0) && (iPrevItemSelect != iItemFocus))) {
|
|
// Deselect all items...
|
|
ListView_SetItemState(_hwndListview, -1, 0, LVIS_SELECTED);
|
|
if (uState)
|
|
ListView_SetItemState(_hwndListview, iItemFocus, LVIS_SELECTED, LVIS_SELECTED);
|
|
}
|
|
ListView_EditLabel(_hwndListview, iItemFocus);
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
// BUGBUG: should not be any of these
|
|
if (!lParam) {
|
|
#ifdef DEBUG
|
|
MessageBox(_hwndView, TEXT("This feature is not implemented yet"), TEXT("UNDER CONSTRUCTION"), MB_OK);
|
|
TraceMsg(TF_ERROR, "command not processed in %s at %d (%x)", __FILE__, __LINE__, uID);
|
|
ASSERT(0);
|
|
#endif // DEBUG
|
|
}
|
|
return(1);
|
|
}
|
|
} // End of switch statement.
|
|
} //if _HasNormalView view
|
|
} // first switch for this-folder specific commands
|
|
|
|
return(0);
|
|
}
|
|
|
|
|
|
LPITEMIDLIST CDefView::_ObjectExists(LPCITEMIDLIST pidl)
|
|
{
|
|
LPITEMIDLIST pidlReal = NULL;
|
|
|
|
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 {
|
|
LPITEMIDLIST ppItems[2];
|
|
|
|
// rename within this folder
|
|
ppItems[0] = (LPITEMIDLIST)ILFindLastID(ppidl[0]);
|
|
ppItems[1] = _ObjectExists(ILFindLastID(ppidl[1]));
|
|
if (ppItems[1]) {
|
|
if (_UpdateObject(ppItems) == -1)
|
|
ILFree(ppItems[1]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// SFVM_UPDATESTATUSBAR return values:
|
|
|
|
// failure code = Callback did not do anything, we must do it all
|
|
|
|
// Otherwise, the GetScode(hres) 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 DV_UpdateStatusBar(CDefView* pdsv, BOOL fInitialize)
|
|
{
|
|
HRESULT hres = pdsv->CallCB(SFVM_UPDATESTATUSBAR, fInitialize, 0);
|
|
|
|
if (FAILED(hres)) {
|
|
// Client wants us to do everything
|
|
DV_DoDefaultStatusBar(pdsv, fInitialize);
|
|
} else if (hres & SFVUSB_INITED) {
|
|
// Client wants us to do text but not initialize
|
|
DV_DoDefaultStatusBar(pdsv, FALSE);
|
|
}
|
|
}
|
|
|
|
|
|
BOOL CDefView::_CanShowWebView()
|
|
{
|
|
// Quattro Pro (QPW) is too stupid to 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 screwed 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.
|
|
SHELLSTATE ss;
|
|
|
|
SHGetSetSettings(&ss, SSF_WIN95CLASSIC | SSF_DESKTOPHTML | SSF_WEBVIEW, FALSE);
|
|
|
|
return(!DV_CDB_IsCommonDialog(this) && !_IsDesktop() && ss.fWebView && !(SHGetAppCompatFlags(ACF_OLDCREATEVIEWWND) & ACF_OLDCREATEVIEWWND) && !(SHGetObjectCompatFlags(_pshf, NULL) & OBJCOMPATF_NO_WEBVIEW));
|
|
}
|
|
|
|
#define FSNDEBUG
|
|
|
|
|
|
// Processes a 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
|
|
|
|
switch (lNotification) {
|
|
case SHCNE_DRIVEADD:
|
|
case SHCNE_CREATE:
|
|
case SHCNE_MKDIR:
|
|
pidlItem = ILFindLastID(ppidl[0]);
|
|
pidl = _ObjectExists(pidlItem);
|
|
if (pidl) {
|
|
// if the item is already in our listview, or if we fail to add it
|
|
// then cleanup because we didnt' store the pidl.
|
|
if (_FindItem(pidlItem, NULL, FALSE) != -1) {
|
|
LPITEMIDLIST pp[2];
|
|
pp[0] = (LPITEMIDLIST)pidlItem;
|
|
pp[1] = pidl;
|
|
if (_UpdateObject(pp) < 0)
|
|
ILFree(pidl); // we're bummed
|
|
} else if (DefView_AddObject(this, pidl) == -1) {
|
|
TraceMsg(TF_DEFVIEW, "CDefView::_OnFSNotify: Item already exists.");
|
|
ILFree(pidl);
|
|
} else {
|
|
// Item got added OK, we'll need to save it's position.
|
|
_bItemsMoved = TRUE;
|
|
}
|
|
}
|
|
break;
|
|
case SHCNE_DRIVEREMOVED:
|
|
case SHCNE_DELETE:
|
|
case SHCNE_RMDIR:
|
|
pidlItem = ILFindLastID(ppidl[0]);
|
|
_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) {
|
|
DefView_UpdateImage(this, iImage);
|
|
}
|
|
} else if (ppidl && ppidl[0]) {
|
|
int iImage = *(int UNALIGNED*)((BYTE*)ppidl[0] + 2);
|
|
DefView_UpdateImage(this, 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:
|
|
// Also include SHCNE_ATTRIBUTE--the print folder uses this when
|
|
// the details view of a printer changes, but not the icon view.
|
|
// As an optimization, we can only do a SetItem in _UpdateObject
|
|
// if we are in non-icon view, since the name and icon didn't
|
|
// change.
|
|
if ((LVStyleFromView(this) & LVS_TYPEMASK) != LVS_REPORT) {
|
|
// Since we are not in report view, don't update the object.
|
|
|
|
// Since we won't call through to update object we may need to let automation know..
|
|
// BUGBUG:: What happens later if we do change to report view, won't we show old attributes?
|
|
CheckIfSelectedAndNotifyAutomation(ILFindLastID(ppidl[0]), -1);
|
|
break;
|
|
}
|
|
|
|
// Fall through to SHCNE_UPDATEITEM handling.
|
|
case SHCNE_MEDIAINSERTED:
|
|
case SHCNE_MEDIAREMOVED:
|
|
case SHCNE_NETUNSHARE:
|
|
case SHCNE_NETSHARE:
|
|
case SHCNE_UPDATEITEM:
|
|
if (ppidl) {
|
|
LPITEMIDLIST pp[2];
|
|
|
|
pp[0] = ILFindLastID(ppidl[0]);
|
|
pp[1] = _ObjectExists(pp[0]);
|
|
if (pp[1]) {
|
|
if (_UpdateObject(pp) < 0) {
|
|
// something went wrong
|
|
DefView_Update(this);
|
|
ILFree(pp[1]);
|
|
}
|
|
} 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 pPidlView = _GetViewPidl();
|
|
if (pPidlView) {
|
|
if (ILIsEqual(ppidl[0], pPidlView))
|
|
DefView_Update(this);
|
|
|
|
ILFree(pPidlView);
|
|
}
|
|
}
|
|
} else // ppidl == NULL means update all items (re-enum them)
|
|
{
|
|
DefView_Update(this);
|
|
}
|
|
break;
|
|
case SHCNE_FREESPACE:
|
|
// BUGBUG: should only do this if the drive is ours
|
|
// and we should change the bool to an enum
|
|
if (_pidlMonitor) {
|
|
TCHAR szPath[MAX_PATH];
|
|
if (SHGetPathFromIDList(_pidlMonitor, szPath)) {
|
|
DWORD dwChangedDrives = *(DWORD UNALIGNED*)((BYTE*)ppidl[0] + 2);
|
|
int idDrive = PathGetDriveNumber(szPath);
|
|
TraceMsg(TF_DEFVIEW, "Changed drives = %x", dwChangedDrives);
|
|
if (idDrive != -1 && ((1 << idDrive) & dwChangedDrives)) {
|
|
DV_UpdateStatusBar(this, TRUE);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
TraceMsg(TF_DEFVIEW, "DefView: unknown FSNotify %08lX, doing full update", lNotification);
|
|
DefView_Update(this);
|
|
break;
|
|
}
|
|
|
|
DV_UpdateStatusBar(this, FALSE);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
BOOL CDefView::_GetItemPosition(LPCITEMIDLIST pidl, POINT* ppt)
|
|
{
|
|
int i = _FindItem(pidl, NULL, FALSE);
|
|
if (i != -1)
|
|
return ListView_GetItemPosition(_hwndListview, i, ppt);
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
// called when some of our objects get put on the clipboard
|
|
LRESULT DSV_OnSetClipboard(CDefView* pdsv, WPARAM idCmd)
|
|
{
|
|
TraceMsg(TF_DEFVIEW, "DSV_OnSetClipboard");
|
|
|
|
ASSERT((idCmd == DFM_CMD_MOVE) || (idCmd == DFM_CMD_COPY));
|
|
|
|
if (idCmd == DFM_CMD_MOVE) // move
|
|
{
|
|
// mark all selected items as being "cut"
|
|
int i = -1;
|
|
while ((i = ListView_GetNextItem(pdsv->_hwndListview, i, LVIS_SELECTED)) != -1) {
|
|
ListView_SetItemState(pdsv->_hwndListview, i, LVIS_CUT, LVIS_CUT);
|
|
//ListView_RedrawItems(pdsv->_hwndListview, i, i+1); // is there a better way?
|
|
pdsv->_bHaveCutStuff = TRUE;
|
|
}
|
|
|
|
// join the clipboard viewer chain so we will know when to
|
|
// "uncut" our selected items.
|
|
if (pdsv->_bHaveCutStuff) {
|
|
ASSERT(!pdsv->_bClipViewer);
|
|
ASSERT(pdsv->_hwndNextViewer == NULL);
|
|
|
|
pdsv->_hwndNextViewer = SetClipboardViewer(pdsv->_hwndView);
|
|
pdsv->_bClipViewer = TRUE;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
// called when the clipboard get changed, clear any items in the "cut" state
|
|
LRESULT DSV_OnClipboardChange(CDefView* pdsv)
|
|
{
|
|
// if we dont have any cut stuff we dont care.
|
|
if (!pdsv->_bHaveCutStuff)
|
|
return 0;
|
|
|
|
ASSERT(pdsv->_bClipViewer);
|
|
TraceMsg(TF_DEFVIEW, "DSV_OnDrawClipboard");
|
|
|
|
pdsv->_RestoreAllGhostedFileView();
|
|
pdsv->_bHaveCutStuff = FALSE;
|
|
|
|
// unhook from the clipboard viewer chain.
|
|
ChangeClipboardChain(pdsv->_hwndView, pdsv->_hwndNextViewer);
|
|
pdsv->_bClipViewer = FALSE;
|
|
pdsv->_hwndNextViewer = NULL;
|
|
return 0;
|
|
}
|
|
|
|
|
|
// Note: this function returns the point in Listview Coordinate
|
|
// space. So any hit testing done with this needs to be converted
|
|
// back to Client coordinate space...
|
|
BOOL DefView_GetDropPoint(CDefView* pdv, POINT* ppt)
|
|
{
|
|
// Check whether we already have gotten the drop anchor (before any
|
|
// menu processing)
|
|
if (pdv->_bDropAnchor) {
|
|
*ppt = pdv->_ptDrop;
|
|
LVUtil_ClientToLV(pdv->_hwndListview, ppt);
|
|
} else if (pdv->_bMouseMenu) {
|
|
*ppt = pdv->_ptDragAnchor;
|
|
return TRUE;
|
|
} else {
|
|
// We need the most up-to-date cursor information, since this
|
|
// may be called during a drop, and the last time the current
|
|
// thread called GetMessage was about 10 minutes ago
|
|
GetCursorPos(ppt);
|
|
LVUtil_ScreenToLV(pdv->_hwndListview, ppt);
|
|
}
|
|
|
|
return pdv->_bDropAnchor;
|
|
}
|
|
|
|
|
|
BOOL DefView_GetDragPoint(CDefView* pdv, POINT* ppt)
|
|
{
|
|
BOOL fSource = pdv->_bDragSource || pdv->_bMouseMenu;
|
|
if (fSource) {
|
|
// if anchor from mouse activity
|
|
*ppt = pdv->_ptDragAnchor;
|
|
} else {
|
|
// if anchor from keyboard activity... use the focused item
|
|
int i = ListView_GetNextItem(pdv->_hwndListview, -1, LVNI_FOCUSED);
|
|
if (i != -1)
|
|
ListView_GetItemPosition(pdv->_hwndListview, i, ppt);
|
|
}
|
|
|
|
return fSource;
|
|
}
|
|
|
|
|
|
void DV_PaintErrMsg(HWND hWnd, CDefView* pdsv)
|
|
{
|
|
PAINTSTRUCT ps;
|
|
HDC hdc = BeginPaint(hWnd, &ps);
|
|
RECT rc;
|
|
|
|
// if we're in an error state, make sure we're not in an extended view
|
|
if (pdsv->m_cFrame.IsWebView()) {
|
|
pdsv->_SwitchToViewID((UINT)CSFVFrame::HIDEEXTVIEW, FALSE);
|
|
}
|
|
|
|
GetClientRect(hWnd, &rc);
|
|
#ifdef DEBUG
|
|
TraceMsg(TF_DEFVIEW, "DV_PaintErrMsg is called (%x,%d)", pdsv->_hres, HRESULT_CODE(pdsv->_hres));
|
|
#endif
|
|
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 DV_DoDefaultStatusBar(CDefView* pdsv, BOOL fInitialize)
|
|
{
|
|
if (pdsv->_psb) {
|
|
// Some of the failure cases do not null hwnd...
|
|
HWND hwndStatus = NULL;
|
|
pdsv->_psb->GetControlWindow(FCW_STATUS, &hwndStatus);
|
|
if (hwndStatus) {
|
|
UINT uMsg;
|
|
int nMsgParam;
|
|
TCHAR szTemp[30];
|
|
|
|
if (fInitialize) {
|
|
int ciParts[] = {-1};
|
|
SendMessage(hwndStatus, SB_SETPARTS, ARRAYSIZE(ciParts), (LPARAM)ciParts);
|
|
}
|
|
|
|
IQueryInfo* pqi;
|
|
LPWSTR pwszTip = NULL;
|
|
LPTSTR pszStatus = NULL;
|
|
TCHAR szStatus[128];
|
|
|
|
nMsgParam = ListView_GetSelectedCount(pdsv->_hwndListview);
|
|
switch (nMsgParam) {
|
|
case 0:
|
|
// No objects selected; show total item count
|
|
nMsgParam = ListView_GetItemCount(pdsv->_hwndListview);
|
|
uMsg = IDS_FSSTATUSBASE;
|
|
break;
|
|
case 1:
|
|
if (SUCCEEDED(DefView_GetUIObjectFromItem(pdsv, IID_IQueryInfo, (LPVOID*)&pqi, SVGIO_SELECTION))) {
|
|
pqi->GetInfoTip(0, &pwszTip);
|
|
pqi->Release();
|
|
if (pwszTip) {
|
|
// Infotips often contain \t\r\n characters, so
|
|
// map control characters to spaces. Also collapse
|
|
// consecutive spaces to make us look less stupid.
|
|
LPWSTR pwszDst, pwszSrc;
|
|
|
|
// BUGBUG NT342538: it would be nice to convert \t\r\n to a comma for readability
|
|
|
|
// Since we are unicode, we don't have to worry about DBCS.
|
|
for (pwszDst = pwszSrc = pwszTip; *pwszSrc; pwszSrc++) {
|
|
if ((UINT)*pwszSrc <= (UINT)L' ') {
|
|
if (pwszDst == pwszTip || pwszDst[-1] != L' ') {
|
|
*pwszDst++ = L' ';
|
|
}
|
|
} else {
|
|
*pwszDst++ = *pwszSrc;
|
|
}
|
|
}
|
|
*pwszDst = L'\0';
|
|
// GetInfoTip can return a Null String too.
|
|
if (L'\0' == *pwszTip) {
|
|
SHFree(pwszTip);
|
|
pwszTip = NULL;
|
|
uMsg = IDS_FSSTATUSSELECTED;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
// Object doesn't support InfoTip; do it the old way
|
|
|
|
// FALL THROUGH
|
|
default:
|
|
uMsg = IDS_FSSTATUSSELECTED;
|
|
break;
|
|
}
|
|
|
|
if (pwszTip) {
|
|
if (g_bBiDiW95Loc) {
|
|
szStatus[0] = szStatus[1] = TEXT('\t');
|
|
SHUnicodeToTChar(pwszTip, &szStatus[2], ARRAYSIZE(szStatus) - 2);
|
|
SendMessage(hwndStatus, SB_SETTEXT, (WPARAM)SBT_RTLREADING, (LPARAM)szStatus);
|
|
} else {
|
|
SendMessage(hwndStatus, SB_SETTEXTW, (WPARAM)0, (LPARAM)pwszTip);
|
|
}
|
|
|
|
SHFree(pwszTip);
|
|
} else {
|
|
pszStatus = ShellConstructMessageString(HINST_THISDLL, MAKEINTRESOURCE(uMsg), AddCommas(nMsgParam, szTemp));
|
|
if (pszStatus) {
|
|
if (g_bBiDiW95Loc) {
|
|
szStatus[0] = szStatus[1] = TEXT('\t');
|
|
StrCpyN(&szStatus[2], pszStatus, ARRAYSIZE(szStatus) - 2);
|
|
SendMessage(hwndStatus, SB_SETTEXT, (WPARAM)SBT_RTLREADING, (LPARAM)szStatus);
|
|
} else {
|
|
SendMessage(hwndStatus, SB_SETTEXT, (WPARAM)0, (LPARAM)pszStatus);
|
|
}
|
|
|
|
LocalFree(pszStatus);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
#define DV_IDTIMER_BUFFERED_REFRESH 3
|
|
#define BUFFERED_REFRESH_TIMEOUT 5000 //5 seconds.
|
|
|
|
|
|
void DefView_Desktop_OnWinIniChange(CDefView* pdsv, WPARAM wParam, LPCTSTR lpszSection)
|
|
{
|
|
if (lpszSection) {
|
|
if (!lstrcmpi(lpszSection, TEXT("ToggleDesktop")))
|
|
pdsv->Command(NULL, SFVIDM_DESKTOPHTML_WEBCONTENT, 0);
|
|
else {
|
|
if (!lstrcmpi(lpszSection, TEXT("RefreshDesktop"))) {
|
|
if (FAILED(pdsv->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(lpszSection, TEXT("BufferedRefresh"))) {
|
|
//See if we have already started a timer to refresh
|
|
if (!pdsv->_fRefreshBuffered) {
|
|
TraceMsg(TF_DEFVIEW, "A Buffered refresh starts the timer");
|
|
SetTimer(pdsv->_hwndView, DV_IDTIMER_BUFFERED_REFRESH, BUFFERED_REFRESH_TIMEOUT, NULL);
|
|
pdsv->_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) {
|
|
DSV_SetFolderColors(pdsv);
|
|
InvalidateRect(pdsv->_hwndListview, NULL, TRUE);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
if (wParam == SPI_SETDESKWALLPAPER || wParam == SPI_SETDESKPATTERN) {
|
|
DSV_SetFolderColors(pdsv);
|
|
InvalidateRect(pdsv->_hwndListview, NULL, TRUE);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void DefView_OnWinIniChange(CDefView* pdsv, WPARAM wParam, LPCTSTR lpszSection)
|
|
{
|
|
if ((wParam == SPI_GETICONTITLELOGFONT) || ((wParam == 0) && lpszSection && !lstrcmpi(lpszSection, TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\IconUnderline")))) {
|
|
UpdateUnderlines(pdsv);
|
|
}
|
|
|
|
if (!wParam || (lpszSection && !lstrcmpi(lpszSection, TEXT("intl")))) {
|
|
// has the time format changed while we're in details mode?
|
|
if (FVM_DETAILS == pdsv->_fs.ViewMode) {
|
|
int iItem, iItemCount;
|
|
UINT uiRealColumn;
|
|
|
|
InvalidateRect(pdsv->_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.
|
|
iItemCount = ListView_GetItemCount(pdsv->_hwndListview);
|
|
for (iItem = 0; iItem < iItemCount; ++iItem) {
|
|
for (uiRealColumn = 0; uiRealColumn < pdsv->m_cColItems; ++uiRealColumn) {
|
|
DWORD dwFlags;
|
|
|
|
dwFlags = pdsv->m_pColItems[uiRealColumn].csFlags;
|
|
if (((dwFlags & SHCOLSTATE_EXTENDED) != 0) && ((dwFlags & SHCOLSTATE_TYPEMASK) == SHCOLSTATE_TYPE_DATE)) {
|
|
UINT uiVisibleColumn;
|
|
|
|
if (SUCCEEDED(pdsv->MapRealToVisibleColumn(uiRealColumn, &uiVisibleColumn))) {
|
|
ListView_SetItemText(pdsv->_hwndListview, iItem, uiVisibleColumn, LPSTR_TEXTCALLBACK);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// we may need to rebuild the icon cache.
|
|
if (wParam == SPI_SETICONMETRICS || wParam == SPI_SETNONCLIENTMETRICS) {
|
|
HIMAGELIST himlLarge, himlSmall;
|
|
|
|
Shell_GetImageLists(&himlLarge, &himlSmall);
|
|
ListView_SetImageList(pdsv->_hwndListview, himlLarge, LVSIL_NORMAL);
|
|
ListView_SetImageList(pdsv->_hwndListview, himlSmall, LVSIL_SMALL);
|
|
}
|
|
|
|
// we need to invalidate the cursor cache
|
|
if (wParam == SPI_SETCURSORS) {
|
|
DAD_InvalidateCursors();
|
|
}
|
|
|
|
if (pdsv->_IsDesktop()) {
|
|
DefView_Desktop_OnWinIniChange(pdsv, wParam, lpszSection);
|
|
}
|
|
}
|
|
|
|
|
|
void SetDefaultViewSettings(CDefView* pdsv)
|
|
{
|
|
// 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
|
|
if (!pdsv->_hwndStatic && !pdsv->_IsDesktop() && pdsv->_HasNormalView()) {
|
|
HWND hwndTree;
|
|
pdsv->_psb->GetControlWindow(FCW_TREE, &hwndTree);
|
|
|
|
// only do this for non-explorers
|
|
if (!hwndTree) {
|
|
SHELLSTATE ss;
|
|
|
|
ss.lParamSort = pdsv->_dvState.lParamSort;
|
|
ss.iSortDirection = pdsv->_dvState.iDirection;
|
|
SHGetSetSettings(&ss, SSF_SORTCOLUMNS, TRUE);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
HWND CDefView::GetChildViewWindow()
|
|
{
|
|
if (m_cFrame.IsWebView())
|
|
return m_cFrame.GetExtendedViewWindow();
|
|
|
|
return _hwndListview;
|
|
}
|
|
|
|
|
|
void DefView_SetFocus(CDefView* pdsv)
|
|
{
|
|
// if it's a combined view then we need to give focus to listview
|
|
if (!pdsv->_fCombinedView && pdsv->m_cFrame.IsWebView()) {
|
|
pdsv->_OnViewWindowActive();
|
|
|
|
if (pdsv->m_cFrame.m_pOleObj) {
|
|
MSG msg = {pdsv->_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.
|
|
pdsv->OnActivate(SVUIA_ACTIVATE_FOCUS);
|
|
pdsv->m_cFrame._UIActivateIO(TRUE, &msg);
|
|
}
|
|
} else if (pdsv->_HasNormalView()) {
|
|
HWND hwnd = NULL;
|
|
pdsv->CallCB(SFVM_SETFOCUS, 0, 0);
|
|
if (pdsv->m_cFrame.IsSFVExtension())
|
|
hwnd = pdsv->m_cFrame.GetExtendedViewWindow();
|
|
else
|
|
hwnd = pdsv->_hwndListview;
|
|
if (hwnd)
|
|
SetFocus(hwnd);
|
|
if (!pdsv->_IsDesktop()) {
|
|
pdsv->m_cFrame.m_uState = SVUIA_ACTIVATE_FOCUS;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
LRESULT CALLBACK DefView_WndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
CDefView* pdsv = (CDefView*)GetWindowLongPtr(hWnd, 0);
|
|
LRESULT l;
|
|
DWORD dwID;
|
|
|
|
if (iMessage == WM_CREATE) {
|
|
pdsv = (CDefView*)(((LPCREATESTRUCT)lParam)->lpCreateParams);
|
|
return pdsv->_OnCreate(hWnd);
|
|
}
|
|
|
|
if (pdsv == NULL) {
|
|
if (iMessage != WM_NCCALCSIZE && iMessage != WM_NCCREATE) {
|
|
ASSERT(0); // we are not supposed to hit this assert
|
|
}
|
|
goto DoDefWndProc;
|
|
}
|
|
|
|
switch (iMessage) {
|
|
// IShellBrowser forwards these to the IShellView. In the case
|
|
// of our IShellView extended views, we need to forward them
|
|
// down too. 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(pdsv->_hwndListview);
|
|
|
|
// We only want to update the sort arrows if they are already present.
|
|
if (hwndHead) {
|
|
Header_GetItem(hwndHead, pdsv->_dvState.lParamSort, &hdi);
|
|
if (hdi.fmt & HDF_BITMAP)
|
|
pdsv->_SetSortArrows();
|
|
}
|
|
// fall through
|
|
}
|
|
case WM_ENTERSIZEMOVE:
|
|
case WM_EXITSIZEMOVE:
|
|
case WM_WININICHANGE:
|
|
case WM_FONTCHANGE:
|
|
if (pdsv->m_cFrame.IsWebView()) {
|
|
HWND hwndExt = pdsv->m_cFrame.GetExtendedViewWindow();
|
|
if (hwndExt) {
|
|
SendMessage(hwndExt, iMessage, wParam, lParam);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
switch (iMessage) {
|
|
|
|
case WM_DESTROY:
|
|
if (GetKeyState(VK_CONTROL) < 0)
|
|
SetDefaultViewSettings(pdsv);
|
|
|
|
// since we're no longer flushing cached context menus
|
|
// on WM_DSV_MENUTERM (because now both Edit and File use different ones
|
|
// and they flush old ones before they create new ones)
|
|
// we need to flush the context menus hanging around before we kill defview
|
|
ATOMICRELEASE(pdsv->_pcmSel);
|
|
ATOMICRELEASE(pdsv->_pcmBackground);
|
|
|
|
pdsv->EmptyBkgrndThread(TRUE);
|
|
|
|
// Depending on when it is closed we may have an outstanding post
|
|
// to us about the rest of the fill data which we should try to
|
|
// process in order to keep from leaking stuff...
|
|
|
|
// logically hWnd == pdsv->_hwndView, but we already zeroed
|
|
// pdsv->_hwndView so use hWnd
|
|
|
|
DefView_CheckForFillDoneOnDestroy(hWnd);
|
|
|
|
// remove ourself as a clipboard viewer
|
|
if (pdsv->_bClipViewer) {
|
|
ChangeClipboardChain(hWnd, pdsv->_hwndNextViewer);
|
|
pdsv->_bClipViewer = FALSE;
|
|
pdsv->_hwndNextViewer = NULL;
|
|
}
|
|
|
|
if (pdsv->_uRegister) {
|
|
ULONG uRegister = pdsv->_uRegister;
|
|
pdsv->_uRegister = 0;
|
|
SHChangeNotifyDeregister(uRegister);
|
|
}
|
|
|
|
ATOMICRELEASE(pdsv->_psd);
|
|
ATOMICRELEASE(pdsv->_pdtgtBack);
|
|
|
|
ASSERT(pdsv->_pdtobjHdrop == NULL); // pdsv should not still be around
|
|
|
|
if (pdsv->_hwndListview) {
|
|
EnableCombinedView(pdsv, FALSE);
|
|
|
|
if (pdsv->_bRegisteredDragDrop)
|
|
RevokeDragDrop(pdsv->_hwndListview);
|
|
}
|
|
|
|
// Release any automation object we may be holding onto, simply call our own
|
|
// SetAutomationObject with NULL
|
|
pdsv->SetAutomationObject(NULL);
|
|
break;
|
|
case WM_NCDESTROY:
|
|
pdsv->_hwndView = NULL;
|
|
ATOMICRELEASE(pdsv);// Now we can release it.
|
|
SetWindowLongPtr(hWnd, 0, 0);
|
|
_IconCacheFlush(FALSE);// get rid of extra junk in the icon cache
|
|
break;
|
|
case WM_ENABLE:
|
|
pdsv->_fDisabled = !wParam;
|
|
break;
|
|
case WM_ERASEBKGND:
|
|
{
|
|
COLORREF cr = ListView_GetBkColor(pdsv->_hwndListview);
|
|
if (cr == CLR_NONE)
|
|
return SendMessage(pdsv->_hwndMain, iMessage, 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 (pdsv->_fEnumFailed)
|
|
DV_PaintErrMsg(hWnd, pdsv);
|
|
else
|
|
goto DoDefWndProc;
|
|
break;
|
|
case WM_LBUTTONUP:
|
|
if (pdsv->_fEnumFailed)
|
|
PostMessage(hWnd, WM_KEYDOWN, (WPARAM)VK_F5, 0);
|
|
else
|
|
goto DoDefWndProc;
|
|
break;
|
|
case WM_SETFOCUS:
|
|
if (pdsv->_hwndView) // Ignore if we are destroying _hwndView.
|
|
{
|
|
DefView_SetFocus(pdsv);
|
|
}
|
|
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(pdsv->_hwndListview, &lvhti.pt);
|
|
ListView_HitTest(pdsv->_hwndListview, &lvhti);
|
|
if (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(pdsv->_hwndListview);
|
|
// if active view created, call active object to allow it to visualize activation.
|
|
if (pdsv->m_cFrame.m_pActive)
|
|
(pdsv->m_cFrame.m_pActive)->OnFrameWindowActivate((BOOL)wParam);
|
|
break;
|
|
case WM_SIZE:
|
|
return pdsv->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 pdsv->DestroyViewWindow, which destroys the ListView
|
|
// which destroys the NMHDR!)
|
|
UINT code = ((NMHDR*)lParam)->code;
|
|
#endif
|
|
pdsv->AddRef(); // just in case
|
|
DefView_StartNotify(pdsv, code);
|
|
l = pdsv->_OnNotify((NMHDR*)lParam);
|
|
DefView_StopNotify(pdsv, code);
|
|
pdsv->Release(); // release
|
|
return l;
|
|
}
|
|
case WM_CONTEXTMENU:
|
|
TraceMsg(TF_DEFVIEW, "GOT WM_CONTEXTMENU %d %d !!!!!!!!!", wParam, lParam);
|
|
if (!pdsv->_fDisabled) {
|
|
if (lParam != (LPARAM)-1) {
|
|
pdsv->_bMouseMenu = TRUE;
|
|
pdsv->_ptDragAnchor.x = GET_X_LPARAM(lParam);
|
|
pdsv->_ptDragAnchor.y = GET_Y_LPARAM(lParam);
|
|
LVUtil_ScreenToLV(pdsv->_hwndListview, &pdsv->_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...
|
|
pdsv->AddRef();
|
|
pdsv->_bContextMenuMode = TRUE;
|
|
pdsv->ContextMenu((DWORD)lParam);
|
|
pdsv->_bContextMenuMode = FALSE;
|
|
if (lParam != (DWORD)-1) {
|
|
pdsv->_bMouseMenu = FALSE;
|
|
}
|
|
ATOMICRELEASE(pdsv);
|
|
}
|
|
break;
|
|
case WM_COMMAND:
|
|
return pdsv->Command(NULL, wParam, lParam);
|
|
case WM_DRAGSELECT:
|
|
case WM_DRAGMOVE:
|
|
case WM_QUERYDROPOBJECT:
|
|
case WM_DROPOBJECT:
|
|
case WM_DROPFILES:
|
|
return pdsv->OldDragMsgs(iMessage, wParam, (const DROPSTRUCT*)lParam);
|
|
case WM_DSV_DISABLEACTIVEDESKTOP:
|
|
DisableActiveDesktop();
|
|
break;
|
|
case WM_DSV_BACKGROUNDENUMDONE:
|
|
pdsv->_PostEnumDoneMessage();
|
|
pdsv->CallCB(SFVM_BACKGROUNDENUMDONE, 0, 0);
|
|
break;
|
|
case WM_DSV_FSNOTIFY:
|
|
{
|
|
LPITEMIDLIST* ppidl;
|
|
LONG lEvent;
|
|
|
|
TIMESTART(pdsv->_FSNotify);
|
|
|
|
LPSHChangeNotificationLock pshcnl = SHChangeNotification_Lock((HANDLE)wParam, (DWORD)lParam, &ppidl, &lEvent);
|
|
if (pshcnl) {
|
|
if (pdsv->_fDisabled || (pdsv->CallCB(SFVM_FSNOTIFY, (WPARAM)ppidl, (LPARAM)lEvent) == S_FALSE)) {
|
|
lParam = 0;
|
|
} else {
|
|
IShellChangeNotify* pscn;
|
|
if (pdsv->m_cFrame.IsSFVExtension() && SUCCEEDED(pdsv->m_cFrame.m_pActiveSFV->QueryInterface(IID_IShellChangeNotify, (void**)&pscn))) {
|
|
lParam = pscn->OnChange(lEvent, ppidl[0], ppidl[1]);
|
|
pscn->Release();
|
|
}
|
|
lParam = pdsv->_OnFSNotify(lEvent, (LPCITEMIDLIST*)ppidl);
|
|
}
|
|
SHChangeNotification_Unlock(pshcnl);
|
|
}
|
|
TIMESTOP(pdsv->_FSNotify);
|
|
}
|
|
return lParam;
|
|
case WM_DSV_DESTROYSTATIC:
|
|
pdsv->FillDone((HDPA)lParam, pdsv->_pSaveHeader, pdsv->_uSaveHeaderLen, (BOOL)wParam, FALSE);
|
|
break;
|
|
|
|
// the background thread will post this message to us
|
|
// when it has finished extracting a icon in the background.
|
|
|
|
// wParam is 0
|
|
// lParam contains PIDL, iIconIndex, and an "already used" flag
|
|
case WM_DSV_UPDATEICON:
|
|
pdsv->_UpdateIcon((CDVGetIconTask*)lParam);
|
|
break;
|
|
case WM_DSV_UPDATECOLDATA:
|
|
pdsv->_UpdateColData((CBackgroundColInfo*)lParam);
|
|
break;
|
|
case WM_DSV_UPDATEOVERLAY:
|
|
pdsv->_UpdateOverlay((int)wParam, (int)lParam);
|
|
break;
|
|
case WM_DSV_SHOWDRAGIMAGE:
|
|
return DAD_ShowDragImage((BOOL)lParam);
|
|
case WM_DSV_DESKTOPCONTEXTMENU:
|
|
{
|
|
HMENU hmenu;
|
|
if (hmenu = CDesktop_GetActiveDesktopMenu()) {
|
|
int idCmd;
|
|
TPMPARAMS tpmp;
|
|
|
|
tpmp.cbSize = sizeof(tpmp);
|
|
GetWindowRect((HWND)wParam, &tpmp.rcExclude);
|
|
|
|
idCmd = TrackPopupMenuEx(hmenu, TPM_RETURNCMD | TPM_LEFTBUTTON | TPM_NONOTIFY, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), hWnd, &tpmp);
|
|
DestroyMenu(hmenu);
|
|
|
|
if (idCmd)
|
|
pdsv->Command(NULL, idCmd, 0);
|
|
}
|
|
}
|
|
break;
|
|
case GET_WM_CTLCOLOR_MSG(CTLCOLOR_STATIC):
|
|
SetBkColor(GET_WM_CTLCOLOR_HDC(wParam, lParam, iMessage), GetSysColor(COLOR_WINDOW));
|
|
return (LRESULT)GetSysColorBrush(COLOR_WINDOW);
|
|
case WM_DRAWCLIPBOARD:
|
|
if (pdsv->_hwndNextViewer != NULL)
|
|
SendMessage(pdsv->_hwndNextViewer, iMessage, wParam, lParam);
|
|
|
|
if (pdsv->_bClipViewer)
|
|
return DSV_OnClipboardChange(pdsv);
|
|
|
|
break;
|
|
case WM_CHANGECBCHAIN:
|
|
if ((HWND)wParam == pdsv->_hwndNextViewer) {
|
|
pdsv->_hwndNextViewer = (HWND)lParam;
|
|
return TRUE;
|
|
}
|
|
|
|
if (pdsv->_hwndNextViewer != NULL)
|
|
return SendMessage(pdsv->_hwndNextViewer, iMessage, wParam, lParam);
|
|
break;
|
|
case WM_WININICHANGE:
|
|
DefView_OnWinIniChange(pdsv, wParam, (LPCTSTR)lParam);
|
|
SendMessage(pdsv->_hwndListview, iMessage, wParam, lParam);
|
|
break;
|
|
case WM_SHELLNOTIFY:
|
|
#define SHELLNOTIFY_SETDESKWALLPAPER 0x0004
|
|
if (wParam == SHELLNOTIFY_SETDESKWALLPAPER) {
|
|
if (pdsv->_IsDesktop()) {
|
|
pdsv->_fHasDeskWallPaper = (lParam != 0);
|
|
DSV_SetFolderColors(pdsv);
|
|
InvalidateRect(pdsv->_hwndListview, NULL, TRUE);
|
|
}
|
|
}
|
|
break;
|
|
case WM_INITMENU:
|
|
DefView_OnInitMenu(pdsv);
|
|
break;
|
|
case WM_INITMENUPOPUP:
|
|
dwID = GetMenuItemID((HMENU)wParam, LOWORD(lParam));
|
|
if (pdsv->OnInitMenuPopup((HMENU)wParam, LOWORD(lParam), HIWORD(lParam)))
|
|
goto RelayToCM;
|
|
break;
|
|
case WM_DSV_MENUTERM:
|
|
// HACKHACK: After the menu goes away, this command is posted to the
|
|
// window, but note that any WM_DSV_MENUTERM from the menu would still
|
|
// be in the queue, so we need to post another WM_DSV_MENUTERM to delete
|
|
// the context menu.
|
|
if (!lParam)
|
|
PostMessage(hWnd, WM_DSV_MENUTERM, 0, 1);
|
|
else
|
|
pdsv->_OnMenuTermination();
|
|
break;
|
|
case WM_EXITMENULOOP:
|
|
// Background: as the user navigates across the menuband, WM_EXITMENULOOP
|
|
// is sent for each menu that closes, and WM_INITMENUPOPUP is sent for
|
|
// the new menu that opens.
|
|
|
|
// Posting the private WM_DSV_MENUTERM puts a wrench in the gears, because
|
|
// it can cause the cached _pcmSel to be freed *after* WM_INITMENUPOPUP
|
|
// had initialized it for the new menu that is popping up. So be sure
|
|
// to post this message only if we have a cached psmSel to really cleanup.
|
|
// Otherwise, don't bother.
|
|
if (pdsv->_pcmSel || pdsv->_pcmBackground)
|
|
PostMessage(hWnd, WM_DSV_MENUTERM, 0, 0);
|
|
break;
|
|
case WM_TIMER:
|
|
ASSERT(hWnd == pdsv->_hwndView);
|
|
KillTimer(hWnd, (UINT)wParam);
|
|
|
|
if (DV_IDTIMER == wParam) {
|
|
if (pdsv->_hwndStatic) {
|
|
WCHAR szName[128];
|
|
HINSTANCE hinst;
|
|
|
|
if (S_OK != pdsv->CallCB(SFVM_GETANIMATION, (WPARAM)&hinst, (LPARAM)szName)) {
|
|
hinst = g_hinst;
|
|
StrCpyW(szName, L"#150");
|
|
}
|
|
|
|
// Animate_OpenEx() except we want the W version always
|
|
SendMessage(pdsv->_hwndStatic, ACM_OPENW, (WPARAM)hinst, (LPARAM)szName);
|
|
}
|
|
} else if (DV_IDTIMER_BUFFERED_REFRESH == wParam) {
|
|
if (pdsv->_fRefreshBuffered) {
|
|
pdsv->_fRefreshBuffered = FALSE;
|
|
PostMessage(pdsv->_hwndView, WM_KEYDOWN, (WPARAM)VK_F5, 0);
|
|
TraceMsg(TF_DEFVIEW, "Buffered Refresh timer causes actual refresh");
|
|
}
|
|
} else if (DV_IDTIMER_NOTIFY_AUTOMATION_SELCHANGE == wParam) {
|
|
pdsv->_bAutoSelChangeTimerSet = FALSE;
|
|
pdsv->NotifyAutomation(DISPID_SELECTIONCHANGED);
|
|
} else {
|
|
ASSERT(DV_IDTIMER_UPDATEPENDING == wParam);
|
|
pdsv->_bUpdatePendingPending = FALSE;
|
|
}
|
|
break;
|
|
case WM_SETCURSOR:
|
|
if (pdsv->_hwndStatic) {
|
|
//TraceMsg(TF_DEFVIEW, "########## SET WAIT CURSOR WM_SETCURSOR %d", pfc->iWaitCount);
|
|
SetCursor(LoadCursor(NULL, IDC_WAIT));
|
|
return TRUE;
|
|
} else
|
|
goto DoDefWndProc;
|
|
case WM_DRAWITEM:
|
|
#define lpdis ((LPDRAWITEMSTRUCT)lParam)
|
|
dwID = lpdis->itemID;
|
|
|
|
if (lpdis->CtlType != ODT_MENU)
|
|
return 0;
|
|
if (InRange(lpdis->itemID, SFVIDM_CLIENT_FIRST, SFVIDM_CLIENT_LAST) && pdsv->HasCB()) {
|
|
pdsv->CallCB(SFVM_DRAWITEM, SFVIDM_CLIENT_FIRST, lParam);
|
|
return 1;
|
|
} else
|
|
goto RelayToCM;
|
|
#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) && pdsv->HasCB()) {
|
|
pdsv->CallCB(SFVM_MEASUREITEM, SFVIDM_CLIENT_FIRST, lParam);
|
|
return 1;
|
|
}
|
|
RelayToCM:
|
|
IContextMenu2* pcm2;
|
|
if (InRange(dwID, SFVIDM_BACK_CONTEXT_FIRST, SFVIDM_BACK_CONTEXT_LAST)) {
|
|
if (pdsv->_pcmBackground) {
|
|
if (SUCCEEDED(pdsv->_pcmBackground->QueryInterface(IID_IContextMenu2, (void**)&pcm2))) {
|
|
pcm2->HandleMenuMsg(iMessage, wParam, lParam);
|
|
ATOMICRELEASE(pcm2);
|
|
}
|
|
}
|
|
} else if (pdsv->_pcmSel || pdsv->_pcmBackground) {
|
|
if (SUCCEEDED((pdsv->_pcmSel ? pdsv->_pcmSel : pdsv->_pcmBackground)->QueryInterface(IID_IContextMenu2, (void**)&pcm2))) {
|
|
pcm2->HandleMenuMsg(iMessage, wParam, lParam);
|
|
ATOMICRELEASE(pcm2);
|
|
}
|
|
}
|
|
return 0;
|
|
case WM_MENUCHAR:
|
|
case WM_NEXTMENU:
|
|
{
|
|
IContextMenu3* pcm3;
|
|
LRESULT ic3Result;
|
|
HRESULT hResic3 = E_FAIL;
|
|
if (pdsv->_pcmSel) {
|
|
if (SUCCEEDED(pdsv->_pcmSel->QueryInterface(IID_IContextMenu3, (void**)&pcm3))) {
|
|
hResic3 = pcm3->HandleMenuMsg2(iMessage, wParam, lParam, &ic3Result);
|
|
ATOMICRELEASE(pcm3);
|
|
}
|
|
if (hResic3 == S_OK)
|
|
return ic3Result;
|
|
}
|
|
//Forward these messages to IContextMenu3 on the DefCm.
|
|
if (pdsv->_pcmBackground) {
|
|
if (SUCCEEDED(pdsv->_pcmBackground->QueryInterface(IID_IContextMenu3, (void**)&pcm3))) {
|
|
hResic3 = pcm3->HandleMenuMsg2(iMessage, wParam, lParam, &ic3Result);
|
|
ATOMICRELEASE(pcm3);
|
|
}
|
|
if (hResic3 == S_OK)
|
|
return ic3Result;
|
|
}
|
|
return MAKELONG(0, MNC_IGNORE);
|
|
break;
|
|
}
|
|
|
|
// there are two possible ways to put help texts in the
|
|
// status bar, (1) processing WM_MENUSELECT or (2) handling MenuHelp
|
|
// messages. (1) is compatible with OLE, but (2) is required anyway
|
|
// for tooltips.
|
|
case WM_MENUSELECT:
|
|
DefView_OnMenuSelect(pdsv, GET_WM_MENUSELECT_CMD(wParam, lParam), GET_WM_MENUSELECT_FLAGS(wParam, lParam), GET_WM_MENUSELECT_HMENU(wParam, lParam));
|
|
break;
|
|
case WM_SYSCOLORCHANGE:
|
|
DSV_SetFolderColors(pdsv);
|
|
SendMessage(pdsv->_hwndListview, iMessage, wParam, lParam);
|
|
break;
|
|
|
|
// BUGBUG: some of these are dead
|
|
|
|
case SVM_SELECTITEM:
|
|
pdsv->SelectItem((LPCITEMIDLIST)lParam, (int)wParam);
|
|
break;
|
|
case SVM_MOVESELECTEDITEMS:
|
|
DefView_MoveSelectedItems(pdsv, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), FALSE);
|
|
break;
|
|
case SVM_GETANCHORPOINT:
|
|
if (wParam)
|
|
return DefView_GetDragPoint(pdsv, (POINT*)lParam);
|
|
else
|
|
return DefView_GetDropPoint(pdsv, (POINT*)lParam);
|
|
case SVM_GETITEMPOSITION:
|
|
return pdsv->_GetItemPosition((LPCITEMIDLIST)wParam, (POINT*)lParam);
|
|
case SVM_SELECTANDPOSITIONITEM:
|
|
{
|
|
UINT i;
|
|
SFM_SAP* psap = (SFM_SAP*)lParam;
|
|
for (i = 0; i < wParam; psap++, i++)
|
|
pdsv->SelectAndPositionItem(psap->pidl, psap->uSelectFlags, psap->fMove ? &psap->pt : NULL);
|
|
break;
|
|
}
|
|
case WM_PALETTECHANGED:
|
|
case WM_QUERYNEWPALETTE:
|
|
{
|
|
HWND hwndT = pdsv->GetChildViewWindow();
|
|
if (!hwndT)
|
|
goto DoDefWndProc;
|
|
|
|
return SendMessage(hwndT, iMessage, wParam, lParam);
|
|
}
|
|
case WM_DSV_REARRANGELISTVIEW:
|
|
pdsv->_ShowAndActivate();
|
|
break;
|
|
case WM_DSV_SENDSELECTIONCHANGED:
|
|
pdsv->_fSelectionChangePending = FALSE;
|
|
DV_UpdateStatusBar(pdsv, FALSE);
|
|
pdsv->_EnableDisableTBButtons();
|
|
|
|
// Send out the selection changed notification to the automation after a delay.
|
|
// Set the timer for the delay here...
|
|
pdsv->_bAutoSelChangeTimerSet = TRUE;
|
|
SetTimer(hWnd, DV_IDTIMER_NOTIFY_AUTOMATION_SELCHANGE, NOTIFY_AUTOMATION_SELCHANGE_TIMEOUT, NULL);
|
|
break;
|
|
case WM_DSV_DESKHTML_CHANGES:
|
|
if (pdsv->_IsDesktop()) {
|
|
IADesktopP2* piadp2;
|
|
|
|
if (SUCCEEDED(SHCoCreateInstance(NULL, &CLSID_ActiveDesktop, NULL, IID_IADesktopP2, (void**)&piadp2))) {
|
|
IActiveDesktopP* piadpp;
|
|
|
|
TraceMsg(TF_DEFVIEW, "WM_DSV_DESKHTML_CHANGES");
|
|
|
|
// 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_IActiveDesktopP, reinterpret_cast<void**>(&piadpp)))) {
|
|
piadpp->EnsureUpdateHTML(); // ignore result
|
|
piadpp->Release();
|
|
}
|
|
piadp2->MakeDynamicChanges(pdsv->m_cFrame.m_pOleObj);
|
|
piadp2->Release();
|
|
}
|
|
}
|
|
break;
|
|
case WM_DSV_FILELISTENUMDONE:
|
|
pdsv->NotifyAutomation(DISPID_FILELISTENUMDONE);
|
|
break;
|
|
default:
|
|
// Handle the magellan mousewheel message.
|
|
if (iMessage == g_msgMSWheel) {
|
|
HWND hwndT = pdsv->GetChildViewWindow();
|
|
|
|
if (!hwndT)
|
|
return 1;
|
|
|
|
return SendMessage(hwndT, iMessage, wParam, lParam);
|
|
}
|
|
|
|
DoDefWndProc:
|
|
return DefWindowProc(hWnd, iMessage, wParam, lParam);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
BOOL DefView_RegisterWindow(void)
|
|
{
|
|
WNDCLASS wc;
|
|
|
|
if (!GetClassInfo(HINST_THISDLL, c_szDefViewClass, &wc)) {
|
|
// don't want vredraw and hredraw because that causes horrible
|
|
// flicker expecially with full drag
|
|
wc.style = CS_PARENTDC;
|
|
wc.lpfnWndProc = DefView_WndProc;
|
|
wc.cbClsExtra = 0;
|
|
wc.cbWndExtra = SIZEOF(CDefView*);
|
|
wc.hInstance = HINST_THISDLL;
|
|
wc.hIcon = NULL;
|
|
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
|
|
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
|
|
wc.lpszMenuName = NULL;
|
|
wc.lpszClassName = c_szDefViewClass;
|
|
|
|
return RegisterClass(&wc);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
CDefView::~CDefView()
|
|
{
|
|
_uState = SVUIA_DEACTIVATE;
|
|
DV_FlushCachedMenu(this);
|
|
|
|
DebugMsg(TF_LIFE, TEXT("dtor CDefView %x"), this);
|
|
|
|
// Just in case, there is a left over.
|
|
_dvdt.LeaveAndReleaseData(this);
|
|
|
|
// We need to give it a chance to clean up.
|
|
CallCB(SFVM_PRERELEASE, 0, 0);
|
|
|
|
DestroyViewWindow();
|
|
|
|
if (_pSaveHeader)
|
|
LocalFree(_pSaveHeader);
|
|
|
|
// We should release _psb after _pshf (for docfindx)
|
|
ATOMICRELEASE(_pshf);
|
|
ATOMICRELEASE(_pshf2);
|
|
ATOMICRELEASE(_psi);
|
|
ATOMICRELEASE(_psio);
|
|
ATOMICRELEASE(_pcdb);
|
|
ATOMICRELEASE(_psb);
|
|
ATOMICRELEASE(_psd);
|
|
ATOMICRELEASE(_pcmSel);
|
|
|
|
// NOTE we dont release psvOuter
|
|
// it has a ref on us
|
|
|
|
if (m_pColItems)
|
|
LocalFree(m_pColItems);
|
|
if (_pcp)
|
|
delete _pcp;
|
|
|
|
if (_pbtn)
|
|
LocalFree(_pbtn);
|
|
|
|
// Cleanup _dvdt
|
|
_dvdt.ReleaseDataObject();
|
|
_dvdt.ReleaseCurrentDropTarget();
|
|
|
|
_ClearSelectList();
|
|
|
|
ATOMICRELEASE(m_pauto);
|
|
|
|
// clean up advisory connection
|
|
ATOMICRELEASE(_padvise);
|
|
|
|
// and our async icon stuff too
|
|
if (_AsyncIconEvent) {
|
|
CloseHandle(_AsyncIconEvent);
|
|
|
|
if (_AsyncIconCount > 0) {
|
|
DebugMsg(TF_WARNING, TEXT("****** possible PIDL memory leak %d"), _AsyncIconCount);
|
|
}
|
|
}
|
|
|
|
//Make sure the task scheduler is gone.
|
|
ATOMICRELEASE(_pScheduler);
|
|
}
|
|
|
|
|
|
BOOL DefView_IdleDoStuff(CDefView* pdsv, LPRUNNABLETASK pTask, REFTASKOWNERID rTID, DWORD_PTR lParam, DWORD dwPriority)
|
|
{
|
|
HRESULT hr;
|
|
|
|
ASSERT(pTask);
|
|
ASSERT(dwPriority > 0);
|
|
|
|
if (pdsv->_pScheduler == NULL) {
|
|
HRESULT hr = CoCreateInstance(CLSID_ShellTaskScheduler, NULL, CLSCTX_INPROC, IID_IShellTaskScheduler, (void**)&(pdsv->_pScheduler));
|
|
if (FAILED(hr)) {
|
|
return FALSE;
|
|
}
|
|
|
|
// set a 60 second timeout
|
|
pdsv->_pScheduler->Status(ITSSFLAG_KILL_ON_DESTROY, DEFVIEW_THREAD_IDLE_TIMEOUT);
|
|
}
|
|
|
|
|
|
hr = pdsv->_pScheduler->AddTask(pTask, rTID, lParam, dwPriority);
|
|
|
|
return SUCCEEDED(hr);
|
|
}
|
|
|
|
|
|
// Get the number of running tasks of the indicated task ID.
|
|
UINT CDefView::_GetBackgroundTaskCount(REFTASKOWNERID rtid)
|
|
{
|
|
if (NULL == _pScheduler)
|
|
return 0;
|
|
|
|
return _pScheduler->CountTasks(rtid);
|
|
}
|
|
|
|
|
|
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 },
|
|
};
|
|
|
|
|
|
// 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 }, 0L, -1 },
|
|
{ VIEW_SMALLICONS | IN_VIEW_BMP, SFVIDM_VIEW_SMALLICON, TBSTATE_ENABLED, BTNS_BUTTON, { 0, 0 }, 0L, -1 },
|
|
{ VIEW_LIST | IN_VIEW_BMP, SFVIDM_VIEW_LIST, TBSTATE_ENABLED, BTNS_BUTTON, { 0, 0 }, 0L, -1 },
|
|
{ VIEW_DETAILS | IN_VIEW_BMP, SFVIDM_VIEW_DETAILS, TBSTATE_ENABLED, BTNS_BUTTON, { 0, 0 }, 0L, -1 },
|
|
};
|
|
|
|
|
|
LRESULT CDefView::_TBNotify(NMHDR* pnm)
|
|
{
|
|
LPTBNOTIFY ptbn = (LPTBNOTIFY)pnm;
|
|
|
|
switch (pnm->code) {
|
|
case TBN_BEGINDRAG:
|
|
DefView_OnMenuSelect(this, 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_IExplorerToolbar, (void**)&piet))) {
|
|
BOOL fGotClsid = TRUE;
|
|
|
|
DWORD dwFlags = VBF_TOOLS | VBF_ADDRESS | VBF_BRAND;
|
|
|
|
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_DefViewFrame2;
|
|
} 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)) {
|
|
if (m_cFrame.IsSFVExtension())
|
|
_EnableDisableTBButtons();
|
|
fRet = TRUE;
|
|
}
|
|
}
|
|
}
|
|
piet->Release();
|
|
}
|
|
|
|
return fRet;
|
|
}
|
|
|
|
|
|
int _FirstHiddenButton(TBBUTTON* ptbn, int cButtons)
|
|
{
|
|
int i;
|
|
|
|
for (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));
|
|
|
|
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 {
|
|
memcpy(tbDefView, c_tbDefView, SIZEOF(TBBUTTON) * ARRAYSIZE(c_tbDefView));
|
|
cDefViewBtns = ARRAYSIZE(c_tbDefView);
|
|
}
|
|
|
|
int cVisibleDefViewBtns = _FirstHiddenButton(tbDefView, cDefViewBtns); // count of visible defview buttons
|
|
|
|
LPTBBUTTON pbtn = (LPTBBUTTON)LocalAlloc(LPTR, (cDefViewBtns + tbinfo.cbuttons) * SIZEOF(TBBUTTON));
|
|
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++) {
|
|
_CopyDefViewButton(&pbtn[i + tbinfo.cbuttons], &tbDefView[i]);// Hidden defview button block gets added after visible & client buttons
|
|
ASSERT(pbtn[i + tbinfo.cbuttons].fsState & TBSTATE_HIDDEN);// If this rips a visible button got mixed in with the hidden block
|
|
pbtn[i + tbinfo.cbuttons].fsState &= ~TBSTATE_HIDDEN;// Rip off the hidden bit
|
|
}
|
|
}
|
|
|
|
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.
|
|
|
|
// BUGBUG: this update cache thing is old and bogus I think... talk to the net guys
|
|
TCHAR szPathAny[MAX_PATH];
|
|
|
|
this->UpdateSelectionMode();
|
|
|
|
// finish any pending edits
|
|
SendMessage(_hwndListview, LVM_EDITLABEL, (WPARAM)-1, 0);
|
|
|
|
GetWindowsDirectory(szPathAny, ARRAYSIZE(szPathAny));
|
|
IsShared(szPathAny, TRUE);
|
|
|
|
// First we have to save all the icon positions, so they will be restored
|
|
// properly during the FillObjects
|
|
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.
|
|
if (_pSaveHeader != NULL) {
|
|
LocalFree(_pSaveHeader);
|
|
_pSaveHeader = NULL;
|
|
}
|
|
_uSaveHeaderLen = _GetSaveHeader(&_pSaveHeader);
|
|
|
|
// 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)
|
|
Icon_FSEvent(SHCNE_UPDATEITEM, _GetPIDL(iItem), NULL);
|
|
|
|
// We should use the existing sort parameters instead of getting the
|
|
// defaults. So, the following line is commented out!
|
|
// _GetSortDefaults(&_dvState);
|
|
return FillObjectsShowHide(TRUE, NULL, 0, TRUE);
|
|
}
|
|
|
|
|
|
HRESULT CDefView::ReloadContent(BOOL fForce)
|
|
{
|
|
HRESULT hresExtView = NOERROR;
|
|
HRESULT hresNormalView = NOERROR;
|
|
HRESULT hresSFVExt = NOERROR;
|
|
SHELLSTATE ss;
|
|
BOOL fHasDVOC = _fGetWindowLV;
|
|
|
|
// Tell the defview client that this window is about to be refreshed
|
|
CallCB(SFVM_REFRESH, TRUE, 0);
|
|
|
|
// The call to GetExtViews below reorders the m_lViews list; m_uView & m_uActiveExtendedView are indices
|
|
// into this list. When this list is reordered m_uView & m_uActiveExtendededView point to the wrong
|
|
// views. The problem came forward when we were in thumbnail view, then customized to a web view...
|
|
// REVIEW: Should this code, which holds on the GUIDs for m_uView & m_uActiveExtendedView be put
|
|
// into GetExtViews?
|
|
GUID idViewGUID = GUID_NULL;
|
|
GUID idActiveExtViewGUID = GUID_NULL;
|
|
if (m_cFrame.IsSFVExtension()) // IShellView extension
|
|
{
|
|
int iView = m_cFrame.m_lViews.NthUnique(m_cFrame.m_uView + 1);
|
|
if (iView != -1) {
|
|
SFVVIEWSDATA* pItem = m_cFrame.m_lViews.GetPtr(iView);
|
|
if (pItem) {
|
|
idViewGUID = pItem->idExtShellView;
|
|
}
|
|
}
|
|
iView = m_cFrame.m_lViews.NthUnique(m_cFrame.m_uActiveExtendedView + 1);
|
|
if (iView != -1) {
|
|
SFVVIEWSDATA* pItem = m_cFrame.m_lViews.GetPtr(iView);
|
|
if (pItem) {
|
|
idActiveExtViewGUID = pItem->idExtShellView;
|
|
}
|
|
}
|
|
}
|
|
|
|
//Reload the Templates even if we are not in extended view.
|
|
m_cFrame.GetExtViews(TRUE); //Reload the template names from registry again.
|
|
|
|
// Part 2 to the code above; after saving the guids for the views held in m_uView & m_uActiveExtendedView
|
|
// we reset the indices to the correct position in the m_lViews list.
|
|
if (IsEqualGUID(idViewGUID, GUID_NULL) == FALSE) {
|
|
int iView;
|
|
for (int i = 0; (iView = m_cFrame.m_lViews.NthUnique(i + 1)) != -1; i++) {
|
|
SFVVIEWSDATA* pItem2 = m_cFrame.m_lViews.GetPtr(iView);
|
|
if (pItem2 && IsEqualGUID(idViewGUID, pItem2->idExtShellView)) {
|
|
m_cFrame.m_uView = i;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (IsEqualGUID(idActiveExtViewGUID, GUID_NULL) == FALSE) {
|
|
int iView;
|
|
for (int i = 0; (iView = m_cFrame.m_lViews.NthUnique(i + 1)) != -1; i++) {
|
|
SFVVIEWSDATA* pItem2 = m_cFrame.m_lViews.GetPtr(iView);
|
|
if (pItem2 && IsEqualGUID(idActiveExtViewGUID, pItem2->idExtShellView)) {
|
|
m_cFrame.m_uActiveExtendedView = i;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// 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))) {
|
|
DebugMsg(TF_DEFVIEW, TEXT("Global state SSF_WIN95CLASSIC changed to [%d] for DefView 0x%x"), _fClassic ? 1 : 0, this);
|
|
_UpdateListviewColors(ss.fWin95Classic);
|
|
}
|
|
|
|
if (m_cFrame.IsSFVExtension()) // IShellView extension
|
|
{
|
|
hresSFVExt = m_cFrame.GetExtendedISV()->Refresh();
|
|
}
|
|
|
|
// BUGBUG PERF: when switching to normal view, we don't need to do this case!
|
|
// We need to special case Desktop here because _CanShowWebView() does not work for desktop.
|
|
if (_IsDesktop() ? m_cFrame.IsWebView() : _CanShowWebView()) // DocObject extension
|
|
{
|
|
// We need to save the icon positions before we hide the view.
|
|
SaveViewState();
|
|
|
|
hresExtView = _SwitchToViewPVID(&VID_WebView, TRUE); //Show ext view again.
|
|
|
|
// if our listview isn't going to be shown in this view, mark it for refresh
|
|
if (!_HasNormalView() && !_bUpdatePendingPending) {
|
|
_bUpdatePending = TRUE;
|
|
}
|
|
} else {
|
|
// Note: _UpdateListviewColors() function sets _fClassic to whatever is passed in as the parameter.
|
|
// Our intention here is NOT to change _fClassic; So, we pass that itself in.
|
|
_UpdateListviewColors(_fClassic);
|
|
}
|
|
|
|
// fHasDVOC is a Cache of whether we had a DVOC when we entered this routine. Because of the
|
|
// view switch above, _fGetWindowLV may now be false because MSHTML has not had enough time to
|
|
// recreate the DVOC, so we check the cached copy as well.
|
|
if (_HasNormalView() || fHasDVOC) // normal view
|
|
{
|
|
//We want to preserve the earlier error if any;
|
|
hresNormalView = _ReloadListviewContent();
|
|
}
|
|
|
|
return FAILED(hresSFVExt) ? hresSFVExt : (FAILED(hresExtView) ? hresExtView : hresNormalView);
|
|
}
|
|
|
|
|
|
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 we are on a drive we invalidate the
|
|
// drive...
|
|
if (_pidlMonitor) {
|
|
TCHAR szPath[MAX_PATH];
|
|
if (SHGetPathFromIDList(_pidlMonitor, szPath)) {
|
|
int iDrive = PathGetDriveNumber(szPath);
|
|
InvalidateDriveType(iDrive);
|
|
}
|
|
}
|
|
|
|
if (_IsDesktop()) {
|
|
SHELLSTATE ss;
|
|
SHGetSetSettings(&ss, SSF_HIDEICONS | SSF_DESKTOPHTML, FALSE);
|
|
|
|
if (ss.fHideIcons)
|
|
_fs.fFlags |= FWF_NOICONS;
|
|
else
|
|
_fs.fFlags &= ~FWF_NOICONS;
|
|
|
|
_SwitchDesktopHTML(ss.fDesktopHTML, TRUE);
|
|
|
|
if (ss.fDesktopHTML) {
|
|
HMODULE hmod;
|
|
|
|
// 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);
|
|
|
|
// In IE40 integrated shell mode, the Java VM only reloads classes
|
|
// on a hard refresh. Therefore we need to kick it once in a while.
|
|
// Currently this is expense because they throw away all their jitted
|
|
// classes. Future versions of the VM should check with wininet for
|
|
// changes before throwing classes away. That will make this call
|
|
// much less expensive.
|
|
hmod = GetModuleHandle(TEXT("msjava.dll"));
|
|
if (hmod) {
|
|
typedef HRESULT(*PFNNOTIFYBROWSERSHUTDOWN)(void*);
|
|
FARPROC fp = GetProcAddress(hmod, "NotifyBrowserShutdown");
|
|
if (fp) {
|
|
HRESULT hr = ((PFNNOTIFYBROWSERSHUTDOWN)fp)(NULL);
|
|
ASSERT(SUCCEEDED(hr));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return ReloadContent(TRUE);
|
|
}
|
|
|
|
|
|
STDMETHODIMP CDefView::CreateViewWindow(IShellView* lpPrevView, LPCFOLDERSETTINGS lpfs, IShellBrowser* psb, RECT* prc, HWND* phWnd)
|
|
{
|
|
SV2CVW2_PARAMS cParams;
|
|
|
|
cParams.cbSize = SIZEOF(SV2CVW2_PARAMS);
|
|
cParams.psvPrev = lpPrevView;
|
|
cParams.pfs = lpfs;
|
|
cParams.psbOwner = psb;
|
|
cParams.prcView = prc;
|
|
cParams.pvid = NULL;
|
|
|
|
HRESULT hres = CreateViewWindow2(&cParams);
|
|
|
|
*phWnd = cParams.hwndView;
|
|
|
|
if (SUCCEEDED(hres) && (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.
|
|
hres = S_OK;
|
|
}
|
|
|
|
return hres;
|
|
}
|
|
|
|
|
|
SHELLVIEWID const* SVIDFromViewMode(UINT ViewMode)
|
|
{
|
|
switch (ViewMode) {
|
|
case FVM_SMALLICON:
|
|
return &VID_SmallIcons;
|
|
case FVM_LIST:
|
|
return &VID_List;
|
|
case FVM_DETAILS:
|
|
return &VID_Details;
|
|
case FVM_ICON:
|
|
default:
|
|
return &VID_LargeIcons;
|
|
}
|
|
}
|
|
|
|
|
|
void ViewModeFromSVID(SHELLVIEWID const* pvid, UINT* pViewMode)
|
|
{
|
|
if (IsEqualIID(*pvid, VID_LargeIcons))
|
|
*pViewMode = FVM_ICON;
|
|
|
|
else if (IsEqualIID(*pvid, VID_SmallIcons))
|
|
*pViewMode = FVM_SMALLICON;
|
|
|
|
else if (IsEqualIID(*pvid, VID_List))
|
|
*pViewMode = FVM_LIST;
|
|
|
|
if (IsEqualIID(*pvid, VID_Details))
|
|
*pViewMode = FVM_DETAILS;
|
|
}
|
|
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
if (m_cFrame.IsSFVExtension()) {
|
|
hr = m_cFrame.GetExtendedISV()->HandleRename(pidl);
|
|
} else if (_HasNormalView()) {
|
|
hr = SelectAndPositionItem(pidl, SVSI_DESELECTOTHERS, 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 (m_cFrame.IsWebView() && m_cFrame.m_pvoActive) {
|
|
return m_cFrame.m_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);
|
|
}
|
|
|
|
|
|
STDMETHODIMP CDefView::GetView(SHELLVIEWID* pvid, ULONG uView)
|
|
{
|
|
HRESULT hres = m_cFrame.GetView(pvid, uView);
|
|
if (NOERROR == hres) {
|
|
return hres;
|
|
}
|
|
|
|
switch (uView) {
|
|
case SV2GV_CURRENTVIEW:
|
|
*pvid = *SVIDFromViewMode(_fs.ViewMode);
|
|
break;
|
|
case SV2GV_DEFAULTVIEW:
|
|
*pvid = VID_LargeIcons;
|
|
break;
|
|
default:
|
|
return hres;
|
|
}
|
|
|
|
return NOERROR;
|
|
}
|
|
|
|
|
|
// 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 cbSize;
|
|
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;
|
|
}
|
|
}
|
|
|
|
|
|
STDMETHODIMP CDefView::CreateViewWindow2(LPSV2CVW2_PARAMS lpParams)
|
|
{
|
|
if (g_dwProfileCAP & 0x00000001)
|
|
StopCAP();
|
|
|
|
if (lpParams->cbSize < SIZEOF(SV2CVW2_PARAMS))
|
|
return E_INVALIDARG;
|
|
|
|
IShellView* lpPrevView = lpParams->psvPrev;
|
|
LPCFOLDERSETTINGS lpfs = lpParams->pfs;
|
|
IShellBrowser* psb = lpParams->psbOwner;
|
|
RECT* prc = lpParams->prcView;
|
|
HWND* phWnd = &lpParams->hwndView;
|
|
SHELLVIEWID const* pvid = lpParams->pvid;
|
|
SHELLSTATE ss;
|
|
|
|
if (pvid && IsEqualIID(*pvid, VID_WebView))
|
|
pvid = NULL;
|
|
|
|
HRESULT hres;
|
|
UINT uLen;
|
|
|
|
*phWnd = NULL;
|
|
|
|
// 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(this->m_cCallback.GetSFVCB(), SAFECAST(this, IShellFolderView*));
|
|
|
|
if (_hwndView || !DefView_RegisterWindow() || !psb)
|
|
return E_UNEXPECTED;
|
|
|
|
ASSERT(_pshf);
|
|
_pshf->QueryInterface(IID_IShellIcon, (void**)&_psi);
|
|
_pshf->QueryInterface(IID_IShellIconOverlay, (void**)&_psio);
|
|
|
|
// We need to make sure to store this before doing the GetWindowRect
|
|
_psb = psb;
|
|
psb->AddRef();
|
|
|
|
psb->QueryInterface(IID_ICommDlgBrowser, (void**)&_pcdb);
|
|
|
|
_fs = *lpfs;
|
|
|
|
BOOL fIsOwnerData = FALSE;
|
|
CallCB(SFVM_ISOWNERDATA, 0, (LONG_PTR)&fIsOwnerData);
|
|
if (fIsOwnerData)
|
|
_fs.fFlags |= FWF_OWNERDATA;
|
|
else
|
|
_fs.fFlags &= ~FWF_OWNERDATA;
|
|
|
|
// This should never fail
|
|
_psb->GetWindow(&_hwndMain);
|
|
ASSERT(IsWindow(_hwndMain));
|
|
CallCB(SFVM_HWNDMAIN, 0, (LPARAM)_hwndMain);
|
|
|
|
// We need to restore the column widths before showing the window
|
|
ASSERT(!_pSaveHeader);
|
|
uLen = _GetSaveHeader(&_pSaveHeader);
|
|
_uSaveHeaderLen = uLen;
|
|
|
|
// Grab other default values we need to set before showing the window
|
|
//PDVSAVEHEADEREX pSaveHeaderEx = _GetSaveHeaderEx(_pSaveHeader);
|
|
if (_pSaveHeader) {
|
|
// set the default sort order incase we are creating an extension view.
|
|
_dvState = _pSaveHeader->dvState;
|
|
}
|
|
|
|
// if there was a previous view that we know about, poke around a bit
|
|
if (_fWin95ViewState) {
|
|
CDefView* pdsvPrev;
|
|
if (lpPrevView && SUCCEEDED(lpPrevView->QueryInterface(IID_CDefView, (void**)&pdsvPrev))) {
|
|
// preserve stuff like sort order
|
|
_dvState = pdsvPrev->_dvState;
|
|
if (_pSaveHeader)
|
|
_pSaveHeader->dvState = pdsvPrev->_dvState;
|
|
|
|
ATOMICRELEASE(pdsvPrev);
|
|
}
|
|
}
|
|
|
|
// See if we should map a VID to the right view mode
|
|
if (pvid)
|
|
ViewModeFromSVID(pvid, &_fs.ViewMode);
|
|
|
|
if (!CreateWindowEx(IS_WINDOW_RTL_MIRRORED(_hwndMain) ? dwExStyleRTLMirrorWnd : 0L, c_szDefViewClass, szNULL, WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_TABSTOP,
|
|
prc->left, prc->top, prc->right - prc->left, prc->bottom - prc->top, _hwndMain, NULL, HINST_THISDLL, this)) {
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
// If it's the desktop folder, force it to reload the ext views
|
|
// - when the desktop is created, GetExtViews() would have
|
|
// been called from shdocvw, but it's actual implementation wouldn't
|
|
// have had enough info to figure out if it was the actual desktop
|
|
// or a desktop folder. So we force it to reload it now.
|
|
m_cFrame.GetExtViews(_IsViewDesktop());
|
|
|
|
SHGetSetSettings(&ss, SSF_WIN95CLASSIC | SSF_DESKTOPHTML | SSF_WEBVIEW, FALSE);
|
|
if (_IsDesktop()) //Is this the Desktop?
|
|
{
|
|
// Does the user want desktop in HyperText view?
|
|
if (ss.fDesktopHTML) {
|
|
pvid = &VID_WebView;
|
|
}
|
|
|
|
// Done immediately below, no need to do it here.
|
|
//this->UpdateSelectionMode();
|
|
}
|
|
|
|
this->UpdateSelectionMode();
|
|
|
|
// Also see if the view would like to handle their own view state...
|
|
|
|
// See if they want to overwrite the selection object
|
|
if (fIsOwnerData) {
|
|
// 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...
|
|
}
|
|
}
|
|
|
|
// Switch into Web View (if it's on).
|
|
// But don't do this if the app requires old-style defview.
|
|
|
|
hres = S_OK; // assume synchronous (ex: Large Icon view)
|
|
if (_CanShowWebView()) {
|
|
// This returns S_FALSE if we're waiting for the view
|
|
// to go ReadyStateInteractive, S_OK otherwise.
|
|
hres = _SwitchToViewPVID(&VID_WebView, FALSE);
|
|
if (FAILED(hres)) {
|
|
TraceMsg(TF_WARNING, "DefView failed extended view switch during creation");
|
|
hres = S_OK;
|
|
}
|
|
}
|
|
|
|
// The browser will ask SHDVID_CANACTIVATENOW at some point,
|
|
// and we have to remember how to answer. Once we answer S_OK
|
|
// to that, our view becomes active.
|
|
_fCanActivateNow = (S_OK == hres); // S_FALSE implies async waiting for ReadyStateInteractive
|
|
|
|
if (pvid) {
|
|
// We manually map VIDs for the 4 normal views to normal
|
|
// views -- don't waste time trying a CoCreateInstance
|
|
|
|
if (!(IsEqualIID(*pvid, VID_LargeIcons) || IsEqualIID(*pvid, VID_SmallIcons) || IsEqualIID(*pvid, VID_List) || IsEqualIID(*pvid, VID_Details))) {
|
|
hres = _SwitchToViewPVID(pvid, FALSE);
|
|
|
|
// Oops, the extended view is no longer there,
|
|
// we already went back to large icon, so assume success here.
|
|
if (FAILED(hres)) {
|
|
TraceMsg(TF_WARNING, "DefView failed extended view switch during creation");
|
|
hres = S_OK;
|
|
}
|
|
|
|
// WebView is handled specifically above for non-desktop,
|
|
// so all pvid's coming through here had better be
|
|
// synchronous ISV extended views. Which means S_OK was
|
|
// the return result.
|
|
ASSERT(_IsDesktop() || (S_OK == hres));
|
|
|
|
// _IsDesktop() cannot go async because CDesktopBrowser removes
|
|
// the SBSC_RELATIVE bit and forces SBSC_NEWWINDOW, which breaks
|
|
// CBaseBrowser's Async Activation notify method that we use
|
|
// when the readystate goes interactive... Force the desktop
|
|
// to go synchronous.
|
|
if (_IsDesktop()) {
|
|
hres = S_OK;
|
|
}
|
|
}
|
|
}
|
|
|
|
// turn on proper background and colors
|
|
// BUGBUG: Since we haven't switchted to the view yet, these colors
|
|
// may come out wrong. We should test this...
|
|
_UpdateListviewColors(ss.fWin95Classic);
|
|
|
|
// NB - Nasty side effect - this needs to be done
|
|
// before calling _BestFit (in DV_FillObjects) so that the parent can handle size changes effectively.
|
|
*phWnd = _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 cabinet window for a long time.
|
|
|
|
// The below if looks redundant, ASSERT that it is for now -- remove redundancy later
|
|
ASSERT(((S_OK == hres) && _fCanActivateNow) || !((S_OK == hres) && _fCanActivateNow));
|
|
if ((S_OK == hres) && _fCanActivateNow) {
|
|
// Show the window early (what old code did)
|
|
SetWindowPos(_hwndView, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
|
|
UpdateWindow(_hwndView);
|
|
_OnMoveWindowToTop(_hwndView);
|
|
|
|
TraceMsg(TF_DEFVIEW, "Creating sync DefView");
|
|
_fIsAsyncDefView = FALSE;
|
|
} else {
|
|
TraceMsg(TF_DEFVIEW, "Creating ASYNC DefView");
|
|
_fIsAsyncDefView = TRUE;
|
|
}
|
|
|
|
// Always start filling the listview with objects now. Even if we
|
|
// have an extended view, it's pretty likely that it will use
|
|
// the defviewoc to get at our listview.
|
|
// BUGBUG PERF NOTE: we could improve perf for such non-listview extended views by checking the bits.
|
|
_bUpdatePending = TRUE;
|
|
{
|
|
HRESULT hresT = FillObjectsShowHide(TRUE, _pSaveHeader, uLen, TRUE);
|
|
if (FAILED(hresT)) {
|
|
// The fill objects failed for some reason, we should return an error.
|
|
this->DestroyViewWindow();
|
|
ASSERT(_hwndView == NULL);
|
|
*phWnd = NULL;
|
|
|
|
// Note that we don't need to clean _psb or _pcdb because this object will be deleted anyway.
|
|
return hresT;
|
|
}
|
|
_PostEnumDoneMessage();
|
|
}
|
|
|
|
// this needs to be done after the enumeration
|
|
if (_pidlMonitor || _lFSEvents) // check both so that views can register for everything
|
|
{
|
|
int fSources = (_lFSEvents & SHCNE_DISKEVENTS) ? SHCNRF_ShellLevel | SHCNRF_InterruptLevel : SHCNRF_ShellLevel;
|
|
UINT uFSEvents = _lFSEvents | SHCNE_UPDATEIMAGE | SHCNE_UPDATEDIR;
|
|
SHChangeNotifyEntry fsne;
|
|
|
|
if (FAILED(CallCB(SFVM_QUERYFSNOTIFY, 0, (LPARAM)&fsne))) {
|
|
// Reset entry
|
|
fsne.pidl = _pidlMonitor;
|
|
fsne.fRecursive = FALSE;
|
|
}
|
|
|
|
_uRegister = SHChangeNotifyRegister(_hwndView, SHCNRF_NewDelivery | fSources, uFSEvents, WM_DSV_FSNOTIFY, 1, &fsne);
|
|
}
|
|
|
|
// We do the toolbar before the menu bar to avoid flash
|
|
if (!_IsDesktop())
|
|
MergeToolBar(TRUE);
|
|
|
|
// BUGBUG: commdlg should support drag drop too!
|
|
ASSERT(_pdtgtBack == NULL);
|
|
|
|
// this may fail
|
|
_pshf->CreateViewObject(_hwndMain, IID_IDropTarget, (void**)&_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(hres))
|
|
|
|
ViewWindow_BestFit(this, FALSE);
|
|
|
|
// Tell the defview client that this windows has been initialized
|
|
CallCB(SFVM_WINDOWCREATED, (WPARAM)_hwndView, 0);
|
|
if (SUCCEEDED(CallCB(SFVM_QUERYCOPYHOOK, 0, 0)))
|
|
AddCopyHook();
|
|
|
|
// BUGBUG:: See if there is a cleaner place to do this.
|
|
if (SUCCEEDED(_GetIPersistHistoryObject(NULL))) {
|
|
IBrowserService* pbs;
|
|
if (SUCCEEDED(_psb->QueryInterface(IID_IBrowserService, (void**)&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_IPersistHistory, (void**)&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();
|
|
}
|
|
}
|
|
|
|
return hres;
|
|
}
|
|
|
|
|
|
STDMETHODIMP CDefView::DestroyViewWindow()
|
|
{
|
|
int i, iHeaderCount;
|
|
HWND hwndHeader;
|
|
|
|
// 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().
|
|
|
|
hwndHeader = ListView_GetHeader(_hwndListview);
|
|
iHeaderCount = Header_GetItemCount(hwndHeader);
|
|
for (i = 0; i < iHeaderCount; ++i) {
|
|
HDITEM hdi;
|
|
|
|
hdi.mask = HDI_BITMAP;
|
|
Header_GetItem(hwndHeader, i, &hdi);
|
|
if (hdi.hbm != NULL)
|
|
TBOOL(DeleteObject(hdi.hbm));
|
|
}
|
|
|
|
m_cFrame.ShowExtView(CSFVFrame::NOEXTVIEW, TRUE);
|
|
|
|
// Just in case...
|
|
OnDeactivate();
|
|
|
|
if (_hwndView) {
|
|
HWND hwndTemp = _hwndView;
|
|
|
|
RemoveCopyHook();// This is a bit lazy implementation, but minimum code.
|
|
_hwndView = NULL;// Put NULL in _hwndView indicating that we are destroying.
|
|
CallCB(SFVM_WINDOWDESTROY, (WPARAM)hwndTemp, 0);// Tell the defview client that this window will be destroyed
|
|
DestroyWindow(hwndTemp);
|
|
}
|
|
|
|
IUnknown_SetSite(this->m_cCallback.GetSFVCB(), NULL);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
void CDefView::MergeViewMenu(HMENU hmenu, HMENU hmenuMerge)
|
|
{
|
|
HMENU hmenuView = _GetMenuFromID(hmenu, FCIDM_MENU_VIEW);
|
|
if (hmenuView) {
|
|
// 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);
|
|
m_cFrame.MergeExtViewsMenu(hmenuView, this); // add extended views to top of menu
|
|
}
|
|
}
|
|
|
|
|
|
void CDefView::_SetUpMenus(UINT uState)
|
|
{
|
|
// If this is desktop, don't bother creating menu
|
|
if (!_IsDesktop()) {
|
|
HMENU hMenu;
|
|
|
|
OnDeactivate();
|
|
|
|
ASSERT(_hmenuCur == NULL);
|
|
|
|
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));
|
|
// remove Choose Columns from the view menu if not in Details view
|
|
if (!_IsReportView())
|
|
DeleteMenu(hMenu, SFVIDM_VIEW_COLSETTINGS, MF_BYCOMMAND);
|
|
|
|
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));
|
|
// remove Choose Columns from the view menu if not in Details view
|
|
if (!_IsReportView())
|
|
DeleteMenu(hMenu, SFVIDM_VIEW_COLSETTINGS, MF_BYCOMMAND);
|
|
|
|
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;
|
|
}
|
|
|
|
|
|
void CDefView::SwapWindow(void)
|
|
{
|
|
if (EVAL(_pocWinMan)) {
|
|
_fGetWindowLV = FALSE;
|
|
HWND hwndXV = m_cFrame.GetExtendedViewWindow();
|
|
if (hwndXV) {
|
|
if ((SUCCEEDED(_pocWinMan->SwapWindow(hwndXV, &_pocWinMan))))
|
|
_fGetWindowLV = TRUE;
|
|
ASSERT(m_cFrame.GetExtendedISFV());
|
|
ASSERT(m_pauto);
|
|
m_cFrame.GetExtendedISFV()->SetAutomationObject(m_pauto);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
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;
|
|
DV_FlushCachedMenu(this);
|
|
}
|
|
|
|
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) {
|
|
//Show Defview only if it is Async - Bug 275266
|
|
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 && m_cFrame.IsWebView() && m_cFrame.m_pOleObj) {
|
|
m_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()) {
|
|
BOOL bOldHasFocus = _bExtHasFocus;
|
|
|
|
m_cFrame._SwitchToNewOleObj();
|
|
// thumbnailview calls defview->UIActivate when
|
|
// we try to set focus to it. since we call defview_setfocus here
|
|
// and it calls setfocus(thumbnailview) we get infinite loop
|
|
// however we still need to call setfocus from here because this
|
|
// method gets called when user is tabbing around so if we don't set
|
|
// focus thumbnailview will be inaccessible.
|
|
// we track SVUIA_ACTIVATE_FOCUS state of an extension with
|
|
// bExtHasFocus, and only when it does not have focus we call
|
|
// DefView_SetFocus
|
|
if (m_cFrame.IsSFVExtension()) {
|
|
if (m_cFrame.m_hActiveSVExtHwnd && !m_cFrame.IsWebView()) // Just to be safe
|
|
{
|
|
// Show the SFVExt window
|
|
SetWindowPos(m_cFrame.m_hActiveSVExtHwnd, NULL, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW | SWP_NOZORDER);
|
|
}
|
|
_bExtHasFocus = (uState == SVUIA_ACTIVATE_FOCUS);
|
|
}
|
|
|
|
// 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 && (!m_cFrame.IsSFVExtension() || !bOldHasFocus)) {
|
|
DefView_SetFocus(this);
|
|
// DefView_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)
|
|
DefView_SetFocus(this);
|
|
}
|
|
|
|
// OnActivate must follow DefView_SetFocus
|
|
OnActivate(uState);
|
|
|
|
if (_fShowListviewIconsOnActivate) {
|
|
_ShowListviewIcons();
|
|
_fShowListviewIconsOnActivate = FALSE;
|
|
}
|
|
|
|
ASSERT(_IsDesktop() || _hmenuCur);
|
|
|
|
m_cFrame._UpdateZonesStatusPane(NULL);
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
STDMETHODIMP CDefView::GetCurrentInfo(LPFOLDERSETTINGS lpfs)
|
|
{
|
|
*lpfs = _fs;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
BOOL IsBackSpace(LPMSG 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 (_HasNormalView()) {
|
|
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) && !m_cFrame.IsSFVExtension() && !m_cFrame.IsWebView() && (GetFocus() != _hwndListview)) {
|
|
DefView_SetFocus(this);
|
|
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.25th (CDTURNER)
|
|
// to avoid passing it to the frame before SFV extension views...
|
|
// SFVExtensions behave very much like the listview of defview, so they
|
|
// must be given a chance to do this before we EVEN consider passing
|
|
// it to frame, otherwise we are deep in it...
|
|
if (m_cFrame.IsSFVExtension()) {
|
|
// if focus is in the view extension have a go at the frame accelerators
|
|
// first so that the menu keys work....
|
|
if (SHIsChildOrSelf(GetFocus(), m_cFrame.GetExtendedViewWindow()) == S_OK) {
|
|
if (::TranslateAccelerator(_hwndView, _hAccel, pmsg)) {
|
|
return S_OK;
|
|
}
|
|
}
|
|
if (this->m_cFrame.m_pActiveSVExt
|
|
&& this->m_cFrame.m_pActiveSVExt->TranslateAccelerator(pmsg) == S_OK) {
|
|
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 (m_pActive) in
|
|
// m_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 (!DV_CDB_IsCommonDialog(this) && !IsVK_TABCycler(pmsg) && !IsBackSpace(pmsg))
|
|
if (S_OK == _psb->TranslateAcceleratorSB(pmsg, 0))
|
|
return S_OK;
|
|
|
|
BOOL bTabOffLastTridentStop = FALSE;
|
|
BOOL bHadIOFocus = (m_cFrame._HasFocusIO() == S_OK); // Cache this here before the m_cFrame.OnTA() call below
|
|
// 2nd, try WebView if it's active
|
|
// note, SFVextensions are handled above....
|
|
if (m_cFrame.IsWebView()) {
|
|
// Let the extension try it
|
|
if (this->m_cFrame.OnTranslateAccelerator(pmsg, &bTabOffLastTridentStop) == S_OK) {
|
|
return S_OK;
|
|
}
|
|
}
|
|
|
|
// We've given m_pActive->TranslateAccelerator() the first shot in
|
|
// m_cFrame.OnTranslateAccelerator, but it failed. Let's try the shell browser.
|
|
if (IsBackSpace(pmsg))
|
|
if (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) && _HasNormalView() && m_cFrame.IsWebView()) {
|
|
HRESULT hr;
|
|
BOOL fBack = (GetAsyncKeyState(VK_SHIFT) < 0);
|
|
|
|
TraceMsg(TF_FOCUS, "cdv.ta(lpMsg=TAB)");
|
|
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 m_cFrame.OnTranslateAccelerator(pmsg, &bTabOffLastTridentStop);
|
|
} else if (m_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.
|
|
TraceMsg(TF_FOCUS, "cdv.ta: deact/uiaio ExtView");
|
|
hr = m_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()) {
|
|
TraceMsg(TF_FOCUS, "cdv.ta: act/sf ListView ret S_OK");
|
|
SetFocus(_hwndListview);
|
|
return S_OK;
|
|
}
|
|
|
|
TraceMsg(TF_FOCUS, "cdv.ta: ret S_FALSE");
|
|
return S_FALSE;
|
|
} else {
|
|
TraceMsg(TF_FOCUS, "cdv.ta: ExtView !act");
|
|
if (!fBack) {
|
|
TraceMsg(TF_FOCUS, "cdv.ta: act/uiaio ListView ret S_OK(?)");
|
|
hr = m_cFrame._UIActivateIO(TRUE, pmsg);
|
|
ASSERT(hr == S_OK || hr == S_FALSE);
|
|
return hr;
|
|
}
|
|
}
|
|
}
|
|
|
|
return S_FALSE;
|
|
}
|
|
|
|
|
|
const UINT c_aiNonCustomizableFolders[] = {
|
|
CSIDL_WINDOWS,
|
|
CSIDL_SYSTEM,
|
|
CSIDL_SYSTEMX86,
|
|
CSIDL_PROGRAM_FILES,
|
|
CSIDL_PROGRAM_FILESX86,
|
|
-1
|
|
};
|
|
|
|
|
|
// pass an array of CSIDL values (-1 terminated)
|
|
BOOL PathIsOneOf(const UINT rgFolders[], LPCTSTR pszFolder)
|
|
{
|
|
for (int i = 0; rgFolders[i] != -1; i++) {
|
|
TCHAR szParent[MAX_PATH];
|
|
SHGetFolderPath(NULL, rgFolders[i] | CSIDL_FLAG_DONT_VERIFY, NULL, SHGFP_TYPE_CURRENT, szParent);
|
|
|
|
// the trailing slashes are assumed to match
|
|
if (lstrcmpi(szParent, pszFolder) == 0)
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
void CDefView::InitViewMenu(HMENU hmInit)
|
|
{
|
|
UINT uiFlags = 0;
|
|
int iCurViewMenuItem;
|
|
|
|
iCurViewMenuItem = CheckCurrentViewMenuItem(hmInit);
|
|
|
|
UINT uAAEnable = ((iCurViewMenuItem == SFVIDM_VIEW_LIST) || (iCurViewMenuItem == SFVIDM_VIEW_DETAILS) || (!_HasNormalView())) ? (MF_GRAYED | MF_BYCOMMAND) : (MF_ENABLED | MF_BYCOMMAND);
|
|
UINT uAGEnable = uAAEnable;
|
|
|
|
if (m_cFrame.IsSFVExtension()) {
|
|
// ask the view extension...
|
|
UINT dwSupport = SFVQS_AUTO_ARRANGE | SFVQS_ARRANGE_GRID;
|
|
|
|
HRESULT hr = m_cFrame.GetExtendedISFV()->QuerySupport(&dwSupport);
|
|
if (hr == S_OK) {
|
|
if (dwSupport & SFVQS_AUTO_ARRANGE)
|
|
uAAEnable = (MF_ENABLED | MF_BYCOMMAND);
|
|
if (dwSupport & SFVQS_ARRANGE_GRID)
|
|
uAGEnable = (MF_ENABLED | MF_BYCOMMAND);
|
|
}
|
|
}
|
|
|
|
if (!DV_SHOWICONS(this))
|
|
uAAEnable = (MF_GRAYED | MF_BYCOMMAND);
|
|
|
|
// 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.
|
|
if (_IsDesktop() || DV_CDB_IsCommonDialog(this)) {
|
|
int iIndex = MenuIndexFromID(hmInit, SFVIDM_VIEW_CUSTOMWIZARD);
|
|
if (iIndex != -1) {
|
|
DeleteMenu(hmInit, iIndex + 1, MF_BYPOSITION); // Remove Menu seperator
|
|
DeleteMenu(hmInit, iIndex, MF_BYPOSITION); // Remove Customize
|
|
}
|
|
} else {
|
|
// Check if we already know if this folder is customizable!
|
|
if (m_iCustomizable == DONTKNOW_IF_CUSTOMIZABLE) {
|
|
m_iCustomizable = NOT_CUSTOMIZABLE;
|
|
|
|
if (0 == SHRestricted(REST_NOCUSTOMIZETHISFOLDER)) {
|
|
// 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(_GetNameAndFlags(SHGDN_FORPARSING, szPath, ARRAYSIZE(szPath), &rgfFolderAttr)) && (SFGAO_CUST_BITS == (rgfFolderAttr & SFGAO_CUST_BITS))) {
|
|
if (PathIsOneOf(c_aiNonCustomizableFolders, szPath))
|
|
m_iCustomizable = NOT_CUSTOMIZABLE;
|
|
else
|
|
m_iCustomizable = MAYBE_CUSTOMIZABLE;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (m_iCustomizable == NOT_CUSTOMIZABLE || SHRestricted(REST_CLASSICSHELL)) {
|
|
int iIndex = MenuIndexFromID(hmInit, SFVIDM_VIEW_CUSTOMWIZARD);
|
|
if (iIndex != -1) {
|
|
DeleteMenu(hmInit, iIndex + 1, MF_BYPOSITION); // Remove Seperator
|
|
DeleteMenu(hmInit, iIndex, MF_BYPOSITION); // Remove Customize
|
|
}
|
|
} else {
|
|
// Do we even have to do this any more?
|
|
EnableMenuItem(hmInit, SFVIDM_VIEW_CUSTOMWIZARD, MF_BYCOMMAND | MF_ENABLED);
|
|
}
|
|
}
|
|
|
|
EnableMenuItem(hmInit, SFVIDM_ARRANGE_GRID, uAAEnable);
|
|
EnableMenuItem(hmInit, SFVIDM_ARRANGE_AUTO, uAGEnable);
|
|
|
|
// If we are in extended view and have an active child, get the folder
|
|
// settings from the child.
|
|
if (m_cFrame.IsSFVExtension()) {
|
|
uiFlags = 0;
|
|
if (m_cFrame.GetExtendedISFV()->GetAutoArrange() == S_OK) {
|
|
uiFlags = FWF_AUTOARRANGE;
|
|
}
|
|
} else
|
|
uiFlags = _fs.fFlags; //else, use the parent's flags.
|
|
|
|
CheckMenuItem(hmInit, SFVIDM_ARRANGE_AUTO, ((uAAEnable == (MF_ENABLED | MF_BYCOMMAND)) && (uiFlags & FWF_AUTOARRANGE)) ? MF_CHECKED : MF_UNCHECKED);
|
|
DeleteMenu(hmInit, SFVIDM_ARRANGE_DISPLAYICONS, MF_BYCOMMAND);
|
|
|
|
_SHPrettyMenu(hmInit);
|
|
// extended views that don't have smarts can't auto arrange...
|
|
// nor can we auto arrange if we aren't showing icons...
|
|
if ((!_HasNormalView() && !m_cFrame.IsSFVExtension()) || (!DV_SHOWICONS(this))) {
|
|
EnableMenuItem(hmInit, SFVIDM_MENU_ARRANGE, MF_GRAYED | MF_BYCOMMAND);
|
|
} else {
|
|
EnableMenuItem(hmInit, SFVIDM_MENU_ARRANGE, MF_ENABLED | MF_BYCOMMAND);
|
|
}
|
|
}
|
|
|
|
|
|
void DV_GetCBText(CDefView* pdsv, UINT_PTR id, UINT uMsgT, UINT uMsgA, UINT uMsgW, LPTSTR psz, UINT cch)
|
|
{
|
|
*psz = 0;
|
|
|
|
WCHAR szW[MAX_PATH];
|
|
if (SUCCEEDED(pdsv->CallCB(uMsgW, MAKEWPARAM(id - SFVIDM_CLIENT_FIRST, ARRAYSIZE(szW)), (LPARAM)szW)))
|
|
SHUnicodeToTChar(szW, psz, cch);
|
|
else {
|
|
char szA[MAX_PATH];
|
|
if (SUCCEEDED(pdsv->CallCB(uMsgA, MAKEWPARAM(id - SFVIDM_CLIENT_FIRST, ARRAYSIZE(szA)), (LPARAM)szA)))
|
|
SHAnsiToTChar(szA, psz, cch);
|
|
else
|
|
pdsv->CallCB(uMsgT, MAKEWPARAM(id - SFVIDM_CLIENT_FIRST, cch), (LPARAM)psz);
|
|
}
|
|
}
|
|
|
|
|
|
void DV_GetMenuHelpText(CDefView* pdsv, UINT_PTR id, LPTSTR pszText, UINT cchText)
|
|
{
|
|
VDATEINPUTBUF(pszText, TCHAR, cchText);
|
|
*pszText = 0;
|
|
|
|
if (InRange(id, SFVIDM_CONTEXT_FIRST, SFVIDM_CONTEXT_LAST) && pdsv->_pcmSel) {
|
|
*pszText = 0;
|
|
// First try to get the stardard help string
|
|
pdsv->_pcmSel->GetCommandString(id - SFVIDM_CONTEXT_FIRST, GCS_HELPTEXT, NULL, (LPSTR)pszText, cchText);
|
|
if (*pszText == 0) {
|
|
// If we didn't get anything, try to grab the other version of the
|
|
// (ansi for a unicode build, or unicode for an ansi build)
|
|
#ifdef UNICODE
|
|
CHAR szText[MAX_PATH];
|
|
szText[0] = 0; // Don't start with garbage in case of failure...
|
|
pdsv->_pcmSel->GetCommandString(id - SFVIDM_CONTEXT_FIRST, GCS_HELPTEXTA, NULL, szText, ARRAYSIZE(szText));
|
|
SHAnsiToUnicode(szText, pszText, cchText);
|
|
#else
|
|
WCHAR szText[MAX_PATH];
|
|
szText[0] = 0; // Don't start with garbage in case of failure...
|
|
pdsv->_pcmSel->GetCommandString(id - SFVIDM_CONTEXT_FIRST, GCS_HELPTEXTW, NULL, (LPSTR)szText, ARRAYSIZE(szText));
|
|
SHUnicodeToAnsi(szText, pszText, cchText);
|
|
#endif
|
|
}
|
|
|
|
#ifdef SN_TRACE
|
|
if (GetKeyState(VK_CONTROL) < 0) {
|
|
UINT cch;
|
|
lstrcat(pszText, TEXT(" (debug only) Canonical Verb = "));
|
|
cch = lstrlen(pszText);
|
|
pdsv->_pcmSel->GetCommandString(id - SFVIDM_CONTEXT_FIRST, GCS_VERB, NULL, (LPSTR)(pszText + cch), cchText - cch);
|
|
if (*(pszText + cch) == TEXT('\0')) {
|
|
#ifdef UNICODE
|
|
CHAR szVerb[MAX_PATH];
|
|
pdsv->_pcmSel->GetCommandString(id - SFVIDM_CONTEXT_FIRST, GCS_VERBA, NULL, szVerb, ARRAYSIZE(szVerb));
|
|
SHAnsiToUnicode(szVerb, pszText + cch, cchText - cch);
|
|
#else
|
|
WCHAR szVerb[MAX_PATH];
|
|
pdsv->_pcmSel->GetCommandString(id - SFVIDM_CONTEXT_FIRST, GCS_VERBW, NULL, (LPSTR)szVerb, ARRAYSIZE(szVerb));
|
|
SHUnicodeToAnsi(szVerb, pszText + cch, cchText - cch);
|
|
#endif
|
|
}
|
|
}
|
|
#endif
|
|
} else if (InRange(id, SFVIDM_CLIENT_FIRST, SFVIDM_CLIENT_LAST) && pdsv->HasCB()) {
|
|
DV_GetCBText(pdsv, id, SFVM_GETHELPTEXT, SFVM_GETHELPTEXTA, SFVM_GETHELPTEXTW, pszText, cchText);
|
|
} else if (InRange(id, SFVIDM_VIEW_EXTFIRST, SFVIDM_VIEW_EXTLAST)) {
|
|
pdsv->m_cFrame._StringFromView(pdsv->m_cFrame.UidFromCmdId((UINT)id), pszText, cchText, ID_EXTVIEWHELPTEXT);
|
|
} else if (InRange(id, SFVIDM_FIRST, SFVIDM_LAST)) {
|
|
if ((id == SFVIDM_EDIT_UNDO) && IsUndoAvailable()) {
|
|
GetUndoText(PeekUndoAtom(), pszText, cchText, UNDO_STATUSTEXT);
|
|
} else {
|
|
LoadString(HINST_THISDLL, (UINT)(id + SFVIDS_MH_FIRST), pszText, cchText);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
#ifdef UNICODE
|
|
#define DV_GetToolTipTextW DV_GetToolTipText
|
|
#else
|
|
void DV_GetToolTipTextW(CDefView* pdsv, UINT_PTR id, LPWSTR pwzText, UINT cchText)
|
|
{
|
|
LPTSTR pszText = (LPTSTR)LocalAlloc(LPTR, cchText * SIZEOF(TCHAR));
|
|
if (pszText) {
|
|
DV_GetToolTipText(pdsv, id, pszText, cchText);
|
|
SHTCharToUnicode(pszText, pwzText, cchText);
|
|
LocalFree(pszText);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
|
|
void DV_GetToolTipText(CDefView* pdsv, UINT_PTR id, LPTSTR pszText, UINT cchText)
|
|
{
|
|
VDATEINPUTBUF(pszText, TCHAR, cchText);
|
|
*pszText = 0;
|
|
|
|
if (InRange(id, SFVIDM_CLIENT_FIRST, SFVIDM_CLIENT_LAST) && pdsv->HasCB()) {
|
|
DV_GetCBText(pdsv, id, SFVM_GETTOOLTIPTEXT, SFVM_GETTOOLTIPTEXTA, SFVM_GETTOOLTIPTEXTW, pszText, cchText);
|
|
} else if (InRange(id, SFVIDM_VIEW_EXTFIRST, SFVIDM_VIEW_EXTLAST)) {
|
|
pdsv->m_cFrame._StringFromView(pdsv->m_cFrame.UidFromCmdId((UINT)id), pszText, cchText, ID_EXTVIEWTTTEXT);
|
|
} else if (InRange(id, SFVIDM_FIRST, SFVIDM_LAST)) {
|
|
if (id == SFVIDM_EDIT_UNDO) {
|
|
if (IsUndoAvailable()) {
|
|
GetUndoText(PeekUndoAtom(), 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, "DV_GetToolTipText: tip request for unknown object");
|
|
}
|
|
}
|
|
|
|
|
|
LRESULT DefView_OnMenuSelect(CDefView* pdsv, UINT id, UINT mf, HMENU hmenu)
|
|
{
|
|
TCHAR szHelpText[80 + 2 * MAX_PATH]; // Lots of stack!
|
|
|
|
// If we dismissed the edit restore our status bar...
|
|
if (!hmenu && LOWORD(mf) == 0xffff) {
|
|
pdsv->_psb->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;
|
|
}
|
|
|
|
// a-msadek; needed only for BiDi Win95 loc
|
|
// Mirroring will take care of that over NT5 & BiDi Win98
|
|
if (g_bBiDiW95Loc) {
|
|
szHelpText[0] = szHelpText[1] = TEXT('\t');
|
|
szHelpText[2] = TEXT('\0');
|
|
DV_GetMenuHelpText(pdsv, id, &szHelpText[2], ARRAYSIZE(szHelpText) - 2);
|
|
|
|
pdsv->_psb->SendControlMsg(FCW_STATUS, SB_SETTEXT, SBT_RTLREADING | SBT_NOBORDERS | 255, (LPARAM)szHelpText, NULL);
|
|
} else {
|
|
DV_GetMenuHelpText(pdsv, id, szHelpText, ARRAYSIZE(szHelpText));
|
|
pdsv->_psb->SendControlMsg(FCW_STATUS, SB_SETTEXT, SBT_NOBORDERS | 255, (LPARAM)szHelpText, NULL);
|
|
}
|
|
pdsv->_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 DefView_DismissEdit(CDefView* pdsv)
|
|
{
|
|
if (pdsv->_uState == SVUIA_ACTIVATE_FOCUS) {
|
|
HWND hwndFocus = GetFocus();
|
|
if (hwndFocus && pdsv->_hwndListview && GetParent(hwndFocus) == pdsv->_hwndListview) {
|
|
SetFocus(pdsv->_hwndListview);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void DefView_OnInitMenu(CDefView* pdsv)
|
|
{
|
|
// We need to dismiss the edit mode if it is any.
|
|
DefView_DismissEdit(pdsv);
|
|
}
|
|
|
|
|
|
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) || InRange(mii.wID, SFVIDM_BACK_CONTEXT_FIRST, SFVIDM_BACK_CONTEXT_LAST)) {
|
|
TraceMsg(TF_DEFVIEW, "OnInitMenuPopup: 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)
|
|
{
|
|
MENUITEMINFO mii;
|
|
ULONG dwAttr;
|
|
|
|
mii.cbSize = SIZEOF(MENUITEMINFO);
|
|
mii.fMask = MIIM_SUBMENU | MIIM_ID;
|
|
mii.cch = 0; // just in case
|
|
|
|
if (!_hmenuCur)
|
|
return 1;
|
|
|
|
if (!GetMenuItemInfo(_hmenuCur, nIndex, TRUE, &mii) || mii.hSubMenu != hmInit) {
|
|
// if we failed to get mii or the menu we are about to show
|
|
// is some sub sub menu (definitely not file, edit or view --
|
|
// which are the only ones we care about bellow) check if there
|
|
// is a callback and if there are any items added by it and do
|
|
// the notify
|
|
if (this->HasCB() && HasClientItems(hmInit))
|
|
goto NotifyClient;
|
|
|
|
return 1;
|
|
}
|
|
|
|
switch (mii.wID) {
|
|
case FCIDM_MENU_FILE:
|
|
// Don't touch the file menu unless we have the focus.
|
|
if (_uState == SVUIA_ACTIVATE_FOCUS) {
|
|
//DWORD dwMenuState;
|
|
//BOOL bDeleteItems = FALSE; //what is this used for?
|
|
IContextMenu* pcmSel;
|
|
IContextMenu* pcmBack;
|
|
// First of all, remove all the menu items we've added.
|
|
_RemoveContextMenuItems(hmInit);
|
|
|
|
// don't remove any separator! it could be named one and we need it!
|
|
// for this we call shprettymenu!
|
|
//dwMenuState = GetMenuState(hmInit, 0, MF_BYPOSITION);
|
|
//if ((dwMenuState & MF_SEPARATOR) &&
|
|
// !(dwMenuState & MF_POPUP))
|
|
//{
|
|
// DeleteMenu(hmInit, 0, MF_BYPOSITION);
|
|
//}
|
|
|
|
// Let the object add the separator.
|
|
|
|
|
|
// Now add item specific commands to the menu
|
|
// This is done by seeing if we already have a context menu
|
|
// object for our selection. If not we generate it now.
|
|
|
|
// since we're now adding extensions to both file and edit menus
|
|
// we can no longer use cached context menu so free it
|
|
DV_FlushCachedMenu(this);
|
|
|
|
pcmSel = _GetContextMenuFromSelection();
|
|
//ASSERT(pcmSel == _pcmSel || m_cFrame.IsWebView());
|
|
|
|
if (SUCCEEDED(_pshf->CreateViewObject(_hwndMain, IID_IContextMenu, (void**)&pcmBack))) {
|
|
if (!pcmSel) {
|
|
pcmSel = pcmBack;
|
|
pcmBack = NULL;
|
|
}
|
|
|
|
if (pcmBack) {
|
|
if (_pcmBackground == NULL) {
|
|
// cache the contextmenu so that the status bar helptext
|
|
// and the command execution for menu items in a viewextension works
|
|
_pcmBackground = pcmBack;
|
|
|
|
IUnknown_SetSite(pcmBack, SAFECAST(this, IShellView2*));
|
|
pcmBack->QueryContextMenu(hmInit, 0, SFVIDM_BACK_CONTEXT_FIRST, SFVIDM_BACK_CONTEXT_LAST, CMF_DVFILE | CMF_NODEFAULT);
|
|
// assumed refcount of pcmBack in _pcmBackground
|
|
} else
|
|
ATOMICRELEASE(pcmBack);
|
|
}
|
|
}
|
|
|
|
if (NULL != pcmSel) {
|
|
if (_pcmSel == NULL) {
|
|
// cache the contextmenu so that the status bar helptext
|
|
// and the command execution for menu items in a viewextension works
|
|
_pcmSel = pcmSel;
|
|
pcmSel->AddRef();
|
|
}
|
|
|
|
IUnknown_SetSite(pcmSel, SAFECAST(this, IShellView2*));
|
|
pcmSel->QueryContextMenu(hmInit, 0, SFVIDM_CONTEXT_FIRST, SFVIDM_CONTEXT_LAST, CMF_DVFILE | CMF_NODEFAULT);
|
|
ATOMICRELEASE(pcmSel);
|
|
}
|
|
|
|
// Enable/disable menuitems in the "File" pulldown.
|
|
dwAttr = DefView_GetAttributesFromSelection(this, SFGAO_CANRENAME | SFGAO_CANDELETE | SFGAO_CANLINK | SFGAO_HASPROPSHEET);
|
|
Def_InitFileCommands(dwAttr, hmInit, SFVIDM_FIRST, FALSE);
|
|
|
|
_SHPrettyMenu(hmInit);
|
|
} else if (_uState == SVUIA_ACTIVATE_NOFOCUS && _pcmBackground == NULL) {
|
|
IContextMenu* pcmBack;
|
|
if (SUCCEEDED(_pshf->CreateViewObject(_hwndMain, IID_IContextMenu, (void**)&pcmBack))) {
|
|
if (_pcmBackground == NULL) {
|
|
// cache the contextmenu so that the status bar helptext
|
|
// and the command execution for menu items in a viewextension works
|
|
_pcmBackground = pcmBack;
|
|
|
|
IUnknown_SetSite(pcmBack, SAFECAST(this, IShellView2*));
|
|
pcmBack->QueryContextMenu(hmInit, 0, SFVIDM_BACK_CONTEXT_FIRST, SFVIDM_BACK_CONTEXT_LAST, CMF_DVFILE);
|
|
|
|
// assumed refcount of pcmBack in _pcmBackground
|
|
} else
|
|
ATOMICRELEASE(pcmBack);
|
|
}
|
|
}
|
|
|
|
_SHPrettyMenu(hmInit);
|
|
break;
|
|
case FCIDM_MENU_EDIT:
|
|
// Enable/disable menuitems in the "Edit" pulldown.
|
|
dwAttr = DefView_GetAttributesFromSelection(this, SFGAO_CANCOPY | SFGAO_CANMOVE);
|
|
|
|
Def_InitEditCommands(dwAttr, hmInit, SFVIDM_FIRST, _pdtgtBack, 0);
|
|
|
|
// Extended views are dumb if they have no DefViewActiveObject
|
|
if (!_HasNormalView()) {
|
|
// if we are a view extension, query it so see what is supported
|
|
if (m_cFrame.IsSFVExtension()) {
|
|
UINT dwSupport = SFVQS_SELECT_ALL | SFVQS_SELECT_INVERT;
|
|
HRESULT hr = m_cFrame.GetExtendedISFV()->QuerySupport(&dwSupport);
|
|
|
|
UINT uSelectAll;
|
|
UINT uInvertSel;
|
|
|
|
if (hr != S_OK)
|
|
dwSupport = 0;
|
|
|
|
uSelectAll = (dwSupport & SFVQS_SELECT_ALL) ? MF_ENABLED | MF_BYCOMMAND : MF_GRAYED | MF_BYCOMMAND;
|
|
uInvertSel = (dwSupport & SFVQS_SELECT_INVERT) ? MF_ENABLED | MF_BYCOMMAND : MF_GRAYED | MF_BYCOMMAND;
|
|
|
|
EnableMenuItem(hmInit, SFVIDM_SELECT_ALL, uSelectAll);
|
|
EnableMenuItem(hmInit, SFVIDM_SELECT_INVERT, uInvertSel);
|
|
} else {
|
|
UINT uEnable = (!_HasNormalView()) ? (MF_GRAYED | MF_BYCOMMAND) : (MF_ENABLED | MF_BYCOMMAND);
|
|
|
|
EnableMenuItem(hmInit, SFVIDM_SELECT_ALL, uEnable);
|
|
EnableMenuItem(hmInit, SFVIDM_SELECT_INVERT, uEnable);
|
|
}
|
|
}
|
|
|
|
_SHPrettyMenu(hmInit);
|
|
break;
|
|
case FCIDM_MENU_VIEW:
|
|
InitViewMenu(hmInit);
|
|
break;
|
|
}
|
|
|
|
// do the notify if there is a callback and it added some items.
|
|
// before we used to do this only in special case, e.g. for file
|
|
// menu when defview has focus (SVUIA_ACTIVATE_FOCUS), but now
|
|
// we do that all the time to give callback a chance to enable/
|
|
// disable items it added. (e.g. recycle bin needs to enable/disable
|
|
// Empty Recycle bin item no matter who has the focus.
|
|
if (this->HasCB() && HasClientItems(hmInit)) {
|
|
NotifyClient:
|
|
// Yes; pass it on to the callback
|
|
CallCB(SFVM_INITMENUPOPUP, MAKEWPARAM(SFVIDM_CLIENT_FIRST, nIndex), (LPARAM)hmInit);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
// Member: CDefView::AddPropertySheetPages
|
|
STDMETHODIMP CDefView::AddPropertySheetPages(IN DWORD dwReserved, IN LPFNADDPROPSHEETPAGE lpfn, IN LPARAM lParam)
|
|
{
|
|
SFVM_PROPPAGE_DATA data;
|
|
|
|
ASSERT(IS_VALID_CODE_PTR(lpfn, FNADDPROPSHEETPAGE));
|
|
|
|
data.dwReserved = dwReserved;
|
|
data.pfn = lpfn;
|
|
data.lParam = lParam;
|
|
|
|
// try any view extensions that are loaded ...
|
|
if (m_cFrame.IsSFVExtension()) {
|
|
m_cFrame.GetExtendedISV()->AddPropertySheetPages(dwReserved, lpfn, lParam);
|
|
|
|
// ignore the return result as we don't really care if they added any or not...
|
|
}
|
|
|
|
// Call the callback to add pages
|
|
CallCB(SFVM_ADDPROPERTYPAGES, 0, (LPARAM)&data);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
HRESULT CDefView::_SaveViewState(IStream* pstm)
|
|
{
|
|
HRESULT hres;
|
|
LARGE_INTEGER dlibMove = {0, 0};
|
|
ULARGE_INTEGER libCurPosition;
|
|
ULONG ulWrite;
|
|
struct
|
|
{
|
|
DVSAVEHEADER dvSaveHeader;
|
|
DVSAVEHEADEREX dvSaveHeaderEx;
|
|
} dv;
|
|
BOOL bDefaultCols;
|
|
|
|
// Position the stream right after the headers, and save the starting
|
|
// position at the same time
|
|
dlibMove.LowPart = SIZEOF(dv);
|
|
hres = pstm->Seek(dlibMove, STREAM_SEEK_CUR, &libCurPosition);
|
|
if (FAILED(hres))
|
|
return(hres); // ungraceful exit - let the caller clean up pstm
|
|
// HACK: Avoid 2 calls to seek by just subtracting
|
|
libCurPosition.LowPart -= SIZEOF(dv);
|
|
|
|
// Save column order and size info
|
|
bDefaultCols = SaveCols(pstm);
|
|
|
|
ZeroMemory(&dv, SIZEOF(dv));
|
|
dv.dvSaveHeader.cbSize = SIZEOF(dv.dvSaveHeader);
|
|
// We save the view mode to determine if the scroll positions are
|
|
// still valid on restore
|
|
dv.dvSaveHeader.ViewMode = _fs.ViewMode;
|
|
dv.dvSaveHeader.ptScroll.x = (SHORT)GetScrollPos(_hwndListview, SB_HORZ);
|
|
dv.dvSaveHeader.ptScroll.y = (SHORT)GetScrollPos(_hwndListview, SB_VERT);
|
|
dv.dvSaveHeader.dvState = _dvState;
|
|
|
|
// dvSaveHeaderEx.cbColOffset holds the true offset.
|
|
// Win95 gets confused when cbColOffset points to the new
|
|
// format. Zeroing this out tells Win95 to use default widths
|
|
// (after uninstall of ie40).
|
|
|
|
// dv.dvSaveHeader.cbColOffset = 0;
|
|
dv.dvSaveHeaderEx.dwSignature = DVSAVEHEADEREX_SIGNATURE;
|
|
dv.dvSaveHeaderEx.cbSize = SIZEOF(dv.dvSaveHeaderEx);
|
|
dv.dvSaveHeaderEx.wVersion = DVSAVEHEADEREX_VERSION;
|
|
|
|
// Check whether everything is the default; clear out the stream if so
|
|
if (_bClearItemPos && IsDefaultState(dv.dvSaveHeader) && bDefaultCols) {
|
|
goto SetSize;
|
|
}
|
|
|
|
if (bDefaultCols) {
|
|
// No need to save column info
|
|
dv.dvSaveHeaderEx.cbColOffset = 0;
|
|
dv.dvSaveHeader.cbPosOffset = SIZEOF(dv);
|
|
dlibMove.LowPart = libCurPosition.LowPart + dv.dvSaveHeader.cbPosOffset;
|
|
pstm->Seek(dlibMove, STREAM_SEEK_SET, NULL);
|
|
} else {
|
|
ULARGE_INTEGER libPosPosition;
|
|
dlibMove.LowPart = 0;
|
|
pstm->Seek(dlibMove, STREAM_SEEK_CUR, &libPosPosition);
|
|
dv.dvSaveHeaderEx.cbColOffset = SIZEOF(dv);
|
|
dv.dvSaveHeader.cbPosOffset = (USHORT)(libPosPosition.LowPart - libCurPosition.LowPart);
|
|
}
|
|
|
|
// Save potision info, currently stream is positioned immediately after column info
|
|
hres = SavePos(pstm);
|
|
if (FAILED(hres)) {
|
|
goto SetSize;
|
|
}
|
|
|
|
// Win95 expects cbPosOffset to be at the end of the stream --
|
|
// don't change it's value and never store anything after
|
|
// the position information.
|
|
|
|
// Calculate size of total information saved.
|
|
// This is needed when we read the stream.
|
|
{
|
|
ULARGE_INTEGER libEndPosition;
|
|
dlibMove.LowPart = 0;
|
|
pstm->Seek(dlibMove, STREAM_SEEK_CUR, &libEndPosition);
|
|
ASSERT(0 == libEndPosition.HighPart);
|
|
dv.dvSaveHeaderEx.cbStreamSize = (DWORD)(libEndPosition.LowPart - libCurPosition.LowPart);
|
|
}
|
|
|
|
// Now save the header information
|
|
dlibMove.LowPart = libCurPosition.LowPart;
|
|
pstm->Seek(dlibMove, STREAM_SEEK_SET, NULL);
|
|
hres = pstm->Write(&dv, SIZEOF(dv), &ulWrite);
|
|
if (FAILED(hres) || ulWrite != SIZEOF(dv)) {
|
|
goto SetSize;
|
|
}
|
|
|
|
// Make sure we save all information written so far
|
|
libCurPosition.LowPart += dv.dvSaveHeaderEx.cbStreamSize;
|
|
|
|
SetSize:
|
|
dlibMove.LowPart = libCurPosition.LowPart;
|
|
pstm->Seek(dlibMove, STREAM_SEEK_SET, NULL);
|
|
pstm->SetSize(libCurPosition);
|
|
hres = S_OK;
|
|
|
|
return(hres);
|
|
}
|
|
|
|
|
|
// Member: CDefView::SaveViewState
|
|
STDMETHODIMP CDefView::SaveViewState()
|
|
{
|
|
IStream* pstm;
|
|
HRESULT hres;
|
|
|
|
// do we have any view extensions...
|
|
// REVIEW: what stops each one overwritting the data from each other ?
|
|
// if ( m_cFrame.IsSFVExtension() )
|
|
// {
|
|
// m_cFrame.GetExtendedISV()->SaveViewState();
|
|
// }
|
|
// NOTE: This has been commented out until the SaveViewState() mechanism changes...
|
|
hres = _psb->GetViewStateStream(STGM_WRITE, &pstm);
|
|
if (FAILED(hres)) {
|
|
// 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...)
|
|
SaveCols(NULL);
|
|
return hres;
|
|
}
|
|
|
|
hres = _SaveViewState(pstm);
|
|
ATOMICRELEASE(pstm);
|
|
|
|
return hres;
|
|
}
|
|
|
|
|
|
HRESULT CDefView::_GetStorageStream(DWORD grfMode, IStream** ppIStream)
|
|
// 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.
|
|
{
|
|
*ppIStream = NULL;
|
|
|
|
CLSID clsid;
|
|
HRESULT hres = IUnknown_GetClassID(_pshf, &clsid);
|
|
if (SUCCEEDED(hres)) {
|
|
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)
|
|
hres = E_FAIL;
|
|
}
|
|
|
|
return hres;
|
|
}
|
|
|
|
|
|
HRESULT CDefView::_SaveGlobalViewState(void)
|
|
// 99/02/05 #226140 vtan: Function called from DefView's
|
|
// implementation of IOleCommandTarget::Exec() which is
|
|
// invoked from CShellBrowser2::SetAsDefFolderSettings().
|
|
{
|
|
HRESULT hResult;
|
|
IStream* pIStream;
|
|
|
|
hResult = _GetStorageStream(STGM_WRITE, &pIStream);
|
|
if (pIStream != NULL) {
|
|
hResult = _SaveViewState(pIStream);
|
|
pIStream->Release();
|
|
}
|
|
|
|
return(hResult);
|
|
}
|
|
|
|
|
|
HRESULT CDefView::_LoadGlobalViewState(IStream** ppIStream)
|
|
// 99/02/05 #226140 vtan: Function called from
|
|
// _GetSaveHeader to get the default view state
|
|
// for this class.
|
|
{
|
|
return(_GetStorageStream(STGM_READ, ppIStream));
|
|
}
|
|
|
|
|
|
HRESULT CDefView::_ResetGlobalViewState(void)
|
|
// 99/02/09 #226140 vtan: Function used to reset the
|
|
// global view states stored by deleting the key
|
|
// that stores all of them.
|
|
{
|
|
return(RegDeleteKey(HKEY_CURRENT_USER, REGSTR_PATH_EXPLORER TEXT("\\Streams\\Defaults")));
|
|
}
|
|
|
|
|
|
BOOL CDefView::_IsGhosted(LPCITEMIDLIST pidl)
|
|
{
|
|
DWORD uFlags = SFGAO_GHOSTED;
|
|
if (S_OK == _pshf->GetAttributesOf(1, &pidl, &uFlags)) {
|
|
if (uFlags & SFGAO_GHOSTED)
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
void CDefView::_RestoreAllGhostedFileView()
|
|
{
|
|
UINT i, j;
|
|
|
|
ListView_SetItemState(_hwndListview, -1, 0, LVIS_CUT);
|
|
|
|
i = ListView_GetItemCount(_hwndListview);
|
|
|
|
for (j = 0; j < i; ++j) {
|
|
if (_IsGhosted((LPCITEMIDLIST)_GetPIDL(j))) {
|
|
ListView_SetItemState(_hwndListview, j, LVIS_CUT, LVIS_CUT);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
HRESULT CDefView::SelectAndPositionItem(LPCITEMIDLIST pidlItem, UINT uFlags, POINT* ppt)
|
|
{
|
|
int i;
|
|
|
|
if (m_cFrame.IsSFVExtension()) {
|
|
return m_cFrame.GetExtendedISV()->SelectAndPositionItem(pidlItem, uFlags, ppt);
|
|
}
|
|
|
|
// See if we should first deselect everything else
|
|
if (!pidlItem) {
|
|
if (uFlags != SVSI_DESELECTOTHERS) {
|
|
// I only know how to deselect everything
|
|
return(E_INVALIDARG);
|
|
}
|
|
|
|
ListView_SetItemState(_hwndListview, -1, 0, LVIS_SELECTED);
|
|
_RestoreAllGhostedFileView();
|
|
return S_OK;
|
|
}
|
|
|
|
RIP(ILFindLastID(pidlItem) == pidlItem);
|
|
if (ILFindLastID(pidlItem) != pidlItem) {
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
i = _FindItem(pidlItem, NULL, FALSE);
|
|
if (i != -1) {
|
|
// set the position first so that the ensure visible scrolls to
|
|
// the new position
|
|
if (ppt) {
|
|
ListView_SetItemPosition32(_hwndListview, i, ppt->x, ppt->y);
|
|
|
|
_bItemsMoved = TRUE;
|
|
_bClearItemPos = FALSE;
|
|
}
|
|
|
|
// The SVSI_EDIT flag also contains SVSI_SELECT and as such
|
|
// a simple & wont work!
|
|
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, i);
|
|
} else {
|
|
UINT stateMask = LVIS_SELECTED;
|
|
UINT state = (uFlags & SVSI_SELECT) ? LVIS_SELECTED : 0;
|
|
if (uFlags & SVSI_FOCUSED) {
|
|
state |= LVIS_FOCUSED;
|
|
stateMask |= LVIS_FOCUSED;
|
|
}
|
|
|
|
// See if we should first deselect everything else
|
|
if (uFlags & SVSI_DESELECTOTHERS) {
|
|
ListView_SetItemState(_hwndListview, -1, 0, LVIS_SELECTED);
|
|
_RestoreAllGhostedFileView();
|
|
}
|
|
|
|
ListView_SetItemState(_hwndListview, i, state, stateMask);
|
|
|
|
if (uFlags & SVSI_ENSUREVISIBLE)
|
|
ListView_EnsureVisible(_hwndListview, i, FALSE);
|
|
|
|
// BUGBUG:: we should only set focus when SVUIA_ACTIVATE_FOCUS
|
|
// bug fixing that might break find target code
|
|
if (uFlags & SVSI_FOCUSED)
|
|
SetFocus(_hwndListview);
|
|
}
|
|
|
|
// BUGBUG:: Is this call needed here? There was an ILFree of pidlSelect here.
|
|
_ClearSelectList();
|
|
return S_OK;
|
|
}
|
|
|
|
return E_FAIL;
|
|
}
|
|
|
|
|
|
// Member: CDefView::SelectItem
|
|
STDMETHODIMP CDefView::SelectItem(LPCITEMIDLIST pidlItem, UINT uFlags)
|
|
{
|
|
// if we're filling in the background, this item may not be visible yet.
|
|
// if we're an extended view, we don't know how to do anything
|
|
// defer this action until later.
|
|
// Likewise if we are in the process of being created we should defer.
|
|
if (_fDeferSelect()) {
|
|
if (!_hdsaSelect) {
|
|
_hdsaSelect = DSA_Create(sizeof(DVDelaySelItem), 4);
|
|
if (!_hdsaSelect)
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
DVDelaySelItem dvdsi;
|
|
dvdsi.pidl = ILClone(pidlItem);
|
|
if (!dvdsi.pidl)
|
|
return E_OUTOFMEMORY;
|
|
dvdsi.uFlagsSelect = uFlags;
|
|
|
|
if (DSA_AppendItem(_hdsaSelect, &dvdsi) == DSA_ERR) {
|
|
ILFree(dvdsi.pidl);
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
// if we are looking at an extension view then tell it to select something...
|
|
if (m_cFrame.IsSFVExtension())
|
|
return this->m_cFrame.GetExtendedISV()->SelectItem(pidlItem, uFlags);
|
|
else
|
|
return SelectAndPositionItem(pidlItem, uFlags, NULL);
|
|
}
|
|
|
|
|
|
void CDefView::_ClearSelectList()
|
|
{
|
|
if (_hdsaSelect) {
|
|
HDSA hdsa = _hdsaSelect;
|
|
_hdsaSelect = NULL;
|
|
int cItems = DSA_GetItemCount(hdsa);
|
|
int i;
|
|
for (i = 0; i < cItems; i++) {
|
|
DVDelaySelItem* pdvdsi = (DVDelaySelItem*)DSA_GetItemPtr(hdsa, i);
|
|
if (pdvdsi)
|
|
ILFree(pdvdsi->pidl);
|
|
}
|
|
DSA_Destroy(hdsa);
|
|
}
|
|
}
|
|
|
|
|
|
// Call this whenever the state changes such that SelectItem (above)
|
|
void CDefView::SelectSelectedItems()
|
|
{
|
|
if (_hdsaSelect && !_fDeferSelect()) {
|
|
HDSA hdsa = _hdsaSelect;
|
|
_hdsaSelect = NULL;
|
|
int cItems = DSA_GetItemCount(hdsa);
|
|
int i;
|
|
for (i = 0; i < cItems; i++) {
|
|
DVDelaySelItem* pdvdsi = (DVDelaySelItem*)DSA_GetItemPtr(hdsa, i);
|
|
if (pdvdsi) {
|
|
this->SelectItem(pdvdsi->pidl, pdvdsi->uFlagsSelect);
|
|
ILFree(pdvdsi->pidl);
|
|
}
|
|
}
|
|
DSA_Destroy(hdsa);
|
|
}
|
|
}
|
|
|
|
|
|
// To be called back from within CDefFolderMenu
|
|
|
|
// Returns:
|
|
// S_OK, if successfully processed.
|
|
// (S_FALSE), if default code should be used.
|
|
HRESULT CALLBACK DefView_DFMCallBackBG(IShellFolder* psf, HWND hwndOwner, IDataObject* pdtobj, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
HRESULT hres;
|
|
|
|
switch (uMsg) {
|
|
case DFM_VALIDATECMD:
|
|
case DFM_INVOKECOMMAND:
|
|
hres = S_FALSE;
|
|
break;
|
|
default:
|
|
hres = E_NOTIMPL;
|
|
break;
|
|
}
|
|
|
|
return hres;
|
|
}
|
|
|
|
|
|
HRESULT CDefView::_GetIPersistHistoryObject(IPersistHistory** ppph)
|
|
{
|
|
// See to see if specific folder wants to handle it...
|
|
HRESULT hr;
|
|
|
|
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;
|
|
pdvph = new CDefViewPersistHistory();
|
|
if (pdvph) {
|
|
hr = pdvph->QueryInterface(IID_IPersistHistory, (void**)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;
|
|
}
|
|
|
|
|
|
// Member: CDefView::GetItemObject
|
|
STDMETHODIMP CDefView::GetItemObject(UINT uItem, REFIID riid, void** ppv)
|
|
{
|
|
*ppv = NULL;
|
|
|
|
switch (uItem) {
|
|
case SVGIO_BACKGROUND:
|
|
if (IsEqualIID(riid, IID_IContextMenu) || IsEqualIID(riid, IID_IContextMenu2) || IsEqualIID(riid, IID_IContextMenu3)) {
|
|
return CBackgrndMenu_CreateInstance(this, riid, ppv);
|
|
}
|
|
|
|
if (IsEqualIID(riid, IID_IDispatch) || IsEqualIID(riid, IID_IDefViewScript)) {
|
|
if (!m_pauto) {
|
|
// try to create an Instance of the Shell disipatch for folder views...
|
|
HRESULT hres = SHExtCoCreateInstance(NULL, &CLSID_ShellFolderView, NULL, IID_IDispatch, (void**)&m_pauto);
|
|
if (SUCCEEDED(hres)) {
|
|
IShellService* pss;
|
|
hres = m_pauto->QueryInterface(IID_IShellService, (void**)&pss);
|
|
if (SUCCEEDED(hres)) {
|
|
// Pass a ref to ourselves
|
|
pss->SetOwner((IShellFolderView*)this);
|
|
pss->Release();
|
|
} else {
|
|
// Failure...
|
|
ATOMICRELEASE(m_pauto);
|
|
ASSERT(m_pauto == NULL);
|
|
}
|
|
}
|
|
}
|
|
|
|
// return the IDispath interface.
|
|
if (m_pauto)
|
|
return m_pauto->QueryInterface(riid, ppv);
|
|
|
|
break;
|
|
}
|
|
|
|
if (IsEqualIID(riid, IID_IPersistHistory)) {
|
|
// See if the folder wants a chance at this. The main
|
|
// case for this is the search results windows.
|
|
// BUGBUG:: what if DVOC also wants a crack?
|
|
_GetIPersistHistoryObject((IPersistHistory**)ppv);
|
|
if (*ppv) {
|
|
IUnknown_SetSite((IUnknown*)*ppv, SAFECAST(this, IShellView2*));
|
|
return S_OK;
|
|
}
|
|
}
|
|
|
|
// fall through...
|
|
|
|
// we don't know what it is, maybe our extended view does
|
|
if (m_cFrame.IsWebView() && m_cFrame.m_pOleObj)
|
|
return m_cFrame.m_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 DESTROYSTATIC
|
|
// message, we should never get a RIP
|
|
if (PeekMessage(&msg, _hwndView, WM_DSV_DESTROYSTATIC, WM_DSV_DESTROYSTATIC, PM_REMOVE) || PeekMessage(&msg, _hwndStatic, 0, 0, PM_REMOVE)) {
|
|
TranslateMessage(&msg);
|
|
DispatchMessage(&msg);
|
|
}
|
|
} while (_hwndStatic);
|
|
|
|
ResetWaitCursor();
|
|
}
|
|
|
|
// Fall through
|
|
|
|
case SVGIO_SELECTION:
|
|
// if we are looking at a view extension, then ask it for the selection
|
|
if (m_cFrame.IsSFVExtension())
|
|
return m_cFrame.GetExtendedISV()->GetItemObject(uItem, riid, ppv);
|
|
|
|
// hitting this is bad, cuz we wont answer correctly...
|
|
ASSERT(_HasNormalView());
|
|
|
|
return DefView_GetUIObjectFromItem(this, riid, ppv, uItem);
|
|
}
|
|
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
|
|
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, IOleCommandTarget), // IID_IOleCommandTarget
|
|
QITABENT(CDefView, IServiceProvider), // IID_IServiceProvider
|
|
QITABENT(CDefView, IDefViewFrame2), // IID_IDefViewFrame
|
|
QITABENTMULTI(CDefView, IDefViewFrame, IDefViewFrame2), // IID_IDefViewFrame
|
|
QITABENT(CDefView, IDocViewSite), // IID_IDocViewSite
|
|
QITABENT(CDefView, IInternetSecurityMgrSite), // IID_IInternetSecurityMgrSite
|
|
{ 0 }
|
|
};
|
|
|
|
HRESULT hres = QISearch(this, qit, riid, ppvObj);
|
|
if (FAILED(hres)) {
|
|
// special case this one as it simply casts this...
|
|
if (IsEqualIID(riid, IID_CDefView)) {
|
|
*ppvObj = (void*)this;
|
|
AddRef();
|
|
hres = S_OK;
|
|
}
|
|
}
|
|
|
|
return hres;
|
|
}
|
|
|
|
|
|
STDMETHODIMP_(ULONG) CDefView::AddRef()
|
|
{
|
|
return InterlockedIncrement(&_cRef);
|
|
}
|
|
|
|
|
|
STDMETHODIMP_(ULONG) CDefView::Release()
|
|
{
|
|
if (InterlockedDecrement(&_cRef))
|
|
return _cRef;
|
|
|
|
delete this;
|
|
return 0;
|
|
}
|
|
|
|
|
|
// Constructor of CDefView class
|
|
CDefView::CDefView(IShellFolder* psf, IShellFolderViewCB* psfvcb, IShellView* psvOuter) : _cRef(1), m_cCallback(psfvcb)
|
|
{
|
|
DebugMsg(TF_LIFE, TEXT("ctor CDefView %x"), this);
|
|
|
|
psf->QueryInterface(IID_IShellFolder, (void**)&_pshf);
|
|
psf->QueryInterface(IID_IShellFolder2, (void**)&_pshf2);
|
|
|
|
LPCITEMIDLIST pidl = NULL;
|
|
LONG lEvents = 0;
|
|
|
|
if (SUCCEEDED(CallCB(SFVM_GETNOTIFY, (WPARAM)&pidl, (LPARAM)&lEvents))) {
|
|
_pidlMonitor = pidl;
|
|
_lFSEvents = lEvents;
|
|
}
|
|
|
|
BOOL fIsOwnerData = FALSE;
|
|
CallCB(SFVM_ISOWNERDATA, 0, (LONG_PTR)&fIsOwnerData);
|
|
if (fIsOwnerData)
|
|
_fs.fFlags |= FWF_OWNERDATA;
|
|
else
|
|
_fs.fFlags &= ~FWF_OWNERDATA;
|
|
|
|
_GetSortDefaults(&_dvState);
|
|
_cRefForIdle = -1;
|
|
_dwAttrSel = (DWORD)-1;
|
|
|
|
// NOTE we dont AddRef() psvOuter
|
|
// it has a ref on us
|
|
_psvOuter = psvOuter;
|
|
|
|
for (int i = 0; i < ARRAYSIZE(_crCustomColors); i++)
|
|
_crCustomColors[i] = CLR_MYINVALID;
|
|
|
|
_UpdateRegFlags();
|
|
|
|
IDLData_InitializeClipboardFormats();
|
|
}
|
|
|
|
|
|
#ifdef DO_NEXT
|
|
STDAPI SHCreateShellFolderViewA(const SFV_CREATE* pcsfv, IShellView** ppsv)
|
|
{
|
|
if (!pcsfv || SIZEOF(*pcsfv) != pcsfv->cbSize) {
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
CDefView* pdsv = new CDefView(pcsfv->_pshf, pcsfv->psfvcb, pcsfv->psvOuter);
|
|
if (pdsv) {
|
|
*ppsv = pdsv;
|
|
return NOERROR;
|
|
}
|
|
return E_OUTOFMEMORY; // error
|
|
}
|
|
|
|
|
|
// BugBug we need to handle both...
|
|
STDAPI SHCreateShellFolderViewW(const SFV_CREATE* pcsfv, IShellView** ppsv)
|
|
#else
|
|
STDAPI SHCreateShellFolderView(const SFV_CREATE* pcsfv, IShellView** ppsv)
|
|
#endif
|
|
{
|
|
*ppsv = NULL;
|
|
|
|
if (!pcsfv || SIZEOF(*pcsfv) != pcsfv->cbSize) {
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
CDefView* pdsv = new CDefView(pcsfv->pshf, pcsfv->psfvcb, pcsfv->psvOuter);
|
|
if (pdsv) {
|
|
*ppsv = pdsv;
|
|
return NOERROR;
|
|
}
|
|
return E_OUTOFMEMORY; // error
|
|
|
|
}
|
|
|
|
|
|
// Drag & Drop
|
|
void CDVDropTarget::LeaveAndReleaseData(CDefView* that)
|
|
{
|
|
that->_dvdt.DragLeave();
|
|
|
|
if (that->_pdtobjHdrop) {
|
|
TraceMsg(TF_DEFVIEW, "releasing 3.1 HDROP data object");
|
|
|
|
ATOMICRELEASE(that->_pdtobjHdrop);
|
|
}
|
|
}
|
|
|
|
|
|
void CDefView::DropFiles(HDROP hdrop)
|
|
{
|
|
if (_pdtobjHdrop) {
|
|
DWORD dwEffect = DROPEFFECT_COPY;
|
|
POINT pt;
|
|
POINTL ptl;
|
|
// hdrop becomes owned by this data object, it will free it
|
|
HRESULT hres = DataObj_SetGlobal(_pdtobjHdrop, CF_HDROP, hdrop);
|
|
|
|
// we created this data object so this should not fail
|
|
ASSERT(SUCCEEDED(hres));
|
|
|
|
DragQueryPoint(hdrop, &pt); // in client coords of window dropped on
|
|
ClientToScreen(_hwndListview, &pt);
|
|
|
|
ptl.x = pt.x;
|
|
ptl.y = pt.y;
|
|
|
|
// MK_LBUTTON means treat as a default drop
|
|
_dvdt.Drop(_pdtobjHdrop, MK_LBUTTON, ptl, &dwEffect);
|
|
|
|
_dvdt.LeaveAndReleaseData(this);
|
|
} else {
|
|
TraceMsg(TF_ERROR, "WM_DROPFILES with no pdtgt or no pdtobjHDrop");
|
|
DragFinish(hdrop); // be sure to free this
|
|
}
|
|
}
|
|
|
|
|
|
// This function processes win 3.1 Drag & Drop messages
|
|
LRESULT CDefView::OldDragMsgs(UINT uMsg, WPARAM wParam, const DROPSTRUCT* lpds)
|
|
{
|
|
DWORD dwEffect = DROPEFFECT_COPY;
|
|
|
|
// We don't need to do this hack if NT defined POINT as typedef POINTL.
|
|
union
|
|
{
|
|
POINT ptScreen;
|
|
POINTL ptlScreen;
|
|
} drop;
|
|
|
|
ASSERT(SIZEOF(drop.ptScreen) == SIZEOF(drop.ptlScreen));
|
|
|
|
if (lpds) // Notes: lpds is NULL, if uMsg is WM_DROPFILES.
|
|
{
|
|
drop.ptScreen = lpds->ptDrop;
|
|
ClientToScreen(_hwndMain, &drop.ptScreen);
|
|
}
|
|
|
|
switch (uMsg) {
|
|
case WM_DRAGSELECT:
|
|
// WM_DRAGSELECT is sent to a sink whenever an new object is dragged inside of it.
|
|
// wParam: TRUE if the sink is being entered, FALSE if it's being exited.
|
|
|
|
if (wParam) {
|
|
TraceMsg(TF_DEFVIEW, "3.1 drag enter");
|
|
|
|
if (_pdtobjHdrop) {
|
|
// can happen if old target fails to generate drag leave properly
|
|
TraceMsg(TF_DEFVIEW, "generating DragLeave on old drag enter");
|
|
_dvdt.LeaveAndReleaseData(this);
|
|
}
|
|
|
|
if (SUCCEEDED(CIDLData_CreateFromIDArray(NULL, 0, NULL, &_pdtobjHdrop))) {
|
|
// promise the CF_HDROP by setting a NULL handle
|
|
// indicating that this dataobject will have an hdrop at Drop() time
|
|
|
|
DataObj_SetGlobal(_pdtobjHdrop, CF_HDROP, (HGLOBAL)NULL);
|
|
|
|
_dvdt.DragEnter(_pdtobjHdrop, MK_LBUTTON, drop.ptlScreen, &dwEffect);
|
|
}
|
|
} else {
|
|
TraceMsg(TF_DEFVIEW, "3.1 drag leave");
|
|
|
|
_dvdt.LeaveAndReleaseData(this);
|
|
}
|
|
break;
|
|
case WM_DRAGMOVE:
|
|
// WM_DRAGMOVE is sent to a sink as the object is being dragged within it.
|
|
// wParam: Unused
|
|
if (_pdtobjHdrop) {
|
|
_dvdt.DragOver(MK_LBUTTON, drop.ptlScreen, &dwEffect);
|
|
}
|
|
break;
|
|
case WM_QUERYDROPOBJECT:
|
|
switch (lpds->wFmt) {
|
|
case DOF_SHELLDATA:
|
|
case DOF_DIRECTORY:
|
|
case DOF_DOCUMENT:
|
|
case DOF_MULTIPLE:
|
|
case DOF_EXECUTABLE:
|
|
// assume all targets can accept HDROP if we don't have the data object yet
|
|
return TRUE; // we will accept the drop
|
|
}
|
|
return FALSE; // don't accept
|
|
case WM_DROPOBJECT:
|
|
if (!_pdtobjHdrop)
|
|
return FALSE;
|
|
|
|
// Check the format of dragged object.
|
|
switch (lpds->wFmt) {
|
|
case DOF_EXECUTABLE:
|
|
case DOF_DOCUMENT:
|
|
case DOF_DIRECTORY:
|
|
case DOF_MULTIPLE:
|
|
case DOF_PROGMAN:
|
|
case DOF_SHELLDATA:
|
|
// We need to unlock this window if this drag&drop is originated
|
|
// from the shell itself.
|
|
DAD_DragLeave();
|
|
|
|
// The source is Win 3.1 app (probably FileMan), request HDROP.
|
|
return DO_DROPFILE; // Send us a WM_DROPFILES with HDROP
|
|
}
|
|
break;
|
|
case WM_DROPFILES:
|
|
DropFiles((HDROP)wParam);
|
|
break;
|
|
}
|
|
|
|
return 0; // Unknown format. Don't drop any
|
|
}
|
|
|
|
|
|
// CDVDropTarget : member
|
|
void CDVDropTarget::ReleaseDataObject()
|
|
{
|
|
if (this->pdtobj) {
|
|
|
|
// JUST-IN-CASE: Put NULL in this->pdtobj before we release it.
|
|
// We do this just in case because we don't know what OLE does
|
|
// from inside Release (it might call back one of our members).
|
|
|
|
IDataObject* pdtobj = this->pdtobj;
|
|
this->pdtobj = NULL;
|
|
ATOMICRELEASE(pdtobj);
|
|
}
|
|
}
|
|
|
|
|
|
void CDVDropTarget::ReleaseCurrentDropTarget()
|
|
{
|
|
CDefView* pdv = IToClass(CDefView, _dvdt, this);
|
|
if (this->pdtgtCur) {
|
|
this->pdtgtCur->DragLeave();
|
|
ATOMICRELEASE(this->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);
|
|
HWND hwndLock;
|
|
IOleCommandTarget* pct;
|
|
|
|
// We can be re-entered due to ui on thread
|
|
// or will will also want to prevent the drag/copy if the defview
|
|
// is hosted by MSHTML.
|
|
if ((this->pdtobj != NULL) || (S_OK != pdv->_ZoneCheck(PUAF_NOUI, URLACTION_SHELL_WEBVIEW_VERB))) {
|
|
*pdwEffect = DROPEFFECT_NONE;
|
|
return S_OK;
|
|
}
|
|
|
|
// Don't allow a drop from our extended view to ourself!
|
|
if (pdv->m_cFrame.IsWebView() && SUCCEEDED(pdv->m_cFrame.GetCommandTarget(&pct))) {
|
|
HRESULT hres;
|
|
VARIANTARG v;
|
|
v.vt = VT_I4;
|
|
v.lVal = 0;
|
|
|
|
hres = pct->Exec(&CGID_ShellDocView, SHDVID_ISDRAGSOURCE, 0, NULL, &v);
|
|
ATOMICRELEASE(pct);
|
|
if (SUCCEEDED(hres) && (v.lVal != 0)) {
|
|
*pdwEffect = DROPEFFECT_NONE;
|
|
return S_OK;
|
|
}
|
|
}
|
|
|
|
g_fDraggingOverSource = FALSE;
|
|
|
|
TraceMsg(TF_DEFVIEW, "CDVDropTarget::DragEnter with *pdwEffect=%x", *pdwEffect);
|
|
|
|
pdtobj->AddRef();
|
|
this->pdtobj = pdtobj;
|
|
this->grfKeyState = grfKeyState;
|
|
|
|
ASSERT(this->pdtgtCur == NULL);
|
|
// don't really need to do this, but this sets the target state
|
|
ReleaseCurrentDropTarget();
|
|
this->itemOver = -2;
|
|
|
|
// In case of Desktop, we should not lock the enter screen.
|
|
hwndLock = pdv->_IsDesktop() ? pdv->_hwndView : pdv->_hwndMain;
|
|
GetWindowRect(hwndLock, &this->rcLockWindow);
|
|
|
|
_DragEnter(hwndLock, ptl, pdtobj);
|
|
|
|
DAD_InitScrollData(&this->asd);
|
|
|
|
this->ptLast.x = this->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 DV_AlterEffect(CDefView* pdv, DWORD grfKeyState, DWORD* pdwEffect, UINT uFlags)
|
|
{
|
|
g_fDraggingOverSource = FALSE;
|
|
|
|
if (DV_IsDropOnSource(pdv, NULL)) {
|
|
if (DV_ISANYICONMODE(pdv->_fs.ViewMode)) {
|
|
// 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)
|
|
{
|
|
HRESULT hres = S_OK;
|
|
CDefView* pdv = IToClass(CDefView, _dvdt, this);
|
|
int itemNew;
|
|
POINT pt;
|
|
RECT rc;
|
|
BOOL fInRect;
|
|
DWORD dwEffectScroll = 0;
|
|
DWORD dwEffectOut = 0;
|
|
BOOL fSameImage = FALSE;
|
|
|
|
// We will want to prevent the drop/paste if the defview
|
|
// is hosted by MSHTML.
|
|
if ((this->pdtobj == NULL) || (S_OK != pdv->_ZoneCheck(PUAF_NOUI, URLACTION_SHELL_WEBVIEW_VERB))) {
|
|
return E_FAIL;
|
|
}
|
|
|
|
pt.x = ptl.x; // in screen coords
|
|
pt.y = ptl.y;
|
|
|
|
GetWindowRect(pdv->_hwndListview, &rc);
|
|
fInRect = PtInRect(&rc, pt);
|
|
|
|
ScreenToClient(pdv->_hwndListview, &pt);
|
|
|
|
// assume coords of our window match listview
|
|
if (DAD_AutoScroll(pdv->_hwndListview, &this->asd, &pt))
|
|
dwEffectScroll = DROPEFFECT_SCROLL;
|
|
|
|
// hilight an item, or unhilight all items (DropTarget returns -1)
|
|
if (fInRect)
|
|
itemNew = DV_HitTest(pdv, &pt);
|
|
else
|
|
itemNew = -1;
|
|
|
|
// If we are dragging over on a different item, get its IDropTarget
|
|
// interface or adjust itemNew to -1.
|
|
if (this->itemOver != itemNew) {
|
|
IDropTarget* pdtgtNew = NULL;
|
|
|
|
this->dwLastTime = GetTickCount(); // keep track for auto-expanding the tree
|
|
|
|
this->itemOver = itemNew;
|
|
|
|
// Avoid dropping onto drag source objects.
|
|
if ((itemNew != -1) && pdv->_bDragSource) {
|
|
UINT uState = ListView_GetItemState(pdv->_hwndListview, itemNew, LVIS_SELECTED);
|
|
if (uState & LVIS_SELECTED)
|
|
itemNew = -1;
|
|
}
|
|
|
|
// If we are dragging over an item, try to get its IDropTarget.
|
|
if (itemNew != -1) {
|
|
// We are dragging over an item.
|
|
LPCITEMIDLIST apidl[1] = {pdv->_GetPIDL(itemNew)};
|
|
if (apidl[0]) {
|
|
DWORD dwAttr = SFGAO_DROPTARGET;
|
|
hres = pdv->_pshf->GetAttributesOf(1, apidl, &dwAttr);
|
|
if (SUCCEEDED(hres) && (dwAttr & SFGAO_DROPTARGET)) {
|
|
hres = pdv->_pshf->GetUIObjectOf(pdv->_hwndMain, 1, apidl, IID_IDropTarget, NULL, (void**)&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(this->pdtgtCur == NULL);
|
|
|
|
// Update pdv->_itemCur which indicates the current target.
|
|
// (Note that it might be different from this->itemOver).
|
|
pdv->_itemCur = itemNew;
|
|
|
|
// If we are dragging over the background or over non-sink item,
|
|
// get the drop target for the folder.
|
|
if (itemNew == -1) {
|
|
// We are dragging over the background, this can be NULL
|
|
ASSERT(pdtgtNew == NULL);
|
|
this->pdtgtCur = pdv->_pdtgtBack;
|
|
if (this->pdtgtCur)
|
|
this->pdtgtCur->AddRef();
|
|
} else {
|
|
ASSERT(pdtgtNew);
|
|
this->pdtgtCur = pdtgtNew;
|
|
}
|
|
|
|
// Hilight the sink item (itemNew != -1) or unhilight all (-1).
|
|
LVUtil_DragSelectItem(pdv->_hwndListview, itemNew);
|
|
|
|
// Call IDropTarget::DragEnter of the target object.
|
|
if (this->pdtgtCur) {
|
|
// Note that pdwEffect is in/out parameter.
|
|
dwEffectOut = *pdwEffect; // pdwEffect in
|
|
|
|
// Special case if we are dragging within a source window
|
|
DV_AlterEffect(pdv, grfKeyState, &dwEffectOut, DVAE_BEFORE);
|
|
hres = this->pdtgtCur->DragEnter(this->pdtobj, grfKeyState, ptl, &dwEffectOut);
|
|
DV_AlterEffect(pdv, grfKeyState, &dwEffectOut, DVAE_AFTER);
|
|
} else {
|
|
ASSERT(dwEffectOut == 0);
|
|
DV_AlterEffect(pdv, grfKeyState, &dwEffectOut, DVAE_BEFORE | DVAE_AFTER);
|
|
}
|
|
|
|
TraceMsg(TF_DEFVIEW, "CDV::DragOver dwEIn=%x, dwEOut=%x", *pdwEffect, dwEffectOut);
|
|
} else {
|
|
ASSERT(pdtgtNew == NULL); // It must be NULL
|
|
goto NoChange;
|
|
}
|
|
} else {
|
|
NoChange:
|
|
if (this->itemOver != -1) {
|
|
DWORD dwNow = GetTickCount();
|
|
|
|
if ((dwNow - this->dwLastTime) >= 1000) {
|
|
this->dwLastTime = dwNow;
|
|
// DAD_ShowDragImage(FALSE);
|
|
// OpenItem(pdv, this->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 ((this->grfKeyState != grfKeyState) && this->pdtgtCur) {
|
|
// Note that pdwEffect is in/out parameter.
|
|
dwEffectOut = *pdwEffect; // pdwEffect in
|
|
// Special case if we are dragging within a source window
|
|
DV_AlterEffect(pdv, grfKeyState, &dwEffectOut, DVAE_BEFORE);
|
|
hres = this->pdtgtCur->DragOver(grfKeyState, ptl, &dwEffectOut);
|
|
DV_AlterEffect(pdv, grfKeyState, &dwEffectOut, DVAE_AFTER);
|
|
} else {
|
|
// Same item and same key state. Use the previous dwEffectOut.
|
|
dwEffectOut = this->dwEffectOut;
|
|
fSameImage = TRUE;
|
|
hres = S_OK;
|
|
}
|
|
}
|
|
|
|
this->grfKeyState = grfKeyState; // store these for the next Drop
|
|
this->dwEffectOut = dwEffectOut; // and DragOver
|
|
|
|
// HACK ALERT:
|
|
// OLE does not call IDropTarget::Drop if we return something
|
|
// valid. We force OLE call it by returning DROPEFFECT_SCROLL.
|
|
if (g_fDraggingOverSource)
|
|
dwEffectScroll = DROPEFFECT_SCROLL;
|
|
|
|
*pdwEffect = dwEffectOut | dwEffectScroll; // pdwEffect out
|
|
|
|
if (!(fSameImage && pt.x == this->ptLast.x && pt.y == this->ptLast.y)) {
|
|
HWND hwndLock = pdv->_IsDesktop() ? pdv->_hwndView : pdv->_hwndMain;
|
|
_DragMove(hwndLock, ptl);
|
|
this->ptLast.x = ptl.x;
|
|
this->ptLast.y = ptl.y;
|
|
}
|
|
|
|
return hres;
|
|
}
|
|
|
|
|
|
HRESULT CDVDropTarget::DragLeave()
|
|
{
|
|
CDefView* pdv = IToClass(CDefView, _dvdt, this);
|
|
|
|
// Make it possible to call it more than necessary.
|
|
if (this->pdtobj) {
|
|
TraceMsg(TF_DEFVIEW, "CDVDropTarget::DragLeave");
|
|
|
|
ReleaseCurrentDropTarget();
|
|
this->itemOver = -2;
|
|
ReleaseDataObject();
|
|
|
|
DAD_DragLeave();
|
|
LVUtil_DragSelectItem(pdv->_hwndListview, -1);
|
|
}
|
|
|
|
g_fDraggingOverSource = FALSE;
|
|
|
|
ASSERT(this->pdtgtCur == NULL);
|
|
ASSERT(this->pdtobj == NULL);
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
HRESULT CDVDropTarget::Drop(IDataObject* pdtobj, DWORD grfKeyState, POINTL pt, DWORD* pdwEffect)
|
|
{
|
|
CDefView* pdv = IToClass(CDefView, _dvdt, this);
|
|
|
|
// Notes: OLE will give us a different data object (fully marshalled)
|
|
// from the one we've got on DragEnter.
|
|
if (pdtobj != this->pdtobj) {
|
|
ATOMICRELEASE(this->pdtobj);
|
|
this->pdtobj = pdtobj;
|
|
this->pdtobj->AddRef();
|
|
}
|
|
|
|
pdv->_ptDrop.x = pt.x;
|
|
pdv->_ptDrop.y = pt.y;
|
|
|
|
ScreenToClient(pdv->_hwndListview, &pdv->_ptDrop);
|
|
|
|
// handle moves within the same window here.
|
|
// depend on DV_AlterEffect forcing in DROPEFFECT_MOVE and only
|
|
// dropeffect move when drag in same window
|
|
|
|
// Notes: We need to use this->grfKeyState instead of grfKeyState
|
|
// to see if the left mouse was used or not during dragging.
|
|
DV_AlterEffect(pdv, this->grfKeyState, pdwEffect, DVAE_BEFORE | DVAE_AFTER);
|
|
|
|
if ((this->grfKeyState & MK_LBUTTON) && (*pdwEffect == DROPEFFECT_MOVE) && (DV_IsDropOnSource(pdv, NULL))) {
|
|
// This means we are left-dropping on ourselves, so we just move
|
|
// the icons.
|
|
DAD_DragLeave();
|
|
|
|
pdv->_SameViewMoveIcons();
|
|
|
|
SetForegroundWindow(pdv->_hwndMain);
|
|
|
|
ASSERT(pdv->_bDropAnchor == FALSE);
|
|
|
|
*pdwEffect = 0; // the underlying objects didn't 'move' anywhere
|
|
|
|
if (this->pdtgtCur) {
|
|
this->pdtgtCur->DragLeave();
|
|
this->pdtgtCur->Release();
|
|
this->pdtgtCur = NULL;
|
|
}
|
|
} else if (this->pdtgtCur) {
|
|
// use this local because if pdtgtCur::Drop does a UnlockWindow
|
|
// then hits an error and needs to put up a dialog,
|
|
// we could get re-entered and clobber the defview's pdtgtCur
|
|
IDropTarget* pdtgtCur = this->pdtgtCur;
|
|
this->pdtgtCur = NULL;
|
|
|
|
// HACK ALERT!!!!
|
|
|
|
// If we don't call LVUtil_DragEnd here, we'll be able to leave
|
|
// dragged icons visible when the menu is displayed. However, because
|
|
// we are calling IDropTarget::Drop() which may create some modeless
|
|
// dialog box or something, we can not ensure the locked state of
|
|
// the list view -- LockWindowUpdate() can lock only one window at
|
|
// a time. Therefore, we skip this call only if the pdtgtCur
|
|
// is a subclass of CIDLDropTarget, assuming its Drop calls
|
|
// CDefView::DragEnd (or CIDLDropTarget_DragDropMenu) appropriately.
|
|
pdv->_bDropAnchor = TRUE;
|
|
|
|
if (!DoesDropTargetSupportDAD(pdtgtCur)) {
|
|
// This will hide the dragged image.
|
|
DAD_DragLeave();
|
|
|
|
// We need to reset the drag image list so that the user
|
|
// can start another drag&drop while we are in this
|
|
// Drop() member function call.
|
|
DAD_SetDragImage(NULL, NULL);
|
|
}
|
|
|
|
// Special case if we are dragging within a source window
|
|
DV_AlterEffect(pdv, grfKeyState, pdwEffect, DVAE_BEFORE | DVAE_AFTER);
|
|
|
|
IUnknown_SetSite(pdtgtCur, SAFECAST(pdv, IShellView2*));
|
|
pdtgtCur->Drop(pdtobj, grfKeyState, pt, pdwEffect);
|
|
IUnknown_SetSite(pdtgtCur, NULL);
|
|
pdtgtCur->Release();
|
|
DAD_DragLeave();
|
|
pdv->_bDropAnchor = FALSE;
|
|
ASSERT(!DAD_IsDragging());
|
|
} else {
|
|
// We come here if Drop is called without DragMove (with DragEnter).
|
|
TraceMsg(TF_DEFVIEW, "CDV::Drop this->pdtgtCur == 0");
|
|
*pdwEffect = 0;
|
|
}
|
|
|
|
DragLeave(); // DoDragDrop does not call DragLeave() after Drop()
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
// HACK ALERT!!! (see CDVDropTarget::Drop as well)
|
|
|
|
// All the subclasses of CIDLDropTarget MUST call this function from
|
|
// within its Drop() member function. Calling CIDLDropTarget_DragDropMenu()
|
|
// is sufficient because it calls CDefView::UnlockWindow.
|
|
|
|
|
|
// lego... make this a #define in defview.h
|
|
#ifndef DefView_UnlockWindow
|
|
void DefView_UnlockWindow()
|
|
{
|
|
DAD_DragLeave();
|
|
}
|
|
#endif
|
|
|
|
|
|
STDAPI_(CDefView*) DV_HwndMain2DefView(HWND _hwndMain)
|
|
{
|
|
CDefView* pdsv = NULL;
|
|
IShellBrowser* psb = FileCabinet_GetIShellBrowser(_hwndMain);
|
|
if (psb) {
|
|
IShellView* psv;
|
|
if (SUCCEEDED(psb->QueryActiveShellView(&psv))) {
|
|
if (SUCCEEDED(psv->QueryInterface(IID_CDefView, (void**)&pdsv))) {
|
|
// HACK: We'll release this now and assume the pointer
|
|
// is still OK afterwards. We really should release this
|
|
// in the calling function
|
|
if (pdsv)
|
|
pdsv->Release();
|
|
} else {
|
|
// We might have the _psvOuter, query for the pdsv
|
|
IShellView* psvInner;
|
|
if (SUCCEEDED(psv->QueryInterface(IID_IShellView, (void**)&psvInner))) {
|
|
if (SUCCEEDED(psvInner->QueryInterface(IID_CDefView, (void**)&pdsv))) {
|
|
// HACK: We'll release this now and assume the pointer
|
|
// is still OK afterwards. We really should release this
|
|
// in the calling function
|
|
if (pdsv)
|
|
pdsv->Release();
|
|
}
|
|
ATOMICRELEASE(psvInner);
|
|
}
|
|
}
|
|
ATOMICRELEASE(psv);
|
|
}
|
|
}
|
|
|
|
return pdsv;
|
|
}
|
|
|
|
|
|
STDAPI_(HWND) DV_HwndMain2HwndView(HWND _hwndMain)
|
|
{
|
|
CDefView* pdsv = DV_HwndMain2DefView(_hwndMain);
|
|
return pdsv ? pdsv->_hwndView : NULL;
|
|
}
|
|
|
|
|
|
STDAPI_(IShellFolderViewCB*) SHGetShellFolderViewCB(HWND _hwndMain)
|
|
{
|
|
CDefView* pdsv = DV_HwndMain2DefView(_hwndMain);
|
|
return pdsv ? pdsv->m_cCallback.GetSFVCB() : NULL;
|
|
}
|
|
|
|
|
|
BOOL DefView_IsBkDropTarget(CDefView* pdsv, IDropTarget* pdtg)
|
|
{
|
|
BOOL fRet = FALSE;
|
|
POINT pt;
|
|
|
|
if (pdsv->_bContextMenuMode) {
|
|
if (ListView_GetSelectedCount(pdsv->_hwndListview) == 0) {
|
|
fRet = TRUE;
|
|
}
|
|
}
|
|
if (!fRet && DefView_GetDropPoint(pdsv, &pt)) {
|
|
// The Drop point is returned in internal listview coordinates
|
|
// space, so we need to convert it back to client space
|
|
// before we call this function...
|
|
LVUtil_LVToClient(pdsv->_hwndListview, &pt);
|
|
if (DV_HitTest(pdsv, &pt) == -1) {
|
|
fRet = TRUE;
|
|
}
|
|
}
|
|
|
|
return fRet;
|
|
}
|
|
|
|
|
|
STDMETHODIMP CDefView::Rearrange(LPARAM lParamSort)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
DECLAREWAITCURSOR;
|
|
|
|
_dvState.iLastColumnClick = (int)_dvState.lParamSort;
|
|
_dvState.lParamSort = lParamSort;
|
|
|
|
SetWaitCursor();
|
|
|
|
// First see if this wants to be handled externally by callback
|
|
if (FAILED(CallCB(SFVM_ARRANGE, 0, lParamSort))) {
|
|
// Nope...
|
|
if (this->m_cFrame.IsSFVExtension()) {
|
|
this->m_cFrame.GetExtendedISFV()->Rearrange(lParamSort);
|
|
}
|
|
|
|
// we always sort the main listview, so the settings match...
|
|
{
|
|
_SetSortArrows();
|
|
hr = _InternalRearrange() ? S_OK : HRESULT_FROM_WIN32(ERROR_CAN_NOT_COMPLETE);
|
|
}
|
|
}
|
|
|
|
ResetWaitCursor();
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
STDMETHODIMP CDefView::ArrangeGrid()
|
|
{
|
|
this->Command(NULL, GET_WM_COMMAND_MPS(SFVIDM_ARRANGE_GRID, 0, 0));
|
|
return NOERROR;
|
|
}
|
|
|
|
|
|
STDMETHODIMP CDefView::AutoArrange()
|
|
{
|
|
this->Command(NULL, GET_WM_COMMAND_MPS(SFVIDM_ARRANGE_AUTO, 0, 0));
|
|
return NOERROR;
|
|
}
|
|
|
|
|
|
STDMETHODIMP CDefView::GetAutoArrange()
|
|
{
|
|
// Even for IShellView extensions, don't use their autoarrange flags.
|
|
// The top-level defview needs to track this so it persists correctly.
|
|
return _fs.fFlags & FWF_AUTOARRANGE ? S_OK : S_FALSE;
|
|
}
|
|
|
|
|
|
STDMETHODIMP CDefView::GetArrangeParam(LPARAM* plParamSort)
|
|
{
|
|
if (this->m_cFrame.IsSFVExtension()) {
|
|
// delegate to the view extension....
|
|
return this->m_cFrame.GetExtendedISFV()->GetArrangeParam(plParamSort);
|
|
}
|
|
|
|
*plParamSort = _dvState.lParamSort;
|
|
return NOERROR;
|
|
}
|
|
|
|
|
|
STDMETHODIMP CDefView::AddObject(LPITEMIDLIST pidl, UINT* puItem)
|
|
{
|
|
HRESULT hr = NOERROR;
|
|
|
|
// BUGBUG: we fail this for DocObject extended views!!
|
|
*puItem = DefView_AddObject(this, pidl, TRUE);
|
|
|
|
// let the def-view have first go so that it can do the work to find out if
|
|
// if it is really needed (could be filtered out in the common dialogs ..)
|
|
if (this->m_cFrame.IsSFVExtension() && (*puItem >= 0)) {
|
|
// delegate to the view extension....
|
|
hr = this->m_cFrame.GetExtendedISFV()->AddObject(pidl, puItem);
|
|
|
|
if (FAILED(hr) || *puItem < 0)
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
// BUGBUG: There should be more error values
|
|
return *puItem >= 0 ? NOERROR : E_OUTOFMEMORY;
|
|
}
|
|
|
|
|
|
STDMETHODIMP CDefView::GetObjectCount(UINT* puCount)
|
|
{
|
|
if (this->m_cFrame.IsSFVExtension()) {
|
|
// delegate to the view extension....
|
|
return this->m_cFrame.GetExtendedISFV()->GetObjectCount(puCount);
|
|
}
|
|
|
|
*puCount = ListView_GetItemCount(_hwndListview);
|
|
return NOERROR;
|
|
}
|
|
|
|
|
|
STDMETHODIMP CDefView::SetObjectCount(UINT uCount, UINT dwFlags)
|
|
{
|
|
// Mask over to the flags that map directly accross
|
|
DWORD dw = dwFlags & SFVSOC_NOSCROLL;
|
|
HRESULT hres;
|
|
UINT uCountOld = 0;
|
|
|
|
GetObjectCount(&uCountOld);
|
|
|
|
if (this->m_cFrame.IsSFVExtension()) {
|
|
// delegate to the view extension....
|
|
this->m_cFrame.GetExtendedISFV()->SetObjectCount(uCount, dwFlags);
|
|
|
|
// fallthrough so that both views have the same count set...
|
|
}
|
|
|
|
if ((dwFlags & SFVSOC_INVALIDATE_ALL) == 0)
|
|
dw |= LVSICF_NOINVALIDATEALL; // gross transform
|
|
|
|
hres = (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)
|
|
_PostSelChangedMessage();
|
|
|
|
return hres;
|
|
}
|
|
|
|
|
|
STDMETHODIMP CDefView::GetObject(LPITEMIDLIST* ppidl, UINT uItem)
|
|
{
|
|
if (this->m_cFrame.IsSFVExtension()) {
|
|
// delegate to the view extension....
|
|
return this->m_cFrame.GetExtendedISFV()->GetObject(ppidl, uItem);
|
|
}
|
|
|
|
// Worse hack, if -42 then return our own pidl...
|
|
if (uItem == (UINT)-42) {
|
|
*ppidl = (LPITEMIDLIST)_pidlMonitor;
|
|
return(*ppidl ? NOERROR : 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 = _GetPIDL(uItem);
|
|
return *ppidl ? NOERROR : E_UNEXPECTED;
|
|
}
|
|
|
|
|
|
STDMETHODIMP CDefView::RemoveObject(LPITEMIDLIST pidl, UINT* puItem)
|
|
{
|
|
*puItem = _RemoveObject(pidl, FALSE);
|
|
|
|
if (this->m_cFrame.IsSFVExtension() && (*puItem >= 0)) {
|
|
// delegate to the view extension....
|
|
HRESULT hr = this->m_cFrame.GetExtendedISFV()->RemoveObject(pidl, puItem);
|
|
if (FAILED(hr) || *puItem < 0)
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
return(*puItem >= 0 ? NOERROR : E_INVALIDARG);
|
|
}
|
|
|
|
|
|
STDMETHODIMP CDefView::UpdateObject(LPITEMIDLIST pidlOld, LPITEMIDLIST pidlNew, UINT* puItem)
|
|
{
|
|
LPITEMIDLIST apidl[2] = {pidlOld, pidlNew};
|
|
|
|
*puItem = _UpdateObject(apidl, TRUE);
|
|
|
|
if (this->m_cFrame.IsSFVExtension() && (*puItem >= 0)) {
|
|
// delegate to the view extension....
|
|
HRESULT hr = this->m_cFrame.GetExtendedISFV()->UpdateObject(pidlOld, pidlNew, puItem);
|
|
|
|
if (FAILED(hr) || *puItem < 0)
|
|
return E_INVALIDARG;
|
|
|
|
// drop through for the def-view
|
|
}
|
|
|
|
return((int)(*puItem) >= 0 ? NOERROR : E_INVALIDARG);
|
|
}
|
|
|
|
|
|
STDMETHODIMP CDefView::RefreshObject(LPITEMIDLIST pidl, UINT* puItem)
|
|
{
|
|
if (this->m_cFrame.IsSFVExtension()) {
|
|
// delegate to the view extension....
|
|
HRESULT hr = this->m_cFrame.GetExtendedISFV()->RefreshObject(pidl, puItem);
|
|
|
|
if (FAILED(hr) || *puItem < 0)
|
|
return E_INVALIDARG;
|
|
|
|
// drop through ..
|
|
}
|
|
|
|
*puItem = _RefreshObject(&pidl);
|
|
return *puItem >= 0 ? NOERROR : E_INVALIDARG;
|
|
}
|
|
|
|
|
|
STDMETHODIMP CDefView::SetRedraw(BOOL bRedraw)
|
|
{
|
|
if (this->m_cFrame.IsSFVExtension()) {
|
|
// delegate to the view extension....
|
|
return this->m_cFrame.GetExtendedISFV()->SetRedraw(bRedraw);
|
|
|
|
// no drop through, assuming this only aplies to the currently visible view. ..
|
|
}
|
|
|
|
SendMessage(_hwndListview, WM_SETREDRAW, (WPARAM)bRedraw, 0);
|
|
return NOERROR;
|
|
}
|
|
|
|
|
|
STDMETHODIMP CDefView::GetSelectedObjects(LPCITEMIDLIST** pppidl, UINT* puItems)
|
|
{
|
|
UINT cItems;
|
|
if (this->m_cFrame.IsSFVExtension()) {
|
|
// delegate to the view extension....
|
|
return this->m_cFrame.GetExtendedISFV()->GetSelectedObjects(pppidl, puItems);
|
|
}
|
|
|
|
HRESULT hres = DefView_GetItemObjects(this, pppidl, SVGIO_SELECTION, &cItems);
|
|
if (SUCCEEDED(hres)) {
|
|
*puItems = cItems;
|
|
return NOERROR;
|
|
}
|
|
|
|
return hres;
|
|
}
|
|
|
|
|
|
STDMETHODIMP CDefView::GetSelectedCount(UINT* puSelected)
|
|
{
|
|
if (this->m_cFrame.IsSFVExtension()) {
|
|
// delegate to the view extension....
|
|
return this->m_cFrame.GetExtendedISFV()->GetSelectedCount(puSelected);
|
|
}
|
|
|
|
*puSelected = ListView_GetSelectedCount(_hwndListview);
|
|
return NOERROR;
|
|
}
|
|
|
|
|
|
STDMETHODIMP CDefView::IsDropOnSource(IDropTarget* pDropTarget)
|
|
{
|
|
if (this->m_cFrame.IsSFVExtension()) {
|
|
// delegate to the view extension....
|
|
return this->m_cFrame.GetExtendedISFV()->IsDropOnSource(pDropTarget);
|
|
}
|
|
|
|
return DV_IsDropOnSource(this, pDropTarget) ? S_OK : S_FALSE;
|
|
}
|
|
|
|
|
|
STDMETHODIMP CDefView::MoveIcons(IDataObject* pDataObject)
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
|
|
STDMETHODIMP CDefView::GetDropPoint(POINT* ppt)
|
|
{
|
|
if (this->m_cFrame.IsSFVExtension()) {
|
|
// delegate to the view extension....
|
|
return this->m_cFrame.GetExtendedISFV()->GetDropPoint(ppt);
|
|
}
|
|
|
|
return DefView_GetDropPoint(this, ppt) ? S_OK : S_FALSE;
|
|
}
|
|
|
|
|
|
STDMETHODIMP CDefView::GetDragPoint(POINT* ppt)
|
|
{
|
|
if (this->m_cFrame.IsSFVExtension()) {
|
|
// delegate to the view extension....
|
|
return this->m_cFrame.GetExtendedISFV()->GetDragPoint(ppt);
|
|
}
|
|
|
|
return DefView_GetDragPoint(this, ppt) ? S_OK : S_FALSE;
|
|
}
|
|
|
|
|
|
STDMETHODIMP CDefView::SetItemPos(LPCITEMIDLIST pidl, POINT* ppt)
|
|
{
|
|
if (this->m_cFrame.IsSFVExtension()) {
|
|
// delegate to the view extension....
|
|
return this->m_cFrame.GetExtendedISFV()->SetItemPos(pidl, ppt);
|
|
}
|
|
|
|
SFV_SETITEMPOS sip;
|
|
sip.pidl = pidl;
|
|
sip.pt = *ppt;
|
|
|
|
DefView_SetItemPos(this, &sip);
|
|
return NOERROR;
|
|
}
|
|
|
|
|
|
STDMETHODIMP CDefView::IsBkDropTarget(IDropTarget* pDropTarget)
|
|
{
|
|
if (this->m_cFrame.IsSFVExtension()) {
|
|
// delegate to the view extension....
|
|
return this->m_cFrame.GetExtendedISFV()->IsBkDropTarget(pDropTarget);
|
|
}
|
|
|
|
return DefView_IsBkDropTarget(this, pDropTarget) ? S_OK : S_FALSE;
|
|
}
|
|
|
|
|
|
STDMETHODIMP CDefView::SetClipboard(BOOL bMove)
|
|
{
|
|
DSV_OnSetClipboard(this, bMove ? DFM_CMD_MOVE : DFM_CMD_COPY);
|
|
|
|
if (this->m_cFrame.IsSFVExtension()) {
|
|
// delegate to the view extension....
|
|
this->m_cFrame.GetExtendedISFV()->SetClipboard(bMove);
|
|
|
|
// drop through so both defview reflects the change as well
|
|
}
|
|
|
|
return NOERROR;
|
|
}
|
|
|
|
|
|
STDMETHODIMP CDefView::SetPoints(IDataObject* pDataObject)
|
|
{
|
|
if (this->m_cFrame.IsSFVExtension()) {
|
|
// delegate to the view extension....
|
|
return this->m_cFrame.GetExtendedISFV()->SetPoints(pDataObject);
|
|
}
|
|
|
|
DefView_SetPoints(this, pDataObject);
|
|
return NOERROR;
|
|
}
|
|
|
|
|
|
STDMETHODIMP CDefView::GetItemSpacing(ITEMSPACING* pSpacing)
|
|
{
|
|
if (this->m_cFrame.IsSFVExtension()) {
|
|
// delegate to the view extension....
|
|
return this->m_cFrame.GetExtendedISFV()->GetItemSpacing(pSpacing);
|
|
}
|
|
|
|
return(DV_GetItemSpacing(this, pSpacing) ? S_OK : S_FALSE);
|
|
}
|
|
|
|
|
|
STDMETHODIMP CDefView::SetCallback(IShellFolderViewCB* pNewCB, IShellFolderViewCB** ppOldCB)
|
|
{
|
|
*ppOldCB = NULL;
|
|
|
|
HRESULT hr = this->m_cCallback.SetCallback(pNewCB, ppOldCB);
|
|
|
|
// allow both IShellFolderViews to hold the Callback ....
|
|
if (this->m_cFrame.IsSFVExtension()) {
|
|
IShellFolderViewCB* pcb;
|
|
|
|
// delegate to the view extension....
|
|
hr = this->m_cFrame.GetExtendedISFV()->SetCallback(pNewCB, &pcb);
|
|
|
|
// we don't care about the old callback
|
|
if (SUCCEEDED(hr))
|
|
ATOMICRELEASE(pcb);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
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) {
|
|
// note, if we are looking at a view-extension, then Command() will delegate to it..
|
|
return (HRESULT)this->Command(NULL, c_rgiSelectFlags[i][1], 0);
|
|
}
|
|
}
|
|
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
|
|
STDMETHODIMP CDefView::QuerySupport(UINT* pdwSupport)
|
|
{
|
|
if (this->m_cFrame.IsSFVExtension()) {
|
|
return this->m_cFrame.GetExtendedISFV()->QuerySupport(pdwSupport);
|
|
}
|
|
|
|
// DefView supports all the operations...
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CDefView::SetAutomationObject(IDispatch* pdisp)
|
|
{
|
|
// Release any previous automation objects we may have...
|
|
if (m_pauto) {
|
|
// Tell the automation object to release the back pointer to us...
|
|
IShellService* pss;
|
|
if (SUCCEEDED(m_pauto->QueryInterface(IID_IShellService, (void**)&pss))) {
|
|
// Pass an Unknown pointer to this guy...
|
|
pss->SetOwner(NULL);
|
|
ATOMICRELEASE(pss);
|
|
}
|
|
|
|
//release our use of it, make sure won't be hit by any callback...
|
|
IDispatch* pauto = m_pauto;
|
|
m_pauto = NULL;
|
|
ATOMICRELEASE(pauto);
|
|
}
|
|
|
|
if (pdisp) {
|
|
// Hold onto the object...
|
|
m_pauto = pdisp;
|
|
pdisp->AddRef();
|
|
// let sfvextensions handle the automation. . .
|
|
if (m_cFrame.IsSFVExtension()) {
|
|
ASSERT(m_cFrame.GetExtendedISFV());
|
|
ASSERT(m_pauto);
|
|
m_cFrame.GetExtendedISFV()->SetAutomationObject(m_pauto);
|
|
}
|
|
|
|
}
|
|
|
|
return NOERROR;
|
|
}
|
|
|
|
|
|
// A common function that given an IShellFolder and and an Idlist that is
|
|
// contained in it, get back the index into the system image list.
|
|
HRESULT _GetILIndexGivenPXIcon(IExtractIcon* pxicon, UINT uFlags, LPCITEMIDLIST pidl, int* piImage, BOOL fAnsiCrossOver)
|
|
{
|
|
TCHAR szIconFile[MAX_PATH];
|
|
#ifdef UNICODE
|
|
CHAR szIconFileA[MAX_PATH];
|
|
IExtractIconA* pxiconA = (IExtractIconA*)pxicon;
|
|
#endif
|
|
int iIndex;
|
|
int iImage = -1;
|
|
UINT wFlags = 0;
|
|
HRESULT hres;
|
|
|
|
ASSERT(g_himlIcons); // you must initialize the icon cache first
|
|
|
|
#ifdef UNICODE
|
|
if (fAnsiCrossOver) {
|
|
szIconFileA[0] = '\0';
|
|
hres = pxiconA->GetIconLocation(uFlags | GIL_FORSHELL, szIconFileA, ARRAYSIZE(szIconFileA), &iIndex, &wFlags);
|
|
SHAnsiToUnicode(szIconFileA, szIconFile, ARRAYSIZE(szIconFile));
|
|
} else {
|
|
#endif
|
|
hres = pxicon->GetIconLocation(uFlags | GIL_FORSHELL, szIconFile, ARRAYSIZE(szIconFile), &iIndex, &wFlags);
|
|
#ifdef UNICODE
|
|
}
|
|
#endif
|
|
|
|
// "*" as the file name means iIndex is already a system
|
|
// icon index, we are done.
|
|
|
|
// this is a hack for our own internal icon handler
|
|
if (SUCCEEDED(hres) && (wFlags & GIL_NOTFILENAME) && szIconFile[0] == TEXT('*') && szIconFile[1] == 0) {
|
|
*piImage = iIndex;
|
|
return hres;
|
|
}
|
|
|
|
if (SUCCEEDED(hres)) {
|
|
// if GIL_DONTCACHE was returned by the icon handler, dont lookup the previous icon, assume a cache miss.
|
|
if (!(wFlags & GIL_DONTCACHE))
|
|
iImage = LookupIconIndex(PathFindFileName(szIconFile), iIndex, wFlags);
|
|
|
|
// if we miss our cache...
|
|
if (iImage == -1) {
|
|
HICON hiconLarge = NULL;
|
|
HICON hiconSmall = NULL;
|
|
|
|
if (uFlags & GIL_ASYNC) {
|
|
// force a lookup incase we are not in explorer.exe
|
|
*piImage = Shell_GetCachedImageIndex(c_szShell32Dll, II_DOCNOASSOC, 0);
|
|
return E_PENDING;
|
|
}
|
|
|
|
// try getting it from the ExtractIcon member fuction
|
|
#ifdef UNICODE
|
|
if (fAnsiCrossOver) {
|
|
hres = pxiconA->Extract(szIconFileA, iIndex, &hiconLarge, &hiconSmall, MAKELONG(g_cxIcon, g_cxSmIcon));
|
|
} else {
|
|
#endif
|
|
hres = pxicon->Extract(szIconFile, iIndex, &hiconLarge, &hiconSmall, MAKELONG(g_cxIcon, g_cxSmIcon));
|
|
#ifdef UNICODE
|
|
}
|
|
#endif
|
|
// S_FALSE means, can you please do it...Thanks
|
|
|
|
if (hres == S_FALSE && !(wFlags & GIL_NOTFILENAME)) {
|
|
hres = SHDefExtractIcon(szIconFile, iIndex, wFlags, &hiconLarge, &hiconSmall, MAKELONG(g_cxIcon, g_cxSmIcon));
|
|
}
|
|
|
|
// if we extracted a icon add it to the cache.
|
|
|
|
if (hiconLarge) {
|
|
// yes! got it, stuff it into our cache
|
|
iImage = SHAddIconsToCache(hiconLarge, hiconSmall, szIconFile, iIndex, wFlags);
|
|
}
|
|
|
|
// if we failed in any way pick a default icon
|
|
|
|
if (iImage == -1) {
|
|
if (wFlags & GIL_SIMULATEDOC)
|
|
iImage = II_DOCUMENT;
|
|
else if ((wFlags & GIL_PERINSTANCE) && PathIsExe(szIconFile))
|
|
iImage = II_APPLICATION;
|
|
else
|
|
iImage = II_DOCNOASSOC;
|
|
|
|
// force a lookup incase we are not in explorer.exe
|
|
iImage = Shell_GetCachedImageIndex(c_szShell32Dll, iImage, 0);
|
|
// if the handler failed dont cache this default icon.
|
|
// so we will try again later and mabey get the right icon.
|
|
|
|
// handlers should only fail if they cant access the file
|
|
// or something equaly bad.
|
|
|
|
if (SUCCEEDED(hres))
|
|
AddToIconTable(szIconFile, iIndex, wFlags, iImage);
|
|
else
|
|
TraceMsg(TF_DEFVIEW, "not caching icon for '%s' because cant access file", szIconFile);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (iImage < 0)
|
|
iImage = Shell_GetCachedImageIndex(c_szShell32Dll, II_DOCNOASSOC, 0);
|
|
|
|
*piImage = iImage;
|
|
return hres;
|
|
}
|
|
|
|
|
|
// A common function that given an IShellFolder and and an Idlist that is
|
|
// contained in it, get back the index into the system image list.
|
|
STDAPI SHGetIconFromPIDL(IShellFolder* psf, IShellIcon* psi, LPCITEMIDLIST pidl, UINT flags, int* piImage)
|
|
{
|
|
IExtractIcon* pxi;
|
|
HRESULT hres;
|
|
|
|
ASSERT(g_himlIcons); // you must initialize the icon cache first
|
|
|
|
if (psi) {
|
|
#ifdef DEBUG
|
|
* piImage = -1;
|
|
#endif
|
|
hres = psi->GetIconOf(pidl, flags, piImage);
|
|
|
|
if (hres == S_OK) {
|
|
ASSERT(*piImage != -1);
|
|
return hres;
|
|
}
|
|
|
|
if (hres == E_PENDING) {
|
|
ASSERT(flags & GIL_ASYNC);
|
|
ASSERT(*piImage != -1);
|
|
return hres;
|
|
}
|
|
}
|
|
|
|
*piImage = Shell_GetCachedImageIndex(c_szShell32Dll, II_DOCNOASSOC, 0);
|
|
|
|
hres = psf->GetUIObjectOf(NULL, 1, pidl ? &pidl : NULL, IID_IExtractIcon, NULL, (void**)&pxi);
|
|
if (SUCCEEDED(hres)) {
|
|
hres = _GetILIndexGivenPXIcon(pxi, flags, pidl, piImage, FALSE);
|
|
pxi->Release();
|
|
}
|
|
#ifdef UNICODE
|
|
else {
|
|
// Try the ANSI interface, see if we are dealing with an old set of code
|
|
IExtractIconA* pxiA;
|
|
|
|
hres = psf->GetUIObjectOf(NULL, 1, pidl ? &pidl : NULL, IID_IExtractIconA, NULL, (void**)&pxiA);
|
|
if (SUCCEEDED(hres)) {
|
|
hres = _GetILIndexGivenPXIcon((IExtractIcon*)pxiA, flags, pidl, piImage, TRUE);
|
|
pxiA->Release();
|
|
}
|
|
}
|
|
#endif
|
|
|
|
return hres;
|
|
}
|
|
|
|
|
|
// A common function that given an IShellFolder and and an Idlist that is
|
|
// contained in it, get back the index into the system image list.
|
|
int WINAPI SHMapPIDLToSystemImageListIndex(IShellFolder* psf, LPCITEMIDLIST pidl, int* piIndexSel)
|
|
{
|
|
int iIndex;
|
|
|
|
ASSERT(g_himlIcons); // you must initialize the icon cache first
|
|
|
|
if (piIndexSel)
|
|
SHGetIconFromPIDL(psf, NULL, pidl, GIL_OPENICON, piIndexSel);
|
|
|
|
SHGetIconFromPIDL(psf, NULL, pidl, 0, &iIndex);
|
|
return iIndex;
|
|
}
|
|
|
|
|
|
// -------------- auto scroll stuff --------------
|
|
|
|
BOOL _AddTimeSample(AUTO_SCROLL_DATA* pad, const POINT* ppt, DWORD dwTime)
|
|
{
|
|
pad->pts[pad->iNextSample] = *ppt;
|
|
pad->dwTimes[pad->iNextSample] = dwTime;
|
|
|
|
pad->iNextSample++;
|
|
|
|
if (pad->iNextSample == ARRAYSIZE(pad->pts))
|
|
pad->bFull = TRUE;
|
|
|
|
pad->iNextSample = pad->iNextSample % ARRAYSIZE(pad->pts);
|
|
|
|
return pad->bFull;
|
|
}
|
|
|
|
|
|
#ifdef DEBUG
|
|
// for debugging, verify we have good averages
|
|
DWORD g_time = 0;
|
|
int g_distance = 0;
|
|
#endif
|
|
|
|
|
|
int _CurrentVelocity(AUTO_SCROLL_DATA* pad)
|
|
{
|
|
int i, iStart, iNext;
|
|
int dx, dy, distance;
|
|
DWORD time;
|
|
|
|
ASSERT(pad->bFull);
|
|
|
|
distance = 0;
|
|
time = 1; // avoid div by zero
|
|
|
|
i = iStart = pad->iNextSample % ARRAYSIZE(pad->pts);
|
|
|
|
do {
|
|
iNext = (i + 1) % ARRAYSIZE(pad->pts);
|
|
|
|
dx = abs(pad->pts[i].x - pad->pts[iNext].x);
|
|
dy = abs(pad->pts[i].y - pad->pts[iNext].y);
|
|
distance += (dx + dy);
|
|
time += abs(pad->dwTimes[i] - pad->dwTimes[iNext]);
|
|
|
|
i = iNext;
|
|
|
|
} while (i != iStart);
|
|
|
|
#ifdef DEBUG
|
|
g_time = time;
|
|
g_distance = distance;
|
|
#endif
|
|
|
|
// scale this so we don't loose accuracy
|
|
return (distance * 1024) / time;
|
|
}
|
|
|
|
|
|
// NOTE: this is duplicated in shell32.dll
|
|
|
|
// checks to see if we are at the end position of a scroll bar
|
|
// to avoid scrolling when not needed (avoid flashing)
|
|
|
|
// in:
|
|
// code SB_VERT or SB_HORZ
|
|
// bDown FALSE is up or left
|
|
// TRUE is down or right
|
|
BOOL CanScroll(HWND hwnd, int code, BOOL bDown)
|
|
{
|
|
SCROLLINFO si;
|
|
|
|
si.cbSize = SIZEOF(SCROLLINFO);
|
|
si.fMask = SIF_ALL;
|
|
GetScrollInfo(hwnd, code, &si);
|
|
|
|
if (bDown) {
|
|
if (si.nPage)
|
|
si.nMax -= si.nPage - 1;
|
|
return si.nPos < si.nMax;
|
|
} else {
|
|
return si.nPos > si.nMin;
|
|
}
|
|
}
|
|
|
|
|
|
#define DSD_NONE 0x0000
|
|
#define DSD_UP 0x0001
|
|
#define DSD_DOWN 0x0002
|
|
#define DSD_LEFT 0x0004
|
|
#define DSD_RIGHT 0x0008
|
|
|
|
|
|
DWORD ListView_GetWindowStyle(HWND hwnd)
|
|
{
|
|
DWORD dwStyle;
|
|
|
|
#ifdef FLAT_SCROLLBAR
|
|
LONG lExStyle;
|
|
lExStyle = ListView_GetExtendedListViewStyle(hwnd);
|
|
if (!(lExStyle & LVS_EX_FLATSB) ||
|
|
!FlatSB_GetScrollProp(hwnd, WSB_PROP_WINSTYLE, (int*)&dwStyle))
|
|
#endif
|
|
{
|
|
dwStyle = GetWindowLong(hwnd, GWL_STYLE);
|
|
}
|
|
|
|
return dwStyle;
|
|
}
|
|
|
|
|
|
DWORD DAD_DragScrollDirection(HWND hwnd, const POINT* ppt)
|
|
{
|
|
RECT rcOuter, rc;
|
|
DWORD dwDSD = DSD_NONE;
|
|
DWORD dwStyle = ListView_GetWindowStyle(hwnd);
|
|
|
|
// BUGBUG: do these as globals
|
|
#define g_cxVScroll GetSystemMetrics(SM_CXVSCROLL)
|
|
#define g_cyHScroll GetSystemMetrics(SM_CYHSCROLL)
|
|
|
|
GetClientRect(hwnd, &rc);
|
|
|
|
if (dwStyle & WS_HSCROLL)
|
|
rc.bottom -= g_cyHScroll;
|
|
|
|
if (dwStyle & WS_VSCROLL)
|
|
rc.right -= g_cxVScroll;
|
|
|
|
// the explorer forwards us drag/drop things outside of our client area
|
|
// so we need to explictly test for that before we do things
|
|
rcOuter = rc;
|
|
InflateRect(&rcOuter, g_cxSmIcon, g_cySmIcon);
|
|
|
|
InflateRect(&rc, -g_cxIcon, -g_cyIcon);
|
|
|
|
if (!PtInRect(&rc, *ppt) && PtInRect(&rcOuter, *ppt)) {
|
|
// Yep - can we scroll?
|
|
if (dwStyle & WS_HSCROLL) {
|
|
if (ppt->x < rc.left) {
|
|
if (CanScroll(hwnd, SB_HORZ, FALSE))
|
|
dwDSD |= DSD_LEFT;
|
|
} else if (ppt->x > rc.right) {
|
|
if (CanScroll(hwnd, SB_HORZ, TRUE))
|
|
dwDSD |= DSD_RIGHT;
|
|
}
|
|
}
|
|
if (dwStyle & WS_VSCROLL) {
|
|
if (ppt->y < rc.top) {
|
|
if (CanScroll(hwnd, SB_VERT, FALSE))
|
|
dwDSD |= DSD_UP;
|
|
} else if (ppt->y > rc.bottom) {
|
|
if (CanScroll(hwnd, SB_VERT, TRUE))
|
|
dwDSD |= DSD_DOWN;
|
|
}
|
|
}
|
|
}
|
|
|
|
return dwDSD;
|
|
}
|
|
|
|
|
|
#define SCROLL_FREQUENCY (GetDoubleClickTime()/2) // 1 line scroll every 1/4 second
|
|
#define MIN_SCROLL_VELOCITY 20 // scaled mouse velocity
|
|
|
|
|
|
BOOL WINAPI DAD_AutoScroll(HWND hwnd, AUTO_SCROLL_DATA* pad, const POINT* pptNow)
|
|
{
|
|
// first time we've been called, init our state
|
|
int v;
|
|
DWORD dwTimeNow = GetTickCount();
|
|
DWORD dwDSD = DAD_DragScrollDirection(hwnd, pptNow);
|
|
|
|
if (!_AddTimeSample(pad, pptNow, dwTimeNow))
|
|
return dwDSD;
|
|
|
|
v = _CurrentVelocity(pad);
|
|
|
|
if (v <= MIN_SCROLL_VELOCITY) {
|
|
// Nope, do some scrolling.
|
|
if ((dwTimeNow - pad->dwLastScroll) < SCROLL_FREQUENCY)
|
|
dwDSD = 0;
|
|
|
|
if (dwDSD & DSD_UP) {
|
|
DAD_ShowDragImage(FALSE);
|
|
FORWARD_WM_VSCROLL(hwnd, NULL, SB_LINEUP, 1, SendMessage);
|
|
} else if (dwDSD & DSD_DOWN) {
|
|
DAD_ShowDragImage(FALSE);
|
|
FORWARD_WM_VSCROLL(hwnd, NULL, SB_LINEDOWN, 1, SendMessage);
|
|
}
|
|
if (dwDSD & DSD_LEFT) {
|
|
DAD_ShowDragImage(FALSE);
|
|
FORWARD_WM_HSCROLL(hwnd, NULL, SB_LINEUP, 1, SendMessage);
|
|
} else if (dwDSD & DSD_RIGHT) {
|
|
DAD_ShowDragImage(FALSE);
|
|
FORWARD_WM_HSCROLL(hwnd, NULL, SB_LINEDOWN, 1, SendMessage);
|
|
}
|
|
|
|
DAD_ShowDragImage(TRUE);
|
|
|
|
if (dwDSD) {
|
|
TraceMsg(TF_DEFVIEW, "v=%d", v);
|
|
pad->dwLastScroll = dwTimeNow;
|
|
}
|
|
}
|
|
|
|
return dwDSD; // bits set if in scroll region
|
|
}
|
|
|
|
|
|
// We need to store this array in per-instance data because we don't
|
|
// want to marshal COPYHOOKINFO data structure across process boundary.
|
|
// However, this implementation does not support multiple instance of the shell.
|
|
HDSA g_hdsaDefViewCopyHook = NULL;
|
|
|
|
|
|
typedef struct _DVCOPYHOOK
|
|
{
|
|
HWND _hwndView;
|
|
CDefView* pdv;
|
|
} DVCOPYHOOK, * LPDVCOPYHOOK;
|
|
|
|
|
|
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) {
|
|
this->AddRef();
|
|
TraceMsg(TF_DEFVIEW, "AddCopyHook successfully added (total=%d)", DSA_GetItemCount(g_hdsaDefViewCopyHook));
|
|
}
|
|
}
|
|
LEAVECRITICAL;
|
|
}
|
|
|
|
|
|
int CDefView::FindCopyHook(BOOL fRemoveInvalid)
|
|
{
|
|
int item;
|
|
ASSERTCRITICAL;
|
|
|
|
if (g_hdsaDefViewCopyHook == NULL) {
|
|
return -1;
|
|
}
|
|
|
|
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) {
|
|
LPDVCOPYHOOK pdvch = (LPDVCOPYHOOK)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);
|
|
}
|
|
|
|
|
|
extern "C" UINT DefView_CopyHook(const COPYHOOKINFO * pchi)
|
|
{
|
|
int item;
|
|
UINT idRet = IDYES;
|
|
|
|
if (g_hdsaDefViewCopyHook == NULL) {
|
|
return idRet;
|
|
}
|
|
|
|
for (item = 0;; item++) {
|
|
DVCOPYHOOK dvch = {NULL, NULL};
|
|
|
|
// We should minimize this critical section (and must not call pfnCallBack which may popup UI!).
|
|
ENTERCRITICAL;
|
|
if (g_hdsaDefViewCopyHook && DSA_GetItem(g_hdsaDefViewCopyHook, item, &dvch)) {
|
|
dvch.pdv->AddRef();
|
|
}
|
|
LEAVECRITICAL;
|
|
|
|
if (dvch.pdv) {
|
|
if (IsWindow(dvch._hwndView)) {
|
|
HRESULT hres = dvch.pdv->CallCB(SFVM_NOTIFYCOPYHOOK, 0, (LPARAM)pchi);
|
|
|
|
ATOMICRELEASE(dvch.pdv);
|
|
if (SUCCEEDED(hres) && (hres != S_OK)) {
|
|
idRet = HRESULT_CODE(hres);
|
|
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 extended view
|
|
STDMETHODIMP CDefView::QueryStatus(const GUID* pguidCmdGroup, ULONG cCmds, OLECMD rgCmds[], OLECMDTEXT* pcmdtext)
|
|
{
|
|
HRESULT hres = OLECMDERR_E_UNKNOWNGROUP;
|
|
BOOL fQSCalled = FALSE;
|
|
|
|
if (m_cFrame.IsWebView()) {
|
|
IOleCommandTarget* pct;
|
|
|
|
if (SUCCEEDED(m_cFrame.GetCommandTarget(&pct))) {
|
|
hres = pct->QueryStatus(pguidCmdGroup, cCmds, rgCmds, pcmdtext);
|
|
fQSCalled = SUCCEEDED(hres);
|
|
ATOMICRELEASE(pct);
|
|
}
|
|
}
|
|
|
|
if (pguidCmdGroup == NULL) {
|
|
if (_HasNormalView()) {
|
|
if (rgCmds == NULL)
|
|
return E_INVALIDARG;
|
|
|
|
UINT i;
|
|
for (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 extended view 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);
|
|
DV_GetToolTipTextW(this, rgCmds[0].cmdID, pcmdtext->rgwz, pcmdtext->cwBuf);
|
|
|
|
// ensure NULL termination
|
|
pcmdtext->rgwz[pcmdtext->cwBuf - 1] = 0;
|
|
pcmdtext->cwActual = lstrlenW(pcmdtext->rgwz);
|
|
|
|
hres = S_OK;
|
|
break;
|
|
default:
|
|
hres = E_FAIL;
|
|
break;
|
|
}
|
|
} else {
|
|
DWORD dwAttr = DefView_GetAttributesFromSelection(this, SFGAO_RELEVANT);
|
|
|
|
for (UINT i = 0; i < cCmds; i++) {
|
|
if (_ShouldEnableButton(rgCmds[i].cmdID, dwAttr, -1))
|
|
rgCmds[i].cmdf = OLECMDF_ENABLED;
|
|
else
|
|
rgCmds[i].cmdf = 0;
|
|
}
|
|
|
|
hres = S_OK;
|
|
}
|
|
}
|
|
|
|
return hres;
|
|
}
|
|
|
|
|
|
STDMETHODIMP CDefView::Exec(const GUID* pguidCmdGroup, DWORD nCmdID, DWORD nCmdexecopt, VARIANTARG* pvarargIn, VARIANTARG* pvarargOut)
|
|
{
|
|
HRESULT hres = OLECMDERR_E_UNKNOWNGROUP;
|
|
|
|
if (pguidCmdGroup == NULL) {
|
|
switch (nCmdID) {
|
|
case OLECMDID_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);
|
|
}
|
|
hres = 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");
|
|
hres = _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");
|
|
hres = _ResetGlobalViewState();
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
} else if (IsEqualGUID(CGID_ShellDocView, *pguidCmdGroup)) {
|
|
switch (nCmdID) {
|
|
case SHDVID_CANACTIVATENOW:
|
|
{
|
|
hres = (_fCanActivateNow) ? S_OK : S_FALSE;
|
|
TraceMsg(TF_DEFVIEW, "Defview SHDVID_CANACTIVATENOW returning 0x%x", hres);
|
|
return hres;
|
|
}
|
|
|
|
// 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
|
|
// extended view. 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;
|
|
|
|
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;
|
|
Command(NULL, nCmdID, (LPARAM)&cd);
|
|
}
|
|
|
|
// no need to pass OLECMDID_REFRESH on to the extended view, as we
|
|
// just nuked and replaced the extended view above -- a super refresh of sorts.
|
|
if (m_cFrame.IsWebView() && hres != 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(m_cFrame.GetCommandTarget(&pct))) {
|
|
hres = pct->Exec(pguidCmdGroup, nCmdID, nCmdexecopt, pvarargIn, pvarargOut);
|
|
ATOMICRELEASE(pct);
|
|
}
|
|
}
|
|
}
|
|
|
|
return hres;
|
|
}
|
|
|
|
|
|
// BUGBUG: remove this and roll into caller...
|
|
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);
|
|
}
|
|
|
|
|
|
// IDefViewFrame2 (available only through QueryService from sfvext!)
|
|
HRESULT CDefView::GetWindowLV2(HWND* phwnd, IUnknown* punk)
|
|
{
|
|
|
|
ASSERT(punk);
|
|
if (punk && !_IsDesktop() && SUCCEEDED(punk->QueryInterface(IID_IWebViewOCWinMan, (void**)&_pocWinMan))) {
|
|
if (!_fGetWindowLV) {
|
|
_fGetWindowLV = TRUE;
|
|
if (m_cFrame.IsSFVExtension()) {
|
|
HWND hwndXV;
|
|
|
|
if (!(hwndXV = m_cFrame.GetExtendedViewWindow()))
|
|
return E_FAIL;
|
|
*phwnd = hwndXV;
|
|
m_cFrame.SetViewWindowStyle(WS_EX_CLIENTEDGE, 0);
|
|
|
|
ASSERT(m_cFrame.GetExtendedISFV());
|
|
ASSERT(m_pauto);
|
|
m_cFrame.GetExtendedISFV()->SetAutomationObject(m_pauto);
|
|
} else {
|
|
// if shellview extension hosted in extended view(thumbnail in webview)
|
|
// return the hwnd of the shellview extension.
|
|
*phwnd = _hwndListview;
|
|
if (_bUpdatePending)
|
|
_ReloadListviewContent();
|
|
}
|
|
|
|
// BUGBUG: This is in the WRONG place, should happen when we
|
|
// go InPlaceActive. Also, this only calls UIActivate, so we
|
|
// should rename the message (and the helper function). Also,
|
|
// We might want to *cache* the UIActivate call (and NOT do it)
|
|
// until we go inplace active, and then do it at that time.
|
|
|
|
// 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. (Ie, remove below _ShowAndActivate calls.)
|
|
if (!_fCanActivateNow)
|
|
PostMessage(_hwndView, WM_DSV_REARRANGELISTVIEW, 0, 0);
|
|
else
|
|
_ShowAndActivate();
|
|
}
|
|
TraceMsg(TF_DEFVIEW, "GetWindowLV - TAKEN");
|
|
return S_OK;
|
|
} else {
|
|
*phwnd = NULL;
|
|
return E_FAIL;
|
|
}
|
|
}
|
|
|
|
|
|
// IDefViewFrame
|
|
HRESULT CDefView::GetWindowLV(HWND* phwnd)
|
|
{
|
|
ASSERT(0); // What should I do here?
|
|
return GetWindowLV2(phwnd, NULL);
|
|
}
|
|
|
|
|
|
HRESULT CDefView::ReleaseWindowLV()
|
|
{
|
|
_fGetWindowLV = FALSE;
|
|
|
|
WndSize(_hwndView); // Make sure we resize _hwndListview
|
|
ATOMICRELEASE(_pocWinMan); // may be NULL
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
HRESULT CDefView::GetShellFolder(IShellFolder** ppsf)
|
|
{
|
|
*ppsf = _pshf;
|
|
if (*ppsf)
|
|
_pshf->AddRef();
|
|
|
|
return *ppsf ? S_OK : E_FAIL;
|
|
}
|
|
|
|
|
|
// IServiceProvider
|
|
|
|
STDMETHODIMP CDefView::QueryService(REFGUID guidService, REFIID riid, void** ppv)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
*ppv = NULL;
|
|
|
|
if (guidService == SID_DefView) {
|
|
// DefViewOCs request this interface
|
|
if (riid != IID_IDefViewFrame || !_IsDesktop())
|
|
hr = QueryInterface(riid, ppv);
|
|
} else if (guidService == SID_ShellFolderViewCB) {
|
|
IShellFolderViewCB* psfvcb = m_cCallback.GetSFVCB();
|
|
if (psfvcb)
|
|
hr = psfvcb->QueryInterface(riid, ppv);
|
|
} else {
|
|
hr = IUnknown_QueryService(_psb, guidService, riid, ppv);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
STDMETHODIMP CDefView::OnSetTitle(VARIANTARG* pvTitle)
|
|
{
|
|
DebugMsg(DM_ERROR, TEXT("CDefView::OnSetTitle - not implemented"));
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
|
|
// friend create function for the Background context menu wrapper
|
|
HRESULT CBackgrndMenu_CreateInstance(CDefView* pDefView, REFIID riid, void** ppvObj)
|
|
{
|
|
HRESULT hr = NOERROR;
|
|
CBackgrndMenu* pMenu = new CBackgrndMenu(pDefView, &hr);
|
|
if (!pMenu) {
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
if (FAILED(hr)) {
|
|
delete pMenu;
|
|
return hr;
|
|
}
|
|
|
|
hr = pMenu->QueryInterface(riid, ppvObj);
|
|
ATOMICRELEASE(pMenu);
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
// The background ContextMenu handler. this is a wrapper for menu creation and
|
|
// the old menu handler than only responds to GetCommandString. This object is
|
|
// obtained by doing DefView->GetItemObjects( SVGIO_BACKGROUND, IID_IContextMenu );
|
|
CBackgrndMenu::CBackgrndMenu(CDefView* pDefView, HRESULT* pHr) : m_pDefView(pDefView), m_cRef(1)
|
|
{
|
|
if (!m_pDefView) {
|
|
*pHr = E_INVALIDARG;
|
|
return;
|
|
}
|
|
|
|
m_pDefView->AddRef();
|
|
m_powsSite = NULL;
|
|
|
|
// create the folder menu that used to be created (this is used to handle the getcommand string)
|
|
|
|
LPITEMIDLIST pidlFolder = pDefView->_GetViewPidl();
|
|
*pHr = CDefFolderMenu_Create(pidlFolder, m_pDefView->_hwndMain, 0, NULL, m_pDefView->_pshf, DefView_DFMCallBackBG, NULL, NULL, (IContextMenu**)&m_pFolderMenu);
|
|
ILFree(pidlFolder);
|
|
}
|
|
|
|
|
|
CBackgrndMenu::~CBackgrndMenu()
|
|
{
|
|
if (!m_fpcmSelAlreadyThere && m_pDefView && m_pcmSel && !m_fFlush) {
|
|
if (m_pDefView->_pcmSel == m_pcmSel) {
|
|
DV_FlushCachedMenu(m_pDefView);
|
|
}
|
|
}
|
|
|
|
// robustness: we leak if we don't get SetSite-d to NULL
|
|
if (m_pcmSel) {
|
|
IUnknown_SetSite(SAFECAST(m_pcmSel, IContextMenu*), NULL);
|
|
m_pcmSel->Release();
|
|
m_pcmSel = NULL;
|
|
}
|
|
ATOMICRELEASE(m_powsSite);
|
|
ATOMICRELEASE(m_pDefView);
|
|
ATOMICRELEASE(m_pFolderMenu);
|
|
}
|
|
|
|
|
|
STDMETHODIMP CBackgrndMenu::QueryInterface(REFIID riid, void** ppvObj)
|
|
{
|
|
static const QITAB qit[] = {
|
|
QITABENT(CBackgrndMenu, IContextMenu3), // IID_IContextMenu3
|
|
QITABENTMULTI(CBackgrndMenu, IContextMenu2, IContextMenu3), // IID_IContextMenu2
|
|
QITABENTMULTI(CBackgrndMenu, IContextMenu, IContextMenu3), // IID_IContextMenu
|
|
QITABENT(CBackgrndMenu, IObjectWithSite), // IID_IObjectWithSite
|
|
{ 0 }
|
|
};
|
|
|
|
return QISearch(this, qit, riid, ppvObj);
|
|
}
|
|
|
|
|
|
STDMETHODIMP_(ULONG) CBackgrndMenu::AddRef()
|
|
{
|
|
return InterlockedIncrement(&m_cRef);
|
|
}
|
|
|
|
|
|
STDMETHODIMP_(ULONG) CBackgrndMenu::Release()
|
|
{
|
|
if (InterlockedDecrement(&m_cRef))
|
|
return m_cRef;
|
|
delete this;
|
|
return 0;
|
|
}
|
|
|
|
|
|
STDMETHODIMP CBackgrndMenu::QueryContextMenu(HMENU hmenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags)
|
|
{
|
|
HMENU hmContext;
|
|
HRESULT hr = NOERROR;
|
|
|
|
if (!(uFlags & (CMF_VERBSONLY | CMF_DVFILE))) {
|
|
hmContext = SHLoadPopupMenu(HINST_THISDLL, POPUP_SFV_BACKGROUND);
|
|
if (!hmContext) {
|
|
// BUGBUG: There should be an error message here
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
// HACK: we are only initializing the Paste command, so we don't
|
|
// need any attributes
|
|
Def_InitEditCommands(0, hmContext, SFVIDM_FIRST, m_pDefView->_pdtgtBack, DIEC_BACKGROUNDCONTEXT);
|
|
|
|
// If this defview is a part of common dialog, don't add the extended view menu items. (except the ones marked as standard)
|
|
m_pDefView->m_cFrame.MergeExtViewsMenu(hmContext, m_pDefView);
|
|
|
|
m_pDefView->InitViewMenu(hmContext);
|
|
} else if (!(hmContext = CreatePopupMenu())) {
|
|
// BUGBUG: There should be an error message here
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
if (m_pDefView->_pcmSel == NULL) {
|
|
IContextMenu* pcm = NULL;
|
|
|
|
// it's okay to create and cache this context menu even if we have
|
|
// an extended view because we're doing the background context menu
|
|
// here, which is handled by the parent defview. _pcmSel is also
|
|
// flushed at the end of this function.
|
|
hr = m_pDefView->_pshf->CreateViewObject(m_pDefView->_hwndMain, IID_IContextMenu, (void**)&pcm);
|
|
if (SUCCEEDED(hr)) {
|
|
m_pDefView->_pcmSel = pcm;
|
|
m_pcmSel = pcm;
|
|
m_pcmSel->AddRef();
|
|
}
|
|
} else {
|
|
m_fpcmSelAlreadyThere = TRUE;
|
|
m_pcmSel = m_pDefView->_pcmSel;
|
|
m_pcmSel->AddRef();
|
|
}
|
|
|
|
if (SUCCEEDED(hr)) {
|
|
IUnknown_SetSite(m_pcmSel, m_powsSite);
|
|
|
|
hr = m_pDefView->_pcmSel->QueryContextMenu(hmContext, (UINT)-1, SFVIDM_CONTEXT_FIRST, SFVIDM_CONTEXT_LAST, uFlags);
|
|
}
|
|
|
|
// Always merge in evan if error. RNAUI - fails the CreateViewObject and they rely on simply having the default stuff...
|
|
Shell_MergeMenus(hmenu, hmContext, indexMenu, idCmdFirst, idCmdLast, uFlags);
|
|
|
|
DestroyMenu(hmContext);
|
|
return hr;
|
|
}
|
|
|
|
|
|
STDMETHODIMP CBackgrndMenu::InvokeCommand(LPCMINVOKECOMMANDINFO lpici)
|
|
{
|
|
UINT idCmd = 0;
|
|
HRESULT hr = E_FAIL;
|
|
|
|
// Some of the commands passed to defview can release this CBackgrndMenu object even when
|
|
// we are still processing this message. So, add an AddRef() - Release() pair of calls.
|
|
AddRef();
|
|
if (!IS_INTRESOURCE(lpici->lpVerb)) {
|
|
// the general view commands don't accept verbs, only the
|
|
// folder is likely to accept verbs, so pass it straight through...
|
|
hr = m_pFolderMenu->InvokeCommand(lpici);
|
|
|
|
if (FAILED(hr)) {
|
|
hr = m_pcmSel->InvokeCommand(lpici);
|
|
}
|
|
} else {
|
|
idCmd = PtrToUlong((PVOID)lpici->lpVerb);
|
|
hr = (HRESULT)m_pDefView->Command(m_pcmSel, GET_WM_COMMAND_MPS(idCmd, 0, 0));
|
|
}
|
|
|
|
if (!m_fpcmSelAlreadyThere) {
|
|
if (m_pDefView->_pcmSel == m_pcmSel) {
|
|
DV_FlushCachedMenu(m_pDefView);
|
|
}
|
|
}
|
|
// we have passed the point when we would have flushed the selection menu..
|
|
m_fFlush = TRUE;
|
|
|
|
Release();
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
STDMETHODIMP CBackgrndMenu::GetCommandString(UINT_PTR idCmd, UINT uType, UINT* pwRes, LPSTR pszName, UINT cchMax)
|
|
{
|
|
return m_pFolderMenu->GetCommandString(idCmd, uType, pwRes, pszName, cchMax);
|
|
}
|
|
|
|
|
|
STDMETHODIMP CBackgrndMenu::HandleMenuMsg(UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
return HandleMenuMsg2(uMsg, wParam, lParam, NULL);
|
|
}
|
|
|
|
|
|
STDMETHODIMP CBackgrndMenu::HandleMenuMsg2(UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT* plResult)
|
|
{
|
|
// we cannot just pass this through to the DefView_WndProc because it will in turn delegate back to the cached context menu (us)
|
|
switch (uMsg) {
|
|
case WM_INITMENUPOPUP:
|
|
if (!(m_pDefView->OnInitMenuPopup((HMENU)wParam, LOWORD(lParam), HIWORD(lParam))))
|
|
return NOERROR;
|
|
break;
|
|
case WM_MEASUREITEM:
|
|
#define lpmis ((LPMEASUREITEMSTRUCT)lParam)
|
|
|
|
if (lpmis->CtlType != ODT_MENU)
|
|
return NOERROR;
|
|
if (InRange(lpmis->itemID, SFVIDM_CLIENT_FIRST, SFVIDM_CLIENT_LAST) && m_pDefView->HasCB()) {
|
|
m_pDefView->CallCB(SFVM_MEASUREITEM, SFVIDM_CLIENT_FIRST, lParam);
|
|
return NOERROR;
|
|
}
|
|
break;
|
|
case WM_DRAWITEM:
|
|
#define lpdis ((LPDRAWITEMSTRUCT)lParam)
|
|
if (lpdis->CtlType != ODT_MENU)
|
|
return NOERROR;
|
|
if (InRange(lpdis->itemID, SFVIDM_CLIENT_FIRST, SFVIDM_CLIENT_LAST) && m_pDefView->HasCB()) {
|
|
m_pDefView->CallCB(SFVM_DRAWITEM, SFVIDM_CLIENT_FIRST, lParam);
|
|
return NOERROR;
|
|
}
|
|
#undef lpdis
|
|
break;
|
|
}
|
|
|
|
if (m_pcmSel) {
|
|
IContextMenu2* pcm2;
|
|
IContextMenu3* pcm3;
|
|
if (SUCCEEDED(m_pcmSel->QueryInterface(IID_IContextMenu3, (void**)&pcm3))) {
|
|
pcm3->HandleMenuMsg2(uMsg, wParam, lParam, plResult);
|
|
ATOMICRELEASE(pcm3);
|
|
} else if (SUCCEEDED(m_pcmSel->QueryInterface(IID_IContextMenu2, (void**)&pcm2))) {
|
|
pcm2->HandleMenuMsg(uMsg, wParam, lParam);
|
|
ATOMICRELEASE(pcm2);
|
|
}
|
|
}
|
|
|
|
return NOERROR;
|
|
}
|
|
|
|
|
|
STDMETHODIMP CBackgrndMenu::SetSite(IUnknown* pSite)
|
|
{
|
|
ATOMICRELEASE(m_powsSite);
|
|
|
|
m_powsSite = pSite;
|
|
|
|
if (pSite)
|
|
m_powsSite->AddRef();
|
|
|
|
if (m_pcmSel)
|
|
IUnknown_SetSite(SAFECAST(m_pcmSel, IContextMenu*), pSite);
|
|
|
|
return NOERROR;
|
|
}
|
|
|
|
|
|
STDMETHODIMP CBackgrndMenu::GetSite(REFIID riid, void** ppvObj)
|
|
{
|
|
if (m_powsSite)
|
|
return m_powsSite->QueryInterface(riid, ppvObj);
|
|
else {
|
|
ASSERT(ppvObj != NULL);
|
|
*ppvObj = NULL;
|
|
return E_NOINTERFACE;
|
|
}
|
|
}
|
|
|
|
|
|
BOOL ShowInfoTip()
|
|
{
|
|
// find out if infotips are on or off, from the registry settings
|
|
SHELLSTATE ss;
|
|
// force a refresh
|
|
SHGetSetSettings(&ss, 0, TRUE);
|
|
SHGetSetSettings(&ss, SSF_SHOWINFOTIP, FALSE);
|
|
return ss.fShowInfoTip;
|
|
}
|
|
|
|
|
|
CBackgroundColInfo::CBackgroundColInfo(LPCITEMIDLIST pidl, UINT uiCol, STRRET& strRet) : _pidl(pidl), _uiCol(uiCol)
|
|
{
|
|
StrRetToBuf(&strRet, NULL, const_cast<TCHAR*>(_szText), ARRAYSIZE(_szText));
|
|
}
|
|
|
|
|
|
CBackgroundColInfo::~CBackgroundColInfo(void)
|
|
{
|
|
ILFree(const_cast<LPITEMIDLIST>(_pidl));
|
|
}
|