ReactOS 0.4.16-dev-250-g3ecd236
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 ListExStyle |= LVS_EX_DOUBLEBUFFER;
832 }
833
834 ViewMode = m_FolderSettings.ViewMode;
835 hr = _DoFolderViewCB(SFVM_DEFVIEWMODE, 0, (LPARAM)&ViewMode);
836 if (SUCCEEDED(hr))
837 {
838 if (ViewMode >= FVM_FIRST && ViewMode <= FVM_LAST)
839 m_FolderSettings.ViewMode = ViewMode;
840 else
841 ERR("Ignoring invalid ViewMode from SFVM_DEFVIEWMODE: %u (was: %u)\n", ViewMode, m_FolderSettings.ViewMode);
842 }
843
845 {
846 case FVM_ICON:
847 dwStyle |= LVS_ICON;
848 break;
849 case FVM_DETAILS:
850 dwStyle |= LVS_REPORT;
851 break;
852 case FVM_SMALLICON:
853 dwStyle |= LVS_SMALLICON;
854 break;
855 case FVM_LIST:
856 dwStyle |= LVS_LIST;
857 break;
858 default:
859 dwStyle |= LVS_LIST;
860 break;
861 }
862
864 dwStyle |= LVS_AUTOARRANGE;
865
867 ListExStyle |= LVS_EX_SNAPTOGRID;
868
870 dwStyle |= LVS_SINGLESEL;
871
873 ListExStyle |= LVS_EX_FULLROWSELECT;
874
876 (!SHELL_GetSetting(SSF_DOUBLECLICKINWEBVIEW, fDoubleClickInWebView) && !SHELL_GetSetting(SSF_WIN95CLASSIC, fWin95Classic)))
878
880 dwStyle |= LVS_NOCOLUMNHEADER;
881
882#if 0
883 // FIXME: Because this is a negative, everyone gets the new flag by default unless they
884 // opt out. This code should be enabled when shell looks like Vista instead of 2003
886 ListExStyle |= LVS_EX_HEADERINALLVIEWS;
887#endif
888
890 dwExStyle &= ~WS_EX_CLIENTEDGE;
891
892 RECT rcListView = {0,0,0,0};
893 m_ListView.Create(m_hWnd, rcListView, L"FolderView", dwStyle, dwExStyle, ID_LISTVIEW);
894
895 if (!m_ListView)
896 return FALSE;
897
899
900 /* UpdateShellSettings(); */
901 return TRUE;
902}
903
905{
907 {
908 /* Check if drop shadows option is enabled */
909 BOOL bDropShadow = FALSE;
910 DWORD cbDropShadow = sizeof(bDropShadow);
911
912 /*
913 * The desktop ListView always take the default desktop colours, by
914 * remaining transparent and letting user32/win32k paint itself the
915 * desktop background color, if any.
916 */
918
919 SHGetValueW(HKEY_CURRENT_USER, L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Advanced",
920 L"ListviewShadow", NULL, &bDropShadow, &cbDropShadow);
921 if (bDropShadow)
922 {
923 /* Set the icon background transparent */
925 m_ListView.SetTextColor(RGB(255, 255, 255));
926 m_ListView.SetExtendedListViewStyle(LVS_EX_TRANSPARENTSHADOWTEXT, LVS_EX_TRANSPARENTSHADOWTEXT);
927 }
928 else
929 {
930 /* Set the icon background as the same colour as the desktop */
932 m_ListView.SetTextBkColor(crDesktop);
933 if (GetRValue(crDesktop) + GetGValue(crDesktop) + GetBValue(crDesktop) > 128 * 3)
934 m_ListView.SetTextColor(RGB(0, 0, 0));
935 else
936 m_ListView.SetTextColor(RGB(255, 255, 255));
937 m_ListView.SetExtendedListViewStyle(0, LVS_EX_TRANSPARENTSHADOWTEXT);
938 }
939 }
940 else
941 {
944
945 // Background is painted by the parent via WM_PRINTCLIENT
946 m_ListView.SetExtendedListViewStyle(LVS_EX_TRANSPARENTBKGND, LVS_EX_TRANSPARENTBKGND);
947 }
948}
949
950// adds all needed columns to the shellview
952{
953 HIMAGELIST big_icons, small_icons;
954
955 TRACE("%p\n", this);
956
958
959 Shell_GetImageLists(&big_icons, &small_icons);
961 m_ListView.SetImageList(small_icons, LVSIL_SMALL);
962
964
966 UINT ColumnCount = pColumns ? DPA_GetPtrCount(m_LoadColumnsList) : 0;
967 LoadColumns(pColumns, ColumnCount);
969 {
973 }
974 return TRUE;
975}
976
977/**********************************************************
978* Column handling
979*/
981{
982 LVCOLUMN lvc;
983 lvc.mask = LVCF_SUBITEM;
984 if (!ListView_GetColumn(List, Col, &lvc))
985 return E_FAIL;
986 else
987 return lvc.iSubItem;
988}
989
991{
992 // This function is only called during column management, performance is not critical.
993 for (UINT i = 0;; ++i)
994 {
996 if ((UINT)r == FoldCol)
997 return i;
998 else if (FAILED(r))
999 return r;
1000 }
1001}
1002
1004{
1005 // This function is called every time a LVITEM::iSubItem is mapped to
1006 // a folder column (calls to GetDetailsOf etc.) and should be fast.
1008 {
1010 if (ListCol < count)
1011 {
1013 assert(col >= 0 && col == SHGetLVColumnSubItem(m_ListView.m_hWnd, ListCol));
1014 return col;
1015 }
1016 else if (count)
1017 {
1018 TRACE("m_ListToFolderColMap cache miss while mapping %d\n", ListCol);
1019 }
1020 }
1021 return SHGetLVColumnSubItem(m_ListView.m_hWnd, ListCol);
1022}
1023
1025{
1026 // According to learn.microsoft.com/en-us/windows/win32/shell/sfvm-getdetailsof
1027 // the query order is IShellFolder2, IShellDetails, SFVM_GETDETAILSOF.
1028 HRESULT hr;
1029 if (m_pSF2Parent)
1030 {
1031 hr = m_pSF2Parent->GetDetailsOf(pidl, FoldCol, &sd);
1032 }
1033 if (FAILED(hr) && m_pSDParent)
1034 {
1035 hr = m_pSDParent->GetDetailsOf(pidl, FoldCol, &sd);
1036 }
1037#if 0 // TODO
1038 if (FAILED(hr))
1039 {
1040 FIXME("Try SFVM_GETDETAILSOF\n");
1041 }
1042#endif
1043 return hr;
1044}
1045
1047{
1049 if (SUCCEEDED(hr))
1050 return GetDetailsByFolderColumn(pidl, hr, sd);
1051 ERR("Unable to determine folder column from list column %d\n", (int) ListCol);
1052 return hr;
1053}
1054
1055HRESULT CDefView::LoadColumn(UINT FoldCol, UINT ListCol, BOOL Insert, UINT ForceWidth)
1056{
1059 HRESULT hr;
1060
1061 sd.str.uType = !STRRET_WSTR; // Make sure "uninitialized" uType is not WSTR
1062 hr = GetDetailsByFolderColumn(NULL, FoldCol, sd);
1063 if (FAILED(hr))
1064 return hr;
1065 hr = StrRetToStrNW(buf, _countof(buf), &sd.str, NULL);
1066 if (FAILED(hr))
1067 return hr;
1068
1069 UINT chavewidth = CalculateCharWidth(m_ListView.m_hWnd);
1070 if (!chavewidth)
1071 chavewidth = 6; // 6 is a reasonable default fallback
1072
1073 LVCOLUMN lvc;
1074 lvc.mask = LVCF_TEXT | LVCF_FMT | LVCF_WIDTH | LVCF_SUBITEM;
1075 lvc.pszText = buf;
1076 lvc.fmt = sd.fmt;
1077 lvc.cx = ForceWidth ? ForceWidth : (sd.cxChar * chavewidth); // FIXME: DPI?
1078 lvc.iSubItem = FoldCol; // Used by MapFolderColumnToListColumn & MapListColumnToFolderColumn
1079 if ((int)ListCol == -1)
1080 {
1081 assert(Insert); // You can insert at the end but you can't change something that is not there
1082 if (Insert)
1083 ListCol = 0x7fffffff;
1084 }
1085 if (Insert)
1086 ListView_InsertColumn(m_ListView.m_hWnd, ListCol, &lvc);
1087 else
1088 ListView_SetColumn(m_ListView.m_hWnd, ListCol, &lvc);
1089 return S_OK;
1090}
1091
1093{
1094 HWND hWndHdr = ListView_GetHeader(m_ListView.m_hWnd);
1095 UINT newColCount = 0, oldColCount = Header_GetItemCount(hWndHdr);
1096 UINT width = 0, foldCol, i;
1097 HRESULT hr = S_FALSE;
1098
1100 for (i = 0, foldCol = 0;; ++foldCol)
1101 {
1102 if (newColCount >= 0xffff)
1103 break; // CompareIDs limit reached
1104
1105 if (pColList)
1106 {
1107 if (i >= ColListCount)
1108 break;
1109 width = HIWORD(pColList[i]);
1110 foldCol = LOWORD(pColList[i++]);
1111 }
1112
1113 SHCOLSTATEF state = 0;
1114 if (!m_pSF2Parent || FAILED(m_pSF2Parent->GetDefaultColumnState(foldCol, &state)))
1116
1117 if (foldCol == 0)
1118 {
1119 // Force the first column
1120 }
1121 else if (state & SHCOLSTATE_HIDDEN)
1122 {
1123 continue;
1124 }
1125 else if (!pColList && !(state & SHCOLSTATE_ONBYDEFAULT))
1126 {
1127 continue;
1128 }
1129
1130 bool insert = newColCount >= oldColCount;
1131 UINT listCol = insert ? -1 : newColCount;
1132 hr = LoadColumn(foldCol, listCol, insert, width);
1133 if (FAILED(hr))
1134 {
1135 if (!pColList)
1136 hr = S_OK; // No more items, we are done
1137 break;
1138 }
1139 ++newColCount;
1140 }
1141 for (i = newColCount; i < oldColCount; ++i)
1142 {
1144 }
1145
1148 assert(SUCCEEDED(MapFolderColumnToListColumn(0))); // We don't allow turning off the Name column
1150 {
1153 }
1154 return hr;
1155}
1156
1158{
1160 m_ListToFolderColMap = NULL; // No cache while we are building the cache
1162 for (UINT i = 0;; ++i)
1163 {
1165 if (FAILED(hr))
1166 break; // No more columns
1167 if (!DPA_SetPtr(cache, i, (void*)(INT_PTR) hr))
1168 break; // Cannot allow holes in the cache, must stop now.
1169 }
1171
1172 for (;;)
1173 {
1175 break;
1176 }
1178 if (hMenu)
1179 {
1180 hMenu = GetSubmenuByID(hMenu, FCIDM_SHVIEW_ARRANGE);
1181 for (UINT i = DVIDM_ARRANGESORT_FIRST; i <= DVIDM_ARRANGESORT_LAST && hMenu; ++i)
1182 {
1183 RemoveMenu(hMenu, i, MF_BYCOMMAND);
1184 }
1185 if ((int) GetMenuItemID(hMenu, 0) <= 0)
1186 RemoveMenu(hMenu, 0, MF_BYPOSITION); // Separator
1187 }
1189 LVCOLUMN lvc;
1190 lvc.mask = LVCF_TEXT;
1191 lvc.pszText = buf;
1192 lvc.cchTextMax = _countof(buf);
1193 for (UINT listCol = 0; listCol < DEFVIEW_ARRANGESORT_MAXENUM; ++listCol)
1194 {
1195 if (!ListView_GetColumn(m_ListView.m_hWnd, listCol, &lvc))
1196 break;
1197 HRESULT foldCol = MapListColumnToFolderColumn(listCol);
1198 assert(SUCCEEDED(foldCol));
1200 DVIDM_ARRANGESORT_FIRST + listCol, lvc.pszText, listCol);
1201 }
1202
1203 ListView_RedrawItems(m_ListView.m_hWnd, 0, 0x7fffffff);
1204 m_ListView.InvalidateRect(NULL, TRUE);
1205}
1206
1207/*************************************************************************
1208 * ShellView_ListViewCompareItems
1209 *
1210 * Compare Function for the Listview (FileOpen Dialog)
1211 *
1212 * PARAMS
1213 * lParam1 [I] the first ItemIdList to compare with
1214 * lParam2 [I] the second ItemIdList to compare with
1215 * lpData [I] The column ID for the header Ctrl to process
1216 *
1217 * RETURNS
1218 * A negative value if the first item should precede the second,
1219 * a positive value if the first item should follow the second,
1220 * or zero if the two items are equivalent
1221 */
1223{
1224 PCUIDLIST_RELATIVE pidl1 = reinterpret_cast<PCUIDLIST_RELATIVE>(lParam1);
1225 PCUIDLIST_RELATIVE pidl2 = reinterpret_cast<PCUIDLIST_RELATIVE>(lParam2);
1226 CDefView *pThis = reinterpret_cast<CDefView*>(lpData);
1227
1228 HRESULT hres = pThis->m_pSFParent->CompareIDs(pThis->m_sortInfo.ListColumn, pidl1, pidl2);
1230 return 0;
1231
1232 SHORT nDiff = HRESULT_CODE(hres);
1233 return nDiff * pThis->m_sortInfo.Direction;
1234}
1235
1237{
1238 HWND hHeader;
1239 HDITEM hColumn;
1240 int prevCol = m_sortInfo.ListColumn;
1242
1243 // FIXME: Is this correct? Who sets this style?
1244 // And if it is set, should it also block sorting using the menu?
1245 // Any why should it block sorting when the view is loaded initially?
1246 if (m_ListView.GetWindowLongPtr(GWL_STYLE) & LVS_NOSORTHEADER)
1247 return TRUE;
1248
1249 hHeader = ListView_GetHeader(m_ListView.m_hWnd);
1250 if (Col != -1)
1251 {
1252 if (Col >= Header_GetItemCount(hHeader))
1253 {
1254 ERR("Sort column out of range\n");
1255 return FALSE;
1256 }
1257
1258 if (prevCol == Col)
1259 m_sortInfo.Direction *= -1;
1260 else
1262 m_sortInfo.ListColumn = Col;
1263 }
1264 if (!m_sortInfo.Direction)
1265 m_sortInfo.Direction += 1;
1266
1267 /* If the sorting column changed, remove the sorting style from the old column */
1268 if (prevCol != -1 && prevCol != m_sortInfo.ListColumn)
1269 {
1270 hColumn.mask = HDI_FORMAT;
1271 Header_GetItem(hHeader, prevCol, &hColumn);
1272 hColumn.fmt &= ~(HDF_SORTUP | HDF_SORTDOWN);
1273 Header_SetItem(hHeader, prevCol, &hColumn);
1274 }
1275
1276 /* Set the sorting style on the new column */
1277 hColumn.mask = HDI_FORMAT;
1278 Header_GetItem(hHeader, m_sortInfo.ListColumn, &hColumn);
1279 hColumn.fmt &= ~(HDF_SORTDOWN | HDF_SORTUP);
1280 hColumn.fmt |= (m_sortInfo.Direction > 0 ? HDF_SORTUP : HDF_SORTDOWN);
1281 Header_SetItem(hHeader, m_sortInfo.ListColumn, &hColumn);
1282
1283 /* Sort the list, using the current values of ListColumn and bIsAscending */
1286}
1287
1289{
1290 if (!m_ListView)
1291 return nullptr;
1292 return reinterpret_cast<PCUITEMID_CHILD>(m_ListView.GetItemData(i));
1293}
1294
1296{
1297 if (!m_ListView)
1298 return nullptr;
1299 return reinterpret_cast<PCUITEMID_CHILD>(lvItem.lParam);
1300}
1301
1303{
1305
1306 int cItems = m_ListView.GetItemCount();
1308 for (int i = 0; i < cItems; i++)
1309 {
1310 PCUITEMID_CHILD currentpidl = _PidlByItem(i);
1311 HRESULT hr = m_pSFParent->CompareIDs(lParam, pidl, currentpidl);
1312 if (SUCCEEDED(hr))
1313 {
1314 if (hr == S_EQUAL)
1315 return i;
1316 }
1317 else
1318 {
1319 for (i = 0; i < cItems; i++)
1320 {
1321 currentpidl = _PidlByItem(i);
1322 if (ILIsEqual(pidl, currentpidl))
1323 return i;
1324 }
1325 break;
1326 }
1327 }
1328 return -1;
1329}
1330
1332{
1333 LVITEMW lvItem;
1334
1335 TRACE("(%p)(pidl=%p)\n", this, pidl);
1336
1338
1340 return -1;
1341
1342 lvItem.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM; // set mask
1343 lvItem.iItem = m_ListView.GetItemCount(); // add item to lists end
1344 lvItem.iSubItem = 0;
1345 lvItem.lParam = reinterpret_cast<LPARAM>(ILClone(pidl)); // set item's data
1346 lvItem.pszText = LPSTR_TEXTCALLBACKW; // get text on a callback basis
1347 lvItem.iImage = I_IMAGECALLBACK; // get image on a callback basis
1348 lvItem.stateMask = LVIS_CUT;
1349
1350 return m_ListView.InsertItem(&lvItem);
1351}
1352
1354{
1355 int nIndex;
1356
1357 TRACE("(%p)(pidl=%p)\n", this, pidl);
1358
1360
1361 nIndex = LV_FindItemByPidl(pidl);
1362 if (nIndex < 0)
1363 return FALSE;
1364
1366
1367 return m_ListView.DeleteItem(nIndex);
1368}
1369
1371{
1372 int nItem;
1373 LVITEMW lvItem;
1374
1375 TRACE("(%p)(pidlold=%p pidlnew=%p)\n", this, pidlOld, pidlNew);
1376
1378
1379 nItem = LV_FindItemByPidl(pidlOld);
1380
1381 if (-1 != nItem)
1382 {
1383 lvItem.mask = LVIF_PARAM; // only the pidl
1384 lvItem.iItem = nItem;
1385 lvItem.iSubItem = 0;
1386 m_ListView.GetItem(&lvItem);
1387
1388 // Store old pidl until new item is replaced
1389 LPVOID oldPidl = reinterpret_cast<LPVOID>(lvItem.lParam);
1390
1391 lvItem.mask = LVIF_PARAM | LVIF_IMAGE | LVIF_TEXT;
1392 lvItem.iItem = nItem;
1393 lvItem.iSubItem = 0;
1394 lvItem.lParam = reinterpret_cast<LPARAM>(ILClone(pidlNew)); // set item's data
1397 m_ListView.SetItem(&lvItem);
1398 m_ListView.Update(nItem);
1399
1400 // Now that the new item is in place, we can safely release the old pidl
1401 SHFree(oldPidl);
1402
1403 return TRUE; // FIXME: better handling
1404 }
1405
1406 return FALSE;
1407}
1408
1410{
1411 int nItem;
1412 LVITEMW lvItem;
1413
1414 TRACE("(%p)(pidl=%p)\n", this, pidl);
1415
1417
1418 nItem = LV_FindItemByPidl(pidl);
1419
1420 if (-1 != nItem)
1421 {
1423
1424 lvItem.mask = LVIF_IMAGE;
1425 lvItem.iItem = nItem;
1426 lvItem.iSubItem = 0;
1428 m_ListView.SetItem(&lvItem);
1429 m_ListView.Update(nItem);
1430 return TRUE;
1431 }
1432
1433 return FALSE;
1434}
1435
1437{
1439
1440 LVITEMW lvItem = { LVIF_IMAGE };
1441 lvItem.iItem = iItem;
1442 lvItem.iImage = I_IMAGECALLBACK;
1443 m_ListView.SetItem(&lvItem);
1444 m_ListView.Update(iItem);
1445}
1446
1448{
1450
1451 for (INT iItem = -1;;)
1452 {
1453 iItem = ListView_GetNextItem(m_ListView, iItem, LVNI_ALL);
1454 if (iItem == -1)
1455 break;
1456
1457 LV_RefreshIcon(iItem);
1458 }
1459}
1460
1462{
1463 PITEMID_CHILD pidl = static_cast<PITEMID_CHILD>(ptr);
1464 CDefView *pThis = static_cast<CDefView *>(arg);
1465
1466 // in a commdlg this works as a filemask
1467 if (pThis->IncludeObject(pidl) == S_OK && pThis->m_ListView)
1468 pThis->LV_AddItem(pidl);
1469
1470 SHFree(pidl);
1471 return TRUE;
1472}
1473
1475// - gets the objectlist from the shellfolder
1476// - sorts the list
1477// - fills the list into the view
1479{
1480 CComPtr<IEnumIDList> pEnumIDList;
1481 PITEMID_CHILD pidl;
1482 DWORD dwFetched;
1483 HRESULT hRes;
1484 HDPA hdpa;
1485 DWORD dFlags = SHCONTF_NONFOLDERS | ((m_FolderSettings.fFlags & FWF_NOSUBFOLDERS) ? 0 : SHCONTF_FOLDERS);
1486
1487 TRACE("%p\n", this);
1488
1489 SHELLSTATE shellstate;
1491 if (GetCommDlgViewFlags() & CDB2GVF_SHOWALLFILES)
1492 shellstate.fShowAllObjects = shellstate.fShowSuperHidden = TRUE;
1493
1494 if (shellstate.fShowAllObjects)
1495 {
1496 dFlags |= SHCONTF_INCLUDEHIDDEN;
1497 m_ListView.SendMessageW(LVM_SETCALLBACKMASK, LVIS_CUT, 0);
1498 }
1499 if (shellstate.fShowSuperHidden)
1500 {
1501 dFlags |= SHCONTF_INCLUDESUPERHIDDEN;
1502 m_ListView.SendMessageW(LVM_SETCALLBACKMASK, LVIS_CUT, 0);
1503 }
1504
1505 // get the itemlist from the shfolder
1506 hRes = m_pSFParent->EnumObjects(m_hWnd, dFlags, &pEnumIDList);
1507 if (hRes != S_OK)
1508 {
1509 if (hRes == S_FALSE)
1510 return(NOERROR);
1511 return(hRes);
1512 }
1513
1514 // create a pointer array
1515 hdpa = DPA_Create(16);
1516 if (!hdpa)
1517 return(E_OUTOFMEMORY);
1518
1519 // copy the items into the array
1520 while((S_OK == pEnumIDList->Next(1, &pidl, &dwFetched)) && dwFetched)
1521 {
1522 if (DPA_InsertPtr(hdpa, 0x7fff, pidl) == -1)
1523 {
1524 SHFree(pidl);
1525 }
1526 }
1527
1528 // turn listview's redrawing off
1530
1531 DPA_DestroyCallback( hdpa, fill_list, this);
1532
1533 /* sort the array */
1534 int sortCol = -1;
1535 if (!IsRefreshCommand && !m_sortInfo.bLoadedFromViewState) // Are we loading for the first time?
1536 {
1538 sortCol = 0; // In case the folder does not know/care
1539 if (m_pSF2Parent)
1540 {
1541 ULONG folderSortCol = sortCol, dummy;
1542 HRESULT hr = m_pSF2Parent->GetDefaultColumn(NULL, &folderSortCol, &dummy);
1543 if (SUCCEEDED(hr))
1544 hr = MapFolderColumnToListColumn(folderSortCol);
1545 if (SUCCEEDED(hr))
1546 sortCol = (int) hr;
1547 }
1548 }
1549 _Sort(sortCol);
1550
1552 {
1555 }
1556
1557 // load custom background image and custom text color
1560
1561 // turn listview's redrawing back on and force it to draw
1563
1565
1567 {
1568 // redraw now
1569 m_ListView.InvalidateRect(NULL, TRUE);
1570 }
1571
1573
1574 return S_OK;
1575}
1576
1578{
1579 if (m_ListView.IsWindow())
1580 m_ListView.UpdateWindow();
1581 bHandled = FALSE;
1582 return 0;
1583}
1584
1586{
1587 return m_ListView.SendMessageW(uMsg, 0, 0);
1588}
1589
1591{
1592 if (!m_Destroyed)
1593 {
1594 m_Destroyed = TRUE;
1595 if (m_hMenu)
1596 {
1598 m_hMenu = NULL;
1599 }
1602 m_hNotify = NULL;
1605 }
1606 bHandled = FALSE;
1607 return 0;
1608}
1609
1611{
1612 /* redirect to parent */
1615
1616 bHandled = FALSE;
1617 return 0;
1618}
1619
1620static VOID
1622{
1623 INT x0 = prc->left, y0 = prc->top, x1 = prc->right, y1 = prc->bottom;
1624 x0 += dx;
1625 y0 += dy;
1626
1627 HDC hMemDC = CreateCompatibleDC(hDC);
1628 HGDIOBJ hbmOld = SelectObject(hMemDC, hbm);
1629
1630 for (INT y = y0; y < y1; y += nHeight)
1631 {
1632 for (INT x = x0; x < x1; x += nWidth)
1633 {
1634 BitBlt(hDC, x, y, nWidth, nHeight, hMemDC, 0, 0, SRCCOPY);
1635 }
1636 }
1637
1638 SelectObject(hMemDC, hbmOld);
1639 DeleteDC(hMemDC);
1640}
1641
1643{
1644 HDC hDC = (HDC)wParam;
1645
1646 RECT rc;
1648
1650 {
1651 BITMAP bm;
1652 if (::GetObject(m_viewinfo_data.hbmBack, sizeof(BITMAP), &bm))
1653 {
1654 INT dx = -(::GetScrollPos(m_ListView, SB_HORZ) % bm.bmWidth);
1655 INT dy = -(::GetScrollPos(m_ListView, SB_VERT) % bm.bmHeight);
1656 DrawTileBitmap(hDC, &rc, m_viewinfo_data.hbmBack, bm.bmWidth, bm.bmHeight, dx, dy);
1657 }
1658 }
1659 else
1660 {
1662 }
1663
1664 bHandled = TRUE;
1665
1666 return TRUE;
1667}
1668
1670{
1671 /* Update desktop labels color */
1673
1674 /* Forward WM_SYSCOLORCHANGE to common controls */
1675 return m_ListView.SendMessageW(uMsg, 0, 0);
1676}
1677
1679{
1680 return reinterpret_cast<LRESULT>(m_pShellBrowser.p);
1681}
1682
1684{
1685 this->AddRef();
1686 bHandled = FALSE;
1687 return 0;
1688}
1689
1691{
1692 this->Release();
1693}
1694
1696{
1699
1700 TRACE("%p\n", this);
1701
1703 {
1704 if (FAILED(RegisterDragDrop(m_hWnd, pdt)))
1705 ERR("Error Registering DragDrop\n");
1706 }
1707
1708 /* register for receiving notifications */
1709 m_pSFParent->QueryInterface(IID_PPV_ARG(IPersistFolder2, &ppf2));
1710 if (ppf2)
1711 {
1712 ppf2->GetCurFolder(&m_pidlParent);
1713 }
1714
1715 if (CreateList())
1716 {
1717 if (InitList())
1718 {
1719 FillList(FALSE);
1720 }
1721 }
1722
1724 {
1725 HWND hwndSB;
1726 m_pShellBrowser->GetWindow(&hwndSB);
1728 }
1729
1730 // Set up change notification
1731 LPITEMIDLIST pidlTarget = NULL;
1732 LONG fEvents = 0;
1733 HRESULT hr = _DoFolderViewCB(SFVM_GETNOTIFY, (WPARAM)&pidlTarget, (LPARAM)&fEvents);
1734 if (FAILED(hr) || (!pidlTarget && !fEvents)) // FIXME: MSDN says both zero means no notifications
1735 {
1736 pidlTarget = m_pidlParent;
1737 fEvents = SHCNE_ALLEVENTS;
1738 }
1739 SHChangeNotifyEntry ntreg = {};
1741 if (FAILED(hr))
1742 {
1743 ntreg.fRecursive = FALSE;
1744 ntreg.pidl = pidlTarget;
1745 }
1749 fEvents, SHV_CHANGE_NOTIFY,
1750 1, &ntreg);
1751
1753
1754 BOOL bPreviousParentSpecial = m_isParentFolderSpecial;
1755
1756 // A folder is special if it is the Desktop folder,
1757 // a network folder, or a Control Panel folder
1760
1761 // Only force StatusBar part refresh if the state
1762 // changed from the previous folder
1763 if (bPreviousParentSpecial != m_isParentFolderSpecial)
1764 {
1765 // This handles changing StatusBar parts
1767 }
1768
1770
1771 return S_OK;
1772}
1773
1774// #### Handling of the menus ####
1775
1777{
1779 if (!hFileMenu)
1780 return E_FAIL;
1781
1782 /* Cleanup the items added previously */
1783 for (int i = GetMenuItemCount(hFileMenu) - 1; i >= 0; i--)
1784 {
1785 UINT id = GetMenuItemID(hFileMenu, i);
1786 if (id < FCIDM_BROWSERFIRST || id > FCIDM_BROWSERLAST)
1787 DeleteMenu(hFileMenu, i, MF_BYPOSITION);
1788 }
1789
1790 // In case we still have this left over, clean it up
1791 if (m_pFileMenu)
1792 {
1795 }
1796 UINT selcount = m_ListView.GetSelectedCount();
1797 // Store context menu in m_pFileMenu and keep it to invoke the selected command later on
1800 return hr;
1801
1803
1804 UINT cmf = GetContextMenuFlags(m_pShellBrowser, SFGAO_CANRENAME);
1805 hr = m_pFileMenu->QueryContextMenu(hmenu, 0, DVIDM_CONTEXTMENU_FIRST, DVIDM_CONTEXTMENU_LAST, cmf);
1807 return hr;
1808
1809 // TODO: filter or something
1810 if (!selcount)
1811 {
1815 }
1816
1817 Shell_MergeMenus(hFileMenu, hmenu, 0, 0, 0xFFFF, MM_ADDSEPARATOR | MM_SUBMENUSHAVEIDS);
1819 return S_OK;
1820}
1821
1823{
1825 if (!hEditMenu)
1826 return E_FAIL;
1827
1828 HMENU hmenuContents = ::LoadMenuW(shell32_hInstance, L"MENU_003");
1829 if (!hmenuContents)
1830 return E_FAIL;
1831
1832 Shell_MergeMenus(hEditMenu, hmenuContents, 0, 0, 0xFFFF, 0);
1833
1834 ::DestroyMenu(hmenuContents);
1835
1836 return S_OK;
1837}
1838
1840{
1842 if (!hViewMenu)
1843 return E_FAIL;
1844
1846 if (!m_hMenuViewModes)
1847 return E_FAIL;
1848
1851
1852 return S_OK;
1853}
1854
1856{
1857 bool forceMerge = false;
1859
1860 // Make sure the column we currently sort by is in the menu
1863 {
1867 LVCOLUMN lvc;
1868 lvc.mask = LVCF_TEXT;
1869 lvc.pszText = buf;
1870 lvc.cchTextMax = _countof(buf);
1871 currentSortId = DVIDM_ARRANGESORT_LAST;
1872 forceMerge = true;
1874 AppendMenuItem(m_hMenuArrangeModes, MF_STRING, currentSortId, lvc.pszText, m_sortInfo.ListColumn);
1875 }
1876
1877 // Prepend the sort-by items unless they are aleady there
1878 if (GetMenuItemID(hmenuArrange, 0) == FCIDM_SHVIEW_AUTOARRANGE || forceMerge)
1879 {
1880 Shell_MergeMenus(hmenuArrange, m_hMenuArrangeModes, 0, 0, 0xFFFF, MM_ADDSEPARATOR);
1881 }
1882
1883 CheckMenuRadioItem(hmenuArrange,
1885 currentSortId, MF_BYCOMMAND);
1886
1888 {
1891 }
1892 else
1893 {
1896
1897 if (GetAutoArrange() == S_OK)
1899 else
1901
1902 if (_GetSnapToGrid() == S_OK)
1904 else
1906 }
1907
1908 return S_OK;
1909}
1910
1912{
1914 {
1915 UINT iItemFirst = FCIDM_SHVIEW_BIGICON;
1916 UINT iItemLast = iItemFirst + FVM_LAST - FVM_FIRST;
1917 UINT iItem = iItemFirst + m_FolderSettings.ViewMode - FVM_FIRST;
1918 CheckMenuRadioItem(hmenuView, iItemFirst, iItemLast, iItem, MF_BYCOMMAND);
1919 }
1920
1921 return S_OK;
1922}
1923
1925{
1926 const UINT maxItems = 15; // Feels about right
1927 const UINT idMore = 0x1337;
1928 UINT idFirst = idMore + 1, idLast = idFirst;
1929 UINT lastValidListCol = 0; // Keep track of where the new column should be inserted
1930 UINT showMore = GetKeyState(VK_SHIFT) < 0;
1932 HWND hWndHdr = ListView_GetHeader(m_ListView.m_hWnd);
1933
1934 if (lParam == ~0)
1935 {
1936 RECT r;
1937 ::GetWindowRect(hWndHdr, &r);
1938 pt.x = r.left + ((r.right - r.left) / 2);
1939 pt.y = r.top + ((r.bottom - r.top) / 2);
1940 }
1941
1942 HMENU hMenu = CreatePopupMenu();
1943 if (!hMenu)
1944 return 0;
1945
1946 for (UINT foldCol = 0;; ++foldCol)
1947 {
1950 sd.str.uType = !STRRET_WSTR;
1951 if (FAILED(GetDetailsByFolderColumn(NULL, foldCol, sd)))
1952 break;
1953 if (FAILED(StrRetToStrNW(buf, _countof(buf), &sd.str, NULL)))
1954 break;
1955
1956 SHCOLSTATEF state = 0;
1957 if (!m_pSF2Parent || FAILED(m_pSF2Parent->GetDefaultColumnState(foldCol, &state)))
1958 state = 0;
1959 showMore |= (state & (SHCOLSTATE_SECONDARYUI));
1960
1961 UINT mf = MF_STRING;
1962 HRESULT listCol = MapFolderColumnToListColumn(foldCol);
1963
1964 if (foldCol == 0)
1965 mf |= MF_CHECKED | MF_GRAYED | MF_DISABLED; // Force column 0
1967 continue;
1968 else if (SUCCEEDED(listCol))
1969 mf |= MF_CHECKED;
1970
1971 if (AppendMenuItem(hMenu, mf, idLast, buf, lastValidListCol + 1))
1972 {
1973 idLast++;
1974 if (SUCCEEDED(listCol))
1975 lastValidListCol = listCol;
1976 }
1977
1978 if (idLast - idFirst == maxItems)
1979 {
1980 showMore++;
1981 break;
1982 }
1983 }
1984
1985 if (showMore)
1986 {
1987#if 0 // TODO
1988 InsertMenuW(hMenu, -1, MF_SEPARATOR, 0, NULL);
1989 InsertMenuW(hMenu, -1, MF_STRING, idMore, L"More...");
1990#endif
1991 }
1992
1993 // A cludge to force the cursor to update so we are not stuck with "size left/right" if
1994 // the right-click was on a column divider.
1996
1997 // Note: Uses the header as the owner so CDefView::OnInitMenuPopup does not mess us up.
1999 pt.x, pt.y, 0, hWndHdr, NULL);
2000 if (idCmd == idMore)
2001 {
2002 FIXME("Open More dialog\n");
2003 }
2004 else if (idCmd)
2005 {
2006 UINT foldCol = idCmd - idFirst;
2007 HRESULT listCol = MapFolderColumnToListColumn(foldCol);
2008 if (SUCCEEDED(listCol))
2009 {
2010 ListView_DeleteColumn(m_ListView.m_hWnd, listCol);
2011 }
2012 else
2013 {
2014 listCol = (UINT) GetMenuItemDataById(hMenu, idCmd);
2015 LoadColumn(foldCol, listCol, TRUE);
2016 }
2018 }
2019 DestroyMenu(hMenu);
2020 return 0;
2021}
2022
2023/**********************************************************
2024* ShellView_GetSelections()
2025*
2026* - fills the m_apidl list with the selected objects
2027*
2028* RETURNS
2029* number of selected items
2030*/
2032{
2034 if (count > m_cidl || !count || !m_apidl) // !count to free possibly large cache, !m_apidl to make sure m_apidl is a valid pointer
2035 {
2036 SHFree(m_apidl);
2037 m_apidl = static_cast<PCUITEMID_CHILD*>(SHAlloc(count * sizeof(PCUITEMID_CHILD)));
2038 if (!m_apidl)
2039 {
2040 m_cidl = 0;
2041 return 0;
2042 }
2043 }
2044 m_cidl = count;
2045
2046 TRACE("-- Items selected =%u\n", m_cidl);
2047
2049
2050 UINT i = 0;
2051 int lvIndex = -1;
2052 while ((lvIndex = m_ListView.GetNextItem(lvIndex, LVNI_SELECTED)) > -1)
2053 {
2054 m_apidl[i] = _PidlByItem(lvIndex);
2055 i++;
2056 if (i == m_cidl)
2057 break;
2058 TRACE("-- selected Item found\n");
2059 }
2060
2061 return m_cidl;
2062}
2063
2065{
2066 CMINVOKECOMMANDINFOEX cmi;
2067
2068 ZeroMemory(&cmi, sizeof(cmi));
2069 cmi.cbSize = sizeof(cmi);
2070 cmi.hwnd = m_hWnd;
2071 cmi.lpVerb = lpVerb;
2072 cmi.nShow = SW_SHOW;
2073
2074 if (GetKeyState(VK_SHIFT) < 0)
2075 cmi.fMask |= CMIC_MASK_SHIFT_DOWN;
2076
2078 cmi.fMask |= CMIC_MASK_CONTROL_DOWN;
2079
2080 if (pt)
2081 {
2082 cmi.fMask |= CMIC_MASK_PTINVOKE;
2083 cmi.ptInvoke = *pt;
2084 }
2085
2086 WCHAR szDirW[MAX_PATH] = L"";
2087 CHAR szDirA[MAX_PATH];
2089 *szDirW != UNICODE_NULL)
2090 {
2091 SHUnicodeToAnsi(szDirW, szDirA, _countof(szDirA));
2092 cmi.fMask |= CMIC_MASK_UNICODE;
2093 cmi.lpDirectory = szDirA;
2094 cmi.lpDirectoryW = szDirW;
2095 }
2096
2097 HRESULT hr = pCM->InvokeCommand((LPCMINVOKECOMMANDINFO)&cmi);
2098 // Most of our callers will do this, but if they would forget (File menu!)
2099 IUnknown_SetSite(pCM, NULL);
2100 pCM.Release();
2101
2103 return hr;
2104
2105 return S_OK;
2106}
2107
2109{
2110 HMENU hMenu;
2111 UINT uCommand;
2112 HRESULT hResult;
2113
2114 if (m_ListView.GetSelectedCount() == 0)
2115 return S_OK;
2116
2117 hResult = OnDefaultCommand();
2118 if (hResult == S_OK)
2119 return hResult;
2120
2121 hMenu = CreatePopupMenu();
2122 if (!hMenu)
2123 return E_FAIL;
2124
2127 MenuCleanup _(pCM, hMenu);
2128 if (FAILED_UNEXPECTEDLY(hResult))
2129 return hResult;
2130
2131 UINT cmf = CMF_DEFAULTONLY | GetContextMenuFlags(m_pShellBrowser, 0);
2132 hResult = pCM->QueryContextMenu(hMenu, 0, DVIDM_CONTEXTMENU_FIRST, DVIDM_CONTEXTMENU_LAST, cmf);
2133 if (FAILED_UNEXPECTEDLY(hResult))
2134 return hResult;
2135
2136 uCommand = GetMenuDefaultItem(hMenu, FALSE, 0);
2137 if (uCommand == (UINT)-1)
2138 {
2139 ERR("GetMenuDefaultItem returned -1\n");
2140 return E_FAIL;
2141 }
2142
2144
2145 return hResult;
2146}
2147
2149{
2151 UINT uCommand;
2152 HRESULT hResult;
2153
2154 TRACE("(%p)\n", this);
2155
2156 if (m_hContextMenu != NULL)
2157 {
2158 ERR("HACK: Aborting context menu in nested call\n");
2159 return 0;
2160 }
2161
2162 HWND hWndHdr = ListView_GetHeader(m_ListView.m_hWnd);
2163 RECT r;
2164 if (::GetWindowRect(hWndHdr, &r) && PtInRect(&r, pt) && ::IsWindowVisible(hWndHdr))
2165 {
2167 }
2168
2170 if (!m_hContextMenu)
2171 return E_FAIL;
2172
2173 if (lParam != ~0) // unless app key (menu key) was pressed
2174 {
2175 LV_HITTESTINFO hittest = { pt };
2176 ScreenToClient(&hittest.pt);
2177 m_ListView.HitTest(&hittest);
2178
2179 // Right-Clicked item is selected? If selected, no selection change.
2180 // If not selected, then reset the selection and select the item.
2181 if ((hittest.flags & LVHT_ONITEM) &&
2183 {
2184 SelectItem(hittest.iItem, SVSI_SELECT | SVSI_DESELECTOTHERS | SVSI_ENSUREVISIBLE);
2185 }
2186 }
2187
2189 // In case we still have this left over, clean it up
2192 if (FAILED_UNEXPECTEDLY(hResult))
2193 return 0;
2194
2195 UINT cmf = GetContextMenuFlags(m_pShellBrowser, SFGAO_CANRENAME);
2196 // Use 1 as the first id we want. 0 means that user canceled the menu
2197 hResult = m_pCM->QueryContextMenu(m_hContextMenu, 0, CONTEXT_MENU_BASE_ID, DVIDM_CONTEXTMENU_LAST, cmf);
2198 if (FAILED_UNEXPECTEDLY(hResult))
2199 return 0;
2200
2201 if (m_pCommDlgBrowser && !(GetCommDlgViewFlags() & CDB2GVF_NOSELECTVERB))
2202 {
2203 HMENU hMenuSource = LoadMenuW(_AtlBaseModule.GetResourceInstance(), MAKEINTRESOURCEW(IDM_DVSELECT));
2205 DestroyMenu(hMenuSource);
2207 // TODO: ICommDlgBrowser2::GetDefaultMenuText == S_OK
2208 }
2209
2210 // There is no position requested, so try to find one
2211 if (lParam == ~0)
2212 {
2213 HWND hFocus = ::GetFocus();
2214 int lvIndex = -1;
2215
2216 if (hFocus == m_ListView.m_hWnd || m_ListView.IsChild(hFocus))
2217 {
2218 // Is there an item focused and selected?
2220 // If not, find the first selected item
2221 if (lvIndex < 0)
2222 lvIndex = m_ListView.GetNextItem(-1, LVNI_SELECTED);
2223 }
2224
2225 // We got something
2226 if (lvIndex > -1)
2227 {
2228 // Find the center of the icon
2229 RECT rc = { LVIR_ICON };
2230 m_ListView.SendMessage(LVM_GETITEMRECT, lvIndex, (LPARAM)&rc);
2231 pt.x = (rc.right + rc.left) / 2;
2232 pt.y = (rc.bottom + rc.top) / 2;
2233 }
2234 else
2235 {
2236 // We have to drop it somewhere
2237 pt.x = pt.y = 0;
2238 }
2239
2240 m_ListView.ClientToScreen(&pt);
2241 }
2242
2245 pcdb2->Notify(static_cast<IShellView*>(this), CDB2N_CONTEXTMENU_START);
2246
2247 // This runs the message loop, calling back to us with f.e. WM_INITPOPUP (hence why m_hContextMenu and m_pCM exist)
2248 uCommand = TrackPopupMenu(m_hContextMenu,
2250 pt.x, pt.y, 0, m_hWnd, NULL);
2251 if (uCommand >= DVIDM_ARRANGESORT_FIRST && uCommand <= DVIDM_ARRANGESORT_LAST)
2252 {
2253 SendMessage(WM_COMMAND, uCommand, 0);
2254 }
2255 else if (uCommand != 0 && !(uCommand == DVIDM_COMMDLG_SELECT && OnDefaultCommand() == S_OK))
2256 {
2258 }
2259
2260 if (pcdb2)
2261 pcdb2->Notify(static_cast<IShellView*>(this), CDB2N_CONTEXTMENU_DONE);
2262 return 0;
2263}
2264
2266{
2267 HRESULT hResult;
2268 HMENU hMenu = NULL;
2269
2271 hResult = GetItemObject(bUseSelection ? SVGIO_SELECTION : SVGIO_BACKGROUND, IID_PPV_ARG(IContextMenu, &pCM));
2272 if (FAILED_UNEXPECTEDLY(hResult))
2273 return 0;
2274
2275 MenuCleanup _(pCM, hMenu);
2276
2277 if ((uCommand != FCIDM_SHVIEW_DELETE) && (uCommand != FCIDM_SHVIEW_RENAME))
2278 {
2279 hMenu = CreatePopupMenu();
2280 if (!hMenu)
2281 return 0;
2282
2283 hResult = pCM->QueryContextMenu(hMenu, 0, DVIDM_CONTEXTMENU_FIRST, DVIDM_CONTEXTMENU_LAST, CMF_NORMAL);
2284 if (FAILED_UNEXPECTEDLY(hResult))
2285 return 0;
2286 }
2287
2288 if (bUseSelection)
2289 {
2290 // FIXME: we should cache this
2291 SFGAOF rfg = SFGAO_BROWSABLE | SFGAO_CANCOPY | SFGAO_CANLINK | SFGAO_CANMOVE | SFGAO_CANDELETE | SFGAO_CANRENAME | SFGAO_HASPROPSHEET | SFGAO_FILESYSTEM | SFGAO_FOLDER;
2292 hResult = m_pSFParent->GetAttributesOf(m_cidl, m_apidl, &rfg);
2293 if (FAILED_UNEXPECTEDLY(hResult))
2294 return 0;
2295
2296 if (!(rfg & SFGAO_CANMOVE) && uCommand == FCIDM_SHVIEW_CUT)
2297 return 0;
2298 if (!(rfg & SFGAO_CANCOPY) && uCommand == FCIDM_SHVIEW_COPY)
2299 return 0;
2300 if (!(rfg & SFGAO_CANDELETE) && uCommand == FCIDM_SHVIEW_DELETE)
2301 return 0;
2302 if (!(rfg & SFGAO_CANRENAME) && uCommand == FCIDM_SHVIEW_RENAME)
2303 return 0;
2304 if (!(rfg & SFGAO_HASPROPSHEET) && uCommand == FCIDM_SHVIEW_PROPERTIES)
2305 return 0;
2306 }
2307
2308 // FIXME: We should probably use the objects position?
2310 return 0;
2311}
2312
2313// ##### message handling #####
2314
2316{
2317 WORD wWidth, wHeight;
2318
2319 wWidth = LOWORD(lParam);
2320 wHeight = HIWORD(lParam);
2321
2322 TRACE("%p width=%u height=%u\n", this, wWidth, wHeight);
2323
2324 // WM_SIZE can come before WM_CREATE
2325 if (!m_ListView)
2326 return 0;
2327
2328 /* Resize the ListView to fit our window */
2329 ::MoveWindow(m_ListView, 0, 0, wWidth, wHeight, TRUE);
2330
2332
2335
2336 return 0;
2337}
2338
2339// internal
2341{
2342 TRACE("%p\n", this);
2343
2345 {
2346 // TODO: cleanup menu after deactivation
2348 }
2349}
2350
2352{
2353 TRACE("%p uState=%x\n", this, uState);
2354
2355 // don't do anything if the state isn't really changing
2356 if (m_uState == uState)
2357 {
2358 return;
2359 }
2360
2361 if (uState == SVUIA_DEACTIVATE)
2362 {
2363 OnDeactivate();
2364 }
2365 else
2366 {
2368 {
2369 FillEditMenu();
2370 FillViewMenu();
2371 m_pShellBrowser->SetMenuSB(m_hMenu, 0, m_hWnd);
2373 }
2374
2375 if (SVUIA_ACTIVATE_FOCUS == uState)
2376 {
2377 m_ListView.SetFocus();
2378 }
2379 }
2380
2381 m_uState = uState;
2382 TRACE("--\n");
2383}
2384
2386{
2387 if (!GetSelections())
2388 return;
2389
2390 SFGAOF rfg = SFGAO_CANCOPY | SFGAO_CANMOVE | SFGAO_FILESYSTEM;
2391 HRESULT hr = m_pSFParent->GetAttributesOf(m_cidl, m_apidl, &rfg);
2393 return;
2394
2395 if (!bCopy && !(rfg & SFGAO_CANMOVE))
2396 return;
2397 if (bCopy && !(rfg & SFGAO_CANCOPY))
2398 return;
2399
2401 hr = m_pSFParent->GetUIObjectOf(m_hWnd, m_cidl, m_apidl, IID_IContextMenu, 0, (void **)&pCM);
2403 return;
2404
2405 InvokeContextMenuCommand(pCM, (bCopy ? "copyto" : "moveto"), NULL);
2406}
2407
2409{
2411 return 0;
2412}
2413
2415{
2416 TRACE("%p\n", this);
2417
2418 /* Tell the browser one of our windows has received the focus. This
2419 should always be done before merging menus (OnActivate merges the
2420 menus) if one of our windows has the focus.*/
2421
2422 m_pShellBrowser->OnViewWindowActive(this);
2424
2425 /* Set the focus to the listview */
2426 m_ListView.SetFocus();
2427
2428 /* Notify the ICommDlgBrowser interface */
2429 OnStateChange(CDBOSC_SETFOCUS);
2430
2431 return 0;
2432}
2433
2435{
2436 TRACE("(%p) stub\n", this);
2437
2439 /* Notify the ICommDlgBrowser */
2440 OnStateChange(CDBOSC_KILLFOCUS);
2441
2442 return 0;
2443}
2444
2445// the CmdID's are the ones from the context menu
2447{
2448 DWORD dwCmdID;
2449 DWORD dwCmd;
2450 HWND hwndCmd;
2451 int nCount;
2452
2453 dwCmdID = GET_WM_COMMAND_ID(wParam, lParam);
2455 hwndCmd = GET_WM_COMMAND_HWND(wParam, lParam);
2456
2457 TRACE("(%p)->(0x%08x 0x%08x %p) stub\n", this, dwCmdID, dwCmd, hwndCmd);
2458
2459 if (dwCmdID >= DVIDM_ARRANGESORT_FIRST && dwCmdID <= DVIDM_ARRANGESORT_LAST)
2460 {
2461 UINT listCol = (UINT)GetMenuItemDataById(m_hMenuArrangeModes, dwCmdID);
2462 _Sort(listCol);
2463 return 0;
2464 }
2465
2466 switch (dwCmdID)
2467 {
2471 CheckToolbar();
2472 break;
2475 m_ListView.ModifyStyle(LVS_TYPEMASK, LVS_ICON);
2476 CheckToolbar();
2477 break;
2480 m_ListView.ModifyStyle(LVS_TYPEMASK, LVS_LIST);
2481 CheckToolbar();
2482 break;
2485 m_ListView.ModifyStyle(LVS_TYPEMASK, LVS_REPORT);
2486 CheckToolbar();
2487 break;
2490 break;
2492 if (_GetSnapToGrid() == S_OK)
2494 else
2495 ArrangeGrid();
2496 break;
2498 if (GetAutoArrange() == S_OK)
2499 m_ListView.ModifyStyle(LVS_AUTOARRANGE, 0);
2500 else
2501 AutoArrange();
2502 break;
2506 break;
2508 nCount = m_ListView.GetItemCount();
2509 for (int i=0; i < nCount; i++)
2511 break;
2513 Refresh();
2514 break;
2516 case FCIDM_SHVIEW_CUT:
2517 case FCIDM_SHVIEW_COPY:
2521 return 0;
2522 return OnExplorerCommand(dwCmdID, TRUE);
2526 return 0;
2528 case FCIDM_SHVIEW_UNDO:
2531 return OnExplorerCommand(dwCmdID, FALSE);
2532 default:
2533 // WM_COMMAND messages from file menu are routed to CDefView to let m_pFileMenu handle them
2534 if (m_pFileMenu && dwCmd == 0)
2535 {
2536 HMENU Dummy = NULL;
2537 MenuCleanup _(m_pFileMenu, Dummy);
2539 }
2540 }
2541
2542 return 0;
2543}
2544
2545static BOOL
2547{
2548 HKEY hKey;
2549 LONG error;
2550 DWORD dwValue = FALSE, cbValue;
2551
2553 if (error)
2554 return dwValue;
2555
2556 cbValue = sizeof(dwValue);
2557 RegQueryValueExW(hKey, L"SelectExtOnRename", NULL, NULL, (LPBYTE)&dwValue, &cbValue);
2558
2560 return !!dwValue;
2561}
2562
2564{
2565 UINT CtlID;
2566 LPNMHDR lpnmh;
2567 LPNMLISTVIEW lpnmlv;
2568 NMLVDISPINFOW *lpdi;
2569 PCUITEMID_CHILD pidl;
2570 BOOL unused;
2571
2572 CtlID = wParam;
2573 lpnmh = (LPNMHDR)lParam;
2574 lpnmlv = (LPNMLISTVIEW)lpnmh;
2575 lpdi = (NMLVDISPINFOW *)lpnmh;
2576
2577 TRACE("%p CtlID=%u lpnmh->code=%x\n", this, CtlID, lpnmh->code);
2578
2579 switch (lpnmh->code)
2580 {
2581 case NM_SETFOCUS:
2582 TRACE("-- NM_SETFOCUS %p\n", this);
2583 OnSetFocus(0, 0, 0, unused);
2584 break;
2585 case NM_KILLFOCUS:
2586 TRACE("-- NM_KILLFOCUS %p\n", this);
2587 OnDeactivate();
2588 /* Notify the ICommDlgBrowser interface */
2589 OnStateChange(CDBOSC_KILLFOCUS);
2590 break;
2591 case NM_CUSTOMDRAW:
2592 TRACE("-- NM_CUSTOMDRAW %p\n", this);
2593 return CDRF_DODEFAULT;
2594 case NM_RELEASEDCAPTURE:
2595 TRACE("-- NM_RELEASEDCAPTURE %p\n", this);
2596 break;
2597 case NM_CLICK:
2598 TRACE("-- NM_CLICK %p\n", this);
2599 break;
2600 case NM_RCLICK:
2601 TRACE("-- NM_RCLICK %p\n", this);
2602 break;
2603 case NM_DBLCLK:
2604 TRACE("-- NM_DBLCLK %p\n", this);
2606 break;
2607 case NM_RETURN:
2608 TRACE("-- NM_RETURN %p\n", this);
2610 break;
2611 case HDN_ENDTRACKW:
2612 TRACE("-- HDN_ENDTRACKW %p\n", this);
2613 //nColumn1 = m_ListView.GetColumnWidth(0);
2614 //nColumn2 = m_ListView.GetColumnWidth(1);
2615 break;
2616 case LVN_DELETEITEM:
2617 TRACE("-- LVN_DELETEITEM %p\n", this);
2618 /*delete the pidl because we made a copy of it*/
2619 SHFree(reinterpret_cast<LPVOID>(lpnmlv->lParam));
2620 break;
2621 case LVN_DELETEALLITEMS:
2622 TRACE("-- LVN_DELETEALLITEMS %p\n", this);
2623 return FALSE;
2624 case LVN_INSERTITEM:
2625 TRACE("-- LVN_INSERTITEM (STUB)%p\n", this);
2626 break;
2627 case LVN_ITEMACTIVATE:
2628 TRACE("-- LVN_ITEMACTIVATE %p\n", this);
2629 OnStateChange(CDBOSC_SELCHANGE); // browser will get the IDataObject
2630 break;
2631 case LVN_COLUMNCLICK:
2632 {
2633 UINT foldercol = MapListColumnToFolderColumn(lpnmlv->iSubItem);
2634 HRESULT hr = S_FALSE;
2635 if (m_pSDParent)
2636 hr = m_pSDParent->ColumnClick(foldercol);
2637 if (hr != S_OK)
2638 hr = _DoFolderViewCB(SFVM_COLUMNCLICK, foldercol, 0);
2639 if (hr != S_OK)
2640 _Sort(lpnmlv->iSubItem);
2641 break;
2642 }
2643 case LVN_GETDISPINFOA:
2644 case LVN_GETDISPINFOW:
2645 TRACE("-- LVN_GETDISPINFO %p\n", this);
2646 pidl = _PidlByItem(lpdi->item);
2647
2648 if (lpdi->item.mask & LVIF_TEXT) /* text requested */
2649 {
2652 break;
2653
2654 if (lpnmh->code == LVN_GETDISPINFOA)
2655 {
2656 /* shouldn't happen */
2657 NMLVDISPINFOA *lpdiA = (NMLVDISPINFOA *)lpnmh;
2658 StrRetToStrNA( lpdiA->item.pszText, lpdiA->item.cchTextMax, &sd.str, NULL);
2659 TRACE("-- text=%s\n", lpdiA->item.pszText);
2660 }
2661 else /* LVN_GETDISPINFOW */
2662 {
2663 StrRetToStrNW( lpdi->item.pszText, lpdi->item.cchTextMax, &sd.str, NULL);
2664 TRACE("-- text=%s\n", debugstr_w(lpdi->item.pszText));
2665 }
2666 }
2667 if(lpdi->item.mask & LVIF_IMAGE) /* image requested */
2668 {
2670 }
2671 if(lpdi->item.mask & LVIF_STATE)
2672 {
2673 ULONG attributes = SFGAO_HIDDEN;
2674 if (SUCCEEDED(m_pSFParent->GetAttributesOf(1, &pidl, &attributes)))
2675 {
2676 if (attributes & SFGAO_HIDDEN)
2677 lpdi->item.state |= LVIS_CUT;
2678 }
2679 }
2680 lpdi->item.mask |= LVIF_DI_SETITEM;
2681 break;
2682 case LVN_ITEMCHANGED:
2683 TRACE("-- LVN_ITEMCHANGED %p\n", this);
2684 if ((lpnmlv->uOldState ^ lpnmlv->uNewState) & (LVIS_SELECTED | LVIS_FOCUSED))
2685 {
2686 OnStateChange(CDBOSC_SELCHANGE); // browser will get the IDataObject
2687 // FIXME: Use LVIS_DROPHILITED instead in drag_notify_subitem
2689 {
2692 }
2693 _DoFolderViewCB(SFVM_SELECTIONCHANGED, NULL/* FIXME */, NULL/* FIXME */);
2694 }
2695 break;
2696 case LVN_BEGINDRAG:
2697 case LVN_BEGINRDRAG:
2698 TRACE("-- LVN_BEGINDRAG\n");
2699 if (GetSelections())
2700 {
2702 DWORD dwAttributes = SFGAO_CANCOPY | SFGAO_CANLINK;
2703 DWORD dwEffect = DROPEFFECT_MOVE;
2704
2705 if (SUCCEEDED(m_pSFParent->GetUIObjectOf(m_hWnd, m_cidl, m_apidl, IID_NULL_PPV_ARG(IDataObject, &pda))))
2706 {
2708
2709 if (SUCCEEDED(m_pSFParent->GetAttributesOf(m_cidl, m_apidl, &dwAttributes)))
2710 dwEffect |= dwAttributes & (SFGAO_CANCOPY | SFGAO_CANLINK);
2711
2713 if (SUCCEEDED(pda->QueryInterface(IID_PPV_ARG(IAsyncOperation, &piaso))))
2714 piaso->SetAsyncMode(TRUE);
2715
2716 DWORD dwEffect2;
2717
2718 m_pSourceDataObject = pda;
2719 m_ptFirstMousePos = params->ptAction;
2722
2723 HIMAGELIST big_icons, small_icons;
2724 Shell_GetImageLists(&big_icons, &small_icons);
2725 PCUITEMID_CHILD pidl = _PidlByItem(params->iItem);
2726 int iIcon = SHMapPIDLToSystemImageListIndex(m_pSFParent, pidl, 0);
2727 POINT ptItem;
2728 m_ListView.GetItemPosition(params->iItem, &ptItem);
2729
2730 ImageList_BeginDrag(big_icons, iIcon, params->ptAction.x - ptItem.x, params->ptAction.y - ptItem.y);
2731 DoDragDrop(pda, this, dwEffect, &dwEffect2);
2733 }
2734 }
2735 break;
2737 {
2738 DWORD dwAttr = SFGAO_CANRENAME;
2739 pidl = _PidlByItem(lpdi->item);
2740
2741 TRACE("-- LVN_BEGINLABELEDITW %p\n", this);
2742
2743 m_pSFParent->GetAttributesOf(1, &pidl, &dwAttr);
2744 if (SFGAO_CANRENAME & dwAttr)
2745 {
2746 HWND hEdit = reinterpret_cast<HWND>(m_ListView.SendMessage(LVM_GETEDITCONTROL));
2748
2749 // smartass-renaming: See CORE-15242
2750 if (!(dwAttr & SFGAO_FOLDER) && (dwAttr & SFGAO_FILESYSTEM) &&
2751 (lpdi->item.mask & LVIF_TEXT) && !SelectExtOnRename())
2752 {
2753 WCHAR szFullPath[MAX_PATH];
2754 PIDLIST_ABSOLUTE pidlFull = ILCombine(m_pidlParent, pidl);
2755 SHGetPathFromIDListW(pidlFull, szFullPath);
2756
2757 INT cchLimit = 0;
2758 _DoFolderViewCB(SFVM_GETNAMELENGTH, (WPARAM)pidlFull, (LPARAM)&cchLimit);
2759 if (cchLimit)
2760 ::SendMessageW(hEdit, EM_SETLIMITTEXT, cchLimit, 0);
2761
2762 if (!SHELL_FS_HideExtension(szFullPath))
2763 {
2764 LPWSTR pszText = lpdi->item.pszText;
2765 LPWSTR pchDotExt = PathFindExtensionW(pszText);
2766 ::PostMessageW(hEdit, EM_SETSEL, 0, pchDotExt - pszText);
2768 }
2769
2770 ILFree(pidlFull);
2771 }
2772
2773 m_isEditing = TRUE;
2774 return FALSE;
2775 }
2776 return TRUE;
2777 }
2778 case LVN_ENDLABELEDITW:
2779 {
2780 TRACE("-- LVN_ENDLABELEDITW %p\n", this);
2782
2783 if (lpdi->item.pszText)
2784 {
2785 HRESULT hr;
2786 LVITEMW lvItem;
2787
2788 pidl = _PidlByItem(lpdi->item);
2789 PITEMID_CHILD pidlNew = NULL;
2790 hr = m_pSFParent->SetNameOf(0, pidl, lpdi->item.pszText, SHGDN_INFOLDER, &pidlNew);
2791
2792 if (SUCCEEDED(hr) && pidlNew)
2793 {
2794 lvItem.mask = LVIF_PARAM|LVIF_IMAGE;
2795 lvItem.iItem = lpdi->item.iItem;
2796 lvItem.iSubItem = 0;
2797 lvItem.lParam = reinterpret_cast<LPARAM>(pidlNew);
2799 m_ListView.SetItem(&lvItem);
2800 m_ListView.Update(lpdi->item.iItem);
2801 return TRUE;
2802 }
2803 }
2804
2805 return FALSE;
2806 }
2807 default:
2808 TRACE("-- %p WM_COMMAND %x unhandled\n", this, lpnmh->code);
2809 break;
2810 }
2811
2812 return 0;
2813}
2814
2815// This is just a quick hack to make the desktop work correctly.
2816// ITranslateShellChangeNotify's IsChildID is undocumented, but most likely the
2817// way that a folder should know if it should update upon a change notification.
2818// It is exported by merged folders at a minimum.
2820{
2821 if (!pidl1 || !pidl2)
2822 return FALSE;
2823 if (ILIsParent(pidl1, pidl2, TRUE))
2824 return TRUE;
2825
2826 CComHeapPtr<ITEMIDLIST_ABSOLUTE> pidl2Clone(ILClone(pidl2));
2827 ILRemoveLastID(pidl2Clone);
2828 return ILIsEqual(pidl1, pidl2Clone);
2829}
2830
2832{
2833 // The change notify can come before WM_CREATE
2834 if (!m_ListView)
2835 return FALSE;
2836
2837 HANDLE hChange = (HANDLE)wParam;
2838 DWORD dwProcID = (DWORD)lParam;
2839 PIDLIST_ABSOLUTE *Pidls;
2840 LONG lEvent;
2841 HANDLE hLock = SHChangeNotification_Lock(hChange, dwProcID, &Pidls, &lEvent);
2842 if (hLock == NULL)
2843 {
2844 ERR("hLock == NULL\n");
2845 return FALSE;
2846 }
2847
2848 TRACE("(%p)(%p,%p,%p)\n", this, Pidls[0], Pidls[1], lParam);
2849
2851 return FALSE;
2852
2853 // Translate child IDLs.
2854 // SHSimpleIDListFromPathW creates fake PIDLs (lacking some attributes)
2855 HRESULT hr;
2856 PITEMID_CHILD child0 = NULL, child1 = NULL;
2857 CComHeapPtr<ITEMIDLIST_RELATIVE> pidl0Temp, pidl1Temp;
2858 if (_ILIsSpecialFolder(Pidls[0]) || ILIsParentOrSpecialParent(m_pidlParent, Pidls[0]))
2859 {
2860 child0 = ILFindLastID(Pidls[0]);
2861 hr = SHGetRealIDL(m_pSFParent, child0, &pidl0Temp);
2862 if (SUCCEEDED(hr))
2863 child0 = pidl0Temp;
2864 }
2865 if (_ILIsSpecialFolder(Pidls[1]) || ILIsParentOrSpecialParent(m_pidlParent, Pidls[1]))
2866 {
2867 child1 = ILFindLastID(Pidls[1]);
2868 hr = SHGetRealIDL(m_pSFParent, child1, &pidl1Temp);
2869 if (SUCCEEDED(hr))
2870 child1 = pidl1Temp;
2871 }
2872
2873 lEvent &= ~SHCNE_INTERRUPT;
2874 switch (lEvent)
2875 {
2876 case SHCNE_MKDIR:
2877 case SHCNE_CREATE:
2878 case SHCNE_DRIVEADD:
2879 if (!child0)
2880 break;
2881 if (LV_FindItemByPidl(child0) < 0)
2882 LV_AddItem(child0);
2883 else
2884 LV_UpdateItem(child0);
2885 break;
2886 case SHCNE_RMDIR:
2887 case SHCNE_DELETE:
2888 case SHCNE_DRIVEREMOVED:
2889 if (child0)
2890 LV_DeleteItem(child0);
2891 break;
2892 case SHCNE_RENAMEFOLDER:
2893 case SHCNE_RENAMEITEM:
2894 if (child0 && child1)
2895 LV_RenameItem(child0, child1);
2896 else if (child0)
2897 LV_DeleteItem(child0);
2898 else if (child1)
2899 LV_AddItem(child1);
2900 break;
2901 case SHCNE_UPDATEITEM:
2902 if (child0)
2903 LV_UpdateItem(child0);
2904 break;
2905 case SHCNE_UPDATEIMAGE:
2907 case SHCNE_MEDIAREMOVED:
2908 case SHCNE_ASSOCCHANGED:
2910 break;
2911 case SHCNE_UPDATEDIR:
2912 case SHCNE_ATTRIBUTES:
2913 Refresh();
2915 break;
2916 case SHCNE_FREESPACE:
2918 break;
2919 }
2920
2922 return TRUE;
2923}
2924
2927
2929{
2930 if (!m_pCM)
2931 {
2932 /* no menu */
2933 ERR("no context menu\n");
2934 return FALSE;
2935 }
2936
2937 // lParam of WM_DRAWITEM WM_MEASUREITEM contains a menu id and
2938 // this also needs to be changed to a menu identifier offset
2939 UINT CmdID;
2940 HRESULT hres = SHGetMenuIdFromMenuMsg(uMsg, lParam, &CmdID);
2941 if (SUCCEEDED(hres))
2943
2944 /* Forward the message to the IContextMenu2 */
2947
2948 return (SUCCEEDED(hres));
2949}
2950
2952{
2953 /* Wallpaper setting affects drop shadows effect */
2954 if (wParam == SPI_SETDESKWALLPAPER || wParam == 0)
2956
2957 return S_OK;
2958}
2959
2961{
2962 HMENU hmenu = (HMENU) wParam;
2963 int nPos = LOWORD(lParam);
2964 UINT menuItemId;
2965
2966 if (m_pCM)
2967 OnCustomItem(uMsg, wParam, lParam, bHandled);
2968
2970
2971 if (GetSelections() == 0)
2972 {
2979 }
2980 else
2981 {
2982 // FIXME: Check copyable
2989 }
2990
2991 /* Lets try to find out what the hell wParam is */
2992 if (hmenu == GetSubMenu(m_hMenu, nPos))
2993 menuItemId = ReallyGetMenuItemID(m_hMenu, nPos);
2994 else if (hViewMenu && hmenu == GetSubMenu(hViewMenu, nPos))
2995 menuItemId = ReallyGetMenuItemID(hViewMenu, nPos);
2996 else if (m_hContextMenu && hmenu == GetSubMenu(m_hContextMenu, nPos))
2997 menuItemId = ReallyGetMenuItemID(m_hContextMenu, nPos);
2998 else
2999 return FALSE;
3000
3001 switch (menuItemId)
3002 {
3003 case FCIDM_MENU_FILE:
3004 FillFileMenu();
3005 break;
3006 case FCIDM_MENU_VIEW:
3007 case FCIDM_SHVIEW_VIEW:
3009 break;
3012 break;
3013 }
3014
3015 return FALSE;
3016}
3017
3018
3019// The INTERFACE of the IShellView object
3020
3022{
3023 TRACE("(%p)\n", this);
3024
3025 *phWnd = m_hWnd;
3026
3027 return S_OK;
3028}
3029
3031{
3032 FIXME("(%p) stub\n", this);
3033
3034 return E_NOTIMPL;
3035}
3036
3037// FIXME: use the accel functions
3039{
3040 if (m_isEditing)
3041 return S_FALSE;
3042
3043 if (lpmsg->message >= WM_KEYFIRST && lpmsg->message <= WM_KEYLAST)
3044 {
3045 if (::TranslateAcceleratorW(m_hWnd, m_hAccel, lpmsg) != 0)
3046 return S_OK;
3047
3048 TRACE("-- key=0x%04lx\n", lpmsg->wParam);
3049 }
3050
3051 return m_pShellBrowser->TranslateAcceleratorSB(lpmsg, 0);
3052}
3053
3055{
3056 FIXME("(%p)\n", this);
3057 return E_NOTIMPL;
3058}
3059
3061{
3062 TRACE("(%p)->(state=%x)\n", this, uState);
3063
3064 // don't do anything if the state isn't changing
3065 if (m_uState == uState)
3066 return S_OK;
3067
3068 // OnActivate handles the menu merging and internal state
3069 DoActivate(uState);
3070
3071 // only do this if we are active
3072 if (uState != SVUIA_DEACTIVATE)
3073 {
3075
3076 // Set the text for the status bar
3078 }
3079
3080 return S_OK;
3081}
3082
3084{
3085 TRACE("(%p)\n", this);
3086
3088
3090 FillList();
3091
3092 return S_OK;
3093}
3094
3096{
3097 return CreateViewWindow3(psb, lpPrevView, SV3CVW3_DEFAULT,
3098 (FOLDERFLAGS)lpfs->fFlags, (FOLDERFLAGS)lpfs->fFlags, (FOLDERVIEWMODE)lpfs->ViewMode, NULL, prcView, phWnd);
3099}
3100
3102{
3103 TRACE("(%p)\n", this);
3104
3105 /* Make absolutely sure all our UI is cleaned up */
3107
3108 if (m_hAccel)
3109 {
3110 // MSDN: Accelerator tables loaded from resources are freed automatically when application terminates
3111 m_hAccel = NULL;
3112 }
3113
3115 {
3118 }
3119
3120 if (m_hMenuViewModes)
3121 {
3124 }
3125
3126 if (m_hMenu)
3127 {
3129 m_hMenu = NULL;
3130 }
3131
3132 if (m_ListView)
3133 {
3134 m_ListView.DestroyWindow();
3135 }
3136
3137 if (m_hWnd)
3138 {
3140 DestroyWindow();
3141 }
3142
3145
3146 return S_OK;
3147}
3148
3150{
3151 TRACE("(%p)->(%p) vmode=%x flags=%x\n", this, lpfs,
3153
3154 if (!lpfs)
3155 return E_INVALIDARG;
3156
3157 *lpfs = m_FolderSettings;
3158 return S_OK;
3159}
3160
3162{
3163 TRACE("(%p)->(0x%lX, %p, %p)\n", this, dwReserved, lpfn, lparam);
3164
3167 return S_OK;
3168}
3169
3171{
3172 ULONG read;
3173 HRESULT hr = pS->Read(buffer, cb, &read);
3174 return FAILED(hr) ? hr : (cb == read ? S_OK : HResultFromWin32(ERROR_MORE_DATA));
3175}
3176
3178{
3179 DWORD value;
3181 return SUCCEEDED(hr) ? value : def;
3182}
3183
3185{
3186 CLSID clsid;
3188 if (SUCCEEDED(hr))
3189 {
3190 WCHAR path[MAX_PATH], name[39];
3191 wsprintfW(path, L"%s\\%s", REGSTR_PATH_EXPLORER, L"Streams\\Default");
3193 *ppStream = SHOpenRegStream2W(HKEY_CURRENT_USER, path, name, Stgm);
3194 hr = *ppStream ? S_OK : E_FAIL;
3195 }
3196 return hr;
3197}
3198
3200{
3202 if (FAILED(hr))
3203 return hr;
3204 if (cols.Signature != PERSISTCOLUMNS::SIG || cols.Count > cols.MAXCOUNT)
3206 return Read(pS, &cols.Columns, sizeof(*cols.Columns) * cols.Count);
3207}
3208
3210{
3212 PERSISTCOLUMNS cols;
3215 bool fallback = false;
3216 HRESULT hrColumns = E_FAIL;
3218 if (SUCCEEDED(hr))
3219 {
3220 DWORD data;
3221 if (FAILED(hr = SHPropertyBag_ReadDWORD(pPB, L"Mode", &data)))
3222 goto loadfallback;
3224 cvs.FolderSettings.fFlags = ReadDWORD(pPB, L"FFlags", FWF_NOGROUPING);
3225 data = ReadDWORD(pPB, L"Sort", ~0ul);
3227 cvs.SortDir = (INT8)ReadDWORD(pPB, L"SortDir", 1);
3228 if (SUCCEEDED(hrColumns = SHPropertyBag_ReadStream(pPB, L"ColInfo", &pS)))
3229 hrColumns = LoadColumnsStream(cols, pS);
3230 }
3231 else
3232 {
3233 if (FAILED(hr = (m_pShellBrowser ? m_pShellBrowser->GetViewStateStream(STGM_READ, &pS) : E_UNEXPECTED)))
3234 {
3235 loadfallback:
3237 fallback = true;
3238 }
3239 if (FAILED(hr) || FAILED(hr = Read(pS, &cvs, sizeof(cvs))))
3240 return hr;
3241 if (cvs.Signature != cvs.SIG)
3243 hrColumns = LoadColumnsStream(cols, pS);
3244 }
3246 m_FolderSettings.fFlags &= ~cvs.VALIDFWF;
3248 if (SUCCEEDED(hrColumns))
3249 {
3250 BOOL failed = FALSE;
3251 if ((m_LoadColumnsList = DPA_Create(cols.Count)) != NULL)
3252 {
3253 for (UINT i = 0; i < cols.Count; ++i)
3254 {
3255 failed |= !DPA_SetPtr(m_LoadColumnsList, i, UlongToPtr(cols.Columns[i]));
3256 }
3257 }
3258 if (failed || !cols.Count)
3259 {
3262 }
3263 }
3266 m_sortInfo.Direction = cvs.SortDir > 0 ? 1 : -1;
3268 return hr;
3269}
3270
3272{
3273 if (!m_ListView.m_hWnd)
3274 return E_UNEXPECTED;
3277 cvs.SortColId = sortcol >= 0 ? (WORD)sortcol : 0;
3279 PERSISTCOLUMNS cols;
3281 cols.Count = 0;
3282 LVCOLUMN lvc;
3283 lvc.mask = LVCF_WIDTH | LVCF_SUBITEM;
3284 for (UINT i = 0, j = 0; i < PERSISTCOLUMNS::MAXCOUNT && ListView_GetColumn(m_ListView, j, &lvc); ++j)
3285 {
3286 HRESULT hr = MapListColumnToFolderColumn(lvc.iSubItem);
3287 if (SUCCEEDED(hr))
3288 {
3289 cols.Columns[i] = MAKELONG(hr, lvc.cx);
3290 cols.Count = ++i;
3291 }
3292 }
3293 UINT cbColumns = FIELD_OFFSET(PERSISTCOLUMNS, Columns) + (sizeof(*cols.Columns) * cols.Count);
3295
3296 IPropertyBag *pPB;
3297 HRESULT hr = S_OK;
3298 if (pStream)
3299 {
3300 pStream->AddRef();
3301 goto stream;
3302 }
3304 if (SUCCEEDED(hr))
3305 {
3306 UINT uViewMode;
3307 GetCurrentViewMode(&uViewMode);
3308 hr = SHPropertyBag_WriteDWORD(pPB, L"Mode", uViewMode);
3310 SHPropertyBag_WriteDWORD(pPB, L"Sort", cvs.SortColId);
3311 SHPropertyBag_WriteDWORD(pPB, L"SortDir", cvs.SortDir);
3312 pStream = cols.Count ? SHCreateMemStream((LPBYTE)&cols, cbColumns) : NULL;
3313 if (!pStream || FAILED(SHPropertyBag_WriteStream(pPB, L"ColInfo", pStream)))
3314 SHPropertyBag_Delete(pPB, L"ColInfo");
3315#if 0 // TODO
3317 memcpy(name, L"ItemPos", sizeof(L"ItemPos"));
3318 if (SHGetPerScreenResName(name + 7, _countof(name) - 7, 0))
3319 {
3320 if (GetAutoArrange() == S_FALSE)
3321 // TODO: Save listview item positions
3322 else
3324 }
3325#endif
3326 pPB->Release();
3327 }
3328 else if (SUCCEEDED(hr = (m_pShellBrowser ? m_pShellBrowser->GetViewStateStream(STGM_WRITE, &pStream) : E_UNEXPECTED)))
3329 {
3330 stream:
3331 ULONG written;
3332 cvs.Signature = cvs.SIG;
3334 hr = pStream->Write(&cvs, sizeof(cvs), &written);
3335 if (SUCCEEDED(hr))
3336 hr = pStream->Write(&cols, cbColumns, &written);
3337 }
3338 if (pStream)
3339 pStream->Release();
3340 return hr;
3341}
3342
3344{
3346 return SaveViewState(NULL);
3347 return S_FALSE;
3348}
3349
3350#define UPDATEFOLDERVIEWFLAGS(bits, bit, set) ( (bits) = ((bits) & ~(bit)) | ((set) ? (bit) : 0) )
3352{
3356}
3357
3359{
3360 int i;
3361
3362 TRACE("(%p)->(pidl=%p, 0x%08x) stub\n", this, pidl, uFlags);
3363
3364 if (!m_ListView)
3365 {
3366 ERR("!m_ListView\n");
3367 return E_FAIL;
3368 }
3369
3370 i = LV_FindItemByPidl(pidl);
3371 if (i == -1)
3372 return S_OK;
3373
3374 LVITEMW lvItem = {0};
3375 lvItem.mask = LVIF_STATE;
3377
3378 while (m_ListView.GetItem(&lvItem))
3379 {
3380 if (lvItem.iItem == i)
3381 {
3382 if (uFlags & SVSI_SELECT)
3383 lvItem.state |= LVIS_SELECTED;
3384 else
3385 lvItem.state &= ~LVIS_SELECTED;
3386
3387 if (uFlags & SVSI_FOCUSED)
3388 lvItem.state |= LVIS_FOCUSED;
3389 else
3390 lvItem.state &= ~LVIS_FOCUSED;
3391 }
3392 else
3393 {
3394 if (uFlags & SVSI_DESELECTOTHERS)
3395 {
3396 lvItem.state &= ~LVIS_SELECTED;
3397 }
3398 lvItem.state &= ~LVIS_FOCUSED;
3399 }
3400
3401 m_ListView.SetItem(&lvItem);
3402 lvItem.iItem++;
3403 }
3404
3405 if (uFlags & SVSI_ENSUREVISIBLE)
3407
3408 if((uFlags & SVSI_EDIT) == SVSI_EDIT)
3410
3411 return S_OK;
3412}
3413
3415{
3417
3418 TRACE("(%p)->(uItem=0x%08x,\n\tIID=%s, ppv=%p)\n", this, uItem, debugstr_guid(&riid), ppvOut);
3419
3420 if (!ppvOut)
3421 return E_INVALIDARG;
3422
3423 *ppvOut = NULL;
3424
3425 switch (uItem)
3426 {
3427 case SVGIO_BACKGROUND:
3428 if (IsEqualIID(riid, IID_IContextMenu))
3429 {
3432 return hr;
3433
3434 IUnknown_SetSite(*((IUnknown**)ppvOut), (IShellView *)this);
3435 }
3436 else if (IsEqualIID(riid, IID_IDispatch))
3437 {
3439 {
3442 return hr;
3443 }
3444 hr = m_pShellFolderViewDual->QueryInterface(riid, ppvOut);
3445 }
3446 break;
3447 case SVGIO_SELECTION:
3448 GetSelections();
3449 hr = m_pSFParent->GetUIObjectOf(m_hWnd, m_cidl, m_apidl, riid, 0, ppvOut);
3451 return hr;
3452
3453 if (IsEqualIID(riid, IID_IContextMenu))
3454 IUnknown_SetSite(*((IUnknown**)ppvOut), (IShellView *)this);
3455
3456 break;
3457 }
3458
3459 TRACE("-- (%p)->(interface=%p)\n", this, *ppvOut);
3460
3461 return hr;
3462}
3463
3465{
3469 mode = temp;
3470 return mode;
3471}
3472
3474{
3475 TRACE("(%p)->(%p), stub\n", this, pViewMode);
3476
3477 if (!pViewMode)
3478 return E_INVALIDARG;
3479
3480 *pViewMode = m_FolderSettings.ViewMode;
3481 return S_OK;
3482}
3483
3485{
3486 DWORD dwStyle;
3487 TRACE("(%p)->(%u), stub\n", this, ViewMode);
3488
3489 /* It's not redundant to check FVM_AUTO because it's a (UINT)-1 */
3490 if (((INT)ViewMode < FVM_FIRST || (INT)ViewMode > FVM_LAST) && ((INT)ViewMode != FVM_AUTO))
3491 return E_INVALIDARG;
3492
3493 /* Windows before Vista uses LVM_SETVIEW and possibly
3494 LVM_SETEXTENDEDLISTVIEWSTYLE to set the style of the listview,
3495 while later versions seem to accomplish this through other
3496 means. */
3497 switch (ViewMode)
3498 {
3499 case FVM_ICON:
3500 dwStyle = LVS_ICON;
3501 break;
3502 case FVM_DETAILS:
3503 dwStyle = LVS_REPORT;
3504 break;
3505 case FVM_SMALLICON:
3506 dwStyle = LVS_SMALLICON;
3507 break;
3508 case FVM_LIST:
3509 dwStyle = LVS_LIST;
3510 break;
3511 default:
3512 {
3513 FIXME("ViewMode %d not implemented\n", ViewMode);
3514 dwStyle = LVS_LIST;
3515 break;
3516 }
3517 }
3518
3519 m_ListView.ModifyStyle(LVS_TYPEMASK, dwStyle);
3520
3521 /* This will not necessarily be the actual mode set above.
3522 This mimics the behavior of Windows XP. */
3523 m_FolderSettings.ViewMode = ViewMode;
3524
3525 return S_OK;
3526}
3527
3529{
3530 if (m_pSFParent == NULL)
3531 return E_FAIL;
3532
3533 return m_pSFParent->QueryInterface(riid, ppv);
3534}
3535
3537{
3538 PCUITEMID_CHILD pidl = _PidlByItem(iItemIndex);
3539 if (pidl)
3540 {
3541 *ppidl = ILClone(pidl);
3542 return S_OK;
3543 }
3544
3545 *ppidl = 0;
3546 return E_INVALIDARG;
3547}
3548
3550{
3551 TRACE("(%p)->(%u %p)\n", this, uFlags, pcItems);
3552
3553 if (uFlags != SVGIO_ALLVIEW)
3554 FIXME("some flags unsupported, %x\n", uFlags & ~SVGIO_ALLVIEW);
3555
3557
3558 return S_OK;
3559}
3560
3562{
3563 return E_NOTIMPL;
3564}
3565
3567{
3568 TRACE("(%p)->(%p)\n", this, piItem);
3569
3570 *piItem = m_ListView.GetSelectionMark();
3571
3572 return S_OK;
3573}
3574
3576{
3577 TRACE("(%p)->(%p)\n", this, piItem);
3578
3579 *piItem = m_ListView.GetNextItem(-1, LVNI_FOCUSED);
3580
3581 return S_OK;
3582}
3583
3585{
3586 if (!m_ListView)
3587 {
3588 ERR("!m_ListView\n");
3589 return E_FAIL;
3590 }
3591
3592 int lvIndex = LV_FindItemByPidl(pidl);
3593 if (lvIndex == -1 || ppt == NULL)
3594 return E_INVALIDARG;
3595
3596 m_ListView.GetItemPosition(lvIndex, ppt);
3597 return S_OK;
3598}
3599
3601{
3602 TRACE("(%p)->(%p)\n", this, ppt);
3603
3604 if (!m_ListView)
3605 {
3606 ERR("!m_ListView\n");
3607 return S_FALSE;
3608 }
3609
3610 if (ppt)
3611 {
3612 SIZE spacing;
3613 m_ListView.GetItemSpacing(spacing);
3614
3615 ppt->x = spacing.cx;
3616 ppt->y = spacing.cy;
3617 }
3618
3619 return S_OK;
3620}
3621
3623{
3624 return E_NOTIMPL;
3625}
3626
3628{
3629 return ((m_ListView.GetStyle() & LVS_AUTOARRANGE) ? S_OK : S_FALSE);
3630}
3631
3633{
3634 DWORD dwExStyle = (DWORD)m_ListView.SendMessage(LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0);
3635 return ((dwExStyle & LVS_EX_SNAPTOGRID) ? S_OK : S_FALSE);
3636}
3637
3639{
3640 LVITEMW lvItem;
3641
3642 TRACE("(%p)->(%d, %x)\n", this, iItem, dwFlags);
3643
3644 lvItem.state = 0;
3645 lvItem.stateMask = LVIS_SELECTED;
3646
3647 if (dwFlags & SVSI_ENSUREVISIBLE)
3648 m_ListView.EnsureVisible(iItem, 0);
3649
3650 /* all items */
3651 if (dwFlags & SVSI_DESELECTOTHERS)
3653
3654 /* this item */
3655 if (dwFlags & SVSI_SELECT)
3656 lvItem.state |= LVIS_SELECTED;
3657
3658 if (dwFlags & SVSI_FOCUSED)
3659 lvItem.stateMask |= LVIS_FOCUSED;
3660
3661 m_ListView.SetItemState(iItem, lvItem.state, lvItem.stateMask);
3662
3663 if ((dwFlags & SVSI_EDIT) == SVSI_EDIT)
3664 m_ListView.EditLabel(iItem);
3665
3666 return S_OK;
3667}
3668
3670{
3672
3673 /* Reset the selection */
3675
3676 int lvIndex;
3677 for (UINT i = 0 ; i < cidl; i++)
3678 {
3679 lvIndex = LV_FindItemByPidl(apidl[i]);
3680 if (lvIndex != -1)
3681 {
3682 SelectItem(lvIndex, dwFlags);
3683 m_ListView.SetItemPosition(lvIndex, &apt[i]);
3684 }
3685 }
3686
3687 return S_OK;
3688}
3689
3690
3691// IShellView2 implementation
3692
3694{
3695 FIXME("(%p)->(%p, %lu) stub\n", this, view_guid, view_type);
3696 return E_NOTIMPL;
3697}
3698
3700{
3701 return CreateViewWindow3(view_params->psbOwner, view_params->psvPrev,
3702 SV3CVW3_DEFAULT, (FOLDERFLAGS)view_params->pfs->fFlags, (FOLDERFLAGS)view_params->pfs->fFlags,
3703 (FOLDERVIEWMODE)view_params->pfs->ViewMode, view_params->pvid, view_params->prcView, &view_params->hwndView);
3704}
3705
3707{
3708 OLEMENUGROUPWIDTHS omw = { { 0, 0, 0, 0, 0, 0 } };
3709 const UINT SUPPORTED_SV3CVW3 = SV3CVW3_FORCEVIEWMODE | SV3CVW3_FORCEFOLDERFLAGS;
3710 const UINT IGNORE_FWF = FWF_OWNERDATA; // FIXME: Support this
3711
3712 *hwnd = NULL;
3713
3714 TRACE("(%p)->(shlview=%p shlbrs=%p rec=%p hwnd=%p vmode=%x flags=%x)\n", this, psvPrevious, psb, prcView, hwnd, mode, flags);
3715 if (prcView != NULL)
3716 TRACE("-- left=%i top=%i right=%i bottom=%i\n", prcView->left, prcView->top, prcView->right, prcView->bottom);
3717
3718 /* Validate the Shell Browser */
3719 if (psb == NULL || m_hWnd)
3720 return E_UNEXPECTED;
3721
3722 if (view_flags & ~SUPPORTED_SV3CVW3)
3723 FIXME("unsupported view flags 0x%08x\n", view_flags & ~SUPPORTED_SV3CVW3);
3724
3725 if (mode == FVM_AUTO)
3726 mode =