//====START_GENERATED_PROLOG======================================
//
//
//   COMPONENT_NAME: oddataxfer
//
//   CLASSES: none
//
//   ORIGINS: 82,27,94
//
//
//   (C) COPYRIGHT International Business Machines Corp. 1995,1996
//   All Rights Reserved
//   Licensed Materials - Property of IBM
//   US Government Users Restricted Rights - Use, duplication or
//   disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
//   	
//   IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
//   ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
//   PURPOSE. IN NO EVENT SHALL IBM BE LIABLE FOR ANY SPECIAL, INDIRECT OR
//   CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
//   USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
//   OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE
//   OR PERFORMANCE OF THIS SOFTWARE.
//
//====END_GENERATED_PROLOG========================================
//
// @(#) 1.50 nt/src/storage/XDragDrp.cpp, oddataxfer, od96nt, odnt9714c 4/2/97 10:57:17 [ 4/2/97 17:55:49 ]
/*
 *  This file was (initially) generated by the SOM Compiler and Emitter Framework.
 *  Generated using: 
 *      SOM Emitter emitxtm.dll: Development
 */

#ifndef SOM_Module_dragdrp_Source
#define SOM_Module_dragdrp_Source
#endif
#define ODDragAndDrop_Class_Source

//  The Novell implementation used the following include file to
//  provide an alternate ODPoint that would do C++ type conversions
//  on Point objects.  Because of the nature of SOM 'xh' and 'xih'
//  generation, this header can't be included just anywhere; it
//  should appear 'early' in a source file - like for instance as
//  the first header included:
//
#ifndef _ALTPOINT_
#include <AltPoint.h>				// Use C++ savvy ODPoint and ODRect
#endif

#ifndef _ODTYPES_
#include <ODTypes.h>				//  the OpenDoc kitchen sink
#endif

#ifdef DAD_DEBUG
#ifndef QPRINTF_MSGDISP
#include "qprintf.h"
#endif
#else
#define		qprintf		//DAD_DEBUG not defined- qprintf
#endif  // DAD_DEBUG

// OLE stuff (in transition):
#ifndef SOM_Module_2e5coledefs_OpenDoc_OleDefs_defined
#include <OleDefs.xh>				//  for kODKindOlePart and kODKindOcxPart
#endif

#ifndef _OLEMGRW_
#include <OleMgrW.h>
#endif

#include <oledata.h>
#include <olexfer.h>

#ifndef SOM_OlePart_xh
#include <OlePart.xh>				//  requires prior definition of Environment
#endif

#ifndef SOM_OLEPromiseKeeper_xh
#include <OLEDPkpr.xh>
#endif

#ifndef SOM_Module_OpenDoc_StdProps_defined
#include <StdProps.xh>				//  for kODPropContents
#endif

#ifndef SOM_Module_OpenDoc_StdTypes_defined
#include <StdTypes.xh>				//  for kODULong, kODDataObject
#endif

#ifndef SOM_ODDragItemIterator_xh
#include <DgItmIt.xh>
#endif

#ifndef _DRAGPRIV_
#include <DragPriv.h>				//  for ODMemDragItem, ODDragLink
#endif

#ifndef SOM_ODTranslation_xh
#include <Translt.xh>
#endif

#ifndef SOM_ODWindow_xh
#include <Window.xh>
#endif

#ifndef SOM_ODWindowState_xh
#include <WinStat.xh>
#endif

#ifndef SOM_ODFacet_xh
#include <Facet.xh>
#endif

#ifndef SOM_ODFacetIterator_xh
#include <FacetItr.xh>
#endif

#ifndef SOM_ODTransform_xh
#include <Trnsform.xh>
#endif

#ifndef SOM_ODFrame_xh
#include <Frame.xh>
#endif

#ifndef SOM_ODFrameFacetIterator_xh
#include <FrFaItr.xh>
#endif

#ifndef SOM_ODSession_xh
#include <ODSessn.xh>
#endif

#ifndef _STORDEF_
#include <StorDef.h>				//  for kODdrag____ definitions
#endif

#ifndef SOM_CMStorageUnit_xh
#include <CMSU.xh>
#endif

#ifndef _TEMPOBJ_
#include <TempObj.h>				//  for TempODWindow, TempODPart, etc.
#endif

#ifndef _ODMATH_
#include <ODMath.h>					//  for ODIntToFixed
#endif

#ifndef _STORUTIL_
#include <StorUtil.h>				//  for StorageUnitGetValue, StorageUnitSetValue, etc
#endif

#ifndef _ODUTILS_
#include <ODUtils.h>				//  for ODReleaseObject
#endif

#ifndef _LINKDLGS_
#include "LinkDlgs.h"				//  for ShowPasteAsDialog
#endif

#ifndef _CONSTDF_
#include "ConstDf.h"				//  looking for kODApplePart definition ...
#endif

#ifndef SOM_ODDragAndDrop_xih
#define VARIABLE_MACROS
#include <DragDrp.xih>				//  requires prior definition of OlePromiseKeeper
#endif

#ifndef _ISOSTR_
#include <ISOStr.h>
#endif


/*	prototypes
 */
HRGN ScaleRGN(Environment *ev, HRGN hRgn, ODTransform *xform);		//  found in Novell RGNSHPE.CPP
void DoProcessHDROP(Environment *ev, LPVOID pDataObj, LinkedList *theDragItemList, ODStorageSystem *storageSystem);
BOOL ValidateCandidateOpenDocDocument(const char *szFileName);
HWND GetDocshellHWND(Environment *ev, const POINT *point, ODSession* session);
extern BOOL CopyBentoIStreamToODClipboard(IDataObject*, CLIPFORMAT, ODStorageUnit*, ODSession* session);
extern BOOL IsBentoStream(IDataObject*, CLIPFORMAT, HRESULT*);


/*	Global Variables		//TDF: yuk - get rid of these where possible ...
 */
ODRgnHandle	 	gImage		= 0;			//  Drag outline shape
ODBoolean		gDragging	= FALSE;		//  TRUE if we are dragging
POINT			gpt 		= { -1, -1 };	//  Offset of drag region
ODPoint 		glocalMouse;				//  Local mouse position
DWORD			gLast_dwEffect;				//  Last drag-over effect


/*	METHOD: Clear
 *
 *	DESCRIPTION:
 *		Free all the user data stored within us at this time
 */
SOM_Scope void  SOMLINK ODDragAndDropClear(ODDragAndDrop *somSelf,
										   Environment *ev)
{
	ODDragAndDropData *somThis = ODDragAndDropGetData(somSelf);
	ODDragAndDropMethodDebug("ODDragAndDrop","ODDragAndDropClear");
	qprintf("Entering ODDragAndDropClear:\n");

	SOM_TRY
		//  Free the drag-item iterator object
		if (_fDragItemIterator)
		{
			delete _fDragItemIterator;
			_fDragItemIterator = kODNULL;
		}

		//  Free up all drag items - this will free any storage
		//  units that we handed out in GetContentStorageUnit
		//  or any storage units we created during ProcessDragOver
		_fDragItemList->DeleteAllLinks();

		//  Destroy our promise keeper, if we have one
		if (_fPromiseKeeper)
		{
			delete _fPromiseKeeper;
			_fPromiseKeeper	= NULL;
		}
	SOM_CATCH_ALL
	SOM_ENDTRY
}

/*	METHOD: GetContentStorageUnit
 *	
 *	DESCRIPTION:
 *		Return a new storage unit, maintained as a member of an ODMemDragItem,
 *		and keep track of the new drag item in the drag item list
 */
SOM_Scope ODStorageUnit *  SOMLINK ODDragAndDropGetContentStorageUnit(ODDragAndDrop *somSelf,
																	  Environment *ev)
{
	ODDragAndDropData *somThis = ODDragAndDropGetData(somSelf);
	ODDragAndDropMethodDebug("ODDragAndDrop","ODDragAndDropGetContentStorageUnit");
	qprintf("Entering ODDragAndDropGetContentStorageUnit:\n");

	ODMemDragItem	*newItem;
	ODDragLink		*newLink;

	//  Create a new drag item...
	newItem = new ODMemDragItem(_fStorageSystem, kODFalse);
	ASSERT(newItem != kODNULL,kODErrCannotAllocateDragItem);
	newItem->Initialize(ev);

	//  ...and link it into our list of user-allocated data
	newLink = new ODDragLink(newItem, kODTrue);
	if (!newLink)
	{
		delete newItem;
		ASSERT(newLink != kODNULL,kODErrCannotAllocateDragItem);
	}
	_fDragItemList->AddLast(newLink);
	return (newItem->fSU);
}

/*	METHOD: StartDrag
 *	
 *	DESCRIPTION:
 *		Use OLE to initiate a modal drag-drop operation with the given drag
 *		image and the current drag data held in our memory storage units
 *
 *	NOTE: Really need to address deficiencies in the design of how drag
 *		  images are handled by the API; also determine the fate of ScaleRGN()
 */
