source: trunk/src/ole32/oleDrag.cpp@ 851

Last change on this file since 851 was 851, checked in by davidr, 26 years ago

Initial port of OLE2.
Added regsvr32

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