reactos/dll/win32/shell32/CDefView.cpp
Whindmar Saksit 6d109254ab
[SHELL32] Correctly compare pidls for SHBrowseForFolder BFFM_SETEXPANDED (#7499)
_ILIsEqualSimple just does a memcmp and that does not work for all items.

Should improve shell change notifications (CORE-13950).
2024-11-16 16:28:28 +01:00

4695 lines
145 KiB
C++

/*
* ShellView
*
* Copyright 1998,1999 <juergen.schmied@debitel.net>
* Copyright 2022 Russell Johnson <russell.johnson@superdark.net>
*
* This is the view visualizing the data provided by the shellfolder.
* No direct access to data from pidls should be done from here.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*
* FIXME: CheckToolbar: handle the "new folder" and "folder up" button
*/
/*
TODO:
- When editing starts on item, set edit text to for editing value.
- Fix shell view to handle view mode popup exec.
- The background context menu should have a pidl just like foreground menus. This
causes crashes when dynamic handlers try to use the NULL pidl.
- Reorder of columns doesn't work - might be bug in comctl32
*/
#include "precomp.h"
#include <atlwin.h>
#include <ui/rosctrls.h>
WINE_DEFAULT_DEBUG_CHANNEL(shell);
// It would be easier to allocate these from 1 and up but the original code used the entire
// FCIDM_SHVIEWFIRST..FCIDM_SHVIEWLAST range when dealing with IContextMenu and to avoid
// breaking anything relying on this we will allocate our ranges from the end instead.
enum {
DEFVIEW_ARRANGESORT_MAXENUM = 9, // Limit the number of the visible columns we will display in the submenu
DEFVIEW_ARRANGESORT_MAX = DEFVIEW_ARRANGESORT_MAXENUM + 1, // Reserve one extra for the current sort-by column
DVIDM_ARRANGESORT_LAST = FCIDM_SHVIEWLAST,
DVIDM_ARRANGESORT_FIRST = DVIDM_ARRANGESORT_LAST - (DEFVIEW_ARRANGESORT_MAX - 1),
DVIDM_COMMDLG_SELECT = DVIDM_ARRANGESORT_FIRST - 1,
DVIDM_CONTEXTMENU_LAST = DVIDM_COMMDLG_SELECT - 1,
// FIXME: FCIDM_SHVIEWFIRST is 0 and using that with QueryContextMenu is a
// bad idea because it hides bugs related to the ids in ici.lpVerb.
// CONTEXT_MENU_BASE_ID acknowledges this but failed to apply the fix everywhere.
DVIDM_CONTEXTMENU_FIRST = FCIDM_SHVIEWFIRST,
};
#undef FCIDM_SHVIEWLAST // Don't use this constant, change DVIDM_CONTEXTMENU_LAST if you need a new id.
struct LISTVIEW_SORT_INFO
{
INT8 Direction;
bool bLoadedFromViewState;
bool bColumnIsFolderColumn;
UINT8 Reserved; // Unused
INT ListColumn;
enum { UNSPECIFIEDCOLUMN = -1 };
void Reset()
{
*(UINT*)this = 0;
ListColumn = UNSPECIFIEDCOLUMN;
}
};
#define SHV_CHANGE_NOTIFY (WM_USER + 0x1111)
#define SHV_UPDATESTATUSBAR (WM_USER + 0x1112)
// For the context menu of the def view, the id of the items are based on 1 because we need
// to call TrackPopupMenu and let it use the 0 value as an indication that the menu was canceled
#define CONTEXT_MENU_BASE_ID 1
struct PERSISTCOLUMNS
{
enum { MAXCOUNT = 100 };
static const UINT SIG = ('R' << 0) | ('O' << 8) | ('S' << 16) | (('c') << 24);
UINT Signature;
UINT Count;
UINT Columns[MAXCOUNT];
};
struct PERSISTCLASSICVIEWSTATE
{
static const UINT SIG = ('R' << 0) | ('O' << 8) | ('S' << 16) | (('c' ^ 'v' ^ 's') << 24);
UINT Signature;
WORD SortColId;
INT8 SortDir;
static const UINT VALIDFWF = FWF_AUTOARRANGE | FWF_SNAPTOGRID | FWF_NOGROUPING; // Note: The desktop applies FWF_NOICONS when appropriate
FOLDERSETTINGS FolderSettings;
};
static UINT
GetContextMenuFlags(IShellBrowser *pSB, SFGAOF sfgao)
{
UINT cmf = CMF_NORMAL;
if (GetKeyState(VK_SHIFT) < 0)
cmf |= CMF_EXTENDEDVERBS;
if (sfgao & SFGAO_CANRENAME)
cmf |= CMF_CANRENAME;
HWND hwnd = NULL;
if (pSB && SUCCEEDED(pSB->GetControlWindow(FCW_TREE, &hwnd)) && hwnd)
cmf |= CMF_EXPLORE;
return cmf;
}
// Convert client coordinates to listview coordinates
static void
ClientToListView(HWND hwndLV, POINT *ppt)
{
POINT Origin = {};
// FIXME: LVM_GETORIGIN is broken. See CORE-17266
if (!ListView_GetOrigin(hwndLV, &Origin))
return;
ppt->x += Origin.x;
ppt->y += Origin.y;
}
// Helper struct to automatically cleanup the IContextMenu
// We want to explicitly reset the Site, so there are no circular references
struct MenuCleanup
{
CComPtr<IContextMenu> &m_pCM;
HMENU &m_hMenu;
MenuCleanup(CComPtr<IContextMenu> &pCM, HMENU& menu)
: m_pCM(pCM), m_hMenu(menu)
{
}
~MenuCleanup()
{
if (m_hMenu)
{
DestroyMenu(m_hMenu);
m_hMenu = NULL;
}
if (m_pCM)
{
IUnknown_SetSite(m_pCM, NULL);
m_pCM.Release();
}
}
};
static BOOL AppendMenuItem(HMENU hMenu, UINT MF, UINT Id, LPCWSTR String, SIZE_T Data = 0)
{
MENUITEMINFOW mii;
mii.cbSize = sizeof(mii);
mii.fMask = MIIM_TYPE | MIIM_ID | MIIM_DATA | MIIM_STATE;
mii.fState = MF & (MFS_CHECKED | MFS_DISABLED);
mii.fType = MF & ~mii.fState;
mii.wID = Id;
mii.dwTypeData = const_cast<LPWSTR>(String);
mii.dwItemData = Data;
return InsertMenuItemW(hMenu, -1, TRUE, &mii);
}
static SIZE_T GetMenuItemDataById(HMENU hMenu, UINT Id)
{
MENUITEMINFOW mii;
mii.cbSize = FIELD_OFFSET(MENUITEMINFOW, hbmpItem);
mii.fMask = MIIM_DATA;
if (GetMenuItemInfoW(hMenu, Id, FALSE, &mii))
return mii.dwItemData;
else
return 0;
}
static HMENU GetSubmenuByID(HMENU hmenu, UINT id)
{
MENUITEMINFOW mii = {sizeof(mii), MIIM_SUBMENU};
if (::GetMenuItemInfoW(hmenu, id, FALSE, &mii))
return mii.hSubMenu;
return NULL;
}
/* ReallyGetMenuItemID returns the id of an item even if it opens a submenu,
GetMenuItemID returns -1 if the specified item opens a submenu */
static UINT ReallyGetMenuItemID(HMENU hmenu, int i)
{
MENUITEMINFOW mii = {sizeof(mii), MIIM_ID};
if (::GetMenuItemInfoW(hmenu, i, TRUE, &mii))
return mii.wID;
return UINT_MAX;
}
static UINT CalculateCharWidth(HWND hwnd)
{
UINT ret = 0;
HDC hDC = GetDC(hwnd);
if (hDC)
{
HGDIOBJ hOrg = SelectObject(hDC, (HGDIOBJ)SendMessage(hwnd, WM_GETFONT, 0, 0));
ret = GdiGetCharDimensions(hDC, NULL, NULL);
SelectObject(hDC, hOrg);
ReleaseDC(hwnd, hDC);
}
return ret;
}
static inline COLORREF GetViewColor(COLORREF Clr, UINT SysFallback)
{
return Clr != CLR_INVALID ? Clr : GetSysColor(SysFallback);
}
class CDefView :
public CWindowImpl<CDefView, CWindow, CControlWinTraits>,
public CComObjectRootEx<CComMultiThreadModelNoCS>,
public IShellView3,
public IFolderView,
public IShellFolderView,
public IOleCommandTarget,
public IDropTarget,
public IDropSource,
public IViewObject,
public IServiceProvider
{
private:
CComPtr<IShellFolder> m_pSFParent;
CComPtr<IShellFolder2> m_pSF2Parent;
CComPtr<IShellDetails> m_pSDParent;
CComPtr<IShellFolderViewCB> m_pShellFolderViewCB;
CComPtr<IShellBrowser> m_pShellBrowser;
CComPtr<ICommDlgBrowser> m_pCommDlgBrowser;
CComPtr<IFolderFilter> m_pFolderFilter;
CComPtr<IShellFolderViewDual> m_pShellFolderViewDual;
CListView m_ListView;
HWND m_hWndParent;
FOLDERSETTINGS m_FolderSettings;
HMENU m_hMenu; // Handle to the menu bar of the browser
HMENU m_hMenuArrangeModes; // Handle to the popup menu with the arrange modes
HMENU m_hMenuViewModes; // Handle to the popup menu with the view modes
HMENU m_hContextMenu; // Handle to the open context menu
BOOL m_bmenuBarInitialized;
UINT m_uState;
UINT m_cidl;
PCUITEMID_CHILD *m_apidl;
PIDLIST_ABSOLUTE m_pidlParent;
HDPA m_LoadColumnsList;
HDPA m_ListToFolderColMap;
LISTVIEW_SORT_INFO m_sortInfo;
ULONG m_hNotify; // Change notification handle
HACCEL m_hAccel;
DWORD m_dwAspects;
DWORD m_dwAdvf;
CComPtr<IAdviseSink> m_pAdvSink;
// for drag and drop
CComPtr<IDataObject> m_pSourceDataObject;
CComPtr<IDropTarget> m_pCurDropTarget; // The sub-item, which is currently dragged over
CComPtr<IDataObject> m_pCurDataObject; // The dragged data-object
LONG m_iDragOverItem; // Dragged over item's index, if m_pCurDropTarget != NULL
UINT m_cScrollDelay; // Send a WM_*SCROLL msg every 250 ms during drag-scroll
POINT m_ptLastMousePos; // Mouse position at last DragOver call
POINT m_ptFirstMousePos; // Mouse position when the drag operation started
DWORD m_grfKeyState;
//
CComPtr<IContextMenu> m_pCM;
CComPtr<IContextMenu> m_pFileMenu;
BOOL m_isEditing;
BOOL m_isParentFolderSpecial;
bool m_ScheduledStatusbarUpdate;
CLSID m_Category;
BOOL m_Destroyed;
SFVM_CUSTOMVIEWINFO_DATA m_viewinfo_data;
HICON m_hMyComputerIcon;
HRESULT _MergeToolbar();
BOOL _Sort(int Col = -1);
HRESULT _DoFolderViewCB(UINT uMsg, WPARAM wParam, LPARAM lParam);
HRESULT _GetSnapToGrid();
void _MoveSelectionOnAutoArrange(POINT pt);
INT _FindInsertableIndexFromPoint(POINT pt);
void _HandleStatusBarResize(int width);
void _ForceStatusBarResize();
void _DoCopyToMoveToFolder(BOOL bCopy);
BOOL IsDesktop() const { return m_FolderSettings.fFlags & FWF_DESKTOP; }
public:
CDefView();
~CDefView();
HRESULT WINAPI Initialize(IShellFolder *shellFolder);
HRESULT IncludeObject(PCUITEMID_CHILD pidl);
HRESULT OnDefaultCommand();
HRESULT OnStateChange(UINT uFlags);
void UpdateStatusbar();
void CheckToolbar();
BOOL CreateList();
void UpdateListColors();
BOOL InitList();
static INT CALLBACK ListViewCompareItems(LPARAM lParam1, LPARAM lParam2, LPARAM lpData);
HRESULT MapFolderColumnToListColumn(UINT FoldCol);
HRESULT MapListColumnToFolderColumn(UINT ListCol);
HRESULT GetDetailsByFolderColumn(PCUITEMID_CHILD pidl, UINT FoldCol, SHELLDETAILS &sd);
HRESULT GetDetailsByListColumn(PCUITEMID_CHILD pidl, UINT ListCol, SHELLDETAILS &sd);
HRESULT LoadColumn(UINT FoldCol, UINT ListCol, BOOL Insert, UINT ForceWidth = 0);
HRESULT LoadColumns(SIZE_T *pColList = NULL, UINT ColListCount = 0);
void ColumnListChanged();
PCUITEMID_CHILD _PidlByItem(int i);
PCUITEMID_CHILD _PidlByItem(LVITEM& lvItem);
int LV_FindItemByPidl(PCUITEMID_CHILD pidl);
int LV_AddItem(PCUITEMID_CHILD pidl);
BOOLEAN LV_DeleteItem(PCUITEMID_CHILD pidl);
BOOLEAN LV_RenameItem(PCUITEMID_CHILD pidlOld, PCUITEMID_CHILD pidlNew);
BOOLEAN LV_UpdateItem(PCUITEMID_CHILD pidl);
void LV_RefreshIcon(INT iItem);
void LV_RefreshIcons();
static INT CALLBACK fill_list(LPVOID ptr, LPVOID arg);
HRESULT FillList(BOOL IsRefreshCommand = TRUE);
HRESULT FillFileMenu();
HRESULT FillEditMenu();
HRESULT FillViewMenu();
HRESULT FillArrangeAsMenu(HMENU hmenuArrange);
HRESULT CheckViewMode(HMENU hmenuView);
LRESULT DoColumnContextMenu(LRESULT lParam);
UINT GetSelections();
HRESULT OpenSelectedItems();
void OnDeactivate();
void DoActivate(UINT uState);
HRESULT drag_notify_subitem(DWORD grfKeyState, POINTL pt, DWORD *pdwEffect);
HRESULT InvokeContextMenuCommand(CComPtr<IContextMenu>& pCM, LPCSTR lpVerb, POINT* pt = NULL);
LRESULT OnExplorerCommand(UINT uCommand, BOOL bUseSelection);
FOLDERVIEWMODE GetDefaultViewMode();
HRESULT GetDefaultViewStream(DWORD Stgm, IStream **ppStream);
HRESULT LoadViewState();
HRESULT SaveViewState(IStream *pStream);
void UpdateFolderViewFlags();
DWORD GetCommDlgViewFlags()
{
CComPtr<ICommDlgBrowser2> pcdb2;
if (m_pCommDlgBrowser && SUCCEEDED(m_pCommDlgBrowser->QueryInterface(IID_PPV_ARG(ICommDlgBrowser2, &pcdb2))))
{
DWORD flags;
if (SUCCEEDED(pcdb2->GetViewFlags(&flags)))
return flags;
}
return 0;
}
// *** IOleWindow methods ***
STDMETHOD(GetWindow)(HWND *lphwnd) override;
STDMETHOD(ContextSensitiveHelp)(BOOL fEnterMode) override;
// *** IShellView methods ***
STDMETHOD(TranslateAccelerator)(MSG *pmsg) override;
STDMETHOD(EnableModeless)(BOOL fEnable) override;
STDMETHOD(UIActivate)(UINT uState) override;
STDMETHOD(Refresh)() override;
STDMETHOD(CreateViewWindow)(IShellView *psvPrevious, LPCFOLDERSETTINGS pfs, IShellBrowser *psb, RECT *prcView, HWND *phWnd) override;
STDMETHOD(DestroyViewWindow)() override;
STDMETHOD(GetCurrentInfo)(LPFOLDERSETTINGS pfs) override;
STDMETHOD(AddPropertySheetPages)(DWORD dwReserved, LPFNSVADDPROPSHEETPAGE pfn, LPARAM lparam) override;
STDMETHOD(SaveViewState)() override;
STDMETHOD(SelectItem)(PCUITEMID_CHILD pidlItem, SVSIF uFlags) override;
STDMETHOD(GetItemObject)(UINT uItem, REFIID riid, void **ppv) override;
// *** IShellView2 methods ***
STDMETHOD(GetView)(SHELLVIEWID *view_guid, ULONG view_type) override;
STDMETHOD(CreateViewWindow2)(LPSV2CVW2_PARAMS view_params) override;
STDMETHOD(HandleRename)(LPCITEMIDLIST new_pidl) override;
STDMETHOD(SelectAndPositionItem)(LPCITEMIDLIST item, UINT flags, POINT *point) override;
// *** IShellView3 methods ***
STDMETHOD(CreateViewWindow3)(
IShellBrowser *psb,
IShellView *psvPrevious,
SV3CVW3_FLAGS view_flags,
FOLDERFLAGS mask,
FOLDERFLAGS flags,
FOLDERVIEWMODE mode,
const SHELLVIEWID *view_id,
const RECT *prcView,
HWND *hwnd) override;
// *** IFolderView methods ***
STDMETHOD(GetCurrentViewMode)(UINT *pViewMode) override;
STDMETHOD(SetCurrentViewMode)(UINT ViewMode) override;
STDMETHOD(GetFolder)(REFIID riid, void **ppv) override;
STDMETHOD(Item)(int iItemIndex, PITEMID_CHILD *ppidl) override;
STDMETHOD(ItemCount)(UINT uFlags, int *pcItems) override;
STDMETHOD(Items)(UINT uFlags, REFIID riid, void **ppv) override;
STDMETHOD(GetSelectionMarkedItem)(int *piItem) override;
STDMETHOD(GetFocusedItem)(int *piItem) override;
STDMETHOD(GetItemPosition)(PCUITEMID_CHILD pidl, POINT *ppt) override;
STDMETHOD(GetSpacing)(POINT *ppt) override;
STDMETHOD(GetDefaultSpacing)(POINT *ppt) override;
STDMETHOD(GetAutoArrange)() override;
STDMETHOD(SelectItem)(int iItem, DWORD dwFlags) override;
STDMETHOD(SelectAndPositionItems)(UINT cidl, PCUITEMID_CHILD_ARRAY apidl, POINT *apt, DWORD dwFlags) override;
// *** IShellFolderView methods ***
STDMETHOD(Rearrange)(LPARAM sort) override;
STDMETHOD(GetArrangeParam)(LPARAM *sort) override;
STDMETHOD(ArrangeGrid)() override;
STDMETHOD(AutoArrange)() override;
STDMETHOD(AddObject)(PITEMID_CHILD pidl, UINT *item) override;
STDMETHOD(GetObject)(PITEMID_CHILD *pidl, UINT item) override;
STDMETHOD(RemoveObject)(PITEMID_CHILD pidl, UINT *item) override;
STDMETHOD(GetObjectCount)(UINT *count) override;
STDMETHOD(SetObjectCount)(UINT count, UINT flags) override;
STDMETHOD(UpdateObject)(PITEMID_CHILD pidl_old, PITEMID_CHILD pidl_new, UINT *item) override;
STDMETHOD(RefreshObject)(PITEMID_CHILD pidl, UINT *item) override;
STDMETHOD(SetRedraw)(BOOL redraw) override;
STDMETHOD(GetSelectedCount)(UINT *count) override;
STDMETHOD(GetSelectedObjects)(PCUITEMID_CHILD **pidl, UINT *items) override;
STDMETHOD(IsDropOnSource)(IDropTarget *drop_target) override;
STDMETHOD(GetDragPoint)(POINT *pt) override;
STDMETHOD(GetDropPoint)(POINT *pt) override;
STDMETHOD(MoveIcons)(IDataObject *obj) override;
STDMETHOD(SetItemPos)(PCUITEMID_CHILD pidl, POINT *pt) override;
STDMETHOD(IsBkDropTarget)(IDropTarget *drop_target) override;
STDMETHOD(SetClipboard)(BOOL move) override;
STDMETHOD(SetPoints)(IDataObject *obj) override;
STDMETHOD(GetItemSpacing)(ITEMSPACING *spacing) override;
STDMETHOD(SetCallback)(IShellFolderViewCB *new_cb, IShellFolderViewCB **old_cb) override;
STDMETHOD(Select)(UINT flags) override;
STDMETHOD(QuerySupport)(UINT *support) override;
STDMETHOD(SetAutomationObject)(IDispatch *disp) override;
// *** IOleCommandTarget methods ***
STDMETHOD(QueryStatus)(const GUID *pguidCmdGroup, ULONG cCmds, OLECMD prgCmds[], OLECMDTEXT *pCmdText) override;
STDMETHOD(Exec)(const GUID *pguidCmdGroup, DWORD nCmdID, DWORD nCmdexecopt, VARIANT *pvaIn, VARIANT *pvaOut) override;
// *** IDropTarget methods ***
STDMETHOD(DragEnter)(IDataObject *pDataObj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect) override;
STDMETHOD(DragOver)(DWORD grfKeyState, POINTL pt, DWORD *pdwEffect) override;
STDMETHOD(DragLeave)() override;
STDMETHOD(Drop)(IDataObject *pDataObj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect) override;
// *** IDropSource methods ***
STDMETHOD(QueryContinueDrag)(BOOL fEscapePressed, DWORD grfKeyState) override;
STDMETHOD(GiveFeedback)(DWORD dwEffect) override;
// *** IViewObject methods ***
STDMETHOD(Draw)(DWORD dwDrawAspect, LONG lindex, void *pvAspect, DVTARGETDEVICE *ptd,
HDC hdcTargetDev, HDC hdcDraw, LPCRECTL lprcBounds, LPCRECTL lprcWBounds,
BOOL (STDMETHODCALLTYPE *pfnContinue)(ULONG_PTR dwContinue), ULONG_PTR dwContinue) override;
STDMETHOD(GetColorSet)(DWORD dwDrawAspect, LONG lindex, void *pvAspect,
DVTARGETDEVICE *ptd, HDC hicTargetDev, LOGPALETTE **ppColorSet) override;
STDMETHOD(Freeze)(DWORD dwDrawAspect, LONG lindex, void *pvAspect, DWORD *pdwFreeze) override;
STDMETHOD(Unfreeze)(DWORD dwFreeze) override;
STDMETHOD(SetAdvise)(DWORD aspects, DWORD advf, IAdviseSink *pAdvSink) override;
STDMETHOD(GetAdvise)(DWORD *pAspects, DWORD *pAdvf, IAdviseSink **ppAdvSink) override;
// *** IServiceProvider methods ***
STDMETHOD(QueryService)(REFGUID guidService, REFIID riid, void **ppvObject) override;
// Message handlers
LRESULT OnShowWindow(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled);
LRESULT OnGetDlgCode(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled);
LRESULT OnDestroy(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled);
LRESULT OnEraseBackground(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled);
LRESULT OnPrintClient(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled);
LRESULT OnSysColorChange(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled);
LRESULT OnGetShellBrowser(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled);
LRESULT OnNCCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled);
LRESULT OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled);
LRESULT OnContextMenu(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled);
LRESULT OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled);
LRESULT OnActivate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled);
LRESULT OnSetFocus(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled);
LRESULT OnKillFocus(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled);
LRESULT OnCommand(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled);
LRESULT OnNotify(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled);
LRESULT OnChangeNotify(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled);
LRESULT OnUpdateStatusbar(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled);
LRESULT OnCustomItem(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled);
LRESULT OnSettingChange(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled);
LRESULT OnInitMenuPopup(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled);
virtual VOID OnFinalMessage(HWND) override;
static ATL::CWndClassInfo& GetWndClassInfo()
{
static ATL::CWndClassInfo wc =
{
{ sizeof(WNDCLASSEX), CS_PARENTDC, StartWindowProc,
0, 0, NULL, NULL,
LoadCursor(NULL, IDC_ARROW), NULL, NULL, L"SHELLDLL_DefView", NULL
},
NULL, NULL, IDC_ARROW, TRUE, 0, _T("")
};
return wc;
}
virtual WNDPROC GetWindowProc()
{
return WindowProc;
}
static LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
CDefView *pThis;
LRESULT result;
// Must hold a reference during message handling
pThis = reinterpret_cast<CDefView *>(hWnd);
pThis->AddRef();
result = CWindowImpl<CDefView, CWindow, CControlWinTraits>::WindowProc(hWnd, uMsg, wParam, lParam);
pThis->Release();
return result;
}
BEGIN_MSG_MAP(CDefView)
MESSAGE_HANDLER(WM_SIZE, OnSize)
MESSAGE_HANDLER(WM_SETFOCUS, OnSetFocus)
MESSAGE_HANDLER(WM_KILLFOCUS, OnKillFocus)
MESSAGE_HANDLER(WM_NCCREATE, OnNCCreate)
MESSAGE_HANDLER(WM_CREATE, OnCreate)
MESSAGE_HANDLER(WM_ACTIVATE, OnActivate)
MESSAGE_HANDLER(WM_NOTIFY, OnNotify)
MESSAGE_HANDLER(WM_COMMAND, OnCommand)
MESSAGE_HANDLER(SHV_CHANGE_NOTIFY, OnChangeNotify)
MESSAGE_HANDLER(SHV_UPDATESTATUSBAR, OnUpdateStatusbar)
MESSAGE_HANDLER(WM_CONTEXTMENU, OnContextMenu)
MESSAGE_HANDLER(WM_DRAWITEM, OnCustomItem)
MESSAGE_HANDLER(WM_MEASUREITEM, OnCustomItem)
MESSAGE_HANDLER(WM_SHOWWINDOW, OnShowWindow)
MESSAGE_HANDLER(WM_GETDLGCODE, OnGetDlgCode)
MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBackground)
MESSAGE_HANDLER(WM_PRINTCLIENT, OnPrintClient)
MESSAGE_HANDLER(WM_SYSCOLORCHANGE, OnSysColorChange)
MESSAGE_HANDLER(CWM_GETISHELLBROWSER, OnGetShellBrowser)
MESSAGE_HANDLER(WM_SETTINGCHANGE, OnSettingChange)
MESSAGE_HANDLER(WM_INITMENUPOPUP, OnInitMenuPopup)
END_MSG_MAP()
BEGIN_COM_MAP(CDefView)
// Windows returns E_NOINTERFACE for IOleWindow
// COM_INTERFACE_ENTRY_IID(IID_IOleWindow, IOleWindow)
COM_INTERFACE_ENTRY_IID(IID_IShellView, IShellView)
COM_INTERFACE_ENTRY_IID(IID_CDefView, IShellView)
COM_INTERFACE_ENTRY_IID(IID_IShellView2, IShellView2)
COM_INTERFACE_ENTRY_IID(IID_IShellView3, IShellView3)
COM_INTERFACE_ENTRY_IID(IID_IFolderView, IFolderView)
COM_INTERFACE_ENTRY_IID(IID_IShellFolderView, IShellFolderView)
COM_INTERFACE_ENTRY_IID(IID_IOleCommandTarget, IOleCommandTarget)
COM_INTERFACE_ENTRY_IID(IID_IDropTarget, IDropTarget)
COM_INTERFACE_ENTRY_IID(IID_IDropSource, IDropSource)
COM_INTERFACE_ENTRY_IID(IID_IViewObject, IViewObject)
COM_INTERFACE_ENTRY_IID(IID_IServiceProvider, IServiceProvider)
END_COM_MAP()
};
#define ID_LISTVIEW 1
// windowsx.h
#define GET_WM_COMMAND_ID(wp, lp) LOWORD(wp)
#define GET_WM_COMMAND_HWND(wp, lp) (HWND)(lp)
#define GET_WM_COMMAND_CMD(wp, lp) HIWORD(wp)
typedef void (CALLBACK *PFNSHGETSETTINGSPROC)(LPSHELLFLAGSTATE lpsfs, DWORD dwMask);
CDefView::CDefView() :
m_ListView(),
m_hWndParent(NULL),
m_hMenu(NULL),
m_hMenuArrangeModes(NULL),
m_hMenuViewModes(NULL),
m_hContextMenu(NULL),
m_bmenuBarInitialized(FALSE),
m_uState(0),
m_cidl(0),
m_apidl(NULL),
m_pidlParent(NULL),
m_LoadColumnsList(NULL),
m_hNotify(0),
m_hAccel(NULL),
m_dwAspects(0),
m_dwAdvf(0),
m_iDragOverItem(0),
m_cScrollDelay(0),
m_isEditing(FALSE),
m_isParentFolderSpecial(FALSE),
m_ScheduledStatusbarUpdate(false),
m_Destroyed(FALSE)
{
ZeroMemory(&m_FolderSettings, sizeof(m_FolderSettings));
ZeroMemory(&m_ptLastMousePos, sizeof(m_ptLastMousePos));
ZeroMemory(&m_Category, sizeof(m_Category));
m_viewinfo_data.clrText = CLR_INVALID;
m_viewinfo_data.clrTextBack = CLR_INVALID;
m_viewinfo_data.hbmBack = NULL;
m_sortInfo.Reset();
m_ListToFolderColMap = DPA_Create(0);
m_hMyComputerIcon = LoadIconW(shell32_hInstance, MAKEINTRESOURCEW(IDI_SHELL_COMPUTER_DESKTOP));
}
CDefView::~CDefView()
{
TRACE(" destroying IShellView(%p)\n", this);
_DoFolderViewCB(SFVM_VIEWRELEASE, 0, 0);
if (m_viewinfo_data.hbmBack)
{
::DeleteObject(m_viewinfo_data.hbmBack);
m_viewinfo_data.hbmBack = NULL;
}
if (m_hWnd)
{
DestroyViewWindow();
}
SHFree(m_apidl);
DPA_Destroy(m_LoadColumnsList);
DPA_Destroy(m_ListToFolderColMap);
}
HRESULT WINAPI CDefView::Initialize(IShellFolder *shellFolder)
{
m_pSFParent = shellFolder;
shellFolder->QueryInterface(IID_PPV_ARG(IShellFolder2, &m_pSF2Parent));
shellFolder->QueryInterface(IID_PPV_ARG(IShellDetails, &m_pSDParent));
return S_OK;
}
// ##### helperfunctions for communication with ICommDlgBrowser #####
HRESULT CDefView::IncludeObject(PCUITEMID_CHILD pidl)
{
HRESULT ret = S_OK;
if (m_pCommDlgBrowser && !(GetCommDlgViewFlags() & CDB2GVF_NOINCLUDEITEM))
{
TRACE("ICommDlgBrowser::IncludeObject pidl=%p\n", pidl);
ret = m_pCommDlgBrowser->IncludeObject(this, pidl);
TRACE("-- returns 0x%08x\n", ret);
}
else if (m_pFolderFilter)
{
ret = m_pFolderFilter->ShouldShow(m_pSFParent, m_pidlParent, pidl);
}
return ret;
}
HRESULT CDefView::OnDefaultCommand()
{
HRESULT ret = S_FALSE;
if (m_pCommDlgBrowser.p != NULL)
{
TRACE("ICommDlgBrowser::OnDefaultCommand\n");
ret = m_pCommDlgBrowser->OnDefaultCommand(this);
TRACE("-- returns 0x%08x\n", ret);
}
return ret;
}
HRESULT CDefView::OnStateChange(UINT uFlags)
{
HRESULT ret = S_FALSE;
if (m_pCommDlgBrowser.p != NULL)
{
TRACE("ICommDlgBrowser::OnStateChange flags=%x\n", uFlags);
ret = m_pCommDlgBrowser->OnStateChange(this, uFlags);
TRACE("--\n");
}
return ret;
}
/**********************************************************
* set the toolbar of the filedialog buttons
*
* - activates the buttons from the shellbrowser according to
* the view state
*/
void CDefView::CheckToolbar()
{
LRESULT result;
TRACE("\n");
if (m_pCommDlgBrowser != NULL)
{
m_pShellBrowser->SendControlMsg(FCW_TOOLBAR, TB_CHECKBUTTON,
FCIDM_TB_SMALLICON, (m_FolderSettings.ViewMode == FVM_LIST) ? TRUE : FALSE, &result);
m_pShellBrowser->SendControlMsg(FCW_TOOLBAR, TB_CHECKBUTTON,
FCIDM_TB_REPORTVIEW, (m_FolderSettings.ViewMode == FVM_DETAILS) ? TRUE : FALSE, &result);
m_pShellBrowser->SendControlMsg(FCW_TOOLBAR, TB_ENABLEBUTTON,
FCIDM_TB_SMALLICON, TRUE, &result);
m_pShellBrowser->SendControlMsg(FCW_TOOLBAR, TB_ENABLEBUTTON,
FCIDM_TB_REPORTVIEW, TRUE, &result);
}
}
void CDefView::UpdateStatusbar()
{
WCHAR szFormat[MAX_PATH] = {0};
WCHAR szPartText[MAX_PATH] = {0};
UINT cSelectedItems;
if (!m_ListView)
return;
cSelectedItems = m_ListView.GetSelectedCount();
if (cSelectedItems)
{
LoadStringW(shell32_hInstance, IDS_OBJECTS_SELECTED, szFormat, _countof(szFormat));
StringCchPrintfW(szPartText, _countof(szPartText), szFormat, cSelectedItems);
}
else
{
LoadStringW(shell32_hInstance, IDS_OBJECTS, szFormat, _countof(szFormat));
StringCchPrintfW(szPartText, _countof(szPartText), szFormat, m_ListView.GetItemCount());
}
LRESULT lResult;
m_pShellBrowser->SendControlMsg(FCW_STATUS, SB_SETTEXT, 0, (LPARAM)szPartText, &lResult);
// Don't bother with the extra processing if we only have one StatusBar part
if (!m_isParentFolderSpecial)
{
UINT64 uTotalFileSize = 0;
WORD uFileFlags = LVNI_ALL;
LPARAM pIcon = NULL;
INT nItem = -1;
bool bIsOnlyFoldersSelected = true;
// If we have something selected then only count selected file sizes
if (cSelectedItems)
{
uFileFlags = LVNI_SELECTED;
}
while ((nItem = m_ListView.GetNextItem(nItem, uFileFlags)) >= 0)
{
PCUITEMID_CHILD pidl = _PidlByItem(nItem);
uTotalFileSize += _ILGetFileSize(pidl, NULL, 0);
if (!_ILIsFolder(pidl))
{
bIsOnlyFoldersSelected = false;
}
}
// Don't show the file size text if there is 0 bytes in the folder
// OR we only have folders selected
if ((cSelectedItems && !bIsOnlyFoldersSelected) || uTotalFileSize)
{
StrFormatByteSizeW(uTotalFileSize, szPartText, _countof(szPartText));
}
else
{
*szPartText = 0;
}
m_pShellBrowser->SendControlMsg(FCW_STATUS, SB_SETTEXT, 1, (LPARAM)szPartText, &lResult);
// If we are in a Recycle Bin then show no text for the location part
if (!_ILIsBitBucket(m_pidlParent))
{
LoadStringW(shell32_hInstance, IDS_MYCOMPUTER, szPartText, _countof(szPartText));
pIcon = (LPARAM)m_hMyComputerIcon;
}
m_pShellBrowser->SendControlMsg(FCW_STATUS, SB_SETICON, 2, pIcon, &lResult);
m_pShellBrowser->SendControlMsg(FCW_STATUS, SB_SETTEXT, 2, (LPARAM)szPartText, &lResult);
}
SFGAOF att = 0;
if (cSelectedItems > 0)
{
UINT maxquery = 42; // Checking the attributes can be slow, only check small selections (_DoCopyToMoveToFolder will verify the full array)
att = SFGAO_CANCOPY | SFGAO_CANMOVE;
if (cSelectedItems <= maxquery && (!GetSelections() || FAILED(m_pSFParent->GetAttributesOf(m_cidl, m_apidl, &att))))
att = 0;
}
m_pShellBrowser->SendControlMsg(FCW_TOOLBAR, TB_ENABLEBUTTON, FCIDM_SHVIEW_COPYTO, (att & SFGAO_CANCOPY) != 0, &lResult);
m_pShellBrowser->SendControlMsg(FCW_TOOLBAR, TB_ENABLEBUTTON, FCIDM_SHVIEW_MOVETO, (att & SFGAO_CANMOVE) != 0, &lResult);
}
LRESULT CDefView::OnUpdateStatusbar(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
{
m_ScheduledStatusbarUpdate = false;
UpdateStatusbar();
return 0;
}
// ##### helperfunctions for initializing the view #####
// creates the list view window
BOOL CDefView::CreateList()
{
HRESULT hr;
DWORD dwStyle, dwExStyle, ListExStyle;
UINT ViewMode;
TRACE("%p\n", this);
dwStyle = WS_TABSTOP | WS_VISIBLE | WS_CHILDWINDOW | WS_CLIPSIBLINGS | WS_CLIPCHILDREN |
LVS_SHAREIMAGELISTS | LVS_EDITLABELS | LVS_AUTOARRANGE; // FIXME: Remove LVS_AUTOARRANGE when the view is able to save ItemPos
dwExStyle = (m_FolderSettings.fFlags & FWF_NOCLIENTEDGE) ? 0 : WS_EX_CLIENTEDGE;
ListExStyle = LVS_EX_INFOTIP | LVS_EX_LABELTIP;
if (m_FolderSettings.fFlags & FWF_DESKTOP)
{
m_FolderSettings.fFlags |= FWF_NOCLIENTEDGE | FWF_NOSCROLL;
dwStyle |= LVS_ALIGNLEFT;
// LVS_EX_REGIONAL?
}
else
{
dwStyle |= LVS_SHOWSELALWAYS; // MSDN says FWF_SHOWSELALWAYS is deprecated, always turn on for folders
dwStyle |= (m_FolderSettings.fFlags & FWF_ALIGNLEFT) ? LVS_ALIGNLEFT : LVS_ALIGNTOP;
ListExStyle |= LVS_EX_DOUBLEBUFFER;
}
ViewMode = m_FolderSettings.ViewMode;
hr = _DoFolderViewCB(SFVM_DEFVIEWMODE, 0, (LPARAM)&ViewMode);
if (SUCCEEDED(hr))
{
if (ViewMode >= FVM_FIRST && ViewMode <= FVM_LAST)
m_FolderSettings.ViewMode = ViewMode;
else
ERR("Ignoring invalid ViewMode from SFVM_DEFVIEWMODE: %u (was: %u)\n", ViewMode, m_FolderSettings.ViewMode);
}
switch (m_FolderSettings.ViewMode)
{
case FVM_ICON:
dwStyle |= LVS_ICON;
break;
case FVM_DETAILS:
dwStyle |= LVS_REPORT;
break;
case FVM_SMALLICON:
dwStyle |= LVS_SMALLICON;
break;
case FVM_LIST:
dwStyle |= LVS_LIST;
break;
default:
dwStyle |= LVS_LIST;
break;
}
if (m_FolderSettings.fFlags & FWF_AUTOARRANGE)
dwStyle |= LVS_AUTOARRANGE;
if (m_FolderSettings.fFlags & FWF_SNAPTOGRID)
ListExStyle |= LVS_EX_SNAPTOGRID;
if (m_FolderSettings.fFlags & FWF_SINGLESEL)
dwStyle |= LVS_SINGLESEL;
if (m_FolderSettings.fFlags & FWF_FULLROWSELECT)
ListExStyle |= LVS_EX_FULLROWSELECT;
if ((m_FolderSettings.fFlags & FWF_SINGLECLICKACTIVATE) ||
(!SHELL_GetSetting(SSF_DOUBLECLICKINWEBVIEW, fDoubleClickInWebView) && !SHELL_GetSetting(SSF_WIN95CLASSIC, fWin95Classic)))
ListExStyle |= LVS_EX_TRACKSELECT | LVS_EX_ONECLICKACTIVATE;
if (m_FolderSettings.fFlags & FWF_NOCOLUMNHEADER)
dwStyle |= LVS_NOCOLUMNHEADER;
#if 0
// FIXME: Because this is a negative, everyone gets the new flag by default unless they
// opt out. This code should be enabled when shell looks like Vista instead of 2003
if (!(m_FolderSettings.fFlags & FWF_NOHEADERINALLVIEWS))
ListExStyle |= LVS_EX_HEADERINALLVIEWS;
#endif
if (m_FolderSettings.fFlags & FWF_NOCLIENTEDGE)
dwExStyle &= ~WS_EX_CLIENTEDGE;
RECT rcListView = {0,0,0,0};
m_ListView.Create(m_hWnd, rcListView, L"FolderView", dwStyle, dwExStyle, ID_LISTVIEW);
if (!m_ListView)
return FALSE;
m_ListView.SetExtendedListViewStyle(ListExStyle);
/* UpdateShellSettings(); */
return TRUE;
}
void CDefView::UpdateListColors()
{
if (m_FolderSettings.fFlags & FWF_DESKTOP)
{
/* Check if drop shadows option is enabled */
BOOL bDropShadow = FALSE;
DWORD cbDropShadow = sizeof(bDropShadow);
/*
* The desktop ListView always take the default desktop colours, by
* remaining transparent and letting user32/win32k paint itself the
* desktop background color, if any.
*/
m_ListView.SetBkColor(CLR_NONE);
SHGetValueW(HKEY_CURRENT_USER, L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Advanced",
L"ListviewShadow", NULL, &bDropShadow, &cbDropShadow);
if (bDropShadow)
{
/* Set the icon background transparent */
m_ListView.SetTextBkColor(CLR_NONE);
m_ListView.SetTextColor(RGB(255, 255, 255));
m_ListView.SetExtendedListViewStyle(LVS_EX_TRANSPARENTSHADOWTEXT, LVS_EX_TRANSPARENTSHADOWTEXT);
}
else
{
/* Set the icon background as the same colour as the desktop */
COLORREF crDesktop = GetSysColor(COLOR_DESKTOP);
m_ListView.SetTextBkColor(crDesktop);
if (GetRValue(crDesktop) + GetGValue(crDesktop) + GetBValue(crDesktop) > 128 * 3)
m_ListView.SetTextColor(RGB(0, 0, 0));
else
m_ListView.SetTextColor(RGB(255, 255, 255));
m_ListView.SetExtendedListViewStyle(0, LVS_EX_TRANSPARENTSHADOWTEXT);
}
}
else
{
m_ListView.SetTextBkColor(GetViewColor(m_viewinfo_data.clrTextBack, COLOR_WINDOW));
m_ListView.SetTextColor(GetViewColor(m_viewinfo_data.clrText, COLOR_WINDOWTEXT));
// Background is painted by the parent via WM_PRINTCLIENT
m_ListView.SetExtendedListViewStyle(LVS_EX_TRANSPARENTBKGND, LVS_EX_TRANSPARENTBKGND);
}
}
// adds all needed columns to the shellview
BOOL CDefView::InitList()
{
HIMAGELIST big_icons, small_icons;
TRACE("%p\n", this);
m_ListView.DeleteAllItems();
Shell_GetImageLists(&big_icons, &small_icons);
m_ListView.SetImageList(big_icons, LVSIL_NORMAL);
m_ListView.SetImageList(small_icons, LVSIL_SMALL);
m_hMenuArrangeModes = CreateMenu();
SIZE_T *pColumns = m_LoadColumnsList ? (SIZE_T*)DPA_GetPtrPtr(m_LoadColumnsList) : NULL;
UINT ColumnCount = pColumns ? DPA_GetPtrCount(m_LoadColumnsList) : 0;
LoadColumns(pColumns, ColumnCount);
if (m_sortInfo.bColumnIsFolderColumn)
{
m_sortInfo.bColumnIsFolderColumn = FALSE;
HRESULT hr = MapFolderColumnToListColumn(m_sortInfo.ListColumn);
m_sortInfo.ListColumn = SUCCEEDED(hr) ? hr : 0;
}
return TRUE;
}
/**********************************************************
* Column handling
*/
static HRESULT SHGetLVColumnSubItem(HWND List, UINT Col)
{
LVCOLUMN lvc;
lvc.mask = LVCF_SUBITEM;
if (!ListView_GetColumn(List, Col, &lvc))
return E_FAIL;
else
return lvc.iSubItem;
}
HRESULT CDefView::MapFolderColumnToListColumn(UINT FoldCol)
{
// This function is only called during column management, performance is not critical.
for (UINT i = 0;; ++i)
{
HRESULT r = SHGetLVColumnSubItem(m_ListView.m_hWnd, i);
if ((UINT)r == FoldCol)
return i;
else if (FAILED(r))
return r;
}
}
HRESULT CDefView::MapListColumnToFolderColumn(UINT ListCol)
{
// This function is called every time a LVITEM::iSubItem is mapped to
// a folder column (calls to GetDetailsOf etc.) and should be fast.
if (m_ListToFolderColMap)
{
UINT count = DPA_GetPtrCount(m_ListToFolderColMap);
if (ListCol < count)
{
HRESULT col = (INT)(INT_PTR)DPA_FastGetPtr(m_ListToFolderColMap, ListCol);
assert(col >= 0 && col == SHGetLVColumnSubItem(m_ListView.m_hWnd, ListCol));
return col;
}
else if (count)
{
TRACE("m_ListToFolderColMap cache miss while mapping %d\n", ListCol);
}
}
return SHGetLVColumnSubItem(m_ListView.m_hWnd, ListCol);
}
HRESULT CDefView::GetDetailsByFolderColumn(PCUITEMID_CHILD pidl, UINT FoldCol, SHELLDETAILS &sd)
{
// According to learn.microsoft.com/en-us/windows/win32/shell/sfvm-getdetailsof
// the query order is IShellFolder2, IShellDetails, SFVM_GETDETAILSOF.
HRESULT hr;
if (m_pSF2Parent)
{
hr = m_pSF2Parent->GetDetailsOf(pidl, FoldCol, &sd);
}
if (FAILED(hr) && m_pSDParent)
{
hr = m_pSDParent->GetDetailsOf(pidl, FoldCol, &sd);
}
#if 0 // TODO
if (FAILED(hr))
{
FIXME("Try SFVM_GETDETAILSOF\n");
}
#endif
return hr;
}
HRESULT CDefView::GetDetailsByListColumn(PCUITEMID_CHILD pidl, UINT ListCol, SHELLDETAILS &sd)
{
HRESULT hr = MapListColumnToFolderColumn(ListCol);
if (SUCCEEDED(hr))
return GetDetailsByFolderColumn(pidl, hr, sd);
ERR("Unable to determine folder column from list column %d\n", (int) ListCol);
return hr;
}
HRESULT CDefView::LoadColumn(UINT FoldCol, UINT ListCol, BOOL Insert, UINT ForceWidth)
{
WCHAR buf[MAX_PATH];
SHELLDETAILS sd;
HRESULT hr;
sd.str.uType = !STRRET_WSTR; // Make sure "uninitialized" uType is not WSTR
hr = GetDetailsByFolderColumn(NULL, FoldCol, sd);
if (FAILED(hr))
return hr;
hr = StrRetToStrNW(buf, _countof(buf), &sd.str, NULL);
if (FAILED(hr))
return hr;
UINT chavewidth = CalculateCharWidth(m_ListView.m_hWnd);
if (!chavewidth)
chavewidth = 6; // 6 is a reasonable default fallback
LVCOLUMN lvc;
lvc.mask = LVCF_TEXT | LVCF_FMT | LVCF_WIDTH | LVCF_SUBITEM;
lvc.pszText = buf;
lvc.fmt = sd.fmt;
lvc.cx = ForceWidth ? ForceWidth : (sd.cxChar * chavewidth); // FIXME: DPI?
lvc.iSubItem = FoldCol; // Used by MapFolderColumnToListColumn & MapListColumnToFolderColumn
if ((int)ListCol == -1)
{
assert(Insert); // You can insert at the end but you can't change something that is not there
if (Insert)
ListCol = 0x7fffffff;
}
if (Insert)
ListView_InsertColumn(m_ListView.m_hWnd, ListCol, &lvc);
else
ListView_SetColumn(m_ListView.m_hWnd, ListCol, &lvc);
return S_OK;
}
HRESULT CDefView::LoadColumns(SIZE_T *pColList, UINT ColListCount)
{
HWND hWndHdr = ListView_GetHeader(m_ListView.m_hWnd);
UINT newColCount = 0, oldColCount = Header_GetItemCount(hWndHdr);
UINT width = 0, foldCol, i;
HRESULT hr = S_FALSE;
m_ListView.SetRedraw(FALSE);
for (i = 0, foldCol = 0;; ++foldCol)
{
if (newColCount >= 0xffff)
break; // CompareIDs limit reached
if (pColList)
{
if (i >= ColListCount)
break;
width = HIWORD(pColList[i]);
foldCol = LOWORD(pColList[i++]);
}
SHCOLSTATEF state = 0;
if (!m_pSF2Parent || FAILED(m_pSF2Parent->GetDefaultColumnState(foldCol, &state)))
state = SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT;
if (foldCol == 0)
{
// Force the first column
}
else if (state & SHCOLSTATE_HIDDEN)
{
continue;
}
else if (!pColList && !(state & SHCOLSTATE_ONBYDEFAULT))
{
continue;
}
bool insert = newColCount >= oldColCount;
UINT listCol = insert ? -1 : newColCount;
hr = LoadColumn(foldCol, listCol, insert, width);
if (FAILED(hr))
{
if (!pColList)
hr = S_OK; // No more items, we are done
break;
}
++newColCount;
}
for (i = newColCount; i < oldColCount; ++i)
{
ListView_DeleteColumn(m_ListView.m_hWnd, i);
}
m_ListView.SetRedraw(TRUE);
ColumnListChanged();
assert(SUCCEEDED(MapFolderColumnToListColumn(0))); // We don't allow turning off the Name column
if (m_LoadColumnsList)
{
DPA_Destroy(m_LoadColumnsList);
m_LoadColumnsList = NULL;
}
return hr;
}
void CDefView::ColumnListChanged()
{
HDPA cache = m_ListToFolderColMap;
m_ListToFolderColMap = NULL; // No cache while we are building the cache
DPA_DeleteAllPtrs(cache);
for (UINT i = 0;; ++i)
{
HRESULT hr = MapListColumnToFolderColumn(i);
if (FAILED(hr))
break; // No more columns
if (!DPA_SetPtr(cache, i, (void*)(INT_PTR) hr))
break; // Cannot allow holes in the cache, must stop now.
}
m_ListToFolderColMap = cache;
for (;;)
{
if (!RemoveMenu(m_hMenuArrangeModes, 0, MF_BYPOSITION))
break;
}
HMENU hMenu = GetSubmenuByID(m_hMenu, FCIDM_MENU_VIEW);
if (hMenu)
{
hMenu = GetSubmenuByID(hMenu, FCIDM_SHVIEW_ARRANGE);
for (UINT i = DVIDM_ARRANGESORT_FIRST; i <= DVIDM_ARRANGESORT_LAST && hMenu; ++i)
{
RemoveMenu(hMenu, i, MF_BYCOMMAND);
}
if ((int) GetMenuItemID(hMenu, 0) <= 0)
RemoveMenu(hMenu, 0, MF_BYPOSITION); // Separator
}
WCHAR buf[MAX_PATH];
LVCOLUMN lvc;
lvc.mask = LVCF_TEXT;
lvc.pszText = buf;
lvc.cchTextMax = _countof(buf);
for (UINT listCol = 0; listCol < DEFVIEW_ARRANGESORT_MAXENUM; ++listCol)
{
if (!ListView_GetColumn(m_ListView.m_hWnd, listCol, &lvc))
break;
HRESULT foldCol = MapListColumnToFolderColumn(listCol);
assert(SUCCEEDED(foldCol));
AppendMenuItem(m_hMenuArrangeModes, MF_STRING,
DVIDM_ARRANGESORT_FIRST + listCol, lvc.pszText, listCol);
}
ListView_RedrawItems(m_ListView.m_hWnd, 0, 0x7fffffff);
m_ListView.InvalidateRect(NULL, TRUE);
}
/*************************************************************************
* ShellView_ListViewCompareItems
*
* Compare Function for the Listview (FileOpen Dialog)
*
* PARAMS
* lParam1 [I] the first ItemIdList to compare with
* lParam2 [I] the second ItemIdList to compare with
* lpData [I] The column ID for the header Ctrl to process
*
* RETURNS
* A negative value if the first item should precede the second,
* a positive value if the first item should follow the second,
* or zero if the two items are equivalent
*/
INT CALLBACK CDefView::ListViewCompareItems(LPARAM lParam1, LPARAM lParam2, LPARAM lpData)
{
PCUIDLIST_RELATIVE pidl1 = reinterpret_cast<PCUIDLIST_RELATIVE>(lParam1);
PCUIDLIST_RELATIVE pidl2 = reinterpret_cast<PCUIDLIST_RELATIVE>(lParam2);
CDefView *pThis = reinterpret_cast<CDefView*>(lpData);
HRESULT hres = pThis->m_pSFParent->CompareIDs(pThis->m_sortInfo.ListColumn, pidl1, pidl2);
if (FAILED_UNEXPECTEDLY(hres))
return 0;
SHORT nDiff = HRESULT_CODE(hres);
return nDiff * pThis->m_sortInfo.Direction;
}
BOOL CDefView::_Sort(int Col)
{
HWND hHeader;
HDITEM hColumn;
int prevCol = m_sortInfo.ListColumn;
m_sortInfo.bLoadedFromViewState = FALSE;
// FIXME: Is this correct? Who sets this style?
// And if it is set, should it also block sorting using the menu?
// Any why should it block sorting when the view is loaded initially?
if (m_ListView.GetWindowLongPtr(GWL_STYLE) & LVS_NOSORTHEADER)
return TRUE;
hHeader = ListView_GetHeader(m_ListView.m_hWnd);
if (Col != -1)
{
if (Col >= Header_GetItemCount(hHeader))
{
ERR("Sort column out of range\n");
return FALSE;
}
if (prevCol == Col)
m_sortInfo.Direction *= -1;
else
m_sortInfo.Direction = 0;
m_sortInfo.ListColumn = Col;
}
if (!m_sortInfo.Direction)
m_sortInfo.Direction += 1;
/* If the sorting column changed, remove the sorting style from the old column */
if (prevCol != -1 && prevCol != m_sortInfo.ListColumn)
{
hColumn.mask = HDI_FORMAT;
Header_GetItem(hHeader, prevCol, &hColumn);
hColumn.fmt &= ~(HDF_SORTUP | HDF_SORTDOWN);
Header_SetItem(hHeader, prevCol, &hColumn);
}
/* Set the sorting style on the new column */
hColumn.mask = HDI_FORMAT;
Header_GetItem(hHeader, m_sortInfo.ListColumn, &hColumn);
hColumn.fmt &= ~(HDF_SORTDOWN | HDF_SORTUP);
hColumn.fmt |= (m_sortInfo.Direction > 0 ? HDF_SORTUP : HDF_SORTDOWN);
Header_SetItem(hHeader, m_sortInfo.ListColumn, &hColumn);
/* Sort the list, using the current values of ListColumn and bIsAscending */
ASSERT(m_sortInfo.Direction == 1 || m_sortInfo.Direction == -1);
return m_ListView.SortItems(ListViewCompareItems, this);
}
PCUITEMID_CHILD CDefView::_PidlByItem(int i)
{
if (!m_ListView)
return nullptr;
return reinterpret_cast<PCUITEMID_CHILD>(m_ListView.GetItemData(i));
}
PCUITEMID_CHILD CDefView::_PidlByItem(LVITEM& lvItem)
{
if (!m_ListView)
return nullptr;
return reinterpret_cast<PCUITEMID_CHILD>(lvItem.lParam);
}
int CDefView::LV_FindItemByPidl(PCUITEMID_CHILD pidl)
{
ASSERT(m_ListView && m_pSFParent);
int cItems = m_ListView.GetItemCount();
LPARAM lParam = m_pSF2Parent ? SHCIDS_CANONICALONLY : 0;
for (int i = 0; i < cItems; i++)
{
PCUITEMID_CHILD currentpidl = _PidlByItem(i);
HRESULT hr = m_pSFParent->CompareIDs(lParam, pidl, currentpidl);
if (SUCCEEDED(hr))
{
if (hr == S_EQUAL)
return i;
}
else
{
for (i = 0; i < cItems; i++)
{
currentpidl = _PidlByItem(i);
if (ILIsEqual(pidl, currentpidl))
return i;
}
break;
}
}
return -1;
}
int CDefView::LV_AddItem(PCUITEMID_CHILD pidl)
{
LVITEMW lvItem;
TRACE("(%p)(pidl=%p)\n", this, pidl);
ASSERT(m_ListView);
if (_DoFolderViewCB(SFVM_ADDINGOBJECT, 0, (LPARAM)pidl) == S_FALSE)
return -1;
lvItem.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM; // set mask
lvItem.iItem = m_ListView.GetItemCount(); // add item to lists end
lvItem.iSubItem = 0;
lvItem.lParam = reinterpret_cast<LPARAM>(ILClone(pidl)); // set item's data
lvItem.pszText = LPSTR_TEXTCALLBACKW; // get text on a callback basis
lvItem.iImage = I_IMAGECALLBACK; // get image on a callback basis
lvItem.stateMask = LVIS_CUT;
return m_ListView.InsertItem(&lvItem);
}
BOOLEAN CDefView::LV_DeleteItem(PCUITEMID_CHILD pidl)
{
int nIndex;
TRACE("(%p)(pidl=%p)\n", this, pidl);
ASSERT(m_ListView);
nIndex = LV_FindItemByPidl(pidl);
if (nIndex < 0)
return FALSE;
_DoFolderViewCB(SFVM_REMOVINGOBJECT, 0, (LPARAM)pidl);
return m_ListView.DeleteItem(nIndex);
}
BOOLEAN CDefView::LV_RenameItem(PCUITEMID_CHILD pidlOld, PCUITEMID_CHILD pidlNew)
{
int nItem;
LVITEMW lvItem;
TRACE("(%p)(pidlold=%p pidlnew=%p)\n", this, pidlOld, pidlNew);
ASSERT(m_ListView);
nItem = LV_FindItemByPidl(pidlOld);
if (-1 != nItem)
{
lvItem.mask = LVIF_PARAM; // only the pidl
lvItem.iItem = nItem;
lvItem.iSubItem = 0;
m_ListView.GetItem(&lvItem);
// Store old pidl until new item is replaced
LPVOID oldPidl = reinterpret_cast<LPVOID>(lvItem.lParam);
lvItem.mask = LVIF_PARAM | LVIF_IMAGE | LVIF_TEXT;
lvItem.iItem = nItem;
lvItem.iSubItem = 0;
lvItem.lParam = reinterpret_cast<LPARAM>(ILClone(pidlNew)); // set item's data
lvItem.pszText = LPSTR_TEXTCALLBACKW;
lvItem.iImage = SHMapPIDLToSystemImageListIndex(m_pSFParent, pidlNew, 0);
m_ListView.SetItem(&lvItem);
m_ListView.Update(nItem);
// Now that the new item is in place, we can safely release the old pidl
SHFree(oldPidl);
return TRUE; // FIXME: better handling
}
return FALSE;
}
BOOLEAN CDefView::LV_UpdateItem(PCUITEMID_CHILD pidl)
{
int nItem;
LVITEMW lvItem;
TRACE("(%p)(pidl=%p)\n", this, pidl);
ASSERT(m_ListView);
nItem = LV_FindItemByPidl(pidl);
if (-1 != nItem)
{
_DoFolderViewCB(SFVM_UPDATINGOBJECT, 0, (LPARAM)pidl);
lvItem.mask = LVIF_IMAGE;
lvItem.iItem = nItem;
lvItem.iSubItem = 0;
lvItem.iImage = SHMapPIDLToSystemImageListIndex(m_pSFParent, pidl, 0);
m_ListView.SetItem(&lvItem);
m_ListView.Update(nItem);
return TRUE;
}
return FALSE;
}
void CDefView::LV_RefreshIcon(INT iItem)
{
ASSERT(m_ListView);
LVITEMW lvItem = { LVIF_IMAGE };
lvItem.iItem = iItem;
lvItem.iImage = I_IMAGECALLBACK;
m_ListView.SetItem(&lvItem);
m_ListView.Update(iItem);
}
void CDefView::LV_RefreshIcons()
{
ASSERT(m_ListView);
for (INT iItem = -1;;)
{
iItem = ListView_GetNextItem(m_ListView, iItem, LVNI_ALL);
if (iItem == -1)
break;
LV_RefreshIcon(iItem);
}
}
INT CALLBACK CDefView::fill_list(LPVOID ptr, LPVOID arg)
{
PITEMID_CHILD pidl = static_cast<PITEMID_CHILD>(ptr);
CDefView *pThis = static_cast<CDefView *>(arg);
// in a commdlg this works as a filemask
if (pThis->IncludeObject(pidl) == S_OK && pThis->m_ListView)
pThis->LV_AddItem(pidl);
SHFree(pidl);
return TRUE;
}
///
// - gets the objectlist from the shellfolder
// - sorts the list
// - fills the list into the view
HRESULT CDefView::FillList(BOOL IsRefreshCommand)
{
CComPtr<IEnumIDList> pEnumIDList;
PITEMID_CHILD pidl;
DWORD dwFetched;
HRESULT hRes;
HDPA hdpa;
DWORD dFlags = SHCONTF_NONFOLDERS | ((m_FolderSettings.fFlags & FWF_NOSUBFOLDERS) ? 0 : SHCONTF_FOLDERS);
TRACE("%p\n", this);
SHELLSTATE shellstate;
SHGetSetSettings(&shellstate, SSF_SHOWALLOBJECTS | SSF_SHOWSUPERHIDDEN, FALSE);
if (GetCommDlgViewFlags() & CDB2GVF_SHOWALLFILES)
shellstate.fShowAllObjects = shellstate.fShowSuperHidden = TRUE;
if (shellstate.fShowAllObjects)
{
dFlags |= SHCONTF_INCLUDEHIDDEN;
m_ListView.SendMessageW(LVM_SETCALLBACKMASK, LVIS_CUT, 0);
}
if (shellstate.fShowSuperHidden)
{
dFlags |= SHCONTF_INCLUDESUPERHIDDEN;
m_ListView.SendMessageW(LVM_SETCALLBACKMASK, LVIS_CUT, 0);
}
// get the itemlist from the shfolder
hRes = m_pSFParent->EnumObjects(m_hWnd, dFlags, &pEnumIDList);
if (hRes != S_OK)
{
if (hRes == S_FALSE)
return(NOERROR);
return(hRes);
}
// create a pointer array
hdpa = DPA_Create(16);
if (!hdpa)
return(E_OUTOFMEMORY);
// copy the items into the array
while((S_OK == pEnumIDList->Next(1, &pidl, &dwFetched)) && dwFetched)
{
if (DPA_InsertPtr(hdpa, 0x7fff, pidl) == -1)
{
SHFree(pidl);
}
}
// turn listview's redrawing off
m_ListView.SetRedraw(FALSE);
DPA_DestroyCallback( hdpa, fill_list, this);
/* sort the array */
int sortCol = -1;
if (!IsRefreshCommand && !m_sortInfo.bLoadedFromViewState) // Are we loading for the first time?
{
m_sortInfo.Direction = 0;
sortCol = 0; // In case the folder does not know/care
if (m_pSF2Parent)
{
ULONG folderSortCol = sortCol, dummy;
HRESULT hr = m_pSF2Parent->GetDefaultColumn(NULL, &folderSortCol, &dummy);
if (SUCCEEDED(hr))
hr = MapFolderColumnToListColumn(folderSortCol);
if (SUCCEEDED(hr))
sortCol = (int) hr;
}
}
_Sort(sortCol);
if (m_viewinfo_data.hbmBack)
{
::DeleteObject(m_viewinfo_data.hbmBack);
m_viewinfo_data.hbmBack = NULL;
}
// load custom background image and custom text color
m_viewinfo_data.cbSize = sizeof(m_viewinfo_data);
_DoFolderViewCB(SFVM_GET_CUSTOMVIEWINFO, 0, (LPARAM)&m_viewinfo_data);
// turn listview's redrawing back on and force it to draw
m_ListView.SetRedraw(TRUE);
UpdateListColors();
if (!(m_FolderSettings.fFlags & FWF_DESKTOP))
{
// redraw now
m_ListView.InvalidateRect(NULL, TRUE);
}
_DoFolderViewCB(SFVM_LISTREFRESHED, 0, 0);
return S_OK;
}
LRESULT CDefView::OnShowWindow(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
{
if (m_ListView.IsWindow())
m_ListView.UpdateWindow();
bHandled = FALSE;
return 0;
}
LRESULT CDefView::OnGetDlgCode(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
{
return m_ListView.SendMessageW(uMsg, 0, 0);
}
LRESULT CDefView::OnDestroy(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
{
if (!m_Destroyed)
{
m_Destroyed = TRUE;
if (m_hMenu)
{
DestroyMenu(m_hMenu);
m_hMenu = NULL;
}
RevokeDragDrop(m_hWnd);
SHChangeNotifyDeregister(m_hNotify);
m_hNotify = NULL;
SHFree(m_pidlParent);
m_pidlParent = NULL;
}
bHandled = FALSE;
return 0;
}
LRESULT CDefView::OnEraseBackground(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
{
/* redirect to parent */
if (m_FolderSettings.fFlags & (FWF_DESKTOP | FWF_TRANSPARENT))
return SendMessageW(GetParent(), WM_ERASEBKGND, wParam, lParam);
bHandled = FALSE;
return 0;
}
static VOID
DrawTileBitmap(HDC hDC, LPCRECT prc, HBITMAP hbm, INT nWidth, INT nHeight, INT dx, INT dy)
{
INT x0 = prc->left, y0 = prc->top, x1 = prc->right, y1 = prc->bottom;
x0 += dx;
y0 += dy;
HDC hMemDC = CreateCompatibleDC(hDC);
HGDIOBJ hbmOld = SelectObject(hMemDC, hbm);
for (INT y = y0; y < y1; y += nHeight)
{
for (INT x = x0; x < x1; x += nWidth)
{
BitBlt(hDC, x, y, nWidth, nHeight, hMemDC, 0, 0, SRCCOPY);
}
}
SelectObject(hMemDC, hbmOld);
DeleteDC(hMemDC);
}
LRESULT CDefView::OnPrintClient(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
{
HDC hDC = (HDC)wParam;
RECT rc;
::GetClientRect(m_ListView, &rc);
if (m_viewinfo_data.hbmBack)
{
BITMAP bm;
if (::GetObject(m_viewinfo_data.hbmBack, sizeof(BITMAP), &bm))
{
INT dx = -(::GetScrollPos(m_ListView, SB_HORZ) % bm.bmWidth);
INT dy = -(::GetScrollPos(m_ListView, SB_VERT) % bm.bmHeight);
DrawTileBitmap(hDC, &rc, m_viewinfo_data.hbmBack, bm.bmWidth, bm.bmHeight, dx, dy);
}
}
else
{
SHFillRectClr(hDC, &rc, GetViewColor(m_viewinfo_data.clrTextBack, COLOR_WINDOW));
}
bHandled = TRUE;
return TRUE;
}
LRESULT CDefView::OnSysColorChange(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
{
/* Update desktop labels color */
UpdateListColors();
/* Forward WM_SYSCOLORCHANGE to common controls */
return m_ListView.SendMessageW(uMsg, 0, 0);
}
LRESULT CDefView::OnGetShellBrowser(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
{
return reinterpret_cast<LRESULT>(m_pShellBrowser.p);
}
LRESULT CDefView::OnNCCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
{
this->AddRef();
bHandled = FALSE;
return 0;
}
VOID CDefView::OnFinalMessage(HWND)
{
this->Release();
}
LRESULT CDefView::OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
{
CComPtr<IDropTarget> pdt;
CComPtr<IPersistFolder2> ppf2;
TRACE("%p\n", this);
if (SUCCEEDED(QueryInterface(IID_PPV_ARG(IDropTarget, &pdt))))
{
if (FAILED(RegisterDragDrop(m_hWnd, pdt)))
ERR("Error Registering DragDrop\n");
}
/* register for receiving notifications */
m_pSFParent->QueryInterface(IID_PPV_ARG(IPersistFolder2, &ppf2));
if (ppf2)
{
ppf2->GetCurFolder(&m_pidlParent);
}
if (CreateList())
{
if (InitList())
{
FillList(FALSE);
}
}
if (m_FolderSettings.fFlags & FWF_DESKTOP)
{
HWND hwndSB;
m_pShellBrowser->GetWindow(&hwndSB);
SetShellWindowEx(hwndSB, m_ListView);
}
// Set up change notification
LPITEMIDLIST pidlTarget = NULL;
LONG fEvents = 0;
HRESULT hr = _DoFolderViewCB(SFVM_GETNOTIFY, (WPARAM)&pidlTarget, (LPARAM)&fEvents);
if (FAILED(hr) || (!pidlTarget && !fEvents)) // FIXME: MSDN says both zero means no notifications
{
pidlTarget = m_pidlParent;
fEvents = SHCNE_ALLEVENTS;
}
SHChangeNotifyEntry ntreg = {};
hr = _DoFolderViewCB(SFVM_QUERYFSNOTIFY, 0, (LPARAM)&ntreg);
if (FAILED(hr))
{
ntreg.fRecursive = FALSE;
ntreg.pidl = pidlTarget;
}
m_hNotify = SHChangeNotifyRegister(m_hWnd,
SHCNRF_InterruptLevel | SHCNRF_ShellLevel |
SHCNRF_NewDelivery,
fEvents, SHV_CHANGE_NOTIFY,
1, &ntreg);
m_hAccel = LoadAcceleratorsW(shell32_hInstance, MAKEINTRESOURCEW(IDA_SHELLVIEW));
BOOL bPreviousParentSpecial = m_isParentFolderSpecial;
// A folder is special if it is the Desktop folder,
// a network folder, or a Control Panel folder
m_isParentFolderSpecial = IsDesktop() || _ILIsNetHood(m_pidlParent)
|| _ILIsControlPanel(ILFindLastID(m_pidlParent));
// Only force StatusBar part refresh if the state
// changed from the previous folder
if (bPreviousParentSpecial != m_isParentFolderSpecial)
{
// This handles changing StatusBar parts
_ForceStatusBarResize();
}
UpdateStatusbar();
return S_OK;
}
// #### Handling of the menus ####
HRESULT CDefView::FillFileMenu()
{
HMENU hFileMenu = GetSubmenuByID(m_hMenu, FCIDM_MENU_FILE);
if (!hFileMenu)
return E_FAIL;
/* Cleanup the items added previously */
for (int i = GetMenuItemCount(hFileMenu) - 1; i >= 0; i--)
{
UINT id = GetMenuItemID(hFileMenu, i);
if (id < FCIDM_BROWSERFIRST || id > FCIDM_BROWSERLAST)
DeleteMenu(hFileMenu, i, MF_BYPOSITION);
}
// In case we still have this left over, clean it up
if (m_pFileMenu)
{
IUnknown_SetSite(m_pFileMenu, NULL);
m_pFileMenu.Release();
}
UINT selcount = m_ListView.GetSelectedCount();
// Store context menu in m_pFileMenu and keep it to invoke the selected command later on
HRESULT hr = GetItemObject(selcount ? SVGIO_SELECTION : SVGIO_BACKGROUND, IID_PPV_ARG(IContextMenu, &m_pFileMenu));
if (FAILED_UNEXPECTEDLY(hr))
return hr;
HMENU hmenu = CreatePopupMenu();
UINT cmf = GetContextMenuFlags(m_pShellBrowser, SFGAO_CANRENAME);
hr = m_pFileMenu->QueryContextMenu(hmenu, 0, DVIDM_CONTEXTMENU_FIRST, DVIDM_CONTEXTMENU_LAST, cmf);
if (FAILED_UNEXPECTEDLY(hr))
return hr;
// TODO: filter or something
if (!selcount)
{
DeleteMenu(hmenu, FCIDM_SHVIEW_VIEW, MF_BYCOMMAND);
DeleteMenu(hmenu, FCIDM_SHVIEW_ARRANGE, MF_BYCOMMAND);
DeleteMenu(hmenu, FCIDM_SHVIEW_REFRESH, MF_BYCOMMAND);
}
Shell_MergeMenus(hFileMenu, hmenu, 0, 0, 0xFFFF, MM_ADDSEPARATOR | MM_SUBMENUSHAVEIDS);
::DestroyMenu(hmenu);
return S_OK;
}
HRESULT CDefView::FillEditMenu()
{
HMENU hEditMenu = GetSubmenuByID(m_hMenu, FCIDM_MENU_EDIT);
if (!hEditMenu)
return E_FAIL;
HMENU hmenuContents = ::LoadMenuW(shell32_hInstance, L"MENU_003");
if (!hmenuContents)
return E_FAIL;
Shell_MergeMenus(hEditMenu, hmenuContents, 0, 0, 0xFFFF, 0);
::DestroyMenu(hmenuContents);
return S_OK;
}
HRESULT CDefView::FillViewMenu()
{
HMENU hViewMenu = GetSubmenuByID(m_hMenu, FCIDM_MENU_VIEW);
if (!hViewMenu)
return E_FAIL;
m_hMenuViewModes = ::LoadMenuW(shell32_hInstance, L"MENU_001");
if (!m_hMenuViewModes)
return E_FAIL;
UINT i = SHMenuIndexFromID(hViewMenu, FCIDM_MENU_VIEW_SEP_OPTIONS);
Shell_MergeMenus(hViewMenu, m_hMenuViewModes, i, 0, 0xFFFF, MM_ADDSEPARATOR | MM_DONTREMOVESEPS | MM_SUBMENUSHAVEIDS);
return S_OK;
}
HRESULT CDefView::FillArrangeAsMenu(HMENU hmenuArrange)
{
bool forceMerge = false;
UINT currentSortId = DVIDM_ARRANGESORT_FIRST + m_sortInfo.ListColumn;
// Make sure the column we currently sort by is in the menu
RemoveMenu(m_hMenuArrangeModes, DVIDM_ARRANGESORT_LAST, MF_BYCOMMAND);
if (m_sortInfo.ListColumn >= DEFVIEW_ARRANGESORT_MAXENUM)
{
C_ASSERT(DEFVIEW_ARRANGESORT_MAXENUM < DEFVIEW_ARRANGESORT_MAX);
C_ASSERT(DVIDM_ARRANGESORT_FIRST + DEFVIEW_ARRANGESORT_MAXENUM == DVIDM_ARRANGESORT_LAST);
WCHAR buf[MAX_PATH];
LVCOLUMN lvc;
lvc.mask = LVCF_TEXT;
lvc.pszText = buf;
lvc.cchTextMax = _countof(buf);
currentSortId = DVIDM_ARRANGESORT_LAST;
forceMerge = true;
ListView_GetColumn(m_ListView.m_hWnd, m_sortInfo.ListColumn, &lvc);
AppendMenuItem(m_hMenuArrangeModes, MF_STRING, currentSortId, lvc.pszText, m_sortInfo.ListColumn);
}
// Prepend the sort-by items unless they are aleady there
if (GetMenuItemID(hmenuArrange, 0) == FCIDM_SHVIEW_AUTOARRANGE || forceMerge)
{
Shell_MergeMenus(hmenuArrange, m_hMenuArrangeModes, 0, 0, 0xFFFF, MM_ADDSEPARATOR);
}
CheckMenuRadioItem(hmenuArrange,
DVIDM_ARRANGESORT_FIRST, DVIDM_ARRANGESORT_LAST,
currentSortId, MF_BYCOMMAND);
if (m_FolderSettings.ViewMode == FVM_DETAILS || m_FolderSettings.ViewMode == FVM_LIST)
{
EnableMenuItem(hmenuArrange, FCIDM_SHVIEW_AUTOARRANGE, MF_BYCOMMAND | MF_GRAYED);
EnableMenuItem(hmenuArrange, FCIDM_SHVIEW_ALIGNTOGRID, MF_BYCOMMAND | MF_GRAYED);
}
else
{
EnableMenuItem(hmenuArrange, FCIDM_SHVIEW_AUTOARRANGE, MF_BYCOMMAND);
EnableMenuItem(hmenuArrange, FCIDM_SHVIEW_ALIGNTOGRID, MF_BYCOMMAND);
if (GetAutoArrange() == S_OK)
CheckMenuItem(hmenuArrange, FCIDM_SHVIEW_AUTOARRANGE, MF_CHECKED);
else
CheckMenuItem(hmenuArrange, FCIDM_SHVIEW_AUTOARRANGE, MF_UNCHECKED);
if (_GetSnapToGrid() == S_OK)
CheckMenuItem(hmenuArrange, FCIDM_SHVIEW_ALIGNTOGRID, MF_CHECKED);
else
CheckMenuItem(hmenuArrange, FCIDM_SHVIEW_ALIGNTOGRID, MF_UNCHECKED);
}
return S_OK;
}
HRESULT CDefView::CheckViewMode(HMENU hmenuView)
{
if (m_FolderSettings.ViewMode >= FVM_FIRST && m_FolderSettings.ViewMode <= FVM_LAST)
{
UINT iItemFirst = FCIDM_SHVIEW_BIGICON;
UINT iItemLast = iItemFirst + FVM_LAST - FVM_FIRST;
UINT iItem = iItemFirst + m_FolderSettings.ViewMode - FVM_FIRST;
CheckMenuRadioItem(hmenuView, iItemFirst, iItemLast, iItem, MF_BYCOMMAND);
}
return S_OK;
}
LRESULT CDefView::DoColumnContextMenu(LPARAM lParam)
{
const UINT maxItems = 15; // Feels about right
const UINT idMore = 0x1337;
UINT idFirst = idMore + 1, idLast = idFirst;
UINT lastValidListCol = 0; // Keep track of where the new column should be inserted
UINT showMore = GetKeyState(VK_SHIFT) < 0;
POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
HWND hWndHdr = ListView_GetHeader(m_ListView.m_hWnd);
if (lParam == ~0)
{
RECT r;
::GetWindowRect(hWndHdr, &r);
pt.x = r.left + ((r.right - r.left) / 2);
pt.y = r.top + ((r.bottom - r.top) / 2);
}
HMENU hMenu = CreatePopupMenu();
if (!hMenu)
return 0;
for (UINT foldCol = 0;; ++foldCol)
{
WCHAR buf[MAX_PATH];
SHELLDETAILS sd;
sd.str.uType = !STRRET_WSTR;
if (FAILED(GetDetailsByFolderColumn(NULL, foldCol, sd)))
break;
if (FAILED(StrRetToStrNW(buf, _countof(buf), &sd.str, NULL)))
break;
SHCOLSTATEF state = 0;
if (!m_pSF2Parent || FAILED(m_pSF2Parent->GetDefaultColumnState(foldCol, &state)))
state = 0;
showMore |= (state & (SHCOLSTATE_SECONDARYUI));
UINT mf = MF_STRING;
HRESULT listCol = MapFolderColumnToListColumn(foldCol);
if (foldCol == 0)
mf |= MF_CHECKED | MF_GRAYED | MF_DISABLED; // Force column 0
else if (state & (SHCOLSTATE_SECONDARYUI | SHCOLSTATE_HIDDEN))
continue;
else if (SUCCEEDED(listCol))
mf |= MF_CHECKED;
if (AppendMenuItem(hMenu, mf, idLast, buf, lastValidListCol + 1))
{
idLast++;
if (SUCCEEDED(listCol))
lastValidListCol = listCol;
}
if (idLast - idFirst == maxItems)
{
showMore++;
break;
}
}
if (showMore)
{
#if 0 // TODO
InsertMenuW(hMenu, -1, MF_SEPARATOR, 0, NULL);
InsertMenuW(hMenu, -1, MF_STRING, idMore, L"More...");
#endif
}
// A cludge to force the cursor to update so we are not stuck with "size left/right" if
// the right-click was on a column divider.
::PostMessage(m_ListView.m_hWnd, WM_SETCURSOR, (WPARAM) m_ListView.m_hWnd, HTCLIENT);
// Note: Uses the header as the owner so CDefView::OnInitMenuPopup does not mess us up.
UINT idCmd = TrackPopupMenu(hMenu, TPM_RETURNCMD | TPM_LEFTBUTTON | TPM_RIGHTBUTTON,
pt.x, pt.y, 0, hWndHdr, NULL);
if (idCmd == idMore)
{
FIXME("Open More dialog\n");
}
else if (idCmd)
{
UINT foldCol = idCmd - idFirst;
HRESULT listCol = MapFolderColumnToListColumn(foldCol);
if (SUCCEEDED(listCol))
{
ListView_DeleteColumn(m_ListView.m_hWnd, listCol);
}
else
{
listCol = (UINT) GetMenuItemDataById(hMenu, idCmd);
LoadColumn(foldCol, listCol, TRUE);
}
ColumnListChanged();
}
DestroyMenu(hMenu);
return 0;
}
/**********************************************************
* ShellView_GetSelections()
*
* - fills the m_apidl list with the selected objects
*
* RETURNS
* number of selected items
*/
UINT CDefView::GetSelections()
{
UINT count = m_ListView.GetSelectedCount();
if (count > m_cidl || !count || !m_apidl) // !count to free possibly large cache, !m_apidl to make sure m_apidl is a valid pointer
{
SHFree(m_apidl);
m_apidl = static_cast<PCUITEMID_CHILD*>(SHAlloc(count * sizeof(PCUITEMID_CHILD)));
if (!m_apidl)
{
m_cidl = 0;
return 0;
}
}
m_cidl = count;
TRACE("-- Items selected =%u\n", m_cidl);
ASSERT(m_ListView);
UINT i = 0;
int lvIndex = -1;
while ((lvIndex = m_ListView.GetNextItem(lvIndex, LVNI_SELECTED)) > -1)
{
m_apidl[i] = _PidlByItem(lvIndex);
i++;
if (i == m_cidl)
break;
TRACE("-- selected Item found\n");
}
return m_cidl;
}
HRESULT CDefView::InvokeContextMenuCommand(CComPtr<IContextMenu>& pCM, LPCSTR lpVerb, POINT* pt)
{
CMINVOKECOMMANDINFOEX cmi;
ZeroMemory(&cmi, sizeof(cmi));
cmi.cbSize = sizeof(cmi);
cmi.hwnd = m_hWnd;
cmi.lpVerb = lpVerb;
cmi.nShow = SW_SHOW;
if (GetKeyState(VK_SHIFT) < 0)
cmi.fMask |= CMIC_MASK_SHIFT_DOWN;
if (GetKeyState(VK_CONTROL) < 0)
cmi.fMask |= CMIC_MASK_CONTROL_DOWN;
if (pt)
{
cmi.fMask |= CMIC_MASK_PTINVOKE;
cmi.ptInvoke = *pt;
}
WCHAR szDirW[MAX_PATH] = L"";
CHAR szDirA[MAX_PATH];
if (SUCCEEDED(_DoFolderViewCB(SFVM_GETCOMMANDDIR, _countof(szDirW), (LPARAM)szDirW)) &&
*szDirW != UNICODE_NULL)
{
SHUnicodeToAnsi(szDirW, szDirA, _countof(szDirA));
cmi.fMask |= CMIC_MASK_UNICODE;
cmi.lpDirectory = szDirA;
cmi.lpDirectoryW = szDirW;
}
HRESULT hr = pCM->InvokeCommand((LPCMINVOKECOMMANDINFO)&cmi);
// Most of our callers will do this, but if they would forget (File menu!)
IUnknown_SetSite(pCM, NULL);
pCM.Release();
if (FAILED_UNEXPECTEDLY(hr))
return hr;
return S_OK;
}
HRESULT CDefView::OpenSelectedItems()
{
HMENU hMenu;
UINT uCommand;
HRESULT hResult;
if (m_ListView.GetSelectedCount() == 0)
return S_OK;
hResult = OnDefaultCommand();
if (hResult == S_OK)
return hResult;
hMenu = CreatePopupMenu();
if (!hMenu)
return E_FAIL;
CComPtr<IContextMenu> pCM;
hResult = GetItemObject(SVGIO_SELECTION, IID_PPV_ARG(IContextMenu, &pCM));
MenuCleanup _(pCM, hMenu);
if (FAILED_UNEXPECTEDLY(hResult))
return hResult;
UINT cmf = CMF_DEFAULTONLY | GetContextMenuFlags(m_pShellBrowser, 0);
hResult = pCM->QueryContextMenu(hMenu, 0, DVIDM_CONTEXTMENU_FIRST, DVIDM_CONTEXTMENU_LAST, cmf);
if (FAILED_UNEXPECTEDLY(hResult))
return hResult;
uCommand = GetMenuDefaultItem(hMenu, FALSE, 0);
if (uCommand == (UINT)-1)
{
ERR("GetMenuDefaultItem returned -1\n");
return E_FAIL;
}
InvokeContextMenuCommand(pCM, MAKEINTRESOURCEA(uCommand), NULL);
return hResult;
}
LRESULT CDefView::OnContextMenu(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
{
POINT pt = { pt.x = GET_X_LPARAM(lParam), pt.y = GET_Y_LPARAM(lParam) };
UINT uCommand;
HRESULT hResult;
TRACE("(%p)\n", this);
if (m_hContextMenu != NULL)
{
ERR("HACK: Aborting context menu in nested call\n");
return 0;
}
HWND hWndHdr = ListView_GetHeader(m_ListView.m_hWnd);
RECT r;
if (::GetWindowRect(hWndHdr, &r) && PtInRect(&r, pt) && ::IsWindowVisible(hWndHdr))
{
return DoColumnContextMenu(lParam);
}
m_hContextMenu = CreatePopupMenu();
if (!m_hContextMenu)
return E_FAIL;
if (lParam != ~0) // unless app key (menu key) was pressed
{
LV_HITTESTINFO hittest = { pt };
ScreenToClient(&hittest.pt);
m_ListView.HitTest(&hittest);
// Right-Clicked item is selected? If selected, no selection change.
// If not selected, then reset the selection and select the item.
if ((hittest.flags & LVHT_ONITEM) &&
m_ListView.GetItemState(hittest.iItem, LVIS_SELECTED) != LVIS_SELECTED)
{
SelectItem(hittest.iItem, SVSI_SELECT | SVSI_DESELECTOTHERS | SVSI_ENSUREVISIBLE);
}
}
UINT count = m_ListView.GetSelectedCount();
// In case we still have this left over, clean it up
hResult = GetItemObject(count ? SVGIO_SELECTION : SVGIO_BACKGROUND, IID_PPV_ARG(IContextMenu, &m_pCM));
MenuCleanup _(m_pCM, m_hContextMenu);
if (FAILED_UNEXPECTEDLY(hResult))
return 0;
UINT cmf = GetContextMenuFlags(m_pShellBrowser, SFGAO_CANRENAME);
// Use 1 as the first id we want. 0 means that user canceled the menu
hResult = m_pCM->QueryContextMenu(m_hContextMenu, 0, CONTEXT_MENU_BASE_ID, DVIDM_CONTEXTMENU_LAST, cmf);
if (FAILED_UNEXPECTEDLY(hResult))
return 0;
if (m_pCommDlgBrowser && !(GetCommDlgViewFlags() & CDB2GVF_NOSELECTVERB))
{
HMENU hMenuSource = LoadMenuW(_AtlBaseModule.GetResourceInstance(), MAKEINTRESOURCEW(IDM_DVSELECT));
Shell_MergeMenus(m_hContextMenu, GetSubMenu(hMenuSource, 0), 0, DVIDM_COMMDLG_SELECT, 0xffff, MM_ADDSEPARATOR | MM_DONTREMOVESEPS);
DestroyMenu(hMenuSource);
SetMenuDefaultItem(m_hContextMenu, DVIDM_COMMDLG_SELECT, MF_BYCOMMAND);
// TODO: ICommDlgBrowser2::GetDefaultMenuText == S_OK
}
// There is no position requested, so try to find one
if (lParam == ~0)
{
HWND hFocus = ::GetFocus();
int lvIndex = -1;
if (hFocus == m_ListView.m_hWnd || m_ListView.IsChild(hFocus))
{
// Is there an item focused and selected?
lvIndex = m_ListView.GetNextItem(-1, LVNI_SELECTED | LVNI_FOCUSED);
// If not, find the first selected item
if (lvIndex < 0)
lvIndex = m_ListView.GetNextItem(-1, LVNI_SELECTED);
}
// We got something
if (lvIndex > -1)
{
// Find the center of the icon
RECT rc = { LVIR_ICON };
m_ListView.SendMessage(LVM_GETITEMRECT, lvIndex, (LPARAM)&rc);
pt.x = (rc.right + rc.left) / 2;
pt.y = (rc.bottom + rc.top) / 2;
}
else
{
// We have to drop it somewhere
pt.x = pt.y = 0;
}
m_ListView.ClientToScreen(&pt);
}
CComPtr<ICommDlgBrowser2> pcdb2;
if (m_pCommDlgBrowser && SUCCEEDED(m_pCommDlgBrowser->QueryInterface(IID_PPV_ARG(ICommDlgBrowser2, &pcdb2))))
pcdb2->Notify(static_cast<IShellView*>(this), CDB2N_CONTEXTMENU_START);
// This runs the message loop, calling back to us with f.e. WM_INITPOPUP (hence why m_hContextMenu and m_pCM exist)
uCommand = TrackPopupMenu(m_hContextMenu,
TPM_LEFTALIGN | TPM_RETURNCMD | TPM_LEFTBUTTON | TPM_RIGHTBUTTON,
pt.x, pt.y, 0, m_hWnd, NULL);
if (uCommand >= DVIDM_ARRANGESORT_FIRST && uCommand <= DVIDM_ARRANGESORT_LAST)
{
SendMessage(WM_COMMAND, uCommand, 0);
}
else if (uCommand != 0 && !(uCommand == DVIDM_COMMDLG_SELECT && OnDefaultCommand() == S_OK))
{
InvokeContextMenuCommand(m_pCM, MAKEINTRESOURCEA(uCommand - CONTEXT_MENU_BASE_ID), &pt);
}
if (pcdb2)
pcdb2->Notify(static_cast<IShellView*>(this), CDB2N_CONTEXTMENU_DONE);
return 0;
}
LRESULT CDefView::OnExplorerCommand(UINT uCommand, BOOL bUseSelection)
{
HRESULT hResult;
HMENU hMenu = NULL;
CComPtr<IContextMenu> pCM;
hResult = GetItemObject(bUseSelection ? SVGIO_SELECTION : SVGIO_BACKGROUND, IID_PPV_ARG(IContextMenu, &pCM));
if (FAILED_UNEXPECTEDLY(hResult))
return 0;
MenuCleanup _(pCM, hMenu);
if ((uCommand != FCIDM_SHVIEW_DELETE) && (uCommand != FCIDM_SHVIEW_RENAME))
{
hMenu = CreatePopupMenu();
if (!hMenu)
return 0;
hResult = pCM->QueryContextMenu(hMenu, 0, DVIDM_CONTEXTMENU_FIRST, DVIDM_CONTEXTMENU_LAST, CMF_NORMAL);
if (FAILED_UNEXPECTEDLY(hResult))
return 0;
}
if (bUseSelection)
{
// FIXME: we should cache this
SFGAOF rfg = SFGAO_BROWSABLE | SFGAO_CANCOPY | SFGAO_CANLINK | SFGAO_CANMOVE | SFGAO_CANDELETE | SFGAO_CANRENAME | SFGAO_HASPROPSHEET | SFGAO_FILESYSTEM | SFGAO_FOLDER;
hResult = m_pSFParent->GetAttributesOf(m_cidl, m_apidl, &rfg);
if (FAILED_UNEXPECTEDLY(hResult))
return 0;
if (!(rfg & SFGAO_CANMOVE) && uCommand == FCIDM_SHVIEW_CUT)
return 0;
if (!(rfg & SFGAO_CANCOPY) && uCommand == FCIDM_SHVIEW_COPY)
return 0;
if (!(rfg & SFGAO_CANDELETE) && uCommand == FCIDM_SHVIEW_DELETE)
return 0;
if (!(rfg & SFGAO_CANRENAME) && uCommand == FCIDM_SHVIEW_RENAME)
return 0;
if (!(rfg & SFGAO_HASPROPSHEET) && uCommand == FCIDM_SHVIEW_PROPERTIES)
return 0;
}
// FIXME: We should probably use the objects position?
InvokeContextMenuCommand(pCM, MAKEINTRESOURCEA(uCommand), NULL);
return 0;
}
// ##### message handling #####
LRESULT CDefView::OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
{
WORD wWidth, wHeight;
wWidth = LOWORD(lParam);
wHeight = HIWORD(lParam);
TRACE("%p width=%u height=%u\n", this, wWidth, wHeight);
// WM_SIZE can come before WM_CREATE
if (!m_ListView)
return 0;
/* Resize the ListView to fit our window */
::MoveWindow(m_ListView, 0, 0, wWidth, wHeight, TRUE);
_DoFolderViewCB(SFVM_SIZE, 0, 0);
_ForceStatusBarResize();
UpdateStatusbar();
return 0;
}
// internal
void CDefView::OnDeactivate()
{
TRACE("%p\n", this);
if (m_uState != SVUIA_DEACTIVATE)
{
// TODO: cleanup menu after deactivation
m_uState = SVUIA_DEACTIVATE;
}
}
void CDefView::DoActivate(UINT uState)
{
TRACE("%p uState=%x\n", this, uState);
// don't do anything if the state isn't really changing
if (m_uState == uState)
{
return;
}
if (uState == SVUIA_DEACTIVATE)
{
OnDeactivate();
}
else
{
if(m_hMenu && !m_bmenuBarInitialized)
{
FillEditMenu();
FillViewMenu();
m_pShellBrowser->SetMenuSB(m_hMenu, 0, m_hWnd);
m_bmenuBarInitialized = TRUE;
}
if (SVUIA_ACTIVATE_FOCUS == uState)
{
m_ListView.SetFocus();
}
}
m_uState = uState;
TRACE("--\n");
}
void CDefView::_DoCopyToMoveToFolder(BOOL bCopy)
{
if (!GetSelections())
return;
SFGAOF rfg = SFGAO_CANCOPY | SFGAO_CANMOVE | SFGAO_FILESYSTEM;
HRESULT hr = m_pSFParent->GetAttributesOf(m_cidl, m_apidl, &rfg);
if (FAILED_UNEXPECTEDLY(hr))
return;
if (!bCopy && !(rfg & SFGAO_CANMOVE))
return;
if (bCopy && !(rfg & SFGAO_CANCOPY))
return;
CComPtr<IContextMenu> pCM;
hr = m_pSFParent->GetUIObjectOf(m_hWnd, m_cidl, m_apidl, IID_IContextMenu, 0, (void **)&pCM);
if (FAILED_UNEXPECTEDLY(hr))
return;
InvokeContextMenuCommand(pCM, (bCopy ? "copyto" : "moveto"), NULL);
}
LRESULT CDefView::OnActivate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
{
DoActivate(SVUIA_ACTIVATE_FOCUS);
return 0;
}
LRESULT CDefView::OnSetFocus(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
{
TRACE("%p\n", this);
/* Tell the browser one of our windows has received the focus. This
should always be done before merging menus (OnActivate merges the
menus) if one of our windows has the focus.*/
m_pShellBrowser->OnViewWindowActive(this);
DoActivate(SVUIA_ACTIVATE_FOCUS);
/* Set the focus to the listview */
m_ListView.SetFocus();
/* Notify the ICommDlgBrowser interface */
OnStateChange(CDBOSC_SETFOCUS);
return 0;
}
LRESULT CDefView::OnKillFocus(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
{
TRACE("(%p) stub\n", this);
DoActivate(SVUIA_ACTIVATE_NOFOCUS);
/* Notify the ICommDlgBrowser */
OnStateChange(CDBOSC_KILLFOCUS);
return 0;
}
// the CmdID's are the ones from the context menu
LRESULT CDefView::OnCommand(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
{
DWORD dwCmdID;
DWORD dwCmd;
HWND hwndCmd;
int nCount;
dwCmdID = GET_WM_COMMAND_ID(wParam, lParam);
dwCmd = GET_WM_COMMAND_CMD(wParam, lParam);
hwndCmd = GET_WM_COMMAND_HWND(wParam, lParam);
TRACE("(%p)->(0x%08x 0x%08x %p) stub\n", this, dwCmdID, dwCmd, hwndCmd);
if (dwCmdID >= DVIDM_ARRANGESORT_FIRST && dwCmdID <= DVIDM_ARRANGESORT_LAST)
{
UINT listCol = (UINT)GetMenuItemDataById(m_hMenuArrangeModes, dwCmdID);
_Sort(listCol);
return 0;
}
switch (dwCmdID)
{
case FCIDM_SHVIEW_SMALLICON:
m_FolderSettings.ViewMode = FVM_SMALLICON;
m_ListView.ModifyStyle(LVS_TYPEMASK, LVS_SMALLICON);
CheckToolbar();
break;
case FCIDM_SHVIEW_BIGICON:
m_FolderSettings.ViewMode = FVM_ICON;
m_ListView.ModifyStyle(LVS_TYPEMASK, LVS_ICON);
CheckToolbar();
break;
case FCIDM_SHVIEW_LISTVIEW:
m_FolderSettings.ViewMode = FVM_LIST;
m_ListView.ModifyStyle(LVS_TYPEMASK, LVS_LIST);
CheckToolbar();
break;
case FCIDM_SHVIEW_REPORTVIEW:
m_FolderSettings.ViewMode = FVM_DETAILS;
m_ListView.ModifyStyle(LVS_TYPEMASK, LVS_REPORT);
CheckToolbar();
break;
case FCIDM_SHVIEW_SNAPTOGRID:
m_ListView.Arrange(LVA_SNAPTOGRID);
break;
case FCIDM_SHVIEW_ALIGNTOGRID:
if (_GetSnapToGrid() == S_OK)
m_ListView.SetExtendedListViewStyle(0, LVS_EX_SNAPTOGRID);
else
ArrangeGrid();
break;
case FCIDM_SHVIEW_AUTOARRANGE:
if (GetAutoArrange() == S_OK)
m_ListView.ModifyStyle(LVS_AUTOARRANGE, 0);
else
AutoArrange();
break;
case FCIDM_SHVIEW_SELECTALL:
if (_DoFolderViewCB(SFVM_CANSELECTALL, 0, 0) != S_FALSE)
m_ListView.SetItemState(-1, LVIS_SELECTED, LVIS_SELECTED);
break;
case FCIDM_SHVIEW_INVERTSELECTION:
nCount = m_ListView.GetItemCount();
for (int i=0; i < nCount; i++)
m_ListView.SetItemState(i, m_ListView.GetItemState(i, LVIS_SELECTED) ? 0 : LVIS_SELECTED, LVIS_SELECTED);
break;
case FCIDM_SHVIEW_REFRESH:
Refresh();
break;
case FCIDM_SHVIEW_DELETE:
case FCIDM_SHVIEW_CUT:
case FCIDM_SHVIEW_COPY:
case FCIDM_SHVIEW_RENAME:
case FCIDM_SHVIEW_PROPERTIES:
if (SHRestricted(REST_NOVIEWCONTEXTMENU))
return 0;
return OnExplorerCommand(dwCmdID, TRUE);
case FCIDM_SHVIEW_COPYTO:
case FCIDM_SHVIEW_MOVETO:
_DoCopyToMoveToFolder(dwCmdID == FCIDM_SHVIEW_COPYTO);
return 0;
case FCIDM_SHVIEW_INSERT:
case FCIDM_SHVIEW_UNDO:
case FCIDM_SHVIEW_INSERTLINK:
case FCIDM_SHVIEW_NEWFOLDER:
return OnExplorerCommand(dwCmdID, FALSE);
default:
// WM_COMMAND messages from file menu are routed to CDefView to let m_pFileMenu handle them
if (m_pFileMenu && dwCmd == 0)
{
HMENU Dummy = NULL;
MenuCleanup _(m_pFileMenu, Dummy);
InvokeContextMenuCommand(m_pFileMenu, MAKEINTRESOURCEA(dwCmdID), NULL);
}
}
return 0;
}
static BOOL
SelectExtOnRename(void)
{
HKEY hKey;
LONG error;
DWORD dwValue = FALSE, cbValue;
error = RegOpenKeyExW(HKEY_CURRENT_USER, REGSTR_PATH_EXPLORER, 0, KEY_READ, &hKey);
if (error)
return dwValue;
cbValue = sizeof(dwValue);
RegQueryValueExW(hKey, L"SelectExtOnRename", NULL, NULL, (LPBYTE)&dwValue, &cbValue);
RegCloseKey(hKey);
return !!dwValue;
}
LRESULT CDefView::OnNotify(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
{
UINT CtlID;
LPNMHDR lpnmh;
LPNMLISTVIEW lpnmlv;
NMLVDISPINFOW *lpdi;
PCUITEMID_CHILD pidl;
BOOL unused;
CtlID = wParam;
lpnmh = (LPNMHDR)lParam;
lpnmlv = (LPNMLISTVIEW)lpnmh;
lpdi = (NMLVDISPINFOW *)lpnmh;
TRACE("%p CtlID=%u lpnmh->code=%x\n", this, CtlID, lpnmh->code);
switch (lpnmh->code)
{
case NM_SETFOCUS:
TRACE("-- NM_SETFOCUS %p\n", this);
OnSetFocus(0, 0, 0, unused);
break;
case NM_KILLFOCUS:
TRACE("-- NM_KILLFOCUS %p\n", this);
OnDeactivate();
/* Notify the ICommDlgBrowser interface */
OnStateChange(CDBOSC_KILLFOCUS);
break;
case NM_CUSTOMDRAW:
TRACE("-- NM_CUSTOMDRAW %p\n", this);
return CDRF_DODEFAULT;
case NM_RELEASEDCAPTURE:
TRACE("-- NM_RELEASEDCAPTURE %p\n", this);
break;
case NM_CLICK:
TRACE("-- NM_CLICK %p\n", this);
break;
case NM_RCLICK:
TRACE("-- NM_RCLICK %p\n", this);
break;
case NM_DBLCLK:
TRACE("-- NM_DBLCLK %p\n", this);
OpenSelectedItems();
break;
case NM_RETURN:
TRACE("-- NM_RETURN %p\n", this);
OpenSelectedItems();
break;
case HDN_ENDTRACKW:
TRACE("-- HDN_ENDTRACKW %p\n", this);
//nColumn1 = m_ListView.GetColumnWidth(0);
//nColumn2 = m_ListView.GetColumnWidth(1);
break;
case LVN_DELETEITEM:
TRACE("-- LVN_DELETEITEM %p\n", this);
/*delete the pidl because we made a copy of it*/
SHFree(reinterpret_cast<LPVOID>(lpnmlv->lParam));
break;
case LVN_DELETEALLITEMS:
TRACE("-- LVN_DELETEALLITEMS %p\n", this);
return FALSE;
case LVN_INSERTITEM:
TRACE("-- LVN_INSERTITEM (STUB)%p\n", this);
break;
case LVN_ITEMACTIVATE:
TRACE("-- LVN_ITEMACTIVATE %p\n", this);
OnStateChange(CDBOSC_SELCHANGE); // browser will get the IDataObject
break;
case LVN_COLUMNCLICK:
{
UINT foldercol = MapListColumnToFolderColumn(lpnmlv->iSubItem);
HRESULT hr = S_FALSE;
if (m_pSDParent)
hr = m_pSDParent->ColumnClick(foldercol);
if (hr != S_OK)
hr = _DoFolderViewCB(SFVM_COLUMNCLICK, foldercol, 0);
if (hr != S_OK)
_Sort(lpnmlv->iSubItem);
break;
}
case LVN_GETDISPINFOA:
case LVN_GETDISPINFOW:
TRACE("-- LVN_GETDISPINFO %p\n", this);
pidl = _PidlByItem(lpdi->item);
if (lpdi->item.mask & LVIF_TEXT) /* text requested */
{
SHELLDETAILS sd;
if (FAILED_UNEXPECTEDLY(GetDetailsByListColumn(pidl, lpdi->item.iSubItem, sd)))
break;
if (lpnmh->code == LVN_GETDISPINFOA)
{
/* shouldn't happen */
NMLVDISPINFOA *lpdiA = (NMLVDISPINFOA *)lpnmh;
StrRetToStrNA( lpdiA->item.pszText, lpdiA->item.cchTextMax, &sd.str, NULL);
TRACE("-- text=%s\n", lpdiA->item.pszText);
}
else /* LVN_GETDISPINFOW */
{
StrRetToStrNW( lpdi->item.pszText, lpdi->item.cchTextMax, &sd.str, NULL);
TRACE("-- text=%s\n", debugstr_w(lpdi->item.pszText));
}
}
if(lpdi->item.mask & LVIF_IMAGE) /* image requested */
{
lpdi->item.iImage = SHMapPIDLToSystemImageListIndex(m_pSFParent, pidl, 0);
}
if(lpdi->item.mask & LVIF_STATE)
{
ULONG attributes = SFGAO_HIDDEN;
if (SUCCEEDED(m_pSFParent->GetAttributesOf(1, &pidl, &attributes)))
{
if (attributes & SFGAO_HIDDEN)
lpdi->item.state |= LVIS_CUT;
}
}
lpdi->item.mask |= LVIF_DI_SETITEM;
break;
case LVN_ITEMCHANGED:
TRACE("-- LVN_ITEMCHANGED %p\n", this);
if ((lpnmlv->uOldState ^ lpnmlv->uNewState) & (LVIS_SELECTED | LVIS_FOCUSED))
{
OnStateChange(CDBOSC_SELCHANGE); // browser will get the IDataObject
// FIXME: Use LVIS_DROPHILITED instead in drag_notify_subitem
if (!m_ScheduledStatusbarUpdate && (m_iDragOverItem == -1 || m_pCurDropTarget == NULL))
{
m_ScheduledStatusbarUpdate = true;
PostMessage(SHV_UPDATESTATUSBAR, 0, 0);
}
_DoFolderViewCB(SFVM_SELECTIONCHANGED, NULL/* FIXME */, NULL/* FIXME */);
}
break;
case LVN_BEGINDRAG:
case LVN_BEGINRDRAG:
TRACE("-- LVN_BEGINDRAG\n");
if (GetSelections())
{
CComPtr<IDataObject> pda;
DWORD dwAttributes = SFGAO_CANCOPY | SFGAO_CANLINK;
DWORD dwEffect = DROPEFFECT_MOVE;
if (SUCCEEDED(m_pSFParent->GetUIObjectOf(m_hWnd, m_cidl, m_apidl, IID_NULL_PPV_ARG(IDataObject, &pda))))
{
LPNMLISTVIEW params = (LPNMLISTVIEW)lParam;
if (SUCCEEDED(m_pSFParent->GetAttributesOf(m_cidl, m_apidl, &dwAttributes)))
dwEffect |= dwAttributes & (SFGAO_CANCOPY | SFGAO_CANLINK);
CComPtr<IAsyncOperation> piaso;
if (SUCCEEDED(pda->QueryInterface(IID_PPV_ARG(IAsyncOperation, &piaso))))
piaso->SetAsyncMode(TRUE);
DWORD dwEffect2;
m_pSourceDataObject = pda;
m_ptFirstMousePos = params->ptAction;
ClientToScreen(&m_ptFirstMousePos);
::ClientToListView(m_ListView, &m_ptFirstMousePos);
HIMAGELIST big_icons, small_icons;
Shell_GetImageLists(&big_icons, &small_icons);
PCUITEMID_CHILD pidl = _PidlByItem(params->iItem);
int iIcon = SHMapPIDLToSystemImageListIndex(m_pSFParent, pidl, 0);
POINT ptItem;
m_ListView.GetItemPosition(params->iItem, &ptItem);
ImageList_BeginDrag(big_icons, iIcon, params->ptAction.x - ptItem.x, params->ptAction.y - ptItem.y);
DoDragDrop(pda, this, dwEffect, &dwEffect2);
m_pSourceDataObject.Release();
}
}
break;
case LVN_BEGINLABELEDITW:
{
DWORD dwAttr = SFGAO_CANRENAME;
pidl = _PidlByItem(lpdi->item);
TRACE("-- LVN_BEGINLABELEDITW %p\n", this);
m_pSFParent->GetAttributesOf(1, &pidl, &dwAttr);
if (SFGAO_CANRENAME & dwAttr)
{
HWND hEdit = reinterpret_cast<HWND>(m_ListView.SendMessage(LVM_GETEDITCONTROL));
SHLimitInputEdit(hEdit, m_pSFParent);
// smartass-renaming: See CORE-15242
if (!(dwAttr & SFGAO_FOLDER) && (dwAttr & SFGAO_FILESYSTEM) &&
(lpdi->item.mask & LVIF_TEXT) && !SelectExtOnRename())
{
WCHAR szFullPath[MAX_PATH];
PIDLIST_ABSOLUTE pidlFull = ILCombine(m_pidlParent, pidl);
SHGetPathFromIDListW(pidlFull, szFullPath);
INT cchLimit = 0;
_DoFolderViewCB(SFVM_GETNAMELENGTH, (WPARAM)pidlFull, (LPARAM)&cchLimit);
if (cchLimit)
::SendMessageW(hEdit, EM_SETLIMITTEXT, cchLimit, 0);
if (!SHELL_FS_HideExtension(szFullPath))
{
LPWSTR pszText = lpdi->item.pszText;
LPWSTR pchDotExt = PathFindExtensionW(pszText);
::PostMessageW(hEdit, EM_SETSEL, 0, pchDotExt - pszText);
::PostMessageW(hEdit, EM_SCROLLCARET, 0, 0);
}
ILFree(pidlFull);
}
m_isEditing = TRUE;
return FALSE;
}
return TRUE;
}
case LVN_ENDLABELEDITW:
{
TRACE("-- LVN_ENDLABELEDITW %p\n", this);
m_isEditing = FALSE;
if (lpdi->item.pszText)
{
HRESULT hr;
LVITEMW lvItem;
pidl = _PidlByItem(lpdi->item);
PITEMID_CHILD pidlNew = NULL;
hr = m_pSFParent->SetNameOf(0, pidl, lpdi->item.pszText, SHGDN_INFOLDER, &pidlNew);
if (SUCCEEDED(hr) && pidlNew)
{
lvItem.mask = LVIF_PARAM|LVIF_IMAGE;
lvItem.iItem = lpdi->item.iItem;
lvItem.iSubItem = 0;
lvItem.lParam = reinterpret_cast<LPARAM>(pidlNew);
lvItem.iImage = SHMapPIDLToSystemImageListIndex(m_pSFParent, pidlNew, 0);
m_ListView.SetItem(&lvItem);
m_ListView.Update(lpdi->item.iItem);
return TRUE;
}
}
return FALSE;
}
default:
TRACE("-- %p WM_COMMAND %x unhandled\n", this, lpnmh->code);
break;
}
return 0;
}
// This is just a quick hack to make the desktop work correctly.
// ITranslateShellChangeNotify's IsChildID is undocumented, but most likely the
// way that a folder should know if it should update upon a change notification.
// It is exported by merged folders at a minimum.
static BOOL ILIsParentOrSpecialParent(PCIDLIST_ABSOLUTE pidl1, PCIDLIST_ABSOLUTE pidl2)
{
if (!pidl1 || !pidl2)
return FALSE;
if (ILIsParent(pidl1, pidl2, TRUE))
return TRUE;
CComHeapPtr<ITEMIDLIST_ABSOLUTE> pidl2Clone(ILClone(pidl2));
ILRemoveLastID(pidl2Clone);
return ILIsEqual(pidl1, pidl2Clone);
}
LRESULT CDefView::OnChangeNotify(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
{
// The change notify can come before WM_CREATE
if (!m_ListView)
return FALSE;
HANDLE hChange = (HANDLE)wParam;
DWORD dwProcID = (DWORD)lParam;
PIDLIST_ABSOLUTE *Pidls;
LONG lEvent;
HANDLE hLock = SHChangeNotification_Lock(hChange, dwProcID, &Pidls, &lEvent);
if (hLock == NULL)
{
ERR("hLock == NULL\n");
return FALSE;
}
TRACE("(%p)(%p,%p,%p)\n", this, Pidls[0], Pidls[1], lParam);
if (_DoFolderViewCB(SFVM_FSNOTIFY, (WPARAM)Pidls, lEvent) == S_FALSE)
return FALSE;
// Translate child IDLs.
// SHSimpleIDListFromPathW creates fake PIDLs (lacking some attributes)
HRESULT hr;
PITEMID_CHILD child0 = NULL, child1 = NULL;
CComHeapPtr<ITEMIDLIST_RELATIVE> pidl0Temp, pidl1Temp;
if (_ILIsSpecialFolder(Pidls[0]) || ILIsParentOrSpecialParent(m_pidlParent, Pidls[0]))
{
child0 = ILFindLastID(Pidls[0]);
hr = SHGetRealIDL(m_pSFParent, child0, &pidl0Temp);
if (SUCCEEDED(hr))
child0 = pidl0Temp;
}
if (_ILIsSpecialFolder(Pidls[1]) || ILIsParentOrSpecialParent(m_pidlParent, Pidls[1]))
{
child1 = ILFindLastID(Pidls[1]);
hr = SHGetRealIDL(m_pSFParent, child1, &pidl1Temp);
if (SUCCEEDED(hr))
child1 = pidl1Temp;
}
lEvent &= ~SHCNE_INTERRUPT;
switch (lEvent)
{
case SHCNE_MKDIR:
case SHCNE_CREATE:
case SHCNE_DRIVEADD:
if (!child0)
break;
if (LV_FindItemByPidl(child0) < 0)
LV_AddItem(child0);
else
LV_UpdateItem(child0);
break;
case SHCNE_RMDIR:
case SHCNE_DELETE:
case SHCNE_DRIVEREMOVED:
if (child0)
LV_DeleteItem(child0);
break;
case SHCNE_RENAMEFOLDER:
case SHCNE_RENAMEITEM:
if (child0 && child1)
LV_RenameItem(child0, child1);
else if (child0)
LV_DeleteItem(child0);
else if (child1)
LV_AddItem(child1);
break;
case SHCNE_UPDATEITEM:
if (child0)
LV_UpdateItem(child0);
break;
case SHCNE_UPDATEIMAGE:
case SHCNE_MEDIAINSERTED:
case SHCNE_MEDIAREMOVED:
case SHCNE_ASSOCCHANGED:
LV_RefreshIcons();
break;
case SHCNE_UPDATEDIR:
case SHCNE_ATTRIBUTES:
Refresh();
UpdateStatusbar();
break;
case SHCNE_FREESPACE:
UpdateStatusbar();
break;
}
SHChangeNotification_Unlock(hLock);
return TRUE;
}
HRESULT SHGetMenuIdFromMenuMsg(UINT uMsg, LPARAM lParam, UINT *CmdId);
HRESULT SHSetMenuIdInMenuMsg(UINT uMsg, LPARAM lParam, UINT CmdId);
LRESULT CDefView::OnCustomItem(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
{
if (!m_pCM)
{
/* no menu */
ERR("no context menu\n");
return FALSE;
}
// lParam of WM_DRAWITEM WM_MEASUREITEM contains a menu id and
// this also needs to be changed to a menu identifier offset
UINT CmdID;
HRESULT hres = SHGetMenuIdFromMenuMsg(uMsg, lParam, &CmdID);
if (SUCCEEDED(hres))
SHSetMenuIdInMenuMsg(uMsg, lParam, CmdID - CONTEXT_MENU_BASE_ID);
/* Forward the message to the IContextMenu2 */
LRESULT result;
hres = SHForwardContextMenuMsg(m_pCM, uMsg, wParam, lParam, &result, TRUE);
return (SUCCEEDED(hres));
}
LRESULT CDefView::OnSettingChange(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
{
/* Wallpaper setting affects drop shadows effect */
if (wParam == SPI_SETDESKWALLPAPER || wParam == 0)
UpdateListColors();
return S_OK;
}
LRESULT CDefView::OnInitMenuPopup(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
{
HMENU hmenu = (HMENU) wParam;
int nPos = LOWORD(lParam);
UINT menuItemId;
if (m_pCM)
OnCustomItem(uMsg, wParam, lParam, bHandled);
HMENU hViewMenu = GetSubmenuByID(m_hMenu, FCIDM_MENU_VIEW);
if (GetSelections() == 0)
{
::EnableMenuItem(hmenu, FCIDM_SHVIEW_CUT, MF_GRAYED);
::EnableMenuItem(hmenu, FCIDM_SHVIEW_COPY, MF_GRAYED);
::EnableMenuItem(hmenu, FCIDM_SHVIEW_RENAME, MF_GRAYED);
::EnableMenuItem(hmenu, FCIDM_SHVIEW_COPYTO, MF_GRAYED);
::EnableMenuItem(hmenu, FCIDM_SHVIEW_MOVETO, MF_GRAYED);
::EnableMenuItem(hmenu, FCIDM_SHVIEW_DELETE, MF_GRAYED);
}
else
{
// FIXME: Check copyable
::EnableMenuItem(hmenu, FCIDM_SHVIEW_CUT, MF_ENABLED);
::EnableMenuItem(hmenu, FCIDM_SHVIEW_COPY, MF_ENABLED);
::EnableMenuItem(hmenu, FCIDM_SHVIEW_RENAME, MF_ENABLED);
::EnableMenuItem(hmenu, FCIDM_SHVIEW_COPYTO, MF_ENABLED);
::EnableMenuItem(hmenu, FCIDM_SHVIEW_MOVETO, MF_ENABLED);
::EnableMenuItem(hmenu, FCIDM_SHVIEW_DELETE, MF_ENABLED);
}
/* Lets try to find out what the hell wParam is */
if (hmenu == GetSubMenu(m_hMenu, nPos))
menuItemId = ReallyGetMenuItemID(m_hMenu, nPos);
else if (hViewMenu && hmenu == GetSubMenu(hViewMenu, nPos))
menuItemId = ReallyGetMenuItemID(hViewMenu, nPos);
else if (m_hContextMenu && hmenu == GetSubMenu(m_hContextMenu, nPos))
menuItemId = ReallyGetMenuItemID(m_hContextMenu, nPos);
else
return FALSE;
switch (menuItemId)
{
case FCIDM_MENU_FILE:
FillFileMenu();
break;
case FCIDM_MENU_VIEW:
case FCIDM_SHVIEW_VIEW:
CheckViewMode(hmenu);
break;
case FCIDM_SHVIEW_ARRANGE:
FillArrangeAsMenu(hmenu);
break;
}
return FALSE;
}
// The INTERFACE of the IShellView object
HRESULT WINAPI CDefView::GetWindow(HWND *phWnd)
{
TRACE("(%p)\n", this);
*phWnd = m_hWnd;
return S_OK;
}
HRESULT WINAPI CDefView::ContextSensitiveHelp(BOOL fEnterMode)
{
FIXME("(%p) stub\n", this);
return E_NOTIMPL;
}
// FIXME: use the accel functions
HRESULT WINAPI CDefView::TranslateAccelerator(LPMSG lpmsg)
{
if (m_isEditing)
return S_FALSE;
if (lpmsg->message >= WM_KEYFIRST && lpmsg->message <= WM_KEYLAST)
{
if (::TranslateAcceleratorW(m_hWnd, m_hAccel, lpmsg) != 0)
return S_OK;
TRACE("-- key=0x%04lx\n", lpmsg->wParam);
}
return m_pShellBrowser->TranslateAcceleratorSB(lpmsg, 0);
}
HRESULT WINAPI CDefView::EnableModeless(BOOL fEnable)
{
FIXME("(%p)\n", this);
return E_NOTIMPL;
}
HRESULT WINAPI CDefView::UIActivate(UINT uState)
{
TRACE("(%p)->(state=%x)\n", this, uState);
// don't do anything if the state isn't changing
if (m_uState == uState)
return S_OK;
// OnActivate handles the menu merging and internal state
DoActivate(uState);
// only do this if we are active
if (uState != SVUIA_DEACTIVATE)
{
_ForceStatusBarResize();
// Set the text for the status bar
UpdateStatusbar();
}
return S_OK;
}
HRESULT WINAPI CDefView::Refresh()
{
TRACE("(%p)\n", this);
_DoFolderViewCB(SFVM_LISTREFRESHED, TRUE, 0);
m_ListView.DeleteAllItems();
FillList();
return S_OK;
}
HRESULT WINAPI CDefView::CreateViewWindow(IShellView *lpPrevView, LPCFOLDERSETTINGS lpfs, IShellBrowser *psb, RECT *prcView, HWND *phWnd)
{
return CreateViewWindow3(psb, lpPrevView, SV3CVW3_DEFAULT,
(FOLDERFLAGS)lpfs->fFlags, (FOLDERFLAGS)lpfs->fFlags, (FOLDERVIEWMODE)lpfs->ViewMode, NULL, prcView, phWnd);
}
HRESULT WINAPI CDefView::DestroyViewWindow()
{
TRACE("(%p)\n", this);
/* Make absolutely sure all our UI is cleaned up */
UIActivate(SVUIA_DEACTIVATE);
if (m_hAccel)
{
// MSDN: Accelerator tables loaded from resources are freed automatically when application terminates
m_hAccel = NULL;
}
if (m_hMenuArrangeModes)
{
DestroyMenu(m_hMenuArrangeModes);
m_hMenuArrangeModes = NULL;
}
if (m_hMenuViewModes)
{
DestroyMenu(m_hMenuViewModes);
m_hMenuViewModes = NULL;
}
if (m_hMenu)
{
DestroyMenu(m_hMenu);
m_hMenu = NULL;
}
if (m_ListView)
{
m_ListView.DestroyWindow();
}
if (m_hWnd)
{
_DoFolderViewCB(SFVM_WINDOWCLOSING, (WPARAM)m_hWnd, 0);
DestroyWindow();
}
m_pShellBrowser.Release();
m_pCommDlgBrowser.Release();
return S_OK;
}
HRESULT WINAPI CDefView::GetCurrentInfo(LPFOLDERSETTINGS lpfs)
{
TRACE("(%p)->(%p) vmode=%x flags=%x\n", this, lpfs,
m_FolderSettings.ViewMode, m_FolderSettings.fFlags);
if (!lpfs)
return E_INVALIDARG;
*lpfs = m_FolderSettings;
return S_OK;
}
HRESULT WINAPI CDefView::AddPropertySheetPages(DWORD dwReserved, LPFNADDPROPSHEETPAGE lpfn, LPARAM lparam)
{
TRACE("(%p)->(0x%lX, %p, %p)\n", this, dwReserved, lpfn, lparam);
SFVM_PROPPAGE_DATA Data = { dwReserved, lpfn, lparam };
_DoFolderViewCB(SFVM_ADDPROPERTYPAGES, 0, (LPARAM)&Data);
return S_OK;
}
static HRESULT Read(IStream *pS, LPVOID buffer, ULONG cb)
{
ULONG read;
HRESULT hr = pS->Read(buffer, cb, &read);
return FAILED(hr) ? hr : (cb == read ? S_OK : HResultFromWin32(ERROR_MORE_DATA));
}
static DWORD ReadDWORD(IPropertyBag *pPB, LPCWSTR name, DWORD def)
{
DWORD value;
HRESULT hr = SHPropertyBag_ReadDWORD(pPB, name, &value);
return SUCCEEDED(hr) ? value : def;
}
HRESULT CDefView::GetDefaultViewStream(DWORD Stgm, IStream **ppStream)
{
CLSID clsid;
HRESULT hr = IUnknown_GetClassID(m_pSFParent, &clsid);
if (SUCCEEDED(hr))
{
WCHAR path[MAX_PATH], name[39];
wsprintfW(path, L"%s\\%s", REGSTR_PATH_EXPLORER, L"Streams\\Default");
StringFromGUID2(clsid, name, 39);
*ppStream = SHOpenRegStream2W(HKEY_CURRENT_USER, path, name, Stgm);
hr = *ppStream ? S_OK : E_FAIL;
}
return hr;
}
static HRESULT LoadColumnsStream(PERSISTCOLUMNS &cols, IStream *pS)
{
HRESULT hr = Read(pS, &cols, FIELD_OFFSET(PERSISTCOLUMNS, Columns));
if (FAILED(hr))
return hr;
if (cols.Signature != PERSISTCOLUMNS::SIG || cols.Count > cols.MAXCOUNT)
return HResultFromWin32(ERROR_INVALID_DATA);
return Read(pS, &cols.Columns, sizeof(*cols.Columns) * cols.Count);
}
HRESULT CDefView::LoadViewState()
{
PERSISTCLASSICVIEWSTATE cvs;
PERSISTCOLUMNS cols;
CComPtr<IStream> pS;
CComPtr<IPropertyBag> pPB;
bool fallback = false;
HRESULT hrColumns = E_FAIL;
HRESULT hr = IUnknown_QueryServicePropertyBag(m_pShellBrowser, SHGVSPB_FOLDER, IID_PPV_ARG(IPropertyBag, &pPB));
if (SUCCEEDED(hr))
{
DWORD data;
if (FAILED(hr = SHPropertyBag_ReadDWORD(pPB, L"Mode", &data)))
goto loadfallback;
cvs.FolderSettings.ViewMode = data;
cvs.FolderSettings.fFlags = ReadDWORD(pPB, L"FFlags", FWF_NOGROUPING);
data = ReadDWORD(pPB, L"Sort", ~0ul);
cvs.SortColId = data != ~0ul ? (WORD)data : LISTVIEW_SORT_INFO::UNSPECIFIEDCOLUMN;
cvs.SortDir = (INT8)ReadDWORD(pPB, L"SortDir", 1);
if (SUCCEEDED(hrColumns = SHPropertyBag_ReadStream(pPB, L"ColInfo", &pS)))
hrColumns = LoadColumnsStream(cols, pS);
}
else
{
if (FAILED(hr = (m_pShellBrowser ? m_pShellBrowser->GetViewStateStream(STGM_READ, &pS) : E_UNEXPECTED)))
{
loadfallback:
hr = GetDefaultViewStream(STGM_READ, &pS);
fallback = true;
}
if (FAILED(hr) || FAILED(hr = Read(pS, &cvs, sizeof(cvs))))
return hr;
if (cvs.Signature != cvs.SIG)
return HResultFromWin32(ERROR_INVALID_DATA);
hrColumns = LoadColumnsStream(cols, pS);
}
m_FolderSettings.ViewMode = cvs.FolderSettings.ViewMode;
m_FolderSettings.fFlags &= ~cvs.VALIDFWF;
m_FolderSettings.fFlags |= cvs.FolderSettings.fFlags & cvs.VALIDFWF;
if (SUCCEEDED(hrColumns))
{
BOOL failed = FALSE;
if ((m_LoadColumnsList = DPA_Create(cols.Count)) != NULL)
{
for (UINT i = 0; i < cols.Count; ++i)
{
failed |= !DPA_SetPtr(m_LoadColumnsList, i, UlongToPtr(cols.Columns[i]));
}
}
if (failed || !cols.Count)
{
DPA_Destroy(m_LoadColumnsList);
m_LoadColumnsList = NULL;
}
}
m_sortInfo.bLoadedFromViewState = !fallback && m_LoadColumnsList && cvs.SortColId != LISTVIEW_SORT_INFO::UNSPECIFIEDCOLUMN;
m_sortInfo.bColumnIsFolderColumn = TRUE;
m_sortInfo.Direction = cvs.SortDir > 0 ? 1 : -1;
m_sortInfo.ListColumn = cvs.SortColId;
return hr;
}
HRESULT CDefView::SaveViewState(IStream *pStream)
{
if (!m_ListView.m_hWnd)
return E_UNEXPECTED;
int sortcol = MapListColumnToFolderColumn(m_sortInfo.ListColumn);
PERSISTCLASSICVIEWSTATE cvs;
cvs.SortColId = sortcol >= 0 ? (WORD)sortcol : 0;
cvs.SortDir = m_sortInfo.Direction;
PERSISTCOLUMNS cols;
cols.Signature = PERSISTCOLUMNS::SIG;
cols.Count = 0;
LVCOLUMN lvc;
lvc.mask = LVCF_WIDTH | LVCF_SUBITEM;
for (UINT i = 0, j = 0; i < PERSISTCOLUMNS::MAXCOUNT && ListView_GetColumn(m_ListView, j, &lvc); ++j)
{
HRESULT hr = MapListColumnToFolderColumn(lvc.iSubItem);
if (SUCCEEDED(hr))
{
cols.Columns[i] = MAKELONG(hr, lvc.cx);
cols.Count = ++i;
}
}
UINT cbColumns = FIELD_OFFSET(PERSISTCOLUMNS, Columns) + (sizeof(*cols.Columns) * cols.Count);
UpdateFolderViewFlags();
IPropertyBag *pPB;
HRESULT hr = S_OK;
if (pStream)
{
pStream->AddRef();
goto stream;
}
hr = IUnknown_QueryServicePropertyBag(m_pShellBrowser, SHGVSPB_FOLDER, IID_PPV_ARG(IPropertyBag, &pPB));
if (SUCCEEDED(hr))
{
UINT uViewMode;
GetCurrentViewMode(&uViewMode);
hr = SHPropertyBag_WriteDWORD(pPB, L"Mode", uViewMode);
SHPropertyBag_WriteDWORD(pPB, L"FFlags", m_FolderSettings.fFlags);
SHPropertyBag_WriteDWORD(pPB, L"Sort", cvs.SortColId);
SHPropertyBag_WriteDWORD(pPB, L"SortDir", cvs.SortDir);
pStream = cols.Count ? SHCreateMemStream((LPBYTE)&cols, cbColumns) : NULL;
if (!pStream || FAILED(SHPropertyBag_WriteStream(pPB, L"ColInfo", pStream)))
SHPropertyBag_Delete(pPB, L"ColInfo");
#if 0 // TODO
WCHAR name[MAX_PATH];
memcpy(name, L"ItemPos", sizeof(L"ItemPos"));
if (SHGetPerScreenResName(name + 7, _countof(name) - 7, 0))
{
if (GetAutoArrange() == S_FALSE)
// TODO: Save listview item positions
else
SHPropertyBag_Delete(pPB, name);
}
#endif
pPB->Release();
}
else if (SUCCEEDED(hr = (m_pShellBrowser ? m_pShellBrowser->GetViewStateStream(STGM_WRITE, &pStream) : E_UNEXPECTED)))
{
stream:
ULONG written;
cvs.Signature = cvs.SIG;
cvs.FolderSettings = m_FolderSettings;
hr = pStream->Write(&cvs, sizeof(cvs), &written);
if (SUCCEEDED(hr))
hr = pStream->Write(&cols, cbColumns, &written);
}
if (pStream)
pStream->Release();
return hr;
}
HRESULT WINAPI CDefView::SaveViewState()
{
if (!(m_FolderSettings.fFlags & FWF_NOBROWSERVIEWSTATE))
return SaveViewState(NULL);
return S_FALSE;
}
#define UPDATEFOLDERVIEWFLAGS(bits, bit, set) ( (bits) = ((bits) & ~(bit)) | ((set) ? (bit) : 0) )
void CDefView::UpdateFolderViewFlags()
{
UPDATEFOLDERVIEWFLAGS(m_FolderSettings.fFlags, FWF_AUTOARRANGE, GetAutoArrange() == S_OK);
UPDATEFOLDERVIEWFLAGS(m_FolderSettings.fFlags, FWF_SNAPTOGRID, _GetSnapToGrid() == S_OK);
UPDATEFOLDERVIEWFLAGS(m_FolderSettings.fFlags, FWF_NOGROUPING, !ListView_IsGroupViewEnabled(m_ListView.m_hWnd));
}
HRESULT WINAPI CDefView::SelectItem(PCUITEMID_CHILD pidl, UINT uFlags)
{
int i;
TRACE("(%p)->(pidl=%p, 0x%08x) stub\n", this, pidl, uFlags);
if (!m_ListView)
{
ERR("!m_ListView\n");
return E_FAIL;
}
i = LV_FindItemByPidl(pidl);
if (i == -1)
return S_OK;
LVITEMW lvItem = {0};
lvItem.mask = LVIF_STATE;
lvItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
while (m_ListView.GetItem(&lvItem))
{
if (lvItem.iItem == i)
{
if (uFlags & SVSI_SELECT)
lvItem.state |= LVIS_SELECTED;
else
lvItem.state &= ~LVIS_SELECTED;
if (uFlags & SVSI_FOCUSED)
lvItem.state |= LVIS_FOCUSED;
else
lvItem.state &= ~LVIS_FOCUSED;
}
else
{
if (uFlags & SVSI_DESELECTOTHERS)
{
lvItem.state &= ~LVIS_SELECTED;
}
lvItem.state &= ~LVIS_FOCUSED;
}
m_ListView.SetItem(&lvItem);
lvItem.iItem++;
}
if (uFlags & SVSI_ENSUREVISIBLE)
m_ListView.EnsureVisible(i, FALSE);
if((uFlags & SVSI_EDIT) == SVSI_EDIT)
m_ListView.EditLabel(i);
return S_OK;
}
HRESULT WINAPI CDefView::GetItemObject(UINT uItem, REFIID riid, LPVOID *ppvOut)
{
HRESULT hr = E_NOINTERFACE;
TRACE("(%p)->(uItem=0x%08x,\n\tIID=%s, ppv=%p)\n", this, uItem, debugstr_guid(&riid), ppvOut);
if (!ppvOut)
return E_INVALIDARG;
*ppvOut = NULL;
switch (uItem)
{
case SVGIO_BACKGROUND:
if (IsEqualIID(riid, IID_IContextMenu))
{
hr = CDefViewBckgrndMenu_CreateInstance(m_pSF2Parent, riid, ppvOut);
if (FAILED_UNEXPECTEDLY(hr))
return hr;
IUnknown_SetSite(*((IUnknown**)ppvOut), (IShellView *)this);
}
else if (IsEqualIID(riid, IID_IDispatch))
{
if (m_pShellFolderViewDual == NULL)
{
hr = CDefViewDual_Constructor(riid, (LPVOID*)&m_pShellFolderViewDual);
if (FAILED_UNEXPECTEDLY(hr))
return hr;
}
hr = m_pShellFolderViewDual->QueryInterface(riid, ppvOut);
}
break;
case SVGIO_SELECTION:
GetSelections();
hr = m_pSFParent->GetUIObjectOf(m_hWnd, m_cidl, m_apidl, riid, 0, ppvOut);
if (FAILED_UNEXPECTEDLY(hr))
return hr;
if (IsEqualIID(riid, IID_IContextMenu))
IUnknown_SetSite(*((IUnknown**)ppvOut), (IShellView *)this);
break;
}
TRACE("-- (%p)->(interface=%p)\n", this, *ppvOut);
return hr;
}
FOLDERVIEWMODE CDefView::GetDefaultViewMode()
{
FOLDERVIEWMODE mode = ((m_FolderSettings.fFlags & FWF_DESKTOP) || !IsOS(OS_SERVERADMINUI)) ? FVM_ICON : FVM_DETAILS;
FOLDERVIEWMODE temp = mode;
if (SUCCEEDED(_DoFolderViewCB(SFVM_DEFVIEWMODE, 0, (LPARAM)&temp)) && temp >= FVM_FIRST && temp <= FVM_LAST)
mode = temp;
return mode;
}
HRESULT STDMETHODCALLTYPE CDefView::GetCurrentViewMode(UINT *pViewMode)
{
TRACE("(%p)->(%p), stub\n", this, pViewMode);
if (!pViewMode)
return E_INVALIDARG;
*pViewMode = m_FolderSettings.ViewMode;
return S_OK;
}
HRESULT STDMETHODCALLTYPE CDefView::SetCurrentViewMode(UINT ViewMode)
{
DWORD dwStyle;
TRACE("(%p)->(%u), stub\n", this, ViewMode);
/* It's not redundant to check FVM_AUTO because it's a (UINT)-1 */
if (((INT)ViewMode < FVM_FIRST || (INT)ViewMode > FVM_LAST) && ((INT)ViewMode != FVM_AUTO))
return E_INVALIDARG;
/* Windows before Vista uses LVM_SETVIEW and possibly
LVM_SETEXTENDEDLISTVIEWSTYLE to set the style of the listview,
while later versions seem to accomplish this through other
means. */
switch (ViewMode)
{
case FVM_ICON:
dwStyle = LVS_ICON;
break;
case FVM_DETAILS:
dwStyle = LVS_REPORT;
break;
case FVM_SMALLICON:
dwStyle = LVS_SMALLICON;
break;
case FVM_LIST:
dwStyle = LVS_LIST;
break;
default:
{
FIXME("ViewMode %d not implemented\n", ViewMode);
dwStyle = LVS_LIST;
break;
}
}
m_ListView.ModifyStyle(LVS_TYPEMASK, dwStyle);
/* This will not necessarily be the actual mode set above.
This mimics the behavior of Windows XP. */
m_FolderSettings.ViewMode = ViewMode;
return S_OK;
}
HRESULT STDMETHODCALLTYPE CDefView::GetFolder(REFIID riid, void **ppv)
{
if (m_pSFParent == NULL)
return E_FAIL;
return m_pSFParent->QueryInterface(riid, ppv);
}
HRESULT STDMETHODCALLTYPE CDefView::Item(int iItemIndex, PITEMID_CHILD *ppidl)
{
PCUITEMID_CHILD pidl = _PidlByItem(iItemIndex);
if (pidl)
{
*ppidl = ILClone(pidl);
return S_OK;
}
*ppidl = 0;
return E_INVALIDARG;
}
HRESULT STDMETHODCALLTYPE CDefView::ItemCount(UINT uFlags, int *pcItems)
{
TRACE("(%p)->(%u %p)\n", this, uFlags, pcItems);
if (uFlags != SVGIO_ALLVIEW)
FIXME("some flags unsupported, %x\n", uFlags & ~SVGIO_ALLVIEW);
*pcItems = m_ListView.GetItemCount();
return S_OK;
}
HRESULT STDMETHODCALLTYPE CDefView::Items(UINT uFlags, REFIID riid, void **ppv)
{
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE CDefView::GetSelectionMarkedItem(int *piItem)
{
TRACE("(%p)->(%p)\n", this, piItem);
*piItem = m_ListView.GetSelectionMark();
return S_OK;
}
HRESULT STDMETHODCALLTYPE CDefView::GetFocusedItem(int *piItem)
{
TRACE("(%p)->(%p)\n", this, piItem);
*piItem = m_ListView.GetNextItem(-1, LVNI_FOCUSED);
return S_OK;
}
HRESULT STDMETHODCALLTYPE CDefView::GetItemPosition(PCUITEMID_CHILD pidl, POINT *ppt)
{
if (!m_ListView)
{
ERR("!m_ListView\n");
return E_FAIL;
}
int lvIndex = LV_FindItemByPidl(pidl);
if (lvIndex == -1 || ppt == NULL)
return E_INVALIDARG;
m_ListView.GetItemPosition(lvIndex, ppt);
return S_OK;
}
HRESULT STDMETHODCALLTYPE CDefView::GetSpacing(POINT *ppt)
{
TRACE("(%p)->(%p)\n", this, ppt);
if (!m_ListView)
{
ERR("!m_ListView\n");
return S_FALSE;
}
if (ppt)
{
SIZE spacing;
m_ListView.GetItemSpacing(spacing);
ppt->x = spacing.cx;
ppt->y = spacing.cy;
}
return S_OK;
}
HRESULT STDMETHODCALLTYPE CDefView::GetDefaultSpacing(POINT *ppt)
{
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE CDefView::GetAutoArrange()
{
return ((m_ListView.GetStyle() & LVS_AUTOARRANGE) ? S_OK : S_FALSE);
}
HRESULT CDefView::_GetSnapToGrid()
{
DWORD dwExStyle = (DWORD)m_ListView.SendMessage(LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0);
return ((dwExStyle & LVS_EX_SNAPTOGRID) ? S_OK : S_FALSE);
}
HRESULT STDMETHODCALLTYPE CDefView::SelectItem(int iItem, DWORD dwFlags)
{
LVITEMW lvItem;
TRACE("(%p)->(%d, %x)\n", this, iItem, dwFlags);
lvItem.state = 0;
lvItem.stateMask = LVIS_SELECTED;
if (dwFlags & SVSI_ENSUREVISIBLE)
m_ListView.EnsureVisible(iItem, 0);
/* all items */
if (dwFlags & SVSI_DESELECTOTHERS)
m_ListView.SetItemState(-1, 0, LVIS_SELECTED);
/* this item */
if (dwFlags & SVSI_SELECT)
lvItem.state |= LVIS_SELECTED;
if (dwFlags & SVSI_FOCUSED)
lvItem.stateMask |= LVIS_FOCUSED;
m_ListView.SetItemState(iItem, lvItem.state, lvItem.stateMask);
if ((dwFlags & SVSI_EDIT) == SVSI_EDIT)
m_ListView.EditLabel(iItem);
return S_OK;
}
HRESULT STDMETHODCALLTYPE CDefView::SelectAndPositionItems(UINT cidl, PCUITEMID_CHILD_ARRAY apidl, POINT *apt, DWORD dwFlags)
{
ASSERT(m_ListView);
/* Reset the selection */
m_ListView.SetItemState(-1, 0, LVIS_SELECTED);
int lvIndex;
for (UINT i = 0 ; i < cidl; i++)
{
lvIndex = LV_FindItemByPidl(apidl[i]);
if (lvIndex != -1)
{
SelectItem(lvIndex, dwFlags);
m_ListView.SetItemPosition(lvIndex, &apt[i]);
}
}
return S_OK;
}
// IShellView2 implementation
HRESULT STDMETHODCALLTYPE CDefView::GetView(SHELLVIEWID *view_guid, ULONG view_type)
{
FIXME("(%p)->(%p, %lu) stub\n", this, view_guid, view_type);
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE CDefView::CreateViewWindow2(LPSV2CVW2_PARAMS view_params)
{
return CreateViewWindow3(view_params->psbOwner, view_params->psvPrev,
SV3CVW3_DEFAULT, (FOLDERFLAGS)view_params->pfs->fFlags, (FOLDERFLAGS)view_params->pfs->fFlags,
(FOLDERVIEWMODE)view_params->pfs->ViewMode, view_params->pvid, view_params->prcView, &view_params->hwndView);
}
HRESULT STDMETHODCALLTYPE CDefView::CreateViewWindow3(IShellBrowser *psb, IShellView *psvPrevious, SV3CVW3_FLAGS view_flags, FOLDERFLAGS mask, FOLDERFLAGS flags, FOLDERVIEWMODE mode, const SHELLVIEWID *view_id, const RECT *prcView, HWND *hwnd)
{
OLEMENUGROUPWIDTHS omw = { { 0, 0, 0, 0, 0, 0 } };
const UINT SUPPORTED_SV3CVW3 = SV3CVW3_FORCEVIEWMODE | SV3CVW3_FORCEFOLDERFLAGS;
const UINT IGNORE_FWF = FWF_OWNERDATA; // FIXME: Support this
*hwnd = NULL;
TRACE("(%p)->(shlview=%p shlbrs=%p rec=%p hwnd=%p vmode=%x flags=%x)\n", this, psvPrevious, psb, prcView, hwnd, mode, flags);
if (prcView != NULL)
TRACE("-- left=%i top=%i right=%i bottom=%i\n", prcView->left, prcView->top, prcView->right, prcView->bottom);
/* Validate the Shell Browser */
if (psb == NULL || m_hWnd)
return E_UNEXPECTED;
if (view_flags & ~SUPPORTED_SV3CVW3)
FIXME("unsupported view flags 0x%08x\n", view_flags & ~SUPPORTED_SV3CVW3);
if (mode == FVM_AUTO)
mode = GetDefaultViewMode();
/* Set up the member variables */
m_pShellBrowser = psb;
m_FolderSettings.ViewMode = mode;
m_FolderSettings.fFlags = (mask & flags) & ~IGNORE_FWF;
if (view_id)
{
if (IsEqualIID(*view_id, VID_LargeIcons))
m_FolderSettings.ViewMode = FVM_ICON;
else if (IsEqualIID(*view_id, VID_SmallIcons))
m_FolderSettings.ViewMode = FVM_SMALLICON;
else if (IsEqualIID(*view_id, VID_List))
m_FolderSettings.ViewMode = FVM_LIST;
else if (IsEqualIID(*view_id, VID_Details))
m_FolderSettings.ViewMode = FVM_DETAILS;
else if (IsEqualIID(*view_id, VID_Thumbnails))
m_FolderSettings.ViewMode = FVM_THUMBNAIL;
else if (IsEqualIID(*view_id, VID_Tile))
m_FolderSettings.ViewMode = FVM_TILE;
else if (IsEqualIID(*view_id, VID_ThumbStrip))
m_FolderSettings.ViewMode = FVM_THUMBSTRIP;
else
FIXME("Ignoring unrecognized VID %s\n", debugstr_guid(view_id));
}
const UINT requestedViewMode = m_FolderSettings.ViewMode;
/* Get our parent window */
m_pShellBrowser->GetWindow(&m_hWndParent);
_DoFolderViewCB(SFVM_HWNDMAIN, 0, (LPARAM)m_hWndParent);
/* Try to get the ICommDlgBrowserInterface, adds a reference !!! */
m_pCommDlgBrowser = NULL;
if (SUCCEEDED(m_pShellBrowser->QueryInterface(IID_PPV_ARG(ICommDlgBrowser, &m_pCommDlgBrowser))))
{
TRACE("-- CommDlgBrowser\n");
}
LoadViewState();
if (view_flags & SV3CVW3_FORCEVIEWMODE)
m_FolderSettings.ViewMode = requestedViewMode;
if (view_flags & SV3CVW3_FORCEFOLDERFLAGS)
m_FolderSettings.fFlags = (mask & flags) & ~IGNORE_FWF;
RECT rcView = *prcView;
Create(m_hWndParent, rcView, NULL, WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_TABSTOP, 0, 0U);
if (m_hWnd == NULL)
return E_FAIL;
*hwnd = m_hWnd;
CheckToolbar();
if (!*hwnd)
return E_FAIL;
_DoFolderViewCB(SFVM_WINDOWCREATED, (WPARAM)m_hWnd, 0);
SetWindowPos(HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
UpdateWindow();
if (!m_hMenu)
{
m_hMenu = CreateMenu();
m_pShellBrowser->InsertMenusSB(m_hMenu, &omw);
TRACE("-- after fnInsertMenusSB\n");
}
_MergeToolbar();
return S_OK;
}
HRESULT STDMETHODCALLTYPE CDefView::HandleRename(LPCITEMIDLIST new_pidl)
{
FIXME("(%p)->(%p) stub\n", this, new_pidl);
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE CDefView::SelectAndPositionItem(LPCITEMIDLIST item, UINT flags, POINT *point)
{
FIXME("(%p)->(%p, %u, %p) stub\n", this, item, flags, point);
return E_NOTIMPL;
}
// IShellFolderView implementation
HRESULT STDMETHODCALLTYPE CDefView::Rearrange(LPARAM sort)
{
FIXME("(%p)->(%ld) stub\n", this, sort);
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE CDefView::GetArrangeParam(LPARAM *sort)
{
FIXME("(%p)->(%p) stub\n", this, sort);
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE CDefView::ArrangeGrid()
{
m_ListView.SetExtendedListViewStyle(LVS_EX_SNAPTOGRID, LVS_EX_SNAPTOGRID);
return S_OK;
}
HRESULT STDMETHODCALLTYPE CDefView::AutoArrange()
{
m_ListView.ModifyStyle(0, LVS_AUTOARRANGE);
m_ListView.Arrange(LVA_DEFAULT);
return S_OK;
}
HRESULT STDMETHODCALLTYPE CDefView::AddObject(PITEMID_CHILD pidl, UINT *item)
{
TRACE("(%p)->(%p %p)\n", this, pidl, item);
if (!m_ListView)
{
ERR("!m_ListView\n");
return E_FAIL;
}
*item = LV_AddItem(pidl);
return (int)*item >= 0 ? S_OK : E_OUTOFMEMORY;
}
HRESULT STDMETHODCALLTYPE CDefView::GetObject(PITEMID_CHILD *pidl, UINT item)
{
TRACE("(%p)->(%p %d)\n", this, pidl, item);
return Item(item, pidl);
}
HRESULT STDMETHODCALLTYPE CDefView::RemoveObject(PITEMID_CHILD pidl, UINT *item)
{
TRACE("(%p)->(%p %p)\n", this, pidl, item);
if (!m_ListView)
{
ERR("!m_ListView\n");
return E_FAIL;
}
if (pidl)
{
*item = LV_FindItemByPidl(ILFindLastID(pidl));
m_ListView.DeleteItem(*item);
}
else
{
*item = 0;
m_ListView.DeleteAllItems();
}
return S_OK;
}
HRESULT STDMETHODCALLTYPE CDefView::GetObjectCount(UINT *count)
{
TRACE("(%p)->(%p)\n", this, count);
*count = m_ListView.GetItemCount();
return S_OK;
}
HRESULT STDMETHODCALLTYPE CDefView::SetObjectCount(UINT count, UINT flags)
{
FIXME("(%p)->(%d %x) stub\n", this, count, flags);
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE CDefView::UpdateObject(PITEMID_CHILD pidl_old, PITEMID_CHILD pidl_new, UINT *item)
{
FIXME("(%p)->(%p %p %p) stub\n", this, pidl_old, pidl_new, item);
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE CDefView::RefreshObject(PITEMID_CHILD pidl, UINT *item)
{
FIXME("(%p)->(%p %p) stub\n", this, pidl, item);
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE CDefView::SetRedraw(BOOL redraw)
{
TRACE("(%p)->(%d)\n", this, redraw);
if (m_ListView)
m_ListView.SetRedraw(redraw);
return S_OK;
}
HRESULT STDMETHODCALLTYPE CDefView::GetSelectedCount(UINT *count)
{
FIXME("(%p)->(%p) stub\n", this, count);
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE CDefView::GetSelectedObjects(PCUITEMID_CHILD **pidl, UINT *items)
{
TRACE("(%p)->(%p %p)\n", this, pidl, items);
*items = GetSelections();
if (*items)
{
*pidl = static_cast<PCUITEMID_CHILD *>(LocalAlloc(0, *items * sizeof(PCUITEMID_CHILD)));
if (!*pidl)
{
return E_OUTOFMEMORY;
}
/* it's documented that caller shouldn't PIDLs, only array itself */
memcpy(*pidl, m_apidl, *items * sizeof(PCUITEMID_CHILD));
}
return S_OK;
}
HRESULT STDMETHODCALLTYPE CDefView::IsDropOnSource(IDropTarget *drop_target)
{
if ((m_iDragOverItem == -1 || m_pCurDropTarget == NULL) &&
(m_pSourceDataObject.p))
{
return S_OK;
}
return S_FALSE;
}
HRESULT STDMETHODCALLTYPE CDefView::GetDragPoint(POINT *pt)
{
if (!pt)
return E_INVALIDARG;
*pt = m_ptFirstMousePos;
return S_OK;
}
HRESULT STDMETHODCALLTYPE CDefView::GetDropPoint(POINT *pt)
{
FIXME("(%p)->(%p) stub\n", this, pt);
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE CDefView::MoveIcons(IDataObject *obj)
{
TRACE("(%p)->(%p)\n", this, obj);
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE CDefView::SetItemPos(PCUITEMID_CHILD pidl, POINT *pt)
{
FIXME("(%p)->(%p %p) stub\n", this, pidl, pt);
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE CDefView::IsBkDropTarget(IDropTarget *drop_target)
{
FIXME("(%p)->(%p) stub\n", this, drop_target);
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE CDefView::SetClipboard(BOOL move)
{
FIXME("(%p)->(%d) stub\n", this, move);
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE CDefView::SetPoints(IDataObject *obj)
{
FIXME("(%p)->(%p) stub\n", this, obj);
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE CDefView::GetItemSpacing(ITEMSPACING *spacing)
{
FIXME("(%p)->(%p) stub\n", this, spacing);
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE CDefView::SetCallback(IShellFolderViewCB *new_cb, IShellFolderViewCB **old_cb)
{
if (old_cb)
*old_cb = m_pShellFolderViewCB.Detach();
m_pShellFolderViewCB = new_cb;
m_pFolderFilter = NULL;
if (new_cb)
new_cb->QueryInterface(IID_PPV_ARG(IFolderFilter, &m_pFolderFilter));
return S_OK;
}
HRESULT STDMETHODCALLTYPE CDefView::Select(UINT flags)
{
FIXME("(%p)->(%d) stub\n", this, flags);
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE CDefView::QuerySupport(UINT *support)
{
TRACE("(%p)->(%p)\n", this, support);
return S_OK;
}
HRESULT STDMETHODCALLTYPE CDefView::SetAutomationObject(IDispatch *disp)
{
FIXME("(%p)->(%p) stub\n", this, disp);
return E_NOTIMPL;
}
HRESULT WINAPI CDefView::QueryStatus(const GUID *pguidCmdGroup, ULONG cCmds, OLECMD *prgCmds, OLECMDTEXT *pCmdText)
{
FIXME("(%p)->(%p(%s) 0x%08x %p %p\n",
this, pguidCmdGroup, debugstr_guid(pguidCmdGroup), cCmds, prgCmds, pCmdText);
if (!prgCmds)
return E_INVALIDARG;
for (UINT i = 0; i < cCmds; i++)
{
FIXME("\tprgCmds[%d].cmdID = %d\n", i, prgCmds[i].cmdID);
prgCmds[i].cmdf = 0;
}
return OLECMDERR_E_UNKNOWNGROUP;
}
///
// ISVOleCmdTarget_Exec(IOleCommandTarget)
//
// nCmdID is the OLECMDID_* enumeration
HRESULT WINAPI CDefView::Exec(const GUID *pguidCmdGroup, DWORD nCmdID, DWORD nCmdexecopt, VARIANT *pvaIn, VARIANT *pvaOut)
{
FIXME("(%p)->(\n\tTarget GUID:%s Command:0x%08x Opt:0x%08x %p %p)\n",
this, debugstr_guid(pguidCmdGroup), nCmdID, nCmdexecopt, pvaIn, pvaOut);
if (!pguidCmdGroup)
return OLECMDERR_E_UNKNOWNGROUP;
if (IsEqualCLSID(*pguidCmdGroup, m_Category))
{
if (nCmdID == FCIDM_SHVIEW_AUTOARRANGE)
{
if (V_VT(pvaIn) != VT_INT_PTR)
return OLECMDERR_E_NOTSUPPORTED;
TPMPARAMS params;
params.cbSize = sizeof(params);
params.rcExclude = *(RECT*) V_INTREF(pvaIn);
if (m_hMenuViewModes)
{
// Duplicate all but the last two items of the view modes menu
HMENU hmenuViewPopup = CreatePopupMenu();
Shell_MergeMenus(hmenuViewPopup, m_hMenuViewModes, 0, 0, 0xFFFF, 0);
DeleteMenu(hmenuViewPopup, GetMenuItemCount(hmenuViewPopup) - 1, MF_BYPOSITION);
DeleteMenu(hmenuViewPopup, GetMenuItemCount(hmenuViewPopup) - 1, MF_BYPOSITION);
CheckViewMode(hmenuViewPopup);
TrackPopupMenuEx(hmenuViewPopup, TPM_LEFTALIGN | TPM_TOPALIGN, params.rcExclude.left, params.rcExclude.bottom, m_hWndParent, &params);
::DestroyMenu(hmenuViewPopup);
}
// pvaOut is VT_I4 with value 0x403 (cmd id of the new mode maybe?)
V_VT(pvaOut) = VT_I4;
V_I4(pvaOut) = 0x403;
}
}
if (IsEqualIID(*pguidCmdGroup, CGID_Explorer) &&
(nCmdID == 0x29) &&
(nCmdexecopt == 4) && pvaOut)
return S_OK;
if (IsEqualIID(*pguidCmdGroup, CGID_ShellDocView) &&
(nCmdID == 9) &&
(nCmdexecopt == 0))
return 1;
if (IsEqualIID(*pguidCmdGroup, CGID_DefView))
{
CComPtr<IStream> pStream;
WCHAR SubKey[MAX_PATH];
switch (nCmdID)
{
case DVCMDID_SET_DEFAULTFOLDER_SETTINGS:
SHDeleteKey(HKEY_CURRENT_USER, L"Software\\Microsoft\\Windows\\ShellNoRoam\\Bags");
if (SUCCEEDED(GetDefaultViewStream(STGM_WRITE, &pStream)))
SaveViewState(pStream);
break;
case DVCMDID_RESET_DEFAULTFOLDER_SETTINGS:
wsprintfW(SubKey, L"%s\\%s", REGSTR_PATH_EXPLORER, L"Streams\\Default");
SHDeleteKey(HKEY_CURRENT_USER, SubKey);
SHDeleteKey(HKEY_CURRENT_USER, L"Software\\Microsoft\\Windows\\ShellNoRoam\\Bags");
m_FolderSettings.fFlags |= FWF_NOBROWSERVIEWSTATE; // Don't let this folder save itself
break;
}
}
return OLECMDERR_E_UNKNOWNGROUP;
}
/**********************************************************
* ISVDropTarget implementation
*/
/******************************************************************************
* drag_notify_subitem [Internal]
*
* Figure out the shellfolder object, which is currently under the mouse cursor
* and notify it via the IDropTarget interface.
*/
#define SCROLLAREAWIDTH 20
HRESULT CDefView::drag_notify_subitem(DWORD grfKeyState, POINTL pt, DWORD *pdwEffect)
{
LONG lResult;
HRESULT hr;
RECT clientRect;
/* The key state on drop doesn't have MK_LBUTTON or MK_RBUTTON because it
reflects the key state after the user released the button, so we need
to remember the last key state when the button was pressed */
m_grfKeyState = grfKeyState;
// Map from global to client coordinates and query the index of the
// listview-item, which is currently under the mouse cursor.
LVHITTESTINFO htinfo = {{pt.x, pt.y}, LVHT_ONITEM};
ScreenToClient(&htinfo.pt);
lResult = m_ListView.HitTest(&htinfo);
/* Send WM_*SCROLL messages every 250 ms during drag-scrolling */
::GetClientRect(m_ListView, &clientRect);
if (htinfo.pt.x == m_ptLastMousePos.x && htinfo.pt.y == m_ptLastMousePos.y &&
(htinfo.pt.x < SCROLLAREAWIDTH || htinfo.pt.x > clientRect.right - SCROLLAREAWIDTH ||
htinfo.pt.y < SCROLLAREAWIDTH || htinfo.pt.y > clientRect.bottom - SCROLLAREAWIDTH))
{
m_cScrollDelay = (m_cScrollDelay + 1) % 5; // DragOver is called every 50 ms
if (m_cScrollDelay == 0)
{
/* Mouse did hover another 250 ms over the scroll-area */
if (htinfo.pt.x < SCROLLAREAWIDTH)
m_ListView.SendMessageW(WM_HSCROLL, SB_LINEUP, 0);
if (htinfo.pt.x > clientRect.right - SCROLLAREAWIDTH)
m_ListView.SendMessageW(WM_HSCROLL, SB_LINEDOWN, 0);
if (htinfo.pt.y < SCROLLAREAWIDTH)
m_ListView.SendMessageW(WM_VSCROLL, SB_LINEUP, 0);
if (htinfo.pt.y > clientRect.bottom - SCROLLAREAWIDTH)
m_ListView.SendMessageW(WM_VSCROLL, SB_LINEDOWN, 0);
}
}
else
{
m_cScrollDelay = 0; // Reset, if cursor is not over the listview's scroll-area
}
m_ptLastMousePos = htinfo.pt;
::ClientToListView(m_ListView, &m_ptLastMousePos);
/* We need to check if we drag the selection over itself */
if (lResult != -1 && m_pSourceDataObject.p != NULL)
{
PCUITEMID_CHILD pidl = _PidlByItem(lResult);
for (UINT i = 0; i < m_cidl; i++)
{
if (pidl == m_apidl[i])
{
/* The item that is being draged is hovering itself. */
lResult = -1;
break;
}
}
}
// If we are still over the previous sub-item, notify it via DragOver and return
if (m_pCurDropTarget && lResult == m_iDragOverItem)
return m_pCurDropTarget->DragOver(grfKeyState, pt, pdwEffect);
// We've left the previous sub-item, notify it via DragLeave and release it
if (m_pCurDropTarget)
{
PCUITEMID_CHILD pidl = _PidlByItem(m_iDragOverItem);
if (pidl)
SelectItem(pidl, 0);
m_pCurDropTarget->DragLeave();
m_pCurDropTarget.Release();
}
m_iDragOverItem = lResult;
if (lResult == -1)
{
// We are not above one of the listview's subitems. Bind to the
// parent folder's DropTarget interface.
hr = m_pSFParent->CreateViewObject(NULL, IID_PPV_ARG(IDropTarget,&m_pCurDropTarget));
}
else
{
// Query the relative PIDL of the shellfolder object represented
// by the currently dragged over listview-item ...
PCUITEMID_CHILD pidl = _PidlByItem(lResult);
// ... and bind m_pCurDropTarget to the IDropTarget interface of an UIObject of this object
hr = m_pSFParent->GetUIObjectOf(m_ListView, 1, &pidl, IID_NULL_PPV_ARG(IDropTarget, &m_pCurDropTarget));
}
IUnknown_SetSite(m_pCurDropTarget, (IShellView *)this);
// If anything failed, m_pCurDropTarget should be NULL now, which ought to be a save state
if (FAILED(hr))
{
*pdwEffect = DROPEFFECT_NONE;
return hr;
}
if (m_iDragOverItem != -1)
{
SelectItem(m_iDragOverItem, SVSI_SELECT);
}
// Notify the item just entered via DragEnter
return m_pCurDropTarget->DragEnter(m_pCurDataObject, grfKeyState, pt, pdwEffect);
}
HRESULT WINAPI CDefView::DragEnter(IDataObject *pDataObject, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect)
{
if (*pdwEffect == DROPEFFECT_NONE)
return S_OK;
/* Get a hold on the data object for later calls to DragEnter on the sub-folders */
m_pCurDataObject = pDataObject;
HRESULT hr = drag_notify_subitem(grfKeyState, pt, pdwEffect);
if (SUCCEEDED(hr))
{
POINT ptClient = {pt.x, pt.y};
ScreenToClient(&ptClient);
ImageList_DragEnter(m_hWnd, ptClient.x, ptClient.y);
}
return hr;
}
HRESULT WINAPI CDefView::DragOver(DWORD grfKeyState, POINTL pt, DWORD *pdwEffect)
{
POINT ptClient = {pt.x, pt.y};
ScreenToClient(&ptClient);
ImageList_DragMove(ptClient.x, ptClient.y);
return drag_notify_subitem(grfKeyState, pt, pdwEffect);
}
HRESULT WINAPI CDefView::DragLeave()
{
ImageList_DragLeave(m_hWnd);
if (m_pCurDropTarget)
{
m_pCurDropTarget->DragLeave();
m_pCurDropTarget.Release();
}
if (m_pCurDataObject != NULL)
{
m_pCurDataObject.Release();
}
m_iDragOverItem = 0;
return S_OK;
}
INT CDefView::_FindInsertableIndexFromPoint(POINT pt)
{
RECT rcBound;
INT i, nCount = m_ListView.GetItemCount();
DWORD dwSpacing;
INT dx, dy;
BOOL bSmall = ((m_ListView.GetStyle() & LVS_TYPEMASK) != LVS_ICON);
// FIXME: LVM_GETORIGIN is broken. See CORE-17266
pt.x += m_ListView.GetScrollPos(SB_HORZ);
pt.y += m_ListView.GetScrollPos(SB_VERT);
if (m_ListView.GetStyle() & LVS_ALIGNLEFT)
{
// vertically
for (i = 0; i < nCount; ++i)
{
dwSpacing = ListView_GetItemSpacing(m_ListView, bSmall);
dx = LOWORD(dwSpacing);
dy = HIWORD(dwSpacing);
ListView_GetItemRect(m_ListView, i, &rcBound, LVIR_SELECTBOUNDS);
rcBound.right = rcBound.left + dx;
rcBound.bottom = rcBound.top + dy;
if (pt.x < rcBound.right && pt.y < (rcBound.top + rcBound.bottom) / 2)
{
return i;
}
}
for (i = nCount - 1; i >= 0; --i)
{
ListView_GetItemRect(m_ListView, i, &rcBound, LVIR_SELECTBOUNDS);
if (rcBound.left < pt.x && rcBound.top < pt.y)
{
return i + 1;
}
}
}
else
{
// horizontally
for (i = 0; i < nCount; ++i)
{
dwSpacing = ListView_GetItemSpacing(m_ListView, bSmall);
dx = LOWORD(dwSpacing);
dy = HIWORD(dwSpacing);
ListView_GetItemRect(m_ListView, i, &rcBound, LVIR_SELECTBOUNDS);
rcBound.right = rcBound.left + dx;
rcBound.bottom = rcBound.top + dy;
if (pt.y < rcBound.bottom && pt.x < rcBound.left)
{
return i;
}
if (pt.y < rcBound.bottom && pt.x < rcBound.right)
{
return i + 1;
}
}
for (i = nCount - 1; i >= 0; --i)
{
ListView_GetItemRect(m_ListView, i, &rcBound, LVIR_SELECTBOUNDS);
if (rcBound.left < pt.x && rcBound.top < pt.y)
{
return i + 1;
}
}
}
return nCount;
}
void CDefView::_HandleStatusBarResize(int nWidth)
{
LRESULT lResult;
if (m_isParentFolderSpecial)
{
int nPartArray[] = {-1};
m_pShellBrowser->SendControlMsg(FCW_STATUS, SB_SETPARTS, _countof(nPartArray), (LPARAM)nPartArray, &lResult);
return;
}
int nFileSizePartLength = 125;
const int nLocationPartLength = 150;
const int nRightPartsLength = nFileSizePartLength + nLocationPartLength;
int nObjectsPartLength = nWidth - nRightPartsLength;
// If the window is small enough just divide each part into thirds
// to match the behavior of Windows Server 2003
if (nObjectsPartLength <= nLocationPartLength)
nObjectsPartLength = nFileSizePartLength = nWidth / 3;
int nPartArray[] = {nObjectsPartLength, nObjectsPartLength + nFileSizePartLength, -1};
m_pShellBrowser->SendControlMsg(FCW_STATUS, SB_SETPARTS, _countof(nPartArray), (LPARAM)nPartArray, &lResult);
}
void CDefView::_ForceStatusBarResize()
{
// Get the handle for the status bar
HWND fStatusBar;
m_pShellBrowser->GetControlWindow(FCW_STATUS, &fStatusBar);
// Get the size of our status bar
RECT statusBarSize;
::GetWindowRect(fStatusBar, &statusBarSize);
// Resize the status bar
_HandleStatusBarResize(statusBarSize.right - statusBarSize.left);
}
typedef CSimpleMap<LPARAM, INT> CLParamIndexMap;
static INT CALLBACK
SelectionMoveCompareFunc(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
{
CLParamIndexMap *pmap = (CLParamIndexMap *)lParamSort;
INT i1 = pmap->Lookup(lParam1), i2 = pmap->Lookup(lParam2);
if (i1 < i2)
return -1;
if (i1 > i2)
return 1;
return 0;
}
void CDefView::_MoveSelectionOnAutoArrange(POINT pt)
{
// get insertable index from position
INT iPosition = _FindInsertableIndexFromPoint(pt);
// create identity mapping of indexes
CSimpleArray<INT> array;
INT nCount = m_ListView.GetItemCount();
for (INT i = 0; i < nCount; ++i)
{
array.Add(i);
}
// re-ordering mapping
INT iItem = -1;
while ((iItem = m_ListView.GetNextItem(iItem, LVNI_SELECTED)) >= 0)
{
INT iFrom = iItem, iTo = iPosition;
if (iFrom < iTo)
--iTo;
if (iFrom >= nCount)
iFrom = nCount - 1;
if (iTo >= nCount)
iTo = nCount - 1;
// shift indexes by swapping (like a bucket relay)
if (iFrom < iTo)
{
for (INT i = iFrom; i < iTo; ++i)
{
// swap array[i] and array[i + 1]
INT tmp = array[i];
array[i] = array[i + 1];
array[i + 1] = tmp;
}
}
else
{
for (INT i = iFrom; i > iTo; --i)
{
// swap array[i] and array[i - 1]
INT tmp = array[i];
array[i] = array[i - 1];
array[i - 1] = tmp;
}
}
}
// create mapping (ListView's lParam to index) from array
CLParamIndexMap map;
for (INT i = 0; i < nCount; ++i)
{
LPARAM lParam = m_ListView.GetItemData(array[i]);
map.Add(lParam, i);
}
// finally sort
m_ListView.SortItems(SelectionMoveCompareFunc, &map);
}
HRESULT WINAPI CDefView::Drop(IDataObject* pDataObject, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect)
{
ImageList_DragLeave(m_hWnd);
ImageList_EndDrag();
if ((IsDropOnSource(NULL) == S_OK) &&
(*pdwEffect & DROPEFFECT_MOVE) &&
(m_grfKeyState & MK_LBUTTON))
{
if (m_pCurDropTarget)
{
m_pCurDropTarget->DragLeave();
m_pCurDropTarget.Release();
}
POINT ptDrop = { pt.x, pt.y };
::ScreenToClient(m_ListView, &ptDrop);
::ClientToListView(m_ListView, &ptDrop);
m_ptLastMousePos = ptDrop;
m_ListView.SetRedraw(FALSE);
if (m_ListView.GetStyle() & LVS_AUTOARRANGE)
{
_MoveSelectionOnAutoArrange(m_ptLastMousePos);
}
else
{
POINT ptItem;
INT iItem = -1;
while ((iItem = m_ListView.GetNextItem(iItem, LVNI_SELECTED)) >= 0)
{
if (m_ListView.GetItemPosition(iItem, &ptItem))
{
ptItem.x += m_ptLastMousePos.x - m_ptFirstMousePos.x;
ptItem.y += m_ptLastMousePos.y - m_ptFirstMousePos.y;
m_ListView.SetItemPosition(iItem, &ptItem);
}
}
}
m_ListView.SetRedraw(TRUE);
}
else if (m_pCurDropTarget)
{
m_pCurDropTarget->Drop(pDataObject, grfKeyState, pt, pdwEffect);
m_pCurDropTarget.Release();
}
m_pCurDataObject.Release();
m_iDragOverItem = 0;
return S_OK;
}
HRESULT WINAPI CDefView::QueryContinueDrag(BOOL fEscapePressed, DWORD grfKeyState)
{
TRACE("(%p)\n", this);
if (fEscapePressed)
return DRAGDROP_S_CANCEL;
else if (!(grfKeyState & MK_LBUTTON) && !(grfKeyState & MK_RBUTTON))
return DRAGDROP_S_DROP;
else
return S_OK;
}
HRESULT WINAPI CDefView::GiveFeedback(DWORD dwEffect)
{
TRACE("(%p)\n", this);
return DRAGDROP_S_USEDEFAULTCURSORS;
}
HRESULT WINAPI CDefView::Draw(DWORD dwDrawAspect, LONG lindex, void *pvAspect, DVTARGETDEVICE *ptd, HDC hdcTargetDev, HDC hdcDraw, LPCRECTL lprcBounds, LPCRECTL lprcWBounds, BOOL (CALLBACK *pfnContinue)(ULONG_PTR dwContinue), ULONG_PTR dwContinue)
{
FIXME("Stub: this=%p\n", this);
return E_NOTIMPL;
}
HRESULT WINAPI CDefView::GetColorSet(DWORD dwDrawAspect, LONG lindex, void *pvAspect, DVTARGETDEVICE *ptd, HDC hicTargetDevice, LOGPALETTE **ppColorSet)
{
FIXME("Stub: this=%p\n", this);
return E_NOTIMPL;
}
HRESULT WINAPI CDefView::Freeze(DWORD dwDrawAspect, LONG lindex, void *pvAspect, DWORD *pdwFreeze)
{
FIXME("Stub: this=%p\n", this);
return E_NOTIMPL;
}
HRESULT WINAPI CDefView::Unfreeze(DWORD dwFreeze)
{
FIXME("Stub: this=%p\n", this);
return E_NOTIMPL;
}
HRESULT WINAPI CDefView::SetAdvise(DWORD aspects, DWORD advf, IAdviseSink *pAdvSink)
{
FIXME("partial stub: %p 0x%08x 0x%08x %p\n", this, aspects, advf, pAdvSink);
// FIXME: we set the AdviseSink, but never use it to send any advice
m_pAdvSink = pAdvSink;
m_dwAspects = aspects;
m_dwAdvf = advf;
return S_OK;
}
HRESULT WINAPI CDefView::GetAdvise(DWORD *pAspects, DWORD *pAdvf, IAdviseSink **ppAdvSink)
{
TRACE("this=%p pAspects=%p pAdvf=%p ppAdvSink=%p\n", this, pAspects, pAdvf, ppAdvSink);
if (ppAdvSink)
{
*ppAdvSink = m_pAdvSink;
m_pAdvSink.p->AddRef();
}
if (pAspects)
*pAspects = m_dwAspects;
if (pAdvf)
*pAdvf = m_dwAdvf;
return S_OK;
}
HRESULT STDMETHODCALLTYPE CDefView::QueryService(REFGUID guidService, REFIID riid, void **ppvObject)
{
if (IsEqualIID(guidService, SID_IShellBrowser) && m_pShellBrowser)
return m_pShellBrowser->QueryInterface(riid, ppvObject);
else if (IsEqualIID(guidService, SID_IFolderView))
return QueryInterface(riid, ppvObject);
return E_NOINTERFACE;
}
HRESULT CDefView::_MergeToolbar()
{
CComPtr<IExplorerToolbar> ptb;
HRESULT hr = S_OK;
hr = IUnknown_QueryService(m_pShellBrowser, IID_IExplorerToolbar, IID_PPV_ARG(IExplorerToolbar, &ptb));
if (FAILED(hr))
return hr;
m_Category = CGID_DefViewFrame;
hr = ptb->SetCommandTarget(static_cast<IOleCommandTarget*>(this), &m_Category, 0);
if (FAILED(hr))
return hr;
if (hr == S_FALSE)
return S_OK;
#if 0
hr = ptb->AddButtons(&m_Category, buttonsCount, buttons);
if (FAILED(hr))
return hr;
#endif
return S_OK;
}
HRESULT CDefView::_DoFolderViewCB(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
HRESULT hr = E_NOTIMPL;
if (m_pShellFolderViewCB)
{
hr = m_pShellFolderViewCB->MessageSFVCB(uMsg, wParam, lParam);
}
return hr;
}
HRESULT CDefView_CreateInstance(IShellFolder *pFolder, REFIID riid, LPVOID * ppvOut)
{
return ShellObjectCreatorInit<CDefView>(pFolder, riid, ppvOut);
}
HRESULT WINAPI SHCreateShellFolderView(const SFV_CREATE *pcsfv,
IShellView **ppsv)
{
CComPtr<IShellView> psv;
HRESULT hRes;
if (!ppsv)
return E_INVALIDARG;
*ppsv = NULL;
if (!pcsfv || pcsfv->cbSize != sizeof(*pcsfv))
return E_INVALIDARG;
TRACE("sf=%p outer=%p callback=%p\n",
pcsfv->pshf, pcsfv->psvOuter, pcsfv->psfvcb);
hRes = CDefView_CreateInstance(pcsfv->pshf, IID_PPV_ARG(IShellView, &psv));
if (FAILED(hRes))
return hRes;
if (pcsfv->psfvcb)
{
CComPtr<IShellFolderView> sfv;
if (SUCCEEDED(psv->QueryInterface(IID_PPV_ARG(IShellFolderView, &sfv))))
{
sfv->SetCallback(pcsfv->psfvcb, NULL);
}
}
*ppsv = psv.Detach();
return hRes;
}