SOM_Scope ODDropResult  SOMLINK ODDragAndDropStartDrag(ODDragAndDrop *somSelf,
													   Environment *ev,
													   ODFrame* srcFrame,
													   ODType imageType,
													   ODByteArray* image,
													   ODPart** destPart,
													   ODByteArray* refCon)
{
	ODDragAndDropData *somThis = ODDragAndDropGetData(somSelf);
	ODDragAndDropMethodDebug("ODDragAndDrop","ODDragAndDropStartDrag");
	qprintf("Entering ODDragAndDropStartDrag:\n");
	ODDropResult       			dropResult	  = kODDropFail;
	LinkedListIterator 			*theIter 	  = kODNULL;
	IDataObject					*pIDataObject = NULL;
	STGMEDIUM			        stm;
	FORMATETC				    fe;
	LPDROPSOURCE    			pIDropSource;
	HRESULT 	    		    hr;
	DWORD       		    	dwEffect;
	ODDragLink		        	*theDragLink;
	ODTranslation				*translate;
	ODStorageUnit				*theSU;
	int							count, index;
	ODType						theISOType;
	ODPlatformType   			platformType;
	POINT						lPt;
	DWORD						dwPos;
	int							dataSize;
	LPSTR						data;
	HGLOBAL						hdata;
	ODRgnHandle           		imageRgn;

	// check for bad input:
	if ((srcFrame == kODNULL) ||				// srcFrame must be != kODNULL
		(destPart == kODNULL))					// destPart must be != kODNULL
	{
		// abuse concept of ASSERT
		ASSERT(0, kODErrIllegalNullInput);

		// return with fail
		return(kODDropFail);
	}

	// imageType must be == kODWindowsRegionHandle or == "RGNH"
	// (to support legacy NT OpenDoc applications, imageType can be == "RGNH";
	// this tolerance should go away in a future release!)
	if ((strncmp(imageType, kODWindowsRegionHandle, strlen(kODWindowsRegionHandle)) != 0)
		&& (strncmp(imageType, "RGNH", strlen("RGNH")) != 0))
	{
		// abuse concept of ASSERT
		ASSERT(0, kODErrUnknownDragImageType);

		// return with fail
		return(kODDropFail);
	}

TRY
	//  Set the dropped on part to NULL. This parameter is only returned if
	//  the drag occurs within this process' address space
	*destPart = kODNULL;

	//  Reset the pointer to the previous facet - this prevents
	//  a trap in the following scenario:
	//  
	//  Two documents exist in the same process, thereby
	//  'sharing' the same ODDragAndDrop object; call the
	//  documents 'A' and 'B'.  Document A initiates and
	//  completes a drag and drop operation, and then is
	//  deleted by the the user.  If document B then attempts
	//  to start a drag and drop operation, the ODDragAndDrop
	//  object will notice the non-null previous facet pointer
	//  during DragEnter processing (left over from document
	//  A's drag operation), and will attempt to call DragLeave
	//  on the indicated facet.  Since document A has been
	//  closed (deleted), the saved facet pointer is non-null
	//  but invalid, and the application traps.  The solution
	//  is to null the previous facet pointer here every time
	//  a drag and drop operation begins ...
	_fPrevFacet = kODNULL;

	//  Set up our variables to actually do the drag...
	//  (use TempODWindow to complement AcquireActiveWindow call)
	TempODWindow activeWindow = _fSession->GetWindowState(ev)->AcquireActiveWindow(ev);
	_fWnd = activeWindow->GetPlatformWindow(ev);
	_fOrigin = ((ODEventData*)refCon->_buffer)->pt;
	imageRgn = *((ODRgnHandle*) image->_buffer);

	//  Copy the drag image into our own private region handle and offset it
	//  relative to the current cursor position.
	CombineRgn(_fImage,imageRgn,imageRgn,RGN_COPY);

	//  scale Region to screen coordinates for 72dpi
	//  (use TempODWindow to complement AcquireODWindow call)
	TempODWindow tempWindow = _fWindowState->AcquireODWindow(ev, _fWnd);
	ODFacet *rootFacet = tempWindow->GetRootFacet(ev);
	ODTransform *scaleTrans = rootFacet->AcquireExternalTransform(ev, kODNULL);
	ODFrameFacetIterator* fi = srcFrame->CreateFacetIterator(ev);
	ODTransform *wcTrans = fi->First(ev)->AcquireWindowContentTransform(ev, kODNULL);
	delete fi;
	Point frameOffset;
	frameOffset = wcTrans->GetOffsetAsPoint(ev);
	OffsetRgn(_fImage, -frameOffset.x, -frameOffset.y);
//	_fImage = ScaleRGN(ev, _fImage, scaleTrans);
	ODReleaseObject(ev, wcTrans);
	ODReleaseObject(ev, scaleTrans);

	lPt.x = 0;
	lPt.y = 0;
	ClientToScreen(_fWnd,&lPt);

	dwPos = GetMessagePos();
	lPt.x = lPt.x - (LOWORD(dwPos) - frameOffset.x);
	lPt.y = lPt.y - (HIWORD(dwPos) - frameOffset.y);
	OffsetRgn(_fImage,lPt.x,lPt.y);

	//  This is a modal operation, so the IDropSource object is local only to
	//  this function. It only gets called by the OLE drag-drop code from
	//  within the DoDragDrop() function
#ifdef DINOSAUR_CODE_DUDE  //TDF:  How this will be handled TBD by OLE team
	//  ... also fail if we can't use our data-object class factory.
	if (!ODOLEManager::GetProcessData()->gDataFactory)
		return dropResult;
#endif //  DINOSAUR_CODE_DUDE
	pIDropSource = (LPDROPSOURCE) new WrapIDropSource();
	if (!pIDropSource)
		return dropResult;

	pIDropSource->AddRef();

	//  Create an IDataObject that encapsulates the storage unit(s) that are
	//  to be transferred to the drop target.
	//  LATER: use CoCreateInstance here perhaps?
#ifdef DINOSAUR_CODE_DUDE  //TDF:  old method of obtaining IDataObject:
	((IClassFactory*)ODOLEManager::GetProcessData()->gDataFactory)->CreateInstance(NULL,
																				   IID_IDataObject,
																				   (void**)&pIDataObject);
#else  //TDF:  (probable) new method of obtaining IDataObject:
	// hey - when does dragOleData get deallocated?
	ODOLEManager *dragOleMgr = _fSession->GetOleManager(ev);
	OleData *dragOleData = new OleData();
	dragOleData->Initialize(dragOleMgr->fCFEmbeddedObject,
							dragOleMgr->fCFEmbedSource,
							dragOleMgr->fCFObjectDescriptor,
							dragOleMgr->fCFOleWrapper);
	dragOleData->QueryInterface(IID_IDataObject, (void**)&pIDataObject);
#endif //  DINOSAUR_CODE_DUDE

	//  just return if QueryInterface failed to get a pIDataObject for us:
	if (!pIDataObject)
		return dropResult;

	//  Make sure our IDataObject knows that it should expect to see
	//  kODApplePart format work in the dragdrop fashion - with multiple
	//  drag items - instead of a single clipboard draft being passed
	//  in memory.
	((DataIDataObject*)pIDataObject)->fDragAndDrop = TRUE;
	
	//  Scan through all items in the drag and add a flavor for each value
	//  in the storage unit's kODContents property
	theIter 	= new LinkedListIterator(_fDragItemList);
	translate 	= _fSession->GetTranslation(ev);
	for ( theDragLink = (ODDragLink*)theIter->First();
		  theDragLink;
		  theDragLink = (ODDragLink*)theIter->Next())
	{
		//  Close and reopen each drag item - this ensures that the storage
		//  system 'prepares' storage units properly for data transfer (NOTE:
		//  strange and subtle bugs appear in part code if this isn't done;
		//  and, OLE content dragged from OLE applications will indicate the
		//  "can't drop" symbol when dragged over parts that really can
		//  accept the dragged data)
		//  
		//  One day it appeared this special little fix would no longer be
		//  necessary (see defect 24398) - it turned out that the Apple code
		//  is buggy with memory containers, and we found that DR5 has a
		//  nasty hack to prevent problems that sound similar to the ones we
		//  were experiencing here.  Unfortunately, disabling these two
		//  lines of code just causes the bugs to reappear ...
	        //theDragLink->fItem->Close(ev);
	        //theDragLink->fItem->Open(ev);

		//  alias the current storage unit
		theSU = theDragLink->fItem->fSU;

                // rrk 20mar97 - if we are read-only, don't export a linkspec
                ODWindow *window = _fSession->GetWindowState(ev)->AcquireActiveWindow(ev);
                if(window) {
		   ODDraft *theDraft = window->GetDraft(ev);
	           if (theDraft) {
                   	ODBoolean isDraftReadOnly = (theDraft->GetPermissions(ev) == kODDPReadOnly);
                   	//PRINT("ExportClipboard: isDraftReadOnly is %d.\n", isDraftReadOnly);
                   	if (isDraftReadOnly)
		      	if ( theSU->Exists(ev, kODPropLinkSpec, kODNULL, 0) )
			      ODSURemoveProperty(ev, theSU, kODPropLinkSpec);
		   }
                   ODReleaseObject(ev,window);
                }

		//  if the storage unit contains just an OlePart object, then drag
		//  just the OLE "Embedded Object", "Object Descriptor" and cached
		//  image information...
	    if (theSU->Exists(ev,kODPropContents, kODKindOlePart, 0) ||
	        theSU->Exists(ev,kODPropContents, kODKindOcxPart, 0))
			ExportOlePart(ev,pIDataObject,theSU);

		//  the storage unit contains something other than an OlePart object,
		//  so set each value in the storage unit on the IDataObject
		else
		{
			theSU->Focus(ev, kODPropContents, kODPosUndefined, 0, 0, kODPosUndefined);
			count = theSU->CountValues(ev);
			for (index = 1; index <= count; ++index)
			{
				theSU->Focus(ev,kODPropContents, kODPosUndefined, 0, index, kODPosUndefined);
				theISOType 	 = theSU->GetType(ev);
				platformType = translate->GetPlatformTypeFromISOType(ev,theISOType);
				ODDisposePtr(theISOType);
				if (!platformType)
					continue;
	
				dataSize = theSU->GetSize(ev);
				data = NULL;
				if(!dataSize)
					continue;
				TRY
					hdata = GlobalAlloc(GMEM_SHARE,dataSize);
					if (hdata)
					{
						data = (LPSTR)GlobalLock(hdata);
						if (data)
							StorageUnitGetValue(theSU, ev, dataSize, (ODValue) data);
						else
							THROW(kODErrOutOfMemory);
					}
					else
						THROW(kODErrOutOfMemory);
				CATCH_ALL
					if (data)
						GlobalUnlock(hdata);
					if (hdata)
						GlobalFree(hdata);
					pIDataObject->Release();
					RERAISE;
				ENDTRY
	
				//  Add the clipboard format to our IDataObject
				memset(&stm,0,sizeof(stm));

				stm.hGlobal	= hdata;
				stm.tymed = TYMED_HGLOBAL;
				SETDEFAULTFORMATETC(fe,(CLIPFORMAT)platformType,TYMED_HGLOBAL);

				hr = pIDataObject->SetData(&fe, &stm, TRUE);
				GlobalUnlock(hdata);
				ASSERT(SUCCEEDED(hr),(long)hr);
				if (FAILED(hr))
				{
					GlobalFree(hdata);
					ODDisposePtr(theISOType);
					pIDataObject->Release();
					THROW(42L);					//TDF: har har har.  Whoo, someone really knows how to party ...
				}								//TDF: When I finally stop laughing, I need to address this
				//  LATER: who frees the memory we allocated when things work?
				//TDF:  I think OLE frees this memory for us automagically ...
			}

			//
                        // OK, now we've left the loop and should check for 
	     		// the kODPropLinkSpec property.  In the Novell code, this was done inside
	       		// the loop because link specs were not kept in their own separate property, as
	       		// they are now.  They were kept right smack dab there in the contents property.
	       		if(theSU->Exists(ev, kODPropLinkSpec, kODNULL, 0) )
	       		  {
	       		    theSU->Focus(ev, kODPropLinkSpec,kODPosUndefined, kODLinkSpec, 0, kODPosUndefined);	  
			    // read the version out of here first, OK?

			    ODULong version;
			    StorageUnitGetValue(theSU, ev, sizeof(ODULong), (ODValue) &version); // rrk 11 jun 96
 
       			    dataSize = theSU->GetSize(ev);
       			    TRY
       			      data  = NULL;
       			      hdata = GlobalAlloc(GMEM_SHARE,dataSize);
       			      if (hdata)
       				{
       				  data	= (LPSTR) GlobalLock(hdata);
       				  if (data)
       				       StorageUnitGetValue(theSU, ev,dataSize, (ODValue) data);
       				  else
       				       THROW(kODErrOutOfMemory);
       				}
       			      else
       			        THROW(kODErrOutOfMemory);
       			    CATCH_ALL
       			      if (data)
       			        GlobalUnlock(hdata);
       			      if (hdata)
       			       	GlobalFree(hdata);
       			      pIDataObject->Release();
       			      RERAISE;
       			    ENDTRY
       			    CreateStreamOnHGlobal(hdata,TRUE,&stm.pstm);
       			    stm.tymed			= TYMED_ISTREAM;
       			    SETDEFAULTFORMATETC(fe,(unsigned long)dragOleMgr->fCFLinkSource,TYMED_ISTREAM);

	       		    hr 	    = pIDataObject->SetData(&fe, &stm, TRUE);                        
	       		    GlobalUnlock(hdata);
	       		    WASSERTMSG_DEBUG(SUCCEEDED(hr),
	       				     "ODDragAndDrop: failure on IDataObject::SetData");
	       		    if (FAILED(hr))
	       		      {
	       			GlobalFree(hdata);
	       			ODDisposePtr(theISOType);
	       			pIDataObject->Release();
	       			THROW(kODErrCannotExportToIDataObject);
	       		      }
	       		  }
	       		// Within the LinkSpec property we should also find the LinkSpecDescriptor
	       		// value.  This value also used to be kept in the contents property in the Novell code.

		       	if(theSU->Exists(ev, kODPropLinkSpec, kODLinkSpecDescriptor, 0) )
		       	  {
		       	    theSU->Focus(ev, kODPropLinkSpec,kODPosUndefined, kODLinkSpecDescriptor, 0, kODPosUndefined);
		       	    dataSize = theSU->GetSize(ev);
		       	    TRY
		       	      data  = NULL;
		       	      hdata = GlobalAlloc(GMEM_SHARE,dataSize);
		       	      if (hdata)
		       		{
		       		  data	= (LPSTR) GlobalLock(hdata);
		       		  if (data)
		       		       StorageUnitGetValue(theSU, ev,dataSize, (ODValue) data);
		       		  else
		       		       THROW(kODErrOutOfMemory);
		       		}
		       	      else
		       		THROW(kODErrOutOfMemory);
		       	    CATCH_ALL
		       	      if (data)
		       	        GlobalUnlock(hdata);
		       	      if (hdata)
		       	       	GlobalFree(hdata);
		       	      pIDataObject->Release();
		       	      RERAISE;
		       	    ENDTRY
		       	    stm.hGlobal = hdata;
		       	    stm.tymed			= TYMED_HGLOBAL;
		       	    SETDEFAULTFORMATETC(fe,(unsigned long)dragOleMgr->fCFLinkSrcDescriptor,TYMED_HGLOBAL);

	       		    hr 	    = pIDataObject->SetData(&fe, &stm, TRUE);                        
	       		    GlobalUnlock(hdata);
	       		    WASSERTMSG_DEBUG(SUCCEEDED(hr),
	       				 "ODDragAndDrop: failure on IDataObject::SetData");
	       		    if (FAILED(hr))
	       		      {
	       			GlobalFree(hdata);
	       			ODDisposePtr(theISOType);
	       			pIDataObject->Release();
	       			THROW(kODErrCannotExportToIDataObject);
	       		      }
	       		  }
	
			//  Always add our special format (kODApplePart) to the IDataObject
			memset(&stm,0,sizeof(stm));
			stm.tymed = TYMED_HGLOBAL;
			SETDEFAULTFORMATETC(fe,
								(CLIPFORMAT)translate->GetPlatformTypeFromISOType(ev,kODApplePart),
								TYMED_HGLOBAL);
			hr = pIDataObject->SetData(&fe, &stm, TRUE);
			ASSERT(SUCCEEDED(hr),(long)hr);

			//  dwj - Always promise CF_METAFILEPICT with HGLOB_BOGUS in the STGMEDIUM unit
			//
            //  When DataIDataObject::GetData sees the HGLOB_BOGUS it will get the
            //  'kOKApplePart' - an indication that the data is really the original
            //  drag item list.  It then will find the first item on the list,
			//  resurrect it as a document and ask it to draw itself into a MetaFile:
			SETDEFAULTFORMATETC(fe,CF_METAFILEPICT,TYMED_MFPICT);
			memset(&stm,0,sizeof(stm));
			stm.hGlobal                     = HGLOB_BOGUS;
			stm.tymed                       = TYMED_MFPICT;
			pIDataObject->SetData(&fe, &stm, TRUE);

			SETDEFAULTFORMATETC(fe,CF_ENHMETAFILE,TYMED_ENHMF);
			memset(&stm,0,sizeof(stm));
			stm.hGlobal                     = HGLOB_BOGUS;
			stm.tymed                       = TYMED_ENHMF;
			pIDataObject->SetData(&fe, &stm, TRUE);

			//  Always promise an OLE-embeddable object
			PromiseOleFormats(pIDataObject);
		}
	}
	delete theIter;
	
	//  Set up our instance variables to contain the frame and part being dragged
	_fSourceFrame		= srcFrame;
	TempODPart tempPart	= srcFrame->AcquirePart(ev);	// (use TempODPart to complement call to AcquirePart)
	_fSourcePart 		= tempPart;
	qprintf("StartDrag: _fSourcePart = %08p\n", _fSourcePart);

	//  More pre-drag initializations:
	_fDestFacet 		= kODNULL;
	theIter 			= new LinkedListIterator(_fDragItemList);
	_fDragItemIterator	= new ODDragItemIterator();
	if (_fDragItemIterator)
		_fDragItemIterator->InitDragItemIterator(ev, theIter);
	_fHasLeftSourceFrame = kODFalse;
	
	//  Draw the initial tracking rectangle
	gDragging 			= TRUE;
	_fDragSource		= TRUE;			//  We are the source process
	gImage				= _fImage;
	
	//  Call the OLE drag-drop API
	hr = DoDragDrop(pIDataObject,
					pIDropSource,
					DROPEFFECT_COPY | DROPEFFECT_MOVE | DROPEFFECT_LINK,
					&dwEffect);

	//  set important status flags
	gDragging 			= FALSE;
	_fDragSource		= FALSE;		
	gImage				= 0;

	//  dump our OLE interfaces
	pIDataObject->Release();
    pIDropSource->Release();
	
	//  modify dropResult and drag attributes based on returns
	//  from DoDragDrop - first make sure that relevant drag
	//  attributes are reset:
	//TDF: is it necessary to set these drag attributes after
	//     DoDragDrop has returned?  Does the source really
	//     care about attributes at this point?
	_fAttributes &= ~(kODDropIsMove | kODDropIsCopy | kODDropIsPasteAs | kODDropIsLink);

	if (SUCCEEDED(hr))
		switch (dwEffect)
		{
			case DROPEFFECT_MOVE:
				_fAttributes |= kODDropIsMove;
				dropResult = kODDropMove;
				break;
			case DROPEFFECT_COPY:
				_fAttributes |= kODDropIsCopy;
				dropResult = kODDropCopy;
				break;
			case DROPEFFECT_LINK:
				_fAttributes |= kODDropIsLink;
				dropResult = kODDropCopy;
				break;
			case DROPEFFECT_NONE:
			default:
				//  dropResult will remain == kODDropFail,
				//  _fAttributes won't indicate a move, copy, or PasteAs
				break;
		}

	//  if _fDestFacet is non-null, it means that a facet in this same
	//  process handled the drop - so I guess this code is here just
	//  to bump the reference count?  The code is straight offa DR4 ...
	if (_fDestFacet)
		*destPart = _fDestFacet->GetFrame(ev)->AcquirePart(ev);
	else
	        *destPart = kODNULL;

	//  Clean up our instance variables
	_fSourceFrame 	= kODNULL;
	_fSourcePart	= kODNULL;
	_fDestFacet 	= kODNULL;

	//  Dump our drag items and storage units (at this point,
	//  the target has already consumed the dragged content)
	somSelf->Clear(ev);
CATCH_ALL
	if (theIter)
		delete theIter;
	RERAISE;
ENDTRY
	qprintf("returning from StartDrag: dropResult = %d, fAttributes = 0x%04X\n",
			dropResult, _fAttributes);
	return dropResult;
}

