mirror of
git://source.winehq.org/git/wine.git
synced 2024-10-31 12:54:13 +00:00
1736 lines
45 KiB
C
1736 lines
45 KiB
C
/*
|
|
* OLE2 library
|
|
*
|
|
* Copyright 1995 Martin von Loewis
|
|
* Copyright 1999 Francis Beaudet
|
|
* Copyright 1999 Noel Borthwick
|
|
*/
|
|
|
|
#include <assert.h>
|
|
|
|
#include "winuser.h"
|
|
#include "winerror.h"
|
|
#include "ole2.h"
|
|
#include "process.h"
|
|
#include "hook.h"
|
|
#include "commctrl.h"
|
|
#include "wine/obj_clientserver.h"
|
|
#include "debug.h"
|
|
#include "ole2ver.h"
|
|
|
|
DEFAULT_DEBUG_CHANNEL(ole)
|
|
|
|
/******************************************************************************
|
|
* These are static/global variables and internal data structures that the
|
|
* OLE module uses to maintain it's state.
|
|
*/
|
|
typedef struct tagDropTargetNode
|
|
{
|
|
HWND hwndTarget;
|
|
IDropTarget* dropTarget;
|
|
struct tagDropTargetNode* prevDropTarget;
|
|
struct tagDropTargetNode* nextDropTarget;
|
|
} DropTargetNode;
|
|
|
|
typedef struct tagTrackerWindowInfo
|
|
{
|
|
IDataObject* dataObject;
|
|
IDropSource* dropSource;
|
|
DWORD dwOKEffect;
|
|
DWORD* pdwEffect;
|
|
BOOL trackingDone;
|
|
HRESULT returnValue;
|
|
|
|
BOOL escPressed;
|
|
HWND curDragTargetHWND;
|
|
IDropTarget* curDragTarget;
|
|
} TrackerWindowInfo;
|
|
|
|
typedef struct tagOleMenuDescriptor /* OleMenuDescriptor */
|
|
{
|
|
HWND hwndFrame; /* The containers frame window */
|
|
HWND hwndActiveObject; /* The active objects window */
|
|
OLEMENUGROUPWIDTHS mgw; /* OLE menu group widths for the shared menu */
|
|
HMENU hmenuCombined; /* The combined menu */
|
|
BOOL bIsServerItem; /* True if the currently open popup belongs to the server */
|
|
} OleMenuDescriptor;
|
|
|
|
typedef struct tagOleMenuHookItem /* OleMenu hook item in per thread hook list */
|
|
{
|
|
DWORD tid; /* Thread Id */
|
|
HANDLE hHeap; /* Heap this is allocated from */
|
|
HHOOK GetMsg_hHook; /* message hook for WH_GETMESSAGE */
|
|
HHOOK CallWndProc_hHook; /* message hook for WH_CALLWNDPROC */
|
|
} OleMenuHookItem;
|
|
|
|
/*
|
|
* Dynamic pointer array of per thread message hooks (maintained by OleSetMenuDescriptor)
|
|
*/
|
|
static HDPA OLEMenu_MsgHookDPA = NULL;
|
|
|
|
/*
|
|
* This is the lock count on the OLE library. It is controlled by the
|
|
* OLEInitialize/OLEUninitialize methods.
|
|
*/
|
|
static ULONG OLE_moduleLockCount = 0;
|
|
|
|
/*
|
|
* Name of our registered window class.
|
|
*/
|
|
static const char OLEDD_DRAGTRACKERCLASS[] = "WineDragDropTracker32";
|
|
|
|
/*
|
|
* This is the head of the Drop target container.
|
|
*/
|
|
static DropTargetNode* targetListHead = NULL;
|
|
|
|
/******************************************************************************
|
|
* These are the prototypes of the utility methods used to manage a shared menu
|
|
*/
|
|
static void OLEMenu_Initialize();
|
|
static void OLEMenu_UnInitialize();
|
|
BOOL OLEMenu_InstallHooks( DWORD tid );
|
|
BOOL OLEMenu_UnInstallHooks( DWORD tid );
|
|
OleMenuHookItem * OLEMenu_IsHookInstalled( DWORD tid, INT *pixHook );
|
|
static BOOL OLEMenu_FindMainMenuIndex( HMENU hMainMenu, HMENU hPopupMenu, UINT *pnPos );
|
|
BOOL OLEMenu_SetIsServerMenu( HMENU hmenu, OleMenuDescriptor *pOleMenuDescriptor );
|
|
LRESULT CALLBACK OLEMenu_CallWndProc(INT code, WPARAM wParam, LPARAM lParam);
|
|
LRESULT CALLBACK OLEMenu_GetMsgProc(INT code, WPARAM wParam, LPARAM lParam);
|
|
|
|
/******************************************************************************
|
|
* These are the prototypes of the utility methods used for OLE Drag n Drop
|
|
*/
|
|
static void OLEDD_Initialize();
|
|
static void OLEDD_UnInitialize();
|
|
static void OLEDD_InsertDropTarget(
|
|
DropTargetNode* nodeToAdd);
|
|
static DropTargetNode* OLEDD_ExtractDropTarget(
|
|
HWND hwndOfTarget);
|
|
static DropTargetNode* OLEDD_FindDropTarget(
|
|
HWND hwndOfTarget);
|
|
static LRESULT WINAPI OLEDD_DragTrackerWindowProc(
|
|
HWND hwnd,
|
|
UINT uMsg,
|
|
WPARAM wParam,
|
|
LPARAM lParam);
|
|
static void OLEDD_TrackMouseMove(
|
|
TrackerWindowInfo* trackerInfo,
|
|
POINT mousePos,
|
|
DWORD keyState);
|
|
static void OLEDD_TrackStateChange(
|
|
TrackerWindowInfo* trackerInfo,
|
|
POINT mousePos,
|
|
DWORD keyState);
|
|
static DWORD OLEDD_GetButtonState();
|
|
|
|
|
|
/******************************************************************************
|
|
* OleBuildVersion [OLE2.1]
|
|
*/
|
|
DWORD WINAPI OleBuildVersion(void)
|
|
{
|
|
TRACE(ole,"(void)\n");
|
|
return (rmm<<16)+rup;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* OleInitialize (OLE2.2) (OLE32.108)
|
|
*/
|
|
HRESULT WINAPI OleInitialize(LPVOID reserved)
|
|
{
|
|
HRESULT hr;
|
|
|
|
TRACE(ole, "(%p)\n", reserved);
|
|
|
|
/*
|
|
* The first duty of the OleInitialize is to initialize the COM libraries.
|
|
*/
|
|
hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
|
|
|
|
/*
|
|
* If the CoInitializeEx call failed, the OLE libraries can't be
|
|
* initialized.
|
|
*/
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
/*
|
|
* Then, it has to initialize the OLE specific modules.
|
|
* This includes:
|
|
* Clipboard
|
|
* Drag and Drop
|
|
* Object linking and Embedding
|
|
* In-place activation
|
|
*/
|
|
if (OLE_moduleLockCount==0)
|
|
{
|
|
/*
|
|
* Initialize the libraries.
|
|
*/
|
|
TRACE(ole, "() - Initializing the OLE libraries\n");
|
|
|
|
/*
|
|
* Drag and Drop
|
|
*/
|
|
OLEDD_Initialize();
|
|
|
|
/*
|
|
* OLE shared menu
|
|
*/
|
|
OLEMenu_Initialize();
|
|
}
|
|
|
|
/*
|
|
* Then, we increase the lock count on the OLE module.
|
|
*/
|
|
OLE_moduleLockCount++;
|
|
|
|
return hr;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* CoGetCurrentProcess [COMPOBJ.34] [OLE2.2][OLE32.108]
|
|
*
|
|
* NOTES
|
|
* Is DWORD really the correct return type for this function?
|
|
*/
|
|
DWORD WINAPI CoGetCurrentProcess(void) {
|
|
return (DWORD)PROCESS_Current();
|
|
}
|
|
|
|
/******************************************************************************
|
|
* OleUninitialize [OLE2.3] [OLE32.131]
|
|
*/
|
|
void WINAPI OleUninitialize(void)
|
|
{
|
|
TRACE(ole, "()\n");
|
|
|
|
/*
|
|
* Decrease the lock count on the OLE module.
|
|
*/
|
|
OLE_moduleLockCount--;
|
|
|
|
/*
|
|
* If we hit the bottom of the lock stack, free the libraries.
|
|
*/
|
|
if (OLE_moduleLockCount==0)
|
|
{
|
|
/*
|
|
* Actually free the libraries.
|
|
*/
|
|
TRACE(ole, "() - Freeing the last reference count\n");
|
|
|
|
/*
|
|
* Drag and Drop
|
|
*/
|
|
OLEDD_UnInitialize();
|
|
|
|
/*
|
|
* OLE shared menu
|
|
*/
|
|
OLEMenu_UnInitialize();
|
|
}
|
|
|
|
/*
|
|
* Then, uninitialize the COM libraries.
|
|
*/
|
|
CoUninitialize();
|
|
}
|
|
|
|
/***********************************************************************
|
|
* OleFlushClipboard [OLE2.76]
|
|
*/
|
|
HRESULT WINAPI OleFlushClipboard16(void)
|
|
{
|
|
return S_OK;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* OleSetClipboard [OLE32.127]
|
|
*/
|
|
HRESULT WINAPI OleSetClipboard(LPVOID pDataObj)
|
|
{
|
|
FIXME(ole,"(%p), stub!\n", pDataObj);
|
|
return S_OK;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* CoRegisterMessageFilter32 [OLE32.38]
|
|
*/
|
|
HRESULT WINAPI CoRegisterMessageFilter(
|
|
LPMESSAGEFILTER lpMessageFilter, /* Pointer to interface */
|
|
LPMESSAGEFILTER *lplpMessageFilter /* Indirect pointer to prior instance if non-NULL */
|
|
) {
|
|
FIXME(ole,"stub\n");
|
|
if (lplpMessageFilter) {
|
|
*lplpMessageFilter = NULL;
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* OleInitializeWOW [OLE32.109]
|
|
*/
|
|
HRESULT WINAPI OleInitializeWOW(DWORD x) {
|
|
FIXME(ole,"(0x%08lx),stub!\n",x);
|
|
return 0;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* RegisterDragDrop16 (OLE2.35)
|
|
*/
|
|
HRESULT WINAPI RegisterDragDrop16(
|
|
HWND16 hwnd,
|
|
LPDROPTARGET pDropTarget
|
|
) {
|
|
FIXME(ole,"(0x%04x,%p),stub!\n",hwnd,pDropTarget);
|
|
return S_OK;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* RegisterDragDrop32 (OLE32.139)
|
|
*/
|
|
HRESULT WINAPI RegisterDragDrop(
|
|
HWND hwnd,
|
|
LPDROPTARGET pDropTarget)
|
|
{
|
|
DropTargetNode* dropTargetInfo;
|
|
|
|
TRACE(ole,"(0x%x,%p)\n", hwnd, pDropTarget);
|
|
|
|
/*
|
|
* First, check if the window is already registered.
|
|
*/
|
|
dropTargetInfo = OLEDD_FindDropTarget(hwnd);
|
|
|
|
if (dropTargetInfo!=NULL)
|
|
return DRAGDROP_E_ALREADYREGISTERED;
|
|
|
|
/*
|
|
* If it's not there, we can add it. We first create a node for it.
|
|
*/
|
|
dropTargetInfo = HeapAlloc(GetProcessHeap(), 0, sizeof(DropTargetNode));
|
|
|
|
if (dropTargetInfo==NULL)
|
|
return E_OUTOFMEMORY;
|
|
|
|
dropTargetInfo->hwndTarget = hwnd;
|
|
dropTargetInfo->prevDropTarget = NULL;
|
|
dropTargetInfo->nextDropTarget = NULL;
|
|
|
|
/*
|
|
* Don't forget that this is an interface pointer, need to nail it down since
|
|
* we keep a copy of it.
|
|
*/
|
|
dropTargetInfo->dropTarget = pDropTarget;
|
|
IDropTarget_AddRef(dropTargetInfo->dropTarget);
|
|
|
|
OLEDD_InsertDropTarget(dropTargetInfo);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* RevokeDragDrop16 (OLE2.36)
|
|
*/
|
|
HRESULT WINAPI RevokeDragDrop16(
|
|
HWND16 hwnd
|
|
) {
|
|
FIXME(ole,"(0x%04x),stub!\n",hwnd);
|
|
return S_OK;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* RevokeDragDrop32 (OLE32.141)
|
|
*/
|
|
HRESULT WINAPI RevokeDragDrop(
|
|
HWND hwnd)
|
|
{
|
|
DropTargetNode* dropTargetInfo;
|
|
|
|
TRACE(ole,"(0x%x)\n", hwnd);
|
|
|
|
/*
|
|
* First, check if the window is already registered.
|
|
*/
|
|
dropTargetInfo = OLEDD_ExtractDropTarget(hwnd);
|
|
|
|
/*
|
|
* If it ain't in there, it's an error.
|
|
*/
|
|
if (dropTargetInfo==NULL)
|
|
return DRAGDROP_E_NOTREGISTERED;
|
|
|
|
/*
|
|
* If it's in there, clean-up it's used memory and
|
|
* references
|
|
*/
|
|
IDropTarget_Release(dropTargetInfo->dropTarget);
|
|
HeapFree(GetProcessHeap(), 0, dropTargetInfo);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* OleRegGetUserType (OLE32.122)
|
|
*/
|
|
HRESULT WINAPI OleRegGetUserType(
|
|
REFCLSID clsid,
|
|
DWORD dwFormOfType,
|
|
LPOLESTR* pszUserType)
|
|
{
|
|
FIXME(ole,",stub!\n");
|
|
return S_OK;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* DoDragDrop32 [OLE32.65]
|
|
*/
|
|
HRESULT WINAPI DoDragDrop (
|
|
IDataObject *pDataObject, /* ptr to the data obj */
|
|
IDropSource* pDropSource, /* ptr to the source obj */
|
|
DWORD dwOKEffect, /* effects allowed by the source */
|
|
DWORD *pdwEffect) /* ptr to effects of the source */
|
|
{
|
|
TrackerWindowInfo trackerInfo;
|
|
HWND hwndTrackWindow;
|
|
MSG msg;
|
|
|
|
TRACE(ole,"(DataObject %p, DropSource %p)\n", pDataObject, pDropSource);
|
|
|
|
/*
|
|
* Setup the drag n drop tracking window.
|
|
*/
|
|
trackerInfo.dataObject = pDataObject;
|
|
trackerInfo.dropSource = pDropSource;
|
|
trackerInfo.dwOKEffect = dwOKEffect;
|
|
trackerInfo.pdwEffect = pdwEffect;
|
|
trackerInfo.trackingDone = FALSE;
|
|
trackerInfo.escPressed = FALSE;
|
|
trackerInfo.curDragTargetHWND = 0;
|
|
trackerInfo.curDragTarget = 0;
|
|
|
|
hwndTrackWindow = CreateWindowA(OLEDD_DRAGTRACKERCLASS,
|
|
"TrackerWindow",
|
|
WS_POPUP,
|
|
CW_USEDEFAULT, CW_USEDEFAULT,
|
|
CW_USEDEFAULT, CW_USEDEFAULT,
|
|
0,
|
|
0,
|
|
0,
|
|
(LPVOID)&trackerInfo);
|
|
|
|
if (hwndTrackWindow!=0)
|
|
{
|
|
/*
|
|
* Capture the mouse input
|
|
*/
|
|
SetCapture(hwndTrackWindow);
|
|
|
|
/*
|
|
* Pump messages. All mouse input should go the the capture window.
|
|
*/
|
|
while (!trackerInfo.trackingDone && GetMessageA(&msg, 0, 0, 0) )
|
|
{
|
|
if ( (msg.message >= WM_KEYFIRST) &&
|
|
(msg.message <= WM_KEYFIRST) )
|
|
{
|
|
/*
|
|
* When keyboard messages are sent to windows on this thread, we
|
|
* want to ignore notify the drop source that the state changed.
|
|
* in the case of the Escape key, we also notify the drop source
|
|
* we give it a special meaning.
|
|
*/
|
|
if ( (msg.message==WM_KEYDOWN) &&
|
|
(msg.wParam==VK_ESCAPE) )
|
|
{
|
|
trackerInfo.escPressed = TRUE;
|
|
}
|
|
|
|
/*
|
|
* Notify the drop source.
|
|
*/
|
|
OLEDD_TrackStateChange(&trackerInfo,
|
|
msg.pt,
|
|
OLEDD_GetButtonState());
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* Dispatch the messages only when it's not a keyboard message.
|
|
*/
|
|
DispatchMessageA(&msg);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Destroy the temporary window.
|
|
*/
|
|
DestroyWindow(hwndTrackWindow);
|
|
|
|
return trackerInfo.returnValue;
|
|
}
|
|
|
|
return E_FAIL;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* OleQueryLinkFromData32 [OLE32.118]
|
|
*/
|
|
HRESULT WINAPI OleQueryLinkFromData(
|
|
IDataObject* pSrcDataObject)
|
|
{
|
|
FIXME(ole,"(%p),stub!\n", pSrcDataObject);
|
|
return S_OK;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* OleRegGetMiscStatus [OLE32.121]
|
|
*/
|
|
HRESULT WINAPI OleRegGetMiscStatus(
|
|
REFCLSID clsid,
|
|
DWORD dwAspect,
|
|
DWORD* pdwStatus)
|
|
{
|
|
FIXME(ole,"(),stub!\n");
|
|
return REGDB_E_CLASSNOTREG;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* OleGetClipboard32 [OLE32.105]
|
|
*/
|
|
HRESULT WINAPI OleGetClipboard(
|
|
IDataObject** ppDataObj)
|
|
{
|
|
FIXME(ole,"(%p),stub!\n", ppDataObj);
|
|
|
|
if (ppDataObj)
|
|
*ppDataObj=0;
|
|
|
|
return E_FAIL;
|
|
}
|
|
|
|
|
|
/**************************************************************************
|
|
* Internal methods to manage the shared OLE menu in response to the
|
|
* OLE***MenuDescriptor API
|
|
*/
|
|
|
|
/***
|
|
* OLEMenu_Initialize()
|
|
*
|
|
* Initializes the OLEMENU data structures.
|
|
*/
|
|
static void OLEMenu_Initialize()
|
|
{
|
|
/* Create a dynamic pointer array to store the hook handles */
|
|
if ( !OLEMenu_MsgHookDPA )
|
|
OLEMenu_MsgHookDPA = DPA_CreateEx( 2, GetProcessHeap() );
|
|
}
|
|
|
|
/***
|
|
* OLEMenu_UnInitialize()
|
|
*
|
|
* Releases the OLEMENU data structures.
|
|
*/
|
|
static void OLEMenu_UnInitialize()
|
|
{
|
|
/* Release the hook table */
|
|
if ( OLEMenu_MsgHookDPA )
|
|
DPA_Destroy( OLEMenu_MsgHookDPA );
|
|
|
|
OLEMenu_MsgHookDPA = NULL;
|
|
}
|
|
|
|
/*************************************************************************
|
|
* OLEMenu_InstallHooks
|
|
* Install thread scope message hooks for WH_GETMESSAGE and WH_CALLWNDPROC
|
|
*
|
|
* RETURNS: TRUE if message hooks were succesfully installed
|
|
* FALSE on failure
|
|
*/
|
|
BOOL OLEMenu_InstallHooks( DWORD tid )
|
|
{
|
|
OleMenuHookItem *pHookItem = NULL;
|
|
|
|
if ( !OLEMenu_MsgHookDPA ) /* No hook table? Create one */
|
|
{
|
|
/* Create a dynamic pointer array to store the hook handles */
|
|
if ( !(OLEMenu_MsgHookDPA = DPA_CreateEx( 2, GetProcessHeap() )) )
|
|
return FALSE;
|
|
}
|
|
|
|
/* Create an entry for the hook table */
|
|
if ( !(pHookItem = HeapAlloc(GetProcessHeap(), 0,
|
|
sizeof(OleMenuHookItem)) ) )
|
|
return FALSE;
|
|
|
|
pHookItem->tid = tid;
|
|
pHookItem->hHeap = GetProcessHeap();
|
|
|
|
/* Install a thread scope message hook for WH_GETMESSAGE */
|
|
pHookItem->GetMsg_hHook = SetWindowsHookExA( WH_GETMESSAGE, OLEMenu_GetMsgProc,
|
|
0, GetCurrentThreadId() );
|
|
if ( !pHookItem->GetMsg_hHook )
|
|
goto CLEANUP;
|
|
|
|
/* Install a thread scope message hook for WH_CALLWNDPROC */
|
|
pHookItem->CallWndProc_hHook = SetWindowsHookExA( WH_CALLWNDPROC, OLEMenu_CallWndProc,
|
|
0, GetCurrentThreadId() );
|
|
if ( !pHookItem->CallWndProc_hHook )
|
|
goto CLEANUP;
|
|
|
|
/* Insert the hook table entry */
|
|
if ( -1 == DPA_InsertPtr( OLEMenu_MsgHookDPA, 0, pHookItem ) )
|
|
goto CLEANUP;
|
|
|
|
return TRUE;
|
|
|
|
CLEANUP:
|
|
/* Unhook any hooks */
|
|
if ( pHookItem->GetMsg_hHook )
|
|
UnhookWindowsHookEx( pHookItem->GetMsg_hHook );
|
|
if ( pHookItem->CallWndProc_hHook )
|
|
UnhookWindowsHookEx( pHookItem->CallWndProc_hHook );
|
|
/* Release the hook table entry */
|
|
HeapFree(pHookItem->hHeap, 0, pHookItem );
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/*************************************************************************
|
|
* OLEMenu_UnInstallHooks
|
|
* UnInstall thread scope message hooks for WH_GETMESSAGE and WH_CALLWNDPROC
|
|
*
|
|
* RETURNS: TRUE if message hooks were succesfully installed
|
|
* FALSE on failure
|
|
*/
|
|
BOOL OLEMenu_UnInstallHooks( DWORD tid )
|
|
{
|
|
INT ixHook;
|
|
OleMenuHookItem *pHookItem = NULL;
|
|
|
|
if ( !OLEMenu_MsgHookDPA ) /* No hooks set */
|
|
return TRUE;
|
|
|
|
/* Lookup the hHook index for this tid */
|
|
if ( !OLEMenu_IsHookInstalled( tid , &ixHook ) )
|
|
return TRUE;
|
|
|
|
/* Remove the hook entry from the table(the pointer itself is not deleted) */
|
|
if ( !( pHookItem = DPA_DeletePtr(OLEMenu_MsgHookDPA, ixHook) ) )
|
|
return FALSE;
|
|
|
|
/* Uninstall the hooks installed for this thread */
|
|
if ( !UnhookWindowsHookEx( pHookItem->GetMsg_hHook ) )
|
|
goto CLEANUP;
|
|
if ( !UnhookWindowsHookEx( pHookItem->CallWndProc_hHook ) )
|
|
goto CLEANUP;
|
|
|
|
/* Release the hook table entry */
|
|
HeapFree(pHookItem->hHeap, 0, pHookItem );
|
|
|
|
return TRUE;
|
|
|
|
CLEANUP:
|
|
/* Release the hook table entry */
|
|
if (pHookItem)
|
|
HeapFree(pHookItem->hHeap, 0, pHookItem );
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/*************************************************************************
|
|
* OLEMenu_IsHookInstalled
|
|
* Tests if OLEMenu hooks have been installed for a thread
|
|
*
|
|
* RETURNS: The pointer and index of the hook table entry for the tid
|
|
* NULL and -1 for the index if no hooks were installed for this thread
|
|
*/
|
|
OleMenuHookItem * OLEMenu_IsHookInstalled( DWORD tid, INT *pixHook )
|
|
{
|
|
INT ixHook;
|
|
OleMenuHookItem *pHookItem = NULL;
|
|
|
|
if ( pixHook )
|
|
*pixHook = -1;
|
|
|
|
if ( !OLEMenu_MsgHookDPA ) /* No hooks set */
|
|
return NULL;
|
|
|
|
/* Do a simple linear search for an entry whose tid matches ours.
|
|
* We really need a map but efficiency is not a concern here. */
|
|
for( ixHook = 0; ; ixHook++ )
|
|
{
|
|
/* Retrieve the hook entry */
|
|
if ( !( pHookItem = DPA_GetPtr(OLEMenu_MsgHookDPA, ixHook) ) )
|
|
return NULL;
|
|
|
|
if ( tid == pHookItem->tid )
|
|
{
|
|
if ( pixHook )
|
|
*pixHook = ixHook;
|
|
return pHookItem;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* OLEMenu_FindMainMenuIndex
|
|
*
|
|
* Used by OLEMenu API to find the top level group a menu item belongs to.
|
|
* On success pnPos contains the index of the item in the top level menu group
|
|
*
|
|
* RETURNS: TRUE if the ID was found, FALSE on failure
|
|
*/
|
|
static BOOL OLEMenu_FindMainMenuIndex( HMENU hMainMenu, HMENU hPopupMenu, UINT *pnPos )
|
|
{
|
|
UINT i, nItems;
|
|
|
|
nItems = GetMenuItemCount( hMainMenu );
|
|
|
|
for (i = 0; i < nItems; i++)
|
|
{
|
|
HMENU hsubmenu;
|
|
|
|
/* Is the current item a submenu? */
|
|
if ( (hsubmenu = GetSubMenu(hMainMenu, i)) )
|
|
{
|
|
/* If the handle is the same we're done */
|
|
if ( hsubmenu == hPopupMenu )
|
|
{
|
|
if (pnPos)
|
|
*pnPos = i;
|
|
return TRUE;
|
|
}
|
|
/* Recursively search without updating pnPos */
|
|
else if ( OLEMenu_FindMainMenuIndex( hsubmenu, hPopupMenu, NULL ) )
|
|
{
|
|
if (pnPos)
|
|
*pnPos = i;
|
|
return TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* OLEMenu_SetIsServerMenu
|
|
*
|
|
* Checks whether a popup menu belongs to a shared menu group which is
|
|
* owned by the server, and sets the menu descriptor state accordingly.
|
|
* All menu messages from these groups should be routed to the server.
|
|
*
|
|
* RETURNS: TRUE if the popup menu is part of a server owned group
|
|
* FASE if the popup menu is part of a container owned group
|
|
*/
|
|
BOOL OLEMenu_SetIsServerMenu( HMENU hmenu, OleMenuDescriptor *pOleMenuDescriptor )
|
|
{
|
|
UINT nPos = 0, nWidth, i;
|
|
|
|
pOleMenuDescriptor->bIsServerItem = FALSE;
|
|
|
|
/* Don't bother searching if the popup is the combined menu itself */
|
|
if ( hmenu == pOleMenuDescriptor->hmenuCombined )
|
|
return FALSE;
|
|
|
|
/* Find the menu item index in the shared OLE menu that this item belongs to */
|
|
if ( !OLEMenu_FindMainMenuIndex( pOleMenuDescriptor->hmenuCombined, hmenu, &nPos ) )
|
|
return FALSE;
|
|
|
|
/* The group widths array has counts for the number of elements
|
|
* in the groups File, Edit, Container, Object, Window, Help.
|
|
* The Edit, Object & Help groups belong to the server object
|
|
* and the other three belong to the container.
|
|
* Loop thru the group widths and locate the group we are a member of.
|
|
*/
|
|
for ( i = 0, nWidth = 0; i < 6; i++ )
|
|
{
|
|
nWidth += pOleMenuDescriptor->mgw.width[i];
|
|
if ( nPos < nWidth )
|
|
{
|
|
/* Odd elements are server menu widths */
|
|
pOleMenuDescriptor->bIsServerItem = (i%2) ? TRUE : FALSE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return pOleMenuDescriptor->bIsServerItem;
|
|
}
|
|
|
|
/*************************************************************************
|
|
* OLEMenu_CallWndProc
|
|
* Thread scope WH_CALLWNDPROC hook proc filter function (callback)
|
|
* This is invoked from a message hook installed in OleSetMenuDescriptor.
|
|
*/
|
|
LRESULT CALLBACK OLEMenu_CallWndProc(INT code, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
LPCWPSTRUCT pMsg = NULL;
|
|
HOLEMENU hOleMenu = 0;
|
|
OleMenuDescriptor *pOleMenuDescriptor = NULL;
|
|
OleMenuHookItem *pHookItem = NULL;
|
|
WORD fuFlags;
|
|
|
|
TRACE(ole,"%i, %04x, %08x\n", code, wParam, (unsigned)lParam );
|
|
|
|
/* Check if we're being asked to process the message */
|
|
if ( HC_ACTION != code )
|
|
goto NEXTHOOK;
|
|
|
|
/* Retrieve the current message being dispatched from lParam */
|
|
pMsg = (LPCWPSTRUCT)lParam;
|
|
|
|
/* Check if the message is destined for a window we are interested in:
|
|
* If the window has an OLEMenu property we may need to dispatch
|
|
* the menu message to its active objects window instead. */
|
|
|
|
hOleMenu = (HOLEMENU)GetPropA( pMsg->hwnd, "PROP_OLEMenuDescriptor" );
|
|
if ( !hOleMenu )
|
|
goto NEXTHOOK;
|
|
|
|
/* Get the menu descriptor */
|
|
pOleMenuDescriptor = (OleMenuDescriptor *) GlobalLock( hOleMenu );
|
|
if ( !pOleMenuDescriptor ) /* Bad descriptor! */
|
|
goto NEXTHOOK;
|
|
|
|
/* Process menu messages */
|
|
switch( pMsg->message )
|
|
{
|
|
case WM_INITMENU:
|
|
{
|
|
/* Reset the menu descriptor state */
|
|
pOleMenuDescriptor->bIsServerItem = FALSE;
|
|
|
|
/* Send this message to the server as well */
|
|
SendMessageA( pOleMenuDescriptor->hwndActiveObject,
|
|
pMsg->message, pMsg->wParam, pMsg->lParam );
|
|
goto NEXTHOOK;
|
|
}
|
|
|
|
case WM_INITMENUPOPUP:
|
|
{
|
|
/* Save the state for whether this is a server owned menu */
|
|
OLEMenu_SetIsServerMenu( (HMENU)pMsg->wParam, pOleMenuDescriptor );
|
|
break;
|
|
}
|
|
|
|
case WM_MENUSELECT:
|
|
{
|
|
fuFlags = HIWORD(pMsg->wParam); /* Get flags */
|
|
if ( fuFlags & MF_SYSMENU )
|
|
goto NEXTHOOK;
|
|
|
|
/* Save the state for whether this is a server owned popup menu */
|
|
else if ( fuFlags & MF_POPUP )
|
|
OLEMenu_SetIsServerMenu( (HMENU)pMsg->lParam, pOleMenuDescriptor );
|
|
|
|
break;
|
|
}
|
|
|
|
case WM_DRAWITEM:
|
|
{
|
|
LPDRAWITEMSTRUCT lpdis = (LPDRAWITEMSTRUCT) pMsg->lParam;
|
|
if ( pMsg->wParam != 0 || lpdis->CtlType != ODT_MENU )
|
|
goto NEXTHOOK; /* Not a menu message */
|
|
|
|
break;
|
|
}
|
|
|
|
default:
|
|
goto NEXTHOOK;
|
|
}
|
|
|
|
/* If the message was for the server dispatch it accordingly */
|
|
if ( pOleMenuDescriptor->bIsServerItem )
|
|
{
|
|
SendMessageA( pOleMenuDescriptor->hwndActiveObject,
|
|
pMsg->message, pMsg->wParam, pMsg->lParam );
|
|
}
|
|
|
|
NEXTHOOK:
|
|
if ( pOleMenuDescriptor )
|
|
GlobalUnlock( hOleMenu );
|
|
|
|
/* Lookup the hook item for the current thread */
|
|
if ( !( pHookItem = OLEMenu_IsHookInstalled( GetCurrentThreadId(), NULL ) ) )
|
|
{
|
|
/* This should never fail!! */
|
|
WARN(ole, "could not retrieve hHook for current thread!\n" );
|
|
return 0;
|
|
}
|
|
|
|
/* Pass on the message to the next hooker */
|
|
return CallNextHookEx( pHookItem->CallWndProc_hHook, code, wParam, lParam );
|
|
}
|
|
|
|
/*************************************************************************
|
|
* OLEMenu_GetMsgProc
|
|
* Thread scope WH_GETMESSAGE hook proc filter function (callback)
|
|
* This is invoked from a message hook installed in OleSetMenuDescriptor.
|
|
*/
|
|
LRESULT CALLBACK OLEMenu_GetMsgProc(INT code, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
LPMSG pMsg = NULL;
|
|
HOLEMENU hOleMenu = 0;
|
|
OleMenuDescriptor *pOleMenuDescriptor = NULL;
|
|
OleMenuHookItem *pHookItem = NULL;
|
|
WORD wCode;
|
|
|
|
TRACE(ole,"%i, %04x, %08x\n", code, wParam, (unsigned)lParam );
|
|
|
|
/* Check if we're being asked to process a messages */
|
|
if ( HC_ACTION != code )
|
|
goto NEXTHOOK;
|
|
|
|
/* Retrieve the current message being dispatched from lParam */
|
|
pMsg = (LPMSG)lParam;
|
|
|
|
/* Check if the message is destined for a window we are interested in:
|
|
* If the window has an OLEMenu property we may need to dispatch
|
|
* the menu message to its active objects window instead. */
|
|
|
|
hOleMenu = (HOLEMENU)GetPropA( pMsg->hwnd, "PROP_OLEMenuDescriptor" );
|
|
if ( !hOleMenu )
|
|
goto NEXTHOOK;
|
|
|
|
/* Process menu messages */
|
|
switch( pMsg->message )
|
|
{
|
|
case WM_COMMAND:
|
|
{
|
|
wCode = HIWORD(pMsg->wParam); /* Get notification code */
|
|
if ( wCode )
|
|
goto NEXTHOOK; /* Not a menu message */
|
|
break;
|
|
}
|
|
default:
|
|
goto NEXTHOOK;
|
|
}
|
|
|
|
/* Get the menu descriptor */
|
|
pOleMenuDescriptor = (OleMenuDescriptor *) GlobalLock( hOleMenu );
|
|
if ( !pOleMenuDescriptor ) /* Bad descriptor! */
|
|
goto NEXTHOOK;
|
|
|
|
/* If the message was for the server dispatch it accordingly */
|
|
if ( pOleMenuDescriptor->bIsServerItem )
|
|
{
|
|
/* Change the hWnd in the message to the active objects hWnd.
|
|
* The message loop which reads this message will automatically
|
|
* dispatch it to the embedded objects window. */
|
|
pMsg->hwnd = pOleMenuDescriptor->hwndActiveObject;
|
|
}
|
|
|
|
NEXTHOOK:
|
|
if ( pOleMenuDescriptor )
|
|
GlobalUnlock( hOleMenu );
|
|
|
|
/* Lookup the hook item for the current thread */
|
|
if ( !( pHookItem = OLEMenu_IsHookInstalled( GetCurrentThreadId(), NULL ) ) )
|
|
{
|
|
/* This should never fail!! */
|
|
WARN(ole, "could not retrieve hHook for current thread!\n" );
|
|
return FALSE;
|
|
}
|
|
|
|
/* Pass on the message to the next hooker */
|
|
return CallNextHookEx( pHookItem->GetMsg_hHook, code, wParam, lParam );
|
|
}
|
|
|
|
/***********************************************************************
|
|
* OleCreateMenuDescriptor [OLE32.97]
|
|
* Creates an OLE menu descriptor for OLE to use when dispatching
|
|
* menu messages and commands.
|
|
*
|
|
* PARAMS:
|
|
* hmenuCombined - Handle to the objects combined menu
|
|
* lpMenuWidths - Pointer to array of 6 LONG's indicating menus per group
|
|
*
|
|
*/
|
|
HOLEMENU WINAPI OleCreateMenuDescriptor(
|
|
HMENU hmenuCombined,
|
|
LPOLEMENUGROUPWIDTHS lpMenuWidths)
|
|
{
|
|
HOLEMENU hOleMenu;
|
|
OleMenuDescriptor *pOleMenuDescriptor;
|
|
int i;
|
|
|
|
if ( !hmenuCombined || !lpMenuWidths )
|
|
return 0;
|
|
|
|
/* Create an OLE menu descriptor */
|
|
if ( !(hOleMenu = GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT,
|
|
sizeof(OleMenuDescriptor) ) ) )
|
|
return 0;
|
|
|
|
pOleMenuDescriptor = (OleMenuDescriptor *) GlobalLock( hOleMenu );
|
|
if ( !pOleMenuDescriptor )
|
|
return 0;
|
|
|
|
/* Initialize menu group widths and hmenu */
|
|
for ( i = 0; i < 6; i++ )
|
|
pOleMenuDescriptor->mgw.width[i] = lpMenuWidths->width[i];
|
|
|
|
pOleMenuDescriptor->hmenuCombined = hmenuCombined;
|
|
pOleMenuDescriptor->bIsServerItem = FALSE;
|
|
GlobalUnlock( hOleMenu );
|
|
|
|
return hOleMenu;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* OleDestroyMenuDescriptor [OLE32.99]
|
|
* Destroy the shared menu descriptor
|
|
*/
|
|
HRESULT WINAPI OleDestroyMenuDescriptor(
|
|
HOLEMENU hmenuDescriptor)
|
|
{
|
|
if ( hmenuDescriptor )
|
|
GlobalFree( hmenuDescriptor );
|
|
return S_OK;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* OleSetMenuDescriptor [OLE32.129]
|
|
* Installs or removes OLE dispatching code for the containers frame window
|
|
* FIXME: The lpFrame and lpActiveObject parameters are currently ignored
|
|
* OLE should install context sensitive help F1 filtering for the app when
|
|
* these are non null.
|
|
*
|
|
* PARAMS:
|
|
* hOleMenu Handle to composite menu descriptor
|
|
* hwndFrame Handle to containers frame window
|
|
* hwndActiveObject Handle to objects in-place activation window
|
|
* lpFrame Pointer to IOleInPlaceFrame on containers window
|
|
* lpActiveObject Pointer to IOleInPlaceActiveObject on active in-place object
|
|
*
|
|
* RETURNS:
|
|
* S_OK - menu installed correctly
|
|
* E_FAIL, E_INVALIDARG, E_UNEXPECTED - failure
|
|
*/
|
|
HRESULT WINAPI OleSetMenuDescriptor(
|
|
HOLEMENU hOleMenu,
|
|
HWND hwndFrame,
|
|
HWND hwndActiveObject,
|
|
LPOLEINPLACEFRAME lpFrame,
|
|
LPOLEINPLACEACTIVEOBJECT lpActiveObject)
|
|
{
|
|
OleMenuDescriptor *pOleMenuDescriptor = NULL;
|
|
|
|
/* Check args */
|
|
if ( !hwndFrame || (hOleMenu && !hwndActiveObject) )
|
|
return E_INVALIDARG;
|
|
|
|
if ( lpFrame || lpActiveObject )
|
|
{
|
|
FIXME(ole,"(%x, %x, %x, %p, %p), Context sensitive help filtering not implemented!\n",
|
|
(unsigned int)hOleMenu,
|
|
hwndFrame,
|
|
hwndActiveObject,
|
|
lpFrame,
|
|
lpActiveObject);
|
|
}
|
|
|
|
/* Set up a message hook to intercept the containers frame window messages.
|
|
* The message filter is responsible for dispatching menu messages from the
|
|
* shared menu which are intended for the object.
|
|
*/
|
|
|
|
if ( hOleMenu ) /* Want to install dispatching code */
|
|
{
|
|
/* If OLEMenu hooks are already installed for this thread, fail
|
|
* Note: This effectively means that OleSetMenuDescriptor cannot
|
|
* be called twice in succession on the same frame window
|
|
* without first calling it with a null hOleMenu to uninstall */
|
|
if ( OLEMenu_IsHookInstalled( GetCurrentThreadId(), NULL ) )
|
|
return E_FAIL;
|
|
|
|
/* Get the menu descriptor */
|
|
pOleMenuDescriptor = (OleMenuDescriptor *) GlobalLock( hOleMenu );
|
|
if ( !pOleMenuDescriptor )
|
|
return E_UNEXPECTED;
|
|
|
|
/* Update the menu descriptor */
|
|
pOleMenuDescriptor->hwndFrame = hwndFrame;
|
|
pOleMenuDescriptor->hwndActiveObject = hwndActiveObject;
|
|
|
|
GlobalUnlock( hOleMenu );
|
|
pOleMenuDescriptor = NULL;
|
|
|
|
/* Add a menu descriptor windows property to the frame window */
|
|
SetPropA( hwndFrame, "PROP_OLEMenuDescriptor", hOleMenu );
|
|
|
|
/* Install thread scope message hooks for WH_GETMESSAGE and WH_CALLWNDPROC */
|
|
if ( !OLEMenu_InstallHooks( GetCurrentThreadId() ) )
|
|
return E_FAIL;
|
|
}
|
|
else /* Want to uninstall dispatching code */
|
|
{
|
|
/* Uninstall the hooks */
|
|
if ( !OLEMenu_UnInstallHooks( GetCurrentThreadId() ) )
|
|
return E_FAIL;
|
|
|
|
/* Remove the menu descriptor property from the frame window */
|
|
RemovePropA( hwndFrame, "PROP_OLEMenuDescriptor" );
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* ReleaseStgMedium [OLE32.140]
|
|
*/
|
|
void WINAPI ReleaseStgMedium(
|
|
STGMEDIUM* pmedium)
|
|
{
|
|
switch (pmedium->tymed)
|
|
{
|
|
case TYMED_HGLOBAL:
|
|
{
|
|
if ( (pmedium->pUnkForRelease==0) &&
|
|
(pmedium->u.hGlobal!=0) )
|
|
GlobalFree(pmedium->u.hGlobal);
|
|
|
|
pmedium->u.hGlobal = 0;
|
|
break;
|
|
}
|
|
case TYMED_FILE:
|
|
{
|
|
if (pmedium->u.lpszFileName!=0)
|
|
{
|
|
if (pmedium->pUnkForRelease==0)
|
|
{
|
|
DeleteFileW(pmedium->u.lpszFileName);
|
|
}
|
|
|
|
CoTaskMemFree(pmedium->u.lpszFileName);
|
|
}
|
|
|
|
pmedium->u.lpszFileName = 0;
|
|
break;
|
|
}
|
|
case TYMED_ISTREAM:
|
|
{
|
|
if (pmedium->u.pstm!=0)
|
|
{
|
|
IStream_Release(pmedium->u.pstm);
|
|
}
|
|
|
|
pmedium->u.pstm = 0;
|
|
break;
|
|
}
|
|
case TYMED_ISTORAGE:
|
|
{
|
|
if (pmedium->u.pstg!=0)
|
|
{
|
|
IStorage_Release(pmedium->u.pstg);
|
|
}
|
|
|
|
pmedium->u.pstg = 0;
|
|
break;
|
|
}
|
|
case TYMED_GDI:
|
|
{
|
|
if ( (pmedium->pUnkForRelease==0) &&
|
|
(pmedium->u.hGlobal!=0) )
|
|
DeleteObject(pmedium->u.hGlobal);
|
|
|
|
pmedium->u.hGlobal = 0;
|
|
break;
|
|
}
|
|
case TYMED_MFPICT:
|
|
{
|
|
if ( (pmedium->pUnkForRelease==0) &&
|
|
(pmedium->u.hMetaFilePict!=0) )
|
|
{
|
|
DeleteMetaFile(pmedium->u.hMetaFilePict);
|
|
GlobalFree(pmedium->u.hMetaFilePict);
|
|
}
|
|
|
|
pmedium->u.hMetaFilePict = 0;
|
|
break;
|
|
}
|
|
case TYMED_ENHMF:
|
|
{
|
|
if ( (pmedium->pUnkForRelease==0) &&
|
|
(pmedium->u.hEnhMetaFile!=0) )
|
|
{
|
|
DeleteEnhMetaFile(pmedium->u.hEnhMetaFile);
|
|
}
|
|
|
|
pmedium->u.hEnhMetaFile = 0;
|
|
break;
|
|
}
|
|
case TYMED_NULL:
|
|
default:
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* After cleaning up, the unknown is released
|
|
*/
|
|
if (pmedium->pUnkForRelease!=0)
|
|
{
|
|
IUnknown_Release(pmedium->pUnkForRelease);
|
|
pmedium->pUnkForRelease = 0;
|
|
}
|
|
}
|
|
|
|
/***
|
|
* OLEDD_Initialize()
|
|
*
|
|
* Initializes the OLE drag and drop data structures.
|
|
*/
|
|
static void OLEDD_Initialize()
|
|
{
|
|
WNDCLASSA wndClass;
|
|
|
|
ZeroMemory (&wndClass, sizeof(WNDCLASSA));
|
|
wndClass.style = CS_GLOBALCLASS;
|
|
wndClass.lpfnWndProc = (WNDPROC)OLEDD_DragTrackerWindowProc;
|
|
wndClass.cbClsExtra = 0;
|
|
wndClass.cbWndExtra = sizeof(TrackerWindowInfo*);
|
|
wndClass.hCursor = 0;
|
|
wndClass.hbrBackground = 0;
|
|
wndClass.lpszClassName = OLEDD_DRAGTRACKERCLASS;
|
|
|
|
RegisterClassA (&wndClass);
|
|
}
|
|
|
|
/***
|
|
* OLEDD_UnInitialize()
|
|
*
|
|
* Releases the OLE drag and drop data structures.
|
|
*/
|
|
static void OLEDD_UnInitialize()
|
|
{
|
|
/*
|
|
* Simply empty the list.
|
|
*/
|
|
while (targetListHead!=NULL)
|
|
{
|
|
RevokeDragDrop(targetListHead->hwndTarget);
|
|
}
|
|
}
|
|
|
|
/***
|
|
* OLEDD_InsertDropTarget()
|
|
*
|
|
* Insert the target node in the tree.
|
|
*/
|
|
static void OLEDD_InsertDropTarget(DropTargetNode* nodeToAdd)
|
|
{
|
|
DropTargetNode* curNode;
|
|
DropTargetNode** parentNodeLink;
|
|
|
|
/*
|
|
* Iterate the tree to find the insertion point.
|
|
*/
|
|
curNode = targetListHead;
|
|
parentNodeLink = &targetListHead;
|
|
|
|
while (curNode!=NULL)
|
|
{
|
|
if (nodeToAdd->hwndTarget<curNode->hwndTarget)
|
|
{
|
|
/*
|
|
* If the node we want to add has a smaller HWND, go left
|
|
*/
|
|
parentNodeLink = &curNode->prevDropTarget;
|
|
curNode = curNode->prevDropTarget;
|
|
}
|
|
else if (nodeToAdd->hwndTarget>curNode->hwndTarget)
|
|
{
|
|
/*
|
|
* If the node we want to add has a larger HWND, go right
|
|
*/
|
|
parentNodeLink = &curNode->nextDropTarget;
|
|
curNode = curNode->nextDropTarget;
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* The item was found in the list. It shouldn't have been there
|
|
*/
|
|
assert(FALSE);
|
|
return;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* If we get here, we have found a spot for our item. The parentNodeLink
|
|
* pointer points to the pointer that we have to modify.
|
|
* The curNode should be NULL. We just have to establish the link and Voila!
|
|
*/
|
|
assert(curNode==NULL);
|
|
assert(parentNodeLink!=NULL);
|
|
assert(*parentNodeLink==NULL);
|
|
|
|
*parentNodeLink=nodeToAdd;
|
|
}
|
|
|
|
/***
|
|
* OLEDD_ExtractDropTarget()
|
|
*
|
|
* Removes the target node from the tree.
|
|
*/
|
|
static DropTargetNode* OLEDD_ExtractDropTarget(HWND hwndOfTarget)
|
|
{
|
|
DropTargetNode* curNode;
|
|
DropTargetNode** parentNodeLink;
|
|
|
|
/*
|
|
* Iterate the tree to find the insertion point.
|
|
*/
|
|
curNode = targetListHead;
|
|
parentNodeLink = &targetListHead;
|
|
|
|
while (curNode!=NULL)
|
|
{
|
|
if (hwndOfTarget<curNode->hwndTarget)
|
|
{
|
|
/*
|
|
* If the node we want to add has a smaller HWND, go left
|
|
*/
|
|
parentNodeLink = &curNode->prevDropTarget;
|
|
curNode = curNode->prevDropTarget;
|
|
}
|
|
else if (hwndOfTarget>curNode->hwndTarget)
|
|
{
|
|
/*
|
|
* If the node we want to add has a larger HWND, go right
|
|
*/
|
|
parentNodeLink = &curNode->nextDropTarget;
|
|
curNode = curNode->nextDropTarget;
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* The item was found in the list. Detach it from it's parent and
|
|
* re-insert it's kids in the tree.
|
|
*/
|
|
assert(parentNodeLink!=NULL);
|
|
assert(*parentNodeLink==curNode);
|
|
|
|
/*
|
|
* We arbitrately re-attach the left sub-tree to the parent.
|
|
*/
|
|
*parentNodeLink = curNode->prevDropTarget;
|
|
|
|
/*
|
|
* And we re-insert the right subtree
|
|
*/
|
|
if (curNode->nextDropTarget!=NULL)
|
|
{
|
|
OLEDD_InsertDropTarget(curNode->nextDropTarget);
|
|
}
|
|
|
|
/*
|
|
* The node we found is still a valid node once we complete
|
|
* the unlinking of the kids.
|
|
*/
|
|
curNode->nextDropTarget=NULL;
|
|
curNode->prevDropTarget=NULL;
|
|
|
|
return curNode;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* If we get here, the node is not in the tree
|
|
*/
|
|
return NULL;
|
|
}
|
|
|
|
/***
|
|
* OLEDD_FindDropTarget()
|
|
*
|
|
* Finds information about the drop target.
|
|
*/
|
|
static DropTargetNode* OLEDD_FindDropTarget(HWND hwndOfTarget)
|
|
{
|
|
DropTargetNode* curNode;
|
|
|
|
/*
|
|
* Iterate the tree to find the HWND value.
|
|
*/
|
|
curNode = targetListHead;
|
|
|
|
while (curNode!=NULL)
|
|
{
|
|
if (hwndOfTarget<curNode->hwndTarget)
|
|
{
|
|
/*
|
|
* If the node we want to add has a smaller HWND, go left
|
|
*/
|
|
curNode = curNode->prevDropTarget;
|
|
}
|
|
else if (hwndOfTarget>curNode->hwndTarget)
|
|
{
|
|
/*
|
|
* If the node we want to add has a larger HWND, go right
|
|
*/
|
|
curNode = curNode->nextDropTarget;
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* The item was found in the list.
|
|
*/
|
|
return curNode;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* If we get here, the item is not in the list
|
|
*/
|
|
return NULL;
|
|
}
|
|
|
|
/***
|
|
* OLEDD_DragTrackerWindowProc()
|
|
*
|
|
* This method is the WindowProcedure of the drag n drop tracking
|
|
* window. During a drag n Drop operation, an invisible window is created
|
|
* to receive the user input and act upon it. This procedure is in charge
|
|
* of this behavior.
|
|
*/
|
|
static LRESULT WINAPI OLEDD_DragTrackerWindowProc(
|
|
HWND hwnd,
|
|
UINT uMsg,
|
|
WPARAM wParam,
|
|
LPARAM lParam)
|
|
{
|
|
switch (uMsg)
|
|
{
|
|
case WM_CREATE:
|
|
{
|
|
LPCREATESTRUCTA createStruct = (LPCREATESTRUCTA)lParam;
|
|
|
|
SetWindowLongA(hwnd, 0, (LONG)createStruct->lpCreateParams);
|
|
|
|
|
|
break;
|
|
}
|
|
case WM_MOUSEMOVE:
|
|
{
|
|
TrackerWindowInfo* trackerInfo = (TrackerWindowInfo*)GetWindowLongA(hwnd, 0);
|
|
POINT mousePos;
|
|
|
|
/*
|
|
* Get the current mouse position in screen coordinates.
|
|
*/
|
|
mousePos.x = LOWORD(lParam);
|
|
mousePos.y = HIWORD(lParam);
|
|
ClientToScreen(hwnd, &mousePos);
|
|
|
|
/*
|
|
* Track the movement of the mouse.
|
|
*/
|
|
OLEDD_TrackMouseMove(trackerInfo, mousePos, wParam);
|
|
|
|
break;
|
|
}
|
|
case WM_LBUTTONUP:
|
|
case WM_MBUTTONUP:
|
|
case WM_RBUTTONUP:
|
|
case WM_LBUTTONDOWN:
|
|
case WM_MBUTTONDOWN:
|
|
case WM_RBUTTONDOWN:
|
|
{
|
|
TrackerWindowInfo* trackerInfo = (TrackerWindowInfo*)GetWindowLongA(hwnd, 0);
|
|
POINT mousePos;
|
|
|
|
/*
|
|
* Get the current mouse position in screen coordinates.
|
|
*/
|
|
mousePos.x = LOWORD(lParam);
|
|
mousePos.y = HIWORD(lParam);
|
|
ClientToScreen(hwnd, &mousePos);
|
|
|
|
/*
|
|
* Notify everyone that the button state changed
|
|
* TODO: Check if the "escape" key was pressed.
|
|
*/
|
|
OLEDD_TrackStateChange(trackerInfo, mousePos, wParam);
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* This is a window proc after all. Let's call the default.
|
|
*/
|
|
return DefWindowProcA (hwnd, uMsg, wParam, lParam);
|
|
}
|
|
|
|
/***
|
|
* OLEDD_TrackMouseMove()
|
|
*
|
|
* This method is invoked while a drag and drop operation is in effect.
|
|
* it will generate the appropriate callbacks in the drop source
|
|
* and drop target. It will also provide the expected feedback to
|
|
* the user.
|
|
*
|
|
* params:
|
|
* trackerInfo - Pointer to the structure identifying the
|
|
* drag & drop operation that is currently
|
|
* active.
|
|
* mousePos - Current position of the mouse in screen
|
|
* coordinates.
|
|
* keyState - Contains the state of the shift keys and the
|
|
* mouse buttons (MK_LBUTTON and the like)
|
|
*/
|
|
static void OLEDD_TrackMouseMove(
|
|
TrackerWindowInfo* trackerInfo,
|
|
POINT mousePos,
|
|
DWORD keyState)
|
|
{
|
|
HWND hwndNewTarget = 0;
|
|
HRESULT hr = S_OK;
|
|
|
|
/*
|
|
* Get the handle of the window under the mouse
|
|
*/
|
|
hwndNewTarget = WindowFromPoint(mousePos);
|
|
|
|
/*
|
|
* If we are hovering over the same target as before, send the
|
|
* DragOver notification
|
|
*/
|
|
if ( (trackerInfo->curDragTarget != 0) &&
|
|
(trackerInfo->curDragTargetHWND==hwndNewTarget) )
|
|
{
|
|
POINTL mousePosParam;
|
|
|
|
/*
|
|
* The documentation tells me that the coordinate should be in the target
|
|
* window's coordinate space. However, the tests I made tell me the
|
|
* coordinates should be in screen coordinates.
|
|
*/
|
|
mousePosParam.x = mousePos.x;
|
|
mousePosParam.y = mousePos.y;
|
|
|
|
IDropTarget_DragOver(trackerInfo->curDragTarget,
|
|
keyState,
|
|
mousePosParam,
|
|
trackerInfo->pdwEffect);
|
|
}
|
|
else
|
|
{
|
|
DropTargetNode* newDropTargetNode = 0;
|
|
|
|
/*
|
|
* If we changed window, we have to notify our old target and check for
|
|
* the new one.
|
|
*/
|
|
if (trackerInfo->curDragTarget!=0)
|
|
{
|
|
IDropTarget_DragLeave(trackerInfo->curDragTarget);
|
|
}
|
|
|
|
/*
|
|
* Make sure we're hovering over a window.
|
|
*/
|
|
if (hwndNewTarget!=0)
|
|
{
|
|
/*
|
|
* Find-out if there is a drag target under the mouse
|
|
*/
|
|
newDropTargetNode = OLEDD_FindDropTarget(hwndNewTarget);
|
|
|
|
trackerInfo->curDragTargetHWND = hwndNewTarget;
|
|
trackerInfo->curDragTarget = newDropTargetNode ? newDropTargetNode->dropTarget : 0;
|
|
|
|
/*
|
|
* If there is, notify it that we just dragged-in
|
|
*/
|
|
if (trackerInfo->curDragTarget!=0)
|
|
{
|
|
POINTL mousePosParam;
|
|
|
|
/*
|
|
* The documentation tells me that the coordinate should be in the target
|
|
* window's coordinate space. However, the tests I made tell me the
|
|
* coordinates should be in screen coordinates.
|
|
*/
|
|
mousePosParam.x = mousePos.x;
|
|
mousePosParam.y = mousePos.y;
|
|
|
|
IDropTarget_DragEnter(trackerInfo->curDragTarget,
|
|
trackerInfo->dataObject,
|
|
keyState,
|
|
mousePosParam,
|
|
trackerInfo->pdwEffect);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* The mouse is not over a window so we don't track anything.
|
|
*/
|
|
trackerInfo->curDragTargetHWND = 0;
|
|
trackerInfo->curDragTarget = 0;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Now that we have done that, we have to tell the source to give
|
|
* us feedback on the work being done by the target. If we don't
|
|
* have a target, simulate no effect.
|
|
*/
|
|
if (trackerInfo->curDragTarget==0)
|
|
{
|
|
*trackerInfo->pdwEffect = DROPEFFECT_NONE;
|
|
}
|
|
|
|
hr = IDropSource_GiveFeedback(trackerInfo->dropSource,
|
|
*trackerInfo->pdwEffect);
|
|
|
|
/*
|
|
* When we ask for feedback from the drop source, sometimes it will
|
|
* do all the necessary work and sometimes it will not handle it
|
|
* when that's the case, we must display the standard drag and drop
|
|
* cursors.
|
|
*/
|
|
if (hr==DRAGDROP_S_USEDEFAULTCURSORS)
|
|
{
|
|
if ( (*trackerInfo->pdwEffect & DROPEFFECT_MOVE) ||
|
|
(*trackerInfo->pdwEffect & DROPEFFECT_COPY) ||
|
|
(*trackerInfo->pdwEffect & DROPEFFECT_LINK) )
|
|
{
|
|
SetCursor(LoadCursorA(0, IDC_SIZEALLA));
|
|
}
|
|
else
|
|
{
|
|
SetCursor(LoadCursorA(0, IDC_NOA));
|
|
}
|
|
}
|
|
}
|
|
|
|
/***
|
|
* OLEDD_TrackStateChange()
|
|
*
|
|
* This method is invoked while a drag and drop operation is in effect.
|
|
* It is used to notify the drop target/drop source callbacks when
|
|
* the state of the keyboard or mouse button change.
|
|
*
|
|
* params:
|
|
* trackerInfo - Pointer to the structure identifying the
|
|
* drag & drop operation that is currently
|
|
* active.
|
|
* mousePos - Current position of the mouse in screen
|
|
* coordinates.
|
|
* keyState - Contains the state of the shift keys and the
|
|
* mouse buttons (MK_LBUTTON and the like)
|
|
*/
|
|
static void OLEDD_TrackStateChange(
|
|
TrackerWindowInfo* trackerInfo,
|
|
POINT mousePos,
|
|
DWORD keyState)
|
|
{
|
|
/*
|
|
* Ask the drop source what to do with the operation.
|
|
*/
|
|
trackerInfo->returnValue = IDropSource_QueryContinueDrag(
|
|
trackerInfo->dropSource,
|
|
trackerInfo->escPressed,
|
|
keyState);
|
|
|
|
/*
|
|
* All the return valued will stop the operation except the S_OK
|
|
* return value.
|
|
*/
|
|
if (trackerInfo->returnValue!=S_OK)
|
|
{
|
|
/*
|
|
* Make sure the message loop in DoDragDrop stops
|
|
*/
|
|
trackerInfo->trackingDone = TRUE;
|
|
|
|
/*
|
|
* Release the mouse in case the drop target decides to show a popup
|
|
* or a menu or something.
|
|
*/
|
|
ReleaseCapture();
|
|
|
|
/*
|
|
* If we end-up over a target, drop the object in the target or
|
|
* inform the target that the operation was cancelled.
|
|
*/
|
|
if (trackerInfo->curDragTarget!=0)
|
|
{
|
|
switch (trackerInfo->returnValue)
|
|
{
|
|
/*
|
|
* If the source wants us to complete the operation, we tell
|
|
* the drop target that we just dropped the object in it.
|
|
*/
|
|
case DRAGDROP_S_DROP:
|
|
{
|
|
POINTL mousePosParam;
|
|
|
|
/*
|
|
* The documentation tells me that the coordinate should be
|
|
* in the target window's coordinate space. However, the tests
|
|
* I made tell me the coordinates should be in screen coordinates.
|
|
*/
|
|
mousePosParam.x = mousePos.x;
|
|
mousePosParam.y = mousePos.y;
|
|
|
|
IDropTarget_Drop(trackerInfo->curDragTarget,
|
|
trackerInfo->dataObject,
|
|
keyState,
|
|
mousePosParam,
|
|
trackerInfo->pdwEffect);
|
|
break;
|
|
}
|
|
/*
|
|
* If the source told us that we should cancel, fool the drop
|
|
* target by telling it that the mouse left it's window.
|
|
*/
|
|
case DRAGDROP_S_CANCEL:
|
|
IDropTarget_DragLeave(trackerInfo->curDragTarget);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/***
|
|
* OLEDD_GetButtonState()
|
|
*
|
|
* This method will use the current state of the keyboard to build
|
|
* a button state mask equivalent to the one passed in the
|
|
* WM_MOUSEMOVE wParam.
|
|
*/
|
|
static DWORD OLEDD_GetButtonState()
|
|
{
|
|
BYTE keyboardState[256];
|
|
DWORD keyMask = 0;
|
|
|
|
GetKeyboardState(keyboardState);
|
|
|
|
if ( (keyboardState[VK_SHIFT] & 0x80) !=0)
|
|
keyMask |= MK_SHIFT;
|
|
|
|
if ( (keyboardState[VK_CONTROL] & 0x80) !=0)
|
|
keyMask |= MK_CONTROL;
|
|
|
|
if ( (keyboardState[VK_LBUTTON] & 0x80) !=0)
|
|
keyMask |= MK_LBUTTON;
|
|
|
|
if ( (keyboardState[VK_RBUTTON] & 0x80) !=0)
|
|
keyMask |= MK_RBUTTON;
|
|
|
|
if ( (keyboardState[VK_MBUTTON] & 0x80) !=0)
|
|
keyMask |= MK_MBUTTON;
|
|
|
|
return keyMask;
|
|
}
|
|
|
|
|