Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

534 lines
12 KiB

/******************************Module*Header*******************************\
* Module Name: DragDrop.c
*
* An attempt to implement dragging and dropping between Multi-selection
* listboxes.
*
* Created: dd-mm-93
* Author: Stephen Estrop [StephenE]
*
* Copyright (c) 1993 Microsoft Corporation
\**************************************************************************/
#define NOOLE
#define NODRAGLIST
#include <windows.h>
#include <windowsx.h>
#include "resource.h"
#include "dragdrop.h"
#define LONG2POINT(l, pt) ((pt).x = (SHORT)LOWORD(l), \
(pt).y = (SHORT)HIWORD(l))
#define DF_ACTUALLYDRAG 0x0001
#define DF_DEFERRED 0x0002
#define INITLINESPERSECOND 36
#define VERTCHANGENUMLINES 25
#define TIMERID 238
#define TIMERLEN 250
#define TIMERLEN2 50
#define DX_INSERT 16
#define DY_INSERT 16
typedef struct {
WNDPROC lpfnDefProc;
HWND hwndDrag;
UINT uFlags;
DWORD dwState;
} DRAGPROP, *PDRAGPROP;
UINT uDragListMsg = 0L;
TCHAR szDragListMsgString[] = TEXT(SJE_DRAGLISTMSGSTRING);
TCHAR szDragProp[] = TEXT("DragMultiProp");
extern HINSTANCE g_hInst;
/******************************Public*Routine******************************\
* InitDragMultiList
*
*
*
* History:
* dd-mm-94 - StephenE - Created
*
\**************************************************************************/
UINT WINAPI
InitDragMultiList(
void
)
{
if (!uDragListMsg) {
uDragListMsg = RegisterWindowMessage(szDragListMsgString);
if (!uDragListMsg) {
return 0;
}
}
return uDragListMsg;
}
/******************************Public*Routine******************************\
* DragListSubclassProc
*
*
*
* History:
* dd-mm-93 - StephenE - Created
*
\**************************************************************************/
LRESULT CALLBACK
DragListSubclassProc(
HWND hLB,
UINT uMsg,
WPARAM wParam,
LPARAM lParam
)
{
PDRAGPROP pDragProp;
DRAGMULTILISTINFO sNotify;
WNDPROC lpfnDefProc;
BOOL bDragging;
pDragProp = (PDRAGPROP)GetProp(hLB, szDragProp);
bDragging = pDragProp->hwndDrag == hLB;
// Save this in case anything happens to pDragProp before we return.
lpfnDefProc = pDragProp->lpfnDefProc;
switch (uMsg) {
case WM_DESTROY:
if (bDragging)
SendMessage(hLB, WM_RBUTTONDOWN, 0, 0L); // cancel drag
// Restore the window proc just in case.
SubclassWindow( hLB, lpfnDefProc );
if (pDragProp) {
LocalFree((HLOCAL)pDragProp);
RemoveProp(hLB, szDragProp);
}
break;
case WM_LBUTTONDOWN:
{
POINT pt;
int nItem;
if (bDragging) // nested button-down
SendMessage(hLB, WM_RBUTTONDOWN, 0, 0L); // cancel drag
SetFocus(hLB);
LONG2POINT(lParam, pt);
ClientToScreen(hLB, &pt);
nItem = LBMultiItemFromPt(hLB, pt, FALSE);
if ( nItem >= 0 ) {
//
// We can only allow dragging if the item is selected.
// If the item is not selected - pass the message on.
//
if ( ListBox_GetSel( hLB, nItem ) <= 0 ) {
return CallWindowProc( lpfnDefProc, hLB, uMsg,
wParam, lParam );
}
pDragProp->dwState = (wParam & MK_CONTROL) ? DL_COPY : DL_MOVE;
sNotify.uNotification = DL_BEGINDRAG;
goto QueryParent;
}
else {
goto FakeDrag;
}
}
case WM_TIMER:
if (wParam != TIMERID) {
break;
}
{
POINT CursorPos;
GetCursorPos( &CursorPos );
ScreenToClient( hLB, &CursorPos );
lParam = MAKELPARAM((WORD)CursorPos.x, (WORD)CursorPos.y);
}
// Fall through
case WM_MOUSEMOVE:
if (bDragging) {
HWND hwndParent;
DWORD dwRet;
// We may be just simulating a drag, but not actually doing
// anything.
if (!(pDragProp->uFlags&DF_ACTUALLYDRAG)) {
return(0L);
}
if ( pDragProp->uFlags & DF_DEFERRED ) {
pDragProp->uFlags &= ~DF_DEFERRED;
KillTimer(hLB, TIMERID);
SetTimer(hLB, TIMERID, TIMERLEN2, NULL);
}
sNotify.uNotification = DL_DRAGGING;
QueryParent:
hwndParent = GetParent( hLB );
sNotify.hWnd = hLB;
sNotify.dwState = pDragProp->dwState;
LONG2POINT( lParam, sNotify.ptCursor );
ClientToScreen( hLB, &sNotify.ptCursor );
dwRet = SendMessage( hwndParent, uDragListMsg, GetDlgCtrlID(hLB),
(LPARAM)(LPDRAGMULTILISTINFO)&sNotify );
if ( uMsg == WM_LBUTTONDOWN ) {
// Some things may not be draggable
if (dwRet) {
SetTimer(hLB, TIMERID, TIMERLEN, NULL);
pDragProp->uFlags = DF_DEFERRED | DF_ACTUALLYDRAG;
}
else {
FakeDrag:
pDragProp->uFlags = 0;
}
// Set capture and change mouse cursor
pDragProp->hwndDrag = hLB;
SetCapture( hLB );
}
// Don't call the def proc, since it may try to change the
// selection or set timers or things like that.
return 0L;
}
break;
case WM_RBUTTONDOWN:
case WM_LBUTTONUP:
// if we are capturing mouse - release it and check for an
// acceptable place where mouse is now to decide drop or not
if (bDragging) {
HWND hwndParent;
pDragProp->hwndDrag = NULL;
KillTimer(hLB, TIMERID);
ReleaseCapture();
SetCursor(LoadCursor(NULL, IDC_ARROW));
hwndParent = GetParent(hLB);
sNotify.uNotification = (uMsg == WM_LBUTTONUP)
? DL_DROPPED : DL_CANCELDRAG;
sNotify.hWnd = hLB;
sNotify.dwState = pDragProp->dwState;
LONG2POINT( lParam, sNotify.ptCursor );
ClientToScreen( hLB, &sNotify.ptCursor );
SendMessage( hwndParent, uDragListMsg, GetDlgCtrlID(hLB),
(LPARAM)(LPDRAGMULTILISTINFO)&sNotify);
//
// If we didn't actually do any dragging just fake a button
// click at the current location.
//
if ( pDragProp->uFlags & DF_DEFERRED ) {
CallWindowProc(lpfnDefProc, hLB, WM_LBUTTONDOWN, wParam, lParam);
CallWindowProc(lpfnDefProc, hLB, uMsg, wParam, lParam);
}
// We need to make sure to return 0 in case this is from a
// keyboard message.
return 0L;
}
break;
case WM_GETDLGCODE:
if (bDragging)
return (CallWindowProc(lpfnDefProc, hLB, uMsg, wParam, lParam)
| DLGC_WANTMESSAGE);
break;
case WM_KEYDOWN:
if (wParam == VK_ESCAPE) {
SendMessage(hLB, WM_RBUTTONDOWN, 0, 0L);
}
case WM_CHAR:
case WM_KEYUP:
// We don't want the listbox processing this if we are dragging.
if (bDragging)
return 0L;
break;
default:
break;
}
return CallWindowProc(lpfnDefProc, hLB, uMsg, wParam, lParam);
}
/******************************Public*Routine******************************\
* MakeMultiDragList
*
*
*
* History:
* dd-mm-93 - StephenE - Created
*
\**************************************************************************/
BOOL WINAPI
MakeMultiDragList(
HWND hLB
)
{
PDRAGPROP pDragProp;
if (!uDragListMsg) {
return FALSE;
}
//
// Check that we have not already subclassed this window.
//
if (GetProp(hLB, szDragProp)) {
return TRUE;
}
pDragProp = (PDRAGPROP)LocalAlloc(LPTR, sizeof(DRAGPROP));
if (pDragProp == NULL ) {
return FALSE;
}
SetProp(hLB, szDragProp, (HANDLE)pDragProp);
pDragProp->lpfnDefProc = SubclassWindow( hLB, DragListSubclassProc );
return TRUE;
}
/******************************Public*Routine******************************\
* LBMultiItemFromPt
*
*
*
* History:
* dd-mm-93 - StephenE - Created
*
\**************************************************************************/
int WINAPI
LBMultiItemFromPt(
HWND hLB,
POINT pt,
BOOL bAutoScroll
)
{
static LONG dwLastScroll = 0;
RECT rc;
DWORD dwNow;
int nItem;
WORD wScrollDelay, wActualDelay;
ScreenToClient(hLB, &pt);
GetClientRect(hLB, &rc);
nItem = ListBox_GetTopIndex( hLB );
//
// Is the point in the LB client area?
//
if ( PtInRect(&rc, pt) ) {
//
// Check each visible item in turn.
//
for ( ; ; ++nItem) {
if ( LB_ERR == ListBox_GetItemRect( hLB, nItem, &rc) ) {
break;
}
if ( PtInRect(&rc, pt) ) {
return nItem;
}
}
}
else {
//
// If we want autoscroll and the point is directly above or below the
// LB, determine the direction and if it is time to scroll yet.
//
if ( bAutoScroll && (UINT)pt.x < (UINT)rc.right ) {
if (pt.y <= 0) {
--nItem;
}
else {
++nItem;
pt.y = rc.bottom - pt.y;
}
wScrollDelay = (WORD)(1000 / (INITLINESPERSECOND - pt.y/VERTCHANGENUMLINES));
dwNow = GetTickCount();
wActualDelay = (WORD)(dwNow - dwLastScroll);
if (wActualDelay > wScrollDelay) {
//
// This will the actual number of scrolls per second to be
// much closer to the required number.
//
if (wActualDelay > wScrollDelay * 2)
dwLastScroll = dwNow;
else
dwLastScroll += wScrollDelay;
ListBox_SetTopIndex( hLB, nItem );
}
}
}
return -1;
}
/******************************Public*Routine******************************\
* DrawInsert
*
*
*
* History:
* dd-mm-93 - StephenE - Created
*
\**************************************************************************/
VOID WINAPI
DrawMultiInsert(
HWND hwndParent,
HWND hLB,
int nItem
)
{
static POINT ptLastInsert;
static INT nLastInsert = -1;
RECT rc;
//
// Erase the old mark if necessary
//
if ( nLastInsert >= 0 && nItem != nLastInsert ) {
rc.left = ptLastInsert.x;
rc.top = ptLastInsert.y;
rc.right = rc.left + DX_INSERT;
rc.bottom = rc.top + DY_INSERT;
//
// Need to update immediately in case the insert rects overlap.
//
InvalidateRect( hwndParent, &rc, TRUE );
UpdateWindow( hwndParent );
nLastInsert = -1;
}
//
// Draw a new mark if necessary
//
if ( nItem != nLastInsert && nItem >= 0 ) {
static HICON hInsert = NULL;
if ( !hInsert ) {
hInsert = LoadIcon( g_hInst, MAKEINTRESOURCE(IDR_INSERT));
}
if ( hInsert ) {
HDC hDC;
int iItemHeight;
GetWindowRect( hLB, &rc );
ScreenToClient( hLB, (LPPOINT)&rc );
ptLastInsert.x = rc.left - DX_INSERT;
iItemHeight = ListBox_GetItemHeight( hLB, nItem );
nLastInsert = nItem;
nItem -= ListBox_GetTopIndex( hLB );
ptLastInsert.y = (nItem * iItemHeight) - DY_INSERT / 2;
ClientToScreen(hLB, &ptLastInsert);
ScreenToClient(hwndParent, &ptLastInsert);
hDC = GetDC(hwndParent);
DrawIcon(hDC, ptLastInsert.x, ptLastInsert.y, hInsert);
ReleaseDC(hwndParent, hDC);
}
}
}