source: trunk/src/ole32/old/oleDrag.cpp@ 6496

Last change on this file since 6496 was 5601, checked in by sandervl, 24 years ago

created

File size: 20.5 KB
Line 
1/* $Id: oleDrag.cpp,v 1.1 2001-04-26 19:26:14 sandervl Exp $ */
2/*
3 *
4 * Project Odin Software License can be found in LICENSE.TXT
5 *
6 */
7/*
8 * OLE Drag Drop functions.
9 *
10 * 5/9/99
11 *
12 * Copyright 1999 David J. Raison
13 *
14 * Some portions from Wine Implementation (2/9/99)
15 * Copyright 1995 Martin von Loewis
16 * Copyright 1999 Francis Beaudet
17 * Copyright 1999 Noel Borthwick
18 */
19
20#include "ole32.h"
21#include "commctrl.h"
22#include "oString.h"
23#include <assert.h>
24
25// ======================================================================
26// Local Data
27// ======================================================================
28
29typedef struct tagDropTargetNode
30{
31 HWND hwndTarget;
32 IDropTarget * dropTarget;
33 struct tagDropTargetNode * prevDropTarget;
34 struct tagDropTargetNode * nextDropTarget;
35} DropTargetNode;
36
37typedef struct tagTrackerWindowInfo
38{
39 IDataObject * dataObject;
40 IDropSource * dropSource;
41 DWORD dwOKEffect;
42 DWORD* pdwEffect;
43 BOOL trackingDone;
44 HRESULT returnValue;
45
46 BOOL escPressed;
47 HWND curDragTargetHWND;
48 IDropTarget * curDragTarget;
49} TrackerWindowInfo;
50
51/*
52 * Name of our registered window class.
53 */
54static const char OLEDD_DRAGTRACKERCLASS[] = "WineDragDropTracker32";
55
56/*
57 * This is the head of the Drop target container.
58 */
59static DropTargetNode * targetListHead = NULL;
60
61// ======================================================================
62// Prototypes.
63// ======================================================================
64
65extern void OLEDD_Initialize();
66extern void OLEDD_UnInitialize();
67static void OLEDD_InsertDropTarget(
68 DropTargetNode* nodeToAdd);
69static DropTargetNode* OLEDD_ExtractDropTarget(
70 HWND hwndOfTarget);
71static DropTargetNode* OLEDD_FindDropTarget(
72 HWND hwndOfTarget);
73static LRESULT WIN32API OLEDD_DragTrackerWindowProc(
74 HWND hwnd,
75 UINT uMsg,
76 WPARAM wParam,
77 LPARAM lParam);
78static void OLEDD_TrackMouseMove(
79 TrackerWindowInfo* trackerInfo,
80 POINT mousePos,
81 DWORD keyState);
82static void OLEDD_TrackStateChange(
83 TrackerWindowInfo* trackerInfo,
84 POINT mousePos,
85 DWORD keyState);
86static DWORD OLEDD_GetButtonState();
87
88// ======================================================================
89// Public API's
90// ======================================================================
91
92// ----------------------------------------------------------------------
93// RegisterDragDrop()
94// ----------------------------------------------------------------------
95HRESULT WIN32API RegisterDragDrop
96 (HWND hwnd,
97 LPDROPTARGET pDropTarget)
98{
99 dprintf(("OLE32: RegisterDragDrop"));
100
101 DropTargetNode * dropTargetInfo;
102
103 // First, check if the window is already registered.
104 dropTargetInfo = OLEDD_FindDropTarget(hwnd);
105 if (dropTargetInfo != NULL)
106 return DRAGDROP_E_ALREADYREGISTERED;
107
108 // If it's not there, we can add it. We first create a node for it.
109 dropTargetInfo = (DropTargetNode *)HeapAlloc(GetProcessHeap(), 0, sizeof(DropTargetNode));
110
111 if (dropTargetInfo == NULL)
112 return E_OUTOFMEMORY;
113
114 dropTargetInfo->hwndTarget = hwnd;
115 dropTargetInfo->prevDropTarget = NULL;
116 dropTargetInfo->nextDropTarget = NULL;
117
118 // Don't forget that this is an interface pointer, need to nail it down since
119 // we keep a copy of it.
120 dropTargetInfo->dropTarget = pDropTarget;
121 IDropTarget_AddRef(dropTargetInfo->dropTarget);
122
123 OLEDD_InsertDropTarget(dropTargetInfo);
124
125 return S_OK;
126}
127
128// ----------------------------------------------------------------------
129// RevokeDragDrop()
130// ----------------------------------------------------------------------
131HRESULT WIN32API RevokeDragDrop
132 (HWND hwnd)
133{
134 dprintf(("OLE32: RevokeDragDrop"));
135
136 DropTargetNode * dropTargetInfo;
137
138 // First, check if the window is already registered.
139 dropTargetInfo = OLEDD_ExtractDropTarget(hwnd);
140 if (dropTargetInfo == NULL)
141 return DRAGDROP_E_NOTREGISTERED;
142
143 // If it's in there, clean-up it's used memory and references
144 IDropTarget_Release(dropTargetInfo->dropTarget);
145 HeapFree(GetProcessHeap(), 0, dropTargetInfo);
146
147 return S_OK;
148}
149
150// ----------------------------------------------------------------------
151// DoDragDrop()
152// ----------------------------------------------------------------------
153HRESULT WIN32API DoDragDrop
154 (IDataObject * pDataObject, // ptr to the data obj
155 IDropSource * pDropSource, // ptr to the source obj
156 DWORD dwOKEffect, // effects allowed by the source
157 DWORD * pdwEffect) // ptr to effects of the source
158{
159 dprintf(("OLE32: DoDragDrop"));
160
161 TrackerWindowInfo trackerInfo;
162 HWND hwndTrackWindow;
163 MSG msg;
164
165 // Setup the drag n drop tracking window.
166 trackerInfo.dataObject = pDataObject;
167 trackerInfo.dropSource = pDropSource;
168 trackerInfo.dwOKEffect = dwOKEffect;
169 trackerInfo.pdwEffect = pdwEffect;
170 trackerInfo.trackingDone = FALSE;
171 trackerInfo.escPressed = FALSE;
172 trackerInfo.curDragTargetHWND = 0;
173 trackerInfo.curDragTarget = 0;
174
175 hwndTrackWindow = CreateWindowA(OLEDD_DRAGTRACKERCLASS,
176 "TrackerWindow",
177 WS_POPUP,
178 CW_USEDEFAULT, CW_USEDEFAULT,
179 CW_USEDEFAULT, CW_USEDEFAULT,
180 0,
181 0,
182 0,
183 (LPVOID)&trackerInfo);
184
185 if (hwndTrackWindow != 0)
186 {
187 // Capture the mouse input
188 SetCapture(hwndTrackWindow);
189
190 // Pump messages. All mouse input should go the the capture window.
191 while (!trackerInfo.trackingDone && GetMessageA(&msg, 0, 0, 0) )
192 {
193 if ( (msg.message >= WM_KEYFIRST) && (msg.message <= WM_KEYFIRST) )
194 {
195 // When keyboard messages are sent to windows on this thread, we
196 // want to ignore notify the drop source that the state changed.
197 // in the case of the Escape key, we also notify the drop source
198 // we give it a special meaning.
199 if ( (msg.message == WM_KEYDOWN) && (msg.wParam == VK_ESCAPE) )
200 trackerInfo.escPressed = TRUE;
201
202 // Notify the drop source.
203 OLEDD_TrackStateChange(&trackerInfo, msg.pt, OLEDD_GetButtonState());
204 }
205 else
206 {
207 // Dispatch the messages only when it's not a keyboard message.
208 DispatchMessageA(&msg);
209 }
210 }
211
212 // Destroy the temporary window.
213 DestroyWindow(hwndTrackWindow);
214
215 return trackerInfo.returnValue;
216 }
217
218 return E_FAIL;
219}
220
221// ----------------------------------------------------------------------
222// OLEDD_Initialize()
223// ----------------------------------------------------------------------
224extern void OLEDD_Initialize()
225{
226 dprintf(("OLE32: OLEDD_Initialize"));
227
228 WNDCLASSA wndClass;
229
230 ZeroMemory (&wndClass, sizeof(WNDCLASSA));
231 wndClass.style = CS_GLOBALCLASS;
232 wndClass.lpfnWndProc = (WNDPROC)OLEDD_DragTrackerWindowProc;
233 wndClass.cbClsExtra = 0;
234 wndClass.cbWndExtra = sizeof(TrackerWindowInfo*);
235 wndClass.hCursor = 0;
236 wndClass.hbrBackground = 0;
237 wndClass.lpszClassName = OLEDD_DRAGTRACKERCLASS;
238
239 RegisterClassA (&wndClass);
240}
241
242// ----------------------------------------------------------------------
243// OLEDD_UnInitialize()
244// ----------------------------------------------------------------------
245extern void OLEDD_UnInitialize()
246{
247 dprintf(("OLE32: OLEDD_UnInitialize"));
248
249 // Simply empty the list.
250 while (targetListHead!=NULL)
251 {
252 RevokeDragDrop(targetListHead->hwndTarget);
253 }
254}
255
256// ======================================================================
257// Private functions.
258// ======================================================================
259
260/***
261 * OLEDD_InsertDropTarget()
262 *
263 * Insert the target node in the tree.
264 */
265static void OLEDD_InsertDropTarget(DropTargetNode* nodeToAdd)
266{
267 DropTargetNode* curNode;
268 DropTargetNode** parentNodeLink;
269
270 /*
271 * Iterate the tree to find the insertion point.
272 */
273 curNode = targetListHead;
274 parentNodeLink = &targetListHead;
275
276 while (curNode!=NULL)
277 {
278 if (nodeToAdd->hwndTarget<curNode->hwndTarget)
279 {
280 /*
281 * If the node we want to add has a smaller HWND, go left
282 */
283 parentNodeLink = &curNode->prevDropTarget;
284 curNode = curNode->prevDropTarget;
285 }
286 else if (nodeToAdd->hwndTarget>curNode->hwndTarget)
287 {
288 /*
289 * If the node we want to add has a larger HWND, go right
290 */
291 parentNodeLink = &curNode->nextDropTarget;
292 curNode = curNode->nextDropTarget;
293 }
294 else
295 {
296 /*
297 * The item was found in the list. It shouldn't have been there
298 */
299 assert(FALSE);
300 return;
301 }
302 }
303
304 /*
305 * If we get here, we have found a spot for our item. The parentNodeLink
306 * pointer points to the pointer that we have to modify.
307 * The curNode should be NULL. We just have to establish the link and Voila!
308 */
309 assert(curNode==NULL);
310 assert(parentNodeLink!=NULL);
311 assert(*parentNodeLink==NULL);
312
313 *parentNodeLink=nodeToAdd;
314}
315
316/***
317 * OLEDD_ExtractDropTarget()
318 *
319 * Removes the target node from the tree.
320 */
321static DropTargetNode* OLEDD_ExtractDropTarget(HWND hwndOfTarget)
322{
323 DropTargetNode* curNode;
324 DropTargetNode** parentNodeLink;
325
326 /*
327 * Iterate the tree to find the insertion point.
328 */
329 curNode = targetListHead;
330 parentNodeLink = &targetListHead;
331
332 while (curNode!=NULL)
333 {
334 if (hwndOfTarget<curNode->hwndTarget)
335 {
336 /*
337 * If the node we want to add has a smaller HWND, go left
338 */
339 parentNodeLink = &curNode->prevDropTarget;
340 curNode = curNode->prevDropTarget;
341 }
342 else if (hwndOfTarget>curNode->hwndTarget)
343 {
344 /*
345 * If the node we want to add has a larger HWND, go right
346 */
347 parentNodeLink = &curNode->nextDropTarget;
348 curNode = curNode->nextDropTarget;
349 }
350 else
351 {
352 /*
353 * The item was found in the list. Detach it from it's parent and
354 * re-insert it's kids in the tree.
355 */
356 assert(parentNodeLink!=NULL);
357 assert(*parentNodeLink==curNode);
358
359 /*
360 * We arbitrately re-attach the left sub-tree to the parent.
361 */
362 *parentNodeLink = curNode->prevDropTarget;
363
364 /*
365 * And we re-insert the right subtree
366 */
367 if (curNode->nextDropTarget!=NULL)
368 {
369 OLEDD_InsertDropTarget(curNode->nextDropTarget);
370 }
371
372 /*
373 * The node we found is still a valid node once we complete
374 * the unlinking of the kids.
375 */
376 curNode->nextDropTarget=NULL;
377 curNode->prevDropTarget=NULL;
378
379 return curNode;
380 }
381 }
382
383 /*
384 * If we get here, the node is not in the tree
385 */
386 return NULL;
387}
388
389/***
390 * OLEDD_FindDropTarget()
391 *
392 * Finds information about the drop target.
393 */
394static DropTargetNode* OLEDD_FindDropTarget(HWND hwndOfTarget)
395{
396 DropTargetNode* curNode;
397
398 /*
399 * Iterate the tree to find the HWND value.
400 */
401 curNode = targetListHead;
402
403 while (curNode!=NULL)
404 {
405 if (hwndOfTarget<curNode->hwndTarget)
406 {
407 /*
408 * If the node we want to add has a smaller HWND, go left
409 */
410 curNode = curNode->prevDropTarget;
411 }
412 else if (hwndOfTarget>curNode->hwndTarget)
413 {
414 /*
415 * If the node we want to add has a larger HWND, go right
416 */
417 curNode = curNode->nextDropTarget;
418 }
419 else
420 {
421 /*
422 * The item was found in the list.
423 */
424 return curNode;
425 }
426 }
427
428 /*
429 * If we get here, the item is not in the list
430 */
431 return NULL;
432}
433
434/***
435 * OLEDD_DragTrackerWindowProc()
436 *
437 * This method is the WindowProcedure of the drag n drop tracking
438 * window. During a drag n Drop operation, an invisible window is created
439 * to receive the user input and act upon it. This procedure is in charge
440 * of this behavior.
441 */
442static LRESULT WIN32API OLEDD_DragTrackerWindowProc(
443 HWND hwnd,
444 UINT uMsg,
445 WPARAM wParam,
446 LPARAM lParam)
447{
448 switch (uMsg)
449 {
450 case WM_CREATE:
451 {
452 LPCREATESTRUCTA createStruct = (LPCREATESTRUCTA)lParam;
453
454 SetWindowLongA(hwnd, 0, (LONG)createStruct->lpCreateParams);
455
456
457 break;
458 }
459 case WM_MOUSEMOVE:
460 {
461 TrackerWindowInfo* trackerInfo = (TrackerWindowInfo*)GetWindowLongA(hwnd, 0);
462 POINT mousePos;
463
464 /*
465 * Get the current mouse position in screen coordinates.
466 */
467 mousePos.x = LOWORD(lParam);
468 mousePos.y = HIWORD(lParam);
469 ClientToScreen(hwnd, &mousePos);
470
471 /*
472 * Track the movement of the mouse.
473 */
474 OLEDD_TrackMouseMove(trackerInfo, mousePos, wParam);
475
476 break;
477 }
478 case WM_LBUTTONUP:
479 case WM_MBUTTONUP:
480 case WM_RBUTTONUP:
481 case WM_LBUTTONDOWN:
482 case WM_MBUTTONDOWN:
483 case WM_RBUTTONDOWN:
484 {
485 TrackerWindowInfo* trackerInfo = (TrackerWindowInfo*)GetWindowLongA(hwnd, 0);
486 POINT mousePos;
487
488 /*
489 * Get the current mouse position in screen coordinates.
490 */
491 mousePos.x = LOWORD(lParam);
492 mousePos.y = HIWORD(lParam);
493 ClientToScreen(hwnd, &mousePos);
494
495 /*
496 * Notify everyone that the button state changed
497 * TODO: Check if the "escape" key was pressed.
498 */
499 OLEDD_TrackStateChange(trackerInfo, mousePos, wParam);
500
501 break;
502 }
503 }
504
505 /*
506 * This is a window proc after all. Let's call the default.
507 */
508 return DefWindowProcA (hwnd, uMsg, wParam, lParam);
509}
510
511/***
512 * OLEDD_TrackMouseMove()
513 *
514 * This method is invoked while a drag and drop operation is in effect.
515 * it will generate the appropriate callbacks in the drop source
516 * and drop target. It will also provide the expected feedback to
517 * the user.
518 *
519 * params:
520 * trackerInfo - Pointer to the structure identifying the
521 * drag & drop operation that is currently
522 * active.
523 * mousePos - Current position of the mouse in screen
524 * coordinates.
525 * keyState - Contains the state of the shift keys and the
526 * mouse buttons (MK_LBUTTON and the like)
527 */
528static void OLEDD_TrackMouseMove(
529 TrackerWindowInfo* trackerInfo,
530 POINT mousePos,
531 DWORD keyState)
532{
533 HWND hwndNewTarget = 0;
534 HRESULT hr = S_OK;
535
536 /*
537 * Get the handle of the window under the mouse
538 */
539 hwndNewTarget = WindowFromPoint(mousePos);
540
541 /*
542 * Every time, we re-initialize the effects passed to the
543 * IDropTarget to the effects allowed by the source.
544 */
545 *trackerInfo->pdwEffect = trackerInfo->dwOKEffect;
546
547 /*
548 * If we are hovering over the same target as before, send the
549 * DragOver notification
550 */
551 if ( (trackerInfo->curDragTarget != 0) &&
552 (trackerInfo->curDragTargetHWND==hwndNewTarget) )
553 {
554 POINTL mousePosParam;
555
556 /*
557 * The documentation tells me that the coordinate should be in the target
558 * window's coordinate space. However, the tests I made tell me the
559 * coordinates should be in screen coordinates.
560 */
561 mousePosParam.x = mousePos.x;
562 mousePosParam.y = mousePos.y;
563
564 IDropTarget_DragOver(trackerInfo->curDragTarget,
565 keyState,
566 mousePosParam,
567 trackerInfo->pdwEffect);
568 }
569 else
570 {
571 DropTargetNode* newDropTargetNode = 0;
572
573 /*
574 * If we changed window, we have to notify our old target and check for
575 * the new one.
576 */
577 if (trackerInfo->curDragTarget!=0)
578 {
579 IDropTarget_DragLeave(trackerInfo->curDragTarget);
580 }
581
582 /*
583 * Make sure we're hovering over a window.
584 */
585 if (hwndNewTarget!=0)
586 {
587 /*
588 * Find-out if there is a drag target under the mouse
589 */
590 newDropTargetNode = OLEDD_FindDropTarget(hwndNewTarget);
591
592 trackerInfo->curDragTargetHWND = hwndNewTarget;
593 trackerInfo->curDragTarget = newDropTargetNode ? newDropTargetNode->dropTarget : 0;
594
595 /*
596 * If there is, notify it that we just dragged-in
597 */
598 if (trackerInfo->curDragTarget!=0)
599 {
600 POINTL mousePosParam;
601
602 /*
603 * The documentation tells me that the coordinate should be in the target
604 * window's coordinate space. However, the tests I made tell me the
605 * coordinates should be in screen coordinates.
606 */
607 mousePosParam.x = mousePos.x;
608 mousePosParam.y = mousePos.y;
609
610 IDropTarget_DragEnter(trackerInfo->curDragTarget,
611 trackerInfo->dataObject,
612 keyState,
613 mousePosParam,
614 trackerInfo->pdwEffect);
615 }
616 }
617 else
618 {
619 /*
620 * The mouse is not over a window so we don't track anything.
621 */
622 trackerInfo->curDragTargetHWND = 0;
623 trackerInfo->curDragTarget = 0;
624 }
625 }
626
627 /*
628 * Now that we have done that, we have to tell the source to give
629 * us feedback on the work being done by the target. If we don't
630 * have a target, simulate no effect.
631 */
632 if (trackerInfo->curDragTarget==0)
633 {
634 *trackerInfo->pdwEffect = DROPEFFECT_NONE;
635 }
636
637 hr = IDropSource_GiveFeedback(trackerInfo->dropSource,
638 *trackerInfo->pdwEffect);
639
640 /*
641 * When we ask for feedback from the drop source, sometimes it will
642 * do all the necessary work and sometimes it will not handle it
643 * when that's the case, we must display the standard drag and drop
644 * cursors.
645 */
646 if (hr==DRAGDROP_S_USEDEFAULTCURSORS)
647 {
648 if ( (*trackerInfo->pdwEffect & DROPEFFECT_MOVE) ||
649 (*trackerInfo->pdwEffect & DROPEFFECT_COPY) ||
650 (*trackerInfo->pdwEffect & DROPEFFECT_LINK) )
651 {
652 SetCursor(LoadCursorA(0, IDC_SIZEALLA));
653 }
654 else
655 {
656 SetCursor(LoadCursorA(0, IDC_NOA));
657 }
658 }
659}
660
661/***
662 * OLEDD_TrackStateChange()
663 *
664 * This method is invoked while a drag and drop operation is in effect.
665 * It is used to notify the drop target/drop source callbacks when
666 * the state of the keyboard or mouse button change.
667 *
668 * params:
669 * trackerInfo - Pointer to the structure identifying the
670 * drag & drop operation that is currently
671 * active.
672 * mousePos - Current position of the mouse in screen
673 * coordinates.
674 * keyState - Contains the state of the shift keys and the
675 * mouse buttons (MK_LBUTTON and the like)
676 */
677static void OLEDD_TrackStateChange(
678 TrackerWindowInfo* trackerInfo,
679 POINT mousePos,
680 DWORD keyState)
681{
682 /*
683 * Ask the drop source what to do with the operation.
684 */
685 trackerInfo->returnValue = IDropSource_QueryContinueDrag(
686 trackerInfo->dropSource,
687 trackerInfo->escPressed,
688 keyState);
689
690 /*
691 * All the return valued will stop the operation except the S_OK
692 * return value.
693 */
694 if (trackerInfo->returnValue!=S_OK)
695 {
696 /*
697 * Make sure the message loop in DoDragDrop stops
698 */
699 trackerInfo->trackingDone = TRUE;
700
701 /*
702 * Release the mouse in case the drop target decides to show a popup
703 * or a menu or something.
704 */
705 ReleaseCapture();
706
707 /*
708 * If we end-up over a target, drop the object in the target or
709 * inform the target that the operation was cancelled.
710 */
711 if (trackerInfo->curDragTarget!=0)
712 {
713 switch (trackerInfo->returnValue)
714 {
715 /*
716 * If the source wants us to complete the operation, we tell
717 * the drop target that we just dropped the object in it.
718 */
719 case DRAGDROP_S_DROP:
720 {
721 POINTL mousePosParam;
722
723 /*
724 * The documentation tells me that the coordinate should be
725 * in the target window's coordinate space. However, the tests
726 * I made tell me the coordinates should be in screen coordinates.
727 */
728 mousePosParam.x = mousePos.x;
729 mousePosParam.y = mousePos.y;
730
731 IDropTarget_Drop(trackerInfo->curDragTarget,
732 trackerInfo->dataObject,
733 keyState,
734 mousePosParam,
735 trackerInfo->pdwEffect);
736 break;
737 }
738 /*
739 * If the source told us that we should cancel, fool the drop
740 * target by telling it that the mouse left it's window.
741 */
742 case DRAGDROP_S_CANCEL:
743 IDropTarget_DragLeave(trackerInfo->curDragTarget);
744 break;
745 }
746 }
747 }
748}
749
750/***
751 * OLEDD_GetButtonState()
752 *
753 * This method will use the current state of the keyboard to build
754 * a button state mask equivalent to the one passed in the
755 * WM_MOUSEMOVE wParam.
756 */
757static DWORD OLEDD_GetButtonState()
758{
759 BYTE keyboardState[256];
760 DWORD keyMask = 0;
761
762 GetKeyboardState(keyboardState);
763
764 if ( (keyboardState[VK_SHIFT] & 0x80) !=0)
765 keyMask |= MK_SHIFT;
766
767 if ( (keyboardState[VK_CONTROL] & 0x80) !=0)
768 keyMask |= MK_CONTROL;
769
770 if ( (keyboardState[VK_LBUTTON] & 0x80) !=0)
771 keyMask |= MK_LBUTTON;
772
773 if ( (keyboardState[VK_RBUTTON] & 0x80) !=0)
774 keyMask |= MK_RBUTTON;
775
776 if ( (keyboardState[VK_MBUTTON] & 0x80) !=0)
777 keyMask |= MK_MBUTTON;
778
779 return keyMask;
780}
781
Note: See TracBrowser for help on using the repository browser.