This commit is contained in:
Whindmar Saksit 2024-05-03 17:14:21 +02:00 committed by GitHub
commit aade7e1701
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 531 additions and 139 deletions

View file

@ -26,7 +26,7 @@
/*
TODO:
- Load/Save the view state from/into the stream provided by the ShellBrowser.
- Load/Save the view state from/into the stream provided by the ShellBrowser unless FWF_NOBROWSERVIEWSTATE is set.
- When editing starts on item, set edit text to for editing value.
- Fix shell view to handle view mode popup exec.
- The background context menu should have a pidl just like foreground menus. This
@ -41,11 +41,27 @@ TODO:
WINE_DEFAULT_DEBUG_CHANNEL(shell);
// It would be easier to allocate these from 1 and up but the original code used the entire
// FCIDM_SHVIEWFIRST..FCIDM_SHVIEWLAST range when dealing with IContextMenu and to avoid
// breaking anything relying on this we will allocate our ranges from the end instead.
enum {
DEFVIEW_ARRANGESORT_MAXENUM = 9, // Limit the number of the visible columns we will display in the submenu
DEFVIEW_ARRANGESORT_MAX = DEFVIEW_ARRANGESORT_MAXENUM + 1, // Reserve one extra for the current sort-by column
DVIDM_ARRANGESORT_LAST = FCIDM_SHVIEWLAST,
DVIDM_ARRANGESORT_FIRST = DVIDM_ARRANGESORT_LAST - (DEFVIEW_ARRANGESORT_MAX - 1),
DVIDM_CONTEXTMENU_LAST = DVIDM_ARRANGESORT_FIRST - 1,
// FIXME: FCIDM_SHVIEWFIRST is 0 and using that with QueryContextMenu is a
// bad idea because it hides bugs related to the ids in ici.lpVerb.
// CONTEXT_MENU_BASE_ID acknowledges this but failed to apply the fix everywhere.
DVIDM_CONTEXTMENU_FIRST = FCIDM_SHVIEWFIRST,
};
#undef FCIDM_SHVIEWLAST // Don't use this constant, change DVIDM_CONTEXTMENU_LAST if you need a new id.
typedef struct
{
BOOL bIsAscending;
INT nHeaderID;
INT nLastHeaderID;
INT ListColumn;
} LISTVIEW_SORT_INFO, *LPLISTVIEW_SORT_INFO;
#define SHV_CHANGE_NOTIFY WM_USER + 0x1111
@ -108,6 +124,65 @@ struct MenuCleanup
}
};
static BOOL AppendMenuItem(HMENU hMenu, UINT MF, UINT Id, LPCWSTR String, SIZE_T Data = 0)
{
MENUITEMINFOW mii;
mii.cbSize = sizeof(mii);
mii.fMask = MIIM_TYPE | MIIM_ID | MIIM_DATA | MIIM_STATE;
mii.fState = MF & (MFS_CHECKED | MFS_DISABLED);
mii.fType = MF & ~mii.fState;
mii.wID = Id;
mii.dwTypeData = const_cast<LPWSTR>(String);
mii.dwItemData = Data;
return InsertMenuItemW(hMenu, -1, TRUE, &mii);
}
static SIZE_T GetMenuItemDataById(HMENU hMenu, UINT Id)
{
MENUITEMINFOW mii;
mii.cbSize = FIELD_OFFSET(MENUITEMINFOW, hbmpItem);
mii.fMask = MIIM_DATA;
if (GetMenuItemInfoW(hMenu, Id, FALSE, &mii))
return mii.dwItemData;
else
return 0;
}
static HMENU GetSubmenuByID(HMENU hmenu, UINT id)
{
MENUITEMINFOW mii = {sizeof(mii), MIIM_SUBMENU};
if (::GetMenuItemInfoW(hmenu, id, FALSE, &mii))
return mii.hSubMenu;
return NULL;
}
/* ReallyGetMenuItemID returns the id of an item even if it opens a submenu,
GetMenuItemID returns -1 if the specified item opens a submenu */
static UINT ReallyGetMenuItemID(HMENU hmenu, int i)
{
MENUITEMINFOW mii = {sizeof(mii), MIIM_ID};
if (::GetMenuItemInfoW(hmenu, i, TRUE, &mii))
return mii.wID;
return UINT_MAX;
}
static UINT CalculateCharWidth(HWND hwnd)
{
UINT ret = 0;
HDC hDC = GetDC(hwnd);
if (hDC)
{
HGDIOBJ hOrg = SelectObject(hDC, (HGDIOBJ)SendMessage(hwnd, WM_GETFONT, 0, 0));
ret = GdiGetCharDimensions(hDC, NULL, NULL);
SelectObject(hDC, hOrg);
ReleaseDC(hwnd, hDC);
}
return ret;
}
class CDefView :
public CWindowImpl<CDefView, CWindow, CControlWinTraits>,
public CComObjectRootEx<CComMultiThreadModelNoCS>,
@ -123,6 +198,7 @@ class CDefView :
private:
CComPtr<IShellFolder> m_pSFParent;
CComPtr<IShellFolder2> m_pSF2Parent;
CComPtr<IShellDetails> m_pSDParent;
CComPtr<IShellFolderViewCB> m_pShellFolderViewCB;
CComPtr<IShellBrowser> m_pShellBrowser;
CComPtr<ICommDlgBrowser> m_pCommDlgBrowser;
@ -139,6 +215,7 @@ private:
UINT m_cidl;
PCUITEMID_CHILD *m_apidl;
PIDLIST_ABSOLUTE m_pidlParent;
HDPA m_ListToFolderColMap;
LISTVIEW_SORT_INFO m_sortInfo;
ULONG m_hNotify; // Change notification handle
HACCEL m_hAccel;
@ -168,7 +245,7 @@ private:
HICON m_hMyComputerIcon;
HRESULT _MergeToolbar();
BOOL _Sort();
BOOL _Sort(int Col = -1);
HRESULT _DoFolderViewCB(UINT uMsg, WPARAM wParam, LPARAM lParam);
HRESULT _GetSnapToGrid();
void _MoveSelectionOnAutoArrange(POINT pt);
@ -191,6 +268,13 @@ public:
BOOL InitList();
static INT CALLBACK ListViewCompareItems(LPARAM lParam1, LPARAM lParam2, LPARAM lpData);
HRESULT MapFolderColumnToListColumn(UINT FoldCol);
HRESULT MapListColumnToFolderColumn(UINT ListCol);
HRESULT GetDetailsByFolderColumn(PCUITEMID_CHILD pidl, UINT FoldCol, SHELLDETAILS &sd);
HRESULT GetDetailsByListColumn(PCUITEMID_CHILD pidl, UINT ListCol, SHELLDETAILS &sd);
HRESULT LoadColumn(UINT FoldCol, UINT ListCol, BOOL Insert);
HRESULT LoadColumns(UINT *pColList = NULL, UINT ColListCount = 0);
void ColumnListChanged();
PCUITEMID_CHILD _PidlByItem(int i);
PCUITEMID_CHILD _PidlByItem(LVITEM& lvItem);
int LV_FindItemByPidl(PCUITEMID_CHILD pidl);
@ -199,12 +283,13 @@ public:
BOOLEAN LV_RenameItem(PCUITEMID_CHILD pidlOld, PCUITEMID_CHILD pidlNew);
BOOLEAN LV_ProdItem(PCUITEMID_CHILD pidl);
static INT CALLBACK fill_list(LPVOID ptr, LPVOID arg);
HRESULT FillList();
HRESULT FillList(BOOL IsRefreshCommand = TRUE);
HRESULT FillFileMenu();
HRESULT FillEditMenu();
HRESULT FillViewMenu();
HRESULT FillArrangeAsMenu(HMENU hmenuArrange);
HRESULT CheckViewMode(HMENU hmenuView);
LRESULT DoColumnContextMenu(LRESULT lParam);
UINT GetSelections();
HRESULT OpenSelectedItems();
void OnDeactivate();
@ -417,11 +502,6 @@ public:
END_COM_MAP()
};
// menu items
#define IDM_VIEW_FILES (FCIDM_SHVIEWFIRST + 0x500)
#define IDM_VIEW_IDW (FCIDM_SHVIEWFIRST + 0x501)
#define IDM_MYFILEITEM (FCIDM_SHVIEWFIRST + 0x502)
#define ID_LISTVIEW 1
// windowsx.h
@ -461,6 +541,7 @@ CDefView::CDefView() :
m_viewinfo_data.clrTextBack = GetSysColor(COLOR_WINDOW);
m_viewinfo_data.hbmBack = NULL;
m_ListToFolderColMap = DPA_Create(0);
m_hMyComputerIcon = LoadIconW(shell32_hInstance, MAKEINTRESOURCEW(IDI_SHELL_COMPUTER_DESKTOP));
}
@ -482,12 +563,14 @@ CDefView::~CDefView()
}
SHFree(m_apidl);
DPA_Destroy(m_ListToFolderColMap);
}
HRESULT WINAPI CDefView::Initialize(IShellFolder *shellFolder)
{
m_pSFParent = shellFolder;
shellFolder->QueryInterface(IID_PPV_ARG(IShellFolder2, &m_pSF2Parent));
shellFolder->QueryInterface(IID_PPV_ARG(IShellDetails, &m_pSDParent));
return S_OK;
}
@ -731,8 +814,7 @@ BOOL CDefView::CreateList()
m_ListView.SetExtendedListViewStyle(ListExStyle);
m_sortInfo.bIsAscending = TRUE;
m_sortInfo.nHeaderID = -1;
m_sortInfo.nLastHeaderID = -1;
m_sortInfo.ListColumn = -1;
/* UpdateShellSettings(); */
return TRUE;
@ -797,42 +879,245 @@ void CDefView::UpdateListColors()
// adds all needed columns to the shellview
BOOL CDefView::InitList()
{
SHELLDETAILS sd;
WCHAR szTemp[50];
HIMAGELIST big_icons, small_icons;
TRACE("%p\n", this);
m_ListView.DeleteAllItems();
m_hMenuArrangeModes = CreateMenu();
if (m_pSF2Parent)
{
for (int i = 0; 1; i++)
{
if (FAILED(m_pSF2Parent->GetDetailsOf(NULL, i, &sd)))
break;
StrRetToStrNW( szTemp, 50, &sd.str, NULL);
m_ListView.InsertColumn(i, szTemp, sd.fmt, sd.cxChar * 8);
InsertMenuW(m_hMenuArrangeModes, -1, MF_STRING, 0x30 + i, szTemp);
}
InsertMenuW(m_hMenuArrangeModes, -1, MF_BYPOSITION | MF_SEPARATOR, 0, 0);
}
else
{
FIXME("no m_pSF2Parent\n");
}
Shell_GetImageLists(&big_icons, &small_icons);
m_ListView.SetImageList(big_icons, LVSIL_NORMAL);
m_ListView.SetImageList(small_icons, LVSIL_SMALL);
m_hMenuArrangeModes = CreateMenu();
LoadColumns();
return TRUE;
}
/**********************************************************
* Column handling
*/
static HRESULT SHGetLVColumnSubItem(HWND List, UINT Col)
{
LVCOLUMN lvc;
lvc.mask = LVCF_SUBITEM;
if (!ListView_GetColumn(List, Col, &lvc))
return E_FAIL;
else
return lvc.iSubItem;
}
HRESULT CDefView::MapFolderColumnToListColumn(UINT FoldCol)
{
// This function is only called during column management, performance is not critical.
for (UINT i = 0;; ++i)
{
HRESULT r = SHGetLVColumnSubItem(m_ListView.m_hWnd, i);
if ((UINT)r == FoldCol)
return i;
else if (FAILED(r))
return r;
}
}
HRESULT CDefView::MapListColumnToFolderColumn(UINT ListCol)
{
// This function is called every time a LVITEM::iSubItem is mapped to
// a folder column (calls to GetDetailsOf etc.) and should be fast.
if (m_ListToFolderColMap)
{
UINT count = DPA_GetPtrCount(m_ListToFolderColMap);
if (ListCol < count)
{
HRESULT col = (INT)(INT_PTR)DPA_FastGetPtr(m_ListToFolderColMap, ListCol);
assert(col >= 0 && col == SHGetLVColumnSubItem(m_ListView.m_hWnd, ListCol));
return col;
}
else if (count)
{
TRACE("m_ListToFolderColMap cache miss while mapping %d\n", ListCol);
}
}
return SHGetLVColumnSubItem(m_ListView.m_hWnd, ListCol);
}
HRESULT CDefView::GetDetailsByFolderColumn(PCUITEMID_CHILD pidl, UINT FoldCol, SHELLDETAILS &sd)
{
// According to learn.microsoft.com/en-us/windows/win32/shell/sfvm-getdetailsof
// the query order is IShellFolder2, IShellDetails, SFVM_GETDETAILSOF.
HRESULT hr;
if (m_pSF2Parent)
{
hr = m_pSF2Parent->GetDetailsOf(pidl, FoldCol, &sd);
}
if (FAILED(hr) && m_pSDParent)
{
hr = m_pSDParent->GetDetailsOf(pidl, FoldCol, &sd);
}
#if 0 // TODO
if (FAILED(hr))
{
FIXME("Try SFVM_GETDETAILSOF\n");
}
#endif
return hr;
}
HRESULT CDefView::GetDetailsByListColumn(PCUITEMID_CHILD pidl, UINT ListCol, SHELLDETAILS &sd)
{
HRESULT hr = MapListColumnToFolderColumn(ListCol);
if (SUCCEEDED(hr))
return GetDetailsByFolderColumn(pidl, hr, sd);
ERR("Unable to determine folder column from list column %d\n", (int) ListCol);
return hr;
}
HRESULT CDefView::LoadColumn(UINT FoldCol, UINT ListCol, BOOL Insert)
{
WCHAR buf[MAX_PATH];
SHELLDETAILS sd;
HRESULT hr;
sd.str.uType = !STRRET_WSTR; // Make sure "uninitialized" uType is not WSTR
hr = GetDetailsByFolderColumn(NULL, FoldCol, sd);
if (FAILED(hr))
return hr;
hr = StrRetToStrNW(buf, _countof(buf), &sd.str, NULL);
if (FAILED(hr))
return hr;
UINT chavewidth = CalculateCharWidth(m_ListView.m_hWnd);
if (!chavewidth)
chavewidth = 6; // 6 is a reasonable default fallback
LVCOLUMN lvc;
lvc.mask = LVCF_TEXT | LVCF_FMT | LVCF_WIDTH | LVCF_SUBITEM;
lvc.pszText = buf;
lvc.fmt = sd.fmt;
lvc.cx = sd.cxChar * chavewidth; // FIXME: DPI?
lvc.iSubItem = FoldCol; // Used by MapFolderColumnToListColumn & MapListColumnToFolderColumn
if ((int)ListCol == -1)
{
assert(Insert); // You can insert at the end but you can't change something that is not there
if (Insert)
ListCol = 0x7fffffff;
}
if (Insert)
ListView_InsertColumn(m_ListView.m_hWnd, ListCol, &lvc);
else
ListView_SetColumn(m_ListView.m_hWnd, ListCol, &lvc);
return S_OK;
}
HRESULT CDefView::LoadColumns(UINT *pColList, UINT ColListCount)
{
HWND hWndHdr = ListView_GetHeader(m_ListView.m_hWnd);
UINT newColCount = 0, oldColCount = Header_GetItemCount(hWndHdr);
UINT foldCol, i;
HRESULT hr = S_FALSE;
m_ListView.SetRedraw(FALSE);
for (i = 0, foldCol = 0;; ++foldCol)
{
if (newColCount >= 0xffff)
break; // CompareIDs limit reached
if (pColList)
{
if (i >= ColListCount)
break;
foldCol = pColList[i++];
}
SHCOLSTATEF state = 0;
if (!m_pSF2Parent || FAILED(m_pSF2Parent->GetDefaultColumnState(foldCol, &state)))
state = SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT;
if (foldCol == 0)
{
// Force the first column
}
else if (state & SHCOLSTATE_HIDDEN)
{
continue;
}
else if (!pColList && !(state & SHCOLSTATE_ONBYDEFAULT))
{
continue;
}
bool insert = newColCount >= oldColCount;
UINT listCol = insert ? -1 : newColCount;
hr = LoadColumn(foldCol, listCol, insert);
if (FAILED(hr))
{
if (!pColList)
hr = S_OK; // No more items, we are done
break;
}
++newColCount;
}
for (i = newColCount; i < oldColCount; ++i)
{
ListView_DeleteColumn(m_ListView.m_hWnd, i);
}
m_ListView.SetRedraw(TRUE);
ColumnListChanged();
assert(SUCCEEDED(MapFolderColumnToListColumn(0))); // We don't allow turning off the Name column
return hr;
}
void CDefView::ColumnListChanged()
{
HDPA cache = m_ListToFolderColMap;
m_ListToFolderColMap = NULL; // No cache while we are building the cache
DPA_DeleteAllPtrs(cache);
for (UINT i = 0;; ++i)
{
HRESULT hr = MapListColumnToFolderColumn(i);
if (FAILED(hr))
break; // No more columns
if (!DPA_SetPtr(cache, i, (void*)(INT_PTR) hr))
break; // Cannot allow holes in the cache, must stop now.
}
m_ListToFolderColMap = cache;
for (;;)
{
if (!RemoveMenu(m_hMenuArrangeModes, 0, MF_BYPOSITION))
break;
}
HMENU hMenu = GetSubmenuByID(m_hMenu, FCIDM_MENU_VIEW);
if (hMenu)
{
hMenu = GetSubmenuByID(hMenu, FCIDM_SHVIEW_ARRANGE);
for (UINT i = DVIDM_ARRANGESORT_FIRST; i <= DVIDM_ARRANGESORT_LAST && hMenu; ++i)
{
RemoveMenu(hMenu, i, MF_BYCOMMAND);
}
if ((int) GetMenuItemID(hMenu, 0) <= 0)
RemoveMenu(hMenu, 0, MF_BYPOSITION); // Separator
}
WCHAR buf[MAX_PATH];
LVCOLUMN lvc;
lvc.mask = LVCF_TEXT;
lvc.pszText = buf;
lvc.cchTextMax = _countof(buf);
for (UINT listCol = 0; listCol < DEFVIEW_ARRANGESORT_MAXENUM; ++listCol)
{
if (!ListView_GetColumn(m_ListView.m_hWnd, listCol, &lvc))
break;
HRESULT foldCol = MapListColumnToFolderColumn(listCol);
assert(SUCCEEDED(foldCol));
AppendMenuItem(m_hMenuArrangeModes, MF_STRING,
DVIDM_ARRANGESORT_FIRST + listCol, lvc.pszText, listCol);
}
ListView_RedrawItems(m_ListView.m_hWnd, 0, 0x7fffffff);
m_ListView.InvalidateRect(NULL, TRUE);
}
/*************************************************************************
* ShellView_ListViewCompareItems
*
@ -854,7 +1139,7 @@ INT CALLBACK CDefView::ListViewCompareItems(LPARAM lParam1, LPARAM lParam2, LPAR
PCUIDLIST_RELATIVE pidl2 = reinterpret_cast<PCUIDLIST_RELATIVE>(lParam2);
CDefView *pThis = reinterpret_cast<CDefView*>(lpData);
HRESULT hres = pThis->m_pSFParent->CompareIDs(pThis->m_sortInfo.nHeaderID, pidl1, pidl2);
HRESULT hres = pThis->m_pSFParent->CompareIDs(pThis->m_sortInfo.ListColumn, pidl1, pidl2);
if (FAILED_UNEXPECTEDLY(hres))
return 0;
@ -864,37 +1149,51 @@ INT CALLBACK CDefView::ListViewCompareItems(LPARAM lParam1, LPARAM lParam2, LPAR
return nDiff;
}
BOOL CDefView::_Sort()
BOOL CDefView::_Sort(int Col)
{
HWND hHeader;
HDITEM hColumn;
int prevCol = m_sortInfo.ListColumn;
// FIXME: Is this correct? Who sets this style?
// And if it is set, should it also block sorting using the menu?
// Any why should it block sorting when the view is loaded initially?
if (m_ListView.GetWindowLongPtr(GWL_STYLE) & LVS_NOSORTHEADER)
return TRUE;
hHeader = (HWND)m_ListView.SendMessage(LVM_GETHEADER, 0, 0);
ZeroMemory(&hColumn, sizeof(hColumn));
// If the sorting column changed, remove sorting style from the old column
if ( (m_sortInfo.nLastHeaderID != -1) &&
(m_sortInfo.nLastHeaderID != m_sortInfo.nHeaderID) )
hHeader = ListView_GetHeader(m_ListView.m_hWnd);
if (Col != -1)
{
hColumn.mask = HDI_FORMAT;
Header_GetItem(hHeader, m_sortInfo.nLastHeaderID, &hColumn);
hColumn.fmt &= ~(HDF_SORTUP | HDF_SORTDOWN);
Header_SetItem(hHeader, m_sortInfo.nLastHeaderID, &hColumn);
if (Col >= Header_GetItemCount(hHeader))
{
ERR("Sort column out of range\n");
return FALSE;
}
if (prevCol == Col)
m_sortInfo.bIsAscending = !m_sortInfo.bIsAscending;
else
m_sortInfo.bIsAscending = TRUE;
m_sortInfo.ListColumn = Col;
}
/* Set the sorting style to the new column */
hColumn.mask = HDI_FORMAT;
Header_GetItem(hHeader, m_sortInfo.nHeaderID, &hColumn);
// If the sorting column changed, remove sorting style from the old column
if (prevCol != -1 && prevCol != m_sortInfo.ListColumn)
{
hColumn.mask = HDI_FORMAT;
Header_GetItem(hHeader, prevCol, &hColumn);
hColumn.fmt &= ~(HDF_SORTUP | HDF_SORTDOWN);
Header_SetItem(hHeader, prevCol, &hColumn);
}
/* Set the sorting style on the new column */
hColumn.mask = HDI_FORMAT;
Header_GetItem(hHeader, m_sortInfo.ListColumn, &hColumn);
hColumn.fmt &= (m_sortInfo.bIsAscending ? ~HDF_SORTDOWN : ~HDF_SORTUP );
hColumn.fmt |= (m_sortInfo.bIsAscending ? HDF_SORTUP : HDF_SORTDOWN);
Header_SetItem(hHeader, m_sortInfo.nHeaderID, &hColumn);
Header_SetItem(hHeader, m_sortInfo.ListColumn, &hColumn);
/* Sort the list, using the current values of nHeaderID and bIsAscending */
m_sortInfo.nLastHeaderID = m_sortInfo.nHeaderID;
/* Sort the list, using the current values of ListColumn and bIsAscending */
return m_ListView.SortItems(ListViewCompareItems, this);
}
@ -1045,7 +1344,7 @@ INT CALLBACK CDefView::fill_list(LPVOID ptr, LPVOID arg)
// - gets the objectlist from the shellfolder
// - sorts the list
// - fills the list into the view
HRESULT CDefView::FillList()
HRESULT CDefView::FillList(BOOL IsRefreshCommand)
{
CComPtr<IEnumIDList> pEnumIDList;
PITEMID_CHILD pidl;
@ -1109,16 +1408,22 @@ HRESULT CDefView::FillList()
DPA_DestroyCallback( hdpa, fill_list, this);
/* sort the array */
if (m_pSF2Parent)
int sortCol = -1;
if (!IsRefreshCommand) // Are we loading for the first time?
{
m_pSF2Parent->GetDefaultColumn(NULL, (ULONG*)&m_sortInfo.nHeaderID, NULL);
m_sortInfo.bIsAscending = TRUE;
sortCol = 0; // In case the folder does not know/care
if (m_pSF2Parent)
{
ULONG folderSortCol = sortCol, dummy;
HRESULT hr = m_pSF2Parent->GetDefaultColumn(NULL, &folderSortCol, &dummy);
if (SUCCEEDED(hr))
hr = MapFolderColumnToListColumn(folderSortCol);
if (SUCCEEDED(hr))
sortCol = (int) hr;
}
}
else
{
FIXME("no m_pSF2Parent\n");
}
m_sortInfo.bIsAscending = TRUE;
_Sort();
_Sort(sortCol);
if (m_viewinfo_data.hbmBack)
{
@ -1288,7 +1593,7 @@ LRESULT CDefView::OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandl
{
if (InitList())
{
FillList();
FillList(FALSE);
}
}
@ -1336,26 +1641,6 @@ LRESULT CDefView::OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandl
extern "C" DWORD WINAPI SHMenuIndexFromID(HMENU hMenu, UINT uID);
HMENU GetSubmenuByID(HMENU hmenu, UINT id)
{
MENUITEMINFOW mii = {sizeof(mii), MIIM_SUBMENU};
if (::GetMenuItemInfoW(hmenu, id, FALSE, &mii))
return mii.hSubMenu;
return NULL;
}
// ReallyGetMenuItemID returns the id of an item even if it opens a submenu,
// GetMenuItemID returns -1 if the specified item opens a submenu
UINT ReallyGetMenuItemID(HMENU hmenu, int i)
{
MENUITEMINFOW mii = {sizeof(mii), MIIM_ID};
if (::GetMenuItemInfoW(hmenu, i, TRUE, &mii))
return mii.wID;
return UINT_MAX;
}
HRESULT CDefView::FillFileMenu()
{
HMENU hFileMenu = GetSubmenuByID(m_hMenu, FCIDM_MENU_FILE);
@ -1383,9 +1668,8 @@ HRESULT CDefView::FillFileMenu()
return hr;
HMENU hmenu = CreatePopupMenu();
UINT cmf = GetContextMenuFlags(m_pShellBrowser, SFGAO_CANRENAME);
hr = m_pFileMenu->QueryContextMenu(hmenu, 0, FCIDM_SHVIEWFIRST, FCIDM_SHVIEWLAST, cmf);
hr = m_pFileMenu->QueryContextMenu(hmenu, 0, DVIDM_CONTEXTMENU_FIRST, DVIDM_CONTEXTMENU_LAST, cmf);
if (FAILED_UNEXPECTEDLY(hr))
return hr;
@ -1433,18 +1717,35 @@ HRESULT CDefView::FillViewMenu()
HRESULT CDefView::FillArrangeAsMenu(HMENU hmenuArrange)
{
/* We only need to fill this once */
if (GetMenuItemID(hmenuArrange, 0) == FCIDM_SHVIEW_AUTOARRANGE)
bool forceMerge = false;
UINT currentSortId = DVIDM_ARRANGESORT_FIRST + m_sortInfo.ListColumn;
// Make sure the column we currently sort by is in the menu
RemoveMenu(m_hMenuArrangeModes, DVIDM_ARRANGESORT_LAST, MF_BYCOMMAND);
if (m_sortInfo.ListColumn >= DEFVIEW_ARRANGESORT_MAXENUM)
{
Shell_MergeMenus(hmenuArrange, m_hMenuArrangeModes, 0, 0, 0xFFFF,0);
C_ASSERT(DEFVIEW_ARRANGESORT_MAXENUM < DEFVIEW_ARRANGESORT_MAX);
C_ASSERT(DVIDM_ARRANGESORT_FIRST + DEFVIEW_ARRANGESORT_MAXENUM == DVIDM_ARRANGESORT_LAST);
WCHAR buf[MAX_PATH];
LVCOLUMN lvc;
lvc.mask = LVCF_TEXT;
lvc.pszText = buf;
lvc.cchTextMax = _countof(buf);
currentSortId = DVIDM_ARRANGESORT_LAST;
forceMerge = true;
ListView_GetColumn(m_ListView.m_hWnd, m_sortInfo.ListColumn, &lvc);
AppendMenuItem(m_hMenuArrangeModes, MF_STRING, currentSortId, lvc.pszText, m_sortInfo.ListColumn);
}
// Prepend the sort-by items unless they are aleady there
if (GetMenuItemID(hmenuArrange, 0) == FCIDM_SHVIEW_AUTOARRANGE || forceMerge)
{
Shell_MergeMenus(hmenuArrange, m_hMenuArrangeModes, 0, 0, 0xFFFF, MM_ADDSEPARATOR);
}
/* Also check the menu item according to which we sort */
CheckMenuRadioItem(hmenuArrange,
0x30,
0x100,
m_sortInfo.nHeaderID + 0x30,
MF_BYCOMMAND);
DVIDM_ARRANGESORT_FIRST, DVIDM_ARRANGESORT_LAST,
currentSortId, MF_BYCOMMAND);
if (m_FolderSettings.ViewMode == FVM_DETAILS || m_FolderSettings.ViewMode == FVM_LIST)
{
@ -1483,6 +1784,105 @@ HRESULT CDefView::CheckViewMode(HMENU hmenuView)
return S_OK;
}
LRESULT CDefView::DoColumnContextMenu(LPARAM lParam)
{
const UINT maxItems = 15; // Feels about right
const UINT idMore = 0x1337;
UINT idFirst = idMore + 1, idLast = idFirst;
UINT lastValidListCol = 0; // Keep track of where the new column should be inserted
UINT showMore = GetKeyState(VK_SHIFT) < 0;
POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
HWND hWndHdr = ListView_GetHeader(m_ListView.m_hWnd);
if (lParam == ~0)
{
RECT r;
::GetWindowRect(hWndHdr, &r);
pt.x = r.left + ((r.right - r.left) / 2);
pt.y = r.top + ((r.bottom - r.top) / 2);
}
HMENU hMenu = CreatePopupMenu();
if (!hMenu)
return 0;
for (UINT foldCol = 0;; ++foldCol)
{
WCHAR buf[MAX_PATH];
SHELLDETAILS sd;
sd.str.uType = !STRRET_WSTR;
if (FAILED(GetDetailsByFolderColumn(NULL, foldCol, sd)))
break;
if (FAILED(StrRetToStrNW(buf, _countof(buf), &sd.str, NULL)))
break;
SHCOLSTATEF state = 0;
if (!m_pSF2Parent || FAILED(m_pSF2Parent->GetDefaultColumnState(foldCol, &state)))
state = 0;
showMore |= (state & (SHCOLSTATE_SECONDARYUI));
UINT mf = MF_STRING;
HRESULT listCol = MapFolderColumnToListColumn(foldCol);
if (foldCol == 0)
mf |= MF_CHECKED | MF_GRAYED | MF_DISABLED; // Force column 0
else if (state & (SHCOLSTATE_SECONDARYUI | SHCOLSTATE_HIDDEN))
continue;
else if (SUCCEEDED(listCol))
mf |= MF_CHECKED;
if (AppendMenuItem(hMenu, mf, idLast, buf, lastValidListCol + 1))
{
idLast++;
if (SUCCEEDED(listCol))
lastValidListCol = listCol;
}
if (idLast - idFirst == maxItems)
{
showMore++;
break;
}
}
if (showMore)
{
#if 0 // TODO
InsertMenuW(hMenu, -1, MF_SEPARATOR, 0, NULL);
InsertMenuW(hMenu, -1, MF_STRING, idMore, L"More...");
#endif
}
// A cludge to force the cursor to update so we are not stuck with "size left/right" if
// the right-click was on a column divider.
::PostMessage(m_ListView.m_hWnd, WM_SETCURSOR, (WPARAM) m_ListView.m_hWnd, HTCLIENT);
// Note: Uses the header as the owner so CDefView::OnInitMenuPopup does not mess us up.
UINT idCmd = TrackPopupMenu(hMenu, TPM_RETURNCMD | TPM_LEFTBUTTON | TPM_RIGHTBUTTON,
pt.x, pt.y, 0, hWndHdr, NULL);
if (idCmd == idMore)
{
FIXME("Open More dialog\n");
}
else if (idCmd)
{
UINT foldCol = idCmd - idFirst;
HRESULT listCol = MapFolderColumnToListColumn(foldCol);
if (SUCCEEDED(listCol))
{
ListView_DeleteColumn(m_ListView.m_hWnd, listCol);
}
else
{
listCol = (UINT) GetMenuItemDataById(hMenu, idCmd);
LoadColumn(foldCol, listCol, TRUE);
}
ColumnListChanged();
}
DestroyMenu(hMenu);
return 0;
}
///
// - fills the m_apidl list with the selected objects
//
@ -1575,7 +1975,7 @@ HRESULT CDefView::OpenSelectedItems()
return hResult;
UINT cmf = CMF_DEFAULTONLY | GetContextMenuFlags(m_pShellBrowser, 0);
hResult = pCM->QueryContextMenu(hMenu, 0, FCIDM_SHVIEWFIRST, FCIDM_SHVIEWLAST, cmf);
hResult = pCM->QueryContextMenu(hMenu, 0, DVIDM_CONTEXTMENU_FIRST, DVIDM_CONTEXTMENU_LAST, cmf);
if (FAILED_UNEXPECTEDLY(hResult))
return hResult;
@ -1593,7 +1993,7 @@ HRESULT CDefView::OpenSelectedItems()
LRESULT CDefView::OnContextMenu(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
{
POINT pt;
POINT pt = { pt.x = GET_X_LPARAM(lParam), pt.y = GET_Y_LPARAM(lParam) };
UINT uCommand;
HRESULT hResult;
@ -1605,15 +2005,19 @@ LRESULT CDefView::OnContextMenu(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &b
return 0;
}
HWND hWndHdr = ListView_GetHeader(m_ListView.m_hWnd);
RECT r;
if (::GetWindowRect(hWndHdr, &r) && PtInRect(&r, pt) && ::IsWindowVisible(hWndHdr))
{
return DoColumnContextMenu(lParam);
}
m_hContextMenu = CreatePopupMenu();
if (!m_hContextMenu)
return E_FAIL;
if (lParam != ~0) // unless app key (menu key) was pressed
{
pt.x = GET_X_LPARAM(lParam);
pt.y = GET_Y_LPARAM(lParam);
LV_HITTESTINFO hittest = { pt };
ScreenToClient(&hittest.pt);
m_ListView.HitTest(&hittest);
@ -1636,7 +2040,7 @@ LRESULT CDefView::OnContextMenu(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &b
UINT cmf = GetContextMenuFlags(m_pShellBrowser, SFGAO_CANRENAME);
// Use 1 as the first id we want. 0 means that user canceled the menu
hResult = m_pCM->QueryContextMenu(m_hContextMenu, 0, CONTEXT_MENU_BASE_ID, FCIDM_SHVIEWLAST, cmf);
hResult = m_pCM->QueryContextMenu(m_hContextMenu, 0, CONTEXT_MENU_BASE_ID, DVIDM_CONTEXTMENU_LAST, cmf);
if (FAILED_UNEXPECTEDLY(hResult))
return 0;
@ -1680,6 +2084,9 @@ LRESULT CDefView::OnContextMenu(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &b
if (uCommand == 0)
return 0;
if (uCommand >= DVIDM_ARRANGESORT_FIRST && uCommand <= DVIDM_ARRANGESORT_LAST)
return SendMessage(WM_COMMAND, uCommand, 0);
if (uCommand == FCIDM_SHVIEW_OPEN && OnDefaultCommand() == S_OK)
return 0;
@ -1700,13 +2107,14 @@ LRESULT CDefView::OnExplorerCommand(UINT uCommand, BOOL bUseSelection)
MenuCleanup _(pCM, hMenu);
// TODO: Why are these two special?
if ((uCommand != FCIDM_SHVIEW_DELETE) && (uCommand != FCIDM_SHVIEW_RENAME))
{
hMenu = CreatePopupMenu();
if (!hMenu)
return 0;
hResult = pCM->QueryContextMenu(hMenu, 0, FCIDM_SHVIEWFIRST, FCIDM_SHVIEWLAST, CMF_NORMAL);
hResult = pCM->QueryContextMenu(hMenu, 0, DVIDM_CONTEXTMENU_FIRST, DVIDM_CONTEXTMENU_LAST, CMF_NORMAL);
if (FAILED_UNEXPECTEDLY(hResult))
return 0;
}
@ -1882,6 +2290,13 @@ LRESULT CDefView::OnCommand(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHand
TRACE("(%p)->(0x%08x 0x%08x %p) stub\n", this, dwCmdID, dwCmd, hwndCmd);
if (dwCmdID >= DVIDM_ARRANGESORT_FIRST && dwCmdID <= DVIDM_ARRANGESORT_LAST)
{
UINT listCol = (UINT)GetMenuItemDataById(m_hMenuArrangeModes, dwCmdID);
_Sort(listCol);
return 0;
}
switch (dwCmdID)
{
case FCIDM_SHVIEW_SMALLICON:
@ -1904,15 +2319,6 @@ LRESULT CDefView::OnCommand(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHand
m_ListView.ModifyStyle(LVS_TYPEMASK, LVS_REPORT);
CheckToolbar();
break;
/* the menu-ID's for sorting are 0x30... see shrec.rc */
case 0x30:
case 0x31:
case 0x32:
case 0x33:
m_sortInfo.nHeaderID = dwCmdID - 0x30;
m_sortInfo.bIsAscending = TRUE;
_Sort();
break;
case FCIDM_SHVIEW_SNAPTOGRID:
m_ListView.Arrange(LVA_SNAPTOGRID);
break;
@ -2058,12 +2464,9 @@ LRESULT CDefView::OnNotify(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandl
OnStateChange(CDBOSC_SELCHANGE); // browser will get the IDataObject
break;
case LVN_COLUMNCLICK:
m_sortInfo.nHeaderID = lpnmlv->iSubItem;
if (m_sortInfo.nLastHeaderID == m_sortInfo.nHeaderID)
m_sortInfo.bIsAscending = !m_sortInfo.bIsAscending;
else
m_sortInfo.bIsAscending = TRUE;
_Sort();
// The folder returns 0 if it sorted, otherwise we do the sorting
if (!m_pSDParent || m_pSDParent->ColumnClick(MapListColumnToFolderColumn(lpnmlv->iSubItem)))
_Sort(lpnmlv->iSubItem);
break;
case LVN_GETDISPINFOA:
case LVN_GETDISPINFOW:
@ -2072,28 +2475,21 @@ LRESULT CDefView::OnNotify(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandl
if (lpdi->item.mask & LVIF_TEXT) /* text requested */
{
if (m_pSF2Parent)
{
SHELLDETAILS sd;
if (FAILED_UNEXPECTEDLY(m_pSF2Parent->GetDetailsOf(pidl, lpdi->item.iSubItem, &sd)))
break;
SHELLDETAILS sd;
if (FAILED_UNEXPECTEDLY(GetDetailsByListColumn(pidl, lpdi->item.iSubItem, sd)))
break;
if (lpnmh->code == LVN_GETDISPINFOA)
{
/* shouldn't happen */
NMLVDISPINFOA *lpdiA = (NMLVDISPINFOA *)lpnmh;
StrRetToStrNA( lpdiA->item.pszText, lpdiA->item.cchTextMax, &sd.str, NULL);
TRACE("-- text=%s\n", lpdiA->item.pszText);
}
else /* LVN_GETDISPINFOW */
{
StrRetToStrNW( lpdi->item.pszText, lpdi->item.cchTextMax, &sd.str, NULL);
TRACE("-- text=%s\n", debugstr_w(lpdi->item.pszText));
}
}
else
if (lpnmh->code == LVN_GETDISPINFOA)
{
FIXME("no m_pSF2Parent\n");
/* shouldn't happen */
NMLVDISPINFOA *lpdiA = (NMLVDISPINFOA *)lpnmh;
StrRetToStrNA( lpdiA->item.pszText, lpdiA->item.cchTextMax, &sd.str, NULL);
TRACE("-- text=%s\n", lpdiA->item.pszText);
}
else /* LVN_GETDISPINFOW */
{
StrRetToStrNW( lpdi->item.pszText, lpdi->item.cchTextMax, &sd.str, NULL);
TRACE("-- text=%s\n", debugstr_w(lpdi->item.pszText));
}
}
if(lpdi->item.mask & LVIF_IMAGE) /* image requested */

View file

@ -256,10 +256,6 @@ CDefViewBckgrndMenu::InvokeCommand(LPCMINVOKECOMMANDINFO lpcmi)
case FCIDM_SHVIEW_SMALLICON:
case FCIDM_SHVIEW_LISTVIEW:
case FCIDM_SHVIEW_REPORTVIEW:
case 0x30: /* FIX IDS in resource files */
case 0x31:
case 0x32:
case 0x33:
case FCIDM_SHVIEW_AUTOARRANGE:
case FCIDM_SHVIEW_SNAPTOGRID:
case FCIDM_SHVIEW_REFRESH: