[COMCTL32][SHELL32] Fix arrow keys and reordering on ListView (#3162)

- Disable special auto-arrange codes in LISTVIEW_GetNextItem function.
- Add auto-arrange reordering codes on CDefView.
CORE-16875
This commit is contained in:
Katayama Hirofumi MZ 2020-09-17 22:18:34 +09:00 committed by GitHub
parent 0f66c66f72
commit 8c87489a43
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 191 additions and 11 deletions

View file

@ -7449,7 +7449,9 @@ static INT LISTVIEW_GetNextItem(const LISTVIEW_INFO *infoPtr, INT nItem, UINT uF
UINT uMask = 0;
LVFINDINFOW lvFindInfo;
INT nCountPerColumn;
#ifndef __REACTOS__
INT nCountPerRow;
#endif
INT i;
TRACE("nItem=%d, uFlags=%x, nItemCount=%d\n", nItem, uFlags, infoPtr->nItemCount);
@ -7490,6 +7492,7 @@ static INT LISTVIEW_GetNextItem(const LISTVIEW_INFO *infoPtr, INT nItem, UINT uF
}
else
{
#ifndef __REACTOS__
/* Special case for autoarrange - move 'til the top of a list */
if (is_autoarrange(infoPtr))
{
@ -7502,6 +7505,7 @@ static INT LISTVIEW_GetNextItem(const LISTVIEW_INFO *infoPtr, INT nItem, UINT uF
}
return -1;
}
#endif
lvFindInfo.flags = LVFI_NEARESTXY;
lvFindInfo.vkDirection = VK_UP;
LISTVIEW_GetItemPosition(infoPtr, nItem, &lvFindInfo.pt);
@ -7525,6 +7529,7 @@ static INT LISTVIEW_GetNextItem(const LISTVIEW_INFO *infoPtr, INT nItem, UINT uF
}
else
{
#ifndef __REACTOS__
/* Special case for autoarrange - move 'til the bottom of a list */
if (is_autoarrange(infoPtr))
{
@ -7537,6 +7542,7 @@ static INT LISTVIEW_GetNextItem(const LISTVIEW_INFO *infoPtr, INT nItem, UINT uF
}
return -1;
}
#endif
lvFindInfo.flags = LVFI_NEARESTXY;
lvFindInfo.vkDirection = VK_DOWN;
LISTVIEW_GetItemPosition(infoPtr, nItem, &lvFindInfo.pt);
@ -7561,6 +7567,7 @@ static INT LISTVIEW_GetNextItem(const LISTVIEW_INFO *infoPtr, INT nItem, UINT uF
}
else if ((infoPtr->uView == LV_VIEW_SMALLICON) || (infoPtr->uView == LV_VIEW_ICON))
{
#ifndef __REACTOS__
/* Special case for autoarrange - move 'til the beginning of a row */
if (is_autoarrange(infoPtr))
{
@ -7573,6 +7580,7 @@ static INT LISTVIEW_GetNextItem(const LISTVIEW_INFO *infoPtr, INT nItem, UINT uF
}
return -1;
}
#endif
lvFindInfo.flags = LVFI_NEARESTXY;
lvFindInfo.vkDirection = VK_LEFT;
LISTVIEW_GetItemPosition(infoPtr, nItem, &lvFindInfo.pt);
@ -7597,6 +7605,7 @@ static INT LISTVIEW_GetNextItem(const LISTVIEW_INFO *infoPtr, INT nItem, UINT uF
}
else if ((infoPtr->uView == LV_VIEW_SMALLICON) || (infoPtr->uView == LV_VIEW_ICON))
{
#ifndef __REACTOS__
/* Special case for autoarrange - move 'til the end of a row */
if (is_autoarrange(infoPtr))
{
@ -7609,6 +7618,7 @@ static INT LISTVIEW_GetNextItem(const LISTVIEW_INFO *infoPtr, INT nItem, UINT uF
}
return -1;
}
#endif
lvFindInfo.flags = LVFI_NEARESTXY;
lvFindInfo.vkDirection = VK_RIGHT;
LISTVIEW_GetItemPosition(infoPtr, nItem, &lvFindInfo.pt);

View file

@ -57,6 +57,20 @@ typedef struct
to call TrackPopupMenu and let it use the 0 value as an indication that the menu was canceled */
#define CONTEXT_MENU_BASE_ID 1
/* Convert client coordinates to listview coordinates */
static void
ClientToListView(HWND hwndLV, POINT *ppt)
{
POINT Origin;
/* FIXME: LVM_GETORIGIN is broken. See CORE-17266 */
if (!ListView_GetOrigin(hwndLV, &Origin))
return;
ppt->x += Origin.x;
ppt->y += Origin.y;
}
class CDefView :
public CWindowImpl<CDefView, CWindow, CControlWinTraits>,
public CComObjectRootEx<CComMultiThreadModelNoCS>,
@ -117,6 +131,8 @@ class CDefView :
BOOL _Sort();
HRESULT _DoFolderViewCB(UINT uMsg, WPARAM wParam, LPARAM lParam);
HRESULT _GetSnapToGrid();
void _MoveSelectionOnAutoArrange(POINT pt);
INT _FindInsertableIndexFromPoint(POINT pt);
public:
CDefView();
@ -2019,6 +2035,7 @@ LRESULT CDefView::OnNotify(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandl
m_pSourceDataObject = pda;
m_ptFirstMousePos = params->ptAction;
ClientToScreen(&m_ptFirstMousePos);
::ClientToListView(m_ListView, &m_ptFirstMousePos);
HIMAGELIST big_icons, small_icons;
Shell_GetImageLists(&big_icons, &small_icons);
@ -3263,6 +3280,7 @@ HRESULT CDefView::drag_notify_subitem(DWORD grfKeyState, POINTL pt, DWORD *pdwEf
}
m_ptLastMousePos = htinfo.pt;
::ClientToListView(m_ListView, &m_ptLastMousePos);
/* We need to check if we drag the selection over itself */
if (lResult != -1 && m_pSourceDataObject.p != NULL)
@ -3378,6 +3396,150 @@ HRESULT WINAPI CDefView::DragLeave()
return S_OK;
}
INT CDefView::_FindInsertableIndexFromPoint(POINT pt)
{
RECT rcBound;
INT i, nCount = m_ListView.GetItemCount();
DWORD dwSpacing;
INT dx, dy;
BOOL bSmall = ((m_ListView.GetStyle() & LVS_TYPEMASK) != LVS_ICON);
/* FIXME: LVM_GETORIGIN is broken. See CORE-17266 */
pt.x += m_ListView.GetScrollPos(SB_HORZ);
pt.y += m_ListView.GetScrollPos(SB_VERT);
if (m_ListView.GetStyle() & LVS_ALIGNLEFT)
{
// vertically
for (i = 0; i < nCount; ++i)
{
dwSpacing = ListView_GetItemSpacing(m_ListView, bSmall);
dx = LOWORD(dwSpacing);
dy = HIWORD(dwSpacing);
ListView_GetItemRect(m_ListView, i, &rcBound, LVIR_SELECTBOUNDS);
rcBound.right = rcBound.left + dx;
rcBound.bottom = rcBound.top + dy;
if (pt.x < rcBound.right && pt.y < (rcBound.top + rcBound.bottom) / 2)
{
return i;
}
}
for (i = nCount - 1; i >= 0; --i)
{
ListView_GetItemRect(m_ListView, i, &rcBound, LVIR_SELECTBOUNDS);
if (rcBound.left < pt.x && rcBound.top < pt.y)
{
return i + 1;
}
}
}
else
{
// horizontally
for (i = 0; i < nCount; ++i)
{
dwSpacing = ListView_GetItemSpacing(m_ListView, bSmall);
dx = LOWORD(dwSpacing);
dy = HIWORD(dwSpacing);
ListView_GetItemRect(m_ListView, i, &rcBound, LVIR_SELECTBOUNDS);
rcBound.right = rcBound.left + dx;
rcBound.bottom = rcBound.top + dy;
if (pt.y < rcBound.bottom && pt.x < rcBound.left)
{
return i;
}
if (pt.y < rcBound.bottom && pt.x < rcBound.right)
{
return i + 1;
}
}
for (i = nCount - 1; i >= 0; --i)
{
ListView_GetItemRect(m_ListView, i, &rcBound, LVIR_SELECTBOUNDS);
if (rcBound.left < pt.x && rcBound.top < pt.y)
{
return i + 1;
}
}
}
return nCount;
}
typedef CSimpleMap<LPARAM, INT> CLParamIndexMap;
static INT CALLBACK
SelectionMoveCompareFunc(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
{
CLParamIndexMap *pmap = (CLParamIndexMap *)lParamSort;
INT i1 = pmap->Lookup(lParam1), i2 = pmap->Lookup(lParam2);
if (i1 < i2)
return -1;
if (i1 > i2)
return 1;
return 0;
}
void CDefView::_MoveSelectionOnAutoArrange(POINT pt)
{
// get insertable index from position
INT iPosition = _FindInsertableIndexFromPoint(pt);
// create identity mapping of indexes
CSimpleArray<INT> array;
INT nCount = m_ListView.GetItemCount();
for (INT i = 0; i < nCount; ++i)
{
array.Add(i);
}
// re-ordering mapping
INT iItem = -1;
while ((iItem = m_ListView.GetNextItem(iItem, LVNI_SELECTED)) >= 0)
{
INT iFrom = iItem, iTo = iPosition;
if (iFrom < iTo)
--iTo;
if (iFrom >= nCount)
iFrom = nCount - 1;
if (iTo >= nCount)
iTo = nCount - 1;
// shift indexes by swapping (like a bucket relay)
if (iFrom < iTo)
{
for (INT i = iFrom; i < iTo; ++i)
{
// swap array[i] and array[i + 1]
INT tmp = array[i];
array[i] = array[i + 1];
array[i + 1] = tmp;
}
}
else
{
for (INT i = iFrom; i > iTo; --i)
{
// swap array[i] and array[i - 1]
INT tmp = array[i];
array[i] = array[i - 1];
array[i - 1] = tmp;
}
}
}
// create mapping (ListView's lParam to index) from array
CLParamIndexMap map;
for (INT i = 0; i < nCount; ++i)
{
LPARAM lParam = m_ListView.GetItemData(array[i]);
map.Add(lParam, i);
}
// finally sort
m_ListView.SortItems(SelectionMoveCompareFunc, &map);
}
HRESULT WINAPI CDefView::Drop(IDataObject* pDataObject, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect)
{
ImageList_DragLeave(m_hWnd);
@ -3393,23 +3555,31 @@ HRESULT WINAPI CDefView::Drop(IDataObject* pDataObject, DWORD grfKeyState, POINT
m_pCurDropTarget.Release();
}
/* Restore the selection */
m_ListView.SetItemState(-1, 0, LVIS_SELECTED);
for (UINT i = 0 ; i < m_cidl; i++)
SelectItem(m_apidl[i], SVSI_SELECT);
POINT ptDrop = { pt.x, pt.y };
::ScreenToClient(m_ListView, &ptDrop);
::ClientToListView(m_ListView, &ptDrop);
m_ptLastMousePos = ptDrop;
/* Reposition the items */
int lvIndex = -1;
while ((lvIndex = m_ListView.GetNextItem(lvIndex, LVNI_SELECTED)) > -1)
m_ListView.SetRedraw(FALSE);
if (m_ListView.GetStyle() & LVS_AUTOARRANGE)
{
_MoveSelectionOnAutoArrange(m_ptLastMousePos);
}
else
{
POINT ptItem;
if (m_ListView.GetItemPosition(lvIndex, &ptItem))
INT iItem = -1;
while ((iItem = m_ListView.GetNextItem(iItem, LVNI_SELECTED)) >= 0)
{
ptItem.x += pt.x - m_ptFirstMousePos.x;
ptItem.y += pt.y - m_ptFirstMousePos.y;
m_ListView.SetItemPosition(lvIndex, &ptItem);
if (m_ListView.GetItemPosition(iItem, &ptItem))
{
ptItem.x += m_ptLastMousePos.x - m_ptFirstMousePos.x;
ptItem.y += m_ptLastMousePos.y - m_ptFirstMousePos.y;
m_ListView.SetItemPosition(iItem, &ptItem);
}
}
}
m_ListView.SetRedraw(TRUE);
}
else if (m_pCurDropTarget)
{