/*	METHOD: ShowPasteAsDialog
 *
 *	DESCRIPTION:
 *		Display the 'Paste As' dialog to let the user choose whether to link or
 *		incorporate the data that was dragged...
 */
SOM_Scope ODBoolean  SOMLINK ODDragAndDropShowPasteAsDialog(ODDragAndDrop *somSelf,
                                                            Environment *ev,
                                                            ODBoolean canPasteLink,
                                                            ODPasteAsMergeSetting mergeSetting,
                                                            ODFacet* facet,
                                                            ODTypeToken viewType,
                                                            ODStorageUnit* contentSU,
                                                            ODPasteAsResult* theResult)
{
	ODDragAndDropData *somThis = ODDragAndDropGetData(somSelf);
	ODDragAndDropMethodDebug("ODDragAndDrop","ODDragAndDropShowPasteAsDialog");
	qprintf("Entering ODDragAndDropShowPasteAsDialog:\n");

	ODBoolean returnValue = kODFalse;

	SOM_TRY

	THROW_IF_NULL(facet, kODErrNullFacetInput);
	THROW_IF_NULL(contentSU, kODErrIllegalNullStorageUnitInput);
	THROW_IF_NULL(theResult, kODErrNullPasteAsResultInput);

	returnValue = ShowPasteAsDialog(canPasteLink,
									mergeSetting,
									(somSelf->GetDropResult(ev) == kODDropMove),
									contentSU,
									facet,
									viewType,
									_fSession->GetWindowSystemData(ev)->parentWindow,
									theResult);

	// if there is a kODDataObject prop, kODULong value, in our SU  and the user requests a link, it cannot be
	// an OpenDoc link -- must be an Ole link.  Ole links are transparent to container parts -- we
	// don't want to do all the linking recipe stuff for Ole links.  Instead, we'll do the following:
	//  1) lie to the part that called us, claiming that no link was requested.
	//  2) stuff a property in our storage unit for OlePart to examine.  The presence of this property 
	//     tells OlePart that this is a link.  See OlePart::InitPartFromStorage for
	//     that part of this implementation. Note that I also had to create a value in this property,
	//     else the property doesn't seem to exist.  The "kODULong" value I create has no other
	//     significance.
	//  3) We needed to do one more thing here for D&D we didn't need to do for clipboard because the timing
	//     of events is different here.  In ProcessDragOver we checked to see if the modifier keys for
	//     linking (cntrl-shift) were in effect.  If so, we added the kODPropEmbedAsLink property.
	//     We needed to do that because (unlike in the clipboard case) olepart::InitPartFromStorage looks 
	//     for kODPropEmbedAsLink before this dialog ever goes up.  This is not good -- it means that this
	//     dialog is irrelevant -- the link is made as soon as we hit ProcessDragOver.
	ODSURemoveProperty(ev,contentSU,kODPropEmbedAsLink);
	if (theResult->pasteLinkSetting)
	{
		if (contentSU->Exists(ev,kODDataObject, kODULong, 0) )
		{
	    	theResult->pasteLinkSetting = kODFalse; 
			ODSUForceFocus(ev, contentSU, kODPropEmbedAsLink, kODULong);
		}
	}

	SOM_CATCH_ALL
		returnValue =  kODFalse;
	SOM_ENDTRY

	return returnValue;
}

/*	METHOD: GetDragAttributes
 *	
 *	DESCRIPTION:
 */
SOM_Scope ODULong  SOMLINK ODDragAndDropGetDragAttributes(ODDragAndDrop *somSelf,
														  Environment *ev)
{
	ODDragAndDropData *somThis = ODDragAndDropGetData(somSelf);
	ODDragAndDropMethodDebug("ODDragAndDrop","ODDragAndDropGetDragAttributes");
	qprintf("Entering ODDragAndDropGetDragAttributes:\n");

	return _fAttributes;
}

SOM_Scope ODPlatformDragReference  SOMLINK ODDragAndDropGetDragReference(ODDragAndDrop *somSelf,
																		 Environment *ev)
{
	ODDragAndDropData *somThis = ODDragAndDropGetData(somSelf);
	ODDragAndDropMethodDebug("ODDragAndDrop","ODDragAndDropGetDragReference");
	qprintf("Entering ODDragAndDropGetDragReference:\n");

	return _fDragReference;
}

/*	METHOD: InitDragAndDrop
 *	
 *	DESCRIPTION:
 *		Initialize the drag-drop manager object
 */
SOM_Scope void  SOMLINK ODDragAndDropInitDragAndDrop(ODDragAndDrop *somSelf,
													 Environment *ev,
                                                     ODSession* session)
{
	ODDragAndDropData *somThis = ODDragAndDropGetData(somSelf);
	ODDragAndDropMethodDebug("ODDragAndDrop","ODDragAndDropInitDragAndDrop");
	qprintf("Entering ODDragAndDropInitDragAndDrop:\n");

	somSelf->InitObject(ev);
	_fSession 			= session;
TRY
	_fWindowState 		= _fSession->GetWindowState(ev);
	_fStorageSystem 	= _fSession->GetStorageSystem(ev);
	_fDragItemList 		= new LinkedList;
	_fImage 			= CreateRectRgn(0,0,0,0);
CATCH_ALL
	_fWindowState 		= kODNULL;
	_fStorageSystem 	= kODNULL;
	if (_fDragItemList)
	{
		delete _fDragItemList;
		_fDragItemList = kODNULL;
	}
	RERAISE;
ENDTRY
}

/*	METHOD: FindTargetFacet
 *	
 *	DESCRIPTION:
 *		Figure out the target facet from the given mouse location.
 *		If the most immediate facet is for some reason unavailable
 *		(the facet's frame is frozen, being dragged, or isn't
 *		set as droppable) then return the closest available
 *		containing facet.
 */
SOM_Scope ODFacet*  SOMLINK ODDragAndDropFindTargetFacet(ODDragAndDrop *somSelf,
														 Environment *ev,
														 ODPlatformWindow theWindow,
														 ODPoint* mouse,
														 ODPoint* localMouse)
{
    ODDragAndDropData *somThis = ODDragAndDropGetData(somSelf);
    ODDragAndDropMethodDebug("ODDragAndDrop","ODDragAndDropFindTargetFacet");

	ODFacet   *targetFacet = kODNULL;

	SOM_TRY
		ODFacet   *curFacet;
		{	
			TempODWindow targetWindow = _fWindowState->AcquireODWindow(ev, theWindow);
			if (!targetWindow)
				return kODNULL;

			curFacet = targetWindow->GetFacetUnderPoint(ev, localMouse);
		}

		//  so how is Apple sure that this loop always terminates?
		//  I guess GetContainingFacet eventually returns NULL?
		while (curFacet && !targetFacet)
		{
			ODFrame* curFrame = curFacet->GetFrame(ev);
			if (!curFrame->IsDragging(ev) &&
				curFrame->IsDroppable(ev) &&
				!curFrame->IsFrozen(ev))
			  targetFacet = curFacet;

			curFacet = curFacet->GetContainingFacet(ev);
		}

	SOM_CATCH_ALL
	SOM_ENDTRY

	return targetFacet;
}

SOM_Scope LinkedList*  SOMLINK ODDragAndDropGetDragItemList(ODDragAndDrop *somSelf,
															Environment *ev)
{
	ODDragAndDropData *somThis = ODDragAndDropGetData(somSelf);
	ODDragAndDropMethodDebug("ODDragAndDrop","ODDragAndDropGetDragItemList");
	qprintf("Entering ODDragAndDropGetDragItemList:\n");

	return _fDragItemList;
}

SOM_Scope void  SOMLINK ODDragAndDropSetDragItemList(ODDragAndDrop *somSelf,
													 Environment *ev,
													 LinkedList* list)
{
	ODDragAndDropData *somThis = ODDragAndDropGetData(somSelf);
	ODDragAndDropMethodDebug("ODDragAndDrop","ODDragAndDropSetDragItemList");
	qprintf("Entering ODDragAndDropSetDragItemList:\n");

	_fDragItemList = list;
}

SOM_Scope ODBoolean  SOMLINK ODDragAndDropGetListFromHandler(ODDragAndDrop *somSelf,
															 Environment *ev)
{
	ODDragAndDropData *somThis = ODDragAndDropGetData(somSelf);
	ODDragAndDropMethodDebug("ODDragAndDrop","ODDragAndDropGetListFromHandler");
	qprintf("Entering ODDragAndDropGetListFromHandler:\n");

    return _fListFromHandler;
}

SOM_Scope void  SOMLINK ODDragAndDropSetListFromHandler(ODDragAndDrop *somSelf,
														Environment *ev,
														ODBoolean listFromHandler)
{
	ODDragAndDropData *somThis = ODDragAndDropGetData(somSelf);
	ODDragAndDropMethodDebug("ODDragAndDrop","ODDragAndDropSetListFromHandler");
	qprintf("Entering ODDragAndDropSetListFromHandler:\n");

	_fListFromHandler = listFromHandler;
}

