test: dragdrop.c Source File

Main Page | Class Hierarchy | Class List | File List | Class Members | File Members

# dragdrop.c

Go to the documentation of this file.

    00001 /****************************** Module Header ******************************\
    00002 * Module Name: dragdrop.c
    00003 *
    00004 * Copyright (c) 1985 - 1999, Microsoft Corporation
    00005 *
    00006 * Stuff for object-oriented direct manipulation, designed first for the shell.
    00007 *
    00008 * History:
    00009 * 08-06-91 darrinm    Ported from Win 3.1.
    00010 \***************************************************************************/
    00011
    00012 #include "precomp.h"
    00013 #pragma hdrstop
    00014
    00015 PCURSOR xxxQueryDropObject(PWND pwnd, LPDROPSTRUCT lpds);
    00016
    00017 /***************************************************************************\
    00018 * DragObject (API)
    00019 *
    00020 * Contains the main dragging loop.
    00021 *
    00022 * History:
    00023 * 08-06-91 darrinm      Ported from Win 3.1 sources.
    00024 \***************************************************************************/
    00025
    00026 DWORD xxxDragObject(
    00027     PWND pwndParent,
    00028     PWND pwndFrom,          // NULL is valid
    00029     UINT wFmt,
    00030     ULONG_PTR dwData,
    00031     PCURSOR pcur)
    00032 {
    00033     MSG msg, msgKey;
    00034     DWORD result = 0;
    00035     BOOL fDrag = TRUE;
    00036     LPDROPSTRUCT lpds;
    00037     PWND pwndDragging = NULL;
    00038     PWND pwndTop;
    00039     PCURSOR pcurOld, pcurT;
    00040     PWND pwndT;
    00041     TL tlpwndT;
    00042     TL tlpwndTop;
    00043     TL tlpwndDragging;
    00044     TL tlPool;
    00045     PTHREADINFO pti = PtiCurrent();
    00046
    00047     CheckLock(pwndParent);
    00048     CheckLock(pwndFrom);
    00049     CheckLock(pcur);
    00050     UserAssert(IsWinEventNotifyDeferredOK());
    00051
    00052     lpds = (LPDROPSTRUCT)UserAllocPoolWithQuota(2 * sizeof(DROPSTRUCT), TAG_DRAGDROP);
    00053     if (lpds == NULL)
    00054         return 0;
    00055
    00056     ThreadLockPool(pti, lpds, &tlPool);
    00057     lpds->hwndSource = HW(pwndFrom);
    00058     lpds->wFmt = wFmt;
    00059     lpds->dwData = dwData;
    00060
    00061     if (pcur != NULL) {
    00062         /*
    00063          * No need to DeferWinEventNotify() - pwndFrom is locked
    00064          */
    00065         pcurOld = zzzSetCursor(pcur);
    00066     } else {
    00067         pcurOld = pti->pq->spcurCurrent;
    00068     }
    00069
    00070     if (pwndFrom) {
    00071         for (pwndTop = pwndFrom; TestwndChild(pwndTop);
    00072                 pwndTop = pwndTop->spwndParent) ;
    00073
    00074         ThreadLockWithPti(pti, pwndTop, &tlpwndTop);
    00075         xxxUpdateWindow(pwndTop);
    00076         ThreadUnlock(&tlpwndTop);
    00077     }
    00078
    00079     if (FWINABLE()) {
    00080         xxxWindowEvent(EVENT_SYSTEM_DRAGDROPSTART, pwndFrom, OBJID_WINDOW, INDEXID_CONTAINER, 0);
    00081     }
    00082
    00083     xxxSetCapture(pwndFrom);
    00084     zzzShowCursor(TRUE);
    00085
    00086     ThreadLockWithPti(pti, pwndDragging, &tlpwndDragging);
    00087
    00088     while (fDrag && pti->pq->spwndCapture == pwndFrom) {
    00089         while (!(xxxPeekMessage(&msg, NULL, WM_MOUSEFIRST, WM_MOUSELAST, PM_REMOVE) ||
    00090                  xxxPeekMessage(&msg, NULL, WM_QUEUESYNC, WM_QUEUESYNC, PM_REMOVE) ||
    00091                  xxxPeekMessage(&msg, NULL, WM_KEYFIRST, WM_KEYLAST, PM_REMOVE))) {
    00092             if (!xxxSleepThread(QS_MOUSE | QS_KEY, 0, TRUE)) {
    00093                 ThreadUnlock(&tlpwndDragging);
    00094                 ThreadUnlockAndFreePool(pti, &tlPool);
    00095                 return 0;
    00096             }
    00097         }
    00098
    00099         /*
    00100          * Be sure to eliminate any extra keydown messages that are
    00101          * being queued up by MOUSE message processing.
    00102          */
    00103
    00104         while (xxxPeekMessage(&msgKey, NULL, WM_KEYFIRST, WM_KEYLAST, PM_REMOVE))
    00105            ;
    00106
    00107         if  ( (pti->pq->spwndCapture != pwndFrom) ||
    00108               (msg.message == WM_KEYDOWN && msg.wParam == VK_ESCAPE) )
    00109         {
    00110             if (pcurT = SYSCUR(NO))
    00111                 zzzSetCursor(pcurT);
    00112             break;
    00113         }
    00114
    00115         RtlCopyMemory(lpds + 1, lpds, sizeof(DROPSTRUCT));
    00116
    00117         /*
    00118          * in screen coordinates
    00119          */
    00120         lpds->ptDrop = msg.pt;
    00121
    00122         pcurT = xxxQueryDropObject(pwndParent, lpds);
    00123
    00124         /*
    00125          * Returning FALSE to a WM_QUERYDROPOBJECT message means drops
    00126          * aren't supported and the 'illegal drop target' cursor should be
    00127          * displayed.  Returning TRUE means the target is valid and the
    00128          * regular drag cursor should be displayed.  Also, through a bit
    00129          * of polymorphic magic one can return a cursor handle to override
    00130          * the normal drag cursor.
    00131          */
    00132         if (pcurT == (PCURSOR)FALSE) {
    00133             pcurT = SYSCUR(NO);
    00134             lpds->hwndSink = NULL;
    00135         } else if (pcurT == (PCURSOR)TRUE) {
    00136             pcurT = pcur;
    00137         }
    00138
    00139         if (pcurT != NULL)
    00140             zzzSetCursor(pcurT);
    00141
    00142         /*
    00143          * send the WM_DRAGLOOP after the above zzzSetCursor() to allow the
    00144          * receiver to change the cursor at WM_DRAGLOOP time with a zzzSetCursor()
    00145          */
    00146         if (pwndFrom) {
    00147             xxxSendMessage(pwndFrom, WM_DRAGLOOP, (pcurT != SYSCUR(NO)),
    00148                     (LPARAM)lpds);
    00149         }
    00150
    00151         /*
    00152          * send these messages internally only
    00153          */
    00154         if (pwndDragging != RevalidateHwnd(lpds->hwndSink)) {
    00155             if (pwndDragging != NULL) {
    00156                 xxxSendMessage(pwndDragging, WM_DRAGSELECT, FALSE,
    00157                         (LPARAM)(lpds + 1));
    00158             }
    00159             pwndDragging = RevalidateHwnd(lpds->hwndSink);
    00160             ThreadUnlock(&tlpwndDragging);
    00161             ThreadLockWithPti(pti, pwndDragging, &tlpwndDragging);
    00162
    00163             if (pwndDragging != NULL) {
    00164                 xxxSendMessage(pwndDragging, WM_DRAGSELECT, TRUE, (LPARAM)lpds);
    00165             }
    00166         } else {
    00167             if (pwndDragging != NULL) {
    00168                 xxxSendMessage(pwndDragging, WM_DRAGMOVE, 0, (LPARAM)lpds);
    00169             }
    00170         }
    00171
    00172         switch (msg.message) {
    00173         case WM_LBUTTONUP:
    00174         case WM_NCLBUTTONUP:
    00175             fDrag = FALSE;
    00176             break;
    00177         }
    00178     }
    00179
    00180     ThreadUnlock(&tlpwndDragging);
    00181
    00182     /*
    00183      * If the capture has been lost (i.e. fDrag == TRUE), don't do the drop.
    00184      */
    00185     if (fDrag)
    00186         pcurT = SYSCUR(NO);
    00187
    00188     /*
    00189      * before the actual drop, clean up the cursor, as the app may do
    00190      * stuff here...
    00191      */
    00192     xxxReleaseCapture();
    00193     zzzShowCursor(FALSE);
    00194
    00195     zzzSetCursor(pcurOld);
    00196
    00197     /*
    00198      * we either got lbuttonup or enter
    00199      */
    00200     if (pcurT != SYSCUR(NO)) {
    00201
    00202         /*
    00203          * object allows drop...  send drop message
    00204          */
    00205         pwndT = ValidateHwnd(lpds->hwndSink);
    00206         if (pwndT != NULL) {
    00207
    00208             ThreadLockAlwaysWithPti(pti, pwndT, &tlpwndT);
    00209
    00210             /*
    00211              * Allow this guy to activate.
    00212              */
    00213             GETPTI(pwndT)->TIF_flags |= TIF_ALLOWFOREGROUNDACTIVATE;
    00214             TAGMSG1(DBGTAG_FOREGROUND, "xxxDragObject set TIF %#p", GETPTI(pwndT));
    00215             result = (DWORD)xxxSendMessage(pwndT, WM_DROPOBJECT,
    00216                     (WPARAM)HW(pwndFrom), (LPARAM)lpds);
    00217
    00218             ThreadUnlock(&tlpwndT);
    00219         }
    00220     }
    00221
    00222     if (FWINABLE()) {
    00223         xxxWindowEvent(EVENT_SYSTEM_DRAGDROPEND, pwndFrom, OBJID_WINDOW, INDEXID_CONTAINER, 0);
    00224     }
    00225
    00226     ThreadUnlockAndFreePool(pti, &tlPool);
    00227     return result;
    00228 }
    00229
    00230
    00231 /***************************************************************************\
    00232 * QueryDropObject
    00233 *
    00234 * Determines where in the window heirarchy the "drop" takes place, and
    00235 * sends a message to the deepest child window first.  If that window does
    00236 * not respond, we go up the heirarchy (recursively, for the moment) until
    00237 * we either get a window that does respond or the parent doesn't respond.
    00238 *
    00239 * History:
    00240 * 08-06-91 darrinm      Ported from Win 3.1 sources.
    00241 \***************************************************************************/
    00242
    00243 PCURSOR xxxQueryDropObject(
    00244     PWND pwnd,
    00245     LPDROPSTRUCT lpds)
    00246 {
    00247     PWND pwndT;
    00248     PCURSOR pcurT = NULL;
    00249     POINT pt;
    00250     BOOL fNC;
    00251     TL tlpwndT;
    00252     CheckLock(pwnd);
    00253
    00254     /*
    00255      *  pt is in screen coordinates
    00256      */
    00257     pt = lpds->ptDrop;
    00258
    00259     /*
    00260      * reject points outside this window or if the window is disabled
    00261      */
    00262     if (!PtInRect(&pwnd->rcWindow, pt) || TestWF(pwnd, WFDISABLED))
    00263         return NULL;
    00264
    00265     /*
    00266      * Check to see if in window region (if it has one)
    00267      */
    00268     if (pwnd->hrgnClip != NULL) {
    00269         if (!GrePtInRegion(pwnd->hrgnClip, pt.x, pt.y))
    00270             return NULL;
    00271     }
    00272
    00273     /*
    00274      * are we dropping in the nonclient area of the window or on an iconic
    00275      * window?
    00276      */
    00277     if (fNC = (TestWF(pwnd, WFMINIMIZED) || !PtInRect(&pwnd->rcClient, pt))) {
    00278         goto SendQueryDrop;
    00279     }
    00280
    00281     /*
    00282      * dropping in client area
    00283      */
    00284     _ScreenToClient(pwnd, &pt);
    00285     pwndT = _ChildWindowFromPointEx(pwnd, pt, CWP_SKIPDISABLED | CWP_SKIPINVISIBLE);
    00286     _ClientToScreen(pwnd, &pt);
    00287
    00288     pcurT = NULL;
    00289     if (pwndT && pwndT != pwnd) {
    00290         ThreadLock(pwndT, &tlpwndT);
    00291         pcurT = xxxQueryDropObject(pwndT, lpds);
    00292         ThreadUnlock(&tlpwndT);
    00293     }
    00294
    00295     if (pcurT == NULL) {
    00296
    00297         /*
    00298          * there are no children who are in the right place or who want
    00299          * drops...  convert the point into client coordinates of the
    00300          * current window.  Because of the recursion, this is already
    00301          * done if a child window grabbed the drop.
    00302          */
    00303 SendQueryDrop:
    00304         _ScreenToClient(pwnd, &lpds->ptDrop);
    00305         lpds->hwndSink = HWq(pwnd);
    00306
    00307         /*
    00308          * To avoid hanging dropper (sender) app we do a SendMessageTimeout to
    00309          * the droppee (receiver)
    00310          */
    00311         if ((PCURSOR)xxxSendMessageTimeout(pwnd, WM_QUERYDROPOBJECT, fNC,
    00312                 (LPARAM)lpds, SMTO_ABORTIFHUNG, 3*1000, (PLONG_PTR)&pcurT) == FALSE)
    00313             pcurT = (PCURSOR)FALSE;
    00314
    00315         if (pcurT != (PCURSOR)FALSE && pcurT != (PCURSOR)TRUE)
    00316             pcurT = HMValidateHandle((HCURSOR)pcurT, TYPE_CURSOR);
    00317
    00318         /*
    00319          * restore drop point to screen coordinates if this window won't
    00320          * take drops
    00321          */
    00322         if (pcurT == NULL)
    00323             lpds->ptDrop = pt;
    00324     }
    00325     return pcurT;
    00326 }
    00327
    00328
    00329 /***************************************************************************\
    00330 * xxxDragDetect (API)
    00331 *
    00332 *
    00333 *
    00334 * History:
    00335 * 08-06-91 darrinm      Ported from Win 3.1 sources.
    00336 \***************************************************************************/
    00337
    00338 BOOL xxxDragDetect(
    00339     PWND pwnd,
    00340     POINT pt)
    00341 {
    00342     return xxxIsDragging(pwnd, pt, WM_LBUTTONUP);
    00343 }
    00344
    00345 /***************************************************************************\
    00346 * xxxIsDragging
    00347 *
    00348 *
    00349 *
    00350 * History:
    00351 * 05-17-94 johnl        Ported from Chicago sources
    00352 \***************************************************************************/
    00353
    00354 BOOL xxxIsDragging(PWND pwnd, POINT ptScreen, UINT uMsg)
    00355 {
    00356     RECT rc;
    00357     MSG  msg;
    00358     BOOL fDragging;
    00359     BOOL fCheck;
    00360     TL   tlpwndDragging;
    00361     PTHREADINFO pti = PtiCurrent();
    00362
    00363     /*
    00364      * Check synchronous mouse state, and punt if the mouse isn't down
    00365      * according to the queue.
    00366      */
    00367     if (!(_GetKeyState((uMsg == WM_LBUTTONUP ? VK_LBUTTON : VK_RBUTTON)) & 0x8000))
    00368         return FALSE;
    00369
    00370     xxxSetCapture(pwnd);
    00371
    00372     *(LPPOINT)&rc.left = ptScreen;
    00373     *(LPPOINT)&rc.right = ptScreen;
    00374     InflateRect(&rc, SYSMET(CXDRAG), SYSMET(CYDRAG));
    00375
    00376     fDragging = FALSE;
    00377     fCheck    = TRUE;
    00378
    00379     ThreadLockWithPti(pti, pwnd, &tlpwndDragging);
    00380     while (fCheck) {
    00381         while ( !(
    00382                   xxxPeekMessage(&msg, NULL, WM_MOUSEFIRST, WM_MOUSELAST,PM_REMOVE) ||
    00383                   xxxPeekMessage(&msg, NULL, WM_QUEUESYNC, WM_QUEUESYNC,PM_REMOVE) ||
    00384                   xxxPeekMessage(&msg, NULL, WM_KEYFIRST, WM_KEYLAST,PM_REMOVE)
    00385                  )
    00386                && (pti->pq->spwndCapture == pwnd)) {
    00387             /*
    00388              * If there is no input for half a second (500ms) consider that
    00389              * we are dragging. If we don't specify a timeout value, the
    00390              * thread may sleep here forever and wouldn't repaint, etc.
    00391              */
    00392             if (!xxxSleepThread(QS_MOUSE | QS_KEY, 500, TRUE)) {
    00393                 fDragging = TRUE;
    00394                 goto Cleanup;
    00395             }
    00396         }
    00397
    00398         /*
    00399          * Cancel if the button was released or we no longer have the capture.
    00400          */
    00401         if ( pti->pq->spwndCapture != pwnd || msg.message == uMsg) {
    00402             fCheck = FALSE;
    00403         } else {
    00404             switch (msg.message) {
    00405
    00406             case WM_MOUSEMOVE:
    00407                 if (!PtInRect(&rc, msg.pt)) {
    00408                     fDragging = TRUE;
    00409                     fCheck    = FALSE;
    00410                 }
    00411                 break;
    00412
    00413             case WM_QUEUESYNC:
    00414                 /*
    00415                  * CBT Hook needs to know
    00416                  */
    00417                 xxxCallHook(HCBT_QS, 0, 0, WH_CBT);
    00418                 break;
    00419
    00420             case WM_KEYDOWN:
    00421                 /*
    00422                  * <Esc> cancels drag detection
    00423                  */
    00424                 if (msg.wParam == VK_ESCAPE)
    00425                     fCheck = FALSE;
    00426                 break;
    00427
    00428             }
    00429         }
    00430     }
    00431
    00432 Cleanup:
    00433     if (pti->pq->spwndCapture == pwnd)
    00434         xxxReleaseCapture();
    00435
    00436     ThreadUnlock(&tlpwndDragging);
    00437     return fDragging ;
    00438 }