https://git.reactos.org/?p=reactos.git;a=commitdiff;h=8c87489a43ce04b4f8a83…
commit 8c87489a43ce04b4f8a83bf75a5c1712a315146f
Author: Katayama Hirofumi MZ <katayama.hirofumi.mz(a)gmail.com>
AuthorDate: Thu Sep 17 22:18:34 2020 +0900
Commit: GitHub <noreply(a)github.com>
CommitDate: Thu Sep 17 22:18:34 2020 +0900
[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
---
dll/win32/comctl32/listview.c | 10 +++
dll/win32/shell32/CDefView.cpp | 192 ++++++++++++++++++++++++++++++++++++++---
2 files changed, 191 insertions(+), 11 deletions(-)
diff --git a/dll/win32/comctl32/listview.c b/dll/win32/comctl32/listview.c
index e26457b48dc..4119c27a90a 100644
--- a/dll/win32/comctl32/listview.c
+++ b/dll/win32/comctl32/listview.c
@@ -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);
diff --git a/dll/win32/shell32/CDefView.cpp b/dll/win32/shell32/CDefView.cpp
index 6495b08610e..b3ed556acf4 100644
--- a/dll/win32/shell32/CDefView.cpp
+++ b/dll/win32/shell32/CDefView.cpp
@@ -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)
{