Reactos
1/*
2 * ShellView
3 *
4 * Copyright 1998,1999 <juergen.schmied@debitel.net>
5 * Copyright 2022 Russell Johnson <russell.johnson@superdark.net>
6 *
7 * This is the view visualizing the data provided by the shellfolder.
8 * No direct access to data from pidls should be done from here.
9 *
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2.1 of the License, or (at your option) any later version.
14 *
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
19 *
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
23 *
24 * FIXME: CheckToolbar: handle the "new folder" and "folder up" button
25 */
26
27/*
28TODO:
29- When editing starts on item, set edit text to for editing value.
30- Fix shell view to handle view mode popup exec.
31- The background context menu should have a pidl just like foreground menus. This
32 causes crashes when dynamic handlers try to use the NULL pidl.
33- Reorder of columns doesn't work - might be bug in comctl32
34*/
35
36#include "precomp.h"
37
38#include <atlwin.h>
39#include <ui/rosctrls.h>
40
41WINE_DEFAULT_DEBUG_CHANNEL(shell);
42
43// It would be easier to allocate these from 1 and up but the original code used the entire
44// FCIDM_SHVIEWFIRST..FCIDM_SHVIEWLAST range when dealing with IContextMenu and to avoid
45// breaking anything relying on this we will allocate our ranges from the end instead.
46enum {
47 DEFVIEW_ARRANGESORT_MAXENUM = 9, // Limit the number of the visible columns we will display in the submenu
48 DEFVIEW_ARRANGESORT_MAX = DEFVIEW_ARRANGESORT_MAXENUM + 1, // Reserve one extra for the current sort-by column
49 DVIDM_ARRANGESORT_LAST = FCIDM_SHVIEWLAST,
50 DVIDM_ARRANGESORT_FIRST = DVIDM_ARRANGESORT_LAST - (DEFVIEW_ARRANGESORT_MAX - 1),
51 DVIDM_COMMDLG_SELECT = DVIDM_ARRANGESORT_FIRST - 1,
52
53 DVIDM_CONTEXTMENU_LAST = DVIDM_COMMDLG_SELECT - 1,
54 // FIXME: FCIDM_SHVIEWFIRST is 0 and using that with QueryContextMenu is a
55 // bad idea because it hides bugs related to the ids in ici.lpVerb.
56 // CONTEXT_MENU_BASE_ID acknowledges this but failed to apply the fix everywhere.
57 DVIDM_CONTEXTMENU_FIRST = FCIDM_SHVIEWFIRST,
58};
59#undef FCIDM_SHVIEWLAST // Don't use this constant, change DVIDM_CONTEXTMENU_LAST if you need a new id.
60
61struct LISTVIEW_SORT_INFO
62{
63 INT8 Direction;
64 bool bLoadedFromViewState;
65 bool bColumnIsFolderColumn;
66 UINT8 Reserved; // Unused
67 INT ListColumn;
68 INT FolderColumn; // Only valid during a sorting operation
69
70 enum { UNSPECIFIEDCOLUMN = -1 };
71 void Reset()
72 {
73 *(UINT*)this = 0;
74 ListColumn = UNSPECIFIEDCOLUMN;
75 }
76};
77
78// For the context menu of the def view, the id of the items are based on 1 because we need
79// to call TrackPopupMenu and let it use the 0 value as an indication that the menu was canceled
80#define CONTEXT_MENU_BASE_ID 1
81
82struct PERSISTCOLUMNS
83{
84 enum { MAXCOUNT = 100 };
85 static const UINT SIG = ('R' << 0) | ('O' << 8) | ('S' << 16) | (('c') << 24);
86 UINT Signature;
87 UINT Count;
88 UINT Columns[MAXCOUNT];
89};
90
91struct PERSISTCLASSICVIEWSTATE
92{
93 static const UINT SIG = ('R' << 0) | ('O' << 8) | ('S' << 16) | (('c' ^ 'v' ^ 's') << 24);
94 UINT Signature;
95 WORD SortColId;
96 INT8 SortDir;
97 static const UINT VALIDFWF = FWF_AUTOARRANGE | FWF_SNAPTOGRID | FWF_NOGROUPING; // Note: The desktop applies FWF_NOICONS when appropriate
98 FOLDERSETTINGS FolderSettings;
99};
100
101static UINT
102GetContextMenuFlags(IShellBrowser *pSB, SFGAOF sfgao)
103{
104 UINT cmf = CMF_NORMAL;
105 if (GetKeyState(VK_SHIFT) < 0)
106 cmf |= CMF_EXTENDEDVERBS;
107 if (sfgao & SFGAO_CANRENAME)
108 cmf |= CMF_CANRENAME;
109 HWND hwnd = NULL;
110 if (pSB && SUCCEEDED(pSB->GetControlWindow(FCW_TREE, &hwnd)) && hwnd)
111 cmf |= CMF_EXPLORE;
112 return cmf;
113}
114
115// Convert client coordinates to listview coordinates
116static void
117ClientToListView(HWND hwndLV, POINT *ppt)
118{
119 POINT Origin = {};
120
121 // FIXME: LVM_GETORIGIN is broken. See CORE-17266
122 if (!ListView_GetOrigin(hwndLV, &Origin))
123 return;
124
125 ppt->x += Origin.x;
126 ppt->y += Origin.y;
127}
128
129// Helper struct to automatically cleanup the IContextMenu
130// We want to explicitly reset the Site, so there are no circular references
131struct MenuCleanup
132{
133 CComPtr<IContextMenu> &m_pCM;
134 HMENU &m_hMenu;
135
136 MenuCleanup(CComPtr<IContextMenu> &pCM, HMENU& menu)
137 : m_pCM(pCM), m_hMenu(menu)
138 {
139 }
140 ~MenuCleanup()
141 {
142 if (m_pCM)
143 {
144 IUnknown_SetSite(m_pCM, NULL);
145 m_pCM.Release();
146 }
147 if (m_hMenu)
148 {
149 DestroyMenu(m_hMenu);
150 m_hMenu = NULL;
151 }
152 }
153};
154
155static BOOL AppendMenuItem(HMENU hMenu, UINT MF, UINT Id, LPCWSTR String, SIZE_T Data = 0)
156{
157 MENUITEMINFOW mii;
158 mii.cbSize = sizeof(mii);
159 mii.fMask = MIIM_TYPE | MIIM_ID | MIIM_DATA | MIIM_STATE;
160 mii.fState = MF & (MFS_CHECKED | MFS_DISABLED);
161 mii.fType = MF & ~mii.fState;
162 mii.wID = Id;
163 mii.dwTypeData = const_cast<LPWSTR>(String);
164 mii.dwItemData = Data;
165 return InsertMenuItemW(hMenu, -1, TRUE, &mii);
166}
167
168static SIZE_T GetMenuItemDataById(HMENU hMenu, UINT Id)
169{
170 MENUITEMINFOW mii;
171 mii.cbSize = FIELD_OFFSET(MENUITEMINFOW, hbmpItem);
172 mii.fMask = MIIM_DATA;
173 if (GetMenuItemInfoW(hMenu, Id, FALSE, &mii))
174 return mii.dwItemData;
175 else
176 return 0;
177}
178
179static HMENU GetSubmenuByID(HMENU hmenu, UINT id)
180{
181 MENUITEMINFOW mii = {sizeof(mii), MIIM_SUBMENU};
182 if (::GetMenuItemInfoW(hmenu, id, FALSE, &mii))
183 return mii.hSubMenu;
184
185 return NULL;
186}
187
188/* ReallyGetMenuItemID returns the id of an item even if it opens a submenu,
189 GetMenuItemID returns -1 if the specified item opens a submenu */
190static UINT ReallyGetMenuItemID(HMENU hmenu, int i)
191{
192 MENUITEMINFOW mii = {sizeof(mii), MIIM_ID};
193 if (::GetMenuItemInfoW(hmenu, i, TRUE, &mii))
194 return mii.wID;
195
196 return UINT_MAX;
197}
198
199static UINT CalculateCharWidth(HWND hwnd)
200{
201 UINT ret = 0;
202 HDC hDC = GetDC(hwnd);
203 if (hDC)
204 {
205 HGDIOBJ hOrg = SelectObject(hDC, (HGDIOBJ)SendMessage(hwnd, WM_GETFONT, 0, 0));
206 ret = GdiGetCharDimensions(hDC, NULL, NULL);
207 SelectObject(hDC, hOrg);
208 ReleaseDC(hwnd, hDC);
209 }
210 return ret;
211}
212
213static inline COLORREF GetViewColor(COLORREF Clr, UINT SysFallback)
214{
215 return Clr != CLR_INVALID ? Clr : GetSysColor(SysFallback);
216}
217
218#define VID_Default ( *(const SHELLVIEWID*)&IID_CDefView )
219extern HRESULT ShellViewIdToFolderViewMode(const SHELLVIEWID *pVid);
220extern const SHELLVIEWID* FolderViewModeToShellViewId(UINT FVM);
221
222class CDefView :
223 public CWindowImpl<CDefView, CWindow, CControlWinTraits>,
224 public CComObjectRootEx<CComMultiThreadModelNoCS>,
225 public IShellView3,
226 public IFolderView,
227 public IShellFolderView,
228 public IOleCommandTarget,
229 public IDropTarget,
230 public IDropSource,
231 public IViewObject,
232 public IServiceProvider
233{
234private:
235 CComPtr<IShellFolder> m_pSFParent;
236 CComPtr<IShellFolder2> m_pSF2Parent;
237 CComPtr<IShellDetails> m_pSDParent;
238 CComPtr<IShellFolderViewCB> m_pShellFolderViewCB;
239 CComPtr<IShellBrowser> m_pShellBrowser;
240 CComPtr<ICommDlgBrowser> m_pCommDlgBrowser;
241 CComPtr<IFolderFilter> m_pFolderFilter;
242 CComPtr<IShellFolderViewDual> m_pShellFolderViewDual;
243 ClipboardViewerChain m_ClipboardChain;
244 CListView m_ListView;
245 HWND m_hWndParent;
246 FOLDERSETTINGS m_FolderSettings;
247 HMENU m_hMenu; // Handle to the menu bar of the browser
248 HMENU m_hMenuArrangeModes; // Handle to the popup menu with the arrange modes
249 HMENU m_hMenuViewModes; // Handle to the popup menu with the view modes
250 HMENU m_hContextMenu; // Handle to the open context menu
251 BOOL m_bmenuBarInitialized;
252 UINT m_uState;
253 UINT m_cidl;
254 PCUITEMID_CHILD *m_apidl;
255 PIDLIST_ABSOLUTE m_pidlParent;
256 HDPA m_LoadColumnsList;
257 HDPA m_ListToFolderColMap;
258 LISTVIEW_SORT_INFO m_sortInfo;
259 ULONG m_hNotify; // Change notification handle
260 HACCEL m_hAccel;
261 DWORD m_dwAspects;
262 DWORD m_dwAdvf;
263 CComPtr<IAdviseSink> m_pAdvSink;
264 // for drag and drop
265 CComPtr<IDataObject> m_pSourceDataObject;
266 CComPtr<IDropTarget> m_pCurDropTarget; // The sub-item, which is currently dragged over
267 CComPtr<IDataObject> m_pCurDataObject; // The dragged data-object
268 LONG m_iDragOverItem; // Dragged over item's index, if m_pCurDropTarget != NULL
269 UINT m_cScrollDelay; // Send a WM_*SCROLL msg every 250 ms during drag-scroll
270 POINT m_ptLastMousePos; // Mouse position at last DragOver call
271 POINT m_ptFirstMousePos; // Mouse position when the drag operation started
272 DWORD m_grfKeyState;
273 //
274 CComPtr<IContextMenu> m_pCM;
275 CComPtr<IContextMenu> m_pFileMenu;
276
277 BOOL m_isEditing;
278 signed char m_SpecialFolder;
279 bool m_isFullStatusBar;
280 bool m_ScheduledStatusbarUpdate;
281 bool m_HasCutItems;
282
283 CLSID m_Category;
284 BOOL m_Destroyed;
285 SFVM_CUSTOMVIEWINFO_DATA m_viewinfo_data;
286
287 HICON m_hMyComputerIcon;
288
289 HRESULT _MergeToolbar();
290 BOOL _Sort(int Col = -1);
291 HRESULT _DoFolderViewCB(UINT uMsg, WPARAM wParam, LPARAM lParam);
292 HRESULT _GetSnapToGrid();
293 void _MoveSelectionOnAutoArrange(POINT pt);
294 INT _FindInsertableIndexFromPoint(POINT pt);
295 void _HandleStatusBarResize(int width);
296 void _ForceStatusBarResize();
297 void _DoCopyToMoveToFolder(BOOL bCopy);
298 BOOL IsDesktop() const { return m_FolderSettings.fFlags & FWF_DESKTOP; }
299
300 inline BOOL IsSpecialFolder(int &csidl) const
301 {
302 csidl = m_SpecialFolder;
303 return m_SpecialFolder >= 0;
304 }
305
306public:
307 CDefView();
308 ~CDefView();
309 HRESULT WINAPI Initialize(IShellFolder *shellFolder);
310 HRESULT IncludeObject(PCUITEMID_CHILD pidl);
311 HRESULT OnDefaultCommand();
312 HRESULT OnStateChange(UINT uFlags);
313 void UpdateStatusbar();
314 void UpdateStatusbarLocation();
315 void CheckToolbar();
316 BOOL CreateList();
317 void UpdateListColors();
318 BOOL InitList();
319 static INT CALLBACK ListViewCompareItems(LPARAM lParam1, LPARAM lParam2, LPARAM lpData);
320
321 HRESULT MapFolderColumnToListColumn(UINT FoldCol);
322 HRESULT MapListColumnToFolderColumn(UINT ListCol);
323 HRESULT GetDetailsByFolderColumn(PCUITEMID_CHILD pidl, UINT FoldCol, SHELLDETAILS &sd);
324 HRESULT GetDetailsByListColumn(PCUITEMID_CHILD pidl, UINT ListCol, SHELLDETAILS &sd);
325 HRESULT LoadColumn(UINT FoldCol, UINT ListCol, BOOL Insert, UINT ForceWidth = 0);
326 HRESULT LoadColumns(SIZE_T *pColList = NULL, UINT ColListCount = 0);
327 void ColumnListChanged();
328 SFGAOF GetItemAttributes(PCUITEMID_CHILD pidl, UINT Query);
329 SFGAOF GetItemAttributes(int i, UINT Query);
330 void RefreshGhostedState();
331 PCUITEMID_CHILD _PidlByItem(int i);
332 PCUITEMID_CHILD _PidlByItem(LVITEM& lvItem);
333 int LV_FindItemByPidl(PCUITEMID_CHILD pidl);
334 int LV_AddItem(PCUITEMID_CHILD pidl);
335 BOOLEAN LV_DeleteItem(PCUITEMID_CHILD pidl);
336 BOOLEAN LV_RenameItem(PCUITEMID_CHILD pidlOld, PCUITEMID_CHILD pidlNew);
337 BOOL LV_UpdateItem(INT nItem, PCUITEMID_CHILD pidl);
338 BOOL LV_UpdateItem(PCUITEMID_CHILD pidl);
339 void LV_RefreshIcon(INT iItem);
340 void LV_RefreshIcons();
341 static INT CALLBACK fill_list(LPVOID ptr, LPVOID arg);
342 HRESULT FillList(BOOL IsRefreshCommand = TRUE);
343 HRESULT FillFileMenu();
344 HRESULT FillEditMenu();
345 HRESULT FillViewMenu();
346 HRESULT FillArrangeAsMenu(HMENU hmenuArrange);
347 HRESULT CheckViewMode(HMENU hmenuView);
348 LRESULT DoColumnContextMenu(LRESULT lParam);
349 HRESULT SelectAndPositionItem(int Idx, UINT fSVSI, POINT *ppt);
350 UINT GetSelections();
351 SFGAOF GetSelectionAttributes(SFGAOF Query);
352 HRESULT OpenSelectedItems(PCSTR pszVerb = NULL);
353 void OnDeactivate();
354 void DoActivate(UINT uState);
355 HRESULT drag_notify_subitem(DWORD grfKeyState, POINTL pt, DWORD *pdwEffect);
356 HRESULT InvokeContextMenuCommand(CComPtr<IContextMenu>& pCM, LPCSTR lpVerb, POINT* pt = NULL, bool TryMapVerb = false);
357 LRESULT OnExplorerCommand(UINT uCommand, BOOL bUseSelection);
358 FOLDERVIEWMODE GetDefaultViewMode();
359 HRESULT GetDefaultViewStream(DWORD Stgm, IStream **ppStream);
360 HRESULT LoadViewState();
361 HRESULT SaveViewState(IStream *pStream);
362 void UpdateFolderViewFlags();
363 UINT GetItemActivateFlags();
364
365 DWORD GetCommDlgViewFlags()
366 {
367 CComPtr<ICommDlgBrowser2> pcdb2;
368 if (m_pCommDlgBrowser && SUCCEEDED(m_pCommDlgBrowser->QueryInterface(IID_PPV_ARG(ICommDlgBrowser2, &pcdb2))))
369 {
370 DWORD flags;
371 if (SUCCEEDED(pcdb2->GetViewFlags(&flags)))
372 return flags;
373 }
374 return 0;
375 }
376
377 static inline bool IsSupportedFolderViewMode(int Mode)
378 {
379 return Mode >= FVM_FIRST && Mode <= FVM_DETAILS; // We don't support Tile nor Thumbstrip
380 }
381
382 // *** IOleWindow methods ***
383 STDMETHOD(GetWindow)(HWND *lphwnd) override;
384 STDMETHOD(ContextSensitiveHelp)(BOOL fEnterMode) override;
385
386 // *** IShellView methods ***
387 STDMETHOD(TranslateAccelerator)(MSG *pmsg) override;
388 STDMETHOD(EnableModeless)(BOOL fEnable) override;
389 STDMETHOD(UIActivate)(UINT uState) override;
390 STDMETHOD(Refresh)() override;
391 STDMETHOD(CreateViewWindow)(IShellView *psvPrevious, LPCFOLDERSETTINGS pfs, IShellBrowser *psb, RECT *prcView, HWND *phWnd) override;
392 STDMETHOD(DestroyViewWindow)() override;
393 STDMETHOD(GetCurrentInfo)(LPFOLDERSETTINGS pfs) override;
394 STDMETHOD(AddPropertySheetPages)(DWORD dwReserved, LPFNSVADDPROPSHEETPAGE pfn, LPARAM lparam) override;
395 STDMETHOD(SaveViewState)() override;
396 STDMETHOD(SelectItem)(PCUITEMID_CHILD pidlItem, SVSIF uFlags) override;
397 STDMETHOD(GetItemObject)(UINT uItem, REFIID riid, void **ppv) override;
398
399 // *** IShellView2 methods ***
400 STDMETHOD(GetView)(SHELLVIEWID *pVid, ULONG view_type) override;
401 STDMETHOD(CreateViewWindow2)(LPSV2CVW2_PARAMS view_params) override;
402 STDMETHOD(HandleRename)(LPCITEMIDLIST pidl) override;
403 STDMETHOD(SelectAndPositionItem)(LPCITEMIDLIST item, UINT flags, POINT *point) override;
404
405 // *** IShellView3 methods ***
406 STDMETHOD(CreateViewWindow3)(
407 IShellBrowser *psb,
408 IShellView *psvPrevious,
409 SV3CVW3_FLAGS view_flags,
410 FOLDERFLAGS mask,
411 FOLDERFLAGS flags,
412 FOLDERVIEWMODE mode,
413 const SHELLVIEWID *view_id,
414 const RECT *prcView,
415 HWND *hwnd) override;
416
417 // *** IFolderView methods ***
418 STDMETHOD(GetCurrentViewMode)(UINT *pViewMode) override;
419 STDMETHOD(SetCurrentViewMode)(UINT ViewMode) override;
420 STDMETHOD(GetFolder)(REFIID riid, void **ppv) override;
421 STDMETHOD(Item)(int iItemIndex, PITEMID_CHILD *ppidl) override;
422 STDMETHOD(ItemCount)(UINT uFlags, int *pcItems) override;
423 STDMETHOD(Items)(UINT uFlags, REFIID riid, void **ppv) override;
424 STDMETHOD(GetSelectionMarkedItem)(int *piItem) override;
425 STDMETHOD(GetFocusedItem)(int *piItem) override;
426 STDMETHOD(GetItemPosition)(PCUITEMID_CHILD pidl, POINT *ppt) override;
427 STDMETHOD(GetSpacing)(POINT *ppt) override;
428 STDMETHOD(GetDefaultSpacing)(POINT *ppt) override;
429 STDMETHOD(GetAutoArrange)() override;
430 STDMETHOD(SelectItem)(int iItem, DWORD dwFlags) override;
431 STDMETHOD(SelectAndPositionItems)(UINT cidl, PCUITEMID_CHILD_ARRAY apidl, POINT *apt, DWORD dwFlags) override;
432
433 // *** IShellFolderView methods ***
434 STDMETHOD(Rearrange)(LPARAM sort) override;
435 STDMETHOD(GetArrangeParam)(LPARAM *sort) override;
436 STDMETHOD(ArrangeGrid)() override;
437 STDMETHOD(AutoArrange)() override;
438 STDMETHOD(AddObject)(PITEMID_CHILD pidl, UINT *item) override;
439 STDMETHOD(GetObject)(PITEMID_CHILD *pidl, UINT item) override;
440 STDMETHOD(RemoveObject)(PITEMID_CHILD pidl, UINT *item) override;
441 STDMETHOD(GetObjectCount)(UINT *count) override;
442 STDMETHOD(SetObjectCount)(UINT count, UINT flags) override;
443 STDMETHOD(UpdateObject)(PITEMID_CHILD pidl_old, PITEMID_CHILD pidl_new, UINT *item) override;
444 STDMETHOD(RefreshObject)(PITEMID_CHILD pidl, UINT *item) override;
445 STDMETHOD(SetRedraw)(BOOL redraw) override;
446 STDMETHOD(GetSelectedCount)(UINT *count) override;
447 STDMETHOD(GetSelectedObjects)(PCUITEMID_CHILD **pidl, UINT *items) override;
448 STDMETHOD(IsDropOnSource)(IDropTarget *drop_target) override;
449 STDMETHOD(GetDragPoint)(POINT *pt) override;
450 STDMETHOD(GetDropPoint)(POINT *pt) override;
451 STDMETHOD(MoveIcons)(IDataObject *obj) override;
452 STDMETHOD(SetItemPos)(PCUITEMID_CHILD pidl, POINT *pt) override;
453 STDMETHOD(IsBkDropTarget)(IDropTarget *drop_target) override;
454 STDMETHOD(SetClipboard)(BOOL move) override;
455 STDMETHOD(SetPoints)(IDataObject *obj) override;
456 STDMETHOD(GetItemSpacing)(ITEMSPACING *spacing) override;
457 STDMETHOD(SetCallback)(IShellFolderViewCB *new_cb, IShellFolderViewCB **old_cb) override;
458 STDMETHOD(Select)(UINT flags) override;
459 STDMETHOD(QuerySupport)(UINT *support) override;
460 STDMETHOD(SetAutomationObject)(IDispatch *disp) override;
461
462 // *** IOleCommandTarget methods ***
463 STDMETHOD(QueryStatus)(const GUID *pguidCmdGroup, ULONG cCmds, OLECMD prgCmds[], OLECMDTEXT *pCmdText) override;
464 STDMETHOD(Exec)(const GUID *pguidCmdGroup, DWORD nCmdID, DWORD nCmdexecopt, VARIANT *pvaIn, VARIANT *pvaOut) override;
465
466 // *** IDropTarget methods ***
467 STDMETHOD(DragEnter)(IDataObject *pDataObj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect) override;
468 STDMETHOD(DragOver)(DWORD grfKeyState, POINTL pt, DWORD *pdwEffect) override;
469 STDMETHOD(DragLeave)() override;
470 STDMETHOD(Drop)(IDataObject *pDataObj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect) override;
471
472 // *** IDropSource methods ***
473 STDMETHOD(QueryContinueDrag)(BOOL fEscapePressed, DWORD grfKeyState) override;
474 STDMETHOD(GiveFeedback)(DWORD dwEffect) override;
475
476 // *** IViewObject methods ***
477 STDMETHOD(Draw)(DWORD dwDrawAspect, LONG lindex, void *pvAspect, DVTARGETDEVICE *ptd,
478 HDC hdcTargetDev, HDC hdcDraw, LPCRECTL lprcBounds, LPCRECTL lprcWBounds,
479 BOOL (STDMETHODCALLTYPE *pfnContinue)(ULONG_PTR dwContinue), ULONG_PTR dwContinue) override;
480 STDMETHOD(GetColorSet)(DWORD dwDrawAspect, LONG lindex, void *pvAspect,
481 DVTARGETDEVICE *ptd, HDC hicTargetDev, LOGPALETTE **ppColorSet) override;
482 STDMETHOD(Freeze)(DWORD dwDrawAspect, LONG lindex, void *pvAspect, DWORD *pdwFreeze) override;
483 STDMETHOD(Unfreeze)(DWORD dwFreeze) override;
484 STDMETHOD(SetAdvise)(DWORD aspects, DWORD advf, IAdviseSink *pAdvSink) override;
485 STDMETHOD(GetAdvise)(DWORD *pAspects, DWORD *pAdvf, IAdviseSink **ppAdvSink) override;
486
487 // *** IServiceProvider methods ***
488 STDMETHOD(QueryService)(REFGUID guidService, REFIID riid, void **ppvObject) override;
489
490 // Message handlers
491 LRESULT OnShowWindow(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled);
492 LRESULT OnGetDlgCode(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled);
493 LRESULT OnDestroy(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled);
494 LRESULT OnEraseBackground(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled);
495 LRESULT OnPrintClient(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled);
496 LRESULT OnSysColorChange(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled);
497 LRESULT OnGetShellBrowser(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled);
498 LRESULT OnNCCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled);
499 LRESULT OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled);
500 LRESULT OnContextMenu(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled);
501 LRESULT OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled);
502 LRESULT OnActivate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled);
503 LRESULT OnSetFocus(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled);
504 LRESULT OnKillFocus(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled);
505 LRESULT OnCommand(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled);
506 LRESULT OnNotify(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled);
507 LRESULT OnChangeNotify(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled);
508 LRESULT OnUpdateStatusbar(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled);
509 LRESULT OnMenuMessage(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled);
510 LRESULT OnSettingChange(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled);
511 LRESULT OnInitMenuPopup(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled);
512 LRESULT OnChangeCBChain(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled);
513 LRESULT OnDrawClipboard(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled);
514
515 virtual VOID OnFinalMessage(HWND) override;
516
517 static ATL::CWndClassInfo& GetWndClassInfo()
518 {
519 static ATL::CWndClassInfo wc =
520 {
521 { sizeof(WNDCLASSEX), CS_PARENTDC, StartWindowProc,
522 0, 0, NULL, NULL,
523 LoadCursor(NULL, IDC_ARROW), NULL, NULL, L"SHELLDLL_DefView", NULL
524 },
525 NULL, NULL, IDC_ARROW, TRUE, 0, _T("")
526 };
527 return wc;
528 }
529
530 virtual WNDPROC GetWindowProc() override
531 {
532 return WindowProc;
533 }
534
535 static LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
536 {
537 CDefView *pThis;
538 LRESULT result;
539
540 // Must hold a reference during message handling
541 pThis = reinterpret_cast<CDefView *>(hWnd);
542 pThis->AddRef();
543 result = CWindowImpl<CDefView, CWindow, CControlWinTraits>::WindowProc(hWnd, uMsg, wParam, lParam);
544 pThis->Release();
545 return result;
546 }
547
548 BEGIN_MSG_MAP(CDefView)
549 MESSAGE_HANDLER(WM_SIZE, OnSize)
550 MESSAGE_HANDLER(WM_SETFOCUS, OnSetFocus)
551 MESSAGE_HANDLER(WM_KILLFOCUS, OnKillFocus)
552 MESSAGE_HANDLER(WM_NCCREATE, OnNCCreate)
553 MESSAGE_HANDLER(WM_CREATE, OnCreate)
554 MESSAGE_HANDLER(WM_ACTIVATE, OnActivate)
555 MESSAGE_HANDLER(WM_NOTIFY, OnNotify)
556 MESSAGE_HANDLER(WM_COMMAND, OnCommand)
557 MESSAGE_HANDLER(SHV_CHANGE_NOTIFY, OnChangeNotify)
558 MESSAGE_HANDLER(SHV_UPDATESTATUSBAR, OnUpdateStatusbar)
559 MESSAGE_HANDLER(WM_CONTEXTMENU, OnContextMenu)
560 MESSAGE_HANDLER(WM_DRAWITEM, OnMenuMessage)
561 MESSAGE_HANDLER(WM_MEASUREITEM, OnMenuMessage)
562 MESSAGE_HANDLER(WM_SHOWWINDOW, OnShowWindow)
563 MESSAGE_HANDLER(WM_GETDLGCODE, OnGetDlgCode)
564 MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
565 MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBackground)
566 MESSAGE_HANDLER(WM_PRINTCLIENT, OnPrintClient)
567 MESSAGE_HANDLER(WM_SYSCOLORCHANGE, OnSysColorChange)
568 MESSAGE_HANDLER(CWM_GETISHELLBROWSER, OnGetShellBrowser)
569 MESSAGE_HANDLER(WM_SETTINGCHANGE, OnSettingChange)
570 MESSAGE_HANDLER(WM_INITMENUPOPUP, OnInitMenuPopup)
571 MESSAGE_HANDLER(WM_CHANGECBCHAIN, OnChangeCBChain)
572 MESSAGE_HANDLER(WM_DRAWCLIPBOARD, OnDrawClipboard)
573 END_MSG_MAP()
574
575 BEGIN_COM_MAP(CDefView)
576 // Windows returns E_NOINTERFACE for IOleWindow
577 // COM_INTERFACE_ENTRY_IID(IID_IOleWindow, IOleWindow)
578 COM_INTERFACE_ENTRY_IID(IID_IShellView, IShellView)
579 COM_INTERFACE_ENTRY_IID(IID_CDefView, IShellView)
580 COM_INTERFACE_ENTRY_IID(IID_IShellView2, IShellView2)
581 COM_INTERFACE_ENTRY_IID(IID_IShellView3, IShellView3)
582 COM_INTERFACE_ENTRY_IID(IID_IFolderView, IFolderView)
583 COM_INTERFACE_ENTRY_IID(IID_IShellFolderView, IShellFolderView)
584 COM_INTERFACE_ENTRY_IID(IID_IOleCommandTarget, IOleCommandTarget)
585 COM_INTERFACE_ENTRY_IID(IID_IDropTarget, IDropTarget)
586 COM_INTERFACE_ENTRY_IID(IID_IDropSource, IDropSource)
587 COM_INTERFACE_ENTRY_IID(IID_IViewObject, IViewObject)
588 COM_INTERFACE_ENTRY_IID(IID_IServiceProvider, IServiceProvider)
589 END_COM_MAP()
590};
591
592#define ID_LISTVIEW 1
593
594// windowsx.h
595#define GET_WM_COMMAND_ID(wp, lp) LOWORD(wp)
596#define GET_WM_COMMAND_HWND(wp, lp) (HWND)(lp)
597#define GET_WM_COMMAND_CMD(wp, lp) HIWORD(wp)
598
599typedef void (CALLBACK *PFNSHGETSETTINGSPROC)(LPSHELLFLAGSTATE lpsfs, DWORD dwMask);
600
601CDefView::CDefView() :
602 m_ListView(),
603 m_hWndParent(NULL),
604 m_hMenu(NULL),
605 m_hMenuArrangeModes(NULL),
606 m_hMenuViewModes(NULL),
607 m_hContextMenu(NULL),
608 m_bmenuBarInitialized(FALSE),
609 m_uState(0),
610 m_cidl(0),
611 m_apidl(NULL),
612 m_pidlParent(NULL),
613 m_LoadColumnsList(NULL),
614 m_hNotify(0),
615 m_hAccel(NULL),
616 m_dwAspects(0),
617 m_dwAdvf(0),
618 m_iDragOverItem(0),
619 m_cScrollDelay(0),
620 m_isEditing(FALSE),
621 m_SpecialFolder(-1),
622 m_isFullStatusBar(true),
623 m_ScheduledStatusbarUpdate(false),
624 m_HasCutItems(false),
625 m_Destroyed(FALSE)
626{
627 ZeroMemory(&m_FolderSettings, sizeof(m_FolderSettings));
628 ZeroMemory(&m_ptLastMousePos, sizeof(m_ptLastMousePos));
629 ZeroMemory(&m_Category, sizeof(m_Category));
630 m_viewinfo_data.clrText = CLR_INVALID;
631 m_viewinfo_data.clrTextBack = CLR_INVALID;
632 m_viewinfo_data.hbmBack = NULL;
633
634 m_sortInfo.Reset();
635 m_ListToFolderColMap = DPA_Create(0);
636 m_hMyComputerIcon = LoadIconW(shell32_hInstance, MAKEINTRESOURCEW(IDI_SHELL_COMPUTER_DESKTOP));
637}
638
639CDefView::~CDefView()
640{
641 TRACE(" destroying IShellView(%p)\n", this);
642
643 _DoFolderViewCB(SFVM_VIEWRELEASE, 0, 0);
644
645 if (m_viewinfo_data.hbmBack)
646 {
647 ::DeleteObject(m_viewinfo_data.hbmBack);
648 m_viewinfo_data.hbmBack = NULL;
649 }
650
651 if (m_hWnd)
652 {
653 DestroyViewWindow();
654 }
655
656 SHFree(m_apidl);
657 DPA_Destroy(m_LoadColumnsList);
658 DPA_Destroy(m_ListToFolderColMap);
659}
660
661HRESULT WINAPI CDefView::Initialize(IShellFolder *shellFolder)
662{
663 m_pSFParent = shellFolder;
664 shellFolder->QueryInterface(IID_PPV_ARG(IShellFolder2, &m_pSF2Parent));
665 shellFolder->QueryInterface(IID_PPV_ARG(IShellDetails, &m_pSDParent));
666
667 return S_OK;
668}
669
670// ##### helperfunctions for communication with ICommDlgBrowser #####
671
672HRESULT CDefView::IncludeObject(PCUITEMID_CHILD pidl)
673{
674 HRESULT ret = S_OK;
675 if (m_pCommDlgBrowser && !(GetCommDlgViewFlags() & CDB2GVF_NOINCLUDEITEM))
676 {
677 TRACE("ICommDlgBrowser::IncludeObject pidl=%p\n", pidl);
678 ret = m_pCommDlgBrowser->IncludeObject(this, pidl);
679 TRACE("-- returns 0x%08x\n", ret);
680 }
681 else if (m_pFolderFilter)
682 {
683 ret = m_pFolderFilter->ShouldShow(m_pSFParent, m_pidlParent, pidl);
684 }
685 return ret;
686}
687
688HRESULT CDefView::OnDefaultCommand()
689{
690 HRESULT ret = S_FALSE;
691
692 if (m_pCommDlgBrowser.p != NULL)
693 {
694 TRACE("ICommDlgBrowser::OnDefaultCommand\n");
695 ret = m_pCommDlgBrowser->OnDefaultCommand(this);
696 TRACE("-- returns 0x%08x\n", ret);
697 }
698
699 return ret;
700}
701
702HRESULT CDefView::OnStateChange(UINT uFlags)
703{
704 HRESULT ret = S_FALSE;
705
706 if (m_pCommDlgBrowser.p != NULL)
707 {
708 TRACE("ICommDlgBrowser::OnStateChange flags=%x\n", uFlags);
709 ret = m_pCommDlgBrowser->OnStateChange(this, uFlags);
710 TRACE("--\n");
711 }
712
713 return ret;
714}
715/**********************************************************
716 * set the toolbar of the filedialog buttons
717 *
718 * - activates the buttons from the shellbrowser according to
719 * the view state
720 */
721void CDefView::CheckToolbar()
722{
723 LRESULT result;
724
725 TRACE("\n");
726
727 if (m_pCommDlgBrowser != NULL)
728 {
729 m_pShellBrowser->SendControlMsg(FCW_TOOLBAR, TB_CHECKBUTTON,
730 FCIDM_TB_SMALLICON, (m_FolderSettings.ViewMode == FVM_LIST) ? TRUE : FALSE, &result);
731 m_pShellBrowser->SendControlMsg(FCW_TOOLBAR, TB_CHECKBUTTON,
732 FCIDM_TB_REPORTVIEW, (m_FolderSettings.ViewMode == FVM_DETAILS) ? TRUE : FALSE, &result);
733 m_pShellBrowser->SendControlMsg(FCW_TOOLBAR, TB_ENABLEBUTTON,
734 FCIDM_TB_SMALLICON, TRUE, &result);
735 m_pShellBrowser->SendControlMsg(FCW_TOOLBAR, TB_ENABLEBUTTON,
736 FCIDM_TB_REPORTVIEW, TRUE, &result);
737 }
738}
739
740void CDefView::UpdateStatusbar()
741{
742 WCHAR szFormat[MAX_PATH];
743 WCHAR szPartText[MAX_PATH];
744 UINT cSelectedItems;
745
746 if (!m_ListView)
747 return;
748
749 cSelectedItems = m_ListView.GetSelectedCount();
750 if (cSelectedItems)
751 {
752 LoadStringW(shell32_hInstance, IDS_OBJECTS_SELECTED, szFormat, _countof(szFormat));
753 StringCchPrintfW(szPartText, _countof(szPartText), szFormat, cSelectedItems);
754 }
755 else
756 {
757 LoadStringW(shell32_hInstance, IDS_OBJECTS, szFormat, _countof(szFormat));
758 StringCchPrintfW(szPartText, _countof(szPartText), szFormat, m_ListView.GetItemCount());
759 }
760
761 LRESULT lResult;
762 m_pShellBrowser->SendControlMsg(FCW_STATUS, SB_SETTEXT, 0, (LPARAM)szPartText, &lResult);
763
764 // Don't bother with the extra processing if we only have one StatusBar part
765 if (m_isFullStatusBar)
766 {
767 UINT64 uTotalFileSize = 0;
768 WORD uFileFlags = LVNI_ALL;
769 INT nItem = -1;
770 bool bIsOnlyFoldersSelected = true;
771
772 // If we have something selected then only count selected file sizes
773 if (cSelectedItems)
774 {
775 uFileFlags = LVNI_SELECTED;
776 }
777
778 while ((nItem = m_ListView.GetNextItem(nItem, uFileFlags)) >= 0)
779 {
780 PCUITEMID_CHILD pidl = _PidlByItem(nItem);
781
782 uTotalFileSize += _ILGetFileSize(pidl, NULL, 0);
783
784 if (!_ILIsFolder(pidl))
785 {
786 bIsOnlyFoldersSelected = false;
787 }
788 }
789
790 // Don't show the file size text if there is 0 bytes in the folder
791 // OR we only have folders selected
792 if ((cSelectedItems && !bIsOnlyFoldersSelected) || uTotalFileSize)
793 StrFormatByteSizeW(uTotalFileSize, szPartText, _countof(szPartText));
794 else
795 *szPartText = 0;
796
797 m_pShellBrowser->SendControlMsg(FCW_STATUS, SB_SETTEXT, 1, (LPARAM)szPartText, &lResult);
798 }
799
800 SFGAOF att = 0;
801 if (cSelectedItems > 0)
802 {
803 UINT maxquery = 42; // Checking the attributes can be slow, only check small selections (_DoCopyToMoveToFolder will verify the full array)
804 att = SFGAO_CANCOPY | SFGAO_CANMOVE;
805 if (cSelectedItems <= maxquery && (!GetSelections() || FAILED(m_pSFParent->GetAttributesOf(m_cidl, m_apidl, &att))))
806 att = 0;
807 }
808 m_pShellBrowser->SendControlMsg(FCW_TOOLBAR, TB_ENABLEBUTTON, FCIDM_SHVIEW_COPYTO, (att & SFGAO_CANCOPY) != 0, &lResult);
809 m_pShellBrowser->SendControlMsg(FCW_TOOLBAR, TB_ENABLEBUTTON, FCIDM_SHVIEW_MOVETO, (att & SFGAO_CANMOVE) != 0, &lResult);
810}
811
812void CDefView::UpdateStatusbarLocation()
813{
814 LRESULT lResult;
815 LPARAM pIcon = NULL;
816 WCHAR szPartText[MAX_PATH];
817 *szPartText = 0;
818 if (m_isFullStatusBar)
819 {
820 // If we are in a Recycle Bin then show no text for the location part
821 int csidl;
822 if (!IsSpecialFolder(csidl) || (csidl != CSIDL_NETWORK && csidl != CSIDL_BITBUCKET))
823 {
824 LoadStringW(shell32_hInstance, IDS_MYCOMPUTER, szPartText, _countof(szPartText));
825 pIcon = (LPARAM)m_hMyComputerIcon;
826 }
827 /*else if (csidl == CSIDL_NETWORK) // TODO: Figure out the type of share (My Computer/Local Intranet/Internet?)
828 {
829 ImageList_GetIconSize(ListView_GetImageList(m_ListView, LVSIL_SMALL), &x, &y);
830 pIcon = (LPARAM)LoadImage(shell32_hInstance, MAKEINTRESOURCEW(IDI_SHELL_MY_NETWORK_PLACES),
831 IMAGE_ICON, x, y, LR_SHARED);
832 }*/
833 }
834 m_pShellBrowser->SendControlMsg(FCW_STATUS, SB_SETICON, 2, pIcon, &lResult);
835 m_pShellBrowser->SendControlMsg(FCW_STATUS, SB_SETTEXT, 2, (LPARAM)szPartText, &lResult);
836}
837
838LRESULT CDefView::OnUpdateStatusbar(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
839{
840 m_ScheduledStatusbarUpdate = false;
841 UpdateStatusbar();
842 return 0;
843}
844
845
846// ##### helperfunctions for initializing the view #####
847
848// creates the list view window
849BOOL CDefView::CreateList()
850{
851 DWORD dwStyle, dwExStyle, ListExStyle;
852 TRACE("%p\n", this);
853
854 dwStyle = WS_TABSTOP | WS_VISIBLE | WS_CHILDWINDOW | WS_CLIPSIBLINGS | WS_CLIPCHILDREN |
855 LVS_SHAREIMAGELISTS | LVS_EDITLABELS | LVS_AUTOARRANGE; // FIXME: Remove LVS_AUTOARRANGE when the view is able to save ItemPos
856 dwExStyle = (m_FolderSettings.fFlags & FWF_NOCLIENTEDGE) ? 0 : WS_EX_CLIENTEDGE;
857 ListExStyle = LVS_EX_INFOTIP | LVS_EX_LABELTIP;
858
859 if (m_FolderSettings.fFlags & FWF_DESKTOP)
860 {
861 m_FolderSettings.fFlags |= FWF_NOCLIENTEDGE | FWF_NOSCROLL;
862 dwStyle |= LVS_ALIGNLEFT;
863 // LVS_EX_REGIONAL?
864 }
865 else
866 {
867 dwStyle |= LVS_SHOWSELALWAYS; // MSDN says FWF_SHOWSELALWAYS is deprecated, always turn on for folders
868 dwStyle |= (m_FolderSettings.fFlags & FWF_ALIGNLEFT) ? LVS_ALIGNLEFT : LVS_ALIGNTOP;
869#if 0 // FIXME: Temporarily disabled until ListView is fixed (CORE-19624, CORE-19818)
870 ListExStyle |= LVS_EX_DOUBLEBUFFER;
871#endif
872 }
873
874 switch (m_FolderSettings.ViewMode)
875 {
876 case FVM_ICON:
877 dwStyle |= LVS_ICON;
878 break;
879 case FVM_DETAILS:
880 dwStyle |= LVS_REPORT;
881 break;
882 case FVM_SMALLICON:
883 dwStyle |= LVS_SMALLICON;
884 break;
885 case FVM_LIST:
886 dwStyle |= LVS_LIST;
887 break;
888 default:
889 dwStyle |= LVS_LIST;
890 break;
891 }
892
893 if (m_FolderSettings.fFlags & FWF_AUTOARRANGE)
894 dwStyle |= LVS_AUTOARRANGE;
895
896 if (m_FolderSettings.fFlags & FWF_SNAPTOGRID)
897 ListExStyle |= LVS_EX_SNAPTOGRID;
898
899 if (m_FolderSettings.fFlags & FWF_SINGLESEL)
900 dwStyle |= LVS_SINGLESEL;
901
902 if (m_FolderSettings.fFlags & FWF_FULLROWSELECT)
903 ListExStyle |= LVS_EX_FULLROWSELECT;
904
905 ListExStyle |= GetItemActivateFlags();
906 if (ListExStyle & LVS_EX_ONECLICKACTIVATE)
907 ListExStyle |= SHELL_GetIconUnderlineFlags();
908
909 if (m_FolderSettings.fFlags & FWF_NOCOLUMNHEADER)
910 dwStyle |= LVS_NOCOLUMNHEADER;
911
912#if 0
913 // FIXME: Because this is a negative, everyone gets the new flag by default unless they
914 // opt out. This code should be enabled when shell looks like Vista instead of 2003
915 if (!(m_FolderSettings.fFlags & FWF_NOHEADERINALLVIEWS))
916 ListExStyle |= LVS_EX_HEADERINALLVIEWS;
917#endif
918
919 if (m_FolderSettings.fFlags & FWF_NOCLIENTEDGE)
920 dwExStyle &= ~WS_EX_CLIENTEDGE;
921
922 RECT rcListView = {0,0,0,0};
923 m_ListView.Create(m_hWnd, rcListView, L"FolderView", dwStyle, dwExStyle, ID_LISTVIEW);
924
925 if (!m_ListView)
926 return FALSE;
927
928 m_ListView.SetExtendedListViewStyle(ListExStyle);
929
930 /* UpdateShellSettings(); */
931 return TRUE;
932}
933
934void CDefView::UpdateListColors()
935{
936 if (m_FolderSettings.fFlags & FWF_DESKTOP)
937 {
938 /* Check if drop shadows option is enabled */
939 BOOL bDropShadow = FALSE;
940 DWORD cbDropShadow = sizeof(bDropShadow);
941
942 /*
943 * The desktop ListView always take the default desktop colours, by
944 * remaining transparent and letting user32/win32k paint itself the
945 * desktop background color, if any.
946 */
947 m_ListView.SetBkColor(CLR_NONE);
948
949 SHGetValueW(HKEY_CURRENT_USER, L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Advanced",
950 L"ListviewShadow", NULL, &bDropShadow, &cbDropShadow);
951 if (bDropShadow)
952 {
953 /* Set the icon background transparent */
954 m_ListView.SetTextBkColor(CLR_NONE);
955 m_ListView.SetTextColor(RGB(255, 255, 255));
956 m_ListView.SetExtendedListViewStyle(LVS_EX_TRANSPARENTSHADOWTEXT, LVS_EX_TRANSPARENTSHADOWTEXT);
957 }
958 else
959 {
960 /* Set the icon background as the same colour as the desktop */
961 COLORREF crDesktop = GetSysColor(COLOR_DESKTOP);
962 m_ListView.SetTextBkColor(crDesktop);
963 if (GetRValue(crDesktop) + GetGValue(crDesktop) + GetBValue(crDesktop) > 128 * 3)
964 m_ListView.SetTextColor(RGB(0, 0, 0));
965 else
966 m_ListView.SetTextColor(RGB(255, 255, 255));
967 m_ListView.SetExtendedListViewStyle(0, LVS_EX_TRANSPARENTSHADOWTEXT);
968 }
969 }
970 else
971 {
972 m_ListView.SetTextBkColor(GetViewColor(m_viewinfo_data.clrTextBack, COLOR_WINDOW));
973 m_ListView.SetTextColor(GetViewColor(m_viewinfo_data.clrText, COLOR_WINDOWTEXT));
974
975 // Background is painted by the parent via WM_PRINTCLIENT
976 m_ListView.SetExtendedListViewStyle(LVS_EX_TRANSPARENTBKGND, LVS_EX_TRANSPARENTBKGND);
977 }
978}
979
980// adds all needed columns to the shellview
981BOOL CDefView::InitList()
982{
983 HIMAGELIST big_icons, small_icons;
984
985 TRACE("%p\n", this);
986
987 m_ListView.DeleteAllItems();
988
989 Shell_GetImageLists(&big_icons, &small_icons);
990 m_ListView.SetImageList(big_icons, LVSIL_NORMAL);
991 m_ListView.SetImageList(small_icons, LVSIL_SMALL);
992
993 m_hMenuArrangeModes = CreateMenu();
994
995 SIZE_T *pColumns = m_LoadColumnsList ? (SIZE_T*)DPA_GetPtrPtr(m_LoadColumnsList) : NULL;
996 UINT ColumnCount = pColumns ? DPA_GetPtrCount(m_LoadColumnsList) : 0;
997 LoadColumns(pColumns, ColumnCount);
998 if (m_sortInfo.bColumnIsFolderColumn)
999 {
1000 m_sortInfo.bColumnIsFolderColumn = FALSE;
1001 HRESULT hr = MapFolderColumnToListColumn(m_sortInfo.ListColumn);
1002 m_sortInfo.ListColumn = SUCCEEDED(hr) ? hr : 0;
1003 }
1004 return TRUE;
1005}
1006
1007/**********************************************************
1008* Column handling
1009*/
1010static HRESULT SHGetLVColumnSubItem(HWND List, UINT Col)
1011{
1012 LVCOLUMN lvc;
1013 lvc.mask = LVCF_SUBITEM;
1014 if (!ListView_GetColumn(List, Col, &lvc))
1015 return E_FAIL;
1016 else
1017 return lvc.iSubItem;
1018}
1019
1020HRESULT CDefView::MapFolderColumnToListColumn(UINT FoldCol)
1021{
1022 // This function is only called during column management, performance is not critical.
1023 for (UINT i = 0;; ++i)
1024 {
1025 HRESULT r = SHGetLVColumnSubItem(m_ListView.m_hWnd, i);
1026 if ((UINT)r == FoldCol)
1027 return i;
1028 else if (FAILED(r))
1029 return r;
1030 }
1031}
1032
1033HRESULT CDefView::MapListColumnToFolderColumn(UINT ListCol)
1034{
1035 // This function is called every time a LVITEM::iSubItem is mapped to
1036 // a folder column (calls to GetDetailsOf etc.) and should be fast.
1037 if (m_ListToFolderColMap)
1038 {
1039 UINT count = DPA_GetPtrCount(m_ListToFolderColMap);
1040 if (ListCol < count)
1041 {
1042 HRESULT col = (INT)(INT_PTR)DPA_FastGetPtr(m_ListToFolderColMap, ListCol);
1043 assert(col >= 0 && col == SHGetLVColumnSubItem(m_ListView.m_hWnd, ListCol));
1044 return col;
1045 }
1046 else if (count)
1047 {
1048 TRACE("m_ListToFolderColMap cache miss while mapping %d\n", ListCol);
1049 }
1050 }
1051 return SHGetLVColumnSubItem(m_ListView.m_hWnd, ListCol);
1052}
1053
1054HRESULT CDefView::GetDetailsByFolderColumn(PCUITEMID_CHILD pidl, UINT FoldCol, SHELLDETAILS &sd)
1055{
1056 // According to learn.microsoft.com/en-us/windows/win32/shell/sfvm-getdetailsof
1057 // the query order is IShellFolder2, IShellDetails, SFVM_GETDETAILSOF.
1058 HRESULT hr = E_FAIL;
1059 if (m_pSF2Parent)
1060 {
1061 hr = m_pSF2Parent->GetDetailsOf(pidl, FoldCol, &sd);
1062 }
1063 if (FAILED(hr) && m_pSDParent)
1064 {
1065 hr = m_pSDParent->GetDetailsOf(pidl, FoldCol, &sd);
1066 }
1067#if 0 // TODO
1068 if (FAILED(hr))
1069 {
1070 FIXME("Try SFVM_GETDETAILSOF\n");
1071 }
1072#endif
1073 return hr;
1074}
1075
1076HRESULT CDefView::GetDetailsByListColumn(PCUITEMID_CHILD pidl, UINT ListCol, SHELLDETAILS &sd)
1077{
1078 HRESULT hr = MapListColumnToFolderColumn(ListCol);
1079 if (SUCCEEDED(hr))
1080 return GetDetailsByFolderColumn(pidl, hr, sd);
1081 ERR("Unable to determine folder column from list column %d\n", (int) ListCol);
1082 return hr;
1083}
1084
1085HRESULT CDefView::LoadColumn(UINT FoldCol, UINT ListCol, BOOL Insert, UINT ForceWidth)
1086{
1087 WCHAR buf[MAX_PATH];
1088 SHELLDETAILS sd;
1089 HRESULT hr;
1090
1091 sd.str.uType = !STRRET_WSTR; // Make sure "uninitialized" uType is not WSTR
1092 hr = GetDetailsByFolderColumn(NULL, FoldCol, sd);
1093 if (FAILED(hr))
1094 return hr;
1095 hr = StrRetToStrNW(buf, _countof(buf), &sd.str, NULL);
1096 if (FAILED(hr))
1097 return hr;
1098
1099 UINT chavewidth = CalculateCharWidth(m_ListView.m_hWnd);
1100 if (!chavewidth)
1101 chavewidth = 6; // 6 is a reasonable default fallback
1102
1103 LVCOLUMN lvc;
1104 lvc.mask = LVCF_TEXT | LVCF_FMT | LVCF_WIDTH | LVCF_SUBITEM;
1105 lvc.pszText = buf;
1106 lvc.fmt = sd.fmt;
1107 lvc.cx = ForceWidth ? ForceWidth : (sd.cxChar * chavewidth); // FIXME: DPI?
1108 lvc.iSubItem = FoldCol; // Used by MapFolderColumnToListColumn & MapListColumnToFolderColumn
1109 if ((int)ListCol == -1)
1110 {
1111 assert(Insert); // You can insert at the end but you can't change something that is not there
1112 if (Insert)
1113 ListCol = 0x7fffffff;
1114 }
1115 if (Insert)
1116 ListView_InsertColumn(m_ListView.m_hWnd, ListCol, &lvc);
1117 else
1118 ListView_SetColumn(m_ListView.m_hWnd, ListCol, &lvc);
1119 return S_OK;
1120}
1121
1122HRESULT CDefView::LoadColumns(SIZE_T *pColList, UINT ColListCount)
1123{
1124 HWND hWndHdr = ListView_GetHeader(m_ListView.m_hWnd);
1125 UINT newColCount = 0, oldColCount = Header_GetItemCount(hWndHdr);
1126 UINT width = 0, foldCol, i;
1127 HRESULT hr = S_FALSE;
1128
1129 m_ListView.SetRedraw(FALSE);
1130 for (i = 0, foldCol = 0;; ++foldCol)
1131 {
1132 if (newColCount >= 0xffff)
1133 break; // CompareIDs limit reached
1134
1135 if (pColList)
1136 {
1137 if (i >= ColListCount)
1138 break;
1139 width = HIWORD(pColList[i]);
1140 foldCol = LOWORD(pColList[i++]);
1141 }
1142
1143 SHCOLSTATEF state = 0;
1144 if (!m_pSF2Parent || FAILED(m_pSF2Parent->GetDefaultColumnState(foldCol, &state)))
1145 state = SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT;
1146
1147 if (foldCol == 0)
1148 {
1149 // Force the first column
1150 }
1151 else if (state & SHCOLSTATE_HIDDEN)
1152 {
1153 continue;
1154 }
1155 else if (!pColList && !(state & SHCOLSTATE_ONBYDEFAULT))
1156 {
1157 continue;
1158 }
1159
1160 bool insert = newColCount >= oldColCount;
1161 UINT listCol = insert ? -1 : newColCount;
1162 hr = LoadColumn(foldCol, listCol, insert, width);
1163 if (FAILED(hr))
1164 {
1165 if (!pColList)
1166 hr = S_OK; // No more items, we are done
1167 break;
1168 }
1169 ++newColCount;
1170 }
1171 for (i = newColCount; i < oldColCount; ++i)
1172 {
1173 ListView_DeleteColumn(m_ListView.m_hWnd, i);
1174 }
1175
1176 m_ListView.SetRedraw(TRUE);
1177 ColumnListChanged();
1178 assert(SUCCEEDED(MapFolderColumnToListColumn(0))); // We don't allow turning off the Name column
1179 if (m_LoadColumnsList)
1180 {
1181 DPA_Destroy(m_LoadColumnsList);
1182 m_LoadColumnsList = NULL;
1183 }
1184 return hr;
1185}
1186
1187void CDefView::ColumnListChanged()
1188{
1189 HDPA cache = m_ListToFolderColMap;
1190 m_ListToFolderColMap = NULL; // No cache while we are building the cache
1191 DPA_DeleteAllPtrs(cache);
1192 for (UINT i = 0;; ++i)
1193 {
1194 HRESULT hr = MapListColumnToFolderColumn(i);
1195 if (FAILED(hr))
1196 break; // No more columns
1197 if (!DPA_SetPtr(cache, i, (void*)(INT_PTR) hr))
1198 break; // Cannot allow holes in the cache, must stop now.
1199 }
1200 m_ListToFolderColMap = cache;
1201
1202 for (;;)
1203 {
1204 if (!RemoveMenu(m_hMenuArrangeModes, 0, MF_BYPOSITION))
1205 break;
1206 }
1207 HMENU hMenu = GetSubmenuByID(m_hMenu, FCIDM_MENU_VIEW);
1208 if (hMenu)
1209 {
1210 hMenu = GetSubmenuByID(hMenu, FCIDM_SHVIEW_ARRANGE);
1211 for (UINT i = DVIDM_ARRANGESORT_FIRST; i <= DVIDM_ARRANGESORT_LAST && hMenu; ++i)
1212 {
1213 RemoveMenu(hMenu, i, MF_BYCOMMAND);
1214 }
1215 if ((int) GetMenuItemID(hMenu, 0) <= 0)
1216 RemoveMenu(hMenu, 0, MF_BYPOSITION); // Separator
1217 }
1218 WCHAR buf[MAX_PATH];
1219 LVCOLUMN lvc;
1220 lvc.mask = LVCF_TEXT;
1221 lvc.pszText = buf;
1222 lvc.cchTextMax = _countof(buf);
1223 for (UINT listCol = 0; listCol < DEFVIEW_ARRANGESORT_MAXENUM; ++listCol)
1224 {
1225 if (!ListView_GetColumn(m_ListView.m_hWnd, listCol, &lvc))
1226 break;
1227 HRESULT foldCol = MapListColumnToFolderColumn(listCol);
1228 assert(SUCCEEDED(foldCol));
1229 DBG_UNREFERENCED_LOCAL_VARIABLE(foldCol);
1230 AppendMenuItem(m_hMenuArrangeModes, MF_STRING,
1231 DVIDM_ARRANGESORT_FIRST + listCol, lvc.pszText, listCol);
1232 }
1233
1234 ListView_RedrawItems(m_ListView.m_hWnd, 0, 0x7fffffff);
1235 m_ListView.InvalidateRect(NULL, TRUE);
1236}
1237
1238/*************************************************************************
1239 * ShellView_ListViewCompareItems
1240 *
1241 * Compare Function for the Listview (FileOpen Dialog)
1242 *
1243 * PARAMS
1244 * lParam1 [I] the first ItemIdList to compare with
1245 * lParam2 [I] the second ItemIdList to compare with
1246 * lpData [I] The column ID for the header Ctrl to process
1247 *
1248 * RETURNS
1249 * A negative value if the first item should precede the second,
1250 * a positive value if the first item should follow the second,
1251 * or zero if the two items are equivalent
1252 */
1253INT CALLBACK CDefView::ListViewCompareItems(LPARAM lParam1, LPARAM lParam2, LPARAM lpData)
1254{
1255 PCUIDLIST_RELATIVE pidl1 = reinterpret_cast<PCUIDLIST_RELATIVE>(lParam1);
1256 PCUIDLIST_RELATIVE pidl2 = reinterpret_cast<PCUIDLIST_RELATIVE>(lParam2);
1257 CDefView *pThis = reinterpret_cast<CDefView*>(lpData);
1258
1259 HRESULT hres = pThis->m_pSFParent->CompareIDs(pThis->m_sortInfo.FolderColumn, pidl1, pidl2);
1260 if (FAILED_UNEXPECTEDLY(hres))
1261 return 0;
1262
1263 SHORT nDiff = HRESULT_CODE(hres);
1264 return nDiff * pThis->m_sortInfo.Direction;
1265}
1266
1267BOOL CDefView::_Sort(int Col)
1268{
1269 HWND hHeader;
1270 HDITEM hColumn;
1271 int prevCol = m_sortInfo.ListColumn;
1272 m_sortInfo.bLoadedFromViewState = FALSE;
1273
1274 // FIXME: Is this correct? Who sets this style?
1275 // And if it is set, should it also block sorting using the menu?
1276 // Any why should it block sorting when the view is loaded initially?
1277 if (m_ListView.GetWindowLongPtr(GWL_STYLE) & LVS_NOSORTHEADER)
1278 return TRUE;
1279
1280 hHeader = ListView_GetHeader(m_ListView.m_hWnd);
1281 if (Col != -1)
1282 {
1283 if (Col >= Header_GetItemCount(hHeader))
1284 {
1285 ERR("Sort column out of range\n");
1286 return FALSE;
1287 }
1288
1289 if (prevCol == Col)
1290 m_sortInfo.Direction *= -1;
1291 else
1292 m_sortInfo.Direction = 0;
1293 m_sortInfo.ListColumn = Col;
1294 }
1295 if (!m_sortInfo.Direction)
1296 m_sortInfo.Direction += 1;
1297
1298 /* If the sorting column changed, remove the sorting style from the old column */
1299 if (prevCol != -1 && prevCol != m_sortInfo.ListColumn)
1300 {
1301 hColumn.mask = HDI_FORMAT;
1302 Header_GetItem(hHeader, prevCol, &hColumn);
1303 hColumn.fmt &= ~(HDF_SORTUP | HDF_SORTDOWN);
1304 Header_SetItem(hHeader, prevCol, &hColumn);
1305 }
1306
1307 /* Set the sorting style on the new column */
1308 hColumn.mask = HDI_FORMAT;
1309 Header_GetItem(hHeader, m_sortInfo.ListColumn, &hColumn);
1310 hColumn.fmt &= ~(HDF_SORTDOWN | HDF_SORTUP);
1311 hColumn.fmt |= (m_sortInfo.Direction > 0 ? HDF_SORTUP : HDF_SORTDOWN);
1312 Header_SetItem(hHeader, m_sortInfo.ListColumn, &hColumn);
1313
1314 m_sortInfo.FolderColumn = MapListColumnToFolderColumn(m_sortInfo.ListColumn);
1315 ASSERT(m_sortInfo.Direction == 1 || m_sortInfo.Direction == -1);
1316 return m_ListView.SortItems(ListViewCompareItems, this);
1317}
1318
1319SFGAOF CDefView::GetItemAttributes(PCUITEMID_CHILD pidl, UINT Query)
1320{
1321 SFGAOF Attr = (SFGAOF)Query;
1322 return SUCCEEDED(m_pSFParent->GetAttributesOf(1, &pidl, &Attr)) ? (Attr & Query) : 0;
1323}
1324
1325SFGAOF CDefView::GetItemAttributes(int i, UINT Query)
1326{
1327 PCUITEMID_CHILD pItem = _PidlByItem(i);
1328 return pItem ? GetItemAttributes(pItem, Query) : 0;
1329}
1330
1331PCUITEMID_CHILD CDefView::_PidlByItem(int i)
1332{
1333 if (!m_ListView)
1334 return nullptr;
1335 return reinterpret_cast<PCUITEMID_CHILD>(m_ListView.GetItemData(i));
1336}
1337
1338PCUITEMID_CHILD CDefView::_PidlByItem(LVITEM& lvItem)
1339{
1340 if (!m_ListView)
1341 return nullptr;
1342 return reinterpret_cast<PCUITEMID_CHILD>(lvItem.lParam);
1343}
1344
1345int CDefView::LV_FindItemByPidl(PCUITEMID_CHILD pidl)
1346{
1347 ASSERT(m_ListView && m_pSFParent);
1348
1349 int cItems = m_ListView.GetItemCount();
1350 LPARAM lParam = m_pSF2Parent ? SHCIDS_CANONICALONLY : 0;
1351 for (int i = 0; i < cItems; i++)
1352 {
1353 PCUITEMID_CHILD currentpidl = _PidlByItem(i);
1354 HRESULT hr = m_pSFParent->CompareIDs(lParam, pidl, currentpidl);
1355 if (SUCCEEDED(hr))
1356 {
1357 if (hr == S_EQUAL)
1358 return i;
1359 }
1360 else
1361 {
1362#if DBG
1363 for (i = 0; pidl && i < cItems; i++)
1364 {
1365 currentpidl = _PidlByItem(i);
1366 if (currentpidl && currentpidl->mkid.cb == pidl->mkid.cb && !memcmp(currentpidl, pidl, pidl->mkid.cb))
1367 DbgPrint("Matched item #%d, broken CompareIDs?\n", i);
1368 }
1369#endif
1370 break;
1371 }
1372 }
1373 return -1;
1374}
1375
1376int CDefView::LV_AddItem(PCUITEMID_CHILD pidl)
1377{
1378 LVITEMW lvItem;
1379
1380 TRACE("(%p)(pidl=%p)\n", this, pidl);
1381
1382 ASSERT(m_ListView);
1383
1384 if (_DoFolderViewCB(SFVM_ADDINGOBJECT, 0, (LPARAM)pidl) == S_FALSE)
1385 return -1;
1386
1387 lvItem.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM;
1388 lvItem.iItem = m_ListView.GetItemCount(); // add item to lists end
1389 lvItem.iSubItem = 0;
1390 lvItem.lParam = reinterpret_cast<LPARAM>(ILClone(pidl)); // set item's data
1391 lvItem.pszText = LPSTR_TEXTCALLBACKW; // get text on a callback basis
1392 lvItem.iImage = I_IMAGECALLBACK; // get image on a callback basis
1393 lvItem.stateMask = LVIS_CUT;
1394 if (m_HasCutItems)
1395 {
1396 lvItem.mask |= LVIF_STATE;
1397 lvItem.state = GetItemAttributes(pidl, SFGAO_HIDDEN | SFGAO_GHOSTED) ? LVIS_CUT : 0;
1398 }
1399
1400 return m_ListView.InsertItem(&lvItem);
1401}
1402
1403BOOLEAN CDefView::LV_DeleteItem(PCUITEMID_CHILD pidl)
1404{
1405 int nIndex;
1406
1407 TRACE("(%p)(pidl=%p)\n", this, pidl);
1408
1409 ASSERT(m_ListView);
1410
1411 nIndex = LV_FindItemByPidl(pidl);
1412 if (nIndex < 0)
1413 return FALSE;
1414
1415 _DoFolderViewCB(SFVM_REMOVINGOBJECT, 0, (LPARAM)pidl);
1416
1417 return m_ListView.DeleteItem(nIndex);
1418}
1419
1420BOOLEAN CDefView::LV_RenameItem(PCUITEMID_CHILD pidlOld, PCUITEMID_CHILD pidlNew)
1421{
1422 int nItem;
1423 LVITEMW lvItem;
1424
1425 TRACE("(%p)(pidlold=%p pidlnew=%p)\n", this, pidlOld, pidlNew);
1426
1427 ASSERT(m_ListView);
1428
1429 nItem = LV_FindItemByPidl(pidlOld);
1430
1431 if (-1 != nItem)
1432 {
1433 lvItem.mask = LVIF_PARAM; // only the pidl
1434 lvItem.iItem = nItem;
1435 lvItem.iSubItem = 0;
1436 m_ListView.GetItem(&lvItem);
1437
1438 // Store old pidl until new item is replaced
1439 LPVOID oldPidl = reinterpret_cast<LPVOID>(lvItem.lParam);
1440
1441 lvItem.mask = LVIF_PARAM | LVIF_IMAGE | LVIF_TEXT;
1442 lvItem.iItem = nItem;
1443 lvItem.iSubItem = 0;
1444 lvItem.lParam = reinterpret_cast<LPARAM>(ILClone(pidlNew)); // set item's data
1445 lvItem.pszText = LPSTR_TEXTCALLBACKW;
1446 lvItem.iImage = SHMapPIDLToSystemImageListIndex(m_pSFParent, pidlNew, 0);
1447 m_ListView.SetItem(&lvItem);
1448 m_ListView.Update(nItem);
1449
1450 // Now that the new item is in place, we can safely release the old pidl
1451 SHFree(oldPidl);
1452
1453 return TRUE; // FIXME: better handling
1454 }
1455
1456 return FALSE;
1457}
1458
1459BOOL CDefView::LV_UpdateItem(INT nItem, PCUITEMID_CHILD pidl)
1460{
1461 BOOL bResult = FALSE;
1462 LVITEMW lvItem;
1463 if (nItem >= 0)
1464 {
1465 _DoFolderViewCB(SFVM_UPDATINGOBJECT, nItem, (LPARAM)pidl);
1466
1467 lvItem.mask = LVIF_IMAGE;
1468 lvItem.iItem = nItem;
1469 lvItem.iSubItem = 0;
1470 lvItem.iImage = SHMapPIDLToSystemImageListIndex(m_pSFParent, pidl, 0);
1471 lvItem.stateMask = LVIS_CUT;
1472 if (m_HasCutItems)
1473 {
1474 lvItem.mask |= LVIF_STATE;
1475 lvItem.state = GetItemAttributes(pidl, SFGAO_HIDDEN | SFGAO_GHOSTED) ? LVIS_CUT : 0;
1476 }
1477 PCUITEMID_CHILD pidlOld = _PidlByItem(nItem);
1478 if (pidlOld && (lvItem.lParam = reinterpret_cast<LPARAM>(ILClone(pidl))) != NULL)
1479 lvItem.mask |= LVIF_PARAM;
1480 bResult = m_ListView.SetItem(&lvItem);
1481 if (bResult && lvItem.lParam && pidlOld)
1482 ILFree(const_cast<PUITEMID_CHILD>(pidlOld));
1483 for (UINT i = 0; m_ListView.SetItemText(nItem, i, LPSTR_TEXTCALLBACK); ++i) {} // Update all columns
1484 }
1485 return bResult;
1486}
1487
1488BOOL CDefView::LV_UpdateItem(PCUITEMID_CHILD pidl)
1489{
1490 TRACE("(%p)(pidl=%p)\n", this, pidl);
1491 ASSERT(m_ListView);
1492 INT nItem = LV_FindItemByPidl(pidl);
1493 return nItem >= 0 ? LV_UpdateItem(nItem, pidl) : FALSE;
1494}
1495
1496void CDefView::LV_RefreshIcon(INT iItem)
1497{
1498 ASSERT(m_ListView);
1499
1500 LVITEMW lvItem = { LVIF_IMAGE };
1501 lvItem.iItem = iItem;
1502 lvItem.iImage = I_IMAGECALLBACK;
1503 m_ListView.SetItem(&lvItem);
1504 m_ListView.Update(iItem);
1505}
1506
1507void CDefView::LV_RefreshIcons()
1508{
1509 ASSERT(m_ListView);
1510
1511 for (INT iItem = -1;;)
1512 {
1513 iItem = ListView_GetNextItem(m_ListView, iItem, LVNI_ALL);
1514 if (iItem == -1)
1515 break;
1516
1517 LV_RefreshIcon(iItem);
1518 }
1519}
1520
1521INT CALLBACK CDefView::fill_list(LPVOID ptr, LPVOID arg)
1522{
1523 PITEMID_CHILD pidl = static_cast<PITEMID_CHILD>(ptr);
1524 CDefView *pThis = static_cast<CDefView *>(arg);
1525
1526 // in a commdlg this works as a filemask
1527 if (pThis->IncludeObject(pidl) == S_OK && pThis->m_ListView)
1528 pThis->LV_AddItem(pidl);
1529
1530 SHFree(pidl);
1531 return TRUE;
1532}
1533
1534///
1535// - gets the objectlist from the shellfolder
1536// - sorts the list
1537// - fills the list into the view
1538HRESULT CDefView::FillList(BOOL IsRefreshCommand)
1539{
1540 CComPtr<IEnumIDList> pEnumIDList;
1541 PITEMID_CHILD pidl;
1542 DWORD dwFetched;
1543 HRESULT hRes;
1544 HDPA hdpa;
1545 DWORD dFlags = SHCONTF_NONFOLDERS | ((m_FolderSettings.fFlags & FWF_NOSUBFOLDERS) ? 0 : SHCONTF_FOLDERS);
1546
1547 TRACE("%p\n", this);
1548
1549 SHELLSTATE shellstate;
1550 SHGetSetSettings(&shellstate, SSF_SHOWALLOBJECTS | SSF_SHOWSUPERHIDDEN, FALSE);
1551 if (GetCommDlgViewFlags() & CDB2GVF_SHOWALLFILES)
1552 shellstate.fShowAllObjects = shellstate.fShowSuperHidden = TRUE;
1553
1554 if (shellstate.fShowAllObjects)
1555 {
1556 dFlags |= SHCONTF_INCLUDEHIDDEN;
1557 m_ListView.SendMessageW(LVM_SETCALLBACKMASK, LVIS_CUT, 0);
1558 }
1559 if (shellstate.fShowSuperHidden)
1560 {
1561 dFlags |= SHCONTF_INCLUDESUPERHIDDEN;
1562 m_ListView.SendMessageW(LVM_SETCALLBACKMASK, LVIS_CUT, 0);
1563 }
1564
1565 // get the itemlist from the shfolder
1566 hRes = m_pSFParent->EnumObjects(m_hWnd, dFlags, &pEnumIDList);
1567 if (hRes != S_OK)
1568 {
1569 if (hRes == S_FALSE)
1570 return(NOERROR);
1571 return(hRes);
1572 }
1573
1574 // create a pointer array
1575 hdpa = DPA_Create(16);
1576 if (!hdpa)
1577 return(E_OUTOFMEMORY);
1578
1579 // copy the items into the array
1580 while((S_OK == pEnumIDList->Next(1, &pidl, &dwFetched)) && dwFetched)
1581 {
1582 if (DPA_AppendPtr(hdpa, pidl) == -1)
1583 {
1584 SHFree(pidl);
1585 }
1586 }
1587
1588 // turn listview's redrawing off
1589 m_ListView.SetRedraw(FALSE);
1590
1591 DPA_DestroyCallback( hdpa, fill_list, this);
1592
1593 /* sort the array */
1594 int sortCol = -1;
1595 if (!IsRefreshCommand && !m_sortInfo.bLoadedFromViewState) // Are we loading for the first time?
1596 {
1597 m_sortInfo.Direction = 0;
1598 sortCol = 0; // In case the folder does not know/care
1599 if (m_pSF2Parent)
1600 {
1601 ULONG folderSortCol = sortCol, dummy;
1602 HRESULT hr = m_pSF2Parent->GetDefaultColumn(NULL, &folderSortCol, &dummy);
1603 if (SUCCEEDED(hr))
1604 hr = MapFolderColumnToListColumn(folderSortCol);
1605 if (SUCCEEDED(hr))
1606 sortCol = (int) hr;
1607 }
1608 }
1609 _Sort(sortCol);
1610
1611 if (m_viewinfo_data.hbmBack)
1612 {
1613 ::DeleteObject(m_viewinfo_data.hbmBack);
1614 m_viewinfo_data.hbmBack = NULL;
1615 }
1616
1617 // load custom background image and custom text color
1618 m_viewinfo_data.cbSize = sizeof(m_viewinfo_data);
1619 _DoFolderViewCB(SFVM_GET_CUSTOMVIEWINFO, 0, (LPARAM)&m_viewinfo_data);
1620
1621 // turn listview's redrawing back on and force it to draw
1622 m_ListView.SetRedraw(TRUE);
1623
1624 UpdateListColors();
1625
1626 if (!(m_FolderSettings.fFlags & FWF_DESKTOP))
1627 {
1628 // redraw now
1629 m_ListView.InvalidateRect(NULL, TRUE);
1630 }
1631
1632 if (IsRefreshCommand)
1633 UpdateStatusbarLocation();
1634
1635 _DoFolderViewCB(SFVM_LISTREFRESHED, 0, 0);
1636
1637 return S_OK;
1638}
1639
1640LRESULT CDefView::OnShowWindow(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
1641{
1642 if (m_ListView.IsWindow())
1643 m_ListView.UpdateWindow();
1644 bHandled = FALSE;
1645 return 0;
1646}
1647
1648LRESULT CDefView::OnGetDlgCode(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
1649{
1650 return m_ListView.SendMessageW(uMsg, 0, 0);
1651}
1652
1653LRESULT CDefView::OnDestroy(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
1654{
1655 if (!m_Destroyed)
1656 {
1657 m_Destroyed = TRUE;
1658 RevokeDragDrop(m_hWnd);
1659 SHChangeNotifyDeregister(m_hNotify);
1660 m_hNotify = NULL;
1661 m_ClipboardChain.Unhook(m_hWnd);
1662 if (m_pFileMenu)
1663 {
1664 IUnknown_SetSite(m_pFileMenu, NULL);
1665 m_pFileMenu = NULL;
1666 }
1667 if (m_hMenu)
1668 {
1669 DestroyMenu(m_hMenu);
1670 m_hMenu = NULL;
1671 }
1672 SHFree(m_pidlParent);
1673 m_pidlParent = NULL;
1674 }
1675 bHandled = FALSE;
1676 return 0;
1677}
1678
1679LRESULT CDefView::OnEraseBackground(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
1680{
1681 /* redirect to parent */
1682 if (m_FolderSettings.fFlags & (FWF_DESKTOP | FWF_TRANSPARENT))
1683 return SendMessageW(GetParent(), WM_ERASEBKGND, wParam, lParam);
1684
1685 bHandled = FALSE;
1686 return 0;
1687}
1688
1689static VOID
1690DrawTileBitmap(HDC hDC, LPCRECT prc, HBITMAP hbm, INT nWidth, INT nHeight, INT dx, INT dy)
1691{
1692 INT x0 = prc->left, y0 = prc->top, x1 = prc->right, y1 = prc->bottom;
1693 x0 += dx;
1694 y0 += dy;
1695
1696 HDC hMemDC = CreateCompatibleDC(hDC);
1697 HGDIOBJ hbmOld = SelectObject(hMemDC, hbm);
1698
1699 for (INT y = y0; y < y1; y += nHeight)
1700 {
1701 for (INT x = x0; x < x1; x += nWidth)
1702 {
1703 BitBlt(hDC, x, y, nWidth, nHeight, hMemDC, 0, 0, SRCCOPY);
1704 }
1705 }
1706
1707 SelectObject(hMemDC, hbmOld);
1708 DeleteDC(hMemDC);
1709}
1710
1711LRESULT CDefView::OnPrintClient(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
1712{
1713 HDC hDC = (HDC)wParam;
1714
1715 RECT rc;
1716 ::GetClientRect(m_ListView, &rc);
1717
1718 if (m_viewinfo_data.hbmBack)
1719 {
1720 BITMAP bm;
1721 if (::GetObject(m_viewinfo_data.hbmBack, sizeof(BITMAP), &bm))
1722 {
1723 INT dx = -(::GetScrollPos(m_ListView, SB_HORZ) % bm.bmWidth);
1724 INT dy = -(::GetScrollPos(m_ListView, SB_VERT) % bm.bmHeight);
1725 DrawTileBitmap(hDC, &rc, m_viewinfo_data.hbmBack, bm.bmWidth, bm.bmHeight, dx, dy);
1726 }
1727 }
1728 else
1729 {
1730 SHFillRectClr(hDC, &rc, GetViewColor(m_viewinfo_data.clrTextBack, COLOR_WINDOW));
1731 }
1732
1733 bHandled = TRUE;
1734
1735 return TRUE;
1736}
1737
1738LRESULT CDefView::OnSysColorChange(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
1739{
1740 /* Update desktop labels color */
1741 UpdateListColors();
1742
1743 /* Forward WM_SYSCOLORCHANGE to common controls */
1744 return m_ListView.SendMessageW(uMsg, 0, 0);
1745}
1746
1747LRESULT CDefView::OnGetShellBrowser(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
1748{
1749 return reinterpret_cast<LRESULT>(m_pShellBrowser.p);
1750}
1751
1752LRESULT CDefView::OnNCCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
1753{
1754 this->AddRef();
1755 bHandled = FALSE;
1756 return 0;
1757}
1758
1759VOID CDefView::OnFinalMessage(HWND)
1760{
1761 this->Release();
1762}
1763
1764LRESULT CDefView::OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
1765{
1766 CComPtr<IDropTarget> pdt;
1767 CComPtr<IPersistFolder2> ppf2;
1768
1769 TRACE("%p\n", this);
1770
1771 if (SUCCEEDED(QueryInterface(IID_PPV_ARG(IDropTarget, &pdt))))
1772 {
1773 if (FAILED(RegisterDragDrop(m_hWnd, pdt)))
1774 ERR("Error Registering DragDrop\n");
1775 }
1776
1777 /* register for receiving notifications */
1778 m_pSFParent->QueryInterface(IID_PPV_ARG(IPersistFolder2, &ppf2));
1779 if (ppf2)
1780 {
1781 ppf2->GetCurFolder(&m_pidlParent);
1782 }
1783
1784 if (CreateList())
1785 {
1786 if (InitList())
1787 {
1788 FillList(FALSE);
1789 }
1790 }
1791
1792 if (m_FolderSettings.fFlags & FWF_DESKTOP)
1793 {
1794 HWND hwndSB;
1795 m_pShellBrowser->GetWindow(&hwndSB);
1796 SetShellWindowEx(hwndSB, m_ListView);
1797 }
1798
1799 // Set up change notification
1800 LPITEMIDLIST pidlTarget = NULL;
1801 LONG fEvents = 0;
1802 HRESULT hr = _DoFolderViewCB(SFVM_GETNOTIFY, (WPARAM)&pidlTarget, (LPARAM)&fEvents);
1803 if (FAILED(hr) || (!pidlTarget && !fEvents)) // FIXME: MSDN says both zero means no notifications
1804 {
1805 pidlTarget = m_pidlParent;
1806 fEvents = SHCNE_ALLEVENTS;
1807 }
1808 SHChangeNotifyEntry ntreg = {};
1809 hr = _DoFolderViewCB(SFVM_QUERYFSNOTIFY, 0, (LPARAM)&ntreg);
1810 if (FAILED(hr))
1811 {
1812 ntreg.fRecursive = FALSE;
1813 ntreg.pidl = pidlTarget;
1814 }
1815 m_hNotify = SHChangeNotifyRegister(m_hWnd,
1816 SHCNRF_InterruptLevel | SHCNRF_ShellLevel |
1817 SHCNRF_NewDelivery,
1818 fEvents, SHV_CHANGE_NOTIFY,
1819 1, &ntreg);
1820
1821 m_hAccel = LoadAcceleratorsW(shell32_hInstance, MAKEINTRESOURCEW(IDA_SHELLVIEW));
1822
1823 int bForceFullStatusBar = false;
1824 BOOL bIsFileSystem = SHGetAttributes(NULL, m_pidlParent, SFGAO_FILESYSTEM) & SFGAO_FILESYSTEM;
1825 m_SpecialFolder = bIsFileSystem ? -1 : 0x7f; // FS folder or "generic" CSIDL
1826 if (_ILIsDesktop(m_pidlParent))
1827 {
1828 m_SpecialFolder = CSIDL_DESKTOP;
1829 }
1830 else if (IsEqualPersistClassID(ppf2, CLSID_RecycleBin))
1831 {
1832 m_SpecialFolder = bForceFullStatusBar = CSIDL_BITBUCKET;
1833 }
1834 else if (bIsFileSystem)
1835 {
1836 CComHeapPtr<ITEMIDLIST> pidlNet(SHCloneSpecialIDList(NULL, CSIDL_NETWORK, FALSE));
1837 if (ILIsParent(pidlNet, m_pidlParent, FALSE) && ILGetSize(pidlNet) < ILGetSize(m_pidlParent))
1838 m_SpecialFolder = CSIDL_NETWORK;
1839 }
1840 m_isFullStatusBar = bIsFileSystem || bForceFullStatusBar;
1841 _ForceStatusBarResize(); // This handles changing StatusBar parts
1842 UpdateStatusbar();
1843 UpdateStatusbarLocation();
1844
1845 return S_OK;
1846}
1847
1848// #### Handling of the menus ####
1849
1850HRESULT CDefView::FillFileMenu()
1851{
1852 HMENU hFileMenu = GetSubmenuByID(m_hMenu, FCIDM_MENU_FILE);
1853 if (!hFileMenu)
1854 return E_FAIL;
1855
1856 /* Cleanup the items added previously */
1857 for (int i = GetMenuItemCount(hFileMenu) - 1; i >= 0; i--)
1858 {
1859 UINT id = GetMenuItemID(hFileMenu, i);
1860 if (id < FCIDM_BROWSERFIRST || id > FCIDM_BROWSERLAST)
1861 DeleteMenu(hFileMenu, i, MF_BYPOSITION);
1862 }
1863
1864 // In case we still have this left over, clean it up
1865 if (m_pFileMenu)
1866 {
1867 IUnknown_SetSite(m_pFileMenu, NULL);
1868 m_pFileMenu.Release();
1869 }
1870 UINT selcount = m_ListView.GetSelectedCount();
1871 // Store context menu in m_pFileMenu and keep it to invoke the selected command later on
1872 HRESULT hr = GetItemObject(selcount ? SVGIO_SELECTION : SVGIO_BACKGROUND, IID_PPV_ARG(IContextMenu, &m_pFileMenu));
1873 if (FAILED_UNEXPECTEDLY(hr))
1874 return hr;
1875
1876 HMENU hmenu = CreatePopupMenu();
1877
1878 UINT cmf = GetContextMenuFlags(m_pShellBrowser, GetSelectionAttributes(SFGAO_CANRENAME));
1879 hr = m_pFileMenu->QueryContextMenu(hmenu, 0, DVIDM_CONTEXTMENU_FIRST, DVIDM_CONTEXTMENU_LAST, cmf);
1880 if (FAILED_UNEXPECTEDLY(hr))
1881 return hr;
1882
1883 // TODO: filter or something
1884 if (!selcount)
1885 {
1886 DeleteMenu(hmenu, FCIDM_SHVIEW_VIEW, MF_BYCOMMAND);
1887 DeleteMenu(hmenu, FCIDM_SHVIEW_ARRANGE, MF_BYCOMMAND);
1888 DeleteMenu(hmenu, FCIDM_SHVIEW_REFRESH, MF_BYCOMMAND);
1889 }
1890
1891 Shell_MergeMenus(hFileMenu, hmenu, 0, 0, 0xFFFF, MM_ADDSEPARATOR | MM_SUBMENUSHAVEIDS);
1892 ::DestroyMenu(hmenu);
1893 return S_OK;
1894}
1895
1896HRESULT CDefView::FillEditMenu()
1897{
1898 HMENU hEditMenu = GetSubmenuByID(m_hMenu, FCIDM_MENU_EDIT);
1899 if (!hEditMenu)
1900 return E_FAIL;
1901
1902 HMENU hmenuContents = ::LoadMenuW(shell32_hInstance, L"MENU_003");
1903 if (!hmenuContents)
1904 return E_FAIL;
1905
1906 Shell_MergeMenus(hEditMenu, hmenuContents, 0, 0, 0xFFFF, 0);
1907
1908 ::DestroyMenu(hmenuContents);
1909
1910 return S_OK;
1911}
1912
1913HRESULT CDefView::FillViewMenu()
1914{
1915 HMENU hViewMenu = GetSubmenuByID(m_hMenu, FCIDM_MENU_VIEW);
1916 if (!hViewMenu)
1917 return E_FAIL;
1918
1919 m_hMenuViewModes = ::LoadMenuW(shell32_hInstance, L"MENU_001");
1920 if (!m_hMenuViewModes)
1921 return E_FAIL;
1922
1923 UINT i = SHMenuIndexFromID(hViewMenu, FCIDM_MENU_VIEW_SEP_OPTIONS);
1924 Shell_MergeMenus(hViewMenu, m_hMenuViewModes, i, 0, 0xFFFF, MM_ADDSEPARATOR | MM_DONTREMOVESEPS | MM_SUBMENUSHAVEIDS);
1925
1926 return S_OK;
1927}
1928
1929HRESULT CDefView::FillArrangeAsMenu(HMENU hmenuArrange)
1930{
1931 bool forceMerge = false;
1932 UINT currentSortId = DVIDM_ARRANGESORT_FIRST + m_sortInfo.ListColumn;
1933
1934 // Make sure the column we currently sort by is in the menu
1935 RemoveMenu(m_hMenuArrangeModes, DVIDM_ARRANGESORT_LAST, MF_BYCOMMAND);
1936 if (m_sortInfo.ListColumn >= DEFVIEW_ARRANGESORT_MAXENUM)
1937 {
1938 C_ASSERT(DEFVIEW_ARRANGESORT_MAXENUM < DEFVIEW_ARRANGESORT_MAX);
1939 C_ASSERT(DVIDM_ARRANGESORT_FIRST + DEFVIEW_ARRANGESORT_MAXENUM == DVIDM_ARRANGESORT_LAST);
1940 WCHAR buf[MAX_PATH];
1941 LVCOLUMN lvc;
1942 lvc.mask = LVCF_TEXT;
1943 lvc.pszText = buf;
1944 lvc.cchTextMax = _countof(buf);
1945 currentSortId = DVIDM_ARRANGESORT_LAST;
1946 forceMerge = true;
1947 ListView_GetColumn(m_ListView.m_hWnd, m_sortInfo.ListColumn, &lvc);
1948 AppendMenuItem(m_hMenuArrangeModes, MF_STRING, currentSortId, lvc.pszText, m_sortInfo.ListColumn);
1949 }
1950
1951 // Prepend the sort-by items unless they are aleady there
1952 if (GetMenuItemID(hmenuArrange, 0) == FCIDM_SHVIEW_AUTOARRANGE || forceMerge)
1953 {
1954 Shell_MergeMenus(hmenuArrange, m_hMenuArrangeModes, 0, 0, 0xFFFF, MM_ADDSEPARATOR);
1955 }
1956
1957 CheckMenuRadioItem(hmenuArrange,
1958 DVIDM_ARRANGESORT_FIRST, DVIDM_ARRANGESORT_LAST,
1959 currentSortId, MF_BYCOMMAND);
1960
1961 if (m_FolderSettings.ViewMode == FVM_DETAILS || m_FolderSettings.ViewMode == FVM_LIST)
1962 {
1963 EnableMenuItem(hmenuArrange, FCIDM_SHVIEW_AUTOARRANGE, MF_BYCOMMAND | MF_GRAYED);
1964 EnableMenuItem(hmenuArrange, FCIDM_SHVIEW_ALIGNTOGRID, MF_BYCOMMAND | MF_GRAYED);
1965 }
1966 else
1967 {
1968 EnableMenuItem(hmenuArrange, FCIDM_SHVIEW_AUTOARRANGE, MF_BYCOMMAND);
1969 EnableMenuItem(hmenuArrange, FCIDM_SHVIEW_ALIGNTOGRID, MF_BYCOMMAND);
1970
1971 if (GetAutoArrange() == S_OK)
1972 CheckMenuItem(hmenuArrange, FCIDM_SHVIEW_AUTOARRANGE, MF_CHECKED);
1973 else
1974 CheckMenuItem(hmenuArrange, FCIDM_SHVIEW_AUTOARRANGE, MF_UNCHECKED);
1975
1976 if (_GetSnapToGrid() == S_OK)
1977 CheckMenuItem(hmenuArrange, FCIDM_SHVIEW_ALIGNTOGRID, MF_CHECKED);
1978 else
1979 CheckMenuItem(hmenuArrange, FCIDM_SHVIEW_ALIGNTOGRID, MF_UNCHECKED);
1980 }
1981
1982 return S_OK;
1983}
1984
1985HRESULT CDefView::CheckViewMode(HMENU hmenuView)
1986{
1987 if (m_FolderSettings.ViewMode >= FVM_FIRST && m_FolderSettings.ViewMode <= FVM_LAST)
1988 {
1989 UINT iItemFirst = FCIDM_SHVIEW_BIGICON;
1990 UINT iItemLast = iItemFirst + FVM_LAST - FVM_FIRST;
1991 UINT iItem = iItemFirst + m_FolderSettings.ViewMode - FVM_FIRST;
1992 CheckMenuRadioItem(hmenuView, iItemFirst, iItemLast, iItem, MF_BYCOMMAND);
1993 }
1994
1995 return S_OK;
1996}
1997
1998LRESULT CDefView::DoColumnContextMenu(LPARAM lParam)
1999{
2000 const UINT maxItems = 15; // Feels about right
2001 const UINT idMore = 0x1337;
2002 UINT idFirst = idMore + 1, idLast = idFirst;
2003 UINT lastValidListCol = 0; // Keep track of where the new column should be inserted
2004 UINT showMore = GetKeyState(VK_SHIFT) < 0;
2005 POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
2006 HWND hWndHdr = ListView_GetHeader(m_ListView.m_hWnd);
2007
2008 if (lParam == ~0)
2009 {
2010 RECT r;
2011 ::GetWindowRect(hWndHdr, &r);
2012 pt.x = r.left + ((r.right - r.left) / 2);
2013 pt.y = r.top + ((r.bottom - r.top) / 2);
2014 }
2015
2016 HMENU hMenu = CreatePopupMenu();
2017 if (!hMenu)
2018 return 0;
2019
2020 for (UINT foldCol = 0;; ++foldCol)
2021 {
2022 WCHAR buf[MAX_PATH];
2023 SHELLDETAILS sd;
2024 sd.str.uType = !STRRET_WSTR;
2025 if (FAILED(GetDetailsByFolderColumn(NULL, foldCol, sd)))
2026 break;
2027 if (FAILED(StrRetToStrNW(buf, _countof(buf), &sd.str, NULL)))
2028 break;
2029
2030 SHCOLSTATEF state = 0;
2031 if (!m_pSF2Parent || FAILED(m_pSF2Parent->GetDefaultColumnState(foldCol, &state)))
2032 state = 0;
2033 showMore |= (state & (SHCOLSTATE_SECONDARYUI));
2034
2035 UINT mf = MF_STRING;
2036 HRESULT listCol = MapFolderColumnToListColumn(foldCol);
2037
2038 if (foldCol == 0)
2039 mf |= MF_CHECKED | MF_GRAYED | MF_DISABLED; // Force column 0
2040 else if (state & (SHCOLSTATE_SECONDARYUI | SHCOLSTATE_HIDDEN))
2041 continue;
2042 else if (SUCCEEDED(listCol))
2043 mf |= MF_CHECKED;
2044
2045 if (AppendMenuItem(hMenu, mf, idLast, buf, lastValidListCol + 1))
2046 {
2047 idLast++;
2048 if (SUCCEEDED(listCol))
2049 lastValidListCol = listCol;
2050 }
2051
2052 if (idLast - idFirst == maxItems)
2053 {
2054 showMore++;
2055 break;
2056 }
2057 }
2058
2059 if (showMore)
2060 {
2061#if 0 // TODO
2062 InsertMenuW(hMenu, -1, MF_SEPARATOR, 0, NULL);
2063 InsertMenuW(hMenu, -1, MF_STRING, idMore, L"More...");
2064#endif
2065 }
2066
2067 // A cludge to force the cursor to update so we are not stuck with "size left/right" if
2068 // the right-click was on a column divider.
2069 ::PostMessage(m_ListView.m_hWnd, WM_SETCURSOR, (WPARAM) m_ListView.m_hWnd, HTCLIENT);
2070
2071 // Note: Uses the header as the owner so CDefView::OnInitMenuPopup does not mess us up.
2072 UINT idCmd = TrackPopupMenu(hMenu, TPM_RETURNCMD | TPM_LEFTBUTTON | TPM_RIGHTBUTTON,
2073 pt.x, pt.y, 0, hWndHdr, NULL);
2074 if (idCmd == idMore)
2075 {
2076 FIXME("Open More dialog\n");
2077 }
2078 else if (idCmd)
2079 {
2080 UINT foldCol = idCmd - idFirst;
2081 HRESULT listCol = MapFolderColumnToListColumn(foldCol);
2082 if (SUCCEEDED(listCol))
2083 {
2084 ListView_DeleteColumn(m_ListView.m_hWnd, listCol);
2085 }
2086 else
2087 {
2088 listCol = (UINT) GetMenuItemDataById(hMenu, idCmd);
2089 LoadColumn(foldCol, listCol, TRUE);
2090 }
2091 ColumnListChanged();
2092 }
2093 DestroyMenu(hMenu);
2094 return 0;
2095}
2096
2097/**********************************************************
2098* ShellView_GetSelections()
2099*
2100* - fills the m_apidl list with the selected objects
2101*
2102* RETURNS
2103* number of selected items
2104*/
2105UINT CDefView::GetSelections()
2106{
2107 UINT count = m_ListView.GetSelectedCount();
2108 if (count > m_cidl || !count || !m_apidl) // !count to free possibly large cache, !m_apidl to make sure m_apidl is a valid pointer
2109 {
2110 SHFree(m_apidl);
2111 m_apidl = static_cast<PCUITEMID_CHILD*>(SHAlloc(count * sizeof(PCUITEMID_CHILD)));
2112 if (!m_apidl)
2113 {
2114 m_cidl = 0;
2115 return 0;
2116 }
2117 }
2118 m_cidl = count;
2119
2120 TRACE("-- Items selected =%u\n", m_cidl);
2121
2122 ASSERT(m_ListView);
2123
2124 UINT i = 0;
2125 int lvIndex = -1;
2126 while ((lvIndex = m_ListView.GetNextItem(lvIndex, LVNI_SELECTED)) > -1)
2127 {
2128 m_apidl[i] = _PidlByItem(lvIndex);
2129 i++;
2130 if (i == m_cidl)
2131 break;
2132 TRACE("-- selected Item found\n");
2133 }
2134
2135 return m_cidl;
2136}
2137
2138SFGAOF CDefView::GetSelectionAttributes(SFGAOF Query)
2139{
2140 if (!GetSelections())
2141 return 0;
2142 SFGAOF Attr = Query;
2143 return SUCCEEDED(m_pSFParent->GetAttributesOf(m_cidl, m_apidl, &Attr)) ? (Attr & Query) : 0;
2144}
2145
2146HRESULT CDefView::InvokeContextMenuCommand(CComPtr<IContextMenu>& pCM, LPCSTR lpVerb, POINT* pt, bool TryMapVerb)
2147{
2148 CMINVOKECOMMANDINFOEX cmi;
2149
2150 ZeroMemory(&cmi, sizeof(cmi));
2151 cmi.cbSize = sizeof(cmi);
2152 cmi.hwnd = m_hWnd;
2153 cmi.lpVerb = lpVerb;
2154 cmi.nShow = SW_SHOW;
2155
2156 WCHAR szverbW[sizeof("properties")];
2157 static const WORD verbmap[] = { FCIDM_SHVIEW_DELETE, FCIDM_SHVIEW_RENAME,
2158 FCIDM_SHVIEW_PROPERTIES, FCIDM_SHVIEW_CREATELINK,
2159 FCIDM_SHVIEW_CUT, FCIDM_SHVIEW_COPY, FCIDM_SHVIEW_INSERT };
2160 for (SIZE_T i = 0; TryMapVerb && i < _countof(verbmap); ++i)
2161 {
2162 if (cmi.lpVerb != MAKEINTRESOURCEA(verbmap[i]))
2163 continue;
2164 if (PCSTR pszverbA = MapFcidmCmdToVerb((UINT_PTR)cmi.lpVerb))
2165 {
2166 // Map our internal commands to canonical verbs so non-shell32 menus can understand us
2167 SHAnsiToUnicode(pszverbA, szverbW, _countof(szverbW));
2168 cmi.lpVerb = pszverbA;
2169 cmi.lpVerbW = szverbW;
2170 break;
2171 }
2172 }
2173
2174 if (GetKeyState(VK_SHIFT) < 0)
2175 cmi.fMask |= CMIC_MASK_SHIFT_DOWN;
2176
2177 if (GetKeyState(VK_CONTROL) < 0)
2178 cmi.fMask |= CMIC_MASK_CONTROL_DOWN;
2179
2180 if (pt)
2181 {
2182 cmi.fMask |= CMIC_MASK_PTINVOKE;
2183 cmi.ptInvoke = *pt;
2184 }
2185
2186 WCHAR szDirW[MAX_PATH] = L"";
2187 CHAR szDirA[MAX_PATH];
2188 if (SUCCEEDED(_DoFolderViewCB(SFVM_GETCOMMANDDIR, _countof(szDirW), (LPARAM)szDirW)) &&
2189 *szDirW != UNICODE_NULL)
2190 {
2191 SHUnicodeToAnsi(szDirW, szDirA, _countof(szDirA));
2192 cmi.fMask |= CMIC_MASK_UNICODE;
2193 cmi.lpDirectory = szDirA;
2194 cmi.lpDirectoryW = szDirW;
2195 }
2196
2197 HRESULT hr = pCM->InvokeCommand((LPCMINVOKECOMMANDINFO)&cmi);
2198 // Most of our callers will do this, but if they would forget (File menu!)
2199 IUnknown_SetSite(pCM, NULL);
2200 pCM.Release();
2201
2202 if (FAILED_UNEXPECTEDLY(hr))
2203 return hr;
2204
2205 return S_OK;
2206}
2207
2208HRESULT CDefView::OpenSelectedItems(PCSTR pszVerb)
2209{
2210 HMENU hMenu;
2211 UINT uCommand;
2212 HRESULT hResult;
2213
2214 if (m_ListView.GetSelectedCount() == 0)
2215 return S_OK;
2216
2217 hResult = OnDefaultCommand();
2218 if (hResult == S_OK)
2219 return hResult;
2220
2221 hMenu = CreatePopupMenu();
2222 if (!hMenu)
2223 return E_FAIL;
2224
2225 CComPtr<IContextMenu> pCM;
2226 hResult = GetItemObject(SVGIO_SELECTION, IID_PPV_ARG(IContextMenu, &pCM));
2227 MenuCleanup _(pCM, hMenu);
2228 if (FAILED_UNEXPECTEDLY(hResult))
2229 return hResult;
2230
2231 UINT cmf = CMF_DEFAULTONLY | GetContextMenuFlags(m_pShellBrowser, 0);
2232 hResult = pCM->QueryContextMenu(hMenu, 0, DVIDM_CONTEXTMENU_FIRST, DVIDM_CONTEXTMENU_LAST, cmf);
2233 if (FAILED_UNEXPECTEDLY(hResult))
2234 return hResult;
2235
2236 uCommand = GetMenuDefaultItem(hMenu, FALSE, 0);
2237 if (uCommand == (UINT)-1 && !pszVerb)
2238 {
2239 ERR("GetMenuDefaultItem returned -1\n");
2240 return E_FAIL;
2241 }
2242 if (!pszVerb)
2243 pszVerb = MAKEINTRESOURCEA(uCommand);
2244
2245 InvokeContextMenuCommand(pCM, pszVerb, NULL);
2246
2247 return hResult;
2248}
2249
2250LRESULT CDefView::OnContextMenu(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
2251{
2252 POINT pt = { pt.x = GET_X_LPARAM(lParam), pt.y = GET_Y_LPARAM(lParam) };
2253 UINT uCommand;
2254 HRESULT hResult;
2255
2256 TRACE("(%p)\n", this);
2257
2258 if (m_hContextMenu != NULL)
2259 {
2260 ERR("HACK: Aborting context menu in nested call\n");
2261 return 0;
2262 }
2263
2264 HWND hWndHdr = ListView_GetHeader(m_ListView.m_hWnd);
2265 RECT r;
2266 if (::GetWindowRect(hWndHdr, &r) && PtInRect(&r, pt) && ::IsWindowVisible(hWndHdr))
2267 {
2268 return DoColumnContextMenu(lParam);
2269 }
2270
2271 m_hContextMenu = CreatePopupMenu();
2272 if (!m_hContextMenu)
2273 return E_FAIL;
2274
2275 if (lParam != ~0) // unless app key (menu key) was pressed
2276 {
2277 LV_HITTESTINFO hittest = { pt };
2278 ScreenToClient(&hittest.pt);
2279 m_ListView.HitTest(&hittest);
2280
2281 // Right-Clicked item is selected? If selected, no selection change.
2282 // If not selected, then reset the selection and select the item.
2283 if ((hittest.flags & LVHT_ONITEM) &&
2284 m_ListView.GetItemState(hittest.iItem, LVIS_SELECTED) != LVIS_SELECTED)
2285 {
2286 SelectItem(hittest.iItem, SVSI_SELECT | SVSI_DESELECTOTHERS | SVSI_ENSUREVISIBLE);
2287 }
2288 }
2289
2290 UINT count = m_ListView.GetSelectedCount();
2291 // In case we still have this left over, clean it up
2292 hResult = GetItemObject(count ? SVGIO_SELECTION : SVGIO_BACKGROUND, IID_PPV_ARG(IContextMenu, &m_pCM));
2293 MenuCleanup _(m_pCM, m_hContextMenu);
2294 if (FAILED_UNEXPECTEDLY(hResult))
2295 return 0;
2296
2297 UINT cmf = GetContextMenuFlags(m_pShellBrowser, GetSelectionAttributes(SFGAO_CANRENAME));
2298 // Use 1 as the first id we want. 0 means that user canceled the menu
2299 hResult = m_pCM->QueryContextMenu(m_hContextMenu, 0, CONTEXT_MENU_BASE_ID, DVIDM_CONTEXTMENU_LAST, cmf);
2300 if (FAILED_UNEXPECTEDLY(hResult))
2301 return 0;
2302
2303 if (m_pCommDlgBrowser && !(GetCommDlgViewFlags() & CDB2GVF_NOSELECTVERB))
2304 {
2305 HMENU hMenuSource = LoadMenuW(_AtlBaseModule.GetResourceInstance(), MAKEINTRESOURCEW(IDM_DVSELECT));
2306 Shell_MergeMenus(m_hContextMenu, GetSubMenu(hMenuSource, 0), 0, DVIDM_COMMDLG_SELECT, 0xffff, MM_ADDSEPARATOR | MM_DONTREMOVESEPS);
2307 DestroyMenu(hMenuSource);
2308 SetMenuDefaultItem(m_hContextMenu, DVIDM_COMMDLG_SELECT, MF_BYCOMMAND);
2309 // TODO: ICommDlgBrowser2::GetDefaultMenuText == S_OK
2310 }
2311
2312 // There is no position requested, so try to find one
2313 if (lParam == ~0)
2314 {
2315 HWND hFocus = ::GetFocus();
2316 int lvIndex = -1;
2317
2318 if (hFocus == m_ListView.m_hWnd || m_ListView.IsChild(hFocus))
2319 {
2320 // Is there an item focused and selected?
2321 lvIndex = m_ListView.GetNextItem(-1, LVNI_SELECTED | LVNI_FOCUSED);
2322 // If not, find the first selected item
2323 if (lvIndex < 0)
2324 lvIndex = m_ListView.GetNextItem(-1, LVNI_SELECTED);
2325 }
2326
2327 // We got something
2328 if (lvIndex > -1)
2329 {
2330 // Find the center of the icon
2331 RECT rc = { LVIR_ICON };
2332 m_ListView.SendMessage(LVM_GETITEMRECT, lvIndex, (LPARAM)&rc);
2333 pt.x = (rc.right + rc.left) / 2;
2334 pt.y = (rc.bottom + rc.top) / 2;
2335 }
2336 else
2337 {
2338 // We have to drop it somewhere
2339 pt.x = pt.y = 0;
2340 }
2341
2342 m_ListView.ClientToScreen(&pt);
2343 }
2344
2345 CComPtr<ICommDlgBrowser2> pcdb2;
2346 if (m_pCommDlgBrowser && SUCCEEDED(m_pCommDlgBrowser->QueryInterface(IID_PPV_ARG(ICommDlgBrowser2, &pcdb2))))
2347 pcdb2->Notify(static_cast<IShellView*>(this), CDB2N_CONTEXTMENU_START);
2348
2349 // This runs the message loop, calling back to us with f.e. WM_INITPOPUP (hence why m_hContextMenu and m_pCM exist)
2350 uCommand = TrackPopupMenu(m_hContextMenu,
2351 TPM_LEFTALIGN | TPM_RETURNCMD | TPM_LEFTBUTTON | TPM_RIGHTBUTTON,
2352 pt.x, pt.y, 0, m_hWnd, NULL);
2353 if (uCommand >= DVIDM_ARRANGESORT_FIRST && uCommand <= DVIDM_ARRANGESORT_LAST)
2354 {
2355 SendMessage(WM_COMMAND, uCommand, 0);
2356 }
2357 else if (uCommand != 0 && !(uCommand == DVIDM_COMMDLG_SELECT && OnDefaultCommand() == S_OK))
2358 {
2359 InvokeContextMenuCommand(m_pCM, MAKEINTRESOURCEA(uCommand - CONTEXT_MENU_BASE_ID), &pt);
2360 }
2361
2362 if (pcdb2)
2363 pcdb2->Notify(static_cast<IShellView*>(this), CDB2N_CONTEXTMENU_DONE);
2364 return 0;
2365}
2366
2367LRESULT CDefView::OnExplorerCommand(UINT uCommand, BOOL bUseSelection)
2368{
2369 HRESULT hResult;
2370 HMENU hMenu = NULL;
2371
2372 CComPtr<IContextMenu> pCM;
2373 hResult = GetItemObject(bUseSelection ? SVGIO_SELECTION : SVGIO_BACKGROUND, IID_PPV_ARG(IContextMenu, &pCM));
2374 if (FAILED_UNEXPECTEDLY(hResult))
2375 return 0;
2376
2377 MenuCleanup _(pCM, hMenu);
2378
2379 if ((uCommand != FCIDM_SHVIEW_DELETE) && (uCommand != FCIDM_SHVIEW_RENAME))
2380 {
2381 hMenu = CreatePopupMenu();
2382 if (!hMenu)
2383 return 0;
2384
2385 hResult = pCM->QueryContextMenu(hMenu, 0, DVIDM_CONTEXTMENU_FIRST, DVIDM_CONTEXTMENU_LAST, CMF_NORMAL);
2386 if (FAILED_UNEXPECTEDLY(hResult))
2387 return 0;
2388 }
2389
2390 if (bUseSelection)
2391 {
2392 // FIXME: we should cache this
2393 SFGAOF rfg = SFGAO_BROWSABLE | SFGAO_CANCOPY | SFGAO_CANLINK | SFGAO_CANMOVE | SFGAO_CANDELETE | SFGAO_CANRENAME | SFGAO_HASPROPSHEET | SFGAO_FILESYSTEM | SFGAO_FOLDER;
2394 hResult = m_pSFParent->GetAttributesOf(m_cidl, m_apidl, &rfg);
2395 if (FAILED_UNEXPECTEDLY(hResult))
2396 return 0;
2397
2398 if (!(rfg & SFGAO_CANMOVE) && uCommand == FCIDM_SHVIEW_CUT)
2399 return 0;
2400 if (!(rfg & SFGAO_CANCOPY) && uCommand == FCIDM_SHVIEW_COPY)
2401 return 0;
2402 if (!(rfg & SFGAO_CANDELETE) && uCommand == FCIDM_SHVIEW_DELETE)
2403 return 0;
2404 if (!(rfg & SFGAO_CANRENAME) && uCommand == FCIDM_SHVIEW_RENAME)
2405 return 0;
2406 if (!(rfg & SFGAO_HASPROPSHEET) && uCommand == FCIDM_SHVIEW_PROPERTIES)
2407 return 0;
2408 }
2409
2410 // FIXME: We should probably use the objects position?
2411 InvokeContextMenuCommand(pCM, MAKEINTRESOURCEA(uCommand), NULL, true);
2412 return 0;
2413}
2414
2415// ##### message handling #####
2416
2417LRESULT CDefView::OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
2418{
2419 WORD wWidth, wHeight;
2420
2421 wWidth = LOWORD(lParam);
2422 wHeight = HIWORD(lParam);
2423
2424 TRACE("%p width=%u height=%u\n", this, wWidth, wHeight);
2425
2426 // WM_SIZE can come before WM_CREATE
2427 if (!m_ListView)
2428 return 0;
2429
2430 /* Resize the ListView to fit our window */
2431 ::MoveWindow(m_ListView, 0, 0, wWidth, wHeight, TRUE);
2432
2433 _DoFolderViewCB(SFVM_SIZE, 0, 0);
2434
2435 _ForceStatusBarResize();
2436 UpdateStatusbar();
2437
2438 return 0;
2439}
2440
2441// internal
2442void CDefView::OnDeactivate()
2443{
2444 TRACE("%p\n", this);
2445
2446 if (m_uState != SVUIA_DEACTIVATE)
2447 {
2448 // TODO: cleanup menu after deactivation
2449 m_uState = SVUIA_DEACTIVATE;
2450 }
2451}
2452
2453void CDefView::DoActivate(UINT uState)
2454{
2455 TRACE("%p uState=%x\n", this, uState);
2456
2457 // don't do anything if the state isn't really changing
2458 if (m_uState == uState)
2459 {
2460 return;
2461 }
2462
2463 if (uState == SVUIA_DEACTIVATE)
2464 {
2465 OnDeactivate();
2466 }
2467 else
2468 {
2469 if(m_hMenu && !m_bmenuBarInitialized)
2470 {
2471 FillEditMenu();
2472 FillViewMenu();
2473 m_pShellBrowser->SetMenuSB(m_hMenu, 0, m_hWnd);
2474 m_bmenuBarInitialized = TRUE;
2475 }
2476
2477 if (SVUIA_ACTIVATE_FOCUS == uState)
2478 {
2479 m_ListView.SetFocus();
2480 }
2481 }
2482
2483 m_uState = uState;
2484 TRACE("--\n");
2485}
2486
2487void CDefView::_DoCopyToMoveToFolder(BOOL bCopy)
2488{
2489 if (!GetSelections())
2490 return;
2491
2492 SFGAOF rfg = SFGAO_CANCOPY | SFGAO_CANMOVE | SFGAO_FILESYSTEM;
2493 HRESULT hr = m_pSFParent->GetAttributesOf(m_cidl, m_apidl, &rfg);
2494 if (FAILED_UNEXPECTEDLY(hr))
2495 return;
2496
2497 if (!bCopy && !(rfg & SFGAO_CANMOVE))
2498 return;
2499 if (bCopy && !(rfg & SFGAO_CANCOPY))
2500 return;
2501
2502 CComPtr<IContextMenu> pCM;
2503 hr = m_pSFParent->GetUIObjectOf(m_hWnd, m_cidl, m_apidl, IID_IContextMenu, 0, (void **)&pCM);
2504 if (FAILED_UNEXPECTEDLY(hr))
2505 return;
2506
2507 InvokeContextMenuCommand(pCM, (bCopy ? "copyto" : "moveto"), NULL);
2508}
2509
2510LRESULT CDefView::OnActivate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
2511{
2512 DoActivate(SVUIA_ACTIVATE_FOCUS);
2513 return 0;
2514}
2515
2516LRESULT CDefView::OnSetFocus(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
2517{
2518 TRACE("%p\n", this);
2519
2520 /* Tell the browser one of our windows has received the focus. This
2521 should always be done before merging menus (OnActivate merges the
2522 menus) if one of our windows has the focus.*/
2523
2524 m_pShellBrowser->OnViewWindowActive(this);
2525 DoActivate(SVUIA_ACTIVATE_FOCUS);
2526
2527 /* Set the focus to the listview */
2528 m_ListView.SetFocus();
2529
2530 /* Notify the ICommDlgBrowser interface */
2531 OnStateChange(CDBOSC_SETFOCUS);
2532
2533 return 0;
2534}
2535
2536LRESULT CDefView::OnKillFocus(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
2537{
2538 TRACE("(%p) stub\n", this);
2539
2540 DoActivate(SVUIA_ACTIVATE_NOFOCUS);
2541 /* Notify the ICommDlgBrowser */
2542 OnStateChange(CDBOSC_KILLFOCUS);
2543
2544 return 0;
2545}
2546
2547// the CmdID's are the ones from the context menu
2548LRESULT CDefView::OnCommand(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
2549{
2550 DWORD dwCmdID;
2551 DWORD dwCmd;
2552 HWND hwndCmd;
2553 int nCount;
2554
2555 dwCmdID = GET_WM_COMMAND_ID(wParam, lParam);
2556 dwCmd = GET_WM_COMMAND_CMD(wParam, lParam);
2557 hwndCmd = GET_WM_COMMAND_HWND(wParam, lParam);
2558
2559 TRACE("(%p)->(0x%08x 0x%08x %p) stub\n", this, dwCmdID, dwCmd, hwndCmd);
2560
2561 if (dwCmdID >= DVIDM_ARRANGESORT_FIRST && dwCmdID <= DVIDM_ARRANGESORT_LAST)
2562 {
2563 UINT listCol = (UINT)GetMenuItemDataById(m_hMenuArrangeModes, dwCmdID);
2564 _Sort(listCol);
2565 return 0;
2566 }
2567
2568 switch (dwCmdID)
2569 {
2570 case FCIDM_SHVIEW_BIGICON: C_ASSERT(FCIDM_SHVIEW_BIGICON == 0x7029);
2571 case FCIDM_SHVIEW_SMALLICON: C_ASSERT(FCIDM_SHVIEW_SMALLICON == 0x702A);
2572 case FCIDM_SHVIEW_LISTVIEW: C_ASSERT(FCIDM_SHVIEW_LISTVIEW == 0x702B);
2573 case FCIDM_SHVIEW_REPORTVIEW: C_ASSERT(FCIDM_SHVIEW_REPORTVIEW == 0x702C);
2574 case 0x702D:
2575 case FCIDM_SHVIEW_TILEVIEW: C_ASSERT(FCIDM_SHVIEW_TILEVIEW == 0x702E);
2576 case 0x702F:
2577 SetCurrentViewMode(dwCmdID - FCIDM_SHVIEW_BIGICON + 1);
2578 break;
2579 case FCIDM_SHVIEW_SNAPTOGRID:
2580 m_ListView.Arrange(LVA_SNAPTOGRID);
2581 break;
2582 case FCIDM_SHVIEW_ALIGNTOGRID:
2583 if (_GetSnapToGrid() == S_OK)
2584 m_ListView.SetExtendedListViewStyle(0, LVS_EX_SNAPTOGRID);
2585 else
2586 ArrangeGrid();
2587 break;
2588 case FCIDM_SHVIEW_AUTOARRANGE:
2589 if (GetAutoArrange() == S_OK)
2590 m_ListView.ModifyStyle(LVS_AUTOARRANGE, 0);
2591 else
2592 AutoArrange();
2593 break;
2594 case FCIDM_SHVIEW_SELECTALL:
2595 if (_DoFolderViewCB(SFVM_CANSELECTALL, 0, 0) != S_FALSE)
2596 m_ListView.SetItemState(-1, LVIS_SELECTED, LVIS_SELECTED);
2597 break;
2598 case FCIDM_SHVIEW_INVERTSELECTION:
2599 nCount = m_ListView.GetItemCount();
2600 for (int i=0; i < nCount; i++)
2601 m_ListView.SetItemState(i, m_ListView.GetItemState(i, LVIS_SELECTED) ^ LVIS_SELECTED, LVIS_SELECTED);
2602 break;
2603 case FCIDM_SHVIEW_DESELECTALL:
2604 m_ListView.SetItemState(-1, 0, LVIS_SELECTED);
2605 break;
2606 case FCIDM_SHVIEW_REFRESH:
2607 Refresh();
2608 break;
2609 case FCIDM_SHVIEW_DELETE:
2610 case FCIDM_SHVIEW_CUT:
2611 case FCIDM_SHVIEW_COPY:
2612 case FCIDM_SHVIEW_RENAME:
2613 case FCIDM_SHVIEW_PROPERTIES:
2614 if (SHRestricted(REST_NOVIEWCONTEXTMENU))
2615 return 0;
2616 return OnExplorerCommand(dwCmdID, TRUE);
2617 case FCIDM_SHVIEW_COPYTO: C_ASSERT(FCIDM_SHVIEW_COPYTO == 0x701e);
2618 case FCIDM_SHVIEW_MOVETO: C_ASSERT(FCIDM_SHVIEW_MOVETO == 0x701f);
2619 _DoCopyToMoveToFolder(dwCmdID == FCIDM_SHVIEW_COPYTO);
2620 return 0;
2621 case FCIDM_SHVIEW_INSERT:
2622 case FCIDM_SHVIEW_UNDO:
2623 case FCIDM_SHVIEW_INSERTLINK:
2624 case FCIDM_SHVIEW_NEWFOLDER:
2625 return OnExplorerCommand(dwCmdID, FALSE);
2626 default:
2627 // WM_COMMAND messages from file menu are routed to CDefView to let m_pFileMenu handle them
2628 if (m_pFileMenu && dwCmd == 0)
2629 {
2630 HMENU Dummy = NULL;
2631 MenuCleanup _(m_pFileMenu, Dummy);
2632 InvokeContextMenuCommand(m_pFileMenu, MAKEINTRESOURCEA(dwCmdID), NULL);
2633 }
2634 }
2635
2636 return 0;
2637}
2638
2639static BOOL
2640SelectExtOnRename(void)
2641{
2642 HKEY hKey;
2643 LONG error;
2644 DWORD dwValue = FALSE, cbValue;
2645
2646 error = RegOpenKeyExW(HKEY_CURRENT_USER, REGSTR_PATH_EXPLORER, 0, KEY_READ, &hKey);
2647 if (error)
2648 return dwValue;
2649
2650 cbValue = sizeof(dwValue);
2651 RegQueryValueExW(hKey, L"SelectExtOnRename", NULL, NULL, (LPBYTE)&dwValue, &cbValue);
2652
2653 RegCloseKey(hKey);
2654 return !!dwValue;
2655}
2656
2657LRESULT CDefView::OnNotify(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
2658{
2659 UINT CtlID;
2660 LPNMHDR lpnmh;
2661 LPNMLISTVIEW lpnmlv;
2662 NMLVDISPINFOW *lpdi;
2663 PCUITEMID_CHILD pidl;
2664 BOOL unused;
2665
2666 CtlID = wParam;
2667 lpnmh = (LPNMHDR)lParam;
2668 lpnmlv = (LPNMLISTVIEW)lpnmh;
2669 lpdi = (NMLVDISPINFOW *)lpnmh;
2670
2671 TRACE("%p CtlID=%u lpnmh->code=%x\n", this, CtlID, lpnmh->code);
2672
2673 switch (lpnmh->code)
2674 {
2675 case NM_SETFOCUS:
2676 TRACE("-- NM_SETFOCUS %p\n", this);
2677 OnSetFocus(0, 0, 0, unused);
2678 break;
2679 case NM_KILLFOCUS:
2680 TRACE("-- NM_KILLFOCUS %p\n", this);
2681 OnDeactivate();
2682 /* Notify the ICommDlgBrowser interface */
2683 OnStateChange(CDBOSC_KILLFOCUS);
2684 break;
2685 case NM_CUSTOMDRAW:
2686 TRACE("-- NM_CUSTOMDRAW %p\n", this);
2687 return CDRF_DODEFAULT;
2688 case NM_RELEASEDCAPTURE:
2689 TRACE("-- NM_RELEASEDCAPTURE %p\n", this);
2690 break;
2691 case NM_CLICK:
2692 TRACE("-- NM_CLICK %p\n", this);
2693 break;
2694 case NM_RCLICK:
2695 TRACE("-- NM_RCLICK %p\n", this);
2696 break;
2697 case NM_DBLCLK:
2698 TRACE("-- NM_DBLCLK %p\n", this);
2699 break;
2700 case NM_RETURN:
2701 TRACE("-- NM_RETURN %p\n", this);
2702 break;
2703 case HDN_ENDTRACKW:
2704 TRACE("-- HDN_ENDTRACKW %p\n", this);
2705 //nColumn1 = m_ListView.GetColumnWidth(0);
2706 //nColumn2 = m_ListView.GetColumnWidth(1);
2707 break;
2708 case LVN_DELETEITEM:
2709 TRACE("-- LVN_DELETEITEM %p\n", this);
2710 /*delete the pidl because we made a copy of it*/
2711 SHFree(reinterpret_cast<LPVOID>(lpnmlv->lParam));
2712 break;
2713 case LVN_DELETEALLITEMS:
2714 TRACE("-- LVN_DELETEALLITEMS %p\n", this);
2715 return FALSE;
2716 case LVN_INSERTITEM:
2717 TRACE("-- LVN_INSERTITEM (STUB)%p\n", this);
2718 break;
2719 case LVN_ITEMACTIVATE:
2720 TRACE("-- LVN_ITEMACTIVATE %p\n", this);
2721 OpenSelectedItems(((NMITEMACTIVATE *)lpnmh)->uKeyFlags & LVKF_ALT ? "properties" : NULL);
2722 break;
2723 case LVN_COLUMNCLICK:
2724 {
2725 UINT foldercol = MapListColumnToFolderColumn(lpnmlv->iSubItem);
2726 HRESULT hr = S_FALSE;
2727 if (m_pSDParent)
2728 hr = m_pSDParent->ColumnClick(foldercol);
2729 if (hr != S_OK)
2730 hr = _DoFolderViewCB(SFVM_COLUMNCLICK, foldercol, 0);
2731 if (hr != S_OK)
2732 _Sort(lpnmlv->iSubItem);
2733 break;
2734 }
2735 case LVN_GETDISPINFOA:
2736 case LVN_GETDISPINFOW:
2737 TRACE("-- LVN_GETDISPINFO %p\n", this);
2738 pidl = _PidlByItem(lpdi->item);
2739
2740 if (lpdi->item.mask & LVIF_TEXT) /* text requested */
2741 {
2742 SHELLDETAILS sd;
2743 if (FAILED_UNEXPECTEDLY(GetDetailsByListColumn(pidl, lpdi->item.iSubItem, sd)))
2744 break;
2745
2746 if (lpnmh->code == LVN_GETDISPINFOA)
2747 {
2748 /* shouldn't happen */
2749 NMLVDISPINFOA *lpdiA = (NMLVDISPINFOA *)lpnmh;
2750 StrRetToStrNA( lpdiA->item.pszText, lpdiA->item.cchTextMax, &sd.str, NULL);
2751 TRACE("-- text=%s\n", lpdiA->item.pszText);
2752 }
2753 else /* LVN_GETDISPINFOW */
2754 {
2755 StrRetToStrNW( lpdi->item.pszText, lpdi->item.cchTextMax, &sd.str, NULL);
2756 TRACE("-- text=%s\n", debugstr_w(lpdi->item.pszText));
2757 }
2758 }
2759 if (lpdi->item.mask & LVIF_IMAGE)
2760 {
2761 lpdi->item.iImage = SHMapPIDLToSystemImageListIndex(m_pSFParent, pidl, 0);
2762 }
2763 if (lpdi->item.mask & LVIF_STATE)
2764 {
2765 if ((lpdi->item.stateMask & LVIS_CUT) && GetItemAttributes(pidl, SFGAO_HIDDEN | SFGAO_GHOSTED))
2766 lpdi->item.state |= LVIS_CUT;
2767 }
2768 lpdi->item.mask |= LVIF_DI_SETITEM;
2769 break;
2770 case LVN_ITEMCHANGED:
2771 TRACE("-- LVN_ITEMCHANGED %p\n", this);
2772 if ((lpnmlv->uOldState ^ lpnmlv->uNewState) & (LVIS_SELECTED | LVIS_FOCUSED))
2773 {
2774 OnStateChange(CDBOSC_SELCHANGE); // browser will get the IDataObject
2775 // FIXME: Use LVIS_DROPHILITED instead in drag_notify_subitem
2776 if (!m_ScheduledStatusbarUpdate && (m_iDragOverItem == -1 || m_pCurDropTarget == NULL))
2777 {
2778 m_ScheduledStatusbarUpdate = true;
2779 PostMessage(SHV_UPDATESTATUSBAR, 0, 0);
2780 }
2781 _DoFolderViewCB(SFVM_SELECTIONCHANGED, NULL/* FIXME */, NULL/* FIXME */);
2782 }
2783 break;
2784 case LVN_BEGINDRAG:
2785 case LVN_BEGINRDRAG:
2786 TRACE("-- LVN_BEGINDRAG\n");
2787 if (GetSelections())
2788 {
2789 CComPtr<IDataObject> pda;
2790 DWORD dwAttributes = SFGAO_CANCOPY | SFGAO_CANLINK;
2791 DWORD dwEffect = DROPEFFECT_MOVE;
2792
2793 if (SUCCEEDED(m_pSFParent->GetUIObjectOf(m_hWnd, m_cidl, m_apidl, IID_NULL_PPV_ARG(IDataObject, &pda))))
2794 {
2795 LPNMLISTVIEW params = (LPNMLISTVIEW)lParam;
2796
2797 if (SUCCEEDED(m_pSFParent->GetAttributesOf(m_cidl, m_apidl, &dwAttributes)))
2798 dwEffect |= dwAttributes & (SFGAO_CANCOPY | SFGAO_CANLINK);
2799
2800 CComPtr<IAsyncOperation> piaso;
2801 if (SUCCEEDED(pda->QueryInterface(IID_PPV_ARG(IAsyncOperation, &piaso))))
2802 piaso->SetAsyncMode(TRUE);
2803
2804 DWORD dwEffect2;
2805
2806 m_pSourceDataObject = pda;
2807 m_ptFirstMousePos = params->ptAction;
2808 ClientToScreen(&m_ptFirstMousePos);
2809 ::ClientToListView(m_ListView, &m_ptFirstMousePos);
2810
2811 HIMAGELIST big_icons, small_icons;
2812 Shell_GetImageLists(&big_icons, &small_icons);
2813 PCUITEMID_CHILD pidl = _PidlByItem(params->iItem);
2814 int iIcon = SHMapPIDLToSystemImageListIndex(m_pSFParent, pidl, 0);
2815 POINT ptItem;
2816 m_ListView.GetItemPosition(params->iItem, &ptItem);
2817
2818 ImageList_BeginDrag(big_icons, iIcon, params->ptAction.x - ptItem.x, params->ptAction.y - ptItem.y);
2819 DoDragDrop(pda, this, dwEffect, &dwEffect2);
2820 m_pSourceDataObject.Release();
2821 }
2822 }
2823 break;
2824 case LVN_BEGINLABELEDITW:
2825 {
2826 TRACE("-- LVN_BEGINLABELEDITW %p\n", this);
2827 HWND hEdit = ListView_GetEditControl(m_ListView);
2828 pidl = _PidlByItem(lpdi->item);
2829 DWORD fAttr = pidl ? GetItemAttributes(pidl, SFGAO_CANRENAME | SFGAO_FOLDER | SFGAO_FILESYSTEM) : 0;
2830 if (!hEdit || !(fAttr & SFGAO_CANRENAME))
2831 {
2832 MessageBeep(0xffffffff);
2833 return TRUE;
2834 }
2835
2836 WCHAR szName[MAX_PATH], *pszText = lpdi->item.pszText;
2837 if (SUCCEEDED(DisplayNameOfW(m_pSFParent, pidl, SHGDN_FOREDITING | SHGDN_INFOLDER,
2838 szName, _countof(szName))))
2839 {
2840 pszText = szName;
2841 ::SetWindowText(hEdit, pszText);
2842 }
2843
2844 // smartass-renaming: See CORE-15242
2845 if (!(fAttr & SFGAO_FOLDER) && (fAttr & SFGAO_FILESYSTEM) &&
2846 (lpdi->item.mask & LVIF_TEXT) && !SelectExtOnRename())
2847 {
2848 CComHeapPtr<ITEMIDLIST_RELATIVE> pidlFull(ILCombine(m_pidlParent, pidl));
2849 WCHAR szFullPath[MAX_PATH];
2850 if (SHGetPathFromIDListW(pidlFull, szFullPath) && !SHELL_FS_HideExtension(szFullPath))
2851 {
2852 LPWSTR pchDotExt = PathFindExtensionW(pszText);
2853 ::PostMessageW(hEdit, EM_SETSEL, 0, pchDotExt - pszText);
2854 ::PostMessageW(hEdit, EM_SCROLLCARET, 0, 0);
2855 }
2856 }
2857
2858 INT cchLimit = 0;
2859 _DoFolderViewCB(SFVM_GETNAMELENGTH, (WPARAM)pidl, (LPARAM)&cchLimit);
2860 if (cchLimit)
2861 ::SendMessageW(hEdit, EM_SETLIMITTEXT, cchLimit, 0);
2862 SHLimitInputEdit(hEdit, m_pSFParent);
2863 m_isEditing = TRUE;
2864 return FALSE;
2865 }
2866 case LVN_ENDLABELEDITW:
2867 {
2868 TRACE("-- LVN_ENDLABELEDITW %p\n", this);
2869 m_isEditing = FALSE;
2870 if (!lpdi->item.pszText)
2871 return TRUE;
2872
2873 pidl = _PidlByItem(lpdi->item);
2874 // We have to copy the old PIDL because SetNameOf might generate a SHCNE_UPDATEITEM
2875 // and that notification can cause us to call LV_UpdateItem and free the old PIDL too soon.
2876 CComHeapPtr<ITEMIDLIST_ABSOLUTE> pidlOld(ILClone(pidl));
2877 if (!pidlOld)
2878 {
2879 SHELL_ErrorBox(lpdi->hdr.hwndFrom, E_OUTOFMEMORY);
2880 return FALSE;
2881 }
2882 PITEMID_CHILD pidlNew = NULL;
2883 HRESULT hr = m_pSFParent->SetNameOf(0, pidlOld, lpdi->item.pszText, SHGDN_INFOLDER, &pidlNew);
2884 if (SUCCEEDED(hr) && pidlNew)
2885 {
2886 int iNew = LV_FindItemByPidl(pidlNew);
2887 if (iNew != lpdi->item.iItem && iNew >= 0)
2888 ILFree(pidlNew);// A SHCNE has updated the item already
2889 else if (!LV_UpdateItem(lpdi->item.iItem, pidlNew))
2890 ILFree(pidlNew);
2891 OnStateChange(CDBOSC_RENAME);
2892 }
2893 else
2894 {
2895 ::PostMessageW(m_ListView, LVM_EDITLABEL, lpdi->item.iItem, 0); // Renaming failed, let the user try again
2896 }
2897 return FALSE;
2898 }
2899 default:
2900 TRACE("-- %p WM_COMMAND %x unhandled\n", this, lpnmh->code);
2901 break;
2902 }
2903
2904 return 0;
2905}
2906
2907// This is just a quick hack to make the desktop work correctly.
2908// ITranslateShellChangeNotify's IsChildID is undocumented, but most likely the
2909// way that a folder should know if it should update upon a change notification.
2910// It is exported by merged folders at a minimum.
2911static BOOL ILIsParentOrSpecialParent(PCIDLIST_ABSOLUTE pidl1, PCIDLIST_ABSOLUTE pidl2)
2912{
2913 if (!pidl1 || !pidl2)
2914 return FALSE;
2915 if (ILIsParent(pidl1, pidl2, TRUE))
2916 return TRUE;
2917
2918 CComHeapPtr<ITEMIDLIST_ABSOLUTE> pidl2Clone(ILClone(pidl2));
2919 ILRemoveLastID(pidl2Clone);
2920 return ILIsEqual(pidl1, pidl2Clone);
2921}
2922
2923LRESULT CDefView::OnChangeNotify(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
2924{
2925 // The change notify can come before WM_CREATE
2926 if (!m_ListView)
2927 return FALSE;
2928
2929 HANDLE hChange = (HANDLE)wParam;
2930 DWORD dwProcID = (DWORD)lParam;
2931 PIDLIST_ABSOLUTE *Pidls;
2932 LONG lEvent;
2933 HANDLE hLock = SHChangeNotification_Lock(hChange, dwProcID, &Pidls, &lEvent);
2934 if (hLock == NULL)
2935 {
2936 ERR("hLock == NULL\n");
2937 return FALSE;
2938 }
2939 lEvent &= ~SHCNE_INTERRUPT;
2940 TRACE("(%p)(%p,%p,%p) %#x\n", this, Pidls[0], Pidls[1], lParam, lEvent);
2941
2942 if (_DoFolderViewCB(SFVM_FSNOTIFY, (WPARAM)Pidls, lEvent) == S_FALSE)
2943 {
2944 SHChangeNotification_Unlock(hLock);
2945 return FALSE;
2946 }
2947
2948 // Translate child IDLs.
2949 // SHSimpleIDListFromPathW creates fake PIDLs (lacking some attributes)
2950 HRESULT hr;
2951 PITEMID_CHILD child0 = NULL, child1 = NULL;
2952 CComHeapPtr<ITEMIDLIST_RELATIVE> pidl0Temp, pidl1Temp;
2953 if (lEvent != SHCNE_UPDATEIMAGE && lEvent < SHCNE_EXTENDED_EVENT)
2954 {
2955 if (_ILIsSpecialFolder(Pidls[0]) || ILIsParentOrSpecialParent(m_pidlParent, Pidls[0]))
2956 {
2957 child0 = ILFindLastID(Pidls[0]);
2958 hr = SHGetRealIDL(m_pSFParent, child0, &pidl0Temp);
2959 if (SUCCEEDED(hr))
2960 child0 = pidl0Temp;
2961 }
2962 if (_ILIsSpecialFolder(Pidls[1]) || ILIsParentOrSpecialParent(m_pidlParent, Pidls[1]))
2963 {
2964 child1 = ILFindLastID(Pidls[1]);
2965 hr = SHGetRealIDL(m_pSFParent, child1, &pidl1Temp);
2966 if (SUCCEEDED(hr))
2967 child1 = pidl1Temp;
2968 }
2969 }
2970
2971 switch (lEvent)
2972 {
2973 case SHCNE_MKDIR:
2974 case SHCNE_CREATE:
2975 case SHCNE_DRIVEADD:
2976 if (!child0)
2977 break;
2978 if (LV_FindItemByPidl(child0) < 0)
2979 LV_AddItem(child0);
2980 else
2981 LV_UpdateItem(child0);
2982 break;
2983 case SHCNE_RMDIR:
2984 case SHCNE_DELETE:
2985 case SHCNE_DRIVEREMOVED:
2986 if (child0)
2987 LV_DeleteItem(child0);
2988 break;
2989 case SHCNE_RENAMEFOLDER:
2990 case SHCNE_RENAMEITEM:
2991 if (child0 && child1)
2992 LV_RenameItem(child0, child1);
2993 else if (child0)
2994 LV_DeleteItem(child0);
2995 else if (child1)
2996 LV_AddItem(child1);
2997 break;
2998 case SHCNE_UPDATEITEM:
2999 if (child0)
3000 LV_UpdateItem(child0);
3001 break;
3002 case SHCNE_UPDATEIMAGE:
3003 case SHCNE_MEDIAINSERTED:
3004 case SHCNE_MEDIAREMOVED:
3005 case SHCNE_ASSOCCHANGED:
3006 LV_RefreshIcons();
3007 break;
3008 case SHCNE_UPDATEDIR:
3009 case SHCNE_ATTRIBUTES:
3010 if (child0)
3011 LV_UpdateItem(child0);
3012 else
3013 Refresh();
3014 UpdateStatusbar();
3015 break;
3016 case SHCNE_FREESPACE:
3017 UpdateStatusbar();
3018 break;
3019 }
3020
3021 SHChangeNotification_Unlock(hLock);
3022 return TRUE;
3023}
3024
3025LRESULT CDefView::OnMenuMessage(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
3026{
3027 if (!m_pCM)
3028 {
3029 /* no menu */
3030 ERR("no context menu\n");
3031 return FALSE;
3032 }
3033 LRESULT result = 0;
3034 HRESULT hres = SHForwardContextMenuMsg(m_pCM, uMsg, wParam, lParam, &result, TRUE);
3035 return SUCCEEDED(hres);
3036}
3037
3038LRESULT CDefView::OnSettingChange(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
3039{
3040 /* Wallpaper setting affects drop shadows effect */
3041 if (wParam == SPI_SETDESKWALLPAPER || wParam == 0)
3042 UpdateListColors();
3043
3044 UINT ListExMask = LVS_EX_TRACKSELECT | LVS_EX_ONECLICKACTIVATE;
3045 UINT ListExBits = GetItemActivateFlags();
3046 if (wParam == SPI_GETICONTITLELOGFONT ||
3047 (lParam && !lstrcmpiW((PWSTR)lParam, REGSTR_PATH_EXPLORER L"\\IconUnderline")))
3048 {
3049 ListExMask |= LVS_EX_UNDERLINEHOT | LVS_EX_UNDERLINECOLD;
3050 ListExBits |= SHELL_GetIconUnderlineFlags();
3051 }
3052 m_ListView.SetExtendedListViewStyle(ListExBits, ListExMask);
3053 m_ListView.SendMessage(uMsg, wParam, lParam);
3054 return S_OK;
3055}
3056
3057LRESULT CDefView::OnInitMenuPopup(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
3058{
3059 HMENU hmenu = (HMENU) wParam;
3060 int nPos = LOWORD(lParam);
3061 UINT menuItemId;
3062
3063 if (m_isEditing)
3064 ListView_CancelEditLabel(m_ListView);
3065 if (m_pCM)
3066 OnMenuMessage(uMsg, wParam, lParam, bHandled);
3067
3068 HMENU hViewMenu = GetSubmenuByID(m_hMenu, FCIDM_MENU_VIEW);
3069
3070 if (GetSelections() == 0)
3071 {
3072 ::EnableMenuItem(hmenu, FCIDM_SHVIEW_CUT, MF_GRAYED);
3073 ::EnableMenuItem(hmenu, FCIDM_SHVIEW_COPY, MF_GRAYED);
3074 ::EnableMenuItem(hmenu, FCIDM_SHVIEW_RENAME, MF_GRAYED);
3075 ::EnableMenuItem(hmenu, FCIDM_SHVIEW_COPYTO, MF_GRAYED);
3076 ::EnableMenuItem(hmenu, FCIDM_SHVIEW_MOVETO, MF_GRAYED);
3077 ::EnableMenuItem(hmenu, FCIDM_SHVIEW_DELETE, MF_GRAYED);
3078 }
3079 else
3080 {
3081 // FIXME: Check copyable
3082 ::EnableMenuItem(hmenu, FCIDM_SHVIEW_CUT, MF_ENABLED);
3083 ::EnableMenuItem(hmenu, FCIDM_SHVIEW_COPY, MF_ENABLED);
3084 ::EnableMenuItem(hmenu, FCIDM_SHVIEW_RENAME, MF_ENABLED);
3085 ::EnableMenuItem(hmenu, FCIDM_SHVIEW_COPYTO, MF_ENABLED);
3086 ::EnableMenuItem(hmenu, FCIDM_SHVIEW_MOVETO, MF_ENABLED);
3087 ::EnableMenuItem(hmenu, FCIDM_SHVIEW_DELETE, MF_ENABLED);
3088 }
3089
3090 /* Lets try to find out what the hell wParam is */
3091 if (hmenu == GetSubMenu(m_hMenu, nPos))
3092 menuItemId = ReallyGetMenuItemID(m_hMenu, nPos);
3093 else if (hViewMenu && hmenu == GetSubMenu(hViewMenu, nPos))
3094 menuItemId = ReallyGetMenuItemID(hViewMenu, nPos);
3095 else if (m_hContextMenu && hmenu == GetSubMenu(m_hContextMenu, nPos))
3096 menuItemId = ReallyGetMenuItemID(m_hContextMenu, nPos);
3097 else
3098 return FALSE;
3099
3100 switch (menuItemId)
3101 {
3102 case FCIDM_MENU_FILE:
3103 FillFileMenu();
3104 break;
3105 case FCIDM_MENU_VIEW:
3106 case FCIDM_SHVIEW_VIEW:
3107 CheckViewMode(hmenu);
3108 break;
3109 case FCIDM_SHVIEW_ARRANGE:
3110 FillArrangeAsMenu(hmenu);
3111 break;
3112 }
3113
3114 return FALSE;
3115}
3116
3117LRESULT CDefView::OnChangeCBChain(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
3118{
3119 bHandled = TRUE;
3120 return m_ClipboardChain.HandleChangeCBChain(wParam, lParam);
3121}
3122
3123LRESULT CDefView::OnDrawClipboard(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
3124{
3125 bHandled = TRUE;
3126 const LRESULT res = m_ClipboardChain.HandleDrawClipboard(wParam, lParam);
3127 if (m_HasCutItems)
3128 {
3129 m_ClipboardChain.Unhook(m_hWnd);
3130 m_HasCutItems = false;
3131 m_ListView.SetItemState(-1, 0, LVIS_CUT); // Somebody copied or pasted, nothing can be "cut" anymore
3132 m_ListView.SendMessageW(LVM_SETCALLBACKMASK, m_ListView.SendMessageW(LVM_GETCALLBACKMASK, 0, 0) | LVIS_CUT, 0);
3133 }
3134 return res;
3135}
3136
3137void CDefView::RefreshGhostedState()
3138{
3139 for (UINT i = 0, c = m_ListView.GetItemCount(); i < c; ++i)
3140 m_ListView.SetItemState(i, GetItemAttributes(i, SFGAO_HIDDEN | SFGAO_GHOSTED) ? LVIS_CUT : 0, LVIS_CUT);
3141}
3142
3143// The INTERFACE of the IShellView object
3144
3145HRESULT WINAPI CDefView::GetWindow(HWND *phWnd)
3146{
3147 TRACE("(%p)\n", this);
3148
3149 *phWnd = m_hWnd;
3150
3151 return S_OK;
3152}
3153
3154HRESULT WINAPI CDefView::ContextSensitiveHelp(BOOL fEnterMode)
3155{
3156 FIXME("(%p) stub\n", this);
3157
3158 return E_NOTIMPL;
3159}
3160
3161// FIXME: use the accel functions
3162HRESULT WINAPI CDefView::TranslateAccelerator(LPMSG lpmsg)
3163{
3164 if (m_isEditing)
3165 return S_FALSE;
3166
3167 if (lpmsg->message >= WM_KEYFIRST && lpmsg->message <= WM_KEYLAST)
3168 {
3169 if (::TranslateAcceleratorW(m_hWnd, m_hAccel, lpmsg) != 0)
3170 return S_OK;
3171
3172 TRACE("-- key=0x%04lx\n", lpmsg->wParam);
3173 }
3174
3175 return m_pShellBrowser ? m_pShellBrowser->TranslateAcceleratorSB(lpmsg, 0) : S_FALSE;
3176}
3177
3178HRESULT WINAPI CDefView::EnableModeless(BOOL fEnable)
3179{
3180 FIXME("(%p)\n", this);
3181 return E_NOTIMPL;
3182}
3183
3184HRESULT WINAPI CDefView::UIActivate(UINT uState)
3185{
3186 TRACE("(%p)->(state=%x)\n", this, uState);
3187
3188 // don't do anything if the state isn't changing
3189 if (m_uState == uState)
3190 return S_OK;
3191
3192 // OnActivate handles the menu merging and internal state
3193 DoActivate(uState);
3194
3195 // only do this if we are active
3196 if (uState != SVUIA_DEACTIVATE)
3197 {
3198 _ForceStatusBarResize();
3199
3200 // Set the text for the status bar
3201 UpdateStatusbar();
3202 }
3203
3204 return S_OK;
3205}
3206
3207HRESULT WINAPI CDefView::Refresh()
3208{
3209 TRACE("(%p)\n", this);
3210
3211 _DoFolderViewCB(SFVM_LISTREFRESHED, TRUE, 0);
3212
3213 m_ListView.DeleteAllItems();
3214 FillList();
3215
3216 return S_OK;
3217}
3218
3219HRESULT WINAPI CDefView::CreateViewWindow(IShellView *lpPrevView, LPCFOLDERSETTINGS lpfs, IShellBrowser *psb, RECT *prcView, HWND *phWnd)
3220{
3221 return CreateViewWindow3(psb, lpPrevView, SV3CVW3_DEFAULT,
3222 (FOLDERFLAGS)lpfs->fFlags, (FOLDERFLAGS)lpfs->fFlags, (FOLDERVIEWMODE)lpfs->ViewMode, NULL, prcView, phWnd);
3223}
3224
3225HRESULT WINAPI CDefView::DestroyViewWindow()
3226{
3227 TRACE("(%p)\n", this);
3228
3229 /* Make absolutely sure all our UI is cleaned up */
3230 UIActivate(SVUIA_DEACTIVATE);
3231
3232 if (m_hAccel)
3233 {
3234 // MSDN: Accelerator tables loaded from resources are freed automatically when application terminates
3235 m_hAccel = NULL;
3236 }
3237
3238 if (m_hMenuArrangeModes)
3239 {
3240 DestroyMenu(m_hMenuArrangeModes);
3241 m_hMenuArrangeModes = NULL;
3242 }
3243
3244 if (m_hMenuViewModes)
3245 {
3246 DestroyMenu(m_hMenuViewModes);
3247 m_hMenuViewModes = NULL;
3248 }
3249
3250 if (m_hMenu)
3251 {
3252 DestroyMenu(m_hMenu);
3253 m_hMenu = NULL;
3254 }
3255
3256 if (m_ListView)
3257 {
3258 m_ListView.DestroyWindow();
3259 }
3260
3261 if (m_hWnd)
3262 {
3263 _DoFolderViewCB(SFVM_WINDOWCLOSING, (WPARAM)m_hWnd, 0);
3264 DestroyWindow();
3265 }
3266
3267 m_pShellBrowser.Release();
3268 m_pCommDlgBrowser.Release();
3269
3270 return S_OK;
3271}
3272
3273HRESULT WINAPI CDefView::GetCurrentInfo(LPFOLDERSETTINGS lpfs)
3274{
3275 TRACE("(%p)->(%p) vmode=%x flags=%x\n", this, lpfs,
3276 m_FolderSettings.ViewMode, m_FolderSettings.fFlags);
3277
3278 if (!lpfs)
3279 return E_INVALIDARG;
3280
3281 *lpfs = m_FolderSettings;
3282 return S_OK;
3283}
3284
3285HRESULT WINAPI CDefView::AddPropertySheetPages(DWORD dwReserved, LPFNADDPROPSHEETPAGE lpfn, LPARAM lparam)
3286{
3287 TRACE("(%p)->(0x%lX, %p, %p)\n", this, dwReserved, lpfn, lparam);
3288
3289 SFVM_PROPPAGE_DATA Data = { dwReserved, lpfn, lparam };
3290 _DoFolderViewCB(SFVM_ADDPROPERTYPAGES, 0, (LPARAM)&Data);
3291 return S_OK;
3292}
3293
3294static HRESULT Read(IStream *pS, LPVOID buffer, ULONG cb)
3295{
3296 ULONG read;
3297 HRESULT hr = pS->Read(buffer, cb, &read);
3298 return FAILED(hr) ? hr : (cb == read ? S_OK : HResultFromWin32(ERROR_MORE_DATA));
3299}
3300
3301static DWORD ReadDWORD(IPropertyBag *pPB, LPCWSTR name, DWORD def)
3302{
3303 DWORD value;
3304 HRESULT hr = SHPropertyBag_ReadDWORD(pPB, name, &value);
3305 return SUCCEEDED(hr) ? value : def;
3306}
3307
3308HRESULT CDefView::GetDefaultViewStream(DWORD Stgm, IStream **ppStream)
3309{
3310 CLSID clsid;
3311 HRESULT hr = IUnknown_GetClassID(m_pSFParent, &clsid);
3312 if (SUCCEEDED(hr))
3313 {
3314 WCHAR path[MAX_PATH], name[39];
3315 wsprintfW(path, L"%s\\%s", REGSTR_PATH_EXPLORER, L"Streams\\Default");
3316 StringFromGUID2(clsid, name, 39);
3317 *ppStream = SHOpenRegStream2W(HKEY_CURRENT_USER, path, name, Stgm);
3318 hr = *ppStream ? S_OK : E_FAIL;
3319 }
3320 return hr;
3321}
3322
3323static HRESULT LoadColumnsStream(PERSISTCOLUMNS &cols, IStream *pS)
3324{
3325 HRESULT hr = Read(pS, &cols, FIELD_OFFSET(PERSISTCOLUMNS, Columns));
3326 if (FAILED(hr))
3327 return hr;
3328 if (cols.Signature != PERSISTCOLUMNS::SIG || cols.Count > cols.MAXCOUNT)
3329 return HResultFromWin32(ERROR_INVALID_DATA);
3330 return Read(pS, &cols.Columns, sizeof(*cols.Columns) * cols.Count);
3331}
3332
3333HRESULT CDefView::LoadViewState()
3334{
3335 PERSISTCLASSICVIEWSTATE cvs;
3336 PERSISTCOLUMNS cols;
3337 CComPtr<IStream> pS;
3338 CComPtr<IPropertyBag> pPB;
3339 bool fallback = false;
3340 HRESULT hrColumns = E_FAIL;
3341 HRESULT hr = IUnknown_QueryServicePropertyBag(m_pShellBrowser, SHGVSPB_FOLDER, IID_PPV_ARG(IPropertyBag, &pPB));
3342 if (SUCCEEDED(hr))
3343 {
3344 DWORD data;
3345 if (FAILED(hr = SHPropertyBag_ReadDWORD(pPB, L"Mode", &data)))
3346 goto loadfallback;
3347 cvs.FolderSettings.ViewMode = data;
3348 cvs.FolderSettings.fFlags = ReadDWORD(pPB, L"FFlags", FWF_NOGROUPING);
3349 data = ReadDWORD(pPB, L"Sort", ~0ul);
3350 cvs.SortColId = data != ~0ul ? (WORD)data : LISTVIEW_SORT_INFO::UNSPECIFIEDCOLUMN;
3351 cvs.SortDir = (INT8)ReadDWORD(pPB, L"SortDir", 1);
3352 if (SUCCEEDED(hrColumns = SHPropertyBag_ReadStream(pPB, L"ColInfo", &pS)))
3353 hrColumns = LoadColumnsStream(cols, pS);
3354 }
3355 else
3356 {
3357 if (FAILED(hr = (m_pShellBrowser ? m_pShellBrowser->GetViewStateStream(STGM_READ, &pS) : E_UNEXPECTED)))
3358 {
3359 loadfallback:
3360 hr = GetDefaultViewStream(STGM_READ, &pS);
3361 fallback = true;
3362 }
3363 if (FAILED(hr) || FAILED(hr = Read(pS, &cvs, sizeof(cvs))))
3364 return hr;
3365 if (cvs.Signature != cvs.SIG)
3366 return HResultFromWin32(ERROR_INVALID_DATA);
3367 hrColumns = LoadColumnsStream(cols, pS);
3368 }
3369 m_FolderSettings.ViewMode = cvs.FolderSettings.ViewMode;
3370 m_FolderSettings.fFlags &= ~cvs.VALIDFWF;
3371 m_FolderSettings.fFlags |= cvs.FolderSettings.fFlags & cvs.VALIDFWF;
3372 if (SUCCEEDED(hrColumns))
3373 {
3374 BOOL failed = FALSE;
3375 if ((m_LoadColumnsList = DPA_Create(cols.Count)) != NULL)
3376 {
3377 for (UINT i = 0; i < cols.Count; ++i)
3378 {
3379 failed |= !DPA_SetPtr(m_LoadColumnsList, i, UlongToPtr(cols.Columns[i]));
3380 }
3381 }
3382 if (failed || !cols.Count)
3383 {
3384 DPA_Destroy(m_LoadColumnsList);
3385 m_LoadColumnsList = NULL;
3386 }
3387 }
3388 m_sortInfo.bLoadedFromViewState = !fallback && m_LoadColumnsList && (int)cvs.SortColId != LISTVIEW_SORT_INFO::UNSPECIFIEDCOLUMN;
3389 m_sortInfo.bColumnIsFolderColumn = TRUE;
3390 m_sortInfo.Direction = cvs.SortDir > 0 ? 1 : -1;
3391 m_sortInfo.ListColumn = cvs.SortColId;
3392 return hr;
3393}
3394
3395HRESULT CDefView::SaveViewState(IStream *pStream)
3396{
3397 if (!m_ListView.m_hWnd)
3398 return E_UNEXPECTED;
3399 int sortcol = MapListColumnToFolderColumn(m_sortInfo.ListColumn);
3400 PERSISTCLASSICVIEWSTATE cvs;
3401 cvs.SortColId = sortcol >= 0 ? (WORD)sortcol : 0;
3402 cvs.SortDir = m_sortInfo.Direction;
3403 PERSISTCOLUMNS cols;
3404 cols.Signature = PERSISTCOLUMNS::SIG;
3405 cols.Count = 0;
3406 LVCOLUMN lvc;
3407 lvc.mask = LVCF_WIDTH;
3408 for (UINT i = 0, j = 0; i < PERSISTCOLUMNS::MAXCOUNT && ListView_GetColumn(m_ListView, j, &lvc); ++j)
3409 {
3410 HRESULT hr = MapListColumnToFolderColumn(j);
3411 if (SUCCEEDED(hr))
3412 {
3413 cols.Columns[i] = MAKELONG(hr, lvc.cx);
3414 cols.Count = ++i;
3415 }
3416 }
3417 UINT cbColumns = FIELD_OFFSET(PERSISTCOLUMNS, Columns) + (sizeof(*cols.Columns) * cols.Count);
3418 UpdateFolderViewFlags();
3419
3420 IPropertyBag *pPB;
3421 HRESULT hr = S_OK;
3422 if (pStream)
3423 {
3424 pStream->AddRef();
3425 goto stream;
3426 }
3427 hr = IUnknown_QueryServicePropertyBag(m_pShellBrowser, SHGVSPB_FOLDER, IID_PPV_ARG(IPropertyBag, &pPB));
3428 if (SUCCEEDED(hr))
3429 {
3430 UINT uViewMode;
3431 GetCurrentViewMode(&uViewMode);
3432 hr = SHPropertyBag_WriteDWORD(pPB, L"Mode", uViewMode);
3433 SHPropertyBag_WriteDWORD(pPB, L"FFlags", m_FolderSettings.fFlags);
3434 SHPropertyBag_WriteDWORD(pPB, L"Sort", cvs.SortColId);
3435 SHPropertyBag_WriteDWORD(pPB, L"SortDir", cvs.SortDir);
3436 pStream = cols.Count ? SHCreateMemStream((LPBYTE)&cols, cbColumns) : NULL;
3437 if (!pStream || FAILED(SHPropertyBag_WriteStream(pPB, L"ColInfo", pStream)))
3438 SHPropertyBag_Delete(pPB, L"ColInfo");
3439#if 0 // TODO
3440 WCHAR name[MAX_PATH];
3441 memcpy(name, L"ItemPos", sizeof(L"ItemPos"));
3442 if (SHGetPerScreenResName(name + 7, _countof(name) - 7, 0))
3443 {
3444 if (GetAutoArrange() == S_FALSE)
3445 // TODO: Save listview item positions
3446 else
3447 SHPropertyBag_Delete(pPB, name);
3448 }
3449#endif
3450 pPB->Release();
3451 }
3452 else if (SUCCEEDED(hr = (m_pShellBrowser ? m_pShellBrowser->GetViewStateStream(STGM_WRITE, &pStream) : E_UNEXPECTED)))
3453 {
3454 stream:
3455 ULONG written;
3456 cvs.Signature = cvs.SIG;
3457 cvs.FolderSettings = m_FolderSettings;
3458 hr = pStream->Write(&cvs, sizeof(cvs), &written);
3459 if (SUCCEEDED(hr))
3460 hr = pStream->Write(&cols, cbColumns, &written);
3461 }
3462 // TODO: else if (SUCCEEDED(_DoFolderViewCB(SFVM_GETCOLUMNSTREAM)))
3463 if (pStream)
3464 pStream->Release();
3465 return hr;
3466}
3467
3468HRESULT WINAPI CDefView::SaveViewState()
3469{
3470 if (!(m_FolderSettings.fFlags & FWF_NOBROWSERVIEWSTATE))
3471 return SaveViewState(NULL);
3472 return S_FALSE;
3473}
3474
3475#define UPDATEFOLDERVIEWFLAGS(bits, bit, set) ( (bits) = ((bits) & ~(bit)) | ((set) ? (bit) : 0) )
3476void CDefView::UpdateFolderViewFlags()
3477{
3478 UPDATEFOLDERVIEWFLAGS(m_FolderSettings.fFlags, FWF_AUTOARRANGE, GetAutoArrange() == S_OK);
3479 UPDATEFOLDERVIEWFLAGS(m_FolderSettings.fFlags, FWF_SNAPTOGRID, _GetSnapToGrid() == S_OK);
3480 UPDATEFOLDERVIEWFLAGS(m_FolderSettings.fFlags, FWF_NOGROUPING, !ListView_IsGroupViewEnabled(m_ListView.m_hWnd));
3481}
3482
3483UINT CDefView::GetItemActivateFlags()
3484{
3485 SHELLSTATE ss;
3486 SHGetSetSettings(&ss, SSF_DOUBLECLICKINWEBVIEW | SSF_WIN95CLASSIC, FALSE);
3487 return ((m_FolderSettings.fFlags & FWF_SINGLECLICKACTIVATE) || (!ss.fDoubleClickInWebView && !ss.fWin95Classic))
3488 ? (LVS_EX_TRACKSELECT | LVS_EX_ONECLICKACTIVATE) : 0;
3489}
3490
3491HRESULT WINAPI CDefView::SelectItem(PCUITEMID_CHILD pidl, UINT uFlags)
3492{
3493 int i;
3494
3495 TRACE("(%p)->(pidl=%p, 0x%08x) stub\n", this, pidl, uFlags);
3496
3497 if (!m_ListView)
3498 {
3499 ERR("!m_ListView\n");
3500 return E_FAIL;
3501 }
3502
3503 i = LV_FindItemByPidl(pidl);
3504 if (i == -1)
3505 return S_OK;
3506
3507 LVITEMW lvItem = {0};
3508 lvItem.mask = LVIF_STATE;
3509 lvItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
3510
3511 while (m_ListView.GetItem(&lvItem))
3512 {
3513 if (lvItem.iItem == i)
3514 {
3515 if (uFlags & SVSI_SELECT)
3516 lvItem.state |= LVIS_SELECTED;
3517 else
3518 lvItem.state &= ~LVIS_SELECTED;
3519
3520 if (uFlags & SVSI_FOCUSED)
3521 lvItem.state |= LVIS_FOCUSED;
3522 else
3523 lvItem.state &= ~LVIS_FOCUSED;
3524 }
3525 else
3526 {
3527 if (uFlags & SVSI_DESELECTOTHERS)
3528 {
3529 lvItem.state &= ~LVIS_SELECTED;
3530 }
3531 lvItem.state &= ~LVIS_FOCUSED;
3532 }
3533
3534 m_ListView.SetItem(&lvItem);
3535 lvItem.iItem++;
3536 }
3537
3538 if (uFlags & SVSI_ENSUREVISIBLE)
3539 m_ListView.EnsureVisible(i, FALSE);
3540
3541 if((uFlags & SVSI_EDIT) == SVSI_EDIT)
3542 m_ListView.EditLabel(i);
3543
3544 return S_OK;
3545}
3546
3547HRESULT WINAPI CDefView::GetItemObject(UINT uItem, REFIID riid, LPVOID *ppvOut)
3548{
3549 HRESULT hr = E_NOINTERFACE;
3550
3551 TRACE("(%p)->(uItem=0x%08x,\n\tIID=%s, ppv=%p)\n", this, uItem, debugstr_guid(&riid), ppvOut);
3552
3553 if (!ppvOut)
3554 return E_INVALIDARG;
3555
3556 *ppvOut = NULL;
3557
3558 switch (uItem)
3559 {
3560 case SVGIO_BACKGROUND:
3561 if (IsEqualIID(riid, IID_IContextMenu))
3562 {
3563 hr = CDefViewBckgrndMenu_CreateInstance(m_pSF2Parent, riid, ppvOut);
3564 if (FAILED_UNEXPECTEDLY(hr))
3565 return hr;
3566
3567 IUnknown_SetSite(*((IUnknown**)ppvOut), (IShellView *)this);
3568 }
3569 else if (IsEqualIID(riid, IID_IDispatch))
3570 {
3571 if (m_pShellFolderViewDual == NULL)
3572 {
3573 hr = CDefViewDual_Constructor(riid, (LPVOID*)&m_pShellFolderViewDual);
3574 if (FAILED_UNEXPECTEDLY(hr))
3575 return hr;
3576 }
3577 hr = m_pShellFolderViewDual->QueryInterface(riid, ppvOut);
3578 }
3579 break;
3580 case SVGIO_SELECTION:
3581 GetSelections();
3582 hr = m_pSFParent->GetUIObjectOf(m_hWnd, m_cidl, m_apidl, riid, 0, ppvOut);
3583 if (FAILED_UNEXPECTEDLY(hr))
3584 return hr;
3585
3586 if (IsEqualIID(riid, IID_IContextMenu))
3587 IUnknown_SetSite(*((IUnknown**)ppvOut), (IShellView *)this);
3588
3589 break;
3590 }
3591
3592 TRACE("-- (%p)->(interface=%p)\n", this, *ppvOut);
3593
3594 return hr;
3595}
3596
3597FOLDERVIEWMODE CDefView::GetDefaultViewMode()
3598{
3599 FOLDERVIEWMODE mode = ((m_FolderSettings.fFlags & FWF_DESKTOP) || !IsOS(OS_SERVERADMINUI)) ? FVM_ICON : FVM_DETAILS;
3600 FOLDERVIEWMODE temp = mode;
3601 if (SUCCEEDED(_DoFolderViewCB(SFVM_DEFVIEWMODE, 0, (LPARAM)&temp)) && IsSupportedFolderViewMode(temp))
3602 mode = temp;
3603 return mode;
3604}
3605
3606HRESULT STDMETHODCALLTYPE CDefView::GetCurrentViewMode(UINT *pViewMode)
3607{
3608 TRACE("(%p)->(%p), stub\n", this, pViewMode);
3609
3610 if (!pViewMode)
3611 return E_INVALIDARG;
3612
3613 *pViewMode = m_FolderSettings.ViewMode;
3614 return S_OK;
3615}
3616
3617HRESULT STDMETHODCALLTYPE CDefView::SetCurrentViewMode(UINT ViewMode)
3618{
3619 DWORD dwStyle;
3620 TRACE("(%p)->(%u), stub\n", this, ViewMode);
3621
3622 /* It's not redundant to check FVM_AUTO because it's a (UINT)-1 */
3623 if (((INT)ViewMode < FVM_FIRST || (INT)ViewMode > FVM_LAST) && ((INT)ViewMode != FVM_AUTO))
3624 return E_INVALIDARG;
3625
3626 /* Windows before Vista uses LVM_SETVIEW and possibly
3627 LVM_SETEXTENDEDLISTVIEWSTYLE to set the style of the listview,
3628 while later versions seem to accomplish this through other
3629 means. */
3630 switch (ViewMode)
3631 {
3632 case FVM_ICON: dwStyle = LVS_ICON; break;
3633 case FVM_SMALLICON: dwStyle = LVS_SMALLICON; break;
3634 case FVM_LIST: dwStyle = LVS_LIST; break;
3635 case FVM_DETAILS: dwStyle = LVS_REPORT; break;
3636 default:
3637 FIXME("ViewMode %d not implemented\n", ViewMode);
3638 dwStyle = LVS_LIST;
3639 break;
3640 }
3641
3642 m_ListView.ModifyStyle(LVS_TYPEMASK, dwStyle);
3643
3644 /* This will not necessarily be the actual mode set above.
3645 This mimics the behavior of Windows XP. */
3646 m_FolderSettings.ViewMode = ViewMode;
3647 CheckToolbar();
3648 return S_OK;
3649}
3650
3651HRESULT STDMETHODCALLTYPE CDefView::GetFolder(REFIID riid, void **ppv)
3652{
3653 if (m_pSFParent == NULL)
3654 return E_FAIL;
3655
3656 return m_pSFParent->QueryInterface(riid, ppv);
3657}
3658
3659HRESULT STDMETHODCALLTYPE CDefView::Item(int iItemIndex, PITEMID_CHILD *ppidl)
3660{
3661 PCUITEMID_CHILD pidl = _PidlByItem(iItemIndex);
3662 if (pidl)
3663 {
3664 *ppidl = ILClone(pidl);
3665 return S_OK;
3666 }
3667
3668 *ppidl = 0;
3669 return E_INVALIDARG;
3670}
3671
3672HRESULT STDMETHODCALLTYPE CDefView::ItemCount(UINT uFlags, int *pcItems)
3673{
3674 TRACE("(%p)->(%u %p)\n", this, uFlags, pcItems);
3675 if (uFlags != SVGIO_ALLVIEW && uFlags != SVGIO_SELECTION)
3676 FIXME("some flags unsupported, %x\n", uFlags & ~(SVGIO_ALLVIEW | SVGIO_SELECTION));
3677 if ((uFlags & SVGIO_TYPE_MASK) == SVGIO_SELECTION)
3678 *pcItems = m_ListView.GetSelectedCount();
3679 else
3680 *pcItems = m_ListView.GetItemCount();
3681 return S_OK;
3682}
3683
3684HRESULT STDMETHODCALLTYPE CDefView::Items(UINT uFlags, REFIID riid, void **ppv)
3685{
3686 return E_NOTIMPL;
3687}
3688
3689HRESULT STDMETHODCALLTYPE CDefView::GetSelectionMarkedItem(int *piItem)
3690{
3691 TRACE("(%p)->(%p)\n", this, piItem);
3692
3693 *piItem = m_ListView.GetSelectionMark();
3694
3695 return S_OK;
3696}
3697
3698HRESULT STDMETHODCALLTYPE CDefView::GetFocusedItem(int *piItem)
3699{
3700 TRACE("(%p)->(%p)\n", this, piItem);
3701
3702 *piItem = m_ListView.GetNextItem(-1, LVNI_FOCUSED);
3703
3704 return S_OK;
3705}
3706
3707HRESULT STDMETHODCALLTYPE CDefView::GetItemPosition(PCUITEMID_CHILD pidl, POINT *ppt)
3708{
3709 if (!m_ListView)
3710 {
3711 ERR("!m_ListView\n");
3712 return E_FAIL;
3713 }
3714
3715 int lvIndex = LV_FindItemByPidl(pidl);
3716 if (lvIndex == -1 || ppt == NULL)
3717 return E_INVALIDARG;
3718
3719 m_ListView.GetItemPosition(lvIndex, ppt);
3720 return S_OK;
3721}
3722
3723HRESULT STDMETHODCALLTYPE CDefView::GetSpacing(POINT *ppt)
3724{
3725 TRACE("(%p)->(%p)\n", this, ppt);
3726
3727 if (!m_ListView)
3728 {
3729 ERR("!m_ListView\n");
3730 return S_FALSE;
3731 }
3732
3733 if (ppt)
3734 {
3735 SIZE spacing;
3736 m_ListView.GetItemSpacing(spacing);
3737
3738 ppt->x = spacing.cx;
3739 ppt->y = spacing.cy;
3740 }
3741
3742 return S_OK;
3743}
3744
3745HRESULT STDMETHODCALLTYPE CDefView::GetDefaultSpacing(POINT *ppt)
3746{
3747 return E_NOTIMPL;
3748}
3749
3750HRESULT STDMETHODCALLTYPE CDefView::GetAutoArrange()
3751{
3752 return ((m_ListView.GetStyle() & LVS_AUTOARRANGE) ? S_OK : S_FALSE);
3753}
3754
3755HRESULT CDefView::_GetSnapToGrid()
3756{
3757 DWORD dwExStyle = (DWORD)m_ListView.SendMessage(LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0);
3758 return ((dwExStyle & LVS_EX_SNAPTOGRID) ? S_OK : S_FALSE);
3759}
3760
3761HRESULT STDMETHODCALLTYPE CDefView::SelectItem(int iItem, DWORD dwFlags)
3762{
3763 LVITEMW lvItem;
3764
3765 TRACE("(%p)->(%d, %x)\n", this, iItem, dwFlags);
3766
3767 lvItem.state = 0;
3768 lvItem.stateMask = LVIS_SELECTED;
3769
3770 if (dwFlags & SVSI_ENSUREVISIBLE)
3771 m_ListView.EnsureVisible(iItem, 0);
3772
3773 /* all items */
3774 if (dwFlags & SVSI_DESELECTOTHERS)
3775 m_ListView.SetItemState(-1, 0, LVIS_SELECTED);
3776
3777 /* this item */
3778 if (dwFlags & SVSI_SELECT)
3779 lvItem.state |= LVIS_SELECTED;
3780
3781 if (dwFlags & SVSI_FOCUSED)
3782 lvItem.stateMask |= LVIS_FOCUSED;
3783
3784 m_ListView.SetItemState(iItem, lvItem.state, lvItem.stateMask);
3785
3786 if ((dwFlags & SVSI_EDIT) == SVSI_EDIT)
3787 m_ListView.EditLabel(iItem);
3788
3789 return S_OK;
3790}
3791
3792HRESULT STDMETHODCALLTYPE CDefView::SelectAndPositionItems(UINT cidl, PCUITEMID_CHILD_ARRAY apidl, POINT *apt, DWORD dwFlags)
3793{
3794 ASSERT(m_ListView);
3795 m_ListView.SetItemState(-1, 0, LVIS_SELECTED); // Reset the selection
3796 for (UINT i = 0 ; i < cidl; i++)
3797 {
3798 int lvIndex = LV_FindItemByPidl(apidl[i]);
3799 if (lvIndex != -1)
3800 SelectAndPositionItem(lvIndex, dwFlags, apt ? &apt[i] : NULL);
3801 }
3802 return S_OK;
3803}
3804
3805
3806// IShellView2 implementation
3807
3808HRESULT STDMETHODCALLTYPE CDefView::GetView(SHELLVIEWID *pVid, ULONG view_type)
3809{
3810 if (view_type == SV2GV_DEFAULTVIEW)
3811 {
3812 *pVid = VID_Default;
3813 return S_OK;
3814 }
3815 if (view_type == SV2GV_CURRENTVIEW)
3816 view_type = m_FolderSettings.ViewMode;
3817 if ((int)view_type < 0)
3818 return E_UNEXPECTED;
3819
3820 view_type += FVM_FIRST;
3821 if (!IsSupportedFolderViewMode(view_type))
3822 return E_INVALIDARG;
3823 *pVid = *FolderViewModeToShellViewId(view_type);
3824 return S_OK;
3825}
3826
3827HRESULT STDMETHODCALLTYPE CDefView::CreateViewWindow2(LPSV2CVW2_PARAMS view_params)
3828{
3829 return CreateViewWindow3(view_params->psbOwner, view_params->psvPrev,
3830 SV3CVW3_DEFAULT, (FOLDERFLAGS)view_params->pfs->fFlags, (FOLDERFLAGS)view_params->pfs->fFlags,
3831 (FOLDERVIEWMODE)view_params->pfs->ViewMode, view_params->pvid, view_params->prcView, &view_params->hwndView);
3832}
3833
3834HRESULT 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)
3835{
3836 OLEMENUGROUPWIDTHS omw = { { 0, 0, 0, 0, 0, 0 } };
3837 const UINT SUPPORTED_SV3CVW3 = SV3CVW3_FORCEVIEWMODE | SV3CVW3_FORCEFOLDERFLAGS;
3838 const UINT IGNORE_FWF = FWF_OWNERDATA; // FIXME: Support this
3839
3840 *hwnd = NULL;
3841
3842 TRACE("(%p)->(shlview=%p shlbrs=%p rec=%p hwnd=%p vmode=%x flags=%x)\n", this, psvPrevious, psb, prcView, hwnd, mode, flags);
3843 if (prcView != NULL)
3844 TRACE("-- left=%i top=%i right=%i bottom=%i\n", prcView->left, prcView->top, prcView->right, prcView->bottom);
3845
3846 /* Validate the Shell Browser */
3847 if (psb == NULL || m_hWnd)
3848 return E_UNEXPECTED;
3849
3850 if (view_flags & ~SUPPORTED_SV3CVW3)
3851 FIXME("unsupported view flags 0x%08x\n", view_flags & ~SUPPORTED_SV3CVW3);
3852
3853 /* Set up the member variables */
3854 m_pShellBrowser = psb;
3855 m_FolderSettings.ViewMode = mode;
3856 m_FolderSettings.fFlags = (mask & flags) & ~IGNORE_FWF;
3857
3858 if (view_id)
3859 {
3860 FOLDERVIEWMODE temp = (FOLDERVIEWMODE)ShellViewIdToFolderViewMode(view_id);
3861 if (IsSupportedFolderViewMode(temp))
3862 mode = temp;
3863 else if (*view_id == VID_Default)
3864 mode = FVM_AUTO;
3865 else
3866 FIXME("Ignoring unrecognized VID %s\n", debugstr_guid(view_id));
3867 }
3868 if (mode == FVM_AUTO)
3869 m_FolderSettings.ViewMode = GetDefaultViewMode();
3870
3871 if (!IsSupportedFolderViewMode(m_FolderSettings.ViewMode))
3872 {
3873 ERR("Ignoring %s FVM %u\n", FolderViewModeToShellViewId(m_FolderSettings.ViewMode)
3874 ? "unsupported" : "invalid", m_FolderSettings.ViewMode);
3875 m_FolderSettings.ViewMode = FVM_ICON;
3876 }
3877 const UINT requestedViewMode = m_FolderSettings.ViewMode;
3878
3879 /* Get our parent window */
3880 m_pShellBrowser->GetWindow(&m_hWndParent);
3881 _DoFolderViewCB(SFVM_HWNDMAIN, 0, (LPARAM)m_hWndParent);
3882
3883 /* Try to get the ICommDlgBrowserInterface, adds a reference !!! */
3884 m_pCommDlgBrowser = NULL;
3885 if (SUCCEEDED(m_pShellBrowser->QueryInterface(IID_PPV_ARG(ICommDlgBrowser, &m_pCommDlgBrowser))))
3886 {
3887 TRACE("-- CommDlgBrowser\n");
3888 }
3889
3890 LoadViewState();
3891 if (view_flags & SV3CVW3_FORCEVIEWMODE)
3892 m_FolderSettings.ViewMode = requestedViewMode;
3893 if (view_flags & SV3CVW3_FORCEFOLDERFLAGS)
3894 m_FolderSettings.fFlags = (mask & flags) & ~IGNORE_FWF;
3895
3896 RECT rcView = *prcView;
3897 Create(m_hWndParent, rcView, NULL, WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_TABSTOP, 0, 0U);
3898 if (m_hWnd == NULL)
3899 return E_FAIL;
3900
3901 *hwnd = m_hWnd;
3902
3903 CheckToolbar();
3904
3905 if (!*hwnd)
3906 return E_FAIL;
3907
3908 _DoFolderViewCB(SFVM_WINDOWCREATED, (WPARAM)m_hWnd, 0);
3909
3910 SetWindowPos(HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
3911 UpdateWindow();
3912
3913 if (!m_hMenu)
3914 {
3915 m_hMenu = CreateMenu();
3916 m_pShellBrowser->InsertMenusSB(m_hMenu, &omw);
3917 TRACE("-- after fnInsertMenusSB\n");
3918 }
3919
3920 _MergeToolbar();
3921
3922 return S_OK;
3923}
3924
3925HRESULT STDMETHODCALLTYPE CDefView::HandleRename(LPCITEMIDLIST pidl)
3926{
3927 if (!pidl)
3928 {
3929 int idx = m_ListView.GetNextItem(-1, LVNI_SELECTED | LVNI_FOCUSED);
3930 if (idx < 0)
3931 idx = m_ListView.GetNextItem(-1, LVNI_SELECTED);
3932 pidl = _PidlByItem(idx);
3933 }
3934 if (ILFindLastID(pidl) != pidl)
3935 return E_INVALIDARG;
3936 return SelectItem(pidl, SVSI_EDIT | SVSI_ENSUREVISIBLE | SVSI_FOCUSED | SVSI_SELECT);
3937}
3938
3939HRESULT CDefView::SelectAndPositionItem(int Idx, UINT fSVSI, POINT *ppt)
3940{
3941 if (Idx == -1)
3942 return fSVSI == SVSI_DESELECTOTHERS ? SelectItem(-2, fSVSI) : E_INVALIDARG;
3943 if (ppt)
3944 m_ListView.SetItemPosition(Idx, ppt);
3945 return SelectItem(Idx, fSVSI); // After SetItemPosition for SVSI_ENSUREVISIBLE
3946}
3947
3948HRESULT STDMETHODCALLTYPE CDefView::SelectAndPositionItem(LPCITEMIDLIST item, UINT flags, POINT *point)
3949{
3950 if (!item)
3951 return SelectAndPositionItem(-1, flags, point);
3952 int idx = LV_FindItemByPidl(item);
3953 return idx != -1 ? SelectAndPositionItem(idx, flags, point) : S_FALSE;
3954}
3955
3956// IShellFolderView implementation
3957
3958HRESULT STDMETHODCALLTYPE CDefView::Rearrange(LPARAM sort)
3959{
3960 _Sort(LOWORD(sort & SHCIDS_COLUMNMASK));
3961 return S_OK;
3962}
3963
3964HRESULT STDMETHODCALLTYPE CDefView::GetArrangeParam(LPARAM *sort)
3965{
3966 int col = m_sortInfo.ListColumn;
3967 *sort = col != LISTVIEW_SORT_INFO::UNSPECIFIEDCOLUMN ? col : 0;
3968 return col != LISTVIEW_SORT_INFO::UNSPECIFIEDCOLUMN ? S_OK : S_FALSE;
3969}
3970
3971HRESULT STDMETHODCALLTYPE CDefView::ArrangeGrid()
3972{
3973 m_ListView.SetExtendedListViewStyle(LVS_EX_SNAPTOGRID, LVS_EX_SNAPTOGRID);
3974 return S_OK;
3975}
3976
3977HRESULT STDMETHODCALLTYPE CDefView::AutoArrange()
3978{
3979 m_ListView.ModifyStyle(0, LVS_AUTOARRANGE);
3980 m_ListView.Arrange(LVA_DEFAULT);
3981 return S_OK;
3982}
3983
3984HRESULT STDMETHODCALLTYPE CDefView::AddObject(PITEMID_CHILD pidl, UINT *item)
3985{
3986 TRACE("(%p)->(%p %p)\n", this, pidl, item);
3987 if (!m_ListView)
3988 {
3989 ERR("!m_ListView\n");
3990 return E_FAIL;
3991 }
3992 *item = LV_AddItem(pidl);
3993 return (int)*item >= 0 ? S_OK : E_OUTOFMEMORY;
3994}
3995
3996HRESULT STDMETHODCALLTYPE CDefView::GetObject(PITEMID_CHILD *pidl, UINT item)
3997{
3998 TRACE("(%p)->(%p %d)\n", this, pidl, item);
3999 return Item(item, pidl);
4000}
4001
4002HRESULT STDMETHODCALLTYPE CDefView::RemoveObject(PITEMID_CHILD pidl, UINT *item)
4003{
4004 TRACE("(%p)->(%p %p)\n", this, pidl, item);
4005
4006 if (!m_ListView)
4007 {
4008 ERR("!m_ListView\n");
4009 return E_FAIL;
4010 }
4011
4012 if (pidl)
4013 {
4014 *item = LV_FindItemByPidl(ILFindLastID(pidl));
4015 m_ListView.DeleteItem(*item);
4016 }
4017 else
4018 {
4019 *item = 0;
4020 m_ListView.DeleteAllItems();
4021 }
4022
4023 return S_OK;
4024}
4025
4026HRESULT STDMETHODCALLTYPE CDefView::GetObjectCount(UINT *count)
4027{
4028 TRACE("(%p)->(%p)\n", this, count);
4029 *count = m_ListView.GetItemCount();
4030 return S_OK;
4031}
4032
4033HRESULT STDMETHODCALLTYPE CDefView::SetObjectCount(UINT count, UINT flags)
4034{
4035 FIXME("(%p)->(%d %x) stub\n", this, count, flags);
4036 return E_NOTIMPL;
4037}
4038
4039HRESULT STDMETHODCALLTYPE CDefView::UpdateObject(PITEMID_CHILD pidl_old, PITEMID_CHILD pidl_new, UINT *item)
4040{
4041 FIXME("(%p)->(%p %p %p) stub\n", this, pidl_old, pidl_new, item);
4042 return E_NOTIMPL;
4043}
4044
4045HRESULT STDMETHODCALLTYPE CDefView::RefreshObject(PITEMID_CHILD pidl, UINT *item)
4046{
4047 FIXME("(%p)->(%p %p) stub\n", this, pidl, item);
4048 return E_NOTIMPL;
4049}
4050
4051HRESULT STDMETHODCALLTYPE CDefView::SetRedraw(BOOL redraw)
4052{
4053 TRACE("(%p)->(%d)\n", this, redraw);
4054 if (m_ListView)
4055 m_ListView.SetRedraw(redraw);
4056 return S_OK;
4057}
4058
4059HRESULT STDMETHODCALLTYPE CDefView::GetSelectedCount(UINT *count)
4060{
4061 *count = m_ListView.GetSelectedCount();
4062 return S_OK;
4063}
4064
4065HRESULT STDMETHODCALLTYPE CDefView::GetSelectedObjects(PCUITEMID_CHILD **pidl, UINT *items)
4066{
4067 TRACE("(%p)->(%p %p)\n", this, pidl, items);
4068
4069 *items = GetSelections();
4070
4071 if (*items)
4072 {
4073 *pidl = static_cast<PCUITEMID_CHILD *>(LocalAlloc(0, *items * sizeof(PCUITEMID_CHILD)));
4074 if (!*pidl)
4075 {
4076 return E_OUTOFMEMORY;
4077 }
4078
4079 // It's documented that caller shouldn't free the PIDLs, only the array itself
4080 memcpy(*pidl, m_apidl, *items * sizeof(PCUITEMID_CHILD));
4081 }
4082
4083 return S_OK;
4084}
4085
4086HRESULT STDMETHODCALLTYPE CDefView::IsDropOnSource(IDropTarget *drop_target)
4087{
4088 if ((m_iDragOverItem == -1 || m_pCurDropTarget == NULL) &&
4089 (m_pSourceDataObject.p))
4090 {
4091 return S_OK;
4092 }
4093
4094 return S_FALSE;
4095}
4096
4097HRESULT STDMETHODCALLTYPE CDefView::GetDragPoint(POINT *pt)
4098{
4099 if (!pt)
4100 return E_INVALIDARG;
4101
4102 *pt = m_ptFirstMousePos;
4103 return S_OK;
4104}
4105
4106HRESULT STDMETHODCALLTYPE CDefView::GetDropPoint(POINT *pt)
4107{
4108 FIXME("(%p)->(%p) stub\n", this, pt);
4109 return E_NOTIMPL;
4110}
4111
4112HRESULT STDMETHODCALLTYPE CDefView::MoveIcons(IDataObject *obj)
4113{
4114 TRACE("(%p)->(%p)\n", this, obj);
4115 return E_NOTIMPL;
4116}
4117
4118HRESULT STDMETHODCALLTYPE CDefView::SetItemPos(PCUITEMID_CHILD pidl, POINT *pt)
4119{
4120 FIXME("(%p)->(%p %p) stub\n", this, pidl, pt);
4121 return E_NOTIMPL;
4122}
4123
4124HRESULT STDMETHODCALLTYPE CDefView::IsBkDropTarget(IDropTarget *drop_target)
4125{
4126 FIXME("(%p)->(%p) stub\n", this, drop_target);
4127 return E_NOTIMPL;
4128}
4129
4130HRESULT STDMETHODCALLTYPE CDefView::SetClipboard(BOOL move)
4131{
4132 if (!move)
4133 return S_OK;
4134
4135 UINT CutCount = 0;
4136 for (int i = -1; (i = m_ListView.GetNextItem(i, LVNI_SELECTED)) != -1;)
4137 {
4138 if (!CutCount++)
4139 {
4140 // Turn off the LVIS_CUT LVM_SETCALLBACKMASK optimization
4141 m_ListView.SendMessageW(LVM_SETCALLBACKMASK, m_ListView.SendMessageW(LVM_GETCALLBACKMASK, 0, 0) & ~LVIS_CUT, 0);
4142 RefreshGhostedState();
4143 }
4144 m_ListView.SetItemState(i, LVIS_CUT, LVIS_CUT);
4145 }
4146 if (CutCount)
4147 {
4148 m_ClipboardChain.Hook(m_hWnd);
4149 m_HasCutItems = true;
4150 }
4151 return S_OK;
4152}
4153
4154HRESULT STDMETHODCALLTYPE CDefView::SetPoints(IDataObject *obj)
4155{
4156 FIXME("(%p)->(%p) stub\n", this, obj);
4157 return E_NOTIMPL;
4158}
4159
4160HRESULT STDMETHODCALLTYPE CDefView::GetItemSpacing(ITEMSPACING *spacing)
4161{
4162 FIXME("(%p)->(%p) stub\n", this, spacing);
4163 return E_NOTIMPL;
4164}
4165
4166HRESULT STDMETHODCALLTYPE CDefView::SetCallback(IShellFolderViewCB *new_cb, IShellFolderViewCB **old_cb)
4167{
4168 if (old_cb)
4169 *old_cb = m_pShellFolderViewCB.Detach();
4170
4171 m_pShellFolderViewCB = new_cb;
4172 m_pFolderFilter = NULL;
4173 if (new_cb)
4174 new_cb->QueryInterface(IID_PPV_ARG(IFolderFilter, &m_pFolderFilter));
4175 return S_OK;
4176}
4177
4178HRESULT STDMETHODCALLTYPE CDefView::Select(UINT flags)
4179{
4180 FIXME("(%p)->(%d) stub\n", this, flags);
4181 return E_NOTIMPL;
4182}
4183
4184HRESULT STDMETHODCALLTYPE CDefView::QuerySupport(UINT *support)
4185{
4186 TRACE("(%p)->(%p)\n", this, support);
4187 return S_OK;
4188}
4189
4190HRESULT STDMETHODCALLTYPE CDefView::SetAutomationObject(IDispatch *disp)
4191{
4192 FIXME("(%p)->(%p) stub\n", this, disp);
4193 return E_NOTIMPL;
4194}
4195
4196HRESULT WINAPI CDefView::QueryStatus(const GUID *pguidCmdGroup, ULONG cCmds, OLECMD *prgCmds, OLECMDTEXT *pCmdText)
4197{
4198 FIXME("(%p)->(%p(%s) 0x%08x %p %p\n",
4199 this, pguidCmdGroup, debugstr_guid(pguidCmdGroup), cCmds, prgCmds, pCmdText);
4200
4201 if (!prgCmds)
4202 return E_INVALIDARG;
4203
4204 for (UINT i = 0; i < cCmds; i++)
4205 {
4206 FIXME("\tprgCmds[%d].cmdID = %d\n", i, prgCmds[i].cmdID);
4207 prgCmds[i].cmdf = 0;
4208 }
4209
4210 return OLECMDERR_E_UNKNOWNGROUP;
4211}
4212
4213///
4214// ISVOleCmdTarget_Exec(IOleCommandTarget)
4215//
4216// nCmdID is the OLECMDID_* enumeration
4217HRESULT WINAPI CDefView::Exec(const GUID *pguidCmdGroup, DWORD nCmdID, DWORD nCmdexecopt, VARIANT *pvaIn, VARIANT *pvaOut)
4218{
4219 FIXME("(%p)->(\n\tTarget GUID:%s Command:0x%08x Opt:0x%08x %p %p)\n",
4220 this, debugstr_guid(pguidCmdGroup), nCmdID, nCmdexecopt, pvaIn, pvaOut);
4221
4222 if (!pguidCmdGroup)
4223 return OLECMDERR_E_UNKNOWNGROUP;
4224
4225 if (IsEqualCLSID(*pguidCmdGroup, m_Category))
4226 {
4227 if (nCmdID == FCIDM_SHVIEW_AUTOARRANGE)
4228 {
4229 if (V_VT(pvaIn) != VT_INT_PTR)
4230 return OLECMDERR_E_NOTSUPPORTED;
4231
4232 TPMPARAMS params;
4233 params.cbSize = sizeof(params);
4234 params.rcExclude = *(RECT*) V_INTREF(pvaIn);
4235
4236 if (m_hMenuViewModes)
4237 {
4238 // Duplicate all but the last two items of the view modes menu
4239 HMENU hmenuViewPopup = CreatePopupMenu();
4240 Shell_MergeMenus(hmenuViewPopup, m_hMenuViewModes, 0, 0, 0xFFFF, 0);
4241 DeleteMenu(hmenuViewPopup, GetMenuItemCount(hmenuViewPopup) - 1, MF_BYPOSITION);
4242 DeleteMenu(hmenuViewPopup, GetMenuItemCount(hmenuViewPopup) - 1, MF_BYPOSITION);
4243 CheckViewMode(hmenuViewPopup);
4244 TrackPopupMenuEx(hmenuViewPopup, TPM_LEFTALIGN | TPM_TOPALIGN, params.rcExclude.left, params.rcExclude.bottom, m_hWndParent, ¶ms);
4245 ::DestroyMenu(hmenuViewPopup);
4246 }
4247
4248 // pvaOut is VT_I4 with value 0x403 (cmd id of the new mode maybe?)
4249 V_VT(pvaOut) = VT_I4;
4250 V_I4(pvaOut) = 0x403;
4251 }
4252 }
4253
4254 if (IsEqualIID(*pguidCmdGroup, CGID_Explorer) &&
4255 (nCmdID == 0x29) &&
4256 (nCmdexecopt == 4) && pvaOut)
4257 return S_OK;
4258
4259 if (IsEqualIID(*pguidCmdGroup, CGID_ShellDocView) &&
4260 (nCmdID == 9) &&
4261 (nCmdexecopt == 0))
4262 return 1;
4263
4264 if (IsEqualIID(*pguidCmdGroup, CGID_DefView))
4265 {
4266 CComPtr<IStream> pStream;
4267 WCHAR SubKey[MAX_PATH];
4268 switch (nCmdID)
4269 {
4270 case DVCMDID_SET_DEFAULTFOLDER_SETTINGS:
4271 SHDeleteKey(HKEY_CURRENT_USER, L"Software\\Microsoft\\Windows\\ShellNoRoam\\Bags");
4272 if (SUCCEEDED(GetDefaultViewStream(STGM_WRITE, &pStream)))
4273 SaveViewState(pStream);
4274 return S_OK;
4275 case DVCMDID_RESET_DEFAULTFOLDER_SETTINGS:
4276 PathCombineW(SubKey, REGSTR_PATH_EXPLORER, L"Streams\\Default");
4277 SHDeleteKey(HKEY_CURRENT_USER, SubKey);
4278 SHDeleteKey(HKEY_CURRENT_USER, L"Software\\Microsoft\\Windows\\ShellNoRoam\\Bags");
4279 m_FolderSettings.fFlags |= FWF_NOBROWSERVIEWSTATE; // Don't let this folder save itself
4280 return S_OK;
4281 }
4282 }
4283
4284 return OLECMDERR_E_UNKNOWNGROUP;
4285}
4286
4287/**********************************************************
4288 * ISVDropTarget implementation
4289 */
4290
4291/******************************************************************************
4292 * drag_notify_subitem [Internal]
4293 *
4294 * Figure out the shellfolder object, which is currently under the mouse cursor
4295 * and notify it via the IDropTarget interface.
4296 */
4297
4298#define SCROLLAREAWIDTH 20
4299
4300HRESULT CDefView::drag_notify_subitem(DWORD grfKeyState, POINTL pt, DWORD *pdwEffect)
4301{
4302 LONG lResult;
4303 HRESULT hr;
4304 RECT clientRect;
4305
4306 /* The key state on drop doesn't have MK_LBUTTON or MK_RBUTTON because it
4307 reflects the key state after the user released the button, so we need
4308 to remember the last key state when the button was pressed */
4309 m_grfKeyState = grfKeyState;
4310
4311 // Map from global to client coordinates and query the index of the
4312 // listview-item, which is currently under the mouse cursor.
4313 LVHITTESTINFO htinfo = {{pt.x, pt.y}, LVHT_ONITEM};
4314 ScreenToClient(&htinfo.pt);
4315 lResult = m_ListView.HitTest(&htinfo);
4316
4317 /* Send WM_*SCROLL messages every 250 ms during drag-scrolling */
4318 ::GetClientRect(m_ListView, &clientRect);
4319 if (htinfo.pt.x == m_ptLastMousePos.x && htinfo.pt.y == m_ptLastMousePos.y &&
4320 (htinfo.pt.x < SCROLLAREAWIDTH || htinfo.pt.x > clientRect.right - SCROLLAREAWIDTH ||
4321 htinfo.pt.y < SCROLLAREAWIDTH || htinfo.pt.y > clientRect.bottom - SCROLLAREAWIDTH))
4322 {
4323 m_cScrollDelay = (m_cScrollDelay + 1) % 5; // DragOver is called every 50 ms
4324 if (m_cScrollDelay == 0)
4325 {
4326 /* Mouse did hover another 250 ms over the scroll-area */
4327 if (htinfo.pt.x < SCROLLAREAWIDTH)
4328 m_ListView.SendMessageW(WM_HSCROLL, SB_LINEUP, 0);
4329
4330 if (htinfo.pt.x > clientRect.right - SCROLLAREAWIDTH)
4331 m_ListView.SendMessageW(WM_HSCROLL, SB_LINEDOWN, 0);
4332
4333 if (htinfo.pt.y < SCROLLAREAWIDTH)
4334 m_ListView.SendMessageW(WM_VSCROLL, SB_LINEUP, 0);
4335
4336 if (htinfo.pt.y > clientRect.bottom - SCROLLAREAWIDTH)
4337 m_ListView.SendMessageW(WM_VSCROLL, SB_LINEDOWN, 0);
4338 }
4339 }
4340 else
4341 {
4342 m_cScrollDelay = 0; // Reset, if cursor is not over the listview's scroll-area
4343 }
4344
4345 m_ptLastMousePos = htinfo.pt;
4346 ::ClientToListView(m_ListView, &m_ptLastMousePos);
4347
4348 /* We need to check if we drag the selection over itself */
4349 if (lResult != -1 && m_pSourceDataObject.p != NULL)
4350 {
4351 PCUITEMID_CHILD pidl = _PidlByItem(lResult);
4352
4353 for (UINT i = 0; i < m_cidl; i++)
4354 {
4355 if (pidl == m_apidl[i])
4356 {
4357 /* The item that is being draged is hovering itself. */
4358 lResult = -1;
4359 break;
4360 }
4361 }
4362 }
4363
4364 // If we are still over the previous sub-item, notify it via DragOver and return
4365 if (m_pCurDropTarget && lResult == m_iDragOverItem)
4366 return m_pCurDropTarget->DragOver(grfKeyState, pt, pdwEffect);
4367
4368 // We've left the previous sub-item, notify it via DragLeave and release it
4369 if (m_pCurDropTarget)
4370 {
4371 PCUITEMID_CHILD pidl = _PidlByItem(m_iDragOverItem);
4372 if (pidl)
4373 SelectItem(pidl, 0);
4374
4375 m_pCurDropTarget->DragLeave();
4376 m_pCurDropTarget.Release();
4377 }
4378
4379 m_iDragOverItem = lResult;
4380
4381 if (lResult == -1)
4382 {
4383 // We are not above one of the listview's subitems. Bind to the
4384 // parent folder's DropTarget interface.
4385 hr = m_pSFParent->CreateViewObject(NULL, IID_PPV_ARG(IDropTarget,&m_pCurDropTarget));
4386 }
4387 else
4388 {
4389 // Query the relative PIDL of the shellfolder object represented
4390 // by the currently dragged over listview-item ...
4391 PCUITEMID_CHILD pidl = _PidlByItem(lResult);
4392
4393 // ... and bind m_pCurDropTarget to the IDropTarget interface of an UIObject of this object
4394 hr = m_pSFParent->GetUIObjectOf(m_ListView, 1, &pidl, IID_NULL_PPV_ARG(IDropTarget, &m_pCurDropTarget));
4395 }
4396
4397 IUnknown_SetSite(m_pCurDropTarget, (IShellView *)this);
4398
4399 // If anything failed, m_pCurDropTarget should be NULL now, which ought to be a save state
4400 if (FAILED(hr))
4401 {
4402 *pdwEffect = DROPEFFECT_NONE;
4403 return hr;
4404 }
4405
4406 if (m_iDragOverItem != -1)
4407 {
4408 SelectItem(m_iDragOverItem, SVSI_SELECT);
4409 }
4410
4411 // Notify the item just entered via DragEnter
4412 return m_pCurDropTarget->DragEnter(m_pCurDataObject, grfKeyState, pt, pdwEffect);
4413}
4414
4415HRESULT WINAPI CDefView::DragEnter(IDataObject *pDataObject, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect)
4416{
4417 if (*pdwEffect == DROPEFFECT_NONE)
4418 return S_OK;
4419
4420 /* Get a hold on the data object for later calls to DragEnter on the sub-folders */
4421 m_pCurDataObject = pDataObject;
4422
4423 HRESULT hr = drag_notify_subitem(grfKeyState, pt, pdwEffect);
4424 if (SUCCEEDED(hr))
4425 {
4426 POINT ptClient = {pt.x, pt.y};
4427 ScreenToClient(&ptClient);
4428 ImageList_DragEnter(m_hWnd, ptClient.x, ptClient.y);
4429 }
4430
4431 return hr;
4432}
4433
4434HRESULT WINAPI CDefView::DragOver(DWORD grfKeyState, POINTL pt, DWORD *pdwEffect)
4435{
4436 POINT ptClient = {pt.x, pt.y};
4437 ScreenToClient(&ptClient);
4438 ImageList_DragMove(ptClient.x, ptClient.y);
4439 return drag_notify_subitem(grfKeyState, pt, pdwEffect);
4440}
4441
4442HRESULT WINAPI CDefView::DragLeave()
4443{
4444 ImageList_DragLeave(m_hWnd);
4445
4446 if (m_pCurDropTarget)
4447 {
4448 m_pCurDropTarget->DragLeave();
4449 m_pCurDropTarget.Release();
4450 }
4451
4452 if (m_pCurDataObject != NULL)
4453 {
4454 m_pCurDataObject.Release();
4455 }
4456
4457 m_iDragOverItem = 0;
4458
4459 return S_OK;
4460}
4461
4462INT CDefView::_FindInsertableIndexFromPoint(POINT pt)
4463{
4464 RECT rcBound;
4465 INT i, nCount = m_ListView.GetItemCount();
4466 DWORD dwSpacing;
4467 INT dx, dy;
4468 BOOL bSmall = ((m_ListView.GetStyle() & LVS_TYPEMASK) != LVS_ICON);
4469
4470 // FIXME: LVM_GETORIGIN is broken. See CORE-17266
4471 pt.x += m_ListView.GetScrollPos(SB_HORZ);
4472 pt.y += m_ListView.GetScrollPos(SB_VERT);
4473
4474 if (m_ListView.GetStyle() & LVS_ALIGNLEFT)
4475 {
4476 // vertically
4477 for (i = 0; i < nCount; ++i)
4478 {
4479 dwSpacing = ListView_GetItemSpacing(m_ListView, bSmall);
4480 dx = LOWORD(dwSpacing);
4481 dy = HIWORD(dwSpacing);
4482 ListView_GetItemRect(m_ListView, i, &rcBound, LVIR_SELECTBOUNDS);
4483 rcBound.right = rcBound.left + dx;
4484 rcBound.bottom = rcBound.top + dy;
4485 if (pt.x < rcBound.right && pt.y < (rcBound.top + rcBound.bottom) / 2)
4486 {
4487 return i;
4488 }
4489 }
4490 for (i = nCount - 1; i >= 0; --i)
4491 {
4492 ListView_GetItemRect(m_ListView, i, &rcBound, LVIR_SELECTBOUNDS);
4493 if (rcBound.left < pt.x && rcBound.top < pt.y)
4494 {
4495 return i + 1;
4496 }
4497 }
4498 }
4499 else
4500 {
4501 // horizontally
4502 for (i = 0; i < nCount; ++i)
4503 {
4504 dwSpacing = ListView_GetItemSpacing(m_ListView, bSmall);
4505 dx = LOWORD(dwSpacing);
4506 dy = HIWORD(dwSpacing);
4507 ListView_GetItemRect(m_ListView, i, &rcBound, LVIR_SELECTBOUNDS);
4508 rcBound.right = rcBound.left + dx;
4509 rcBound.bottom = rcBound.top + dy;
4510 if (pt.y < rcBound.bottom && pt.x < rcBound.left)
4511 {
4512 return i;
4513 }
4514 if (pt.y < rcBound.bottom && pt.x < rcBound.right)
4515 {
4516 return i + 1;
4517 }
4518 }
4519 for (i = nCount - 1; i >= 0; --i)
4520 {
4521 ListView_GetItemRect(m_ListView, i, &rcBound, LVIR_SELECTBOUNDS);
4522 if (rcBound.left < pt.x && rcBound.top < pt.y)
4523 {
4524 return i + 1;
4525 }
4526 }
4527 }
4528
4529 return nCount;
4530}
4531
4532void CDefView::_HandleStatusBarResize(int nWidth)
4533{
4534 LRESULT lResult;
4535
4536 if (!m_isFullStatusBar)
4537 {
4538 int nPartArray[] = {-1};
4539 m_pShellBrowser->SendControlMsg(FCW_STATUS, SB_SETPARTS, _countof(nPartArray), (LPARAM)nPartArray, &lResult);
4540 return;
4541 }
4542
4543 int nFileSizePartLength = 125;
4544 const int nLocationPartLength = 150;
4545 const int nRightPartsLength = nFileSizePartLength + nLocationPartLength;
4546 int nObjectsPartLength = nWidth - nRightPartsLength;
4547
4548 // If the window is small enough just divide each part into thirds
4549 // to match the behavior of Windows Server 2003
4550 if (nObjectsPartLength <= nLocationPartLength)
4551 nObjectsPartLength = nFileSizePartLength = nWidth / 3;
4552
4553 int nPartArray[] = {nObjectsPartLength, nObjectsPartLength + nFileSizePartLength, -1};
4554
4555 m_pShellBrowser->SendControlMsg(FCW_STATUS, SB_SETPARTS, _countof(nPartArray), (LPARAM)nPartArray, &lResult);
4556}
4557
4558void CDefView::_ForceStatusBarResize()
4559{
4560 // Get the handle for the status bar
4561 HWND fStatusBar;
4562 m_pShellBrowser->GetControlWindow(FCW_STATUS, &fStatusBar);
4563
4564 // Get the size of our status bar
4565 RECT statusBarSize;
4566 ::GetWindowRect(fStatusBar, &statusBarSize);
4567
4568 // Resize the status bar
4569 _HandleStatusBarResize(statusBarSize.right - statusBarSize.left);
4570}
4571
4572typedef CSimpleMap<LPARAM, INT> CLParamIndexMap;
4573
4574static INT CALLBACK
4575SelectionMoveCompareFunc(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
4576{
4577 CLParamIndexMap *pmap = (CLParamIndexMap *)lParamSort;
4578 INT i1 = pmap->Lookup(lParam1), i2 = pmap->Lookup(lParam2);
4579 if (i1 < i2)
4580 return -1;
4581 if (i1 > i2)
4582 return 1;
4583 return 0;
4584}
4585
4586void CDefView::_MoveSelectionOnAutoArrange(POINT pt)
4587{
4588 // get insertable index from position
4589 INT iPosition = _FindInsertableIndexFromPoint(pt);
4590
4591 // create identity mapping of indexes
4592 CSimpleArray<INT> array;
4593 INT nCount = m_ListView.GetItemCount();
4594 for (INT i = 0; i < nCount; ++i)
4595 {
4596 array.Add(i);
4597 }
4598
4599 // re-ordering mapping
4600 INT iItem = -1;
4601 while ((iItem = m_ListView.GetNextItem(iItem, LVNI_SELECTED)) >= 0)
4602 {
4603 INT iFrom = iItem, iTo = iPosition;
4604 if (iFrom < iTo)
4605 --iTo;
4606 if (iFrom >= nCount)
4607 iFrom = nCount - 1;
4608 if (iTo >= nCount)
4609 iTo = nCount - 1;
4610
4611 // shift indexes by swapping (like a bucket relay)
4612 if (iFrom < iTo)
4613 {
4614 for (INT i = iFrom; i < iTo; ++i)
4615 {
4616 // swap array[i] and array[i + 1]
4617 INT tmp = array[i];
4618 array[i] = array[i + 1];
4619 array[i + 1] = tmp;
4620 }
4621 }
4622 else
4623 {
4624 for (INT i = iFrom; i > iTo; --i)
4625 {
4626 // swap array[i] and array[i - 1]
4627 INT tmp = array[i];
4628 array[i] = array[i - 1];
4629 array[i - 1] = tmp;
4630 }
4631 }
4632 }
4633
4634 // create mapping (ListView's lParam to index) from array
4635 CLParamIndexMap map;
4636 for (INT i = 0; i < nCount; ++i)
4637 {
4638 LPARAM lParam = m_ListView.GetItemData(array[i]);
4639 map.Add(lParam, i);
4640 }
4641
4642 // finally sort
4643 m_ListView.SortItems(SelectionMoveCompareFunc, &map);
4644}
4645
4646HRESULT WINAPI CDefView::Drop(IDataObject* pDataObject, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect)
4647{
4648 ImageList_DragLeave(m_hWnd);
4649 ImageList_EndDrag();
4650
4651 if ((IsDropOnSource(NULL) == S_OK) &&
4652 (*pdwEffect & DROPEFFECT_MOVE) &&
4653 (m_grfKeyState & MK_LBUTTON))
4654 {
4655 if (m_pCurDropTarget)
4656 {
4657 m_pCurDropTarget->DragLeave();
4658 m_pCurDropTarget.Release();
4659 }
4660
4661 POINT ptDrop = { pt.x, pt.y };
4662 ::ScreenToClient(m_ListView, &ptDrop);
4663 ::ClientToListView(m_ListView, &ptDrop);
4664 m_ptLastMousePos = ptDrop;
4665
4666 m_ListView.SetRedraw(FALSE);
4667 if (m_ListView.GetStyle() & LVS_AUTOARRANGE)
4668 {
4669 _MoveSelectionOnAutoArrange(m_ptLastMousePos);
4670 }
4671 else
4672 {
4673 POINT ptItem;
4674 INT iItem = -1;
4675 while ((iItem = m_ListView.GetNextItem(iItem, LVNI_SELECTED)) >= 0)
4676 {
4677 if (m_ListView.GetItemPosition(iItem, &ptItem))
4678 {
4679 ptItem.x += m_ptLastMousePos.x - m_ptFirstMousePos.x;
4680 ptItem.y += m_ptLastMousePos.y - m_ptFirstMousePos.y;
4681 m_ListView.SetItemPosition(iItem, &ptItem);
4682 }
4683 }
4684 }
4685 m_ListView.SetRedraw(TRUE);
4686 }
4687 else if (m_pCurDropTarget)
4688 {
4689 m_pCurDropTarget->Drop(pDataObject, grfKeyState, pt, pdwEffect);
4690 m_pCurDropTarget.Release();
4691 }
4692
4693 m_pCurDataObject.Release();
4694 m_iDragOverItem = 0;
4695 return S_OK;
4696}
4697
4698HRESULT WINAPI CDefView::QueryContinueDrag(BOOL fEscapePressed, DWORD grfKeyState)
4699{
4700 TRACE("(%p)\n", this);
4701
4702 if (fEscapePressed)
4703 return DRAGDROP_S_CANCEL;
4704 else if (!(grfKeyState & MK_LBUTTON) && !(grfKeyState & MK_RBUTTON))
4705 return DRAGDROP_S_DROP;
4706 else
4707 return S_OK;
4708}
4709
4710HRESULT WINAPI CDefView::GiveFeedback(DWORD dwEffect)
4711{
4712 TRACE("(%p)\n", this);
4713
4714 return DRAGDROP_S_USEDEFAULTCURSORS;
4715}
4716
4717HRESULT 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)
4718{
4719 FIXME("Stub: this=%p\n", this);
4720 return E_NOTIMPL;
4721}
4722
4723HRESULT WINAPI CDefView::GetColorSet(DWORD dwDrawAspect, LONG lindex, void *pvAspect, DVTARGETDEVICE *ptd, HDC hicTargetDevice, LOGPALETTE **ppColorSet)
4724{
4725 FIXME("Stub: this=%p\n", this);
4726 return E_NOTIMPL;
4727}
4728
4729HRESULT WINAPI CDefView::Freeze(DWORD dwDrawAspect, LONG lindex, void *pvAspect, DWORD *pdwFreeze)
4730{
4731 FIXME("Stub: this=%p\n", this);
4732 return E_NOTIMPL;
4733}
4734
4735HRESULT WINAPI CDefView::Unfreeze(DWORD dwFreeze)
4736{
4737 FIXME("Stub: this=%p\n", this);
4738 return E_NOTIMPL;
4739}
4740
4741HRESULT WINAPI CDefView::SetAdvise(DWORD aspects, DWORD advf, IAdviseSink *pAdvSink)
4742{
4743 FIXME("partial stub: %p 0x%08x 0x%08x %p\n", this, aspects, advf, pAdvSink);
4744
4745 // FIXME: we set the AdviseSink, but never use it to send any advice
4746 m_pAdvSink = pAdvSink;
4747 m_dwAspects = aspects;
4748 m_dwAdvf = advf;
4749
4750 return S_OK;
4751}
4752
4753HRESULT WINAPI CDefView::GetAdvise(DWORD *pAspects, DWORD *pAdvf, IAdviseSink **ppAdvSink)
4754{
4755 TRACE("this=%p pAspects=%p pAdvf=%p ppAdvSink=%p\n", this, pAspects, pAdvf, ppAdvSink);
4756
4757 if (ppAdvSink)
4758 {
4759 *ppAdvSink = m_pAdvSink;
4760 m_pAdvSink.p->AddRef();
4761 }
4762
4763 if (pAspects)
4764 *pAspects = m_dwAspects;
4765
4766 if (pAdvf)
4767 *pAdvf = m_dwAdvf;
4768
4769 return S_OK;
4770}
4771
4772HRESULT STDMETHODCALLTYPE CDefView::QueryService(REFGUID guidService, REFIID riid, void **ppvObject)
4773{
4774 if (IsEqualIID(guidService, SID_SFolderView))
4775 return QueryInterface(riid, ppvObject);
4776 return m_pShellBrowser ? IUnknown_QueryService(m_pShellBrowser, guidService, riid, ppvObject) : E_NOINTERFACE;
4777}
4778
4779HRESULT CDefView::_MergeToolbar()
4780{
4781 CComPtr<IExplorerToolbar> ptb;
4782 HRESULT hr = S_OK;
4783
4784 hr = IUnknown_QueryService(m_pShellBrowser, IID_IExplorerToolbar, IID_PPV_ARG(IExplorerToolbar, &ptb));
4785 if (FAILED(hr))
4786 return hr;
4787
4788 m_Category = CGID_DefViewFrame;
4789
4790 hr = ptb->SetCommandTarget(static_cast<IOleCommandTarget*>(this), &m_Category, 0);
4791 if (FAILED(hr))
4792 return hr;
4793
4794 if (hr == S_FALSE)
4795 return S_OK;
4796
4797#if 0
4798 hr = ptb->AddButtons(&m_Category, buttonsCount, buttons);
4799 if (FAILED(hr))
4800 return hr;
4801#endif
4802
4803 return S_OK;
4804}
4805
4806HRESULT CDefView::_DoFolderViewCB(UINT uMsg, WPARAM wParam, LPARAM lParam)
4807{
4808 HRESULT hr = E_NOTIMPL;
4809
4810 if (m_pShellFolderViewCB)
4811 {
4812 hr = m_pShellFolderViewCB->MessageSFVCB(uMsg, wParam, lParam);
4813 }
4814
4815 return hr;
4816}
4817
4818HRESULT CDefView_CreateInstance(IShellFolder *pFolder, REFIID riid, LPVOID * ppvOut)
4819{
4820 return ShellObjectCreatorInit<CDefView>(pFolder, riid, ppvOut);
4821}
4822
4823HRESULT WINAPI SHCreateShellFolderView(const SFV_CREATE *pcsfv, IShellView **ppsv)
4824{
4825 CComPtr<IShellView> psv;
4826 HRESULT hRes;
4827
4828 if (!ppsv)
4829 return E_INVALIDARG;
4830
4831 *ppsv = NULL;
4832
4833 if (!pcsfv || pcsfv->cbSize != sizeof(*pcsfv))
4834 return E_INVALIDARG;
4835
4836 TRACE("sf=%p outer=%p callback=%p\n",
4837 pcsfv->pshf, pcsfv->psvOuter, pcsfv->psfvcb);
4838
4839 hRes = CDefView_CreateInstance(pcsfv->pshf, IID_PPV_ARG(IShellView, &psv));
4840 if (FAILED(hRes))
4841 return hRes;
4842
4843 if (pcsfv->psfvcb)
4844 {
4845 CComPtr<IShellFolderView> sfv;
4846 if (SUCCEEDED(psv->QueryInterface(IID_PPV_ARG(IShellFolderView, &sfv))))
4847 {
4848 sfv->SetCallback(pcsfv->psfvcb, NULL);
4849 }
4850 }
4851
4852 *ppsv = psv.Detach();
4853 return hRes;
4854}