https://git.reactos.org/?p=reactos.git;a=commitdiff;h=8c87489a43ce04b4f8a83bf75a5c1712a315146f

commit 8c87489a43ce04b4f8a83bf75a5c1712a315146f
Author:     Katayama Hirofumi MZ <[email protected]>
AuthorDate: Thu Sep 17 22:18:34 2020 +0900
Commit:     GitHub <[email protected]>
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)
     {

Reply via email to