#include <stdarg.h>
#include "windef.h"
#include "winbase.h"
#include "wingdi.h"
#include "winuser.h"
#include "winnls.h"
#include "commctrl.h"
#include "comctl32.h"
#include "wine/debug.h"
WINE_DEFAULT_DEBUG_CHANNEL(commctrl);
#define DRAGLIST_SUBCLASSID 0
#define DRAGLIST_SCROLLPERIOD 200
#define DRAGLIST_TIMERID 666
#define DRAGICON_HOTSPOT_X 17
#define DRAGICON_HOTSPOT_Y 7
#define DRAGICON_HEIGHT 32
typedef struct _DRAGLISTDATA
{
BOOL dragging;
HCURSOR cursor;
LRESULT last_dragging_response;
RECT last_drag_icon_rect;
} DRAGLISTDATA;
UINT uDragListMessage = 0;
static DWORD dwLastScrollTime = 0;
static HICON hDragArrow = NULL;
static LRESULT DragList_Notify(HWND hwndLB, UINT uNotification)
{
DRAGLISTINFO dli;
dli.hWnd = hwndLB;
dli.uNotification = uNotification;
GetCursorPos(&dli.ptCursor);
return SendMessageW(GetParent(hwndLB), uDragListMessage, GetDlgCtrlID(hwndLB), (LPARAM)&dli);
}
static void DragList_EndDrag(HWND hwnd, DRAGLISTDATA * data)
{
KillTimer(hwnd, DRAGLIST_TIMERID);
ReleaseCapture();
InvalidateRect(GetParent(hwnd), &data->last_drag_icon_rect, TRUE);
memset(data, 0, sizeof(*data));
}
static LRESULT CALLBACK
DragList_SubclassWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
{
DRAGLISTDATA * data = (DRAGLISTDATA*)dwRefData;
switch (uMsg)
{
case WM_LBUTTONDOWN:
SetFocus(hwnd);
data->dragging = DragList_Notify(hwnd, DL_BEGINDRAG);
if (data->dragging)
{
SetCapture(hwnd);
SetTimer(hwnd, DRAGLIST_TIMERID, DRAGLIST_SCROLLPERIOD, NULL);
}
break;
case WM_KEYDOWN:
case WM_RBUTTONDOWN:
if ((data->dragging) &&
((uMsg == WM_RBUTTONDOWN) || (wParam == VK_ESCAPE)))
{
DragList_EndDrag(hwnd, data);
DragList_Notify(hwnd, DL_CANCELDRAG);
return 0;
}
break;
case WM_MOUSEMOVE:
case WM_TIMER:
if (data->dragging)
{
LRESULT cursor = DragList_Notify(hwnd, DL_DRAGGING);
if (data->last_dragging_response != cursor)
{
switch (cursor)
{
case DL_STOPCURSOR:
data->cursor = LoadCursorW(NULL, (LPCWSTR)IDC_NO);
SetCursor(data->cursor);
break;
case DL_COPYCURSOR:
data->cursor = LoadCursorW(COMCTL32_hModule, (LPCWSTR)IDC_COPY);
SetCursor(data->cursor);
break;
case DL_MOVECURSOR:
data->cursor = LoadCursorW(NULL, (LPCWSTR)IDC_ARROW);
SetCursor(data->cursor);
break;
}
data->last_dragging_response = cursor;
}
return 0;
}
break;
case WM_LBUTTONUP:
if (data->dragging)
{
DragList_EndDrag(hwnd, data);
DragList_Notify(hwnd, DL_DROPPED);
}
break;
case WM_GETDLGCODE:
if (data->dragging)
return DLGC_WANTALLKEYS;
break;
case WM_NCDESTROY:
RemoveWindowSubclass(hwnd, DragList_SubclassWindowProc, DRAGLIST_SUBCLASSID);
Free(data);
break;
}
return DefSubclassProc(hwnd, uMsg, wParam, lParam);
}
BOOL WINAPI MakeDragList (HWND hwndLB)
{
DRAGLISTDATA *data = Alloc(sizeof(DRAGLISTDATA));
TRACE("(%p)\n", hwndLB);
if (!uDragListMessage)
uDragListMessage = RegisterWindowMessageW(DRAGLISTMSGSTRINGW);
return SetWindowSubclass(hwndLB, DragList_SubclassWindowProc, DRAGLIST_SUBCLASSID, (DWORD_PTR)data);
}
VOID WINAPI DrawInsert (HWND hwndParent, HWND hwndLB, INT nItem)
{
RECT rcItem, rcListBox, rcDragIcon;
HDC hdc;
DRAGLISTDATA * data;
TRACE("(%p %p %d)\n", hwndParent, hwndLB, nItem);
if (!hDragArrow)
hDragArrow = LoadIconW(COMCTL32_hModule, (LPCWSTR)IDI_DRAGARROW);
if (LB_ERR == SendMessageW(hwndLB, LB_GETITEMRECT, nItem, (LPARAM)&rcItem))
return;
if (!GetWindowRect(hwndLB, &rcListBox))
return;
if (!MapWindowPoints(hwndLB, hwndParent, (LPPOINT)&rcItem, 2))
return;
if (!MapWindowPoints(HWND_DESKTOP, hwndParent, (LPPOINT)&rcListBox, 2))
return;
rcDragIcon.left = rcListBox.left - DRAGICON_HOTSPOT_X;
rcDragIcon.top = rcItem.top - DRAGICON_HOTSPOT_Y;
rcDragIcon.right = rcListBox.left;
rcDragIcon.bottom = rcDragIcon.top + DRAGICON_HEIGHT;
if (!GetWindowSubclass(hwndLB, DragList_SubclassWindowProc, DRAGLIST_SUBCLASSID, (DWORD_PTR*)&data))
return;
if (nItem < 0)
SetRectEmpty(&rcDragIcon);
if (!EqualRect(&rcDragIcon, &data->last_drag_icon_rect))
{
RedrawWindow(hwndParent, &data->last_drag_icon_rect, NULL,
RDW_INTERNALPAINT | RDW_ERASE | RDW_INVALIDATE | RDW_UPDATENOW);
data->last_drag_icon_rect = rcDragIcon;
if (nItem >= 0)
{
hdc = GetDC(hwndParent);
DrawIcon(hdc, rcDragIcon.left, rcDragIcon.top, hDragArrow);
ReleaseDC(hwndParent, hdc);
}
}
}
INT WINAPI LBItemFromPt (HWND hwndLB, POINT pt, BOOL bAutoScroll)
{
RECT rcClient;
INT nIndex;
DWORD dwScrollTime;
TRACE("%p, %ld x %ld, %s\n", hwndLB, pt.x, pt.y, bAutoScroll ? "TRUE" : "FALSE");
ScreenToClient (hwndLB, &pt);
GetClientRect (hwndLB, &rcClient);
nIndex = (INT)SendMessageW (hwndLB, LB_GETTOPINDEX, 0, 0);
if (PtInRect (&rcClient, pt))
{
while (TRUE)
{
if (SendMessageW (hwndLB, LB_GETITEMRECT, nIndex, (LPARAM)&rcClient) == LB_ERR)
return -1;
if (PtInRect (&rcClient, pt))
return nIndex;
nIndex++;
}
}
else
{
if (!bAutoScroll)
return -1;
if ((pt.x > rcClient.right) || (pt.x < rcClient.left))
return -1;
if (pt.y < 0)
nIndex--;
else
nIndex++;
dwScrollTime = GetTickCount ();
if ((dwScrollTime - dwLastScrollTime) < DRAGLIST_SCROLLPERIOD)
return -1;
dwLastScrollTime = dwScrollTime;
SendMessageW (hwndLB, LB_SETTOPINDEX, nIndex, 0);
}
return -1;
}