SOM_Scope ODFrame*  SOMLINK ODDragAndDropGetSourceFrame(ODDragAndDrop *somSelf,
														Environment *ev)
{
	ODDragAndDropData *somThis = ODDragAndDropGetData(somSelf);
	ODDragAndDropMethodDebug("ODDragAndDrop","ODDragAndDropGetSourceFrame");
	qprintf("Entering ODDragAndDropGetSourceFrame:\n");

	return _fSourceFrame;
}

SOM_Scope void  SOMLINK ODDragAndDropSetSourceFrame(ODDragAndDrop *somSelf,
													Environment *ev,
													ODFrame* sourceFrame)
{
	ODDragAndDropData *somThis = ODDragAndDropGetData(somSelf);
	ODDragAndDropMethodDebug("ODDragAndDrop","ODDragAndDropSetSourceFrame");
	qprintf("Entering ODDragAndDropSetSourceFrame:\n");

	_fSourceFrame = sourceFrame;
}

SOM_Scope ODULong  SOMLINK ODDragAndDropGetAttributes(ODDragAndDrop *somSelf,
													  Environment *ev)
{
	ODDragAndDropData *somThis = ODDragAndDropGetData(somSelf);
	ODDragAndDropMethodDebug("ODDragAndDrop","ODDragAndDropGetAttributes");
	qprintf("Entering ODDragAndDropGetAttributes:\n");

	return _fAttributes;
}

SOM_Scope void  SOMLINK ODDragAndDropSetAttributes(ODDragAndDrop *somSelf,
												   Environment *ev,
												   ODULong attr)
{
	ODDragAndDropData *somThis = ODDragAndDropGetData(somSelf);
	ODDragAndDropMethodDebug("ODDragAndDrop","ODDragAndDropSetAttributes");
	qprintf("Entering ODDragAndDropSetAttributes:\n");

	_fAttributes = attr;
}

SOM_Scope ODPart*  SOMLINK ODDragAndDropGetSourcePart(ODDragAndDrop *somSelf,
													  Environment *ev)
{
	ODDragAndDropData *somThis = ODDragAndDropGetData(somSelf);
	ODDragAndDropMethodDebug("ODDragAndDrop","ODDragAndDropGetSourcePart");
	qprintf("Entering ODDragAndDropGetSourcePart:\n");

	return _fSourcePart;
}

SOM_Scope void  SOMLINK ODDragAndDropSetSourcePart(ODDragAndDrop *somSelf,
												   Environment *ev,
												   ODPart* sourcePart)
{
	ODDragAndDropData *somThis = ODDragAndDropGetData(somSelf);
	ODDragAndDropMethodDebug("ODDragAndDrop","ODDragAndDropSetSourcePart");
	qprintf("Entering ODDragAndDropSetSourcePart:\n");

	_fSourcePart = sourcePart;
}

SOM_Scope ODDragItemIterator*  SOMLINK ODDragAndDropGetDragItemIterator(ODDragAndDrop *somSelf,
																		Environment *ev)
{
	ODDragAndDropData *somThis = ODDragAndDropGetData(somSelf);
	ODDragAndDropMethodDebug("ODDragAndDrop","ODDragAndDropGetDragItemIterator");
	qprintf("Entering ODDragAndDropGetDragItemIterator:\n");

	return _fDragItemIterator;
}

SOM_Scope void  SOMLINK ODDragAndDropSetDragItemIterator(ODDragAndDrop *somSelf,
														 Environment *ev,
														 ODDragItemIterator* iter)
{
	ODDragAndDropData *somThis = ODDragAndDropGetData(somSelf);
	ODDragAndDropMethodDebug("ODDragAndDrop","ODDragAndDropSetDragItemIterator");
	qprintf("Entering ODDragAndDropSetDragItemIterator:\n");

	_fDragItemIterator = iter;
}

SOM_Scope ODSession*  SOMLINK ODDragAndDropGetSession(ODDragAndDrop *somSelf,
													  Environment *ev)
{
	ODDragAndDropData *somThis = ODDragAndDropGetData(somSelf);
	ODDragAndDropMethodDebug("ODDragAndDrop","ODDragAndDropGetSession");
	qprintf("Entering ODDragAndDropGetSession:\n");

	return _fSession;
}

SOM_Scope ODStorageSystem*  SOMLINK ODDragAndDropGetStorageSystem(ODDragAndDrop *somSelf,
																  Environment *ev)
{
	ODDragAndDropData *somThis = ODDragAndDropGetData(somSelf);
	ODDragAndDropMethodDebug("ODDragAndDrop","ODDragAndDropGetStorageSystem");
	qprintf("Entering ODDragAndDropGetStorageSystem:\n");

	return _fStorageSystem;
}

SOM_Scope void  SOMLINK ODDragAndDropSetDragReference(ODDragAndDrop *somSelf,
													  Environment *ev,
													  ODPlatformDragReference dragReference)
{
	ODDragAndDropData *somThis = ODDragAndDropGetData(somSelf);
	ODDragAndDropMethodDebug("ODDragAndDrop","ODDragAndDropSetDragReference");
	qprintf("Entering ODDragAndDropSetDragReference:\n");
	
	_fDragReference = dragReference;
}

SOM_Scope ODFacet*  SOMLINK ODDragAndDropGetPrevFacet(ODDragAndDrop *somSelf,
													  Environment *ev)
{
	ODDragAndDropData *somThis = ODDragAndDropGetData(somSelf);
	ODDragAndDropMethodDebug("ODDragAndDrop","ODDragAndDropGetPrevFacet");
	qprintf("Entering ODDragAndDropGetPrevFacet:\n");

	return _fPrevFacet;
}

SOM_Scope void  SOMLINK ODDragAndDropSetPrevFacet(ODDragAndDrop *somSelf,
												  Environment *ev,
												  ODFacet* prevFacet)
{
	ODDragAndDropData *somThis = ODDragAndDropGetData(somSelf);
	ODDragAndDropMethodDebug("ODDragAndDrop","ODDragAndDropSetPrevFacet");
	qprintf("Entering ODDragAndDropSetPrevFacet:\n");

	_fPrevFacet = prevFacet;
}

SOM_Scope ODFacet*  SOMLINK ODDragAndDropGetDestFacet(ODDragAndDrop *somSelf,
													  Environment *ev)
{
	ODDragAndDropData *somThis = ODDragAndDropGetData(somSelf);
	ODDragAndDropMethodDebug("ODDragAndDrop","ODDragAndDropGetDestFacet");
	qprintf("Entering ODDragAndDropGetDestFacet:\n");

	return _fDestFacet;
}

SOM_Scope void  SOMLINK ODDragAndDropSetDestFacet(ODDragAndDrop *somSelf,
												  Environment *ev,
												  ODFacet* destFacet)
{
	ODDragAndDropData *somThis = ODDragAndDropGetData(somSelf);
	ODDragAndDropMethodDebug("ODDragAndDrop","ODDragAndDropSetDestFacet");
	qprintf("Entering ODDragAndDropSetDestFacet:\n");

	_fDestFacet = destFacet;
}

SOM_Scope ODDropResult  SOMLINK ODDragAndDropGetDropResult(ODDragAndDrop *somSelf,
														   Environment *ev)
{
	ODDragAndDropData *somThis = ODDragAndDropGetData(somSelf);
	ODDragAndDropMethodDebug("ODDragAndDrop","ODDragAndDropGetDropResult");
	qprintf("Entering ODDragAndDropGetDropResult:\n");

	return _fDropResult;
}

SOM_Scope void  SOMLINK ODDragAndDropSetDropResult(ODDragAndDrop *somSelf,
												   Environment *ev,
												   ODDropResult dropResult)
{
	ODDragAndDropData *somThis = ODDragAndDropGetData(somSelf);
	ODDragAndDropMethodDebug("ODDragAndDrop","ODDragAndDropSetDropResult");
	qprintf("Entering ODDragAndDropSetDropResult:\n");

	_fDropResult = dropResult;
}

/*	METHOD: ODDragAndDrop::CanvasRequested
 *	
 *	DESCRIPTION:
 *		Temporarily unlock our window to allow objects to update and to permit
 *		them to draw target emphasis.
 */
SOM_Scope void  SOMLINK ODDragAndDropCanvasRequested(ODDragAndDrop *somSelf,
													 Environment *ev,
                                                     ODBoolean bRequest)
{
	ODDragAndDropData *somThis = ODDragAndDropGetData(somSelf);
	ODDragAndDropMethodDebug("ODDragAndDrop","ODDragAndDropCanvasRequested");
	qprintf("Entering ODDragAndDropCanvasRequested:\n");

	if (_fDropTarget)
	{
		if (bRequest)
		{
			somSelf->DrawDrag(ev,kODdragErase);
			LockWindowUpdate(NULL);
		}
		else
		{
			LockWindowUpdate(NULL);
			somSelf->DrawDrag(ev,kODdragDraw);
		}
	}
}

/*	METHOD: ODDragAndDrop::Locked
 *	
 *	DESCRIPTION:
 *		When our window gets locked because we are a drop target, this method is
 *		called so that we perform temporary unlocks and allow parts to update
 *		dynamically and draw target emphasis.
 */
SOM_Scope void  SOMLINK ODDragAndDropLocked(ODDragAndDrop *somSelf,
											Environment *ev,
                                            ODBoolean bLock)
{
	ODDragAndDropData *somThis = ODDragAndDropGetData(somSelf);
	ODDragAndDropMethodDebug("ODDragAndDrop","ODDragAndDropLocked");
	qprintf("Entering ODDragAndDropLocked:\n");

	_fDropTarget	= bLock;
}

/*	METHOD: ODDragAndDrop::DrawDrag
 *	
 *	DESCRIPTION:
 *		Draw the tracking region in desktop coordinates. Note that this function
 *		uses an invert function so that calling DrawDrag() will invert the
 *		rectangle, and calling it a second time on the same rectangle will
 *		erase the rectangle
 */
SOM_Scope void  SOMLINK ODDragAndDropDrawDrag(ODDragAndDrop *somSelf,
											  Environment *ev,
                                              ODULong EraseDraw)
{
	ODDragAndDropData *somThis = ODDragAndDropGetData(somSelf);
	ODDragAndDropMethodDebug("ODDragAndDrop","ODDragAndDropDrawDrag");
	qprintf("Entering ODDragAndDropDrawDrag:\n");
	static ODULong	gLastAction	= 0;
	POINT			pt;
	BOOL			bRedraw		= TRUE;
	DWORD			dwPos;
	HDC 			hdc;

	if (!_fWnd)
	{
		//  use TempODWindow to complement AcquireActiveWindow call
		TempODWindow activeWindow = _fSession->GetWindowState(ev)->AcquireActiveWindow(ev);
		_fWnd = activeWindow->GetPlatformWindow(ev);
	}

	//  Get the current mouse position. Also avoid needless repaints of the
	//  same rectangle ... since OLE sends us DragOver() way too often !
	dwPos 	= GetMessagePos();
	pt.x	= LOWORD(dwPos);
	pt.y	= HIWORD(dwPos);
	ScreenToClient(_fWnd,&pt);

	if (gDragging && gImage)
	{
		//  Get a special DC that is usable during LockWindowUpdate
		hdc = GetDCEx(_fWnd,
					  NULL,
					  DCX_CACHE | DCX_LOCKWINDOWUPDATE | DCX_CLIPSIBLINGS);
		SetROP2(hdc, R2_NOT);

		if ((pt.x == gpt.x) 					//  Same X coord
			&& (pt.y == gpt.y)					//  Same Y coord
			&& (gLastAction == EraseDraw)	    //  Same action
			&& (EraseDraw == (kODdragErase | kODdragDraw)))
		    bRedraw	= FALSE;

		if (bRedraw)
		{						
			//  Erase an existing outline of the dragged object
			if (EraseDraw & kODdragErase)
			{
				OffsetRgn(gImage, gpt.x, gpt.y);
				FrameRgn(hdc, gImage, GetStockObject(BLACK_BRUSH), 1, 1);
				OffsetRgn(gImage, -gpt.x, -gpt.y);
			}

			//  Draw an outline of the dragged object in its current position
			gpt.x			= pt.x;
			gpt.y			= pt.y;
			gLastAction		= EraseDraw;
			if (EraseDraw & kODdragDraw)
			{
				OffsetRgn(gImage, gpt.x, gpt.y);
				FrameRgn(hdc, gImage, GetStockObject(BLACK_BRUSH), 1, 1);
				OffsetRgn(gImage, -gpt.x, -gpt.y);
			}
		}
		ReleaseDC(_fWnd, hdc);
	}
}

