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