https://git.reactos.org/?p=reactos.git;a=commitdiff;h=8c87489a43ce04b4f8a83b...
commit 8c87489a43ce04b4f8a83bf75a5c1712a315146f Author: Katayama Hirofumi MZ katayama.hirofumi.mz@gmail.com AuthorDate: Thu Sep 17 22:18:34 2020 +0900 Commit: GitHub noreply@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) {