/*	METHOD: ODDragAndDrop::ProcessDragOver
 *	
 *	DESCRIPTION:
 *      
 *		This method is the handler for the OLE IDropTarget's DragEnter and
 *		DragOver methods.  When an HWND that is registered as an OLE drop
 *		target (via RegisterDragDrop) is dragged into and over, OLE calls
 *		that window's IDropTarget interface's DragEnter and DragOver
 *		methods.  The OpenDoc OLE wrapper code that implements the window's
 *		IDropTarget methods calls this ProcessDragOver method in both
 *		cases.  This method then decides how to handle and respond to the
 *		data that is being dragged within the target's facets.
 *		[This method is called in the context of the drag and drop target's
 *		process]
 */
SOM_Scope void  SOMLINK ODDragAndDropProcessDragOver(ODDragAndDrop *somSelf,
													 Environment *ev,
                                                     DWORD grfKeyState,		// IN: Key and mouse state flags
                                                     POINTL pt,				// IN: Drag-over location in screen coords
                                                     DWORD *pdwEffect,		// OUT: Drag effect that we return
                                                     LPVOID pDataObj)
{
	ODDragAndDropData *somThis = ODDragAndDropGetData(somSelf);
	ODDragAndDropMethodDebug("ODDragAndDrop","ODDragAndDropProcessDragOver");
	qprintf("Entering ODDragAndDropProcessDragOver: pDataObj = %08p, _fDragSource = %08p\n", pDataObj, _fDragSource);

	LPENUMFORMATETC		pEnumFE		= NULL;
	BOOL				bCanDrop	= FALSE;
	LinkedListIterator 	*theIter	= kODNULL;
	BOOL				fODDrag		= FALSE;
	BOOL				fFiles		= FALSE;
	Point 				cPt;
	ULONG				cfe;
	FORMATETC			fe;
	STGMEDIUM			stm;
	ODTranslation		*translate;
	ODStorageUnit		*unit;
	HRESULT				hr;
	LPAPPLEPART			pAP;
	ULONG				dataSize;
	PBYTE				pbData;
	ODMemDragItem		*dragItem;
	ODType				isoType;

	//  If we were passed a non-NULL IDataObject, we know that we have been
	//  called as part of DragEnter processing.  So, if we aren't in the
	//  same process as the source of the DoDragDrop call, we will copy the
	//  data into the ODDragAndDrop manager for this process
	if (pDataObj && !_fDragSource)
	{
		//  clear out any existing drag and drop object content
		somSelf->Clear(ev);

		//  If we haven't yet created our PromiseKeeper, do it now. It has to
		//  honor all requests to fulfill promises on the storage units we
		//  hand out to OpenDoc that really came from an OLE source process...
		_fPromiseKeeper	= new OLEPromiseKeeper();
		if (_fPromiseKeeper)
			_fPromiseKeeper->InitOLEPromiseKeeper(ev, pDataObj);

		//  Get the FORMATETC enumerator, and loop through all the available
		//  formats on this drag.
		ODMemDragItem *newItem = new ODMemDragItem(_fStorageSystem, kODTrue);
		if (!newItem)
			//TDF:  Well, alright!  You really can't have enough goto's in C++ code ...
			goto InterProcessError;
		newItem->Initialize(ev);
		unit = newItem->fSU;
		translate = _fSession->GetTranslation(ev);
		((IDataObject *)pDataObj)->EnumFormatEtc(DATADIR_GET, &pEnumFE);
		if (pEnumFE && unit)		
		{
			while (!fODDrag && !FAILED(pEnumFE->Next(1,&fe,&cfe)) && cfe)
			{
				//  Get the ISO type for this FORMATETC, and write it into
				//  the content list of the drag-drop storage unit we have
				//  created. Shove a promise in, so that we don't have to
				//  actually get the data from the other process until the
				//  target receives a drop and requests a certain property
				//  and value...
				isoType = translate->GetISOTypeFromPlatformType(ev,
																fe.cfFormat,
																kODPlatformDataType);
				if (isoType)
				{
					//  OLE interop added the 'Embed Source' code:
					if  ( (ODISOStrEqual("Embed Source", isoType))
						 || (ODISOStrEqual(kODObjSpec, isoType)) )
					{

						// dwj - The Embed Source or kODObjSpec is either an ordinary OLE object or
						// an OpenDoc object that we have placed into an IStorate/IStream to be 
						// treated as an OLE object.  If it is the former then we want to
						// add a promise to the OpenDoc clipboard for eventually handling by OleWrapper.
						// If it is the latter, then we don't want to handle it as an ordinary Ole object 
						// because that would lead to double wrapping -- OlePart wrapping OleWrappper around 
						// around the original OpenDoc part. Instead, we want to "unwarp"  the OpenDoc part 
						// from the IStorage/IStream to get to the original OpenDoc object and let it 
						// clone itself to the clipboard. 
						 
					        HRESULT GetDataHRESULT = 0;
						if( IsBentoStream((LPDATAOBJECT)pDataObj, fe.cfFormat, &GetDataHRESULT) ) 
						{
						    CopyBentoIStreamToODClipboard((LPDATAOBJECT)pDataObj, fe.cfFormat, unit, _fSession);

						}
						// If we do not have Bento stuffed into our ole stream, then we should treat the
						// contents of the IDataObject as pure Ole.  We add a kODPropContentFrame for
						// use by olepart.  
						//
						// It can happen, however, that the the IDataObject::GetData call in IsBentoStream
						// fails -- we have seen this consistently when the source is MS Word.  For this
						// reason, IsBentoStream passes back the HRESULT produced by GetData, and we check
						// for the particular bad result codes generated by Word.  Using a check like
						// "if (SUCCEEDED(GetDataHRESULT))" turns out to be too restrictive -- we sometimes
						// receive bad return codes from GetData even though the whole procedure is
						// successful.
						else if ((GetDataHRESULT != E_INVALIDARG) && (GetDataHRESULT != DV_E_FORMATETC)) 
						{
						    if (! unit->Exists(ev,kODPropContentFrame, kODWeakStorageUnitRef, 0)) 
							{
								unit->AddProperty(ev, kODPropContentFrame);
								StorageUnitSetPromiseValue(unit, ev,kODWeakStorageUnitRef,0,0,kODNULL,_fPromiseKeeper);
							}
						}
					}
					else
					{
						unit->AddProperty(ev, kODPropContents);
						// unit->AddValue(ev, isoType);			//  just store promise value:
						StorageUnitSetPromiseValue(unit, ev, isoType, 0, 0, kODNULL, _fPromiseKeeper);
					}
	qprintf("StorageUnitSetPromiseValue(): isoType = \"%s\" from fe.cfFormat 0x%08X\n", isoType, fe.cfFormat);

					//  set our "user is dragging files" flag, if necessary:
					if (fe.cfFormat == CF_HDROP) 
						fFiles = TRUE; 

					//  If we find "Apple:Part", this is an IDataObject that
					//  was created by OpenDoc, and we should use the real
					//  storage units from the source process...
					if (!strcmp(isoType, kODApplePart))
						fODDrag	= TRUE;

					ODDisposePtr(isoType);
				}
			}
			//  OlePart: provide a direct pointer to the IDataObject interface
			//  so that OlePart can paste or paste-link in the usual OLE way
			if(!fODDrag)
			{
				unit->AddProperty(ev, kODDataObject)->AddValue(ev, kODULong);
				StorageUnitSetValue(unit, ev, sizeof(pDataObj), &pDataObj);
			}
			pEnumFE->Release();
		}
		//  well, the following two lines are here because they seem to lessen
		//  the frequency of traps that are observed when OLE-originated content
		//  is dragged quickly in and out of OpenDoc parts.  For more information
		//  about this problem (copious remarks), see defect 22556.
		//		newItem->Close(ev);
		//newItem->Open(ev);

		if	(fODDrag)
		{
			//  When the drag source is OpenDoc
			//  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
			//  Get the storage units from the source process and add them as
			//  ODDragLink items to the ODDragAndDrop object in this process.

			//  first, dump the OLE-specific drag item we just built:
			delete newItem;

			//  Get the list of DragItems from the source process
			//
			//  Don't bother with a call to SETDEFAULTFORMATETC;
			//  if we are in this code body ("if (fODDrag)") we
			//  know that the just-performed format enumeration
			//  set our fe variable up properly (but call QueryGetData
			//  just to be really anal)
			//
			//  Note that this code must interpret the data as it was
			//  formatted by our AllocDragItemList method
			if (NOERROR == ((IDataObject *)pDataObj)->QueryGetData(&fe))
			{
				hr = ((IDataObject *)pDataObj)->GetData(&fe, &stm);
				if (!FAILED(hr))
				{
					if ((stm.tymed == TYMED_HGLOBAL) && stm.hGlobal)
					{
						pAP	= (LPAPPLEPART)GlobalLock(stm.hGlobal);
						if (pAP)
						{
							if (gDragging != pAP->gDragging)
								_fNotDllVersion	= TRUE;

//TDF: I really should figure out what this Novell comment means one of these days:
#ifdef WIN32		//  LATER: does Win95 protect GDI handles like NT does?
							if(_fImage)
								DeleteObject(_fImage);
							_fImage 	= CreateRectRgn(pAP->gImageRect.left,
														pAP->gImageRect.top,
														pAP->gImageRect.right,
														pAP->gImageRect.bottom);
							gImage		= _fImage;
#else			
							gImage		= pAP->gImage;
#endif			
							gDragging	= pAP->gDragging;
							gpt.x  		= -0x8000;
							gpt.y  		= -0x8000;

							//  this logic corresponds to the logic in AllocDragItemList:
							//
							//  point to ULONG immediately following APPLEPART struct and 
							//  interpret it as the length of a 'flattened' drag item that
							//  follows.  If the APPLEPART structure indicates that there
							//  are more than one drag items (pAP->cItems > 1), then the
							//  ULONG following the first flattened drag item will indicate
							//  the length of the next drag item in the list.  This continues
							//  until pAP->cItems drag items have been recovered from the
							//  global memory block.  For each flattened drag item that is 
							//  encountered in the shared memory, a corresponding ODMemDragItem
							//  is constructed and added to this process' drag item list
							pbData = (PBYTE)(pAP + 1);
				
							//  free up any existing storage units
							_fDragItemList->DeleteAllLinks();
				
							while (pAP->cItems > 0)
							{
								//  use first ULONG in memory block to get block size
								dataSize = *((ULONG *)pbData);
								
								//  create a new drag item to hold contents of global memory
								dragItem = new ODMemDragItem(_fStorageSystem, kODTrue);
								if (!dragItem)
									goto InterProcessError;

								//  initialize the drag item, then close it
								//  so we can set its data
								dragItem->Initialize(ev);
								dragItem->Close(ev);
								
								//  pour data from global memory into memory container
								dragItem->fContainerHandle = ODNewHandle(dataSize);
								memcpy(*((ODHandle)dragItem->fContainerHandle),
									   pbData + sizeof(ULONG),
									   dataSize);
								
								//  now open the new drag item
								//  (this causes the dragItem to 'commit' its new
								//   data to a real live container object)
								dragItem->Open(ev);
								
								//  Add each drag item to the drag item linked list
								ODDragLink *newLink = new ODDragLink(dragItem, kODTrue);
								_fDragItemList->AddLast((Link*)newLink);
									
								//  point to next element in list of drag items
								pbData += dataSize + sizeof(ULONG);
				
								pAP->cItems--;
							}
							GlobalUnlock(stm.hGlobal);
						}
					}
					GlobalFree(stm.hGlobal);
					stm.pUnkForRelease->Release();
				}
				else
					goto InterProcessError;
			}
#ifdef DAD_DEBUG
			else
				qprintf("Whoa - howzitthat QueryGetData failed?\n");
#endif  // DAD_DEBUG
		}
		else	//  The drag source is an OLE or Windows application:
				//  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
			if (fFiles)
				//  if our fFiles flag is set, we know that the user is
				//  dragging one or more files from the desktop into our
				//  document - figure out how to handle them:
				DoProcessHDROP(ev, pDataObj, _fDragItemList, _fStorageSystem);
			else
			{
                //  the user is dragging OLE originated content that does not
                //  include any files.  Use the drag item that we just filled
                //  with translated OLE formats to create the drag item list
                //  (the list of one or more drag items that is given to parts
                //  that are dragged over).  This allows parts to interpret OLE
                //  text, graphics or whatever as if it comes from OpenDoc,
                //  instead of forcing parts to understand the platform
                //  specific OLE IDataObject object:
				_fListFromHandler 		= TRUE;							//TDF: probably no longer used
				ODDragLink *newLink   	= new ODDragLink(newItem, kODTrue);
				_fDragItemList->AddLast((Link*)newLink);
			}

		//  now initialize iterator (handed to target facets by DragEnter, 
		//  DragWithin and Drop)
		theIter 				= new LinkedListIterator(_fDragItemList);
		_fDragItemIterator 		= new ODDragItemIterator();
		if (_fDragItemIterator)
			_fDragItemIterator->InitDragItemIterator(ev, theIter);
	}

//TDF: snide goto comment already in place (see above)
InterProcessError:

	//  Figure out whether this is a copy, move or link operation
	*pdwEffect = DROPEFFECT_MOVE;
	if (grfKeyState & MK_CONTROL)
	{
		*pdwEffect = DROPEFFECT_COPY;
		if (grfKeyState & MK_SHIFT)
			*pdwEffect = DROPEFFECT_LINK;
	}

	//  Now pass on a DragWithin or DragEnter method call to that part that
	//  lives within the facet that is being dragged over...

	//TDF: optimization: can't the following be done during DragEnter and saved until DragLeave?
	//TDF: perhaps test with a multifaceted part ...
	cPt.x	= pt.x;
	cPt.y	= pt.y;
	HWND platformHWND = GetDocshellHWND(ev, &cPt, _fSession);
	ScreenToClient(platformHWND, &cPt);
	glocalMouse   = cPt;	
													
	//  since all drag attributes are determinable in every drag within,
	//  let's clear all flags and set them according to the results of
	//  the FindTargetFacet call
	_fAttributes = 0;

	ODFacet *targetFacet = somSelf->FindTargetFacet(ev, platformHWND, &glocalMouse, &glocalMouse);
	if (targetFacet)
	{
		if ((targetFacet->GetFrame(ev) == _fSourceFrame) && (_fHasLeftSourceFrame == kODFalse))
			_fAttributes |= kODDragIsInSourcePart | kODDragIsInSourceFrame;
		else
		{
			_fHasLeftSourceFrame = kODTrue;
			_fAttributes = kODDragHasLeftSourceFrame;
			// (use TempODPart to complement call to AcquirePart)
			TempODPart tempPart	= targetFacet->GetFrame(ev)->AcquirePart(ev);
			if (tempPart == _fSourcePart)
				_fAttributes |= kODDragIsInSourcePart;
		}

		//  Set the drag attributes which tell the part what kind of drag
		//  operation this really is
		//TDF: are these attributes relevant at this point during the
		//     drag operation?  Shouldn't they only be set immediately
		//     prior to the drop?  Also, not that kODDragHasLeftSourceFrame
		//     has the same value as kODDropIsMove (more ammunition against
		//     setting these attributes now, or against the frivolous
		//     kODDragHasLeftSourceFrame, or really against both practices)
		switch (*pdwEffect)
		{
			case DROPEFFECT_MOVE:
				_fAttributes |= kODDropIsMove;
				break;
			case DROPEFFECT_COPY:
				_fAttributes |= kODDropIsCopy;
				break;
			case DROPEFFECT_LINK:
				_fAttributes |= kODDropIsLink;
				break;
			case DROPEFFECT_NONE:
			default:
				//  _fAttributes won't indicate a move, copy, or PasteAs
				break;
		}

		//  call DragWithin or DragEnter as appropriate, and call
		//  DragLeave if we are no longer over the previous facet
		if (_fPrevFacet == targetFacet)
			bCanDrop = targetFacet->DragWithin(ev, &glocalMouse, _fDragItemIterator, kODNULL);
		else
		{
			if (_fPrevFacet)
#ifdef DAD_DEBUG
			{
				qprintf("DragLeave in PDO, #1\n");
				_fPrevFacet->DragLeave(ev, &glocalMouse, kODNULL);
			}
#else
				_fPrevFacet->DragLeave(ev, &glocalMouse, kODNULL);
#endif  // DAD_DEBUG
			bCanDrop = targetFacet->DragEnter(ev, &glocalMouse, _fDragItemIterator, kODNULL);
		}
	}
	else
	{
		//  We weren't over any facet; therefore, we
		//  must have left our source frame ...
		_fHasLeftSourceFrame = kODTrue;
		_fAttributes = kODDragHasLeftSourceFrame;

		//  and if there was a previous facet, we must
		//  also have left it
		if (_fPrevFacet)
#ifdef DAD_DEBUG
		{
			qprintf("DragLeave in PDO, #2\n");
			_fPrevFacet->DragLeave(ev, &glocalMouse, kODNULL);
		}
#else
			_fPrevFacet->DragLeave(ev, &glocalMouse, kODNULL);
#endif  // DAD_DEBUG
	}
	_fPrevFacet = targetFacet;
	_fKeyState = GetKeyState(VK_CONTROL);			//TDF: probably no longer needed

	//  If we can't drop here, make sure we show a NODROP cursor ...
	if (!bCanDrop)
		*pdwEffect = DROPEFFECT_NONE;
	gLast_dwEffect = *pdwEffect;
}

