ReactOS 0.4.16-dev-716-g2b2bdab
CDefView.cpp
Go to the documentation of this file.
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
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
52
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.
58};
59#undef FCIDM_SHVIEWLAST // Don't use this constant, change DVIDM_CONTEXTMENU_LAST if you need a new id.
60
62{
66 UINT8 Reserved; // Unused
68
69 enum { UNSPECIFIEDCOLUMN = -1 };
70 void Reset()
71 {
72 *(UINT*)this = 0;
74 }
75};
76
77#define SHV_CHANGE_NOTIFY (WM_USER + 0x1111)
78#define SHV_UPDATESTATUSBAR (WM_USER + 0x1112)
79
80// For the context menu of the def view, the id of the items are based on 1 because we need
81// to call TrackPopupMenu and let it use the 0 value as an indication that the menu was canceled
82#define CONTEXT_MENU_BASE_ID 1
83
85{
86 enum { MAXCOUNT = 100 };
87 static const UINT SIG = ('R' << 0) | ('O' << 8) | ('S' << 16) | (('c') << 24);
91};
92
94{
95 static const UINT SIG = ('R' << 0) | ('O' << 8) | ('S' << 16) | (('c' ^ 'v' ^ 's') << 24);
99 static const UINT VALIDFWF = FWF_AUTOARRANGE | FWF_SNAPTOGRID | FWF_NOGROUPING; // Note: The desktop applies FWF_NOICONS when appropriate
101};
102
103static UINT
105{
106 UINT cmf = CMF_NORMAL;
107 if (GetKeyState(VK_SHIFT) < 0)
108 cmf |= CMF_EXTENDEDVERBS;
109 if (sfgao & SFGAO_CANRENAME)
110 cmf |= CMF_CANRENAME;
111 HWND hwnd = NULL;
112 if (pSB && SUCCEEDED(pSB->GetControlWindow(FCW_TREE, &hwnd)) && hwnd)
113 cmf |= CMF_EXPLORE;
114 return cmf;
115}
116
117// Convert client coordinates to listview coordinates
118static void
120{
121 POINT Origin = {};
122
123 // FIXME: LVM_GETORIGIN is broken. See CORE-17266
124 if (!ListView_GetOrigin(hwndLV, &Origin))
125 return;
126
127 ppt->x += Origin.x;
128 ppt->y += Origin.y;
129}
130
131// Helper struct to automatically cleanup the IContextMenu
132// We want to explicitly reset the Site, so there are no circular references
134{
137
139 : m_pCM(pCM), m_hMenu(menu)
140 {
141 }
143 {
144 if (m_hMenu)
145 {
147 m_hMenu = NULL;
148 }
149 if (m_pCM)
150 {
152 m_pCM.Release();
153 }
154 }
155};
156
158{
159 MENUITEMINFOW mii;
160 mii.cbSize = sizeof(mii);
162 mii.fState = MF & (MFS_CHECKED | MFS_DISABLED);
163 mii.fType = MF & ~mii.fState;
164 mii.wID = Id;
165 mii.dwTypeData = const_cast<LPWSTR>(String);
166 mii.dwItemData = Data;
167 return InsertMenuItemW(hMenu, -1, TRUE, &mii);
168}
169
171{
172 MENUITEMINFOW mii;
173 mii.cbSize = FIELD_OFFSET(MENUITEMINFOW, hbmpItem);
174 mii.fMask = MIIM_DATA;
175 if (GetMenuItemInfoW(hMenu, Id, FALSE, &mii))
176 return mii.dwItemData;
177 else
178 return 0;
179}
180
182{
183 MENUITEMINFOW mii = {sizeof(mii), MIIM_SUBMENU};
184 if (::GetMenuItemInfoW(hmenu, id, FALSE, &mii))
185 return mii.hSubMenu;
186
187 return NULL;
188}
189
190/* ReallyGetMenuItemID returns the id of an item even if it opens a submenu,
191 GetMenuItemID returns -1 if the specified item opens a submenu */
193{
194 MENUITEMINFOW mii = {sizeof(mii), MIIM_ID};
195 if (::GetMenuItemInfoW(hmenu, i, TRUE, &mii))
196 return mii.wID;
197
198 return UINT_MAX;
199}
200
202{
203 UINT ret = 0;
204 HDC hDC = GetDC(hwnd);
205 if (hDC)
206 {
209 SelectObject(hDC, hOrg);
211 }
212 return ret;
213}
214
215static inline COLORREF GetViewColor(COLORREF Clr, UINT SysFallback)
216{
217 return Clr != CLR_INVALID ? Clr : GetSysColor(SysFallback);
218}
219
220class CDefView :
221 public CWindowImpl<CDefView, CWindow, CControlWinTraits>,
222 public CComObjectRootEx<CComMultiThreadModelNoCS>,
223 public IShellView3,
224 public IFolderView,
225 public IShellFolderView,
226 public IOleCommandTarget,
227 public IDropTarget,
228 public IDropSource,
229 public IViewObject,
230 public IServiceProvider
231{
232private:
244 HMENU m_hMenu; // Handle to the menu bar of the browser
245 HMENU m_hMenuArrangeModes; // Handle to the popup menu with the arrange modes
246 HMENU m_hMenuViewModes; // Handle to the popup menu with the view modes
247 HMENU m_hContextMenu; // Handle to the open context menu
256 ULONG m_hNotify; // Change notification handle
257 HACCEL m_hAccel;
261 // for drag and drop
263 CComPtr<IDropTarget> m_pCurDropTarget; // The sub-item, which is currently dragged over
264 CComPtr<IDataObject> m_pCurDataObject; // The dragged data-object
265 LONG m_iDragOverItem; // Dragged over item's index, if m_pCurDropTarget != NULL
266 UINT m_cScrollDelay; // Send a WM_*SCROLL msg every 250 ms during drag-scroll
267 POINT m_ptLastMousePos; // Mouse position at last DragOver call
268 POINT m_ptFirstMousePos; // Mouse position when the drag operation started
270 //
273
277
281
283
285 BOOL _Sort(int Col = -1);
292 void _DoCopyToMoveToFolder(BOOL bCopy);
294
295public:
296 CDefView();
297 ~CDefView();
302 void UpdateStatusbar();
303 void CheckToolbar();
305 void UpdateListColors();
306 BOOL InitList();
307 static INT CALLBACK ListViewCompareItems(LPARAM lParam1, LPARAM lParam2, LPARAM lpData);
308
313 HRESULT LoadColumn(UINT FoldCol, UINT ListCol, BOOL Insert, UINT ForceWidth = 0);
314 HRESULT LoadColumns(SIZE_T *pColList = NULL, UINT ColListCount = 0);
315 void ColumnListChanged();
319 int LV_AddItem(PCUITEMID_CHILD pidl);
323 void LV_RefreshIcon(INT iItem);
324 void LV_RefreshIcons();
326 HRESULT FillList(BOOL IsRefreshCommand = TRUE);
330 HRESULT FillArrangeAsMenu(HMENU hmenuArrange);
331 HRESULT CheckViewMode(HMENU hmenuView);
335 void OnDeactivate();
336 void DoActivate(UINT uState);
337 HRESULT drag_notify_subitem(DWORD grfKeyState, POINTL pt, DWORD *pdwEffect);
339 LRESULT OnExplorerCommand(UINT uCommand, BOOL bUseSelection);
341 HRESULT GetDefaultViewStream(DWORD Stgm, IStream **ppStream);
343 HRESULT SaveViewState(IStream *pStream);
345
347 {
350 {
351 DWORD flags;
352 if (SUCCEEDED(pcdb2->GetViewFlags(&flags)))
353 return flags;
354 }
355 return 0;
356 }
357
358 // *** IOleWindow methods ***
359 STDMETHOD(GetWindow)(HWND *lphwnd) override;
360 STDMETHOD(ContextSensitiveHelp)(BOOL fEnterMode) override;
361
362 // *** IShellView methods ***
363 STDMETHOD(TranslateAccelerator)(MSG *pmsg) override;
364 STDMETHOD(EnableModeless)(BOOL fEnable) override;
365 STDMETHOD(UIActivate)(UINT uState) override;
366 STDMETHOD(Refresh)() override;
367 STDMETHOD(CreateViewWindow)(IShellView *psvPrevious, LPCFOLDERSETTINGS pfs, IShellBrowser *psb, RECT *prcView, HWND *phWnd) override;
368 STDMETHOD(DestroyViewWindow)() override;
371 STDMETHOD(SaveViewState)() override;
372 STDMETHOD(SelectItem)(PCUITEMID_CHILD pidlItem, SVSIF uFlags) override;
373 STDMETHOD(GetItemObject)(UINT uItem, REFIID riid, void **ppv) override;
374
375 // *** IShellView2 methods ***
376 STDMETHOD(GetView)(SHELLVIEWID *view_guid, ULONG view_type) override;
377 STDMETHOD(CreateViewWindow2)(LPSV2CVW2_PARAMS view_params) override;
378 STDMETHOD(HandleRename)(LPCITEMIDLIST new_pidl) override;
380
381 // *** IShellView3 methods ***
383 IShellBrowser *psb,
384 IShellView *psvPrevious,
385 SV3CVW3_FLAGS view_flags,
389 const SHELLVIEWID *view_id,
390 const RECT *prcView,
391 HWND *hwnd) override;
392
393 // *** IFolderView methods ***
394 STDMETHOD(GetCurrentViewMode)(UINT *pViewMode) override;
395 STDMETHOD(SetCurrentViewMode)(UINT ViewMode) override;
396 STDMETHOD(GetFolder)(REFIID riid, void **ppv) override;
397 STDMETHOD(Item)(int iItemIndex, PITEMID_CHILD *ppidl) override;
398 STDMETHOD(ItemCount)(UINT uFlags, int *pcItems) override;
399 STDMETHOD(Items)(UINT uFlags, REFIID riid, void **ppv) override;
400 STDMETHOD(GetSelectionMarkedItem)(int *piItem) override;
401 STDMETHOD(GetFocusedItem)(int *piItem) override;
402 STDMETHOD(GetItemPosition)(PCUITEMID_CHILD pidl, POINT *ppt) override;
403 STDMETHOD(GetSpacing)(POINT *ppt) override;
404 STDMETHOD(GetDefaultSpacing)(POINT *ppt) override;
405 STDMETHOD(GetAutoArrange)() override;
406 STDMETHOD(SelectItem)(int iItem, DWORD dwFlags) override;
408
409 // *** IShellFolderView methods ***
410 STDMETHOD(Rearrange)(LPARAM sort) override;
412 STDMETHOD(ArrangeGrid)() override;
413 STDMETHOD(AutoArrange)() override;
414 STDMETHOD(AddObject)(PITEMID_CHILD pidl, UINT *item) override;
415 STDMETHOD(GetObject)(PITEMID_CHILD *pidl, UINT item) override;
416 STDMETHOD(RemoveObject)(PITEMID_CHILD pidl, UINT *item) override;
419 STDMETHOD(UpdateObject)(PITEMID_CHILD pidl_old, PITEMID_CHILD pidl_new, UINT *item) override;
421 STDMETHOD(SetRedraw)(BOOL redraw) override;
424 STDMETHOD(IsDropOnSource)(IDropTarget *drop_target) override;
425 STDMETHOD(GetDragPoint)(POINT *pt) override;
426 STDMETHOD(GetDropPoint)(POINT *pt) override;
428 STDMETHOD(SetItemPos)(PCUITEMID_CHILD pidl, POINT *pt) override;
429 STDMETHOD(IsBkDropTarget)(IDropTarget *drop_target) override;
430 STDMETHOD(SetClipboard)(BOOL move) override;
432 STDMETHOD(GetItemSpacing)(ITEMSPACING *spacing) override;
433 STDMETHOD(SetCallback)(IShellFolderViewCB *new_cb, IShellFolderViewCB **old_cb) override;
434 STDMETHOD(Select)(UINT flags) override;
435 STDMETHOD(QuerySupport)(UINT *support) override;
436 STDMETHOD(SetAutomationObject)(IDispatch *disp) override;
437
438 // *** IOleCommandTarget methods ***
439 STDMETHOD(QueryStatus)(const GUID *pguidCmdGroup, ULONG cCmds, OLECMD prgCmds[], OLECMDTEXT *pCmdText) override;
440 STDMETHOD(Exec)(const GUID *pguidCmdGroup, DWORD nCmdID, DWORD nCmdexecopt, VARIANT *pvaIn, VARIANT *pvaOut) override;
441
442 // *** IDropTarget methods ***
443 STDMETHOD(DragEnter)(IDataObject *pDataObj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect) override;
444 STDMETHOD(DragOver)(DWORD grfKeyState, POINTL pt, DWORD *pdwEffect) override;
445 STDMETHOD(DragLeave)() override;
446 STDMETHOD(Drop)(IDataObject *pDataObj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect) override;
447
448 // *** IDropSource methods ***
449 STDMETHOD(QueryContinueDrag)(BOOL fEscapePressed, DWORD grfKeyState) override;
450 STDMETHOD(GiveFeedback)(DWORD dwEffect) override;
451
452 // *** IViewObject methods ***
453 STDMETHOD(Draw)(DWORD dwDrawAspect, LONG lindex, void *pvAspect, DVTARGETDEVICE *ptd,
454 HDC hdcTargetDev, HDC hdcDraw, LPCRECTL lprcBounds, LPCRECTL lprcWBounds,
455 BOOL (STDMETHODCALLTYPE *pfnContinue)(ULONG_PTR dwContinue), ULONG_PTR dwContinue) override;
456 STDMETHOD(GetColorSet)(DWORD dwDrawAspect, LONG lindex, void *pvAspect,
457 DVTARGETDEVICE *ptd, HDC hicTargetDev, LOGPALETTE **ppColorSet) override;
458 STDMETHOD(Freeze)(DWORD dwDrawAspect, LONG lindex, void *pvAspect, DWORD *pdwFreeze) override;
459 STDMETHOD(Unfreeze)(DWORD dwFreeze) override;
460 STDMETHOD(SetAdvise)(DWORD aspects, DWORD advf, IAdviseSink *pAdvSink) override;
461 STDMETHOD(GetAdvise)(DWORD *pAspects, DWORD *pAdvf, IAdviseSink **ppAdvSink) override;
462
463 // *** IServiceProvider methods ***
464 STDMETHOD(QueryService)(REFGUID guidService, REFIID riid, void **ppvObject) override;
465
466 // Message handlers
469 LRESULT OnDestroy(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled);
475 LRESULT OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled);
477 LRESULT OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled);
481 LRESULT OnCommand(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled);
482 LRESULT OnNotify(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled);
488
489 virtual VOID OnFinalMessage(HWND) override;
490
492 {
493 static ATL::CWndClassInfo wc =
494 {
496 0, 0, NULL, NULL,
497 LoadCursor(NULL, IDC_ARROW), NULL, NULL, L"SHELLDLL_DefView", NULL
498 },
499 NULL, NULL, IDC_ARROW, TRUE, 0, _T("")
500 };
501 return wc;
502 }
503
505 {
506 return WindowProc;
507 }
508
510 {
511 CDefView *pThis;
513
514 // Must hold a reference during message handling
515 pThis = reinterpret_cast<CDefView *>(hWnd);
516 pThis->AddRef();
518 pThis->Release();
519 return result;
520 }
521
546
548 // Windows returns E_NOINTERFACE for IOleWindow
549 // COM_INTERFACE_ENTRY_IID(IID_IOleWindow, IOleWindow)
550 COM_INTERFACE_ENTRY_IID(IID_IShellView, IShellView)
552 COM_INTERFACE_ENTRY_IID(IID_IShellView2, IShellView2)
553 COM_INTERFACE_ENTRY_IID(IID_IShellView3, IShellView3)
554 COM_INTERFACE_ENTRY_IID(IID_IFolderView, IFolderView)
555 COM_INTERFACE_ENTRY_IID(IID_IShellFolderView, IShellFolderView)
556 COM_INTERFACE_ENTRY_IID(IID_IOleCommandTarget, IOleCommandTarget)
557 COM_INTERFACE_ENTRY_IID(IID_IDropTarget, IDropTarget)
558 COM_INTERFACE_ENTRY_IID(IID_IDropSource, IDropSource)
560 COM_INTERFACE_ENTRY_IID(IID_IServiceProvider, IServiceProvider)
562};
563
564#define ID_LISTVIEW 1
565
566// windowsx.h
567#define GET_WM_COMMAND_ID(wp, lp) LOWORD(wp)
568#define GET_WM_COMMAND_HWND(wp, lp) (HWND)(lp)
569#define GET_WM_COMMAND_CMD(wp, lp) HIWORD(wp)
570
572
574 m_ListView(),
576 m_hMenu(NULL),
581 m_uState(0),
582 m_cidl(0),
583 m_apidl(NULL),
586 m_hNotify(0),
587 m_hAccel(NULL),
588 m_dwAspects(0),
589 m_dwAdvf(0),
596{
603
607}
608
610{
611 TRACE(" destroying IShellView(%p)\n", this);
612
614
616 {
619 }
620
621 if (m_hWnd)
622 {
624 }
625
629}
630
632{
633 m_pSFParent = shellFolder;
635 shellFolder->QueryInterface(IID_PPV_ARG(IShellDetails, &m_pSDParent));
636
637 return S_OK;
638}
639
640// ##### helperfunctions for communication with ICommDlgBrowser #####
641
643{
644 HRESULT ret = S_OK;
645 if (m_pCommDlgBrowser && !(GetCommDlgViewFlags() & CDB2GVF_NOINCLUDEITEM))
646 {
647 TRACE("ICommDlgBrowser::IncludeObject pidl=%p\n", pidl);
648 ret = m_pCommDlgBrowser->IncludeObject(this, pidl);
649 TRACE("-- returns 0x%08x\n", ret);
650 }
651 else if (m_pFolderFilter)
652 {
653 ret = m_pFolderFilter->ShouldShow(m_pSFParent, m_pidlParent, pidl);
654 }
655 return ret;
656}
657
659{
661
662 if (m_pCommDlgBrowser.p != NULL)
663 {
664 TRACE("ICommDlgBrowser::OnDefaultCommand\n");
665 ret = m_pCommDlgBrowser->OnDefaultCommand(this);
666 TRACE("-- returns 0x%08x\n", ret);
667 }
668
669 return ret;
670}
671
673{
675
676 if (m_pCommDlgBrowser.p != NULL)
677 {
678 TRACE("ICommDlgBrowser::OnStateChange flags=%x\n", uFlags);
679 ret = m_pCommDlgBrowser->OnStateChange(this, uFlags);
680 TRACE("--\n");
681 }
682
683 return ret;
684}
685/**********************************************************
686 * set the toolbar of the filedialog buttons
687 *
688 * - activates the buttons from the shellbrowser according to
689 * the view state
690 */
692{
694
695 TRACE("\n");
696
697 if (m_pCommDlgBrowser != NULL)
698 {
699 m_pShellBrowser->SendControlMsg(FCW_TOOLBAR, TB_CHECKBUTTON,
701 m_pShellBrowser->SendControlMsg(FCW_TOOLBAR, TB_CHECKBUTTON,
703 m_pShellBrowser->SendControlMsg(FCW_TOOLBAR, TB_ENABLEBUTTON,
705 m_pShellBrowser->SendControlMsg(FCW_TOOLBAR, TB_ENABLEBUTTON,
707 }
708}
709
711{
712 WCHAR szFormat[MAX_PATH] = {0};
713 WCHAR szPartText[MAX_PATH] = {0};
714 UINT cSelectedItems;
715
716 if (!m_ListView)
717 return;
718
719 cSelectedItems = m_ListView.GetSelectedCount();
720 if (cSelectedItems)
721 {
723 StringCchPrintfW(szPartText, _countof(szPartText), szFormat, cSelectedItems);
724 }
725 else
726 {
727 LoadStringW(shell32_hInstance, IDS_OBJECTS, szFormat, _countof(szFormat));
728 StringCchPrintfW(szPartText, _countof(szPartText), szFormat, m_ListView.GetItemCount());
729 }
730
731 LRESULT lResult;
732 m_pShellBrowser->SendControlMsg(FCW_STATUS, SB_SETTEXT, 0, (LPARAM)szPartText, &lResult);
733
734 // Don't bother with the extra processing if we only have one StatusBar part
736 {
737 UINT64 uTotalFileSize = 0;
738 WORD uFileFlags = LVNI_ALL;
739 LPARAM pIcon = NULL;
740 INT nItem = -1;
741 bool bIsOnlyFoldersSelected = true;
742
743 // If we have something selected then only count selected file sizes
744 if (cSelectedItems)
745 {
746 uFileFlags = LVNI_SELECTED;
747 }
748
749 while ((nItem = m_ListView.GetNextItem(nItem, uFileFlags)) >= 0)
750 {
751 PCUITEMID_CHILD pidl = _PidlByItem(nItem);
752
753 uTotalFileSize += _ILGetFileSize(pidl, NULL, 0);
754
755 if (!_ILIsFolder(pidl))
756 {
757 bIsOnlyFoldersSelected = false;
758 }
759 }
760
761 // Don't show the file size text if there is 0 bytes in the folder
762 // OR we only have folders selected
763 if ((cSelectedItems && !bIsOnlyFoldersSelected) || uTotalFileSize)
764 {
765 StrFormatByteSizeW(uTotalFileSize, szPartText, _countof(szPartText));
766 }
767 else
768 {
769 *szPartText = 0;
770 }
771
772 m_pShellBrowser->SendControlMsg(FCW_STATUS, SB_SETTEXT, 1, (LPARAM)szPartText, &lResult);
773
774 // If we are in a Recycle Bin then show no text for the location part
776 {
777 LoadStringW(shell32_hInstance, IDS_MYCOMPUTER, szPartText, _countof(szPartText));
778 pIcon = (LPARAM)m_hMyComputerIcon;
779 }
780
781 m_pShellBrowser->SendControlMsg(FCW_STATUS, SB_SETICON, 2, pIcon, &lResult);
782 m_pShellBrowser->SendControlMsg(FCW_STATUS, SB_SETTEXT, 2, (LPARAM)szPartText, &lResult);
783 }
784
785 SFGAOF att = 0;
786 if (cSelectedItems > 0)
787 {
788 UINT maxquery = 42; // Checking the attributes can be slow, only check small selections (_DoCopyToMoveToFolder will verify the full array)
789 att = SFGAO_CANCOPY | SFGAO_CANMOVE;
790 if (cSelectedItems <= maxquery && (!GetSelections() || FAILED(m_pSFParent->GetAttributesOf(m_cidl, m_apidl, &att))))
791 att = 0;
792 }
793 m_pShellBrowser->SendControlMsg(FCW_TOOLBAR, TB_ENABLEBUTTON, FCIDM_SHVIEW_COPYTO, (att & SFGAO_CANCOPY) != 0, &lResult);
794 m_pShellBrowser->SendControlMsg(FCW_TOOLBAR, TB_ENABLEBUTTON, FCIDM_SHVIEW_MOVETO, (att & SFGAO_CANMOVE) != 0, &lResult);
795}
796
798{
801 return 0;
802}
803
804
805// ##### helperfunctions for initializing the view #####
806
807// creates the list view window
809{
810 HRESULT hr;
811 DWORD dwStyle, dwExStyle, ListExStyle;
812 UINT ViewMode;
813
814 TRACE("%p\n", this);
815
817 LVS_SHAREIMAGELISTS | LVS_EDITLABELS | LVS_AUTOARRANGE; // FIXME: Remove LVS_AUTOARRANGE when the view is able to save ItemPos
819 ListExStyle = LVS_EX_INFOTIP | LVS_EX_LABELTIP;
820
822 {
824 dwStyle |= LVS_ALIGNLEFT;
825 // LVS_EX_REGIONAL?
826 }
827 else
828 {
829 dwStyle |= LVS_SHOWSELALWAYS; // MSDN says FWF_SHOWSELALWAYS is deprecated, always turn on for folders
831#if 0 // FIXME: Temporarily disabled until ListView is fixed (CORE-19624, CORE-19818)
832 ListExStyle |= LVS_EX_DOUBLEBUFFER;
833#endif
834 }
835
836 ViewMode = m_FolderSettings.ViewMode;
837 hr = _DoFolderViewCB(SFVM_DEFVIEWMODE, 0, (LPARAM)&ViewMode);
838 if (SUCCEEDED(hr))
839 {
840 if (ViewMode >= FVM_FIRST && ViewMode <= FVM_LAST)
841 m_FolderSettings.ViewMode = ViewMode;
842 else
843 ERR("Ignoring invalid ViewMode from SFVM_DEFVIEWMODE: %u (was: %u)\n", ViewMode, m_FolderSettings.ViewMode);
844 }
845
847 {
848 case FVM_ICON:
849 dwStyle |= LVS_ICON;
850 break;
851 case FVM_DETAILS:
852 dwStyle |= LVS_REPORT;
853 break;
854 case FVM_SMALLICON:
855 dwStyle |= LVS_SMALLICON;
856 break;
857 case FVM_LIST:
858 dwStyle |= LVS_LIST;
859 break;
860 default:
861 dwStyle |= LVS_LIST;
862 break;
863 }
864
866 dwStyle |= LVS_AUTOARRANGE;
867
869 ListExStyle |= LVS_EX_SNAPTOGRID;
870
872 dwStyle |= LVS_SINGLESEL;
873
875 ListExStyle |= LVS_EX_FULLROWSELECT;
876
878 (!SHELL_GetSetting(SSF_DOUBLECLICKINWEBVIEW, fDoubleClickInWebView) && !SHELL_GetSetting(SSF_WIN95CLASSIC, fWin95Classic)))
880
882 dwStyle |= LVS_NOCOLUMNHEADER;
883
884#if 0
885 // FIXME: Because this is a negative, everyone gets the new flag by default unless they
886 // opt out. This code should be enabled when shell looks like Vista instead of 2003
888 ListExStyle |= LVS_EX_HEADERINALLVIEWS;
889#endif
890
892 dwExStyle &= ~WS_EX_CLIENTEDGE;
893
894 RECT rcListView = {0,0,0,0};
895 m_ListView.Create(m_hWnd, rcListView, L"FolderView", dwStyle, dwExStyle, ID_LISTVIEW);
896
897 if (!m_ListView)
898 return FALSE;
899
901
902 /* UpdateShellSettings(); */
903 return TRUE;
904}
905
907{
909 {
910 /* Check if drop shadows option is enabled */
911 BOOL bDropShadow = FALSE;
912 DWORD cbDropShadow = sizeof(bDropShadow);
913
914 /*
915 * The desktop ListView always take the default desktop colours, by
916 * remaining transparent and letting user32/win32k paint itself the
917 * desktop background color, if any.
918 */
920
921 SHGetValueW(HKEY_CURRENT_USER, L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Advanced",
922 L"ListviewShadow", NULL, &bDropShadow, &cbDropShadow);
923 if (bDropShadow)
924 {
925 /* Set the icon background transparent */
927 m_ListView.SetTextColor(RGB(255, 255, 255));
928 m_ListView.SetExtendedListViewStyle(LVS_EX_TRANSPARENTSHADOWTEXT, LVS_EX_TRANSPARENTSHADOWTEXT);
929 }
930 else
931 {
932 /* Set the icon background as the same colour as the desktop */
934 m_ListView.SetTextBkColor(crDesktop);
935 if (GetRValue(crDesktop) + GetGValue(crDesktop) + GetBValue(crDesktop) > 128 * 3)
936 m_ListView.SetTextColor(RGB(0, 0, 0));
937 else
938 m_ListView.SetTextColor(RGB(255, 255, 255));
939 m_ListView.SetExtendedListViewStyle(0, LVS_EX_TRANSPARENTSHADOWTEXT);
940 }
941 }
942 else
943 {
946
947 // Background is painted by the parent via WM_PRINTCLIENT
948 m_ListView.SetExtendedListViewStyle(LVS_EX_TRANSPARENTBKGND, LVS_EX_TRANSPARENTBKGND);
949 }
950}
951
952// adds all needed columns to the shellview
954{
955 HIMAGELIST big_icons, small_icons;
956
957 TRACE("%p\n", this);
958
960
961 Shell_GetImageLists(&big_icons, &small_icons);
963 m_ListView.SetImageList(small_icons, LVSIL_SMALL);
964
966
968 UINT ColumnCount = pColumns ? DPA_GetPtrCount(m_LoadColumnsList) : 0;
969 LoadColumns(pColumns, ColumnCount);
971 {
975 }
976 return TRUE;
977}
978
979/**********************************************************
980* Column handling
981*/
983{
984 LVCOLUMN lvc;
985 lvc.mask = LVCF_SUBITEM;
986 if (!ListView_GetColumn(List, Col, &lvc))
987 return E_FAIL;
988 else
989 return lvc.iSubItem;
990}
991
993{
994 // This function is only called during column management, performance is not critical.
995 for (UINT i = 0;; ++i)
996 {
998 if ((UINT)r == FoldCol)
999 return i;
1000 else if (FAILED(r))
1001 return r;
1002 }
1003}
1004
1006{
1007 // This function is called every time a LVITEM::iSubItem is mapped to
1008 // a folder column (calls to GetDetailsOf etc.) and should be fast.
1010 {
1012 if (ListCol < count)
1013 {
1015 assert(col >= 0 && col == SHGetLVColumnSubItem(m_ListView.m_hWnd, ListCol));
1016 return col;
1017 }
1018 else if (count)
1019 {
1020 TRACE("m_ListToFolderColMap cache miss while mapping %d\n", ListCol);
1021 }
1022 }
1023 return SHGetLVColumnSubItem(m_ListView.m_hWnd, ListCol);
1024}
1025
1027{
1028 // According to learn.microsoft.com/en-us/windows/win32/shell/sfvm-getdetailsof
1029 // the query order is IShellFolder2, IShellDetails, SFVM_GETDETAILSOF.
1030 HRESULT hr;
1031 if (m_pSF2Parent)
1032 {
1033 hr = m_pSF2Parent->GetDetailsOf(pidl, FoldCol, &sd);
1034 }
1035 if (FAILED(hr) && m_pSDParent)
1036 {
1037 hr = m_pSDParent->GetDetailsOf(pidl, FoldCol, &sd);
1038 }
1039#if 0 // TODO
1040 if (FAILED(hr))
1041 {
1042 FIXME("Try SFVM_GETDETAILSOF\n");
1043 }
1044#endif
1045 return hr;
1046}
1047
1049{
1051 if (SUCCEEDED(hr))
1052 return GetDetailsByFolderColumn(pidl, hr, sd);
1053 ERR("Unable to determine folder column from list column %d\n", (int) ListCol);
1054 return hr;
1055}
1056
1057HRESULT CDefView::LoadColumn(UINT FoldCol, UINT ListCol, BOOL Insert, UINT ForceWidth)
1058{
1061 HRESULT hr;
1062
1063 sd.str.uType = !STRRET_WSTR; // Make sure "uninitialized" uType is not WSTR
1064 hr = GetDetailsByFolderColumn(NULL, FoldCol, sd);
1065 if (FAILED(hr))
1066 return hr;
1067 hr = StrRetToStrNW(buf, _countof(buf), &sd.str, NULL);
1068 if (FAILED(hr))
1069 return hr;
1070
1071 UINT chavewidth = CalculateCharWidth(m_ListView.m_hWnd);
1072 if (!chavewidth)
1073 chavewidth = 6; // 6 is a reasonable default fallback
1074
1075 LVCOLUMN lvc;
1076 lvc.mask = LVCF_TEXT | LVCF_FMT | LVCF_WIDTH | LVCF_SUBITEM;
1077 lvc.pszText = buf;
1078 lvc.fmt = sd.fmt;
1079 lvc.cx = ForceWidth ? ForceWidth : (sd.cxChar * chavewidth); // FIXME: DPI?
1080 lvc.iSubItem = FoldCol; // Used by MapFolderColumnToListColumn & MapListColumnToFolderColumn
1081 if ((int)ListCol == -1)
1082 {
1083 assert(Insert); // You can insert at the end but you can't change something that is not there
1084 if (Insert)
1085 ListCol = 0x7fffffff;
1086 }
1087 if (Insert)
1088 ListView_InsertColumn(m_ListView.m_hWnd, ListCol, &lvc);
1089 else
1090 ListView_SetColumn(m_ListView.m_hWnd, ListCol, &lvc);
1091 return S_OK;
1092}
1093
1095{
1096 HWND hWndHdr = ListView_GetHeader(m_ListView.m_hWnd);
1097 UINT newColCount = 0, oldColCount = Header_GetItemCount(hWndHdr);
1098 UINT width = 0, foldCol, i;
1099 HRESULT hr = S_FALSE;
1100
1102 for (i = 0, foldCol = 0;; ++foldCol)
1103 {
1104 if (newColCount >= 0xffff)
1105 break; // CompareIDs limit reached
1106
1107 if (pColList)
1108 {
1109 if (i >= ColListCount)
1110 break;
1111 width = HIWORD(pColList[i]);
1112 foldCol = LOWORD(pColList[i++]);
1113 }
1114
1115 SHCOLSTATEF state = 0;
1116 if (!m_pSF2Parent || FAILED(m_pSF2Parent->GetDefaultColumnState(foldCol, &state)))
1118
1119 if (foldCol == 0)
1120 {
1121 // Force the first column
1122 }
1123 else if (state & SHCOLSTATE_HIDDEN)
1124 {
1125 continue;
1126 }
1127 else if (!pColList && !(state & SHCOLSTATE_ONBYDEFAULT))
1128 {
1129 continue;
1130 }
1131
1132 bool insert = newColCount >= oldColCount;
1133 UINT listCol = insert ? -1 : newColCount;
1134 hr = LoadColumn(foldCol, listCol, insert, width);
1135 if (FAILED(hr))
1136 {
1137 if (!pColList)
1138 hr = S_OK; // No more items, we are done
1139 break;
1140 }
1141 ++newColCount;
1142 }
1143 for (i = newColCount; i < oldColCount; ++i)
1144 {
1146 }
1147
1150 assert(SUCCEEDED(MapFolderColumnToListColumn(0))); // We don't allow turning off the Name column
1152 {
1155 }
1156 return hr;
1157}
1158
1160{
1162 m_ListToFolderColMap = NULL; // No cache while we are building the cache
1164 for (UINT i = 0;; ++i)
1165 {
1167 if (FAILED(hr))
1168 break; // No more columns
1169 if (!DPA_SetPtr(cache, i, (void*)(INT_PTR) hr))
1170 break; // Cannot allow holes in the cache, must stop now.
1171 }
1173
1174 for (;;)
1175 {
1177 break;
1178 }
1180 if (hMenu)
1181 {
1182 hMenu = GetSubmenuByID(hMenu, FCIDM_SHVIEW_ARRANGE);
1183 for (UINT i = DVIDM_ARRANGESORT_FIRST; i <= DVIDM_ARRANGESORT_LAST && hMenu; ++i)
1184 {
1185 RemoveMenu(hMenu, i, MF_BYCOMMAND);
1186 }
1187 if ((int) GetMenuItemID(hMenu, 0) <= 0)
1188 RemoveMenu(hMenu, 0, MF_BYPOSITION); // Separator
1189 }
1191 LVCOLUMN lvc;
1192 lvc.mask = LVCF_TEXT;
1193 lvc.pszText = buf;
1194 lvc.cchTextMax = _countof(buf);
1195 for (UINT listCol = 0; listCol < DEFVIEW_ARRANGESORT_MAXENUM; ++listCol)
1196 {
1197 if (!ListView_GetColumn(m_ListView.m_hWnd, listCol, &lvc))
1198 break;
1199 HRESULT foldCol = MapListColumnToFolderColumn(listCol);
1200 assert(SUCCEEDED(foldCol));
1202 DVIDM_ARRANGESORT_FIRST + listCol, lvc.pszText, listCol);
1203 }
1204
1205 ListView_RedrawItems(m_ListView.m_hWnd, 0, 0x7fffffff);
1206 m_ListView.InvalidateRect(NULL, TRUE);
1207}
1208
1209/*************************************************************************
1210 * ShellView_ListViewCompareItems
1211 *
1212 * Compare Function for the Listview (FileOpen Dialog)
1213 *
1214 * PARAMS
1215 * lParam1 [I] the first ItemIdList to compare with
1216 * lParam2 [I] the second ItemIdList to compare with
1217 * lpData [I] The column ID for the header Ctrl to process
1218 *
1219 * RETURNS
1220 * A negative value if the first item should precede the second,
1221 * a positive value if the first item should follow the second,
1222 * or zero if the two items are equivalent
1223 */
1225{
1226 PCUIDLIST_RELATIVE pidl1 = reinterpret_cast<PCUIDLIST_RELATIVE>(lParam1);
1227 PCUIDLIST_RELATIVE pidl2 = reinterpret_cast<PCUIDLIST_RELATIVE>(lParam2);
1228 CDefView *pThis = reinterpret_cast<CDefView*>(lpData);
1229
1230 HRESULT hres = pThis->m_pSFParent->CompareIDs(pThis->m_sortInfo.ListColumn, pidl1, pidl2);
1232 return 0;
1233
1234 SHORT nDiff = HRESULT_CODE(hres);
1235 return nDiff * pThis->m_sortInfo.Direction;
1236}
1237
1239{
1240 HWND hHeader;
1241 HDITEM hColumn;
1242 int prevCol = m_sortInfo.ListColumn;
1244
1245 // FIXME: Is this correct? Who sets this style?
1246 // And if it is set, should it also block sorting using the menu?
1247 // Any why should it block sorting when the view is loaded initially?
1248 if (m_ListView.GetWindowLongPtr(GWL_STYLE) & LVS_NOSORTHEADER)
1249 return TRUE;
1250
1251 hHeader = ListView_GetHeader(m_ListView.m_hWnd);
1252 if (Col != -1)
1253 {
1254 if (Col >= Header_GetItemCount(hHeader))
1255 {
1256 ERR("Sort column out of range\n");
1257 return FALSE;
1258 }
1259
1260 if (prevCol == Col)
1261 m_sortInfo.Direction *= -1;
1262 else
1264 m_sortInfo.ListColumn = Col;
1265 }
1266 if (!m_sortInfo.Direction)
1267 m_sortInfo.Direction += 1;
1268
1269 /* If the sorting column changed, remove the sorting style from the old column */
1270 if (prevCol != -1 && prevCol != m_sortInfo.ListColumn)
1271 {
1272 hColumn.mask = HDI_FORMAT;
1273 Header_GetItem(hHeader, prevCol, &hColumn);
1274 hColumn.fmt &= ~(HDF_SORTUP | HDF_SORTDOWN);
1275 Header_SetItem(hHeader, prevCol, &hColumn);
1276 }
1277
1278 /* Set the sorting style on the new column */
1279 hColumn.mask = HDI_FORMAT;
1280 Header_GetItem(hHeader, m_sortInfo.ListColumn, &hColumn);
1281 hColumn.fmt &= ~(HDF_SORTDOWN | HDF_SORTUP);
1282 hColumn.fmt |= (m_sortInfo.Direction > 0 ? HDF_SORTUP : HDF_SORTDOWN);
1283 Header_SetItem(hHeader, m_sortInfo.ListColumn, &hColumn);
1284
1285 /* Sort the list, using the current values of ListColumn and bIsAscending */
1288}
1289
1291{
1292 if (!m_ListView)
1293 return nullptr;
1294 return reinterpret_cast<PCUITEMID_CHILD>(m_ListView.GetItemData(i));
1295}
1296
1298{
1299 if (!m_ListView)
1300 return nullptr;
1301 return reinterpret_cast<PCUITEMID_CHILD>(lvItem.lParam);
1302}
1303
1305{
1307
1308 int cItems = m_ListView.GetItemCount();
1310 for (int i = 0; i < cItems; i++)
1311 {
1312 PCUITEMID_CHILD currentpidl = _PidlByItem(i);
1313 HRESULT hr = m_pSFParent->CompareIDs(lParam, pidl, currentpidl);
1314 if (SUCCEEDED(hr))
1315 {
1316 if (hr == S_EQUAL)
1317 return i;
1318 }
1319 else
1320 {
1321 for (i = 0; i < cItems; i++)
1322 {
1323 //FIXME: ILIsEqual needs absolute pidls!
1324 currentpidl = _PidlByItem(i);
1325 if (ILIsEqual(pidl, currentpidl))
1326 return i;
1327 }
1328 break;
1329 }
1330 }
1331 return -1;
1332}
1333
1335{
1336 LVITEMW lvItem;
1337
1338 TRACE("(%p)(pidl=%p)\n", this, pidl);
1339
1341
1343 return -1;
1344
1345 lvItem.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM; // set mask
1346 lvItem.iItem = m_ListView.GetItemCount(); // add item to lists end
1347 lvItem.iSubItem = 0;
1348 lvItem.lParam = reinterpret_cast<LPARAM>(ILClone(pidl)); // set item's data
1349 lvItem.pszText = LPSTR_TEXTCALLBACKW; // get text on a callback basis
1350 lvItem.iImage = I_IMAGECALLBACK; // get image on a callback basis
1351 lvItem.stateMask = LVIS_CUT;
1352
1353 return m_ListView.InsertItem(&lvItem);
1354}
1355
1357{
1358 int nIndex;
1359
1360 TRACE("(%p)(pidl=%p)\n", this, pidl);
1361
1363
1364 nIndex = LV_FindItemByPidl(pidl);
1365 if (nIndex < 0)
1366 return FALSE;
1367
1369
1370 return m_ListView.DeleteItem(nIndex);
1371}
1372
1374{
1375 int nItem;
1376 LVITEMW lvItem;
1377
1378 TRACE("(%p)(pidlold=%p pidlnew=%p)\n", this, pidlOld, pidlNew);
1379
1381
1382 nItem = LV_FindItemByPidl(pidlOld);
1383
1384 if (-1 != nItem)
1385 {
1386 lvItem.mask = LVIF_PARAM; // only the pidl
1387 lvItem.iItem = nItem;
1388 lvItem.iSubItem = 0;
1389 m_ListView.GetItem(&lvItem);
1390
1391 // Store old pidl until new item is replaced
1392 LPVOID oldPidl = reinterpret_cast<LPVOID>(lvItem.lParam);
1393
1394 lvItem.mask = LVIF_PARAM | LVIF_IMAGE | LVIF_TEXT;
1395 lvItem.iItem = nItem;
1396 lvItem.iSubItem = 0;
1397 lvItem.lParam = reinterpret_cast<LPARAM>(ILClone(pidlNew)); // set item's data
1400 m_ListView.SetItem(&lvItem);
1401 m_ListView.Update(nItem);
1402
1403 // Now that the new item is in place, we can safely release the old pidl
1404 SHFree(oldPidl);
1405
1406 return TRUE; // FIXME: better handling
1407 }
1408
1409 return FALSE;
1410}
1411
1413{
1414 int nItem;
1415 LVITEMW lvItem;
1416
1417 TRACE("(%p)(pidl=%p)\n", this, pidl);
1418
1420
1421 nItem = LV_FindItemByPidl(pidl);
1422
1423 if (-1 != nItem)
1424 {
1426
1427 lvItem.mask = LVIF_IMAGE;
1428 lvItem.iItem = nItem;
1429 lvItem.iSubItem = 0;
1431 m_ListView.SetItem(&lvItem);
1432 m_ListView.Update(nItem);
1433 return TRUE;
1434 }
1435
1436 return FALSE;
1437}
1438
1440{
1442
1443 LVITEMW lvItem = { LVIF_IMAGE };
1444 lvItem.iItem = iItem;
1445 lvItem.iImage = I_IMAGECALLBACK;
1446 m_ListView.SetItem(&lvItem);
1447 m_ListView.Update(iItem);
1448}
1449
1451{
1453
1454 for (INT iItem = -1;;)
1455 {
1456 iItem = ListView_GetNextItem(m_ListView, iItem, LVNI_ALL);
1457 if (iItem == -1)
1458 break;
1459
1460 LV_RefreshIcon(iItem);
1461 }
1462}
1463
1465{
1466 PITEMID_CHILD pidl = static_cast<PITEMID_CHILD>(ptr);
1467 CDefView *pThis = static_cast<CDefView *>(arg);
1468
1469 // in a commdlg this works as a filemask
1470 if (pThis->IncludeObject(pidl) == S_OK && pThis->m_ListView)
1471 pThis->LV_AddItem(pidl);
1472
1473 SHFree(pidl);
1474 return TRUE;
1475}
1476
1478// - gets the objectlist from the shellfolder
1479// - sorts the list
1480// - fills the list into the view
1482{
1483 CComPtr<IEnumIDList> pEnumIDList;
1484 PITEMID_CHILD pidl;
1485 DWORD dwFetched;
1486 HRESULT hRes;
1487 HDPA hdpa;
1488 DWORD dFlags = SHCONTF_NONFOLDERS | ((m_FolderSettings.fFlags & FWF_NOSUBFOLDERS) ? 0 : SHCONTF_FOLDERS);
1489
1490 TRACE("%p\n", this);
1491
1492 SHELLSTATE shellstate;
1494 if (GetCommDlgViewFlags() & CDB2GVF_SHOWALLFILES)
1495 shellstate.fShowAllObjects = shellstate.fShowSuperHidden = TRUE;
1496
1497 if (shellstate.fShowAllObjects)
1498 {
1499 dFlags |= SHCONTF_INCLUDEHIDDEN;
1500 m_ListView.SendMessageW(LVM_SETCALLBACKMASK, LVIS_CUT, 0);
1501 }
1502 if (shellstate.fShowSuperHidden)
1503 {
1504 dFlags |= SHCONTF_INCLUDESUPERHIDDEN;
1505 m_ListView.SendMessageW(LVM_SETCALLBACKMASK, LVIS_CUT, 0);
1506 }
1507
1508 // get the itemlist from the shfolder
1509 hRes = m_pSFParent->EnumObjects(m_hWnd, dFlags, &pEnumIDList);
1510 if (hRes != S_OK)
1511 {
1512 if (hRes == S_FALSE)
1513 return(NOERROR);
1514 return(hRes);
1515 }
1516
1517 // create a pointer array
1518 hdpa = DPA_Create(16);
1519 if (!hdpa)
1520 return(E_OUTOFMEMORY);
1521
1522 // copy the items into the array
1523 while((S_OK == pEnumIDList->Next(1, &pidl, &dwFetched)) && dwFetched)
1524 {
1525 if (DPA_InsertPtr(hdpa, 0x7fff, pidl) == -1)
1526 {
1527 SHFree(pidl);
1528 }
1529 }
1530
1531 // turn listview's redrawing off
1533
1534 DPA_DestroyCallback( hdpa, fill_list, this);
1535
1536 /* sort the array */
1537 int sortCol = -1;
1538 if (!IsRefreshCommand && !m_sortInfo.bLoadedFromViewState) // Are we loading for the first time?
1539 {
1541 sortCol = 0; // In case the folder does not know/care
1542 if (m_pSF2Parent)
1543 {
1544 ULONG folderSortCol = sortCol, dummy;
1545 HRESULT hr = m_pSF2Parent->GetDefaultColumn(NULL, &folderSortCol, &dummy);
1546 if (SUCCEEDED(hr))
1547 hr = MapFolderColumnToListColumn(folderSortCol);
1548 if (SUCCEEDED(hr))
1549 sortCol = (int) hr;
1550 }
1551 }
1552 _Sort(sortCol);
1553
1555 {
1558 }
1559
1560 // load custom background image and custom text color
1563
1564 // turn listview's redrawing back on and force it to draw
1566
1568
1570 {
1571 // redraw now
1572 m_ListView.InvalidateRect(NULL, TRUE);
1573 }
1574
1576
1577 return S_OK;
1578}
1579
1581{
1582 if (m_ListView.IsWindow())
1583 m_ListView.UpdateWindow();
1584 bHandled = FALSE;
1585 return 0;
1586}
1587
1589{
1590 return m_ListView.SendMessageW(uMsg, 0, 0);
1591}
1592
1594{
1595 if (!m_Destroyed)
1596 {
1597 m_Destroyed = TRUE;
1600 if (m_pFileMenu)
1601 {
1603 m_pFileMenu = NULL;
1604 }
1605 if (m_hMenu)
1606 {
1608 m_hMenu = NULL;
1609 }
1610 m_hNotify = NULL;
1613 }
1614 bHandled = FALSE;
1615 return 0;
1616}
1617
1619{
1620 /* redirect to parent */
1623
1624 bHandled = FALSE;
1625 return 0;
1626}
1627
1628static VOID
1630{
1631 INT x0 = prc->left, y0 = prc->top, x1 = prc->right, y1 = prc->bottom;
1632 x0 += dx;
1633 y0 += dy;
1634
1635 HDC hMemDC = CreateCompatibleDC(hDC);
1636 HGDIOBJ hbmOld = SelectObject(hMemDC, hbm);
1637
1638 for (INT y = y0; y < y1; y += nHeight)
1639 {
1640 for (INT x = x0; x < x1; x += nWidth)
1641 {
1642 BitBlt(hDC, x, y, nWidth, nHeight, hMemDC, 0, 0, SRCCOPY);
1643 }
1644 }
1645
1646 SelectObject(hMemDC, hbmOld);
1647 DeleteDC(hMemDC);
1648}
1649
1651{
1652 HDC hDC = (HDC)wParam;
1653
1654 RECT rc;
1656
1658 {
1659 BITMAP bm;
1660 if (::GetObject(m_viewinfo_data.hbmBack, sizeof(BITMAP), &bm))
1661 {
1662 INT dx = -(::GetScrollPos(m_ListView, SB_HORZ) % bm.bmWidth);
1663 INT dy = -(::GetScrollPos(m_ListView, SB_VERT) % bm.bmHeight);
1664 DrawTileBitmap(hDC, &rc, m_viewinfo_data.hbmBack, bm.bmWidth, bm.bmHeight, dx, dy);
1665 }
1666 }
1667 else
1668 {
1670 }
1671
1672 bHandled = TRUE;
1673
1674 return TRUE;
1675}
1676
1678{
1679 /* Update desktop labels color */
1681
1682 /* Forward WM_SYSCOLORCHANGE to common controls */
1683 return m_ListView.SendMessageW(uMsg, 0, 0);
1684}
1685
1687{
1688 return reinterpret_cast<LRESULT>(m_pShellBrowser.p);
1689}
1690
1692{
1693 this->AddRef();
1694 bHandled = FALSE;
1695 return 0;
1696}
1697
1699{
1700 this->Release();
1701}
1702
1704{
1707
1708 TRACE("%p\n", this);
1709
1711 {
1712 if (FAILED(RegisterDragDrop(m_hWnd, pdt)))
1713 ERR("Error Registering DragDrop\n");
1714 }
1715
1716 /* register for receiving notifications */
1717 m_pSFParent->QueryInterface(IID_PPV_ARG(IPersistFolder2, &ppf2));
1718 if (ppf2)
1719 {
1720 ppf2->GetCurFolder(&m_pidlParent);
1721 }
1722
1723 if (CreateList())
1724 {
1725 if (InitList())
1726 {
1727 FillList(FALSE);
1728 }
1729 }
1730
1732 {
1733 HWND hwndSB;
1734 m_pShellBrowser->GetWindow(&hwndSB);
1736 }
1737
1738 // Set up change notification
1739 LPITEMIDLIST pidlTarget = NULL;
1740 LONG fEvents = 0;
1741 HRESULT hr = _DoFolderViewCB(SFVM_GETNOTIFY, (WPARAM)&pidlTarget, (LPARAM)&fEvents);
1742 if (FAILED(hr) || (!pidlTarget && !fEvents)) // FIXME: MSDN says both zero means no notifications
1743 {
1744 pidlTarget = m_pidlParent;
1745 fEvents = SHCNE_ALLEVENTS;
1746 }
1747 SHChangeNotifyEntry ntreg = {};
1749 if (FAILED(hr))
1750 {
1751 ntreg.fRecursive = FALSE;
1752 ntreg.pidl = pidlTarget;
1753 }
1757 fEvents, SHV_CHANGE_NOTIFY,
1758 1, &ntreg);
1759
1761
1762 BOOL bPreviousParentSpecial = m_isParentFolderSpecial;
1763
1764 // A folder is special if it is the Desktop folder,
1765 // a network folder, or a Control Panel folder
1768
1769 // Only force StatusBar part refresh if the state
1770 // changed from the previous folder
1771 if (bPreviousParentSpecial != m_isParentFolderSpecial)
1772 {
1773 // This handles changing StatusBar parts
1775 }
1776
1778
1779 return S_OK;
1780}
1781
1782// #### Handling of the menus ####
1783
1785{
1787 if (!hFileMenu)
1788 return E_FAIL;
1789
1790 /* Cleanup the items added previously */
1791 for (int i = GetMenuItemCount(hFileMenu) - 1; i >= 0; i--)
1792 {
1793 UINT id = GetMenuItemID(hFileMenu, i);
1794 if (id < FCIDM_BROWSERFIRST || id > FCIDM_BROWSERLAST)
1795 DeleteMenu(hFileMenu, i, MF_BYPOSITION);
1796 }
1797
1798 // In case we still have this left over, clean it up
1799 if (m_pFileMenu)
1800 {
1803 }
1804 UINT selcount = m_ListView.GetSelectedCount();
1805 // Store context menu in m_pFileMenu and keep it to invoke the selected command later on
1808 return hr;
1809
1811
1812 UINT cmf = GetContextMenuFlags(m_pShellBrowser, SFGAO_CANRENAME);
1813 hr = m_pFileMenu->QueryContextMenu(hmenu, 0, DVIDM_CONTEXTMENU_FIRST, DVIDM_CONTEXTMENU_LAST, cmf);
1815 return hr;
1816
1817 // TODO: filter or something
1818 if (!selcount)
1819 {
1823 }
1824
1825 Shell_MergeMenus(hFileMenu, hmenu, 0, 0, 0xFFFF, MM_ADDSEPARATOR | MM_SUBMENUSHAVEIDS);
1827 return S_OK;
1828}
1829
1831{
1833 if (!hEditMenu)
1834 return E_FAIL;
1835
1836 HMENU hmenuContents = ::LoadMenuW(shell32_hInstance, L"MENU_003");
1837 if (!hmenuContents)
1838 return E_FAIL;
1839
1840 Shell_MergeMenus(hEditMenu, hmenuContents, 0, 0, 0xFFFF, 0);
1841
1842 ::DestroyMenu(hmenuContents);
1843
1844 return S_OK;
1845}
1846
1848{
1850 if (!hViewMenu)
1851 return E_FAIL;
1852
1854 if (!m_hMenuViewModes)
1855 return E_FAIL;
1856
1859
1860 return S_OK;
1861}
1862
1864{
1865 bool forceMerge = false;
1867
1868 // Make sure the column we currently sort by is in the menu
1871 {
1875 LVCOLUMN lvc;
1876 lvc.mask = LVCF_TEXT;
1877 lvc.pszText = buf;
1878 lvc.cchTextMax = _countof(buf);
1879 currentSortId = DVIDM_ARRANGESORT_LAST;
1880 forceMerge = true;
1882 AppendMenuItem(m_hMenuArrangeModes, MF_STRING, currentSortId, lvc.pszText, m_sortInfo.ListColumn);
1883 }
1884
1885 // Prepend the sort-by items unless they are aleady there
1886 if (GetMenuItemID(hmenuArrange, 0) == FCIDM_SHVIEW_AUTOARRANGE || forceMerge)
1887 {
1888 Shell_MergeMenus(hmenuArrange, m_hMenuArrangeModes, 0, 0, 0xFFFF, MM_ADDSEPARATOR);
1889 }
1890
1891 CheckMenuRadioItem(hmenuArrange,
1893 currentSortId, MF_BYCOMMAND);
1894
1896 {
1899 }
1900 else
1901 {
1904
1905 if (GetAutoArrange() == S_OK)
1907 else
1909
1910 if (_GetSnapToGrid() == S_OK)
1912 else
1914 }
1915
1916 return S_OK;
1917}
1918
1920{
1922 {
1923 UINT iItemFirst = FCIDM_SHVIEW_BIGICON;
1924 UINT iItemLast = iItemFirst + FVM_LAST - FVM_FIRST;
1925 UINT iItem = iItemFirst + m_FolderSettings.ViewMode - FVM_FIRST;
1926 CheckMenuRadioItem(hmenuView, iItemFirst, iItemLast, iItem, MF_BYCOMMAND);
1927 }
1928
1929 return S_OK;
1930}
1931
1933{
1934 const UINT maxItems = 15; // Feels about right
1935 const UINT idMore = 0x1337;
1936 UINT idFirst = idMore + 1, idLast = idFirst;
1937 UINT lastValidListCol = 0; // Keep track of where the new column should be inserted
1938 UINT showMore = GetKeyState(VK_SHIFT) < 0;
1940 HWND hWndHdr = ListView_GetHeader(m_ListView.m_hWnd);
1941
1942 if (lParam == ~0)
1943 {
1944 RECT r;
1945 ::GetWindowRect(hWndHdr, &r);
1946 pt.x = r.left + ((r.right - r.left) / 2);
1947 pt.y = r.top + ((r.bottom - r.top) / 2);
1948 }
1949
1950 HMENU hMenu = CreatePopupMenu();
1951 if (!hMenu)
1952 return 0;
1953
1954 for (UINT foldCol = 0;; ++foldCol)
1955 {
1958 sd.str.uType = !STRRET_WSTR;
1959 if (FAILED(GetDetailsByFolderColumn(NULL, foldCol, sd)))
1960 break;
1961 if (FAILED(StrRetToStrNW(buf, _countof(buf), &sd.str, NULL)))
1962 break;
1963
1964 SHCOLSTATEF state = 0;
1965 if (!m_pSF2Parent || FAILED(m_pSF2Parent->GetDefaultColumnState(foldCol, &state)))
1966 state = 0;
1967 showMore |= (state & (SHCOLSTATE_SECONDARYUI));
1968
1969 UINT mf = MF_STRING;
1970 HRESULT listCol = MapFolderColumnToListColumn(foldCol);
1971
1972 if (foldCol == 0)
1973 mf |= MF_CHECKED | MF_GRAYED | MF_DISABLED; // Force column 0
1975 continue;
1976 else if (SUCCEEDED(listCol))
1977 mf |= MF_CHECKED;
1978
1979 if (AppendMenuItem(hMenu, mf, idLast, buf, lastValidListCol + 1))
1980 {
1981 idLast++;
1982 if (SUCCEEDED(listCol))
1983 lastValidListCol = listCol;
1984 }
1985
1986 if (idLast - idFirst == maxItems)
1987 {
1988 showMore++;
1989 break;
1990 }
1991 }
1992
1993 if (showMore)
1994 {
1995#if 0 // TODO
1996 InsertMenuW(hMenu, -1, MF_SEPARATOR, 0, NULL);
1997 InsertMenuW(hMenu, -1, MF_STRING, idMore, L"More...");
1998#endif
1999 }
2000
2001 // A cludge to force the cursor to update so we are not stuck with "size left/right" if
2002 // the right-click was on a column divider.
2004
2005 // Note: Uses the header as the owner so CDefView::OnInitMenuPopup does not mess us up.
2007 pt.x, pt.y, 0, hWndHdr, NULL);
2008 if (idCmd == idMore)
2009 {
2010 FIXME("Open More dialog\n");
2011 }
2012 else if (idCmd)
2013 {
2014 UINT foldCol = idCmd - idFirst;
2015 HRESULT listCol = MapFolderColumnToListColumn(foldCol);
2016 if (SUCCEEDED(listCol))
2017 {
2018 ListView_DeleteColumn(m_ListView.m_hWnd, listCol);
2019 }
2020 else
2021 {
2022 listCol = (UINT) GetMenuItemDataById(hMenu, idCmd);
2023 LoadColumn(foldCol, listCol, TRUE);
2024 }
2026 }
2027 DestroyMenu(hMenu);
2028 return 0;
2029}
2030
2031/**********************************************************
2032* ShellView_GetSelections()
2033*
2034* - fills the m_apidl list with the selected objects
2035*
2036* RETURNS
2037* number of selected items
2038*/
2040{
2042 if (count > m_cidl || !count || !m_apidl) // !count to free possibly large cache, !m_apidl to make sure m_apidl is a valid pointer
2043 {
2044 SHFree(m_apidl);
2045 m_apidl = static_cast<PCUITEMID_CHILD*>(SHAlloc(count * sizeof(PCUITEMID_CHILD)));
2046 if (!m_apidl)
2047 {
2048 m_cidl = 0;
2049 return 0;
2050 }
2051 }
2052 m_cidl = count;
2053
2054 TRACE("-- Items selected =%u\n", m_cidl);
2055
2057
2058 UINT i = 0;
2059 int lvIndex = -1;
2060 while ((lvIndex = m_ListView.GetNextItem(lvIndex, LVNI_SELECTED)) > -1)
2061 {
2062 m_apidl[i] = _PidlByItem(lvIndex);
2063 i++;
2064 if (i == m_cidl)
2065 break;
2066 TRACE("-- selected Item found\n");
2067 }
2068
2069 return m_cidl;
2070}
2071
2073{
2074 CMINVOKECOMMANDINFOEX cmi;
2075
2076 ZeroMemory(&cmi, sizeof(cmi));
2077 cmi.cbSize = sizeof(cmi);
2078 cmi.hwnd = m_hWnd;
2079 cmi.lpVerb = lpVerb;
2080 cmi.nShow = SW_SHOW;
2081
2082 if (GetKeyState(VK_SHIFT) < 0)
2083 cmi.fMask |= CMIC_MASK_SHIFT_DOWN;
2084
2086 cmi.fMask |= CMIC_MASK_CONTROL_DOWN;
2087
2088 if (pt)
2089 {
2090 cmi.fMask |= CMIC_MASK_PTINVOKE;
2091 cmi.ptInvoke = *pt;
2092 }
2093
2094 WCHAR szDirW[MAX_PATH] = L"";
2095 CHAR szDirA[MAX_PATH];
2097 *szDirW != UNICODE_NULL)
2098 {
2099 SHUnicodeToAnsi(szDirW, szDirA, _countof(szDirA));
2100 cmi.fMask |= CMIC_MASK_UNICODE;
2101 cmi.lpDirectory = szDirA;
2102 cmi.lpDirectoryW = szDirW;
2103 }
2104
2105 HRESULT hr = pCM->InvokeCommand((LPCMINVOKECOMMANDINFO)&cmi);
2106 // Most of our callers will do this, but if they would forget (File menu!)
2107 IUnknown_SetSite(pCM, NULL);
2108 pCM.Release();
2109
2111 return hr;
2112
2113 return S_OK;
2114}
2115
2117{
2118 HMENU hMenu;
2119 UINT uCommand;
2120 HRESULT hResult;
2121
2122 if (m_ListView.GetSelectedCount() == 0)
2123 return S_OK;
2124
2125 hResult = OnDefaultCommand();
2126 if (hResult == S_OK)
2127 return hResult;
2128
2129 hMenu = CreatePopupMenu();
2130 if (!hMenu)
2131 return E_FAIL;
2132
2135 MenuCleanup _(pCM, hMenu);
2136 if (FAILED_UNEXPECTEDLY(hResult))
2137 return hResult;
2138
2139 UINT cmf = CMF_DEFAULTONLY | GetContextMenuFlags(m_pShellBrowser, 0);
2140 hResult = pCM->QueryContextMenu(hMenu, 0, DVIDM_CONTEXTMENU_FIRST, DVIDM_CONTEXTMENU_LAST, cmf);
2141 if (FAILED_UNEXPECTEDLY(hResult))
2142 return hResult;
2143
2144 uCommand = GetMenuDefaultItem(hMenu, FALSE, 0);
2145 if (uCommand == (UINT)-1)
2146 {
2147 ERR("GetMenuDefaultItem returned -1\n");
2148 return E_FAIL;
2149 }
2150
2152
2153 return hResult;
2154}
2155
2157{
2159 UINT uCommand;
2160 HRESULT hResult;
2161
2162 TRACE("(%p)\n", this);
2163
2164 if (m_hContextMenu != NULL)
2165 {
2166 ERR("HACK: Aborting context menu in nested call\n");
2167 return 0;
2168 }
2169
2170 HWND hWndHdr = ListView_GetHeader(m_ListView.m_hWnd);
2171 RECT r;
2172 if (::GetWindowRect(hWndHdr, &r) && PtInRect(&r, pt) && ::IsWindowVisible(hWndHdr))
2173 {
2175 }
2176
2178 if (!m_hContextMenu)
2179 return E_FAIL;
2180
2181 if (lParam != ~0) // unless app key (menu key) was pressed
2182 {
2183 LV_HITTESTINFO hittest = { pt };
2184 ScreenToClient(&hittest.pt);
2185 m_ListView.HitTest(&hittest);
2186
2187 // Right-Clicked item is selected? If selected, no selection change.
2188 // If not selected, then reset the selection and select the item.
2189 if ((hittest.flags & LVHT_ONITEM) &&
2191 {
2192 SelectItem(hittest.iItem, SVSI_SELECT | SVSI_DESELECTOTHERS | SVSI_ENSUREVISIBLE);
2193 }
2194 }
2195
2197 // In case we still have this left over, clean it up
2200 if (FAILED_UNEXPECTEDLY(hResult))
2201 return 0;
2202
2203 UINT cmf = GetContextMenuFlags(m_pShellBrowser, SFGAO_CANRENAME);
2204 // Use 1 as the first id we want. 0 means that user canceled the menu
2205 hResult = m_pCM->QueryContextMenu(m_hContextMenu, 0, CONTEXT_MENU_BASE_ID, DVIDM_CONTEXTMENU_LAST, cmf);
2206 if (FAILED_UNEXPECTEDLY(hResult))
2207 return 0;
2208
2209 if (m_pCommDlgBrowser && !(GetCommDlgViewFlags() & CDB2GVF_NOSELECTVERB))
2210 {
2211 HMENU hMenuSource = LoadMenuW(_AtlBaseModule.GetResourceInstance(), MAKEINTRESOURCEW(IDM_DVSELECT));
2213 DestroyMenu(hMenuSource);
2215 // TODO: ICommDlgBrowser2::GetDefaultMenuText == S_OK
2216 }
2217
2218 // There is no position requested, so try to find one
2219 if (lParam == ~0)
2220 {
2221 HWND hFocus = ::GetFocus();
2222 int lvIndex = -1;
2223
2224 if (hFocus == m_ListView.m_hWnd || m_ListView.IsChild(hFocus))
2225 {
2226 // Is there an item focused and selected?
2228 // If not, find the first selected item
2229 if (lvIndex < 0)
2230 lvIndex = m_ListView.GetNextItem(-1, LVNI_SELECTED);
2231 }
2232
2233 // We got something
2234 if (lvIndex > -1)
2235 {
2236 // Find the center of the icon
2237 RECT rc = { LVIR_ICON };
2238 m_ListView.SendMessage(LVM_GETITEMRECT, lvIndex, (LPARAM)&rc);
2239 pt.x = (rc.right + rc.left) / 2;
2240 pt.y = (rc.bottom + rc.top) / 2;
2241 }
2242 else
2243 {
2244 // We have to drop it somewhere
2245 pt.x = pt.y = 0;
2246 }
2247
2248 m_ListView.ClientToScreen(&pt);
2249 }
2250
2253 pcdb2->Notify(static_cast<IShellView*>(this), CDB2N_CONTEXTMENU_START);
2254
2255 // This runs the message loop, calling back to us with f.e. WM_INITPOPUP (hence why m_hContextMenu and m_pCM exist)
2256 uCommand = TrackPopupMenu(m_hContextMenu,
2258 pt.x, pt.y, 0, m_hWnd, NULL);
2259 if (uCommand >= DVIDM_ARRANGESORT_FIRST && uCommand <= DVIDM_ARRANGESORT_LAST)
2260 {
2261 SendMessage(WM_COMMAND, uCommand, 0);
2262 }
2263 else if (uCommand != 0 && !(uCommand == DVIDM_COMMDLG_SELECT && OnDefaultCommand() == S_OK))
2264 {
2266 }
2267
2268 if (pcdb2)
2269 pcdb2->Notify(static_cast<IShellView*>(this), CDB2N_CONTEXTMENU_DONE);
2270 return 0;
2271}
2272
2274{
2275 HRESULT hResult;
2276 HMENU hMenu = NULL;
2277
2279 hResult = GetItemObject(bUseSelection ? SVGIO_SELECTION : SVGIO_BACKGROUND, IID_PPV_ARG(IContextMenu, &pCM));
2280 if (FAILED_UNEXPECTEDLY(hResult))
2281 return 0;
2282
2283 MenuCleanup _(pCM, hMenu);
2284
2285 if ((uCommand != FCIDM_SHVIEW_DELETE) && (uCommand != FCIDM_SHVIEW_RENAME))
2286 {
2287 hMenu = CreatePopupMenu();
2288 if (!hMenu)
2289 return 0;
2290
2291 hResult = pCM->QueryContextMenu(hMenu, 0, DVIDM_CONTEXTMENU_FIRST, DVIDM_CONTEXTMENU_LAST, CMF_NORMAL);
2292 if (FAILED_UNEXPECTEDLY(hResult))
2293 return 0;
2294 }
2295
2296 if (bUseSelection)
2297 {
2298 // FIXME: we should cache this
2299 SFGAOF rfg = SFGAO_BROWSABLE | SFGAO_CANCOPY | SFGAO_CANLINK | SFGAO_CANMOVE | SFGAO_CANDELETE | SFGAO_CANRENAME | SFGAO_HASPROPSHEET | SFGAO_FILESYSTEM | SFGAO_FOLDER;
2300 hResult = m_pSFParent->GetAttributesOf(m_cidl, m_apidl, &rfg);
2301 if (FAILED_UNEXPECTEDLY(hResult))
2302 return 0;
2303
2304 if (!(rfg & SFGAO_CANMOVE) && uCommand == FCIDM_SHVIEW_CUT)
2305 return 0;
2306 if (!(rfg & SFGAO_CANCOPY) && uCommand == FCIDM_SHVIEW_COPY)
2307 return 0;
2308 if (!(rfg & SFGAO_CANDELETE) && uCommand == FCIDM_SHVIEW_DELETE)
2309 return 0;
2310 if (!(rfg & SFGAO_CANRENAME) && uCommand == FCIDM_SHVIEW_RENAME)
2311 return 0;
2312 if (!(rfg & SFGAO_HASPROPSHEET) && uCommand == FCIDM_SHVIEW_PROPERTIES)
2313 return 0;
2314 }
2315
2316 // FIXME: We should probably use the objects position?
2318 return 0;
2319}
2320
2321// ##### message handling #####
2322
2324{
2325 WORD wWidth, wHeight;
2326
2327 wWidth = LOWORD(lParam);
2328 wHeight = HIWORD(lParam);
2329
2330 TRACE("%p width=%u height=%u\n", this, wWidth, wHeight);
2331
2332 // WM_SIZE can come before WM_CREATE
2333 if (!m_ListView)
2334 return 0;
2335
2336 /* Resize the ListView to fit our window */
2337 ::MoveWindow(m_ListView, 0, 0, wWidth, wHeight, TRUE);
2338
2340
2343
2344 return 0;
2345}
2346
2347// internal
2349{
2350 TRACE("%p\n", this);
2351
2353 {
2354 // TODO: cleanup menu after deactivation
2356 }
2357}
2358
2360{
2361 TRACE("%p uState=%x\n", this, uState);
2362
2363 // don't do anything if the state isn't really changing
2364 if (m_uState == uState)
2365 {
2366 return;
2367 }
2368
2369 if (uState == SVUIA_DEACTIVATE)
2370 {
2371 OnDeactivate();
2372 }
2373 else
2374 {
2376 {
2377 FillEditMenu();
2378 FillViewMenu();
2379 m_pShellBrowser->SetMenuSB(m_hMenu, 0, m_hWnd);
2381 }
2382
2383 if (SVUIA_ACTIVATE_FOCUS == uState)
2384 {
2385 m_ListView.SetFocus();
2386 }
2387 }
2388
2389 m_uState = uState;
2390 TRACE("--\n");
2391}
2392
2394{
2395 if (!GetSelections())
2396 return;
2397
2398 SFGAOF rfg = SFGAO_CANCOPY | SFGAO_CANMOVE | SFGAO_FILESYSTEM;
2399 HRESULT hr = m_pSFParent->GetAttributesOf(m_cidl, m_apidl, &rfg);
2401 return;
2402
2403 if (!bCopy && !(rfg & SFGAO_CANMOVE))
2404 return;
2405 if (bCopy && !(rfg & SFGAO_CANCOPY))
2406 return;
2407
2409 hr = m_pSFParent->GetUIObjectOf(m_hWnd, m_cidl, m_apidl, IID_IContextMenu, 0, (void **)&pCM);
2411 return;
2412
2413 InvokeContextMenuCommand(pCM, (bCopy ? "copyto" : "moveto"), NULL);
2414}
2415
2417{
2419 return 0;
2420}
2421
2423{
2424 TRACE("%p\n", this);
2425
2426 /* Tell the browser one of our windows has received the focus. This
2427 should always be done before merging menus (OnActivate merges the
2428 menus) if one of our windows has the focus.*/
2429
2430 m_pShellBrowser->OnViewWindowActive(this);
2432
2433 /* Set the focus to the listview */
2434 m_ListView.SetFocus();
2435
2436 /* Notify the ICommDlgBrowser interface */
2437 OnStateChange(CDBOSC_SETFOCUS);
2438
2439 return 0;
2440}
2441
2443{
2444 TRACE("(%p) stub\n", this);
2445
2447 /* Notify the ICommDlgBrowser */
2448 OnStateChange(CDBOSC_KILLFOCUS);
2449
2450 return 0;
2451}
2452
2453// the CmdID's are the ones from the context menu
2455{
2456 DWORD dwCmdID;
2457 DWORD dwCmd;
2458 HWND hwndCmd;
2459 int nCount;
2460
2461 dwCmdID = GET_WM_COMMAND_ID(wParam, lParam);
2463 hwndCmd = GET_WM_COMMAND_HWND(wParam, lParam);
2464
2465 TRACE("(%p)->(0x%08x 0x%08x %p) stub\n", this, dwCmdID, dwCmd, hwndCmd);
2466
2467 if (dwCmdID >= DVIDM_ARRANGESORT_FIRST && dwCmdID <= DVIDM_ARRANGESORT_LAST)
2468 {
2469 UINT listCol = (UINT)GetMenuItemDataById(m_hMenuArrangeModes, dwCmdID);
2470 _Sort(listCol);
2471 return 0;
2472 }
2473
2474 switch (dwCmdID)
2475 {
2479 CheckToolbar();
2480 break;
2483 m_ListView.ModifyStyle(LVS_TYPEMASK, LVS_ICON);
2484 CheckToolbar();
2485 break;
2488 m_ListView.ModifyStyle(LVS_TYPEMASK, LVS_LIST);
2489 CheckToolbar();
2490 break;
2493 m_ListView.ModifyStyle(LVS_TYPEMASK, LVS_REPORT);
2494 CheckToolbar();
2495 break;
2498 break;
2500 if (_GetSnapToGrid() == S_OK)
2502 else
2503 ArrangeGrid();
2504 break;
2506 if (GetAutoArrange() == S_OK)
2507 m_ListView.ModifyStyle(LVS_AUTOARRANGE, 0);
2508 else
2509 AutoArrange();
2510 break;
2514 break;
2516 nCount = m_ListView.GetItemCount();
2517 for (int i=0; i < nCount; i++)
2519 break;
2521 Refresh();
2522 break;
2524 case FCIDM_SHVIEW_CUT:
2525 case FCIDM_SHVIEW_COPY:
2529 return 0;
2530 return OnExplorerCommand(dwCmdID, TRUE);
2534 return 0;
2536 case FCIDM_SHVIEW_UNDO:
2539 return OnExplorerCommand(dwCmdID, FALSE);
2540 default:
2541 // WM_COMMAND messages from file menu are routed to CDefView to let m_pFileMenu handle them
2542 if (m_pFileMenu && dwCmd == 0)
2543 {
2544 HMENU Dummy = NULL;
2545 MenuCleanup _(m_pFileMenu, Dummy);
2547 }
2548 }
2549
2550 return 0;
2551}
2552
2553static BOOL
2555{
2556 HKEY hKey;
2557 LONG error;
2558 DWORD dwValue = FALSE, cbValue;
2559
2561 if (error)
2562 return dwValue;
2563
2564 cbValue = sizeof(dwValue);
2565 RegQueryValueExW(hKey, L"SelectExtOnRename", NULL, NULL, (LPBYTE)&dwValue, &cbValue);
2566
2568 return !!dwValue;
2569}
2570
2572{
2573 UINT CtlID;
2574 LPNMHDR lpnmh;
2575 LPNMLISTVIEW lpnmlv;
2576 NMLVDISPINFOW *lpdi;
2577 PCUITEMID_CHILD pidl;
2578 BOOL unused;
2579
2580 CtlID = wParam;
2581 lpnmh = (LPNMHDR)lParam;
2582 lpnmlv = (LPNMLISTVIEW)lpnmh;
2583 lpdi = (NMLVDISPINFOW *)lpnmh;
2584
2585 TRACE("%p CtlID=%u lpnmh->code=%x\n", this, CtlID, lpnmh->code);
2586
2587 switch (lpnmh->code)
2588 {
2589 case NM_SETFOCUS:
2590 TRACE("-- NM_SETFOCUS %p\n", this);
2591 OnSetFocus(0, 0, 0, unused);
2592 break;
2593 case NM_KILLFOCUS:
2594 TRACE("-- NM_KILLFOCUS %p\n", this);
2595 OnDeactivate();
2596 /* Notify the ICommDlgBrowser interface */
2597 OnStateChange(CDBOSC_KILLFOCUS);
2598 break;
2599 case NM_CUSTOMDRAW:
2600 TRACE("-- NM_CUSTOMDRAW %p\n", this);
2601 return CDRF_DODEFAULT;
2602 case NM_RELEASEDCAPTURE:
2603 TRACE("-- NM_RELEASEDCAPTURE %p\n", this);
2604 break;
2605 case NM_CLICK:
2606 TRACE("-- NM_CLICK %p\n", this);
2607 break;
2608 case NM_RCLICK:
2609 TRACE("-- NM_RCLICK %p\n", this);
2610 break;
2611 case NM_DBLCLK:
2612 TRACE("-- NM_DBLCLK %p\n", this);
2614 break;
2615 case NM_RETURN:
2616 TRACE("-- NM_RETURN %p\n", this);
2618 break;
2619 case HDN_ENDTRACKW:
2620 TRACE("-- HDN_ENDTRACKW %p\n", this);
2621 //nColumn1 = m_ListView.GetColumnWidth(0);
2622 //nColumn2 = m_ListView.GetColumnWidth(1);
2623 break;
2624 case LVN_DELETEITEM:
2625 TRACE("-- LVN_DELETEITEM %p\n", this);
2626 /*delete the pidl because we made a copy of it*/
2627 SHFree(reinterpret_cast<LPVOID>(lpnmlv->lParam));
2628 break;
2629 case LVN_DELETEALLITEMS:
2630 TRACE("-- LVN_DELETEALLITEMS %p\n", this);
2631 return FALSE;
2632 case LVN_INSERTITEM:
2633 TRACE("-- LVN_INSERTITEM (STUB)%p\n", this);
2634 break;
2635 case LVN_ITEMACTIVATE:
2636 TRACE("-- LVN_ITEMACTIVATE %p\n", this);
2637 OnStateChange(CDBOSC_SELCHANGE); // browser will get the IDataObject
2638 break;
2639 case LVN_COLUMNCLICK:
2640 {
2641 UINT foldercol = MapListColumnToFolderColumn(lpnmlv->iSubItem);
2642 HRESULT hr = S_FALSE;
2643 if (m_pSDParent)
2644 hr = m_pSDParent->ColumnClick(foldercol);
2645 if (hr != S_OK)
2646 hr = _DoFolderViewCB(SFVM_COLUMNCLICK, foldercol, 0);
2647 if (hr != S_OK)
2648 _Sort(lpnmlv->iSubItem);
2649 break;
2650 }
2651 case LVN_GETDISPINFOA:
2652 case LVN_GETDISPINFOW:
2653 TRACE("-- LVN_GETDISPINFO %p\n", this);
2654 pidl = _PidlByItem(lpdi->item);
2655
2656 if (lpdi->item.mask & LVIF_TEXT) /* text requested */
2657 {
2660 break;
2661
2662 if (lpnmh->code == LVN_GETDISPINFOA)
2663 {
2664 /* shouldn't happen */
2665 NMLVDISPINFOA *lpdiA = (NMLVDISPINFOA *)lpnmh;
2666 StrRetToStrNA( lpdiA->item.pszText, lpdiA->item.cchTextMax, &sd.str, NULL);
2667 TRACE("-- text=%s\n", lpdiA->item.pszText);
2668 }
2669 else /* LVN_GETDISPINFOW */
2670 {
2671 StrRetToStrNW( lpdi->item.pszText, lpdi->item.cchTextMax, &sd.str, NULL);
2672 TRACE("-- text=%s\n", debugstr_w(lpdi->item.pszText));
2673 }
2674 }
2675 if(lpdi->item.mask & LVIF_IMAGE) /* image requested */
2676 {
2678 }
2679 if(lpdi->item.mask & LVIF_STATE)
2680 {
2681 ULONG attributes = SFGAO_HIDDEN;
2682 if (SUCCEEDED(m_pSFParent->GetAttributesOf(1, &pidl, &attributes)))
2683 {
2684 if (attributes & SFGAO_HIDDEN)
2685 lpdi->item.state |= LVIS_CUT;
2686 }
2687 }
2688 lpdi->item.mask |= LVIF_DI_SETITEM;
2689 break;
2690 case LVN_ITEMCHANGED:
2691 TRACE("-- LVN_ITEMCHANGED %p\n", this);
2692 if ((lpnmlv->uOldState ^ lpnmlv->uNewState) & (LVIS_SELECTED | LVIS_FOCUSED))
2693 {
2694 OnStateChange(CDBOSC_SELCHANGE); // browser will get the IDataObject
2695 // FIXME: Use LVIS_DROPHILITED instead in drag_notify_subitem
2697 {
2700 }
2701 _DoFolderViewCB(SFVM_SELECTIONCHANGED, NULL/* FIXME */, NULL/* FIXME */);
2702 }
2703 break;
2704 case LVN_BEGINDRAG:
2705 case LVN_BEGINRDRAG:
2706 TRACE("-- LVN_BEGINDRAG\n");
2707 if (GetSelections())
2708 {
2710 DWORD dwAttributes = SFGAO_CANCOPY | SFGAO_CANLINK;
2711 DWORD dwEffect = DROPEFFECT_MOVE;
2712
2713 if (SUCCEEDED(m_pSFParent->GetUIObjectOf(m_hWnd, m_cidl, m_apidl, IID_NULL_PPV_ARG(IDataObject, &pda))))
2714 {
2716
2717 if (SUCCEEDED(m_pSFParent->GetAttributesOf(m_cidl, m_apidl, &dwAttributes)))
2718 dwEffect |= dwAttributes & (SFGAO_CANCOPY | SFGAO_CANLINK);
2719
2721 if (SUCCEEDED(pda->QueryInterface(IID_PPV_ARG(IAsyncOperation, &piaso))))
2722 piaso->SetAsyncMode(TRUE);
2723
2724 DWORD dwEffect2;
2725
2726 m_pSourceDataObject = pda;
2727 m_ptFirstMousePos = params->ptAction;
2730
2731 HIMAGELIST big_icons, small_icons;
2732 Shell_GetImageLists(&big_icons, &small_icons);
2733 PCUITEMID_CHILD pidl = _PidlByItem(params->iItem);
2734 int iIcon = SHMapPIDLToSystemImageListIndex(m_pSFParent, pidl, 0);
2735 POINT ptItem;
2736 m_ListView.GetItemPosition(params->iItem, &ptItem);
2737
2738 ImageList_BeginDrag(big_icons, iIcon, params->ptAction.x - ptItem.x, params->ptAction.y - ptItem.y);
2739 DoDragDrop(pda, this, dwEffect, &dwEffect2);
2741 }
2742 }
2743 break;
2745 {
2746 DWORD dwAttr = SFGAO_CANRENAME;
2747 pidl = _PidlByItem(lpdi->item);
2748
2749 TRACE("-- LVN_BEGINLABELEDITW %p\n", this);
2750
2751 m_pSFParent->GetAttributesOf(1, &pidl, &dwAttr);
2752 if (SFGAO_CANRENAME & dwAttr)
2753 {
2754 HWND hEdit = reinterpret_cast<HWND>(m_ListView.SendMessage(LVM_GETEDITCONTROL));
2756
2757 // smartass-renaming: See CORE-15242
2758 if (!(dwAttr & SFGAO_FOLDER) && (dwAttr & SFGAO_FILESYSTEM) &&
2759 (lpdi->item.mask & LVIF_TEXT) && !SelectExtOnRename())
2760 {
2761 WCHAR szFullPath[MAX_PATH];
2762 PIDLIST_ABSOLUTE pidlFull = ILCombine(m_pidlParent, pidl);
2763 SHGetPathFromIDListW(pidlFull, szFullPath);
2764
2765 INT cchLimit = 0;
2766 _DoFolderViewCB(SFVM_GETNAMELENGTH, (WPARAM)pidlFull, (LPARAM)&cchLimit);
2767 if (cchLimit)
2768 ::SendMessageW(hEdit, EM_SETLIMITTEXT, cchLimit, 0);
2769
2770 if (!SHELL_FS_HideExtension(szFullPath))
2771 {
2772 LPWSTR pszText = lpdi->item.pszText;
2773 LPWSTR pchDotExt = PathFindExtensionW(pszText);
2774 ::PostMessageW(hEdit, EM_SETSEL, 0, pchDotExt - pszText);
2776 }
2777
2778 ILFree(pidlFull);
2779 }
2780
2781 m_isEditing = TRUE;
2782 return FALSE;
2783 }
2784 return TRUE;
2785 }
2786 case LVN_ENDLABELEDITW:
2787 {
2788 TRACE("-- LVN_ENDLABELEDITW %p\n", this);
2790
2791 if (lpdi->item.pszText)
2792 {
2793 HRESULT hr;
2794 LVITEMW lvItem;
2795
2796 pidl = _PidlByItem(lpdi->item);
2797 PITEMID_CHILD pidlNew = NULL;
2798 hr = m_pSFParent->SetNameOf(0, pidl, lpdi->item.pszText, SHGDN_INFOLDER, &pidlNew);
2799
2800 if (SUCCEEDED(hr) && pidlNew)
2801 {
2802 lvItem.mask = LVIF_PARAM|LVIF_IMAGE;
2803 lvItem.iItem = lpdi->item.iItem;
2804 lvItem.iSubItem = 0;
2805 lvItem.lParam = reinterpret_cast<LPARAM>(pidlNew);
2807 m_ListView.SetItem(&lvItem);
2808 m_ListView.Update(lpdi->item.iItem);
2809 return TRUE;
2810 }
2811 }
2812
2813 return FALSE;
2814 }
2815 default:
2816 TRACE("-- %p WM_COMMAND %x unhandled\n", this, lpnmh->code);
2817 break;
2818 }
2819
2820 return 0;
2821}
2822
2823// This is just a quick hack to make the desktop work correctly.
2824// ITranslateShellChangeNotify's IsChildID is undocumented, but most likely the
2825// way that a folder should know if it should update upon a change notification.
2826// It is exported by merged folders at a minimum.
2828{
2829 if (!pidl1 || !pidl2)
2830 return FALSE;
2831 if (ILIsParent(pidl1, pidl2, TRUE))
2832 return TRUE;
2833
2834 CComHeapPtr<ITEMIDLIST_ABSOLUTE> pidl2Clone(ILClone(pidl2));
2835 ILRemoveLastID(pidl2Clone);
2836 return ILIsEqual(pidl1, pidl2Clone);
2837}
2838
2840{
2841 // The change notify can come before WM_CREATE
2842 if (!m_ListView)
2843 return FALSE;
2844
2845 HANDLE hChange = (HANDLE)wParam;
2846 DWORD dwProcID = (DWORD)lParam;
2847 PIDLIST_ABSOLUTE *Pidls;
2848 LONG lEvent;
2849 HANDLE hLock = SHChangeNotification_Lock(hChange, dwProcID, &Pidls, &lEvent);
2850 if (hLock == NULL)
2851 {
2852 ERR("hLock == NULL\n");
2853 return FALSE;
2854 }
2855
2856 TRACE("(%p)(%p,%p,%p)\n", this, Pidls[0], Pidls[1], lParam);
2857
2859 {
2861 return FALSE;
2862 }
2863
2864 // Translate child IDLs.
2865 // SHSimpleIDListFromPathW creates fake PIDLs (lacking some attributes)
2866 lEvent &= ~SHCNE_INTERRUPT;
2867 HRESULT hr;
2868 PITEMID_CHILD child0 = NULL, child1 = NULL;
2869 CComHeapPtr<ITEMIDLIST_RELATIVE> pidl0Temp, pidl1Temp;
2871 {
2872 if (_ILIsSpecialFolder(Pidls[0]) || ILIsParentOrSpecialParent(m_pidlParent, Pidls[0]))
2873 {
2874 child0 = ILFindLastID(Pidls[0]);
2875 hr = SHGetRealIDL(m_pSFParent, child0, &pidl0Temp);
2876 if (SUCCEEDED(hr))
2877 child0 = pidl0Temp;
2878 }
2879 if (_ILIsSpecialFolder(Pidls[1]) || ILIsParentOrSpecialParent(m_pidlParent, Pidls[1]))
2880 {
2881 child1 = ILFindLastID(Pidls[1]);
2882 hr = SHGetRealIDL(m_pSFParent, child1, &pidl1Temp);
2883 if (SUCCEEDED(hr))
2884 child1 = pidl1Temp;
2885 }
2886 }
2887
2888 switch (lEvent)
2889 {
2890 case SHCNE_MKDIR:
2891 case SHCNE_CREATE:
2892 case SHCNE_DRIVEADD:
2893 if (!child0)
2894 break;
2895 if (LV_FindItemByPidl(child0) < 0)
2896 LV_AddItem(child0);
2897 else
2898 LV_UpdateItem(child0);
2899 break;
2900 case SHCNE_RMDIR:
2901 case SHCNE_DELETE:
2902 case SHCNE_DRIVEREMOVED:
2903 if (child0)
2904 LV_DeleteItem(child0);
2905 break;
2906 case SHCNE_RENAMEFOLDER:
2907 case SHCNE_RENAMEITEM:
2908 if (child0 && child1)
2909 LV_RenameItem(child0, child1);
2910 else if (child0)
2911 LV_DeleteItem(child0);
2912 else if (child1)
2913 LV_AddItem(child1);
2914 break;
2915 case SHCNE_UPDATEITEM:
2916 if (child0)
2917 LV_UpdateItem(child0);
2918 break;
2919 case SHCNE_UPDATEIMAGE:
2921 case SHCNE_MEDIAREMOVED:
2922 case SHCNE_ASSOCCHANGED:
2924 break;
2925 case SHCNE_UPDATEDIR:
2926 case SHCNE_ATTRIBUTES:
2927 Refresh();
2929 break;
2930 case SHCNE_FREESPACE:
2932 break;
2933 }
2934
2936 return TRUE;
2937}
2938
2941
2943{
2944 if (!m_pCM)
2945 {
2946 /* no menu */
2947 ERR("no context menu\n");
2948 return FALSE;
2949 }
2950
2951 // lParam of WM_DRAWITEM WM_MEASUREITEM contains a menu id and
2952 // this also needs to be changed to a menu identifier offset
2953 UINT CmdID;
2954 HRESULT hres = SHGetMenuIdFromMenuMsg(uMsg, lParam, &CmdID);
2955 if (SUCCEEDED(hres))
2957
2958 /* Forward the message to the IContextMenu2 */
2961
2962 return (SUCCEEDED(hres));
2963}
2964
2966{
2967 /* Wallpaper setting affects drop shadows effect */
2968 if (wParam == SPI_SETDESKWALLPAPER || wParam == 0)
2970
2971 return S_OK;
2972}
2973
2975{
2976 HMENU hmenu = (HMENU) wParam;
2977 int nPos = LOWORD(lParam);
2978 UINT menuItemId;
2979
2980 if (m_pCM)
2981 OnCustomItem(uMsg, wParam, lParam, bHandled);
2982
2984
2985 if (GetSelections() == 0)
2986 {
2993 }
2994 else
2995 {
2996 // FIXME: Check copyable
3003 }
3004
3005 /* Lets try to find out what the hell wParam is */
3006 if (hmenu == GetSubMenu(m_hMenu, nPos))
3007 menuItemId = ReallyGetMenuItemID(m_hMenu, nPos);
3008 else if (hViewMenu && hmenu == GetSubMenu(hViewMenu, nPos))
3009 menuItemId = ReallyGetMenuItemID(hViewMenu, nPos);
3010 else if (m_hContextMenu && hmenu == GetSubMenu(m_hContextMenu, nPos))
3011 menuItemId = ReallyGetMenuItemID(m_hContextMenu, nPos);
3012 else
3013 return FALSE;
3014
3015 switch (menuItemId)
3016 {
3017 case FCIDM_MENU_FILE:
3018 FillFileMenu();
3019 break;
3020 case FCIDM_MENU_VIEW:
3021 case FCIDM_SHVIEW_VIEW:
3023 break;
3026 break;
3027 }
3028
3029 return FALSE;
3030}
3031
3032
3033// The INTERFACE of the IShellView object
3034
3036{
3037 TRACE("(%p)\n", this);
3038
3039 *phWnd = m_hWnd;
3040
3041 return S_OK;
3042}
3043
3045{
3046 FIXME("(%p) stub\n", this);
3047
3048 return E_NOTIMPL;
3049}
3050
3051// FIXME: use the accel functions
3053{
3054 if (m_isEditing)
3055 return S_FALSE;
3056
3057 if (lpmsg->message >= WM_KEYFIRST && lpmsg->message <= WM_KEYLAST)
3058 {
3059 if (::TranslateAcceleratorW(m_hWnd, m_hAccel, lpmsg) != 0)
3060 return S_OK;
3061
3062 TRACE("-- key=0x%04lx\n", lpmsg->wParam);
3063 }
3064
3065 return m_pShellBrowser ? m_pShellBrowser->TranslateAcceleratorSB(lpmsg, 0) : S_FALSE;
3066}
3067
3069{
3070 FIXME("(%p)\n", this);
3071 return E_NOTIMPL;
3072}
3073
3075{
3076 TRACE("(%p)->(state=%x)\n", this, uState);
3077
3078 // don't do anything if the state isn't changing
3079 if (m_uState == uState)
3080 return S_OK;
3081
3082 // OnActivate handles the menu merging and internal state
3083 DoActivate(uState);
3084
3085 // only do this if we are active
3086 if (uState != SVUIA_DEACTIVATE)
3087 {
3089
3090 // Set the text for the status bar
3092 }
3093
3094 return S_OK;
3095}
3096
3098{
3099 TRACE("(%p)\n", this);
3100
3102
3104 FillList();
3105
3106 return S_OK;
3107}
3108
3110{
3111 return CreateViewWindow3(psb, lpPrevView, SV3CVW3_DEFAULT,
3112 (FOLDERFLAGS)lpfs->fFlags, (FOLDERFLAGS)lpfs->fFlags, (FOLDERVIEWMODE)lpfs->ViewMode, NULL, prcView, phWnd);
3113}
3114
3116{
3117 TRACE("(%p)\n", this);
3118
3119 /* Make absolutely sure all our UI is cleaned up */
3121
3122 if (m_hAccel)
3123 {
3124 // MSDN: Accelerator tables loaded from resources are freed automatically when application terminates
3125 m_hAccel = NULL;
3126 }
3127
3129 {
3132 }
3133
3134 if (m_hMenuViewModes)
3135 {
3138 }
3139
3140 if (m_hMenu)
3141 {
3143 m_hMenu = NULL;
3144 }
3145
3146 if (m_ListView)
3147 {
3148 m_ListView.DestroyWindow();
3149 }
3150
3151 if (m_hWnd)
3152 {
3154 DestroyWindow();
3155 }
3156
3159
3160 return S_OK;
3161}
3162
3164{
3165 TRACE("(%p)->(%p) vmode=%x flags=%x\n", this, lpfs,
3167
3168 if (!lpfs)
3169 return E_INVALIDARG;
3170
3171 *lpfs = m_FolderSettings;
3172 return S_OK;
3173}
3174
3176{
3177 TRACE("(%p)->(0x%lX, %p, %p)\n", this, dwReserved, lpfn, lparam);
3178
3181 return S_OK;
3182}
3183
3185{
3186 ULONG read;
3187 HRESULT hr = pS->Read(buffer, cb, &read);
3188 return FAILED(hr) ? hr : (cb == read ? S_OK : HResultFromWin32(ERROR_MORE_DATA));
3189}
3190
3192{
3193 DWORD value;
3195 return SUCCEEDED(hr) ? value : def;
3196}
3197
3199{
3200 CLSID clsid;
3202 if (SUCCEEDED(hr))
3203 {
3204 WCHAR path[MAX_PATH], name[39];
3205 wsprintfW(path, L"%s\\%s", REGSTR_PATH_EXPLORER, L"Streams\\Default");
3207 *ppStream = SHOpenRegStream2W(HKEY_CURRENT_USER, path, name, Stgm);
3208 hr = *ppStream ? S_OK : E_FAIL;
3209 }
3210 return hr;
3211}
3212
3214{
3216 if (FAILED(hr))
3217 return hr;
3218 if (cols.Signature != PERSISTCOLUMNS::SIG || cols.Count > cols.MAXCOUNT)
3220 return Read(pS, &cols.Columns, sizeof(*cols.Columns) * cols.Count);
3221}
3222
3224{
3226 PERSISTCOLUMNS cols;
3229 bool fallback = false;
3230 HRESULT hrColumns = E_FAIL;
3232 if (SUCCEEDED(hr))
3233 {
3234 DWORD data;
3235 if (FAILED(hr = SHPropertyBag_ReadDWORD(pPB, L"Mode", &data)))
3236 goto loadfallback;
3238 cvs.FolderSettings.fFlags = ReadDWORD(pPB, L"FFlags", FWF_NOGROUPING);
3239 data = ReadDWORD(pPB, L"Sort", ~0ul);
3241 cvs.SortDir = (INT8)ReadDWORD(pPB, L"SortDir", 1);
3242 if (SUCCEEDED(hrColumns = SHPropertyBag_ReadStream(pPB, L"ColInfo", &pS)))
3243 hrColumns = LoadColumnsStream(cols, pS);
3244 }
3245 else
3246 {
3247 if (FAILED(hr = (m_pShellBrowser ? m_pShellBrowser->GetViewStateStream(STGM_READ, &pS) : E_UNEXPECTED)))
3248 {
3249 loadfallback:
3251 fallback = true;
3252 }
3253 if (FAILED(hr) || FAILED(hr = Read(pS, &cvs, sizeof(cvs))))
3254 return hr;
3255 if (cvs.Signature != cvs.SIG)
3257 hrColumns = LoadColumnsStream(cols, pS);
3258 }
3260 m_FolderSettings.fFlags &= ~cvs.VALIDFWF;
3262 if (SUCCEEDED(hrColumns))
3263 {
3264 BOOL failed = FALSE;
3265 if ((m_LoadColumnsList = DPA_Create(cols.Count)) != NULL)
3266 {
3267 for (UINT i = 0; i < cols.Count; ++i)
3268 {
3269 failed |= !DPA_SetPtr(m_LoadColumnsList, i, UlongToPtr(cols.Columns[i]));
3270 }
3271 }
3272 if (failed || !cols.Count)
3273 {
3276 }
3277 }
3280 m_sortInfo.Direction = cvs.SortDir > 0 ? 1 : -1;
3282 return hr;
3283}
3284
3286{
3287 if (!m_ListView.m_hWnd)
3288 return E_UNEXPECTED;
3291 cvs.SortColId = sortcol >= 0 ? (WORD)sortcol : 0;
3293 PERSISTCOLUMNS cols;
3295 cols.Count = 0;
3296 LVCOLUMN lvc;
3297 lvc.mask = LVCF_WIDTH | LVCF_SUBITEM;
3298 for (UINT i = 0, j = 0; i < PERSISTCOLUMNS::MAXCOUNT && ListView_GetColumn(m_ListView, j, &lvc); ++j)
3299 {
3301 if (SUCCEEDED(hr))
3302 {
3303 cols.Columns[i] = MAKELONG(hr, lvc.cx);
3304 cols.Count = ++i;
3305 }
3306 }
3307 UINT cbColumns = FIELD_OFFSET(PERSISTCOLUMNS, Columns) + (sizeof(*cols.Columns) * cols.Count);
3309
3310 IPropertyBag *pPB;
3311 HRESULT hr = S_OK;
3312 if (pStream)
3313 {
3314 pStream->AddRef();
3315 goto stream;
3316 }
3318 if (SUCCEEDED(hr))
3319 {
3320 UINT uViewMode;
3321 GetCurrentViewMode(&uViewMode);
3322 hr = SHPropertyBag_WriteDWORD(pPB, L"Mode", uViewMode);
3324 SHPropertyBag_WriteDWORD(pPB, L"Sort", cvs.SortColId);
3325 SHPropertyBag_WriteDWORD(pPB, L"SortDir", cvs.SortDir);
3326 pStream = cols.Count ? SHCreateMemStream((LPBYTE)&cols, cbColumns) : NULL;
3327 if (!pStream || FAILED(SHPropertyBag_WriteStream(pPB, L"ColInfo", pStream)))
3328 SHPropertyBag_Delete(pPB, L"ColInfo");
3329#if 0 // TODO
3331 memcpy(name, L"ItemPos", sizeof(L"ItemPos"));
3332 if (SHGetPerScreenResName(name + 7, _countof(name) - 7, 0))
3333 {
3334 if (GetAutoArrange() == S_FALSE)
3335 // TODO: Save listview item positions
3336 else
3338 }
3339#endif
3340 pPB->Release();
3341 }
3342 else if (SUCCEEDED(hr = (m_pShellBrowser ? m_pShellBrowser->GetViewStateStream(STGM_WRITE, &pStream) : E_UNEXPECTED)))
3343 {
3344 stream:
3345 ULONG written;
3346 cvs.Signature = cvs.SIG;
3348 hr = pStream->Write(&cvs, sizeof(cvs), &written);
3349 if (SUCCEEDED(hr))
3350 hr = pStream->Write(&cols, cbColumns, &written);
3351 }
3352 if (pStream)
3353 pStream->Release();
3354 return hr;
3355}
3356
3358{
3360 return SaveViewState(NULL);
3361 return S_FALSE;
3362}
3363
3364#define UPDATEFOLDERVIEWFLAGS(bits, bit, set) ( (bits) = ((bits) & ~(bit)) | ((set) ? (bit) : 0) )
3366{
3370}
3371
3373{
3374 int i;
3375
3376 TRACE("(%p)->(pidl=%p, 0x%08x) stub\n", this, pidl, uFlags);
3377
3378 if (!m_ListView)
3379 {
3380 ERR("!m_ListView\n");
3381 return E_FAIL;
3382 }
3383
3384 i = LV_FindItemByPidl(pidl);
3385 if (i == -1)
3386 return S_OK;
3387
3388 LVITEMW lvItem = {0};
3389 lvItem.mask = LVIF_STATE;
3391
3392 while (m_ListView.GetItem(&lvItem))
3393 {
3394 if (lvItem.iItem == i)
3395 {
3396 if (uFlags & SVSI_SELECT)
3397 lvItem.state |= LVIS_SELECTED;
3398 else
3399 lvItem.state &= ~LVIS_SELECTED;
3400
3401 if (uFlags & SVSI_FOCUSED)
3402 lvItem.state |= LVIS_FOCUSED;
3403 else
3404 lvItem.state &= ~LVIS_FOCUSED;
3405 }
3406 else
3407 {
3408 if (uFlags & SVSI_DESELECTOTHERS)
3409 {
3410 lvItem.state &= ~LVIS_SELECTED;
3411 }
3412 lvItem.state &= ~LVIS_FOCUSED;
3413 }
3414
3415 m_ListView.SetItem(&lvItem);
3416 lvItem.iItem++;
3417 }
3418
3419 if (uFlags & SVSI_ENSUREVISIBLE)
3421
3422 if((uFlags & SVSI_EDIT) == SVSI_EDIT)
3424
3425 return S_OK;
3426}
3427
3429{
3431
3432 TRACE("(%p)->(uItem=0x%08x,\n\tIID=%s, ppv=%p)\n", this, uItem, debugstr_guid(&riid), ppvOut);
3433
3434 if (!ppvOut)
3435 return E_INVALIDARG;
3436
3437 *ppvOut = NULL;
3438
3439 switch (uItem)
3440 {
3441 case SVGIO_BACKGROUND:
3442 if (IsEqualIID(riid, IID_IContextMenu))
3443 {
3446 return hr;
3447
3448 IUnknown_SetSite(*((IUnknown**)ppvOut), (IShellView *)this);
3449 }
3450 else if (IsEqualIID(riid, IID_IDispatch))
3451 {
3453 {
3456 return hr;
3457 }
3458 hr = m_pShellFolderViewDual->QueryInterface(riid, ppvOut);
3459 }
3460 break;
3461 case SVGIO_SELECTION:
3462 GetSelections();
3463 hr = m_pSFParent->GetUIObjectOf(m_hWnd, m_cidl, m_apidl, riid, 0, ppvOut);
3465 return hr;
3466
3467 if (IsEqualIID(riid, IID_IContextMenu))
3468 IUnknown_SetSite(*((IUnknown**)ppvOut), (IShellView *)this);
3469
3470 break;
3471 }
3472
3473 TRACE("-- (%p)->(interface=%p)\n", this, *ppvOut);
3474
3475 return hr;
3476}
3477
3479{
3483 mode = temp;
3484 return mode;
3485}
3486
3488{
3489 TRACE("(%p)->(%p), stub\n", this, pViewMode);
3490
3491 if (!pViewMode)
3492 return E_INVALIDARG;
3493
3494 *pViewMode = m_FolderSettings.ViewMode;
3495 return S_OK;
3496}
3497
3499{
3500 DWORD dwStyle;
3501 TRACE("(%p)->(%u), stub\n", this, ViewMode);
3502
3503 /* It's not redundant to check FVM_AUTO because it's a (UINT)-1 */
3504 if (((INT)ViewMode < FVM_FIRST || (INT)ViewMode > FVM_LAST) && ((INT)ViewMode != FVM_AUTO))
3505 return E_INVALIDARG;
3506
3507 /* Windows before Vista uses LVM_SETVIEW and possibly
3508 LVM_SETEXTENDEDLISTVIEWSTYLE to set the style of the listview,
3509 while later versions seem to accomplish this through other
3510 means. */
3511 switch (ViewMode)
3512 {
3513 case FVM_ICON:
3514 dwStyle = LVS_ICON;
3515 break;
3516 case FVM_DETAILS:
3517 dwStyle = LVS_REPORT;
3518 break;
3519 case FVM_SMALLICON:
3520 dwStyle = LVS_SMALLICON;
3521 break;
3522 case FVM_LIST:
3523 dwStyle = LVS_LIST;
3524 break;
3525 default:
3526 {
3527 FIXME("ViewMode %d not implemented\n", ViewMode);
3528 dwStyle = LVS_LIST;
3529 break;
3530 }
3531 }
3532
3533 m_ListView.ModifyStyle(LVS_TYPEMASK, dwStyle);
3534
3535 /* This will not necessarily be the actual mode set above.
3536 This mimics the behavior of Windows XP. */
3537 m_FolderSettings.ViewMode = ViewMode;
3538
3539 return S_OK;
3540}
3541
3543{
3544 if (m_pSFParent == NULL)
3545 return E_FAIL;
3546
3547 return m_pSFParent->QueryInterface(riid, ppv);
3548}
3549
3551{
3552 PCUITEMID_CHILD pidl = _PidlByItem(iItemIndex);
3553 if (pidl)
3554 {
3555 *ppidl = ILClone(pidl);
3556 return S_OK;
3557 }
3558
3559 *ppidl = 0;
3560 return E_INVALIDARG;
3561}
3562
3564{
3565 TRACE("(%p)->(%u %p)\n", this, uFlags, pcItems);
3566
3567 if (uFlags != SVGIO_ALLVIEW)
3568 FIXME("some flags unsupported, %x\n", uFlags & ~SVGIO_ALLVIEW);
3569
3571
3572 return S_OK;
3573}
3574
3576{
3577 return E_NOTIMPL;
3578}
3579
3581{
3582 TRACE("(%p)->(%p)\n", this, piItem);
3583
3584 *piItem = m_ListView.GetSelectionMark();
3585
3586 return S_OK;
3587}
3588
3590{
3591 TRACE("(%p)->(%p)\n", this, piItem);
3592
3593 *piItem = m_ListView.GetNextItem(-1, LVNI_FOCUSED);
3594
3595 return S_OK;
3596}
3597
3599{
3600 if (!m_ListView)
3601 {
3602 ERR("!m_ListView\n");
3603 return E_FAIL;
3604 }
3605
3606 int lvIndex = LV_FindItemByPidl(pidl);
3607 if (lvIndex == -1 || ppt == NULL)
3608 return E_INVALIDARG;
3609
3610 m_ListView.GetItemPosition(lvIndex, ppt);
3611 return S_OK;
3612}
3613
3615{
3616 TRACE("(%p)->(%p)\n", this, ppt);
3617
3618 if (!m_ListView)
3619 {
3620 ERR("!m_ListView\n");
3621 return S_FALSE;
3622 }
3623
3624 if (ppt)
3625 {
3626 SIZE spacing;
3627 m_ListView.GetItemSpacing(spacing);
3628
3629 ppt->x = spacing.cx;
3630 ppt->y = spacing.cy;
3631 }
3632
3633 return S_OK;
3634}
3635
3637{
3638 return E_NOTIMPL;
3639}
3640
3642{
3643 return ((m_ListView.GetStyle() & LVS_AUTOARRANGE) ? S_OK : S_FALSE);
3644}
3645
3647{
3648 DWORD dwExStyle = (DWORD)m_ListView.SendMessage(LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0);
3649 return ((dwExStyle & LVS_EX_SNAPTOGRID) ? S_OK : S_FALSE);
3650}
3651
3653{
3654 LVITEMW lvItem;
3655
3656 TRACE("(%p)->(%d, %x)\n", this, iItem, dwFlags);
3657
3658 lvItem.state = 0;
3659 lvItem.stateMask = LVIS_SELECTED;
3660
3661 if (dwFlags & SVSI_ENSUREVISIBLE)
3662 m_ListView.EnsureVisible(iItem, 0);
3663
3664 /* all items */
3665 if (dwFlags & SVSI_DESELECTOTHERS)
3667
3668 /* this item */
3669 if (dwFlags & SVSI_SELECT)
3670 lvItem.state |= LVIS_SELECTED;
3671
3672 if (dwFlags & SVSI_FOCUSED)
3673 lvItem.stateMask |= LVIS_FOCUSED;
3674
3675 m_ListView.SetItemState(iItem, lvItem.state, lvItem.stateMask);
3676
3677 if ((dwFlags & SVSI_EDIT) == SVSI_EDIT)
3678 m_ListView.EditLabel(iItem);
3679
3680 return S_OK;
3681}
3682
3684{
3686
3687 /* Reset the selection */
3689
3690 int lvIndex;
3691 for (UINT i = 0 ; i < cidl; i++)
3692 {
3693 lvIndex = LV_FindItemByPidl(apidl[i]);
3694 if (lvIndex != -1)
3695 {
3696 SelectItem(lvIndex, dwFlags);
3697 m_ListView.SetItemPosition(lvIndex, &apt[i]);
3698 }
3699 }
3700
3701 return S_OK;
3702}
3703
3704
3705// IShellView2 implementation
3706
3708{
3709 FIXME("(%p)->(%p, %lu) stub\n", this, view_guid, view_type);
3710 return E_NOTIMPL;
3711}
3712
3714{
3715 return CreateViewWindow3(view_params->psbOwner, view_params->psvPrev,
3716 SV3CVW3_DEFAULT, (FOLDERFLAGS)view_params->pfs->fFlags, (FOLDERFLAGS)view_params->pfs->fFlags,
3717 (FOLDERVIEWMODE)view_params->pfs->ViewMode, view_params->pvid, view_params->prcView, &view_params->hwndView);
3718}
3719
3721{
3722 OLEMENUGROUPWIDTHS omw = { { 0, 0, 0, 0, 0, 0 } };
3723 const UINT SUPPORTED_SV3CVW3 = SV3CVW3_FORCEVIEWMODE | SV3CVW3_FORCEFOLDERFLAGS;
3724 const UINT IGNORE_FWF = FWF_OWNERDATA; // FIXME: Support this
3725
3726 *hwnd = NULL;
3727
3728 TRACE("(%p)->(shlview=%p shlbrs=%p rec=%p hwnd=%p vmode=%x flags=%x)\n", th