Fixed a little Drag and Drop bug.
[wine] / ole / ole2.c
1 /*
2  *      OLE2 library
3  *
4  *      Copyright 1995  Martin von Loewis
5  *      Copyright 1999  Francis Beaudet
6  *      Copyright 1999  Noel Borthwick 
7  */
8
9 #include <assert.h>
10
11 #include "winuser.h"
12 #include "winerror.h"
13 #include "ole2.h"
14 #include "process.h"
15 #include "hook.h"
16 #include "commctrl.h"
17 #include "wine/obj_clientserver.h"
18 #include "debug.h"
19 #include "ole2ver.h"
20
21 DEFAULT_DEBUG_CHANNEL(ole)
22
23 /******************************************************************************
24  * These are static/global variables and internal data structures that the 
25  * OLE module uses to maintain it's state.
26  */
27 typedef struct tagDropTargetNode
28 {
29   HWND          hwndTarget;
30   IDropTarget*    dropTarget;
31   struct tagDropTargetNode* prevDropTarget;
32   struct tagDropTargetNode* nextDropTarget;
33 } DropTargetNode;
34
35 typedef struct tagTrackerWindowInfo
36 {
37   IDataObject* dataObject;
38   IDropSource* dropSource;
39   DWORD        dwOKEffect;
40   DWORD*       pdwEffect;
41   BOOL       trackingDone;
42   HRESULT      returnValue;
43
44   BOOL       escPressed;
45   HWND       curDragTargetHWND;
46   IDropTarget* curDragTarget;
47 } TrackerWindowInfo;
48
49 typedef struct tagOleMenuDescriptor  /* OleMenuDescriptor */
50 {
51   HWND               hwndFrame;         /* The containers frame window */
52   HWND               hwndActiveObject;  /* The active objects window */
53   OLEMENUGROUPWIDTHS mgw;               /* OLE menu group widths for the shared menu */
54   HMENU              hmenuCombined;     /* The combined menu */
55   BOOL               bIsServerItem;     /* True if the currently open popup belongs to the server */
56 } OleMenuDescriptor;
57
58 typedef struct tagOleMenuHookItem   /* OleMenu hook item in per thread hook list */
59 {
60   DWORD tid;                /* Thread Id  */
61   HANDLE hHeap;             /* Heap this is allocated from */
62   HHOOK GetMsg_hHook;       /* message hook for WH_GETMESSAGE */
63   HHOOK CallWndProc_hHook;  /* message hook for WH_CALLWNDPROC */
64 } OleMenuHookItem;
65
66 /*
67  * Dynamic pointer array of per thread message hooks (maintained by OleSetMenuDescriptor)
68  */
69 static HDPA OLEMenu_MsgHookDPA = NULL;
70
71 /*
72  * This is the lock count on the OLE library. It is controlled by the
73  * OLEInitialize/OLEUninitialize methods.
74  */
75 static ULONG OLE_moduleLockCount = 0;
76
77 /*
78  * Name of our registered window class.
79  */
80 static const char OLEDD_DRAGTRACKERCLASS[] = "WineDragDropTracker32";
81
82 /*
83  * This is the head of the Drop target container.
84  */
85 static DropTargetNode* targetListHead = NULL;
86
87 /******************************************************************************
88  * These are the prototypes of the utility methods used to manage a shared menu
89  */
90 static void OLEMenu_Initialize();
91 static void OLEMenu_UnInitialize();
92 BOOL OLEMenu_InstallHooks( DWORD tid );
93 BOOL OLEMenu_UnInstallHooks( DWORD tid );
94 OleMenuHookItem * OLEMenu_IsHookInstalled( DWORD tid, INT *pixHook );
95 static BOOL OLEMenu_FindMainMenuIndex( HMENU hMainMenu, HMENU hPopupMenu, UINT *pnPos );
96 BOOL OLEMenu_SetIsServerMenu( HMENU hmenu, OleMenuDescriptor *pOleMenuDescriptor );
97 LRESULT CALLBACK OLEMenu_CallWndProc(INT code, WPARAM wParam, LPARAM lParam);
98 LRESULT CALLBACK OLEMenu_GetMsgProc(INT code, WPARAM wParam, LPARAM lParam);
99
100 /******************************************************************************
101  * These are the prototypes of the utility methods used for OLE Drag n Drop
102  */
103 static void            OLEDD_Initialize();
104 static void            OLEDD_UnInitialize();
105 static void            OLEDD_InsertDropTarget(
106                          DropTargetNode* nodeToAdd);
107 static DropTargetNode* OLEDD_ExtractDropTarget(
108                          HWND hwndOfTarget);
109 static DropTargetNode* OLEDD_FindDropTarget(
110                          HWND hwndOfTarget);
111 static LRESULT WINAPI  OLEDD_DragTrackerWindowProc(
112                          HWND   hwnd, 
113                          UINT   uMsg,
114                          WPARAM wParam, 
115                          LPARAM   lParam);
116 static void OLEDD_TrackMouseMove(
117                          TrackerWindowInfo* trackerInfo,
118                          POINT            mousePos,
119                          DWORD              keyState);
120 static void OLEDD_TrackStateChange(
121                          TrackerWindowInfo* trackerInfo,
122                          POINT            mousePos,
123                          DWORD              keyState);
124 static DWORD OLEDD_GetButtonState();
125
126
127 /******************************************************************************
128  *              OleBuildVersion [OLE2.1]
129  */
130 DWORD WINAPI OleBuildVersion(void)
131 {
132     TRACE(ole,"(void)\n");
133     return (rmm<<16)+rup;
134 }
135
136 /***********************************************************************
137  *           OleInitialize       (OLE2.2) (OLE32.108)
138  */
139 HRESULT WINAPI OleInitialize(LPVOID reserved)
140 {
141   HRESULT hr;
142
143   TRACE(ole, "(%p)\n", reserved);
144
145   /*
146    * The first duty of the OleInitialize is to initialize the COM libraries.
147    */
148   hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
149
150   /*
151    * If the CoInitializeEx call failed, the OLE libraries can't be 
152    * initialized.
153    */
154   if (FAILED(hr))
155     return hr;    
156
157   /*
158    * Then, it has to initialize the OLE specific modules.
159    * This includes:
160    *     Clipboard
161    *     Drag and Drop
162    *     Object linking and Embedding
163    *     In-place activation
164    */
165   if (OLE_moduleLockCount==0)
166 {
167     /* 
168      * Initialize the libraries.
169      */
170     TRACE(ole, "() - Initializing the OLE libraries\n");
171
172     /*
173      * Drag and Drop
174      */
175     OLEDD_Initialize();
176
177     /*
178      * OLE shared menu
179      */
180     OLEMenu_Initialize();
181 }
182
183   /*
184    * Then, we increase the lock count on the OLE module.
185    */
186   OLE_moduleLockCount++;  
187
188   return hr;
189 }
190
191 /******************************************************************************
192  *              CoGetCurrentProcess     [COMPOBJ.34] [OLE2.2][OLE32.108]
193  *
194  * NOTES
195  *   Is DWORD really the correct return type for this function?
196  */
197 DWORD WINAPI CoGetCurrentProcess(void) {
198         return (DWORD)PROCESS_Current();
199 }
200
201 /******************************************************************************
202  *              OleUninitialize [OLE2.3] [OLE32.131]
203  */
204 void WINAPI OleUninitialize(void)
205 {
206   TRACE(ole, "()\n");
207
208   /*
209    * Decrease the lock count on the OLE module.
210    */
211   OLE_moduleLockCount--;
212
213   /*
214    * If we hit the bottom of the lock stack, free the libraries.
215    */
216   if (OLE_moduleLockCount==0)
217   {
218     /*
219      * Actually free the libraries.
220      */
221     TRACE(ole, "() - Freeing the last reference count\n");
222
223     /*
224      * Drag and Drop
225      */
226     OLEDD_UnInitialize();
227     
228     /*
229      * OLE shared menu
230      */
231     OLEMenu_UnInitialize();
232   }
233   
234   /*
235    * Then, uninitialize the COM libraries.
236    */
237   CoUninitialize();
238 }
239
240 /***********************************************************************
241  *           OleFlushClipboard   [OLE2.76]
242  */
243 HRESULT WINAPI OleFlushClipboard16(void)
244 {
245     return S_OK;
246 }
247
248 /***********************************************************************
249  *           OleSetClipboard     [OLE32.127]
250  */
251 HRESULT WINAPI OleSetClipboard(LPVOID pDataObj)
252 {
253     FIXME(ole,"(%p), stub!\n", pDataObj);
254     return S_OK;
255 }
256
257 /******************************************************************************
258  *              CoRegisterMessageFilter32       [OLE32.38]
259  */
260 HRESULT WINAPI CoRegisterMessageFilter(
261     LPMESSAGEFILTER lpMessageFilter,    /* Pointer to interface */
262     LPMESSAGEFILTER *lplpMessageFilter  /* Indirect pointer to prior instance if non-NULL */
263 ) {
264     FIXME(ole,"stub\n");
265     if (lplpMessageFilter) {
266         *lplpMessageFilter = NULL;
267     }
268     return S_OK;
269 }
270
271 /******************************************************************************
272  *              OleInitializeWOW        [OLE32.109]
273  */
274 HRESULT WINAPI OleInitializeWOW(DWORD x) {
275         FIXME(ole,"(0x%08lx),stub!\n",x);
276         return 0;
277 }
278
279 /***********************************************************************
280  *           RegisterDragDrop16 (OLE2.35)
281  */
282 HRESULT WINAPI RegisterDragDrop16(
283         HWND16 hwnd,
284         LPDROPTARGET pDropTarget
285 ) {
286         FIXME(ole,"(0x%04x,%p),stub!\n",hwnd,pDropTarget);
287         return S_OK;
288 }
289
290 /***********************************************************************
291  *           RegisterDragDrop32 (OLE32.139)
292  */
293 HRESULT WINAPI RegisterDragDrop(
294         HWND hwnd,
295         LPDROPTARGET pDropTarget) 
296 {
297   DropTargetNode* dropTargetInfo;
298
299   TRACE(ole,"(0x%x,%p)\n", hwnd, pDropTarget);
300
301   /*
302    * First, check if the window is already registered.
303    */
304   dropTargetInfo = OLEDD_FindDropTarget(hwnd);
305
306   if (dropTargetInfo!=NULL)
307     return DRAGDROP_E_ALREADYREGISTERED;
308
309   /*
310    * If it's not there, we can add it. We first create a node for it.
311    */
312   dropTargetInfo = HeapAlloc(GetProcessHeap(), 0, sizeof(DropTargetNode));
313
314   if (dropTargetInfo==NULL)
315     return E_OUTOFMEMORY;
316
317   dropTargetInfo->hwndTarget     = hwnd;
318   dropTargetInfo->prevDropTarget = NULL;
319   dropTargetInfo->nextDropTarget = NULL;
320
321   /*
322    * Don't forget that this is an interface pointer, need to nail it down since
323    * we keep a copy of it.
324    */
325   dropTargetInfo->dropTarget  = pDropTarget;
326   IDropTarget_AddRef(dropTargetInfo->dropTarget);
327   
328   OLEDD_InsertDropTarget(dropTargetInfo);
329
330         return S_OK;
331 }
332
333 /***********************************************************************
334  *           RevokeDragDrop16 (OLE2.36)
335  */
336 HRESULT WINAPI RevokeDragDrop16(
337         HWND16 hwnd
338 ) {
339         FIXME(ole,"(0x%04x),stub!\n",hwnd);
340         return S_OK;
341 }
342
343 /***********************************************************************
344  *           RevokeDragDrop32 (OLE32.141)
345  */
346 HRESULT WINAPI RevokeDragDrop(
347         HWND hwnd)
348 {
349   DropTargetNode* dropTargetInfo;
350
351   TRACE(ole,"(0x%x)\n", hwnd);
352
353   /*
354    * First, check if the window is already registered.
355    */
356   dropTargetInfo = OLEDD_ExtractDropTarget(hwnd);
357
358   /*
359    * If it ain't in there, it's an error.
360    */
361   if (dropTargetInfo==NULL)
362     return DRAGDROP_E_NOTREGISTERED;
363
364   /*
365    * If it's in there, clean-up it's used memory and
366    * references
367    */
368   IDropTarget_Release(dropTargetInfo->dropTarget);
369   HeapFree(GetProcessHeap(), 0, dropTargetInfo);  
370
371         return S_OK;
372 }
373
374 /***********************************************************************
375  *           OleRegGetUserType (OLE32.122)
376  */
377 HRESULT WINAPI OleRegGetUserType( 
378         REFCLSID clsid, 
379         DWORD dwFormOfType,
380         LPOLESTR* pszUserType)
381 {
382         FIXME(ole,",stub!\n");
383         return S_OK;
384 }
385
386 /***********************************************************************
387  * DoDragDrop32 [OLE32.65]
388  */
389 HRESULT WINAPI DoDragDrop (
390   IDataObject *pDataObject,  /* ptr to the data obj           */
391   IDropSource* pDropSource,  /* ptr to the source obj         */
392   DWORD       dwOKEffect,    /* effects allowed by the source */
393   DWORD       *pdwEffect)    /* ptr to effects of the source  */
394 {
395   TrackerWindowInfo trackerInfo;
396   HWND            hwndTrackWindow;
397   MSG             msg;
398
399   TRACE(ole,"(DataObject %p, DropSource %p)\n", pDataObject, pDropSource);
400
401   /*
402    * Setup the drag n drop tracking window.
403    */
404   trackerInfo.dataObject        = pDataObject;
405   trackerInfo.dropSource        = pDropSource;
406   trackerInfo.dwOKEffect        = dwOKEffect;
407   trackerInfo.pdwEffect         = pdwEffect;
408   trackerInfo.trackingDone      = FALSE;
409   trackerInfo.escPressed        = FALSE;
410   trackerInfo.curDragTargetHWND = 0;
411   trackerInfo.curDragTarget     = 0;
412
413   hwndTrackWindow = CreateWindowA(OLEDD_DRAGTRACKERCLASS,
414                                     "TrackerWindow",
415                                     WS_POPUP,
416                                     CW_USEDEFAULT, CW_USEDEFAULT,
417                                     CW_USEDEFAULT, CW_USEDEFAULT,
418                                     0,
419                                     0,
420                                     0,
421                                     (LPVOID)&trackerInfo);
422
423   if (hwndTrackWindow!=0)
424   {
425     /*
426      * Capture the mouse input
427      */
428     SetCapture(hwndTrackWindow);
429
430     /*
431      * Pump messages. All mouse input should go the the capture window.
432      */
433     while (!trackerInfo.trackingDone && GetMessageA(&msg, 0, 0, 0) )
434     {
435       if ( (msg.message >= WM_KEYFIRST) && 
436            (msg.message <= WM_KEYFIRST) )
437       {
438         /*
439          * When keyboard messages are sent to windows on this thread, we
440          * want to ignore notify the drop source that the state changed.
441          * in the case of the Escape key, we also notify the drop source
442          * we give it a special meaning.
443          */
444         if ( (msg.message==WM_KEYDOWN) &&
445              (msg.wParam==VK_ESCAPE) )
446         {
447           trackerInfo.escPressed = TRUE;
448         }
449
450         /*
451          * Notify the drop source.
452          */       
453         OLEDD_TrackStateChange(&trackerInfo,
454                                msg.pt,
455                                OLEDD_GetButtonState());
456       }
457       else
458       {
459         /*
460          * Dispatch the messages only when it's not a keyboard message.
461          */
462         DispatchMessageA(&msg);
463       }
464     }
465
466     /*
467      * Destroy the temporary window.
468      */
469     DestroyWindow(hwndTrackWindow);
470
471     return trackerInfo.returnValue;
472   }
473
474   return E_FAIL;
475 }
476
477 /***********************************************************************
478  * OleQueryLinkFromData32 [OLE32.118]
479  */
480 HRESULT WINAPI OleQueryLinkFromData(
481   IDataObject* pSrcDataObject)
482 {
483   FIXME(ole,"(%p),stub!\n", pSrcDataObject);
484   return S_OK;
485 }
486
487 /***********************************************************************
488  * OleRegGetMiscStatus [OLE32.121]
489  */
490 HRESULT WINAPI OleRegGetMiscStatus(
491   REFCLSID clsid,
492   DWORD    dwAspect,
493   DWORD*   pdwStatus)
494 {
495   FIXME(ole,"(),stub!\n");
496   return REGDB_E_CLASSNOTREG;
497 }
498
499 /***********************************************************************
500  * OleGetClipboard32 [OLE32.105]
501  */
502 HRESULT WINAPI OleGetClipboard(
503   IDataObject** ppDataObj)
504 {
505   FIXME(ole,"(%p),stub!\n", ppDataObj);
506
507   if (ppDataObj)
508     *ppDataObj=0;
509
510   return E_FAIL;
511 }
512
513
514 /**************************************************************************
515  * Internal methods to manage the shared OLE menu in response to the
516  * OLE***MenuDescriptor API
517  */
518
519 /***
520  * OLEMenu_Initialize()
521  *
522  * Initializes the OLEMENU data structures.
523  */
524 static void OLEMenu_Initialize()
525 {
526   /* Create a dynamic pointer array to store the hook handles */
527   if ( !OLEMenu_MsgHookDPA )
528     OLEMenu_MsgHookDPA = DPA_CreateEx( 2, GetProcessHeap() );
529 }
530
531 /***
532  * OLEMenu_UnInitialize()
533  *
534  * Releases the OLEMENU data structures.
535  */
536 static void OLEMenu_UnInitialize()
537 {
538   /* Release the hook table */
539   if ( OLEMenu_MsgHookDPA )
540     DPA_Destroy( OLEMenu_MsgHookDPA );
541
542   OLEMenu_MsgHookDPA = NULL;
543 }
544
545 /*************************************************************************
546  * OLEMenu_InstallHooks
547  * Install thread scope message hooks for WH_GETMESSAGE and WH_CALLWNDPROC
548  *
549  * RETURNS: TRUE if message hooks were succesfully installed
550  *          FALSE on failure
551  */
552 BOOL OLEMenu_InstallHooks( DWORD tid )
553 {
554   OleMenuHookItem *pHookItem = NULL;
555
556   if ( !OLEMenu_MsgHookDPA ) /* No hook table? Create one */
557   {
558     /* Create a dynamic pointer array to store the hook handles */
559     if ( !(OLEMenu_MsgHookDPA = DPA_CreateEx( 2, GetProcessHeap() )) ) 
560       return FALSE;
561   }
562
563   /* Create an entry for the hook table */
564   if ( !(pHookItem = HeapAlloc(GetProcessHeap(), 0,
565                                sizeof(OleMenuHookItem)) ) )
566     return FALSE;
567
568   pHookItem->tid = tid;
569   pHookItem->hHeap = GetProcessHeap();
570   
571   /* Install a thread scope message hook for WH_GETMESSAGE */
572   pHookItem->GetMsg_hHook = SetWindowsHookExA( WH_GETMESSAGE, OLEMenu_GetMsgProc,
573                                                0, GetCurrentThreadId() );
574   if ( !pHookItem->GetMsg_hHook )
575     goto CLEANUP;
576
577   /* Install a thread scope message hook for WH_CALLWNDPROC */
578   pHookItem->CallWndProc_hHook = SetWindowsHookExA( WH_CALLWNDPROC, OLEMenu_CallWndProc,
579                                                     0, GetCurrentThreadId() );
580   if ( !pHookItem->CallWndProc_hHook )
581     goto CLEANUP;
582
583   /* Insert the hook table entry */
584   if ( -1 == DPA_InsertPtr( OLEMenu_MsgHookDPA, 0, pHookItem ) )
585     goto CLEANUP;
586   
587   return TRUE;
588   
589 CLEANUP:
590   /* Unhook any hooks */
591   if ( pHookItem->GetMsg_hHook )
592     UnhookWindowsHookEx( pHookItem->GetMsg_hHook );
593   if ( pHookItem->CallWndProc_hHook )
594     UnhookWindowsHookEx( pHookItem->CallWndProc_hHook );
595   /* Release the hook table entry */
596   HeapFree(pHookItem->hHeap, 0, pHookItem );
597   
598   return FALSE;
599 }
600
601 /*************************************************************************
602  * OLEMenu_UnInstallHooks
603  * UnInstall thread scope message hooks for WH_GETMESSAGE and WH_CALLWNDPROC
604  *
605  * RETURNS: TRUE if message hooks were succesfully installed
606  *          FALSE on failure
607  */
608 BOOL OLEMenu_UnInstallHooks( DWORD tid )
609 {
610   INT ixHook;
611   OleMenuHookItem *pHookItem = NULL;
612
613   if ( !OLEMenu_MsgHookDPA )  /* No hooks set */
614     return TRUE;
615
616   /* Lookup the hHook index for this tid */
617   if ( !OLEMenu_IsHookInstalled( tid , &ixHook ) )
618     return TRUE;
619
620   /* Remove the hook entry from the table(the pointer itself is not deleted) */
621   if ( !( pHookItem = DPA_DeletePtr(OLEMenu_MsgHookDPA, ixHook) ) )
622     return FALSE;
623
624   /* Uninstall the hooks installed for this thread */
625   if ( !UnhookWindowsHookEx( pHookItem->GetMsg_hHook ) )
626     goto CLEANUP;
627   if ( !UnhookWindowsHookEx( pHookItem->CallWndProc_hHook ) )
628     goto CLEANUP;
629
630   /* Release the hook table entry */
631   HeapFree(pHookItem->hHeap, 0, pHookItem );
632
633   return TRUE;
634
635 CLEANUP:
636   /* Release the hook table entry */
637   if (pHookItem)
638     HeapFree(pHookItem->hHeap, 0, pHookItem );
639
640   return FALSE;
641 }
642
643 /*************************************************************************
644  * OLEMenu_IsHookInstalled
645  * Tests if OLEMenu hooks have been installed for a thread
646  *
647  * RETURNS: The pointer and index of the hook table entry for the tid
648  *          NULL and -1 for the index if no hooks were installed for this thread
649  */
650 OleMenuHookItem * OLEMenu_IsHookInstalled( DWORD tid, INT *pixHook )
651 {
652   INT ixHook;
653   OleMenuHookItem *pHookItem = NULL;
654
655   if ( pixHook )
656     *pixHook = -1;
657   
658   if ( !OLEMenu_MsgHookDPA )  /* No hooks set */
659     return NULL;
660
661   /* Do a simple linear search for an entry whose tid matches ours.
662    * We really need a map but efficiency is not a concern here. */
663   for( ixHook = 0; ; ixHook++ )
664   {
665     /* Retrieve the hook entry */
666     if ( !( pHookItem = DPA_GetPtr(OLEMenu_MsgHookDPA, ixHook) ) )
667       return NULL;
668
669     if ( tid == pHookItem->tid )
670     {
671       if ( pixHook )
672         *pixHook = ixHook;
673       return pHookItem;
674     }
675   }
676   
677   return NULL;
678 }
679
680 /***********************************************************************
681  *           OLEMenu_FindMainMenuIndex
682  *
683  * Used by OLEMenu API to find the top level group a menu item belongs to.
684  * On success pnPos contains the index of the item in the top level menu group
685  *
686  * RETURNS: TRUE if the ID was found, FALSE on failure
687  */
688 static BOOL OLEMenu_FindMainMenuIndex( HMENU hMainMenu, HMENU hPopupMenu, UINT *pnPos )
689 {
690   UINT i, nItems;
691
692   nItems = GetMenuItemCount( hMainMenu );
693
694   for (i = 0; i < nItems; i++)
695   {
696     HMENU hsubmenu;
697       
698     /*  Is the current item a submenu? */
699     if ( (hsubmenu = GetSubMenu(hMainMenu, i)) )
700     {
701       /* If the handle is the same we're done */
702       if ( hsubmenu == hPopupMenu )
703       {
704         if (pnPos)
705           *pnPos = i;
706         return TRUE;
707       }
708       /* Recursively search without updating pnPos */
709       else if ( OLEMenu_FindMainMenuIndex( hsubmenu, hPopupMenu, NULL ) )
710       {
711         if (pnPos)
712           *pnPos = i;
713         return TRUE;
714       }
715     }
716   }
717
718   return FALSE;
719 }
720
721 /***********************************************************************
722  *           OLEMenu_SetIsServerMenu
723  *
724  * Checks whether a popup menu belongs to a shared menu group which is
725  * owned by the server, and sets the menu descriptor state accordingly.
726  * All menu messages from these groups should be routed to the server.
727  *
728  * RETURNS: TRUE if the popup menu is part of a server owned group
729  *          FASE if the popup menu is part of a container owned group
730  */
731 BOOL OLEMenu_SetIsServerMenu( HMENU hmenu, OleMenuDescriptor *pOleMenuDescriptor )
732 {
733   UINT nPos = 0, nWidth, i;
734
735   pOleMenuDescriptor->bIsServerItem = FALSE;
736
737   /* Don't bother searching if the popup is the combined menu itself */
738   if ( hmenu == pOleMenuDescriptor->hmenuCombined )
739     return FALSE;
740
741   /* Find the menu item index in the shared OLE menu that this item belongs to */
742   if ( !OLEMenu_FindMainMenuIndex( pOleMenuDescriptor->hmenuCombined, hmenu,  &nPos ) )
743     return FALSE;
744   
745   /* The group widths array has counts for the number of elements
746    * in the groups File, Edit, Container, Object, Window, Help.
747    * The Edit, Object & Help groups belong to the server object
748    * and the other three belong to the container.
749    * Loop thru the group widths and locate the group we are a member of.
750    */
751   for ( i = 0, nWidth = 0; i < 6; i++ )
752   {
753     nWidth += pOleMenuDescriptor->mgw.width[i];
754     if ( nPos < nWidth )
755     {
756       /* Odd elements are server menu widths */
757       pOleMenuDescriptor->bIsServerItem = (i%2) ? TRUE : FALSE;
758       break;
759     }
760   }
761
762   return pOleMenuDescriptor->bIsServerItem;
763 }
764
765 /*************************************************************************
766  * OLEMenu_CallWndProc
767  * Thread scope WH_CALLWNDPROC hook proc filter function (callback)
768  * This is invoked from a message hook installed in OleSetMenuDescriptor.
769  */
770 LRESULT CALLBACK OLEMenu_CallWndProc(INT code, WPARAM wParam, LPARAM lParam)
771 {
772   LPCWPSTRUCT pMsg = NULL;
773   HOLEMENU hOleMenu = 0;
774   OleMenuDescriptor *pOleMenuDescriptor = NULL;
775   OleMenuHookItem *pHookItem = NULL;
776   WORD fuFlags;
777   
778   TRACE(ole,"%i, %04x, %08x\n", code, wParam, (unsigned)lParam );
779
780   /* Check if we're being asked to process the message */
781   if ( HC_ACTION != code )
782     goto NEXTHOOK;
783       
784   /* Retrieve the current message being dispatched from lParam */
785   pMsg = (LPCWPSTRUCT)lParam;
786
787   /* Check if the message is destined for a window we are interested in:
788    * If the window has an OLEMenu property we may need to dispatch
789    * the menu message to its active objects window instead. */
790
791   hOleMenu = (HOLEMENU)GetPropA( pMsg->hwnd, "PROP_OLEMenuDescriptor" );
792   if ( !hOleMenu )
793     goto NEXTHOOK;
794
795   /* Get the menu descriptor */
796   pOleMenuDescriptor = (OleMenuDescriptor *) GlobalLock( hOleMenu );
797   if ( !pOleMenuDescriptor ) /* Bad descriptor! */
798     goto NEXTHOOK;
799
800   /* Process menu messages */
801   switch( pMsg->message )
802   {
803     case WM_INITMENU:
804     {
805       /* Reset the menu descriptor state */
806       pOleMenuDescriptor->bIsServerItem = FALSE;
807
808       /* Send this message to the server as well */
809       SendMessageA( pOleMenuDescriptor->hwndActiveObject,
810                   pMsg->message, pMsg->wParam, pMsg->lParam );
811       goto NEXTHOOK;
812     }
813     
814     case WM_INITMENUPOPUP:
815     {
816       /* Save the state for whether this is a server owned menu */
817       OLEMenu_SetIsServerMenu( (HMENU)pMsg->wParam, pOleMenuDescriptor );
818       break;
819     }
820     
821     case WM_MENUSELECT:
822     {
823       fuFlags = HIWORD(pMsg->wParam);  /* Get flags */
824       if ( fuFlags & MF_SYSMENU )
825          goto NEXTHOOK;
826
827       /* Save the state for whether this is a server owned popup menu */
828       else if ( fuFlags & MF_POPUP )
829         OLEMenu_SetIsServerMenu( (HMENU)pMsg->lParam, pOleMenuDescriptor );
830
831       break;
832     }
833     
834     case WM_DRAWITEM:
835     {
836       LPDRAWITEMSTRUCT lpdis = (LPDRAWITEMSTRUCT) pMsg->lParam;
837       if ( pMsg->wParam != 0 || lpdis->CtlType != ODT_MENU )
838         goto NEXTHOOK;  /* Not a menu message */
839
840       break;
841     }
842
843     default:
844       goto NEXTHOOK;
845   }
846
847   /* If the message was for the server dispatch it accordingly */
848   if ( pOleMenuDescriptor->bIsServerItem )
849   {
850     SendMessageA( pOleMenuDescriptor->hwndActiveObject,
851                   pMsg->message, pMsg->wParam, pMsg->lParam );
852   }
853     
854 NEXTHOOK:
855   if ( pOleMenuDescriptor )
856     GlobalUnlock( hOleMenu );
857   
858   /* Lookup the hook item for the current thread */
859   if ( !( pHookItem = OLEMenu_IsHookInstalled( GetCurrentThreadId(), NULL ) ) )
860   {
861     /* This should never fail!! */
862     WARN(ole, "could not retrieve hHook for current thread!\n" );
863     return 0;
864   }
865   
866   /* Pass on the message to the next hooker */
867   return CallNextHookEx( pHookItem->CallWndProc_hHook, code, wParam, lParam );
868 }
869
870 /*************************************************************************
871  * OLEMenu_GetMsgProc
872  * Thread scope WH_GETMESSAGE hook proc filter function (callback)
873  * This is invoked from a message hook installed in OleSetMenuDescriptor.
874  */
875 LRESULT CALLBACK OLEMenu_GetMsgProc(INT code, WPARAM wParam, LPARAM lParam)
876 {
877   LPMSG pMsg = NULL;
878   HOLEMENU hOleMenu = 0;
879   OleMenuDescriptor *pOleMenuDescriptor = NULL;
880   OleMenuHookItem *pHookItem = NULL;
881   WORD wCode;
882   
883   TRACE(ole,"%i, %04x, %08x\n", code, wParam, (unsigned)lParam );
884
885   /* Check if we're being asked to process a  messages */
886   if ( HC_ACTION != code )
887     goto NEXTHOOK;
888       
889   /* Retrieve the current message being dispatched from lParam */
890   pMsg = (LPMSG)lParam;
891
892   /* Check if the message is destined for a window we are interested in:
893    * If the window has an OLEMenu property we may need to dispatch
894    * the menu message to its active objects window instead. */
895
896   hOleMenu = (HOLEMENU)GetPropA( pMsg->hwnd, "PROP_OLEMenuDescriptor" );
897   if ( !hOleMenu )
898     goto NEXTHOOK;
899
900   /* Process menu messages */
901   switch( pMsg->message )
902   {
903     case WM_COMMAND:
904     {
905       wCode = HIWORD(pMsg->wParam);  /* Get notification code */
906       if ( wCode )
907         goto NEXTHOOK;  /* Not a menu message */
908       break;
909     }
910     default:
911       goto NEXTHOOK;
912   }
913
914   /* Get the menu descriptor */
915   pOleMenuDescriptor = (OleMenuDescriptor *) GlobalLock( hOleMenu );
916   if ( !pOleMenuDescriptor ) /* Bad descriptor! */
917     goto NEXTHOOK;
918
919   /* If the message was for the server dispatch it accordingly */
920   if ( pOleMenuDescriptor->bIsServerItem )
921   {
922     /* Change the hWnd in the message to the active objects hWnd.
923      * The message loop which reads this message will automatically
924      * dispatch it to the embedded objects window. */
925     pMsg->hwnd = pOleMenuDescriptor->hwndActiveObject;
926   }
927     
928 NEXTHOOK:
929   if ( pOleMenuDescriptor )
930     GlobalUnlock( hOleMenu );
931   
932   /* Lookup the hook item for the current thread */
933   if ( !( pHookItem = OLEMenu_IsHookInstalled( GetCurrentThreadId(), NULL ) ) )
934   {
935     /* This should never fail!! */
936     WARN(ole, "could not retrieve hHook for current thread!\n" );
937     return FALSE;
938   }
939   
940   /* Pass on the message to the next hooker */
941   return CallNextHookEx( pHookItem->GetMsg_hHook, code, wParam, lParam );
942 }
943
944 /***********************************************************************
945  * OleCreateMenuDescriptor [OLE32.97]
946  * Creates an OLE menu descriptor for OLE to use when dispatching
947  * menu messages and commands.
948  *
949  * PARAMS:
950  *    hmenuCombined  -  Handle to the objects combined menu
951  *    lpMenuWidths   -  Pointer to array of 6 LONG's indicating menus per group
952  *
953  */
954 HOLEMENU WINAPI OleCreateMenuDescriptor(
955   HMENU                hmenuCombined,
956   LPOLEMENUGROUPWIDTHS lpMenuWidths)
957 {
958   HOLEMENU hOleMenu;
959   OleMenuDescriptor *pOleMenuDescriptor;
960   int i;
961
962   if ( !hmenuCombined || !lpMenuWidths )
963     return 0;
964
965   /* Create an OLE menu descriptor */
966   if ( !(hOleMenu = GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT,
967                                 sizeof(OleMenuDescriptor) ) ) )
968   return 0;
969
970   pOleMenuDescriptor = (OleMenuDescriptor *) GlobalLock( hOleMenu );
971   if ( !pOleMenuDescriptor )
972     return 0;
973
974   /* Initialize menu group widths and hmenu */
975   for ( i = 0; i < 6; i++ )
976     pOleMenuDescriptor->mgw.width[i] = lpMenuWidths->width[i];
977   
978   pOleMenuDescriptor->hmenuCombined = hmenuCombined;
979   pOleMenuDescriptor->bIsServerItem = FALSE;
980   GlobalUnlock( hOleMenu );
981       
982   return hOleMenu;
983 }
984
985 /***********************************************************************
986  * OleDestroyMenuDescriptor [OLE32.99]
987  * Destroy the shared menu descriptor
988  */
989 HRESULT WINAPI OleDestroyMenuDescriptor(
990   HOLEMENU hmenuDescriptor)
991 {
992   if ( hmenuDescriptor )
993     GlobalFree( hmenuDescriptor );
994         return S_OK;
995 }
996
997 /***********************************************************************
998  * OleSetMenuDescriptor [OLE32.129]
999  * Installs or removes OLE dispatching code for the containers frame window
1000  * FIXME: The lpFrame and lpActiveObject parameters are currently ignored
1001  * OLE should install context sensitive help F1 filtering for the app when
1002  * these are non null.
1003  * 
1004  * PARAMS:
1005  *     hOleMenu         Handle to composite menu descriptor
1006  *     hwndFrame        Handle to containers frame window
1007  *     hwndActiveObject Handle to objects in-place activation window
1008  *     lpFrame          Pointer to IOleInPlaceFrame on containers window
1009  *     lpActiveObject   Pointer to IOleInPlaceActiveObject on active in-place object
1010  *
1011  * RETURNS:
1012  *      S_OK                               - menu installed correctly
1013  *      E_FAIL, E_INVALIDARG, E_UNEXPECTED - failure
1014  */
1015 HRESULT WINAPI OleSetMenuDescriptor(
1016   HOLEMENU               hOleMenu,
1017   HWND                   hwndFrame,
1018   HWND                   hwndActiveObject,
1019   LPOLEINPLACEFRAME        lpFrame,
1020   LPOLEINPLACEACTIVEOBJECT lpActiveObject)
1021 {
1022   OleMenuDescriptor *pOleMenuDescriptor = NULL;
1023
1024   /* Check args */
1025   if ( !hwndFrame || (hOleMenu && !hwndActiveObject) )
1026     return E_INVALIDARG;
1027
1028   if ( lpFrame || lpActiveObject )
1029   {
1030      FIXME(ole,"(%x, %x, %x, %p, %p), Context sensitive help filtering not implemented!\n",
1031         (unsigned int)hOleMenu,
1032         hwndFrame,
1033         hwndActiveObject,
1034         lpFrame,
1035         lpActiveObject);
1036   }
1037
1038   /* Set up a message hook to intercept the containers frame window messages.
1039    * The message filter is responsible for dispatching menu messages from the
1040    * shared menu which are intended for the object.
1041    */
1042
1043   if ( hOleMenu )  /* Want to install dispatching code */
1044   {
1045     /* If OLEMenu hooks are already installed for this thread, fail
1046      * Note: This effectively means that OleSetMenuDescriptor cannot
1047      * be called twice in succession on the same frame window
1048      * without first calling it with a null hOleMenu to uninstall */
1049     if ( OLEMenu_IsHookInstalled( GetCurrentThreadId(), NULL ) )
1050   return E_FAIL;
1051         
1052     /* Get the menu descriptor */
1053     pOleMenuDescriptor = (OleMenuDescriptor *) GlobalLock( hOleMenu );
1054     if ( !pOleMenuDescriptor )
1055       return E_UNEXPECTED;
1056
1057     /* Update the menu descriptor */
1058     pOleMenuDescriptor->hwndFrame = hwndFrame;
1059     pOleMenuDescriptor->hwndActiveObject = hwndActiveObject;
1060
1061     GlobalUnlock( hOleMenu );
1062     pOleMenuDescriptor = NULL;
1063     
1064     /* Add a menu descriptor windows property to the frame window */
1065     SetPropA( hwndFrame, "PROP_OLEMenuDescriptor", hOleMenu );
1066
1067     /* Install thread scope message hooks for WH_GETMESSAGE and WH_CALLWNDPROC */
1068     if ( !OLEMenu_InstallHooks( GetCurrentThreadId() ) )
1069       return E_FAIL;
1070   }
1071   else  /* Want to uninstall dispatching code */
1072   {
1073     /* Uninstall the hooks */
1074     if ( !OLEMenu_UnInstallHooks( GetCurrentThreadId() ) )
1075       return E_FAIL;
1076     
1077     /* Remove the menu descriptor property from the frame window */
1078     RemovePropA( hwndFrame, "PROP_OLEMenuDescriptor" );
1079   }
1080       
1081   return S_OK;
1082 }
1083
1084 /***********************************************************************
1085  * ReleaseStgMedium [OLE32.140]
1086  */
1087 void WINAPI ReleaseStgMedium(
1088   STGMEDIUM* pmedium)
1089 {
1090   switch (pmedium->tymed)
1091   {
1092     case TYMED_HGLOBAL:
1093     {
1094       if ( (pmedium->pUnkForRelease==0) && 
1095            (pmedium->u.hGlobal!=0) )
1096         GlobalFree(pmedium->u.hGlobal);
1097
1098       pmedium->u.hGlobal = 0;
1099       break;
1100     }
1101     case TYMED_FILE:
1102     {
1103       if (pmedium->u.lpszFileName!=0)
1104       {
1105         if (pmedium->pUnkForRelease==0)
1106         {
1107           DeleteFileW(pmedium->u.lpszFileName);
1108         }
1109         
1110         CoTaskMemFree(pmedium->u.lpszFileName);
1111       }
1112
1113       pmedium->u.lpszFileName = 0;
1114       break;
1115     }
1116     case TYMED_ISTREAM:
1117     {
1118       if (pmedium->u.pstm!=0)
1119       {
1120         IStream_Release(pmedium->u.pstm);
1121       }
1122
1123       pmedium->u.pstm = 0;
1124       break;
1125     }
1126     case TYMED_ISTORAGE:
1127     {
1128       if (pmedium->u.pstg!=0)
1129       {
1130         IStorage_Release(pmedium->u.pstg);
1131       }
1132
1133       pmedium->u.pstg = 0;
1134       break;
1135     }
1136     case TYMED_GDI:
1137     {
1138       if ( (pmedium->pUnkForRelease==0) && 
1139            (pmedium->u.hGlobal!=0) )
1140         DeleteObject(pmedium->u.hGlobal);
1141
1142       pmedium->u.hGlobal = 0;
1143       break;
1144     }
1145     case TYMED_MFPICT:
1146     {
1147       if ( (pmedium->pUnkForRelease==0) && 
1148            (pmedium->u.hMetaFilePict!=0) )
1149       {
1150         DeleteMetaFile(pmedium->u.hMetaFilePict);
1151         GlobalFree(pmedium->u.hMetaFilePict);
1152       }
1153
1154       pmedium->u.hMetaFilePict = 0;
1155       break;
1156     }
1157     case TYMED_ENHMF:
1158     {
1159       if ( (pmedium->pUnkForRelease==0) && 
1160            (pmedium->u.hEnhMetaFile!=0) )
1161       {
1162         DeleteEnhMetaFile(pmedium->u.hEnhMetaFile);
1163       }
1164
1165       pmedium->u.hEnhMetaFile = 0;
1166       break;
1167     }
1168     case TYMED_NULL:
1169     default:
1170       break;
1171   }
1172
1173   /*
1174    * After cleaning up, the unknown is released
1175    */
1176   if (pmedium->pUnkForRelease!=0)
1177   {
1178     IUnknown_Release(pmedium->pUnkForRelease);
1179     pmedium->pUnkForRelease = 0;
1180   }
1181 }
1182
1183 /***
1184  * OLEDD_Initialize()
1185  *
1186  * Initializes the OLE drag and drop data structures.
1187  */
1188 static void OLEDD_Initialize()
1189 {
1190     WNDCLASSA wndClass;
1191
1192     ZeroMemory (&wndClass, sizeof(WNDCLASSA));
1193     wndClass.style         = CS_GLOBALCLASS;
1194     wndClass.lpfnWndProc   = (WNDPROC)OLEDD_DragTrackerWindowProc;
1195     wndClass.cbClsExtra    = 0;
1196     wndClass.cbWndExtra    = sizeof(TrackerWindowInfo*);
1197     wndClass.hCursor       = 0;
1198     wndClass.hbrBackground = 0;
1199     wndClass.lpszClassName = OLEDD_DRAGTRACKERCLASS;
1200  
1201     RegisterClassA (&wndClass);
1202 }
1203
1204 /***
1205  * OLEDD_UnInitialize()
1206  *
1207  * Releases the OLE drag and drop data structures.
1208  */
1209 static void OLEDD_UnInitialize()
1210 {
1211   /*
1212    * Simply empty the list.
1213    */
1214   while (targetListHead!=NULL)
1215   {
1216     RevokeDragDrop(targetListHead->hwndTarget);
1217   }
1218 }
1219
1220 /***
1221  * OLEDD_InsertDropTarget()
1222  *
1223  * Insert the target node in the tree.
1224  */
1225 static void OLEDD_InsertDropTarget(DropTargetNode* nodeToAdd)
1226 {
1227   DropTargetNode*  curNode;
1228   DropTargetNode** parentNodeLink;
1229
1230   /*
1231    * Iterate the tree to find the insertion point.
1232    */
1233   curNode        = targetListHead;
1234   parentNodeLink = &targetListHead;
1235
1236   while (curNode!=NULL)
1237   {
1238     if (nodeToAdd->hwndTarget<curNode->hwndTarget)
1239     {
1240       /*
1241        * If the node we want to add has a smaller HWND, go left
1242        */
1243       parentNodeLink = &curNode->prevDropTarget;
1244       curNode        =  curNode->prevDropTarget;
1245     }
1246     else if (nodeToAdd->hwndTarget>curNode->hwndTarget)
1247     {
1248       /*
1249        * If the node we want to add has a larger HWND, go right
1250        */
1251       parentNodeLink = &curNode->nextDropTarget;
1252       curNode        =  curNode->nextDropTarget;
1253     }
1254     else
1255     {
1256       /*
1257        * The item was found in the list. It shouldn't have been there
1258        */
1259       assert(FALSE);
1260       return;
1261     }
1262   }
1263
1264   /*
1265    * If we get here, we have found a spot for our item. The parentNodeLink
1266    * pointer points to the pointer that we have to modify. 
1267    * The curNode should be NULL. We just have to establish the link and Voila!
1268    */
1269   assert(curNode==NULL);
1270   assert(parentNodeLink!=NULL);
1271   assert(*parentNodeLink==NULL);
1272
1273   *parentNodeLink=nodeToAdd;
1274 }
1275
1276 /***
1277  * OLEDD_ExtractDropTarget()
1278  *
1279  * Removes the target node from the tree.
1280  */
1281 static DropTargetNode* OLEDD_ExtractDropTarget(HWND hwndOfTarget)
1282 {
1283   DropTargetNode*  curNode;
1284   DropTargetNode** parentNodeLink;
1285
1286   /*
1287    * Iterate the tree to find the insertion point.
1288    */
1289   curNode        = targetListHead;
1290   parentNodeLink = &targetListHead;
1291
1292   while (curNode!=NULL)
1293   {
1294     if (hwndOfTarget<curNode->hwndTarget)
1295     {
1296       /*
1297        * If the node we want to add has a smaller HWND, go left
1298        */
1299       parentNodeLink = &curNode->prevDropTarget;
1300       curNode        =  curNode->prevDropTarget;
1301     }
1302     else if (hwndOfTarget>curNode->hwndTarget)
1303     {
1304       /*
1305        * If the node we want to add has a larger HWND, go right
1306        */
1307       parentNodeLink = &curNode->nextDropTarget;
1308       curNode        =  curNode->nextDropTarget;
1309     }
1310     else
1311     {
1312       /*
1313        * The item was found in the list. Detach it from it's parent and 
1314        * re-insert it's kids in the tree.
1315        */
1316       assert(parentNodeLink!=NULL);
1317       assert(*parentNodeLink==curNode);
1318
1319       /*
1320        * We arbitrately re-attach the left sub-tree to the parent.
1321        */
1322       *parentNodeLink = curNode->prevDropTarget;
1323
1324       /*
1325        * And we re-insert the right subtree
1326        */
1327       if (curNode->nextDropTarget!=NULL)
1328       {
1329         OLEDD_InsertDropTarget(curNode->nextDropTarget);
1330       }
1331
1332       /*
1333        * The node we found is still a valid node once we complete
1334        * the unlinking of the kids.
1335        */
1336       curNode->nextDropTarget=NULL;
1337       curNode->prevDropTarget=NULL;
1338
1339       return curNode;
1340     }
1341   }
1342
1343   /*
1344    * If we get here, the node is not in the tree
1345    */
1346   return NULL;
1347 }
1348
1349 /***
1350  * OLEDD_FindDropTarget()
1351  *
1352  * Finds information about the drop target.
1353  */
1354 static DropTargetNode* OLEDD_FindDropTarget(HWND hwndOfTarget)
1355 {
1356   DropTargetNode*  curNode;
1357
1358   /*
1359    * Iterate the tree to find the HWND value.
1360    */
1361   curNode        = targetListHead;
1362
1363   while (curNode!=NULL)
1364   {
1365     if (hwndOfTarget<curNode->hwndTarget)
1366     {
1367       /*
1368        * If the node we want to add has a smaller HWND, go left
1369        */
1370       curNode =  curNode->prevDropTarget;
1371     }
1372     else if (hwndOfTarget>curNode->hwndTarget)
1373     {
1374       /*
1375        * If the node we want to add has a larger HWND, go right
1376        */
1377       curNode =  curNode->nextDropTarget;
1378     }
1379     else
1380     {
1381       /*
1382        * The item was found in the list.
1383        */
1384       return curNode;
1385     }
1386   }
1387
1388   /*
1389    * If we get here, the item is not in the list
1390    */
1391   return NULL;
1392 }
1393
1394 /***
1395  * OLEDD_DragTrackerWindowProc()
1396  *
1397  * This method is the WindowProcedure of the drag n drop tracking
1398  * window. During a drag n Drop operation, an invisible window is created
1399  * to receive the user input and act upon it. This procedure is in charge
1400  * of this behavior.
1401  */
1402 static LRESULT WINAPI OLEDD_DragTrackerWindowProc(
1403                          HWND   hwnd, 
1404                          UINT   uMsg,
1405                          WPARAM wParam, 
1406                          LPARAM   lParam)
1407 {
1408   switch (uMsg)
1409   {
1410     case WM_CREATE:
1411     {
1412       LPCREATESTRUCTA createStruct = (LPCREATESTRUCTA)lParam;
1413
1414       SetWindowLongA(hwnd, 0, (LONG)createStruct->lpCreateParams); 
1415
1416       
1417       break;
1418     }
1419     case WM_MOUSEMOVE:
1420     {
1421       TrackerWindowInfo* trackerInfo = (TrackerWindowInfo*)GetWindowLongA(hwnd, 0);
1422       POINT            mousePos;
1423
1424       /*
1425        * Get the current mouse position in screen coordinates.
1426        */
1427       mousePos.x = LOWORD(lParam);
1428       mousePos.y = HIWORD(lParam);
1429       ClientToScreen(hwnd, &mousePos);
1430
1431       /*
1432        * Track the movement of the mouse.
1433        */
1434       OLEDD_TrackMouseMove(trackerInfo, mousePos, wParam);
1435
1436       break;
1437     }
1438     case WM_LBUTTONUP:
1439     case WM_MBUTTONUP:
1440     case WM_RBUTTONUP:
1441     case WM_LBUTTONDOWN:
1442     case WM_MBUTTONDOWN:
1443     case WM_RBUTTONDOWN:
1444     {
1445       TrackerWindowInfo* trackerInfo = (TrackerWindowInfo*)GetWindowLongA(hwnd, 0);
1446       POINT            mousePos;
1447
1448       /*
1449        * Get the current mouse position in screen coordinates.
1450        */
1451       mousePos.x = LOWORD(lParam);
1452       mousePos.y = HIWORD(lParam);
1453       ClientToScreen(hwnd, &mousePos);
1454
1455       /*
1456        * Notify everyone that the button state changed
1457        * TODO: Check if the "escape" key was pressed.
1458        */
1459       OLEDD_TrackStateChange(trackerInfo, mousePos, wParam);
1460
1461       break;
1462     }
1463   }
1464
1465   /*
1466    * This is a window proc after all. Let's call the default.
1467    */
1468   return DefWindowProcA (hwnd, uMsg, wParam, lParam);
1469 }
1470
1471 /***
1472  * OLEDD_TrackMouseMove()
1473  *
1474  * This method is invoked while a drag and drop operation is in effect.
1475  * it will generate the appropriate callbacks in the drop source
1476  * and drop target. It will also provide the expected feedback to
1477  * the user.
1478  *
1479  * params:
1480  *    trackerInfo - Pointer to the structure identifying the
1481  *                  drag & drop operation that is currently
1482  *                  active.
1483  *    mousePos    - Current position of the mouse in screen
1484  *                  coordinates.
1485  *    keyState    - Contains the state of the shift keys and the
1486  *                  mouse buttons (MK_LBUTTON and the like)
1487  */
1488 static void OLEDD_TrackMouseMove(
1489   TrackerWindowInfo* trackerInfo,
1490   POINT            mousePos,
1491   DWORD              keyState)
1492 {
1493   HWND   hwndNewTarget = 0;
1494   HRESULT  hr = S_OK;
1495
1496   /*
1497    * Get the handle of the window under the mouse
1498    */
1499   hwndNewTarget = WindowFromPoint(mousePos);
1500
1501   /*
1502    * Every time, we re-initialize the effects passed to the
1503    * IDropTarget to the effects allowed by the source.
1504    */
1505   *trackerInfo->pdwEffect = trackerInfo->dwOKEffect;
1506
1507   /*
1508    * If we are hovering over the same target as before, send the
1509    * DragOver notification
1510    */
1511   if ( (trackerInfo->curDragTarget != 0) && 
1512        (trackerInfo->curDragTargetHWND==hwndNewTarget) )
1513   {
1514     POINTL  mousePosParam;
1515     
1516     /*
1517      * The documentation tells me that the coordinate should be in the target
1518      * window's coordinate space. However, the tests I made tell me the
1519      * coordinates should be in screen coordinates.
1520      */
1521     mousePosParam.x = mousePos.x;
1522     mousePosParam.y = mousePos.y;
1523     
1524     IDropTarget_DragOver(trackerInfo->curDragTarget,
1525                          keyState,
1526                          mousePosParam,
1527                          trackerInfo->pdwEffect);
1528   }
1529   else
1530   {
1531     DropTargetNode* newDropTargetNode = 0;
1532     
1533     /*
1534      * If we changed window, we have to notify our old target and check for
1535      * the new one.
1536      */
1537     if (trackerInfo->curDragTarget!=0)
1538     {
1539       IDropTarget_DragLeave(trackerInfo->curDragTarget);
1540     }
1541     
1542     /*
1543      * Make sure we're hovering over a window.
1544      */
1545     if (hwndNewTarget!=0)
1546     {
1547       /*
1548        * Find-out if there is a drag target under the mouse
1549        */
1550       newDropTargetNode = OLEDD_FindDropTarget(hwndNewTarget);
1551       
1552       trackerInfo->curDragTargetHWND = hwndNewTarget;
1553       trackerInfo->curDragTarget     = newDropTargetNode ? newDropTargetNode->dropTarget : 0;
1554       
1555       /*
1556        * If there is, notify it that we just dragged-in
1557        */
1558       if (trackerInfo->curDragTarget!=0)
1559       {
1560         POINTL  mousePosParam;
1561         
1562         /*
1563          * The documentation tells me that the coordinate should be in the target
1564          * window's coordinate space. However, the tests I made tell me the
1565          * coordinates should be in screen coordinates.
1566          */
1567         mousePosParam.x = mousePos.x;
1568         mousePosParam.y = mousePos.y;
1569         
1570         IDropTarget_DragEnter(trackerInfo->curDragTarget,
1571                               trackerInfo->dataObject,
1572                               keyState,
1573                               mousePosParam,
1574                               trackerInfo->pdwEffect);
1575       }
1576     }
1577     else
1578     {
1579       /*
1580        * The mouse is not over a window so we don't track anything.
1581        */
1582       trackerInfo->curDragTargetHWND = 0;
1583       trackerInfo->curDragTarget     = 0;
1584     }
1585   }
1586
1587   /*
1588    * Now that we have done that, we have to tell the source to give 
1589    * us feedback on the work being done by the target.  If we don't 
1590    * have a target, simulate no effect.
1591    */
1592   if (trackerInfo->curDragTarget==0)
1593   {
1594     *trackerInfo->pdwEffect = DROPEFFECT_NONE;
1595   }
1596
1597   hr = IDropSource_GiveFeedback(trackerInfo->dropSource,
1598                                 *trackerInfo->pdwEffect);
1599
1600   /*
1601    * When we ask for feedback from the drop source, sometimes it will
1602    * do all the necessary work and sometimes it will not handle it
1603    * when that's the case, we must display the standard drag and drop
1604    * cursors.
1605    */
1606   if (hr==DRAGDROP_S_USEDEFAULTCURSORS)
1607   {
1608     if ( (*trackerInfo->pdwEffect & DROPEFFECT_MOVE) ||
1609          (*trackerInfo->pdwEffect & DROPEFFECT_COPY) ||
1610          (*trackerInfo->pdwEffect & DROPEFFECT_LINK) )
1611     {
1612       SetCursor(LoadCursorA(0, IDC_SIZEALLA));
1613     }
1614     else
1615     {
1616       SetCursor(LoadCursorA(0, IDC_NOA));
1617     }
1618   }  
1619 }
1620
1621 /***
1622  * OLEDD_TrackStateChange()
1623  *
1624  * This method is invoked while a drag and drop operation is in effect.
1625  * It is used to notify the drop target/drop source callbacks when
1626  * the state of the keyboard or mouse button change.
1627  *
1628  * params:
1629  *    trackerInfo - Pointer to the structure identifying the
1630  *                  drag & drop operation that is currently
1631  *                  active.
1632  *    mousePos    - Current position of the mouse in screen
1633  *                  coordinates.
1634  *    keyState    - Contains the state of the shift keys and the
1635  *                  mouse buttons (MK_LBUTTON and the like)
1636  */
1637 static void OLEDD_TrackStateChange(
1638   TrackerWindowInfo* trackerInfo,
1639   POINT            mousePos,
1640   DWORD              keyState)
1641 {
1642   /*
1643    * Ask the drop source what to do with the operation.
1644    */
1645   trackerInfo->returnValue = IDropSource_QueryContinueDrag(
1646                                trackerInfo->dropSource,
1647                                trackerInfo->escPressed, 
1648                                keyState);
1649   
1650   /*
1651    * All the return valued will stop the operation except the S_OK
1652    * return value.
1653    */
1654   if (trackerInfo->returnValue!=S_OK)
1655   {
1656     /*
1657      * Make sure the message loop in DoDragDrop stops
1658      */
1659     trackerInfo->trackingDone = TRUE;
1660
1661     /*
1662      * Release the mouse in case the drop target decides to show a popup 
1663      * or a menu or something.
1664      */
1665     ReleaseCapture();
1666     
1667     /*
1668      * If we end-up over a target, drop the object in the target or 
1669      * inform the target that the operation was cancelled.
1670      */
1671     if (trackerInfo->curDragTarget!=0)
1672     {
1673       switch (trackerInfo->returnValue)
1674       {
1675         /*
1676          * If the source wants us to complete the operation, we tell 
1677          * the drop target that we just dropped the object in it.
1678          */
1679         case DRAGDROP_S_DROP:
1680         {
1681           POINTL  mousePosParam;
1682         
1683           /*
1684            * The documentation tells me that the coordinate should be 
1685            * in the target window's coordinate space. However, the tests
1686            * I made tell me the coordinates should be in screen coordinates.
1687            */
1688           mousePosParam.x = mousePos.x;
1689           mousePosParam.y = mousePos.y;
1690           
1691           IDropTarget_Drop(trackerInfo->curDragTarget,
1692                            trackerInfo->dataObject,
1693                            keyState,
1694                            mousePosParam,
1695                            trackerInfo->pdwEffect);
1696           break;
1697         }
1698         /*
1699          * If the source told us that we should cancel, fool the drop 
1700          * target by telling it that the mouse left it's window.
1701          */
1702         case DRAGDROP_S_CANCEL:
1703           IDropTarget_DragLeave(trackerInfo->curDragTarget);
1704           break;
1705       }
1706     }
1707   }
1708 }
1709
1710 /***
1711  * OLEDD_GetButtonState()
1712  *
1713  * This method will use the current state of the keyboard to build
1714  * a button state mask equivalent to the one passed in the
1715  * WM_MOUSEMOVE wParam.
1716  */
1717 static DWORD OLEDD_GetButtonState()
1718 {
1719   BYTE  keyboardState[256];
1720   DWORD keyMask = 0;
1721
1722   GetKeyboardState(keyboardState);
1723
1724   if ( (keyboardState[VK_SHIFT] & 0x80) !=0)
1725     keyMask |= MK_SHIFT;
1726
1727   if ( (keyboardState[VK_CONTROL] & 0x80) !=0)
1728     keyMask |= MK_CONTROL;
1729
1730   if ( (keyboardState[VK_LBUTTON] & 0x80) !=0)
1731     keyMask |= MK_LBUTTON;
1732
1733   if ( (keyboardState[VK_RBUTTON] & 0x80) !=0)
1734     keyMask |= MK_RBUTTON;
1735
1736   if ( (keyboardState[VK_MBUTTON] & 0x80) !=0)
1737     keyMask |= MK_MBUTTON;
1738
1739   return keyMask;
1740 }
1741
1742