/*	METHOD: ODDragAndDrop::ProcessDragLeave
 *	
 *	DESCRIPTION:
 *		Implement the DragLeave method from IDropTarget...
 *		[This method is called in the context of the drag and drop target's process]
 */
SOM_Scope void  SOMLINK ODDragAndDropProcessDragLeave(ODDragAndDrop *somSelf,
													  Environment *ev)
{
	ODDragAndDropData *somThis = ODDragAndDropGetData(somSelf);
	ODDragAndDropMethodDebug("ODDragAndDrop","ODDragAndDropProcessDragLeave");
	qprintf("Entering ODDragAndDropProcessDragLeave:\n");

	if (_fNotDllVersion)
	{
		gDragging		= FALSE;
		gImage			= NULL;
		_fNotDllVersion	= FALSE;
	}

	//  Send a DragLeave to the last target facet, and
	//  clear all drag attributes but the following:
	_fAttributes = kODDragHasLeftSourceFrame;
	if (_fPrevFacet)
#ifdef DAD_DEBUG
	{
		qprintf("DragLeave in PDL\n");
		_fPrevFacet->DragLeave(ev, &glocalMouse, kODNULL);
	}
#else
		_fPrevFacet->DragLeave(ev, &glocalMouse, kODNULL);
#endif  // DAD_DEBUG
	_fPrevFacet = NULL;
	_fKeyState = GetKeyState(VK_CONTROL);			//TDF: probably no longer needed

	//  any given DragLeave will mean that the drag is
	//  currently leaving the source frame or must have
	//  already left the source frame on a previous
	//  DragLeave, so it is always appropriate to set
	//  this flag:
	_fHasLeftSourceFrame = kODTrue;

	//  The user's gesture is leaving the current facet - if
	//  the current facet isn't in the same process as the
	//  part that started the drag, dump our drag items
	qprintf("ProcessDragLeave: _fDragSource is %s\n", _fDragSource ? "TRUE" : "FALSE");
	if (!_fDragSource)
	{
		somSelf->Clear(ev);
#ifdef WIN32
		gImage	= 0;
#endif
	}
}

/*	METHOD: ODDragAndDrop::ProcessDrop
 *	
 *	DESCRIPTION:
 *		Implement the Drop method from IDropTarget...
 *		[This method is called in the context of the drag and drop target's process]
 *
 *		NOTE: most of the logic of this method came from Apple DR4's 'MyReceiveDropHandler'
 */
SOM_Scope void SOMLINK ODDragAndDropProcessDrop(ODDragAndDrop *somSelf,
												Environment *ev,
												DWORD grfKeyState,	// IN: Key and mouse state flags
												POINTL pt,			// IN: Drag-over location in screen coords
												DWORD *pdwEffect)	// OUT:	Drag effect that we return
{
	ODDragAndDropData *somThis = ODDragAndDropGetData(somSelf);
	ODDragAndDropMethodDebug("ODDragAndDrop","ODDragAndDropProcessDrop");
	qprintf("Entering ODDragAndDropProcessDrop:\n");

	Point		 WinPt;
	ODDropResult dropResult;
	HWND 		 platformHWND = NULL;
	ODFacet		 *targetFacet;
	ODPoint		 mouse;

	//TDF: Whazzis?
	if (_fNotDllVersion)
	{
		gDragging		= FALSE;
		gImage			= NULL;
		_fNotDllVersion	= FALSE;
	}

	//  since we made it into ProcessDrop, OLE obviously thinks that a
	//  drop has occurred, and a return from StartDrag is imminent -
	//  set the drag attributes to a default kODDragHasLeftSourceFrame
	//
	//  [note that the whole 'drag attributes' thing could stand some
	//   redesign ...]
	_fAttributes = kODDragHasLeftSourceFrame;

	//  initialize the drop effect to none (no drop)
	*pdwEffect	= DROPEFFECT_NONE;

	//  get the HWND of the main OpenDoc window for the facet we are
	//  dropping in:
	WinPt.x		= pt.x;
	WinPt.y		= pt.y;
	platformHWND = GetDocshellHWND(ev, &WinPt, _fSession);

	//  if we are over a valid window ...
	if (platformHWND)
	{
		//  Find the target facet
		ODPoint deltaOrigin = WinPt;
		deltaOrigin.x -= _fOrigin.x;
		deltaOrigin.y -= _fOrigin.y;
		ScreenToClient(platformHWND, &WinPt);
		mouse = WinPt;
		targetFacet = somSelf->FindTargetFacet(ev, platformHWND, &mouse, &mouse);

		//  if we aren't over a valid target, or if somehow we aren't
		//  really dragging anything, then just return
		if (!targetFacet || !_fDragItemList->Count())
			return;

		//  Weird stuff here. OLE gives us a drop message even if we
		//  don't care to receive one - so avoid any confusion by not
		//  letting it through ...
		if (gLast_dwEffect == DROPEFFECT_NONE)
#ifdef DAD_DEBUG
		{
			qprintf("DragLeave in PDrop\n");
			targetFacet->DragLeave(ev, &mouse, kODNULL);
		}
#else	
			targetFacet->DragLeave(ev, &mouse, kODNULL);
#endif  // DAD_DEBUG
		else
		{
			//  offset the Origin for 72DPI
			//  (use TempODWindow to complement AcquireODWindow call)
			TempODWindow tempWindow = _fWindowState->AcquireODWindow(ev, platformHWND);
			ODFacet *rootFacet = tempWindow->GetRootFacet(ev);
			ODTransform *xform = rootFacet->AcquireExternalTransform(ev, kODNULL);
			ODPoint deltaTmp = deltaOrigin;
			xform->InvertPoint(ev, &deltaTmp);
			_fOrigin.x += deltaOrigin.x - deltaTmp.x;
			_fOrigin.y += deltaOrigin.y - deltaTmp.y;
			//  mind the ref count ...
			ODReleaseObject(ev, xform);

			//  grab the target part to allow us to determine whether or not
			//  the target of the drop is the same as the source of the drop
			//  (use TempODPart to complement call to AcquirePart)
			TempODPart targetPart = targetFacet->GetFrame(ev)->AcquirePart(ev);

			//  determine the initial state of _fDropResult:

			//  start by assuming the _fDropResult will be kODDropMove
			somSelf->SetDropResult(ev, kODDropMove);

			//  if we are going across drafts, make kODDropCopy the default
			if ((_fSourcePart == kODNULL) ||
				(targetPart->GetStorageUnit(ev)->GetDraft(ev) !=
				 _fSourcePart->GetStorageUnit(ev)->GetDraft(ev)))
				somSelf->SetDropResult(ev, kODDropCopy);
			
			//  set attributes for inspection by target:

			//  first get rid of default attribute 'kODDragHasLeftSourceFrame',
			//  'cuz those whiz kids at Novell cleverly defined it to the same
			//  value as kODDropIsMove (!)
			_fAttributes = 0;

			//  now set the real 'Drop' attributes:
			if (targetFacet->GetFrame(ev) == _fSourceFrame)
				_fAttributes = kODDropIsInSourcePart | kODDropIsInSourceFrame;
			else
				if (targetPart == _fSourcePart)
					_fAttributes |= kODDropIsInSourcePart;

			//  interpret modifier keys
			if (grfKeyState & MK_ALT)					// ALT key overrides other modifiers
			  _fAttributes |= kODDropIsPasteAs;
			  //  do I also need to set drop result to Move here?
			else
			  if (grfKeyState & MK_CONTROL)				// CTRL key indicates Copy or ...
			  {
				somSelf->SetDropResult(ev, kODDropCopy);
			  	if (grfKeyState & MK_SHIFT)				// with SHIFT key indicates Link
			  	  _fAttributes |= kODDropIsLink;
				else
				  _fAttributes |= kODDropIsCopy;
			  }
			  else
			  	if (grfKeyState & MK_SHIFT)				// SHIFT key alone indicates Move
				{
				  _fAttributes |= kODDropIsMove;
				  somSelf->SetDropResult(ev, kODDropMove);
				}
				else
				  //  no modifier keys pressed - use the initial state
				  //  of _fDropResult to set the initial drop attribute:
				  if (somSelf->GetDropResult(ev) == kODDropCopy)
					_fAttributes |= kODDropIsCopy;
				  else
					_fAttributes |= kODDropIsMove;

			//  check to see whether the source wants to allow only copy
			//
			//  NOTE: Apple DR4 placed this block of code immediately after
			//  the interpretation of modifier keys, which means that any
			//  attempts by the user to override a default copy operation
			//  with modifier keys will be ignored.  Although this seems
			//  overly restrictive, we have uncovered at least one situation
			//  where this logic appears to be neccessary: dragging content
			//  that is participating in a link from one part to another
			//  part in the same draft.  In this situation, the destination
			//  part must not be allowed to modify the drag from a copy to a
			//  move because that would allow Draft code to 'reparent' the
			//  content into the destination, rather than enforcing the
			//  desired copy.  This reparenting is designed to be an
			//  optimization by the draft code, but it unfortunately opens a
			//  hole in Apple's design - if content is reparented, then both
			//  the source and the destination parts 'own' the content
			//  simultaneously until the source cleans itself up after the
			//  drop.
			//  
			//  This becomes a problem when content participating in a link
			//  is dragged from a part that supports linking to a part that
			//  does not support linking: if the user is allowed to modify
			//  the drop from a copy to a move, the non-linking destination
			//  part will receive the content and update the content's link
			//  status information to indicate the content is no longer in a
			//  link-capable environment.  However, because of the
			//  reparenting that occurs as discussed above, the source still
			//  'owns' (points to) that same dropped content.  When the Drop
			//  method invoked on the destination returns and control comes
			//  back to the source via the return from the source's
			//  StartDrag call, the source will attempt to clean up its
			//  links, but it will be unable to determine if it should
			//  update any link status that concerns the content that was
			//  involved in the drag and drop because the destination will
			//  have ALREADY cleared the relevant information from the
			//  content.  The source then disregards content that should be
			//  marked as 'no longer involved in a link', and when a
			//  corresponding link is updated, the source will trap.
			if (somSelf->GetDropResult(ev) != kODDropCopy)
			{
				ODDragItemIterator* dragItemIter = somSelf->GetDragItemIterator(ev);
				if (dragItemIter)
				{
					ODStorageUnit* su = dragItemIter->First(ev);
					ODDraft* draft = su->GetDraft(ev);
					ODCloneKind cloneKind = GetOriginalCloneKind(ev, draft);
					if (cloneKind == kODCloneCopy)
					{
						somSelf->SetDropResult(ev, kODDropCopy);
						_fAttributes &= ~kODDropIsMove;
						_fAttributes |= kODDropIsCopy;
					}
				}
			}

			//  cache the destination facet and call Drop on the part
			_fDestFacet = targetFacet;
			_fPrevFacet = NULL;
			qprintf("ProcessDrop: before drop is called on facet - drop result = %d, attributes = 0x%04X\n",
					_fDropResult, _fAttributes);
			dropResult = targetFacet->Drop(ev, &mouse, _fDragItemIterator, kODNULL);
			qprintf("ProcessDrop: targetFacet->Drop returned %d\n", dropResult);

            //  update our _fDropResult - it will remain the same
            //  value as determined before the target's Drop method
            //  was called unless the target returned kODDropFail,
            //  or if the target turned a move into a copy:
			if ((dropResult == kODDropFail) ||
				((somSelf->GetDropResult(ev) == kODDropMove) &&
				 (dropResult == kODDropCopy)))
			  somSelf->SetDropResult(ev, dropResult);
		}
	}

	//  Set the drop-effect flag properly.
	//  This will return user's intentions to OLE for
	//  subsequent interpretation by source's StartDrag
	switch (somSelf->GetDropResult(ev))
	{
		case kODDropMove:
			*pdwEffect = DROPEFFECT_MOVE;
			break;
		case kODDropCopy:
			if (_fAttributes & kODDropIsLink)
			  *pdwEffect = DROPEFFECT_LINK;
			else
			  *pdwEffect = DROPEFFECT_COPY;
			break;

		//	How do I communicate back to the source the
		//	kODDropIsPasteAs attribute?  Does the source
		//	even care or need to know?

		case kODDropFail:
		default:
			*pdwEffect = DROPEFFECT_NONE;
			break;
	}
	qprintf("ProcessDrop: returning, drop result set to %d, *pdwEffect set to %d\n",
			_fDropResult, *pdwEffect);
#ifdef WIN32
	//TDF: Whazzis?
	if (!_fDragSource)
		gImage	= 0;
#endif
	return;
}

