Leaked source code of windows server 2003
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.
 
 
 
 
 
 

434 lines
13 KiB

/****************************** Module Header ******************************\
* Module Name: dragdrop.c
*
* Copyright (c) 1985 - 1999, Microsoft Corporation
*
* Stuff for object-oriented direct manipulation, designed first for the shell.
*
* History:
* 08-06-91 darrinm Ported from Win 3.1.
\***************************************************************************/
#include "precomp.h"
#pragma hdrstop
PCURSOR xxxQueryDropObject(PWND pwnd, LPDROPSTRUCT lpds);
/***************************************************************************\
* DragObject (API)
*
* Contains the main dragging loop.
*
* History:
* 08-06-91 darrinm Ported from Win 3.1 sources.
\***************************************************************************/
DWORD xxxDragObject(
PWND pwndParent,
PWND pwndFrom, // NULL is valid
UINT wFmt,
ULONG_PTR dwData,
PCURSOR pcur)
{
MSG msg, msgKey;
DWORD result = 0;
BOOL fDrag = TRUE;
LPDROPSTRUCT lpds;
PWND pwndDragging = NULL;
PWND pwndTop;
PCURSOR pcurOld, pcurT;
PWND pwndT;
TL tlpwndT;
TL tlpwndTop;
TL tlpwndDragging;
TL tlPool;
PTHREADINFO pti = PtiCurrent();
CheckLock(pwndParent);
CheckLock(pwndFrom);
CheckLock(pcur);
UserAssert(IsWinEventNotifyDeferredOK());
lpds = (LPDROPSTRUCT)UserAllocPoolWithQuota(2 * sizeof(DROPSTRUCT), TAG_DRAGDROP);
if (lpds == NULL)
return 0;
ThreadLockPool(pti, lpds, &tlPool);
lpds->hwndSource = HW(pwndFrom);
lpds->wFmt = wFmt;
lpds->dwData = dwData;
if (pcur != NULL) {
/*
* No need to DeferWinEventNotify() - pwndFrom is locked
*/
pcurOld = zzzSetCursor(pcur);
} else {
pcurOld = pti->pq->spcurCurrent;
}
if (pwndFrom) {
for (pwndTop = pwndFrom; TestwndChild(pwndTop);
pwndTop = pwndTop->spwndParent) ;
ThreadLockWithPti(pti, pwndTop, &tlpwndTop);
xxxUpdateWindow(pwndTop);
ThreadUnlock(&tlpwndTop);
}
xxxWindowEvent(EVENT_SYSTEM_DRAGDROPSTART, pwndFrom, OBJID_WINDOW, INDEXID_CONTAINER, 0);
xxxSetCapture(pwndFrom);
zzzShowCursor(TRUE);
ThreadLockWithPti(pti, pwndDragging, &tlpwndDragging);
while (fDrag && pti->pq->spwndCapture == pwndFrom) {
while (!(xxxPeekMessage(&msg, NULL, WM_MOUSEFIRST, WM_MOUSELAST, PM_REMOVE) ||
xxxPeekMessage(&msg, NULL, WM_QUEUESYNC, WM_QUEUESYNC, PM_REMOVE) ||
xxxPeekMessage(&msg, NULL, WM_KEYFIRST, WM_KEYLAST, PM_REMOVE))) {
if (!xxxSleepThread(QS_MOUSE | QS_KEY, 0, TRUE)) {
ThreadUnlock(&tlpwndDragging);
ThreadUnlockAndFreePool(pti, &tlPool);
return 0;
}
}
/*
* Be sure to eliminate any extra keydown messages that are
* being queued up by MOUSE message processing.
*/
while (xxxPeekMessage(&msgKey, NULL, WM_KEYFIRST, WM_KEYLAST, PM_REMOVE))
;
if ( (pti->pq->spwndCapture != pwndFrom) ||
(msg.message == WM_KEYDOWN && msg.wParam == VK_ESCAPE) )
{
if (pcurT = SYSCUR(NO))
zzzSetCursor(pcurT);
break;
}
RtlCopyMemory(lpds + 1, lpds, sizeof(DROPSTRUCT));
/*
* in screen coordinates
*/
lpds->ptDrop = msg.pt;
pcurT = xxxQueryDropObject(pwndParent, lpds);
/*
* Returning FALSE to a WM_QUERYDROPOBJECT message means drops
* aren't supported and the 'illegal drop target' cursor should be
* displayed. Returning TRUE means the target is valid and the
* regular drag cursor should be displayed. Also, through a bit
* of polymorphic magic one can return a cursor handle to override
* the normal drag cursor.
*/
if (pcurT == (PCURSOR)FALSE) {
pcurT = SYSCUR(NO);
lpds->hwndSink = NULL;
} else if (pcurT == (PCURSOR)TRUE) {
pcurT = pcur;
}
if (pcurT != NULL)
zzzSetCursor(pcurT);
/*
* send the WM_DRAGLOOP after the above zzzSetCursor() to allow the
* receiver to change the cursor at WM_DRAGLOOP time with a zzzSetCursor()
*/
if (pwndFrom) {
xxxSendMessage(pwndFrom, WM_DRAGLOOP, (pcurT != SYSCUR(NO)),
(LPARAM)lpds);
}
/*
* send these messages internally only
*/
if (pwndDragging != RevalidateHwnd(lpds->hwndSink)) {
if (pwndDragging != NULL) {
xxxSendMessage(pwndDragging, WM_DRAGSELECT, FALSE,
(LPARAM)(lpds + 1));
}
pwndDragging = RevalidateHwnd(lpds->hwndSink);
ThreadUnlock(&tlpwndDragging);
ThreadLockWithPti(pti, pwndDragging, &tlpwndDragging);
if (pwndDragging != NULL) {
xxxSendMessage(pwndDragging, WM_DRAGSELECT, TRUE, (LPARAM)lpds);
}
} else {
if (pwndDragging != NULL) {
xxxSendMessage(pwndDragging, WM_DRAGMOVE, 0, (LPARAM)lpds);
}
}
switch (msg.message) {
case WM_LBUTTONUP:
case WM_NCLBUTTONUP:
fDrag = FALSE;
break;
}
}
ThreadUnlock(&tlpwndDragging);
/*
* If the capture has been lost (i.e. fDrag == TRUE), don't do the drop.
*/
if (fDrag)
pcurT = SYSCUR(NO);
/*
* before the actual drop, clean up the cursor, as the app may do
* stuff here...
*/
xxxReleaseCapture();
zzzShowCursor(FALSE);
zzzSetCursor(pcurOld);
/*
* we either got lbuttonup or enter
*/
if (pcurT != SYSCUR(NO)) {
/*
* object allows drop... send drop message
*/
pwndT = ValidateHwnd(lpds->hwndSink);
if (pwndT != NULL) {
ThreadLockAlwaysWithPti(pti, pwndT, &tlpwndT);
/*
* Allow this guy to activate.
*/
GETPTI(pwndT)->TIF_flags |= TIF_ALLOWFOREGROUNDACTIVATE;
TAGMSG1(DBGTAG_FOREGROUND, "xxxDragObject set TIF %#p", GETPTI(pwndT));
result = (DWORD)xxxSendMessage(pwndT, WM_DROPOBJECT,
(WPARAM)HW(pwndFrom), (LPARAM)lpds);
ThreadUnlock(&tlpwndT);
}
}
xxxWindowEvent(EVENT_SYSTEM_DRAGDROPEND, pwndFrom, OBJID_WINDOW, INDEXID_CONTAINER, 0);
ThreadUnlockAndFreePool(pti, &tlPool);
return result;
}
/***************************************************************************\
* QueryDropObject
*
* Determines where in the window heirarchy the "drop" takes place, and
* sends a message to the deepest child window first. If that window does
* not respond, we go up the heirarchy (recursively, for the moment) until
* we either get a window that does respond or the parent doesn't respond.
*
* History:
* 08-06-91 darrinm Ported from Win 3.1 sources.
\***************************************************************************/
PCURSOR xxxQueryDropObject(
PWND pwnd,
LPDROPSTRUCT lpds)
{
PWND pwndT;
PCURSOR pcurT = NULL;
POINT pt;
BOOL fNC;
TL tlpwndT;
CheckLock(pwnd);
/*
* pt is in screen coordinates
*/
pt = lpds->ptDrop;
/*
* reject points outside this window or if the window is disabled
*/
if (!PtInRect(&pwnd->rcWindow, pt) || TestWF(pwnd, WFDISABLED))
return NULL;
/*
* Check to see if in window region (if it has one)
*/
if (pwnd->hrgnClip != NULL) {
if (!GrePtInRegion(pwnd->hrgnClip, pt.x, pt.y))
return NULL;
}
/*
* are we dropping in the nonclient area of the window or on an iconic
* window?
*/
if (fNC = (TestWF(pwnd, WFMINIMIZED) || !PtInRect(&pwnd->rcClient, pt))) {
goto SendQueryDrop;
}
/*
* dropping in client area
*/
_ScreenToClient(pwnd, &pt);
pwndT = _ChildWindowFromPointEx(pwnd, pt, CWP_SKIPDISABLED | CWP_SKIPINVISIBLE);
_ClientToScreen(pwnd, &pt);
pcurT = NULL;
if (pwndT && pwndT != pwnd) {
ThreadLock(pwndT, &tlpwndT);
pcurT = xxxQueryDropObject(pwndT, lpds);
ThreadUnlock(&tlpwndT);
}
if (pcurT == NULL) {
/*
* there are no children who are in the right place or who want
* drops... convert the point into client coordinates of the
* current window. Because of the recursion, this is already
* done if a child window grabbed the drop.
*/
SendQueryDrop:
_ScreenToClient(pwnd, &lpds->ptDrop);
lpds->hwndSink = HWq(pwnd);
/*
* To avoid hanging dropper (sender) app we do a SendMessageTimeout to
* the droppee (receiver)
*/
if ((PCURSOR)xxxSendMessageTimeout(pwnd, WM_QUERYDROPOBJECT, fNC,
(LPARAM)lpds, SMTO_ABORTIFHUNG, 3*1000, (PLONG_PTR)&pcurT) == FALSE)
pcurT = (PCURSOR)FALSE;
if (pcurT != (PCURSOR)FALSE && pcurT != (PCURSOR)TRUE)
pcurT = HMValidateHandle((HCURSOR)pcurT, TYPE_CURSOR);
/*
* restore drop point to screen coordinates if this window won't
* take drops
*/
if (pcurT == NULL)
lpds->ptDrop = pt;
}
return pcurT;
}
/***************************************************************************\
* xxxDragDetect (API)
*
*
*
* History:
* 08-06-91 darrinm Ported from Win 3.1 sources.
\***************************************************************************/
BOOL xxxDragDetect(
PWND pwnd,
POINT pt)
{
return xxxIsDragging(pwnd, pt, WM_LBUTTONUP);
}
/***************************************************************************\
* xxxIsDragging
*
*
*
* History:
* 05-17-94 johnl Ported from Chicago sources
\***************************************************************************/
BOOL xxxIsDragging(PWND pwnd, POINT ptScreen, UINT uMsg)
{
RECT rc;
MSG msg;
BOOL fDragging;
BOOL fCheck;
TL tlpwndDragging;
PTHREADINFO pti = PtiCurrent();
/*
* Check synchronous mouse state, and punt if the mouse isn't down
* according to the queue.
*/
if (!(_GetKeyState((uMsg == WM_LBUTTONUP ? VK_LBUTTON : VK_RBUTTON)) & 0x8000))
return FALSE;
xxxSetCapture(pwnd);
*(LPPOINT)&rc.left = ptScreen;
*(LPPOINT)&rc.right = ptScreen;
InflateRect(&rc, SYSMET(CXDRAG), SYSMET(CYDRAG));
fDragging = FALSE;
fCheck = TRUE;
ThreadLockWithPti(pti, pwnd, &tlpwndDragging);
while (fCheck) {
while ( !(
xxxPeekMessage(&msg, NULL, WM_MOUSEFIRST, WM_MOUSELAST,PM_REMOVE) ||
xxxPeekMessage(&msg, NULL, WM_QUEUESYNC, WM_QUEUESYNC,PM_REMOVE) ||
xxxPeekMessage(&msg, NULL, WM_KEYFIRST, WM_KEYLAST,PM_REMOVE)
)
&& (pti->pq->spwndCapture == pwnd)) {
/*
* If there is no input for half a second (500ms) consider that
* we are dragging. If we don't specify a timeout value, the
* thread may sleep here forever and wouldn't repaint, etc.
*/
if (!xxxSleepThread(QS_MOUSE | QS_KEY, 500, TRUE)) {
fDragging = TRUE;
goto Cleanup;
}
}
/*
* Cancel if the button was released or we no longer have the capture.
*/
if ( pti->pq->spwndCapture != pwnd || msg.message == uMsg) {
fCheck = FALSE;
} else {
switch (msg.message) {
case WM_MOUSEMOVE:
if (!PtInRect(&rc, msg.pt)) {
fDragging = TRUE;
fCheck = FALSE;
}
break;
case WM_QUEUESYNC:
/*
* CBT Hook needs to know
*/
xxxCallHook(HCBT_QS, 0, 0, WH_CBT);
break;
case WM_KEYDOWN:
/*
* <Esc> cancels drag detection
*/
if (msg.wParam == VK_ESCAPE)
fCheck = FALSE;
break;
}
}
}
Cleanup:
if (pti->pq->spwndCapture == pwnd)
xxxReleaseCapture();
ThreadUnlock(&tlpwndDragging);
return fDragging ;
}