/*	METHOD: GetDragOrigin
 *	
 *	DESCRIPTION:
 *		Return the origin point of the current drag-drop operation
 */
SOM_Scope ODPoint  SOMLINK ODDragAndDropGetDragOrigin(ODDragAndDrop *somSelf,
													  Environment *ev)
{
	ODDragAndDropData *somThis = ODDragAndDropGetData(somSelf);
	ODDragAndDropMethodDebug("ODDragAndDrop","ODDragAndDropGetDragOrigin");
	qprintf("Entering ODDragAndDropGetDragOrigin:\n");

	return somThis->fOrigin;
}

//TDF: probably no longer needed:
SOM_Scope ODULong  SOMLINK ODDragAndDropGetDropKeyState(ODDragAndDrop *somSelf,
														Environment *ev)
{
	ODDragAndDropData *somThis = ODDragAndDropGetData(somSelf);
	ODDragAndDropMethodDebug("ODDragAndDrop","ODDragAndDropGetDropKeyState");
	qprintf("Entering ODDragAndDropGetDropKeyState:\n");

	return _fKeyState;
}

/*	METHOD: AllocDragItemList
 *	
 *	DESCRIPTION:
 *		Allocate a global list of drag items
 *
 *  As near as I can tell, this method is the key to inter-process
 *  communication of drag items.  For some reason, Novell accomplished
 *  this communication by creating a temporary file for each item
 *  being dragged in the Windows system directory (!).  A data
 *  structure was then built containing a list of these filenames and
 *  the structure was passed in global memory.  During
 *  ODDragAndDrop::ProcessDragOver, this structure was examined and a
 *  new list of drag items was constructed from the contents of the 
 *  temporary files (at least, I believe that was the intent).
 *
 *  Well, this does not sound terribly efficient; I am would like to
 *  replace the file-based IPC with a shared memory-based
 *  implementation.  I plan to copy the contents of the ODMemDragItem
 *  'memory containers' to blocks of global memory, and to also pass
 *  an array of handles to those memory blocks via the ole drag and
 *  drop API.  ProcessDragOver will then use that array to recreate
 *  the ODMemDragItems on the drop side of the drag and drop
 *  interaction and in turn create a duplicate drag item list.  This
 *  may be far from the best design, but we don't have time to do it
 *  right, right?
 *  
 *  Surprise, surprise, the method outlined in the above paragraph
 *  won't work - it assumes that global memory is shareable in Win32
 *  (it isn't).  The design still holds, however, if memory mapped
 *  files (Win32's lone shared memory mechanism [d'oh!]) are used in
 *  place of GlobalAlloc memory handles.  However, I think that in the
 *  interest of expedience, I will just glop all of
 *  the data I was previously sharing in separate regions of memory
 *  into one big ugly stream of formless muck (in the grand tradition
 *  of IBM management).
 *
 *	[This method is called in the context of the drag and drop source process]
 */
SOM_Scope HGLOBAL  SOMLINK ODDragAndDropAllocDragItemList(ODDragAndDrop *somSelf,
														  Environment *ev)
{
	ODDragAndDropData *somThis = ODDragAndDropGetData(somSelf);
	ODDragAndDropMethodDebug("ODDragAndDrop","ODDragAndDropAllocDragItemList");
	qprintf("Entering ODDragAndDropAllocDragItemList:\n");

	HGLOBAL				hGlobList;
	LinkedListIterator	*theIter;
	ODDragLink			*theDragLink;
	ODMemDragItem		*theDragItem;
	LPAPPLEPART			pAP;
	ULONG				dataSize,
						curItemOffset,
						totalSize;
	PBYTE				pbFlattenedDragItems,
						pbLocalData,
						pbHGLOBAL;

	//  initially allocate memory to hold an APPLEPART
	totalSize = sizeof(APPLEPART);
	pbFlattenedDragItems = (PBYTE)malloc(totalSize);
	{
		//  write LPAPPLEPART data
		pAP	= (LPAPPLEPART)pbFlattenedDragItems;
		if (pAP)
		{
			pAP->cItems		= _fDragItemList->Count();
			pAP->gImage		= gImage;
			GetRgnBox(gImage,&pAP->gImageRect);
			pAP->gpt		= gpt;
			pAP->gDragging	= gDragging;

			//  Add 'stream' of each of the drag items to the end of the
			//  APPLEPART structure in our (growing) chunk of memory
			//  (this weird glob of data will be interpreted in 
			//  ProcessDragOver)
			curItemOffset = totalSize;
			theIter = new LinkedListIterator(_fDragItemList);					//TDF: why new rather than auto?
			for ( theDragLink = (ODDragLink*)theIter->First();
				  theDragLink;
				  theDragLink = (ODDragLink*)theIter->Next())
			{
				//  manipulate drag items via ODMemDragItem
				theDragItem = (ODMemDragItem *)theDragLink->fItem;

				//  prepare to copy current drag item contents to memory:
				//  create and lock handle to container data, assign pointer
				//  Note that drag item must be closed first!!!!
				theDragItem->Close(ev);
				dataSize = ODGetHandleSize(theDragItem->fContainerHandle);
				totalSize += dataSize + sizeof(ULONG);
				ODLockHandle(theDragItem->fContainerHandle);
				pbLocalData = (PBYTE)*((ODHandle)theDragItem->fContainerHandle);
				
				//  reallocate (expand) memory for this container's contents
				pbFlattenedDragItems = (PBYTE)realloc(pbFlattenedDragItems, totalSize);
				
				if (pbFlattenedDragItems)
				{
					//  indicate length of drag item byte stream in
					//  first ULONG of memory block
					*((ULONG *)(pbFlattenedDragItems + curItemOffset)) = dataSize;
					
					//  copy drag item byte stream to memory block
					memcpy(pbFlattenedDragItems + curItemOffset + sizeof(ULONG), 
						   pbLocalData, 
						   dataSize);

					//  update memory offset
					curItemOffset = totalSize;
				}
				ODUnlockHandle(theDragItem->fContainerHandle);
//TDF:  Should these really be disabled?
//				ODDisposeHandle(theDragItem->fContainerHandle);
//				theDragItem->fContainerHandle = (ODHandle) kODNULL;
				qprintf("in AllocDragItemList: pbFlattenedDragItems = %08p, dataSize = %d, totalSize = %d\n", 
						pbFlattenedDragItems,
						dataSize,
						totalSize);
				//  make each drag item available again by reopening it
				theDragItem->Open(ev);
			}
		}
	}

	//  if we have valid pbFlattenedDragItems, stuff them into global memory
	if (pbFlattenedDragItems)
	{
		hGlobList = GlobalAlloc(GMEM_SHARE, totalSize);
		if (hGlobList)
		{
			pbHGLOBAL = (PBYTE)GlobalLock(hGlobList);
			if (pbHGLOBAL)
			{
				memcpy(pbHGLOBAL, pbFlattenedDragItems, totalSize);
				GlobalUnlock(hGlobList);
			}
		}
		free(pbFlattenedDragItems);
	}

	delete	theIter;
	return	hGlobList;			//TDF:  uhh ... does OLE free this guy?
}

/*	METHOD: FreeDragItemList
 *	
 *	DESCRIPTION:
 *		Free our shared memory list of drag items
 */
SOM_Scope void  SOMLINK ODDragAndDropFreeDragItemList(ODDragAndDrop *somSelf,
													  Environment *ev,
                                                      HGLOBAL hGlobList)
{
	//  ODDragAndDropData *somThis = ODDragAndDropGetData(somSelf);
	ODDragAndDropMethodDebug("ODDragAndDrop","ODDragAndDropFreeDragItemList");
	qprintf("Entering ODDragAndDropFreeDragItemList:\n");

	GlobalFree(hGlobList);
}

/*	METHOD: CloseAllDragItems
 *	
 *	DESCRIPTION:
 *		Helper function to close all the current dragitems
 */
SOM_Scope void  SOMLINK ODDragAndDropCloseAllDragItems(ODDragAndDrop *somSelf,
													   Environment *ev)
{
	ODDragAndDropData *somThis = ODDragAndDropGetData(somSelf);
	ODDragAndDropMethodDebug("ODDragAndDrop","ODDragAndDropCloseAllDragItems");
	qprintf("Entering ODDragAndDropCloseAllDragItems:\n");

	if (_fDragItemList)
	{
		ODDragLink			*theDragLink;
		LinkedListIterator	*theIter	= new LinkedListIterator(_fDragItemList);
		for (theDragLink = (ODDragLink*)theIter->First();
			 theDragLink;
			 theDragLink = (ODDragLink*)theIter->Next())
			theDragLink->fItem->Close(ev);
		delete theIter;
	}
}

/*	METHOD: somUninit
 *	
 *	DESCRIPTION:
 *		At this point we may want to check the usecount on the
 *		Drag System Storage and delete it if we are the last app
 *      //TDF:  uhhh ... what?
 */
SOM_Scope void  SOMLINK ODDragAndDropsomUninit(ODDragAndDrop *somSelf)
{
	ODDragAndDropData *somThis = ODDragAndDropGetData(somSelf);
	ODDragAndDropMethodDebug("ODDragAndDrop","ODDragAndDropsomUninit");
	qprintf("Entering ODDragAndDropsomUninit:\n");

	Environment		  *ev	   = somGetGlobalEnvironment();

	//  Cleanup any resources we currently own
	somSelf->Clear(ev);
	if (_fImage)
		DeleteObject (_fImage);
}

#ifndef WRAPPER_IS_READY
//	NOTE: this function and comments were copied from ole\olewrap.cpp ...
//	it should go away when the ole wrapper is complete.

/* METHOD: SetupDropTarget
 *
 * DESCRIPTION:
 *		Register the specified window as a target of an OLE drag/drop  
 *		operation and specify the IDropTarget instance to use for drop operations.
 *
 *		RegisterDragDrop is a weak registration function within OLE -
 *		OLE will create a stub that holds the WrapIDropTarget pointer
 *		but does not call AddRef through it. After a drag drop operation
 *		accesses the target pointer from the source's process, the pointer
 *		is released (destroys the stub in the target's process).
 *		CoLockObjectExternal forces a strong lock to keep the stub within memory.
 */
SOM_Scope void  SOMLINK ODDragAndDropSetupDropTarget(ODDragAndDrop *somSelf, 
													 Environment *ev, 
                                                     HWND hwnd)					// IN: Window to register as a target
																				//     of an OLE drag/drop operation
{
	ODDragAndDropData *somThis = ODDragAndDropGetData(somSelf);
	ODDragAndDropMethodDebug("ODDragAndDrop","ODDragAndDropSetupDropTarget");
	qprintf("Entering ODDragAndDropSetupDropTarget:\n");

	if (hwnd)
	{
		if (!_fpIDropTarget)   
			_fpIDropTarget = new WrapIDropTarget();  
	  
		if (_fpIDropTarget)
		{
			_fpIDropTarget->AddRef();   
			CoLockObjectExternal((LPUNKNOWN)_fpIDropTarget, TRUE, FALSE);   
			RegisterDragDrop(hwnd, (LPDROPTARGET)_fpIDropTarget);  
		}  
	}   
}
#endif  // WRAPPER_IS_READY

/* METHOD: DraftClosing
 *
 * DESCRIPTION:
 *		This method should clean up any content that is referenced by or
 *		has a reference to the passed ODDraft object 
 */
SOM_Scope void  SOMLINK ODDragAndDropDraftClosing(ODDragAndDrop *somSelf, 
												  Environment *ev, 
												  ODDraft* draft)
{
	ODDragAndDropData *somThis = ODDragAndDropGetData(somSelf);
	ODDragAndDropMethodDebug("ODDragAndDrop","ODDragAndDropDraftClosing");
	qprintf("Entering ODDragAndDropDraftClosing:\n");

	SOM_TRY

		//  currently, we don't care what comes across in the draft pointer ...
		// if (draft) {

		//  if we aren't currently involved in a drag ...
		//  [This check is important because OLE code can create and destroy
		//  new documents in this same process during a drag.  If we don't
		//  perform this check, we will delete all our drag items before
		//  the drop completes]
		if (!gDragging)
			//  clear our drag items
			somSelf->Clear(ev);
#ifdef DAD_DEBUG
		else
			qprintf("DraftClosing called while drag in progress!\n");
#endif  // DAD_DEBUG

	SOM_CATCH_ALL



	SOM_ENDTRY
	return;
}

/* METHOD: DoProcessHDROP
 *
 * DESCRIPTION:
 *      This function processes the files obtained via an OLE
 *      CF_HDROP format by turning them into OpenDoc drag items.
 *      If all the files indicated by the GetData call are valid
 *      OpenDoc documents, then the passed DragItemList is updated
 *      by adding the actual OpenDoc storage units that each file
 *      contains.  If one or more of the files fails the OpenDoc
 *      document validation test, then discard the storage units in
 *      the list that were read in during previous iterations and
 *      create a list of storage units where each storage unit
 *      contains just the 'FileName' OpenDoc property and the
 *      actual name of the dragged file.
 *
 * NOTE:
 *		The 'file reading' portion of this function was ported from
 *		the OS/2 DragDrp.cpp dmDoReceive method
 */
void DoProcessHDROP(Environment *ev,
					LPVOID pDataObj,
					LinkedList *theDragItemList,
					ODStorageSystem *storageSystem)
{
	qprintf("Entering DoProcessHDROP:\n");
	ODMemDragItem *newItem;
	ODStorageUnit *unit;
	FORMATETC fe;
	STGMEDIUM stm;
	HRESULT hr; 
	int rc, numFiles;
	char *buf = NULL;
	ODBoolean allFilesAreOpenDoc = kODTrue;

	//  set up FORMATETC struct to query for file-based data
	fe.cfFormat = CF_HDROP;
	fe.dwAspect = DVASPECT_CONTENT;
	fe.ptd      = NULL;
	fe.tymed    = TYMED_HGLOBAL;
	fe.lindex   = -1;

	//  see if the passed IDataObject has any file-based data
	if (((IDataObject *)pDataObj)->QueryGetData(&fe) != NOERROR)
		return;
	
	//  file-based data is available, so get it
	hr = ((IDataObject *)pDataObj)->GetData(&fe, &stm);
	if (FAILED(hr))
		return;
	
	//  make sure we can use HGLOBAL storage medium to obtain data
	if ((stm.tymed != TYMED_HGLOBAL) || (!stm.hGlobal))
		return;

	//  how many files were dragged?
	numFiles = DragQueryFile(stm.hGlobal, -1, NULL, 0);
	qprintf("DoProcessHDROP: found %d files.\n", numFiles);
	
	//  interpret each dragged file:
	for (int i = 0; i < numFiles; i++)
	{
		//  get the i'th filename
		rc = DragQueryFile(stm.hGlobal, i, NULL, 0);
		if (rc)
		{
			rc++;
			buf = (char *)malloc(rc);
			rc = DragQueryFile(stm.hGlobal, i, buf, rc);
			qprintf("DoProcessHDROP: DragQueryFile filename %d: \"%s\"\n", i+1, buf);
	
			//  see if valid OpenDoc file
			if (!ValidateCandidateOpenDocDocument(buf))
			{
				qprintf("DoProcessHDROP: Found non-OpenDoc file.\n");
				allFilesAreOpenDoc = kODFalse;
				break;
			}
	
			//  assumed to be OpenDoc file, so read it in
			if (rc)
			{
				// open file and get size
				HANDLE hf = CreateFile(buf,
									   GENERIC_READ,
									   FILE_SHARE_WRITE,
									   NULL,
									   OPEN_EXISTING,
									   FILE_FLAG_SEQUENTIAL_SCAN,
									   NULL);
				if (hf != INVALID_HANDLE_VALUE)
				{
					int size = GetFileSize(hf, NULL);
					if (size != 0xFFFFFFFF)
					{
						newItem = new ODMemDragItem(storageSystem, kODTrue);
	
						// close memory container (important!) and 'size' it
						newItem->Close(ev);
						newItem->fContainerHandle = ODNewHandle(size);
						
						// read file contents into memory container
						DWORD bytesRead;
						if (!ReadFile(hf,
									  *((ODHandle)newItem->fContainerHandle),
									  size,
									  &bytesRead,
									  NULL)
							|| (bytesRead != size))
						{
							// raise some exception? continue with next file?
						}
						qprintf("     read %d bytes out of %d\n", bytesRead, size);
	
						// close file
						CloseHandle(hf);
						
						// re-open memory container (important!)
						ODBoolean fileOK = kODTrue;
						TRY
							newItem->Open(ev);
						CATCH_ALL
							fileOK = kODFalse;
						ENDTRY
	
						//  if we were able to successfully create and open the
						//  drag item, we should add an appropriate mouse down
						//  offset to the associated storage unit and add the
						//  drag item to our list:
						if (fileOK)
						{
							if(!newItem->fSU->Exists(ev, kODPropMouseDownOffset, kODPoint, 0))
						  	{
								ODPoint offset;
	
								//  just offset this relative to its position in the
								//  list of files we were given by the IDataObject:
								offset.x = ODIntToFixed(10*i);
								offset.y = ODIntToFixed(10*i);
								newItem->fSU->AddProperty(ev,
														  kODPropMouseDownOffset)->
																AddValue(ev,
																		 (ODValueType)kODPoint);
								StorageUnitSetValue(newItem->fSU, ev, sizeof(ODPoint), ODValue(&offset));
						  	}
							ODDragLink *newLink = new ODDragLink(newItem, kODTrue);
							theDragItemList->AddLast((Link*)newLink);
						}
					}
				}
			}
		}
	}  // end of for loop
	
	//  clean up
	if (buf)
	{
		free(buf);
		buf = NULL;
	}
	
	//  we are done if all the files were OpenDoc files ...
	if (allFilesAreOpenDoc)
		return;
	
    //  at least one file was not an OpenDoc file, so dump
    //  any existing drag items ...
	theDragItemList->DeleteAllLinks();

	//  ... and create a new list of 'skinny' drag items
    //  (drag items whose storage units contain only a
    //  kODFileType type and the name of a dragged file)
	for (i = 0; i < numFiles; i++)
	{
		//  for every file, create a new drag item ...
		newItem = new ODMemDragItem(storageSystem, kODTrue);
		if (newItem)
			newItem->Initialize(ev);
		else
		{
			ASSERT(newItem != kODNULL, kODErrCannotAllocateDragItem);
			return;
		}
		unit = newItem->fSU;
		rc = DragQueryFile(stm.hGlobal, i, NULL, 0);
		if (rc && unit)
		{
			rc++;
			buf = (char *)malloc(rc);
			rc = DragQueryFile(stm.hGlobal, i, buf, rc);
			qprintf("DoProcessHDROP (2nd pass): DragQueryFile filename %d: \"%s\"\n", i+1, buf);
			if (rc)
			{
				//  ... and stick the filename in the drag item's storage unit:
				unit->AddProperty(ev, kODPropContents)->AddValue(ev, kODFileType);
				StorageUnitSetValue(unit, ev, strlen(buf) + 1, buf);
	
				//  add the drag item to the list
				ODDragLink *newLink = new ODDragLink(newItem, kODTrue);
				theDragItemList->AddLast((Link*)newLink);
			}
	
			//  clean up
			if (buf)
			{
				free(buf);
				buf = NULL;
			}
		}
	}  // end of for loop
}

/* METHOD: ValidateCandidateOpenDocDocument
 *
 * DESCRIPTION:
 *		This function checks to see if the passed filename
 *		represents a valid OpenDoc document.
 *
 * RETURNS:
 *		TRUE if the passed filename is an OpenDoc document
 *		FALSE otherwise
 */
BOOL ValidateCandidateOpenDocDocument(const char *szFileName)
{
  //NOTE: add more strenuous test(s) in the future!

#define	OPENDOC_FILE_EXT	".OD"

  unsigned int odExtLength = strlen(OPENDOC_FILE_EXT),
               fileNameLength = strlen(szFileName);

  if (fileNameLength < odExtLength)
	return FALSE;

  //  compare last 'strlen(OPENDOC_FILE_EXT)' characters
  //  of szFileName to OPENDOC_FILE_EXT to see if there
  //  is a match (case insensitive)
  if (strnicmp(&szFileName[fileNameLength - odExtLength],
			   OPENDOC_FILE_EXT,
			   odExtLength)
	  == 0) 
	return TRUE;
  else
	return FALSE;
}

/* METHOD: GetDocshellHWND
 *
 * DESCRIPTION:
 *		This function attempts to determine the 'main' docshell
 *		HWND given a point on the desktop in screen coordinates
 *
 * RETURNS:
 *		The 'outer' docshell HWND if determinable, or
 *		NULL otherwise
 */
HWND GetDocshellHWND(Environment *ev, const POINT *point, ODSession* session)
{
	HWND mainHWND;

	//  if the point is no good, return NULL
	if ((mainHWND = WindowFromPoint(*point)) == NULL)
		return mainHWND;

        //  Check to see if we already have a valid ODWindow
        if (session->GetWindowState(ev)->IsODWindow(ev, mainHWND))
	  return mainHWND;

	//  ask the facet code to for a facet corresponding to
	//  the HWND
	ODFacet	*facet = _ODFacet->clsGetFacetFromHWND(ev, mainHWND);
	if (facet != kODNULL)
	{
		ODWindow *window = facet->GetWindow(ev);
		if (window != kODNULL)
		{
			mainHWND = window->GetPlatformWindow(ev);
			return mainHWND;
		}
	}

	//  couldn't associate a docshell with the HWND, so
	//  return NULL
	return NULL;
}
