//====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.41 nt/src/storage/WClipbd.cpp, oddataxfer, od96nt, odnt9712d 3/20/97 14:06:35 [ 3/21/97 17:14:00 ]
/*
	File:		Clipbd.cpp

	Contains:	Implementation for ODClipboard class.

	Owned by:	Craig Carper

	Copyright:	© 1993 - 1995 by Apple Computer, Inc., all rights reserved.

	Change History (most recent first):

		<54>	 10/9/95	CC		1289794: Avoid calling GetScrap if scrap is
									in an inconsistent state.
									1288241: SetPlatformClipboard Prevents
									exporting clipboard.
									1290782: Purge is not implemented.
									1286400: Preflight scrap memory
									allocations.
		<53>	 10/3/95	TJ		Changes done by RefBall Team
		<52>	 9/26/95	EL		1285709: ODClipboard does not close down
									properly if it was never accessed.
		<51>	  9/8/95	T‚		1281096 FB2:Many constants in ODTypesB
									without kOD prefix!
		<50>	 8/31/95	DM		1273863, 1275336: add ODClipboard::
									DraftSaved(), make GetOriginalDraft() honor
									standard endianess
		<49>	 8/30/95	EL		1279832: Storage THROW_IF_NULL problems.
		<48>	 8/29/95	DM		1276549: call parent Purge in ODClipboard
		<47>	 8/26/95	T‚		1274606 FB2: Patching Remarks
		<46>	 8/25/95	CC		1264154: Added ActionDone, ActionUndone,
									and ActionRedone methods.
		<45>	 8/21/95	VL		1277291: Use GetOriginalCloneKind from
									StorUtil.
		<44>	 8/16/95	NP		1274946: ErrorDef.idl problems. Add include
									file.
		<43>	 8/12/95	T‚		1276812 Need to use TempObjs and TempRefs
									for exception safety and to avoid TRY
									blocks, 1276807 Opt./Bug: use StdTypIO
									routines for portable streaming & smaller
									footprint
		<42>	  8/3/95	RR		#1257260: Collapse B classes. Remove
									somInit methods. Don't call IsInitialized
									or SubclassResponsibility
		<41>	 7/26/95	DM		#1270320: Memory leak fixes: dispose handle
									on return in ODClipboardImportContent
		<40>	 7/20/95	CC		1153954: ExportPlatformTypes: Don't move
									types to scrap that are not platform types.
									1260342: Clear: fail if process is in the
									background.
		<39>	  7/1/95	CC		1264197: DraftClosing must not remove
									Original Draft property.  Instead, change
									original clone kind to kODCloneCopy.
									1257374: Added ForceZeroScrap(), called
									from SetPlatformClipboard() and
									ExportClipboard() methods.
		<38>	 6/23/95	CC		1260902: ImportContent: Skip if scrap
									handle is null, and Ignore types of size
									zero.
									1257374: GetContentStorageUnit: Never issue
									debugger break on failure to import from
									the system scrap.
		<37>	 6/19/95	CC		1259079: Added local function
									IsFrontProcess.
									GetContentStorageUnit: Don't warn if
									process is in background.
		<36>	 5/26/95	VL		1251403: Multithreading naming support.
		<35>	 5/22/95	CC		1251106: CloseClipboard externalizes root
									SU to resolve promises before externalizing
									the clipboard draft.
		<34>	 5/18/95	CC		1250280: ShowPasteAsDialog throws on
									invalid null arguments.
		<33>	 5/17/95	RR		#1250135/1250137/1250143 Getters increment
									refcount
		<32>	 5/16/95	CC		1244991: ExportClipboard: Re-export
									clipboard if link spec has been removed.
									ExportClipboard: Set fOriginalDraft field.
									DiscardClipboard: Added SOM_CATCH return.
		<31>	  5/5/95	CC		1224474: ImportContent: Fixed memory leak
									due to variable theISOType
		<30>	 4/25/95	CC		1242555: PutContentOnPlatformClipboard:
									Removed obsolete $5 comment.
		<29>	 4/14/95	T‚		#1235279 BB: InfoUtil & StdTypIO functions
									should take Environment* and SU* when
									possible
		<28>	 4/11/95	CC		1231378: DraftClosing: Removed link spec if
									present.
		<27>	 3/22/95	CC		1230322: Changed parameter to
									ShowPasteAsDialog().
		<26>	 3/10/95	CC		1225050: Added DraftClosing() method.
									1227468: Clear: Set fScrapCount to ensure a
									changed clipboard is exported again.
		<25>	 2/24/95	CC		1222076: BB: Clipboard contents lost on
									error converting to Mac scrap.
									1184034:	BB: Error -108 during
									cross-document paste link.
									1186774, 1201430, 1218790:	Ignore GetScrap
									errors.
									1223020:	ODClipboard calls LoadScrap() at
									innapropriate times.
									Fixed mismatched delimiters: #include
									<ODDebug.h".
		<24>	 2/14/95	jpa		Added return statement to a SOM_CATCH
									[1215160]
		<23>	 2/10/95	CG		#1177475: Added include for StdDefs.xh.
		<22>	  2/7/95	CC		1211295: Added GetOriginalCloneKind().
									Pass isMove parameter to ShowPasteAsDialog.
									1216124: ShowPasteAsDialog aquires modal
									focus.
		<21>	  2/1/95	CC		1153802: Improvements to
									ImportStyledTextType().
									ImportContent(): Create stxt only if not
									already present.
		<20>	 1/26/95	VL		#???: Use updated Storage Unit Ref API.
		<19>	 1/25/95	CC		1153802 Conversion between 'styl' and
									'stxt' types: Added ImportStyledTextType
									and ExportStylType functions.
		<18>	 1/19/95	CC		1212419 Add parameter to
									GetMemoryContainer.
									1212833 Change to use
									kODScrapTypeODBentoContainer.
		<17>	12/20/94	VL		1195012: Make Storage calls be
									marshallable.
		<16>	 12/8/94	CC		1186774, 1201430 - Suppress errors returned
									when reading the scrap.
		<15>	 9/29/94	RA		1189812: Mods for 68K build.
		<14>	 9/23/94	VL		1155579, 1184272: Use StorUtil to
									create/get container and get its file.
		<13>	 9/19/94	CC		1187315 - somUninit() must call parent
									method last.
									1160121 - Clear() must not return an error.
		<12>	 9/15/94	CC		1186774 - Suppress GetScrap() errors.
		<11>	 8/26/94	VL		1183174: Use updated cloning APIs.
		<10>	 8/15/94	T‚		#1180922 Removed most obsolete types from
									StdTypes.idl
		 <9>	  8/3/94	CC		Removed Lock() and Unlock(); removed
									ODClipboardKey parameter from other
									methods.  (1160484)
		 <8>	  8/3/94	VL		1153123: Storage to ODStor.
		 <7>	  8/2/94	CC		#1178169 - ShowPasteAsDialog() takes
									ODFacet* instead of ODFrame*.
		 <6>	 7/31/94	CC		Change WASSERTs to WARNs.
		 <5>	 7/12/94	CC		Companion to .idl checkin -- no changes.
		 <4>	 6/28/94	VL		Used Translt.xh.
		 <3>	 6/24/94	CC		Use kODScrapTypePart constant in
									ConstDef.h.
		 <2>	 6/24/94	CC		Fleshed out ShowPasteAsDialog().
		 <1>	 6/21/94	CC		first checked in

		------------------- CC ---- Converted to SOM

		<13>	  5/9/94	MB		Changes necessary to install MMM. Bug
									#1162181.
		<12>	  4/4/94	CC		SetPlatformClipboard() &
									ExportPlatformClipboard(): Parameter
									changed from XMPTypeSet to XMPTypeList
									(1153046)
		<11>	 3/28/94	CG		1153547: Renamed XMPSessn.h to XMPSessM.h
		<10>	 3/27/94	T‚		#1153523.  Adjust name of
									kXMPPropRootPartSU to more clearly indicate
									what it is supposed to be used for.
		 <9>	 3/25/94	JA		Added missing #include (1147588)
		 <8>	 3/18/94	CC		Added counting locks to Lock() and
									Unlock(); Implementation of and calls to
									ValidateKey(). (1151853)
		 <7>	 3/16/94	CG		#1151186: Added call to InitPlatformTypeSet
									after new XMPPlatformTypeSet.
		 <6>	  3/2/94	CC		Lock() & Unlock(): Commented out erroneous
									WASSERTs. (1145487)
		 <5>	 2/28/94	VL		Changed code that may cause memory
									problems.
		 <4>	 2/22/94	VL		THROW -> THROW_IF_ERROR.
		 <3>	 2/15/94	CC		Bug #1142933 - Added key parameter to
									SetPlatformClipboard.
		 <2>	 2/15/94	CC		Bug #1142949 - Adapted to method name
									changes in XMPPlatformTypeSet.
		 <3>	  2/8/94	VL		Use new exception macros.
		 <2>	  2/4/94	VL		Session.h -> XMPSessn.h.
		 <1>	  2/4/94	VL		first checked in

		<21>	  2/4/94	VL		Moved to PPC Header and began code cleanup.
		<20>	 1/28/94	CC		Implemented change ids; replaced DebugStrs
									with WASSERTMs.
		<19>	 1/21/94	CC		Converted from XMPTimeStamp to XMPUpdateID.
									Added GetUpdateID.  GetLock() returns
									boolean result and returns key in second
									parameter.
		<18>	 1/21/94	CG		Renamed kXMPMemoryContainer to
									kXMPDefaultMemoryContainer.
		<17>	 1/18/94	CG		Added include for StorgDef.yh
		<16>	 1/18/94	CC		Use kXMPScrapTypePart; release draft
									properties on failure in NewClipboard() and
									ImportClipboard().
		<15>	 1/14/94	CC		Added type XMPClipboardKey, added key
									parameter to various routines, replaced
									GetGeneration() with GetTimeStamp, renamed
									AcquireStorageUnit() GetContentStorageUnit();
									changed from using a handle to a pointer in
									ExportPlatformTypes() and
									PutContentOnPlatformClipboard().
		<14>	 1/11/94	T‚		Init... changes
		<13>	12/21/93	VL		Changed XMPStorageUnit::GetValueSize to
									StorageUnit::GetSize.
		<12>	12/15/93	T‚		more InitObject changes, remove
									Initialize() method
		<11>	12/15/93	T‚		InitObject changes
		<10>	 12/2/93	CC		Added Lock(), Unlock(), and GetGeneration()
									methods.
		 <9>	11/10/93	CC		Doesn't externalize draft before disposing
									memory container
		 <8>	 11/1/93	VL		Used Strong reference.
		 <7>	10/29/93	RR		XMPClipboard:: -> XMPMacClipboard
		 <6>	10/27/93	CC		Added methods to transfer to/from the
									platform clipboard.
		 <5>	10/20/93	CC		Added session parameter to XMPClipboard
									constructor; added SetPlatformClipboard
									method; added code to create the clipboard
									container.
		 <3>	 4/29/93	VL		More 8.3 Name Change.
		 <2>	 4/29/93	VL		8.3 Name Change.
		 <1>	 4/27/93	VL		first checked in

	To Do:
	¥ Reuse fContainerHandle when possible after exporting it.
	¥ Add a resolvePromises parameter to ExportClipboard (but maybe we need to
		do this all the time).
	¥ Vincent will add a draft change seed.  The clipboard object can examine
		the seed for the clipboard draft to determine if the clipboard has
		been changed since it was exported (if the clipboard generation is the
		same).
	¥ XMPClipboard::Clear() should clear the system clipboard.
	
	In Progress:
		
*/


#define VARIABLE_MACROS

#define ODClipboard_Class_Source
#include <Clipbd.xih>

#ifndef SOM_Module_OpenDoc_StdDefs_defined
#include <StdDefs.xh>
#endif

#ifndef _EXCEPT_
#include <Except.h>
#endif

#ifndef _ODMEMORY_
#include <ODMemory.h>
#endif

#ifndef _CONSTDEF_
#include <ConstDf.h>
#endif

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

#ifndef _PLFMDEF_
#include <PlfmDef.h>
#endif

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

#ifndef _ODUTILS_
#include <ODUtils.h>
#endif

#ifndef SOM_Module_OpenDoc_StdProps_defined
#include <StdProps.xh>
#endif

#ifndef SOM_Module_OpenDoc_StdTypes_defined
#include <StdTypes.xh>
#endif

#ifndef SOM_Module_OpenDoc_Foci_defined
#include <Foci.xh>
#endif

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

#ifndef SOM_ODArbitrator_xh
#include <Arbitrat.xh>
#endif

#ifndef SOM_ODContainer_xh
#include <ODCtr.xh>
#endif

#ifndef SOM_ODDocument_xh
#include <Document.xh>
#endif

#ifndef SOM_ODDraft_xh
#include <Draft.xh>
#endif

#ifndef SOM_ODStorageSystem_xh
#include <ODStor.xh>
#endif

#ifndef SOM_ODStorageUnit_xh
#include <StorageU.xh>
#endif

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

#ifndef SOM_ODTypeList_xh
#include <TypeList.xh>
#endif

#ifndef SOM_ODPlatformTypeList_xh
#include <PfTypLs.xh>
#endif

#ifndef _LINKDLGS_
#include <LinkDlgs.h>
#endif

#ifndef _ODDEBUG_
#include <ODDebug.h>
#endif

#ifndef __ERRORS__
#include <Errors.h>
#endif

#ifndef _STDTYPIO_
#include <StdTypIO.h>
#endif

#ifndef _TEMPOBJ_
#include <TempObj.h>
#endif

#ifndef _STORUTIL_
#include <StorUtil.h>
#endif

#ifndef _UTILERRS_
#include "UtilErrs.h"
#endif

#ifndef _ODUTILW_
#include "ODUtilW.h"
#endif

#ifndef _OLEDATA_H_
#include "OleData.h"
#endif

#ifndef _OLEXFER_H_
#include "OleXfer.h"
#endif

#ifndef SOM_OLEPromisKeeper_xh
#include <oledpkpr.xh>
#endif 

#ifndef SOM_Module_2e5coledefs_OpenDoc_OleDefs_defined
#include "OleDefs.xh"				//  for kODKindOlePart and kODKindOcxPart
#endif

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

#ifndef SOM_RegistryManager_xh
#include "RManager.xh"	
#endif

#ifndef SOM_ODPartHandlerRegistry_xh
#include "ODPrtReg.xh"	
#endif

#ifndef SOM_ODTypeListIterator_xh
#include <TypLsItr.xh>
#endif

#ifndef _ISTRHDR_
#include <istrhdr.h>
#endif
 
#ifndef _BARRAY_
#include <BArray.h>
#endif

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

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

#ifndef SOM_ODShape_xh
#include <Shape.xh>
#endif


#include <ODMessag.h> // Header file for catalog IDs

#pragma segment ODClipboard

#include "ClipbdB.cpp"	// Platform-independent methods, if any

#if ODDebug
#define ODDebugClipboard 0
#undef _REPORT_SCRAP_ERRORS_
#else
#define ODDebugClipboard 0
#undef _REPORT_SCRAP_ERRORS_
#endif

#if DEBUG
#include "qprintf.h"
#include <wchar.h>
#endif //DEBUG
//==============================================================================
// Constants
//==============================================================================

#define MagicByteSequence "\xA4""CM""\xA5""Hdr""\xD7"  // Bento eye-catcher copied from Containr.h

const short kInvalidScrapCount = -1;	// Must never match the scrap count!

//==============================================================================
// Global variables
//==============================================================================
// clipboardUpdateCount is incremented any time WIN32 changes the platform clipboard.
UINT clipboardUpdateCount = 0;
// pendingClear is set to true between the time when we clear the clipboard and the time
// we receive a WM_DRAWCLIPBOARD for that clear.  During this time the clipboardUpdateCount
ODBoolean pendingClear = kODFalse;

// globalPromiseCount keeps track of how many promises we've made
// using OlePromiseKeeper.  We need to issue that many releases against 
// the promise keeper when we destroy it.
ULONG globalPromiseCount = 0;

//==============================================================================
// Function Prototypes
//==============================================================================

ODStatic void ODCloseClipboard(ODClipboard *somSelf, Environment *ev, ODBoolean externalize);
ODStatic void ODOpenClipboard(ODClipboard *somSelf, Environment *ev);
ODStatic short GetMacScrapCount();
BOOL CopyBentoIStreamToODClipboard(IDataObject*, CLIPFORMAT, ODStorageUnit*, ODSession* session);
BOOL IsBentoStream(IDataObject*, CLIPFORMAT, HRESULT*);

/* Constants
 */
#define kODClipboardMonitorClass 	"OpenDoc:ClipboardMonitor"
#define kODClipboardObject			0
#define kODhWndNext					4

/* Scalar Types
 */

/* external functions
 */
//extern void PromiseOleFormats(IDataObject *pIDataObject);
//extern void ExportOlePart(Environment *ev, void* pDataObject, void* storageUnit);
/* Local functions
 */
LRESULT CALLBACK ClipboardMonitorWndProc(HWND,UINT,WPARAM,LPARAM);
ODBoolean RegisterClipboardMonitorClass(HINSTANCE);

//==============================================================================
// Utility Functions
//==============================================================================

//------------------------------------------------------------------------------
// SetOriginalCloneKind
//------------------------------------------------------------------------------

static void SetOriginalCloneKind(Environment* ev, ODDraft* draft, ODCloneKind cloneKind)
{
	if ( draft != kODNULL )
	{
		TempODStorageUnit draftProperties = draft->AcquireDraftProperties(ev);
		ODSetULongProp(ev, draftProperties, kODPropOriginalCloneKind, kODULong, (ODULong)cloneKind);
	}
}

//------------------------------------------------------------------------------
// GetOriginalDraft
//------------------------------------------------------------------------------
// Returns kODNULL if the original draft is unknown.  This is the case when content
// was placed in the draft without cloning.

static ODDraft* GetOriginalDraft(Environment* ev, ODDraft* draft)
{
	TempODStorageUnit	draftProperties = draft->AcquireDraftProperties(ev);
	return (ODDraft*)ODGetULongProp(ev, draftProperties, kODPropOriginalDraft, kODULong);
}

//==============================================================================
// ODClipboard
//==============================================================================

//------------------------------------------------------------------------------
// ODClipboard::somUninit
//------------------------------------------------------------------------------

SOM_Scope void  SOMLINK ODClipboardsomUninit(ODClipboard *somSelf)
{
	ODClipboardData *somThis = ODClipboardGetData(somSelf);
	ODClipboardMethodDebug("ODClipboard","somUninit");
	
	Environment *ev = somGetGlobalEnvironment();
	// somSelf->DiscardClipboard(ev); 
	// It is incorrect to call methods on somSelf inside somUninit, 
	// a subclass may have already been somUninited.  See OpenDoc Building Code for details.
	// Instead the implementation of DiscardClipboard has been copied here.
	
	// BEGIN copy of DiscardClipboard implementation
	  //  This is pretty ugly.  We need to decrement the reference count for _fPromiseKeeper.  We
	  //  incremented it when it was first created, and then it has been further incremented 
	  //  every time SetPromiseValue was called in ODClipboard::ImportContents.  The only way I can
	  //  see to decrement it correctly is to fulfill all the promises.  So the logic here is:
	  //
	  //  IF we put any promises in our storage unit (if globalPromiseCount > 0)
	  //     for each value in the contents property:
	  //        Get its size
          //
	  //  The GetSize method will fulfill the promises, forcing the reference count down.
	  //	
	if (_fPromiseKeeper)
	{
	  ODULong index, theSize;

          if (globalPromiseCount > 0) {
			_fSU->Focus(ev,kODPropContents, kODPosUndefined, 0, 0, kODPosUndefined);
			ODULong countOfValues = _fSU->CountValues(ev);

			for ( index = 1; index <= countOfValues; ++index)
			{
				_fSU->Focus(ev,
							kODPropContents,
							kODPosUndefined,
							0,
							index,
							kODPosUndefined);
				theSize = _fSU->GetSize(ev);
				//				_fPromiseKeeper->Release(ev);
			}

	  }

        	globalPromiseCount = 0;
	        ODRelease(ev, _fPromiseKeeper);
		delete	_fPromiseKeeper;
		_fPromiseKeeper	= NULL;
	}
	ODCloseClipboard(somSelf, ev, kODFalse);
	
	ODDisposeHandle(_fContainerHandle);
	_fContainerHandle = (ODHandle) kODNULL;
	// END copy of DiscardClipboard implementation
        // rrk 2 jul 96 - if our data is on the clipboard, perform a flush.  This allows formats
	//                to stay on the clipboard after we're gone.
	if (  _fpIDataObjectLast
	   && SUCCEEDED(OleIsCurrentClipboard((IDataObject*)_fpIDataObjectLast))
	   )
	  OleFlushClipboard();

	if(_fhwndClipboardMonitor)
		DestroyWindow(_fhwndClipboardMonitor);

	parent_somUninit(somSelf);
}

//------------------------------------------------------------------------------
// ODClipboard::InitClipboard
//------------------------------------------------------------------------------

SOM_Scope void  SOMLINK ODClipboardInitClipboard(ODClipboard *somSelf, Environment *ev,
		ODSession* session)
{
    ODClipboardData *somThis = ODClipboardGetData(somSelf);
    ODClipboardMethodDebug("ODClipboard","InitClipboard");

	/* Moved from somInit. SOM itself sets fields to zero
	_fSession			= (ODSession*) kODNULL;
	_fContainerHandle	= (ODHandle) kODNULL;
	_fContainer			= (ODContainer*) kODNULL;
	_fDocument			= (ODDocument*) kODNULL;
	_fDraft				= (ODDraft*) kODNULL;
	_fSU				= (ODStorageUnit*) kODNULL;
	
	_fOriginalDraft		= (ODDraft*) kODNULL;
	_fExportedLinkSpec	= kODFalse;

	_fClonePasteCount	= 0;
	*/
	somSelf->InitObject(ev);
			
	_fSession = session;

	// Initialize our scrap count to an invalid value so a paste will force copying
	// from the platform scrap.
	_fScrapCount			= kInvalidScrapCount;
	_fScrapCountLastChange	= kInvalidScrapCount;

	_fOleManager = session->GetOleManager(ev);

	// Initialize our scrap count to an invalid value so a paste will force copying
	// from the platform scrap.

	RegisterClipboardMonitorClass(_fSession->GetWindowSystemData(ev)->instance); // rrk 24 apr 96
	_fUpdateID = kODUnknownUpdate;
	_fNextUpdateID = _fSession->UniqueUpdateID(ev);
	_fhwndClipboardMonitor	= CreateWindowEx(0L,
											 kODClipboardMonitorClass,		
											 "OpenDoc Clipboard Monitor",
											 WS_OVERLAPPEDWINDOW,
											 0, 0, 0, 0,
											 NULL,	NULL,	
						                                         _fSession->GetWindowSystemData(ev)->instance,

											 somSelf);

	_fOriginalCloneKind = kODCloneCopy;

}

//------------------------------------------------------------------------------
// ODClipboard::Purge (OVERRIDE)
//------------------------------------------------------------------------------

SOM_Scope ODSize  SOMLINK ODClipboardPurge(ODClipboard *somSelf, Environment *ev,
	ODSize size)
{
	ODClipboardData *somThis = ODClipboardGetData(somSelf);
	ODClipboardMethodDebug("ODClipboard","Purge");
	
	ODSize freed = 0;
	return freed;
}

//------------------------------------------------------------------------------
// ODClipboard::NewClipboard
//------------------------------------------------------------------------------

SOM_Scope void  SOMLINK ODClipboardNewClipboard(ODClipboard *somSelf, Environment *ev)
{
    ODClipboardData *somThis = ODClipboardGetData(somSelf);
    ODClipboardMethodDebug("ODClipboard","NewClipboard");

	SOM_TRY

	ODStorageUnit* draftProperties = (ODStorageUnit*) kODNULL;
	
	ODVolatile(draftProperties);
	ODVolatile(somSelf);
	ODVolatile(ev);

	TRY
		// Create a new clipboard in-memory container
		_fContainerHandle = ODNewHandle(0);
		_fContainer = CreateMemoryContainer(ev, _fSession, _fContainerHandle, kODBentoMemoryContainer);

		// Create the root content storage unit
		_fDocument = _fContainer->AcquireDocument(ev, kODDefaultDocument);
		_fDraft = _fDocument->AcquireBaseDraft(ev, kODDPExclusiveWrite);
		_fSU = _fDraft->CreateStorageUnit(ev);

		// Store a reference to the content storage unit in the draft properties
		draftProperties = _fDraft->AcquireDraftProperties(ev);
		ODSetStrongSURefProp(ev, draftProperties, kODPropRootPartSU, kODStrongStorageUnitRef, _fSU->GetID(ev));	
		draftProperties->Release(ev);

	CATCH_ALL
#ifdef _REPORT_SCRAP_ERRORS_
		WARNMSG_DEBUG(WARN_INDEX(-1),"ODClipboard: Cannot create clipboard - error %ld",ErrorCode());
#endif
		somSelf->DiscardClipboard(ev);
		ODReleaseObject(ev, draftProperties);
		RERAISE;
	ENDTRY

	SOM_CATCH_ALL

	SOM_ENDTRY
	  return;
}

//------------------------------------------------------------------------------
// ODCloseClipboard
//------------------------------------------------------------------------------
// Close the current clipboard container, without disposing of the container handle.
// Note: ODCloseClipboard must not make any method calls to somSelf, since
//		 ODCloseClipboard is called from the somUninit method.

ODStatic void ODCloseClipboard(ODClipboard *somSelf, Environment *ev, ODBoolean externalize)
{
	ODClipboardData *somThis = ODClipboardGetData(somSelf);
	
	if ( externalize && _fDraft )
	{
		// Externalize may fail with an iterator-out-of-sync error because 
		// resolving promises may add storage units to the draft's collection
		// during iteration.  In theory, the only promises should be in the
		// root storage unit, so explicitly externalize it first to resolve
		// promises before interating over all storage units.
		if ( _fSU )
			_fSU->Externalize(ev);
		_fDraft->Externalize(ev);
	}

	ODReleaseObject(ev,_fSU);
	ODReleaseObject(ev,_fDraft);
	ODReleaseObject(ev,_fDocument);
	ODReleaseObject(ev,_fContainer);

}

//------------------------------------------------------------------------------
// ODClipboard::DiscardClipboard
//------------------------------------------------------------------------------

SOM_Scope void  SOMLINK ODClipboardDiscardClipboard(ODClipboard *somSelf, Environment *ev)
{
	ODClipboardData *somThis = ODClipboardGetData(somSelf);
	ODClipboardMethodDebug("ODClipboard","DiscardClipboard");

	SOM_TRY
	  //  This is pretty ugly.  We need to decrement the reference count for _fPromiseKeeper.  We
	  //  incremented it when it was first created, and then it has been further incremented 
	  //  every time SetPromiseValue was called in ODClipboard::ImportContents.  The only way I can
	  //  see to decrement it correctly is to fulfill all the promises.  So the logic here is:
	  //
	  //  IF we put any promises in our storage unit (if globalPromiseCount > 0)
	  //     for each value in the contents property:
	  //        Get its size
          //
	  //  The GetSize method will fulfill the promises, forcing the reference count down.
	  //	
	if (_fPromiseKeeper)
	{
	  ODULong index, theSize;

          if (globalPromiseCount > 0) {
			_fSU->Focus(ev,kODPropContents, kODPosUndefined, 0, 0, kODPosUndefined);
			ODULong countOfValues = _fSU->CountValues(ev);

			for ( index = 1; index <= countOfValues; ++index)
			{
				_fSU->Focus(ev,
							kODPropContents,
							kODPosUndefined,
							0,
							index,
							kODPosUndefined);
				theSize = _fSU->GetSize(ev);
				//				_fPromiseKeeper->Release(ev);
			}

	  }

        	globalPromiseCount = 0;
	        ODRelease(ev, _fPromiseKeeper);
		delete	_fPromiseKeeper;
		_fPromiseKeeper	= NULL;
	}

	ODCloseClipboard(somSelf, ev, kODFalse);
	
	ODDisposeHandle(_fContainerHandle);
	_fContainerHandle = (ODHandle) kODNULL;

	/* Discard our promise keeper, if we have one
	 */


	SOM_CATCH_ALL

	SOM_ENDTRY
	  return;
}

/* METHOD: ImportContent
 *
 * DESCRIPTION:
 *	 Copy resources from the OLE clipboard to values in the root storage unit
 *   of this OpenDoc clipboard object.  THROWs an exception if an error occurs,
 *   which may leave the clipboard in the partially-imported state.
 */
SOM_Scope void SOMLINK ODClipboardImportContent(
	ODClipboard		*somSelf,
	Environment		*ev,
	void			*pDataObj)
{
	ODClipboardData 	*somThis 		= ODClipboardGetData(somSelf);
	LPENUMFORMATETC		pEnumFE		  	= NULL;
	IDataObject			*pIDataObject 	= (IDataObject*)pDataObj;
	ODType				theISOType;
	FORMATETC			fe;
	ULONG				cfe;

	pIDataObject->EnumFormatEtc(DATADIR_GET,&pEnumFE);
	globalPromiseCount = 0;
	if (pEnumFE)		
	{
		/* If we haven't yet created our PromiseKeeper, do it now. He has to
		 * honour 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,pIDataObject);
			_fPromiseKeeper->Acquire(ev);
		}
		else
			return;


		/* For all the formats available from the IDataObject
		 */
		while ( !FAILED(pEnumFE->Next(1,&fe,&cfe)) && cfe)
		{
			/* Get the ISO type for this FORMATETC, and write it into
			 * the content list of the clipboard storage unit. Shove a
			 * promise in, so that we don't have to actually get the data
			 * from the other process until the object reads the data.
			 */
			theISOType = _fSession->GetTranslation(ev)->GetISOTypeFromPlatformType(
							ev,
							(ODPlatformType) fe.cfFormat,
							kODPlatformDataType);
			if (theISOType != (ODType)kODNULL)
			{
			  if (    (ODISOStrEqual("Embed Source", theISOType))
		               || (ODISOStrEqual(kODObjSpec,     theISOType))
			       || (ODISOStrEqual(kODLinkSpec,    theISOType))
			     )
			    {

				// 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;  // used only in Drag and Drop
				if( IsBentoStream(pIDataObject, fe.cfFormat, &GetDataHRESULT) ) 
				{
 					    CopyBentoIStreamToODClipboard(pIDataObject, fe.cfFormat, _fSU, _fSession);

				} 
				else
				{
				     if (! _fSU->Exists(ev,kODPropContentFrame, kODWeakStorageUnitRef, 0)) 
				     {
				       _fSU->AddProperty(ev, kODPropContentFrame);
				       StorageUnitSetPromiseValue(_fSU, ev,kODWeakStorageUnitRef,0,0,kODNULL,_fPromiseKeeper);
				     }

				}
			    }
			    else
			    {
				  _fSU->AddProperty(ev,kODPropContents);

				  StorageUnitSetPromiseValue(_fSU, ev,theISOType,0,0,kODNULL,_fPromiseKeeper);
				  globalPromiseCount++;

				  ODDisposePtr(theISOType);
			    }
			  // We need to put kODLinkSpec into the contents property if "Link Source" was one of
			  // the formats provided.  We do this for one reason:  if the IDataObject is pure Ole
			  // (no OpenDoc) and if the user requests the PasteAs dialog, the dialog procedure needs
			  // to decide whether or not to enable the "paste link" button.  In the pure Ole case
			  // it won't find a kODPropLinkSpec property (signifying OpenDoc link), but it should
			  // be able to see that the source Ole app allows linking.  kODLinkSpec in the contents
			  // property is the indicator of this condition. 
			  if (ODISOStrEqual(kODLinkSpec, theISOType))
			    {
				  _fSU->AddProperty(ev,kODPropContents);

				  StorageUnitSetPromiseValue(_fSU, ev,theISOType,0,0,kODNULL,_fPromiseKeeper);
				  globalPromiseCount++;

				  ODDisposePtr(theISOType);
			    }
			}
		}

		/* OlePart: provide a direct pointer to the IDataObject interface
		 * so that OlePart can paste or paste-link in the usual OLE manner
		 */
		_fSU->AddProperty(ev,kODDataObject)->AddValue(ev,kODULong);
		StorageUnitSetValue(_fSU,ev,sizeof(pDataObj),&pDataObj);

		pEnumFE->Release();
	}
}


/* METHOD: ExportPlatformTypes
 *
 * DESCRIPTION:
 *   Export data of the argument platform type to the host clipboard.
 *   Since only one ISO type corresponds directly to a platform type (no
 *   translation is attempted), we just examine each value on the clipboard
 *   and check if its platform equivalent is present in the argument set.
 */
SOM_Scope void SOMLINK ODClipboardExportPlatformTypes(
	ODClipboard			*somSelf,
	Environment			*ev,
	ODPlatformTypeList	*typeList,
	ODBoolean			bExportClipboard)
{
	ODClipboardData *somThis 	= ODClipboardGetData(somSelf);
	IDataObject		*pIDataObject	= NULL;
	ODULong			countOfValues;
	ODULong			index;
	ODULong			countToExport;
	ODType			theISOType;
	ODULong			dataSize;
	ODPlatformType	platformType;
	STGMEDIUM		stm;
	FORMATETC		fe;
	ODPtr			data;
	HGLOBAL			hdata;
	HRESULT			hr;

	/* Create a data transfer object
	 */       			        // LATER: use CoCreateInstance here perhaps?
#ifdef LATER // rrk  30 Apr 96 We are not yet certain whether we'll need or want a factory for OleData.
	     //                We are leaning toward "not."  Anyway, this CreateInstance call is copied 
	     //                from Novell - it would need to be changed if we were to use it.  
	     //
	     //                For now we will use "new" to get an OleData, and initialize it via
	     //                OleData::Initialize, which is what the factory would do.
	((IClassFactory*) gpProcessData->gDataFactory)->CreateInstance(
													NULL,
													IID_IDataObject,
													(void**)&pIDataObject);
#endif

	OleData *tempOleData = new OleData();
	tempOleData->Initialize(_fOleManager->fCFEmbeddedObject,
				_fOleManager->fCFEmbedSource,
				_fOleManager->fCFObjectDescriptor,
				_fOleManager->fCFOleWrapper);
	tempOleData->QueryInterface(IID_IDataObject, (void**)&pIDataObject);

	if (pIDataObject)
	{
                // rrk 20mar97 - if we are read-only, don't export a linkspec
                ODWindow *window = _fSession->GetWindowState(ev)->AcquireActiveWindow(ev);
                if(window) {
                   ODBoolean isDraftReadOnly = (window->GetDraft(ev)->GetPermissions(ev) == kODDPReadOnly);
                   //PRINT("ExportClipboard: isDraftReadOnly is %d.\n", isDraftReadOnly);
                   if (isDraftReadOnly)
		      if ( _fSU->Exists(ev, kODPropLinkSpec, kODNULL, 0) )
			      ODSURemoveProperty(ev, _fSU, kODPropLinkSpec);
                   ODReleaseObject(ev,window);
                }
		/* If the storage unit contains just an OlePart object, then put up
		 * just the OLE "Embedded Object", "Object Descriptor" and cached
		 * image information...
		 */
		if (  bExportClipboard
		   && (  _fSU->Exists(ev,kODPropContents, kODKindOlePart, 0)
		      || _fSU->Exists(ev,kODPropContents, kODKindOcxPart, 0)
			  )
		   ) {
			ExportOlePart(ev,pIDataObject,_fSU);
			somSelf->Clear(ev);
		     }
		else
		{  
			/* Figure out how many things we have to export, either by counting the
			 * number of items in the contents of this storage unit, or from the
			 * passed in set of types.
			 */
			_fSU->Focus(ev,kODPropContents, kODPosUndefined, 0, 0, kODPosUndefined);
			countOfValues = _fSU->CountValues(ev);
			if (typeList != (ODPlatformTypeList*) kODNULL)
				countToExport = typeList->Count(ev);
			else
				countToExport = countOfValues;
		
			/* For each of the values to be exported
			 */
			for ( index = 1;
				  (index <= countOfValues) && (countToExport > 0);
				  ++index
				)
			{
				/* Get the type of the index'th value
				 */
				_fSU->Focus(ev,
							kODPropContents,
							kODPosUndefined,
							0,
							index,
							kODPosUndefined);
				theISOType = _fSU->GetType(ev);
		
				/* Translate the type into a Windows clipboard format
				 */
				platformType = _fSession->GetTranslation(ev)->GetPlatformTypeFromISOType(ev,theISOType);
				if (  (typeList == (ODPlatformTypeList*) kODNULL)
				   || (typeList->Contains(ev,platformType))
				   )
				{
					/* Get the bits and bytes of this value into an allocated block
					 * of memory.
					 */
					_fSU->Focus(ev,
								kODPropContents,
		        					kODPosUndefined,
								theISOType,
								0,
								kODPosUndefined);
					dataSize = _fSU->GetSize(ev);
					TRY
						data  = NULL;
						hdata = GlobalAlloc(GMEM_SHARE,dataSize);
						if (hdata)
						{
							data	= GlobalLock(hdata);
							if (data)
								StorageUnitGetValue(_fSU, ev,dataSize, (ODValue) data);
							else
								THROW(kODErrOutOfMemory);
						}
						else
							THROW(kODErrOutOfMemory);
					CATCH_ALL
						ODDisposePtr(theISOType);
						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,(unsigned long)platformType,TYMED_HGLOBAL);

					hr 	    = pIDataObject->SetData(&fe, &stm, TRUE);
					GlobalUnlock(hdata);
					WASSERTMSG_DEBUG(SUCCEEDED(hr),
							 "ODClipboard: failure on IDataObject::SetData");
					if (FAILED(hr))
					{
						GlobalFree(hdata);
						ODDisposePtr(theISOType);
						pIDataObject->Release();
						THROW(kODErrCannotExportToIDataObject);
					}
					/* LATER: who frees the memory we allocated when things work?
					 */
		
					/* Remove this type from the set of types passed in
					 */
					if (typeList != (ODPlatformTypeList*) kODNULL)
						typeList->Remove(ev,platformType);
					countToExport--;
				}
				ODDisposePtr(theISOType);
			}
                        // 26 Mar 96 rrk; 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(_fSU->Exists(ev, kODPropLinkSpec, kODNULL, 0) )
	       		{
			        _fSU->Focus(ev, kODPropLinkSpec,kODPosUndefined, /*kODNULL*/ kODLinkSpec, 0, kODPosUndefined);	  

				// read the version out of here first, OK?

				ODULong version;
				StorageUnitGetValue(_fSU, ev, sizeof(ODULong), (ODValue) &version); // rrk 11 jun 96

				dataSize = _fSU->GetSize(ev);
				TRY
				    data  = NULL;
				    hdata = GlobalAlloc(GMEM_SHARE,dataSize);
				    if (hdata)
				       {
					  data	= GlobalLock(hdata);
					  if (data)
					       StorageUnitGetValue(_fSU, 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)_fOleManager->fCFLinkSource,TYMED_ISTREAM);


			        hr 	    = pIDataObject->SetData(&fe, &stm, TRUE);                        
			        GlobalUnlock(hdata);
			        WASSERTMSG_DEBUG(SUCCEEDED(hr),
			        "ODClipboard: failure on IDataObject::SetData");
			        if (FAILED(hr))
        			{
        			   GlobalFree(hdata);
	        		   ODDisposePtr(theISOType);
		        	   pIDataObject->Release();
			           THROW(kODErrCannotExportToIDataObject);
		        	}
	   	       	}
                        // 6 Jun 96 rrk; 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(_fSU->Exists(ev, kODPropLinkSpec, kODLinkSpecDescriptor, 0) )
	       		{
			        _fSU->Focus(ev, kODPropLinkSpec,kODPosUndefined, kODLinkSpecDescriptor, 0, kODPosUndefined);
				dataSize = _fSU->GetSize(ev);
				TRY
				    data  = NULL;
				    hdata = GlobalAlloc(GMEM_SHARE,dataSize);
				    if (hdata)
				       {
					  data	= GlobalLock(hdata);
					  if (data)
					       StorageUnitGetValue(_fSU, 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)_fOleManager->fCFLinkSrcDescriptor,TYMED_HGLOBAL);

				hr 	    = pIDataObject->SetData(&fe, &stm, TRUE);                        
				GlobalUnlock(hdata);
				WASSERTMSG_DEBUG(SUCCEEDED(hr),
						 "ODClipboard: failure on IDataObject::SetData");
				if (FAILED(hr))
				  {
				    GlobalFree(hdata);
				    ODDisposePtr(theISOType);
				    pIDataObject->Release();
				    THROW(kODErrCannotExportToIDataObject);
				  }
	   	       	}
		        /* Always promise an OLE-embeddable object 			
			 */
			//#ifdef _LATER_ // rrk PromiseOleFormats is defined in OleWrap.h -- removed ifdef rrk 4 jun 96
			PromiseOleFormats(pIDataObject);	
			//#endif			
	
			/* Export the whole clipboard storage unit
			 */
			if (bExportClipboard)
				somSelf->PutClipboardOnPlatformClipboard(ev,pIDataObject);


			// dwj - Promise CF_METAFILEPICT with HGLOB_BOGUS in the STGMEDIUM unit.  
			// When DataIDataObject::GetData sees the HGLOB_BOGUS is will get the kOKApplePart 
			// (in other words, the original OpenDoc clipboard) from the OLE Clipboard
			// 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);


		}

		/* Finally, put the IDataObject we created up to the clipboard - it
		 * now contains all the data as separate FORMATETC's. This API
		 * addrefs!
		 */

		OleSetClipboard(pIDataObject);
		// release the clipboard so that the clipboard becomes owner
		pIDataObject->Release();
		_fpIDataObjectLast	= pIDataObject;
	}
}


//------------------------------------------------------------------------------
// ODOpenClipboard
//------------------------------------------------------------------------------

ODStatic void ODOpenClipboard(ODClipboard *somSelf, Environment *ev)
{
    ODClipboardData *somThis = ODClipboardGetData(somSelf);

	ODStorageUnitID	suID;
	
	ODVolatile(somSelf);
	ODVolatile(ev);

	if ( _fContainerHandle )
	{
		TRY
			_fContainer = GetMemoryContainer(ev, _fSession, _fContainerHandle, kODBentoMemoryContainer);		
			_fDocument = _fContainer->AcquireDocument(ev, kODDefaultDocument);
			_fDraft = _fDocument->AcquireBaseDraft(ev, kODDPExclusiveWrite);
	
			{ TempODStorageUnit draftProperties = _fDraft->AcquireDraftProperties(ev);
			  suID = ODGetStrongSURefProp(ev, draftProperties, kODPropRootPartSU, kODStrongStorageUnitRef);
			}
	
			_fSU = _fDraft->AcquireStorageUnit(ev, suID);
		CATCH_ALL
			ODCloseClipboard(somSelf, ev, kODFalse);
			RERAISE;
		ENDTRY
	}
}
	
/* METHOD: PutClipboardOnPlatformClipboard
 *
 * DESCRIPTION:
 * 	 This method has the side effect of discarding this clipboard.  This is
 *   not unreasonable since presumably the document is being deactivated,
 *   and the platform clipboard may be changed.  However, the clipboard is
 *   discarded even if an error occurs copying to the scrap, which is
 *   unfortunate.
 *
 *   Also note that by externalizing the draft, all promises are resolved.
 */
SOM_Scope void SOMLINK ODClipboardPutClipboardOnPlatformClipboard(
	ODClipboard		*somSelf,
	Environment		*ev,
	void			*pDataObj)
{
	ODClipboardData *somThis 	= ODClipboardGetData(somSelf);
	IDataObject		*pIDataObject	= (IDataObject*)pDataObj;
	ODPlatformType	platformType;
	HRESULT			hr;
	FORMATETC		fe;
	STGMEDIUM		stm;

	/* Externalize the draft and close the document so that the data is
	 * all written out to the clipboard's Bento container.
	 */
	ODCloseClipboard(somSelf, ev, kODTrue);

	/* Now put the closed memory container out onto the clipboard in native
	 *  OpenDoc format.
	 */
	// 30 Apr 96 RRK - please note: these calls to ODLock/UnlockHandle are commented out in the
	//                              Novell source
//	(LPSTR)ODLockHandle(_fContainerHandle);
	platformType 		= _fSession->GetTranslation(ev)->GetPlatformTypeFromISOType(ev,kODApplePart);
	memset(&stm,0,sizeof(stm));
	//---------------------------------------------------------------------------
	// The following code differs somewhat from Novell's implementation.  When we
	// get a container handle (_fContainerHandle) we use ODNewHandle.  In the Novell
	// Windows code, ODNewHandle issues a GlobalAlloc, and we get some memory we can
	// use in Ole data interchange.  Unfortunately, ODNewHandle does not now use ODNewHandle.
	// this means that we can't just do what Novell did:
	//    stm.hGlobal			= _fContainerHandle;
	//    stm.tymed			= TYMED_HGLOBAL;
        //    SETDEFAULTFORMATETC(fe,(unsigned long)platformType,TYMED_HGLOBAL);
	//    hr 					= pIDataObject->SetData(&fe, &stm, TRUE);
	// because our _fContainerHandle is not an HGLOBAL.  Instead, we need to create some
	// global memory and copy our container into it:
	//   - get the size of the container via ODGetHandleSize
	//   - allocate memory from WIN32 via GlobalAlloc
	//   - lock the handle returned by GlobalAlloc
	//   - lock the container handle
	//   - memcpy the data
	//   - unlock the memory container
	//   - assign the handle to our STGMEDIUM struction and issue SetData
	//   - Unlock the handle
	// 
	// One other little detail in this change, is that we no longer set _fContainerHandle to
	// zero at the end of this method.  The Novell guys did this because in their case, this
	// OpenDoc process no longer owned the storage associated with the container handle.  We've
	// made a copy, so we'll keep the handle current.  I'm not absolutely convinced we shouldn't
	// do an ODReleaseObject/ODDispose on the container handle.  
	// rrk 13 jun 96 x-doc clipboard probs
	//---------------------------------------------------------------------------
        ODULong                  dataSize = ODGetHandleSize(_fContainerHandle);
        HGLOBAL                  hdata;
	ODPtr                    data;
	hdata = GlobalAlloc(GMEM_SHARE,dataSize);
#if ODDebugClipboard
        ODULong xx = GlobalSize(hdata);
        PRINT("PutClipboardOnPlatformClipboard: requested %d; GlobalSize gives %d.\n", dataSize, xx);
#endif
	if (hdata) {
		      data	= GlobalLock(hdata);
		      if (data) {
			LPSTR lpstrFrom = (LPSTR) ODLockHandle(_fContainerHandle);
			memcpy(data, lpstrFrom, dataSize);
			ODUnlockHandle((ODHandle) lpstrFrom);
		      }
		      else
			 THROW(kODErrOutOfMemory);
		    }
	 else
	    THROW(kODErrOutOfMemory);

        stm.hGlobal                     = hdata;
	//	stm.hGlobal			= _fContainerHandle;
	stm.tymed			= TYMED_HGLOBAL;
        SETDEFAULTFORMATETC(fe,(unsigned long)platformType,TYMED_HGLOBAL);
	hr 					= pIDataObject->SetData(&fe, &stm, TRUE);
	GlobalUnlock(hdata);

	/* Free the memory container from this process
	 */
//	ODUnlockHandle(_fContainerHandle);
	WASSERTMSG_DEBUG(SUCCEEDED(hr),
			 "ODClipboard: failure on IDataObject::SetData");

	/* LATER: who frees the container when things work ?
	 */
//	GlobalFree(_fContainerHandle);
	// rrk 1 jun 96.  We need to zero out the container handle because we don't own this storage any longer.
	//                We issued IDataObject::SetData with the third parameter set to "TRUE," meaning that
	//                we delegate storage cleanup to the data object.  

	//_fContainerHandle 	= 0;

	THROW_IF_ERROR((long)hr,"ODClipboard: Unable to put clipboard up");
}


/* METHOD: ImportClipboard
 *
 * DESCRIPTION:
 *	 Import the 'Apple:ScrapType:odcn' type from the clipboard - if it is
 *   available. That basically means that we just bring down a global
 *	 memory segment from the clipboard that is the closed Bento container
 *   that we shoved up there by this or another running instance of OpenDoc...
 *
 * 	 $$$$$ If GetContainer throws, its probably because a bad resource of type
 *   'part' was found on the clipboard (currently error 1012, but it may
 *   change and there may be others).  In this case, we should try to read
 *   other resource types.
 */
SOM_Scope ODBoolean SOMLINK ODClipboardImportClipboard(
	ODClipboard		*somSelf,
	Environment		*ev,
	void			*pDataObj)
{
	ODClipboardData 	*somThis 		= ODClipboardGetData(somSelf);
	BOOL				rc				= FALSE;
	ODStorageUnit*		draftProperties = (ODStorageUnit*) kODNULL;
	IDataObject			*pIDataObject	= (IDataObject*)pDataObj;
	LPSTR				lpstrFrom, lpstrTo;
	ODULong				dataSize;
	ODStorageUnitRef	suRef;
	ODStorageUnitID	suID;
	ODPlatformType		platformType;
	HRESULT				hr;
	FORMATETC			fe;
	STGMEDIUM			stm;

	/* Verify that we aren't mis-using the clipboard methods
	 */
	WASSERTMSG_DEBUG(_fContainerHandle == 0,
			 "ODClipboard: _fContainerHandle not null");
	WASSERTMSG_DEBUG(_fContainer == (ODContainer*) kODNULL,
			 "ODClipboard: _fContainer not null");
	WASSERTMSG_DEBUG(_fDocument == (ODDocument*) kODNULL,
			 "ODClipboard: _fDocument not null");
	WASSERTMSG_DEBUG(_fDraft == (ODDraft*) kODNULL,
			 "ODClipboard: _fDraft not null");
	WASSERTMSG_DEBUG(_fSU == (ODStorageUnit*) kODNULL,
			 "ODClipboard: _fSU not null");


	/* If the clipboard doesn't have the 'Apple:ScrapType:odcn' type up
	 * at the moment, we can't directly import the OpenDoc clipboard as a
	 * memory Bento container.
	 */
	platformType = _fSession->GetTranslation(ev)->GetPlatformTypeFromISOType(ev,kODApplePart);
	if (somSelf->PlatformClipboardHasType(ev,pIDataObject,platformType))
	{
		/* Get the OpenDoc clipboard from the OLE clipboard IDataObject
		 */
                SETDEFAULTFORMATETC(fe,(unsigned long)platformType,TYMED_HGLOBAL);
		hr	= pIDataObject->GetData(&fe,&stm);
		if (SUCCEEDED(hr))
		{
			/* Now try to take the global memory segment we got back
			 * from OLE2 and try to resurrect the Bento container within
			 * it that contains all the clipboard information
			 */
			if (stm.tymed == TYMED_HGLOBAL)
			{
				TRY
					/* Clone the clipboard segment into our own memory
					 */
	//---------------------------------------------------------------------------
	// The following code differs somewhat from Novell's implementation, as described above 
	// in PutClipboardOnPlatformClipboard.
	// Novell did:
	//     dataSize		 = ODGetHandleSize((ODHandle)stm.hGlobal);
	//     _fContainerHandle = (ODHandle)ODNewHandle(dataSize);
	//     lpstrFrom		 = (LPSTR)ODLockHandle((ODHandle)stm.hGlobal);
	//     lpstrTo			 = (LPSTR)ODLockHandle((ODHandle)_fContainerHandle);
	//     memcpy(lpstrTo,lpstrFrom,dataSize);
	//     ODUnlockHandle((ODHandle) lpstrFrom);
	//     ODUnlockHandle((ODHandle) lpstrTo);
	// 
	// The dataSize calculation doesn't work because our stm.hGlobal is an HGLOBAL, not an ODHandle;
	// we use GlobalSize on the HGLOBAL instead.
	//
	// When running on Windows 95 we have another interesting complication.  We assume in the following
	// that the length of data we put on the clipboard back in PutClipboardOnPlatformClipboard will be the
      	// the same length we get back from OLE here.  Not so, if the source IDataObject has gone away in the 
	// interim.  Windows 95 wants to round up to the next 32-byte multiple.  This puts in an awkward position;
        // the solution here is to scan the data we receive looking for a good Bento container, adjusting the 
	// length according to what we find.
	//---------------------------------------------------------------------------
				  //					dataSize		 = ODGetHandleSize((ODHandle)stm.hGlobal);
				        dataSize  = GlobalSize(stm.hGlobal);
#if ODDebugClipboard
					PRINT("ImportClipboard; size of data is %d.\n", dataSize);
#endif

		                        ODPtr data	= GlobalLock(stm.hGlobal);
		                        if (data) {
					    // scan from the back looking for our magic byte sequence, signifying that
					    // we have a valid bento container. 
					    ODPtr temp = (ODPtr) ((ODULong) data + dataSize - strlen(MagicByteSequence));
					    ODULong oldSize = dataSize;
					    for(int i=0; i < oldSize-strlen(MagicByteSequence); i++) {
						if(memcmp(MagicByteSequence, temp, strlen(MagicByteSequence)) == 0) {
							// found Magic bytes
							dataSize = ((ODULong)temp - (ODULong) data) + 24;
							break;
						}
						temp = (ODPtr) ((ODULong) temp - 1);
					    }
#if ODDebugClipboard
					    PRINT("ImportClipboard; adjusted size is %d.\n", dataSize);	
#endif
					    _fContainerHandle = (ODHandle)ODNewHandle(dataSize);
                                            lpstrTo = (LPSTR)ODLockHandle((ODHandle)_fContainerHandle);
					    memcpy(lpstrTo, data, dataSize);
			                    ODUnlockHandle((ODHandle) lpstrTo);
                                	}
					GlobalUnlock(stm.hGlobal);

					/* Now open our copy of the clipboard memory segment
					 */
                        		ODOpenClipboard(somSelf, ev);
					rc				= TRUE;
				CATCH_ALL			
					_fContainerHandle = 0;	
					somSelf->DiscardClipboard(ev);
					if (draftProperties != (ODStorageUnit*) kODNULL)
						draftProperties->Release(ev);
					RERAISE;
				ENDTRY
			}

			/* Release the storage medium that we were given
			 */
			ReleaseStgMedium(&stm);
		}
	}
	return rc;
}


/* METHOD OVERRIDE: GetUpdateID
 *
 * DESCRIPTION:
 * 	 Return the current change id value for the clipboard. In the windows
 *   implementation, this is a process-specific unique id, not to be compared
 *   between processes. We start with a value of 42 and increment from there
 *   every time our clipboard viewer is told that a change has been made.
 */
SOM_Scope ODUpdateID SOMLINK ODClipboardGetUpdateID(
	ODClipboard		*somSelf,
	Environment		*ev)
{
	ODClipboardData *somThis = ODClipboardGetData(somSelf);

	short curScrapCount = GetMacScrapCount();
	
	// If the desk scrap has not changed since we last imported it, 
	// or if the desk scrap has not changed since the last change to this clipboard,
	// return the current change id, otherwise, return the next change id.
	// Note that this routine will return the same "next" change id
	// even if the desk scrap has changed since the last call to this routine.
	if (curScrapCount == _fScrapCount)
		return _fUpdateID;
	else if (curScrapCount == _fScrapCountLastChange)
		return _fUpdateID;
	else
		return _fNextUpdateID;
	//	return _fUpdateID; rrk 31 may 96
}

					
/* METHOD OVERRIDE: Clear
 *
 * DESCRIPTION:
 *   Clear all data that we put on the clipboard
 */
SOM_Scope void SOMLINK ODClipboardClear(
	ODClipboard		*somSelf,
	Environment		*ev)
{
	ODClipboardData *somThis = ODClipboardGetData(somSelf);

	/* Verify that this method is being used properly
	 */
	WASSERTMSG_DEBUG((_fSU == (ODStorageUnit*) kODNULL) || (_fSU->GetRefCount(ev) == 1),
			 "ODClipboard::Clear - Storage unit refCount not 1");

	/* Cleanup and clear the system clipboard (as per design spec?!)
	 */
	somSelf->DiscardClipboard(ev);
	_fUpdateID = _fNextUpdateID;
	_fNextUpdateID = _fSession->UniqueUpdateID(ev);

	_fOriginalDraft = kODNULL;
	_fOriginalCloneKind = kODCloneCopy;
	_fClonePasteCount = 0;

	IDataObject	*pCurClipboardIDataObject	= NULL;
	HRESULT hr;
	hr	  	= OleGetClipboard(&pCurClipboardIDataObject);
	if (SUCCEEDED(hr) && pCurClipboardIDataObject)
	{
	        pendingClear = kODTrue;                       // will be true until WM_DRAWCLIPBOARD is received
		OleSetClipboard(NULL);
		_fbClear			= TRUE;
		pCurClipboardIDataObject->Release();
	}	

	// Remember to export to desk scrap
	_fScrapCountLastChange = _fScrapCount = GetMacScrapCount();
}

//------------------------------------------------------------------------------
// ODClipboard::GetContentStorageUnit
//------------------------------------------------------------------------------
//
// If importing from the host clipboard fails, this method returns a new, empty
// storage unit.  No alert is displayed to inform the user.  This method will
// retry importing until Clear() is called.  An exception is returned only if
// an empty clipboard cannot be created.
//
// If this routine is called when the process is in the background, GetScrap()
// will probably return memFullError.  Even if it did return scrap data, the
// data would not be reliable because the forground application might be keeping
// the true clipboard data in a private scrap.  So rather than returning a scrap
// error, just return an empty clipboard.

SOM_Scope ODStorageUnit*  SOMLINK ODClipboardGetContentStorageUnit(ODClipboard *somSelf, Environment *ev)
{
	ODClipboardData *somThis = ODClipboardGetData(somSelf);
	ODClipboardMethodDebug("ODClipboard","GetContentStorageUnit");

        IDataObject	*pIDataObject	= NULL;
	HRESULT		hr;	

      SOM_TRY
	
	ODVolatile(somSelf);
	ODVolatile(ev);

	short curScrapCount = GetMacScrapCount();
	
#if ODDebugClipboard
	if ( (_fSU != (ODStorageUnit*) kODNULL) && (_fSU->GetRefCount(ev) != 1) )
		PRINT("ODClipboard::GetContentStorageUnit - Storage unit refCount = %d\n", _fSU->GetRefCount(ev));
#endif

	// _fScrapCount is the platform scrap count that was last imported or in effect when the clipboard changed.
	// If "curScrapCount == _fScrapCount", the platform scrap has not changed since it was last imported.
	// _fScrapCountLastChange is the platform scrap count at the time the OpenDoc clipboard last changed.
	// If "curScrapCount == _fScrapCountLastChange", the platform scrap has not changed since the
	// OpenDoc clipboard last changed.

        // RRK 1 Jun 96 - Even if these counts ARE the same, if we do not have a container handle, we will import.
       
	if ((!_fContainerHandle) || ((curScrapCount != _fScrapCount) && (curScrapCount != _fScrapCountLastChange) ) )
	{
		// Import from desk scrap into the OpenDoc clipboard
#if ODDebugClipboard
		PRINT("ODClipboard: GetContentStorageUnit: Updating clipboard from scrap\n");
#endif

		somSelf->DiscardClipboard(ev);

		TRY
                        hr = OleGetClipboard(&pIDataObject);
			if (SUCCEEDED(hr) && pIDataObject)
			{
				if ( !somSelf->ImportClipboard(ev, pIDataObject) )
				{
					somSelf->NewClipboard(ev);
					somSelf->ImportContent(ev, pIDataObject);
				}
			}
			else
			{
				somSelf->NewClipboard(ev);
			}
			_fScrapCount = GetMacScrapCount();
			_fUpdateID = _fNextUpdateID;
			_fNextUpdateID = _fSession->UniqueUpdateID(ev);
		CATCH_ALL

			// Importing an OpenDoc clipboard, content, or NewClibpard failed;
			// just create a new, empty clipboard
			somSelf->DiscardClipboard(ev);
			somSelf->NewClipboard(ev);
		ENDTRY
		_fOriginalDraft = kODNULL;
		_fOriginalCloneKind = kODCloneCopy;
		_fClonePasteCount = 0;
	}
	else if ( _fContainerHandle == (ODHandle) kODNULL )
	{
		// Open a new clipboard container if a container handle doesn't exist
		// This case can only happen after an error or after Clear() is called
		somSelf->NewClipboard(ev);
		_fOriginalDraft = kODNULL;
		_fOriginalCloneKind = kODCloneCopy;
		_fClonePasteCount = 0;
	}
	else
	{
		// reopen the container handle if necessary.
		if ( _fContainer == kODNULL )
			ODOpenClipboard(somSelf, ev);
		if ( _fOriginalDraft == kODNULL )
			_fOriginalDraft = GetOriginalDraft(ev, _fDraft);
	}

	return _fSU;
      
      SOM_CATCH_ALL

      SOM_ENDTRY
        return kODNULL;
}


//------------------------------------------------------------------------------
// ODClipboard::ActionDone
//------------------------------------------------------------------------------

SOM_Scope ODUpdateID  SOMLINK ODClipboardActionDone(ODClipboard *somSelf, Environment *ev,
	ODCloneKind cloneKind)
{
	ODClipboardData *somThis = ODClipboardGetData(somSelf);
	ODClipboardMethodDebug("ODClipboard","ActionDone");

	ODUpdateID update = kODUnknownUpdate;
	
	SOM_TRY

		// Note that if the clipboard is changed by one OpenDoc document,
		// and used (via Paste) in another, the sequence of undo, redo, 
		// and paste actions is separate in each document. That is, the
		// first document is unaware of the Paste in the second document.
		// This isn't semantically correct, but doesn't cause errors [cc].

		if ( (cloneKind == kODCloneCut) || (cloneKind == kODCloneCopy) )
		{
			_fOriginalCloneKind = cloneKind;
			_fClonePasteCount = 0;
		}
		else if ( cloneKind == kODClonePaste )
		{
			_fClonePasteCount += 1;
		}
		else
			THROW(kODErrIllegalClipboardCloneKind);

		update = _fUpdateID;
	
	SOM_CATCH_ALL
	
	SOM_ENDTRY
	
	return update;
}

//------------------------------------------------------------------------------
// ODClipboard::ActionUndone
//------------------------------------------------------------------------------

SOM_Scope void  SOMLINK ODClipboardActionUndone(ODClipboard *somSelf, Environment *ev,
	ODUpdateID update,
	ODCloneKind originalCloneKind)
{
	ODClipboardData *somThis = ODClipboardGetData(somSelf);
	ODClipboardMethodDebug("ODClipboard","ActionUndone");

	SOM_TRY

		if ( update == _fUpdateID )
		{
			if ( originalCloneKind == kODCloneCut )
			{
				SetOriginalCloneKind(ev, _fDraft, kODCloneCopy);
			}
			else if ( originalCloneKind == kODClonePaste )
			{
				_fClonePasteCount -= 1;
				if ( (_fClonePasteCount == 0) && (_fOriginalCloneKind == kODCloneCut) )
					SetOriginalCloneKind(ev, _fDraft, kODCloneCut);
			}
			else if ( originalCloneKind != kODCloneCopy )
				THROW(kODErrIllegalClipboardCloneKind);
		}

	SOM_CATCH_ALL
	
	SOM_ENDTRY
}

//------------------------------------------------------------------------------
// ODClipboard::ActionRedone
//------------------------------------------------------------------------------

SOM_Scope void  SOMLINK ODClipboardActionRedone(ODClipboard *somSelf, Environment *ev,
	ODUpdateID update,
	ODCloneKind originalCloneKind)
{
	ODClipboardData *somThis = ODClipboardGetData(somSelf);
	ODClipboardMethodDebug("ODClipboard","ActionRedone");

	SOM_TRY

		if ( update == _fUpdateID )
		{
			if ( originalCloneKind == kODCloneCut )
			{
				SetOriginalCloneKind(ev, _fDraft, kODCloneCut);
			}
			else if ( originalCloneKind == kODClonePaste )
			{
				_fClonePasteCount += 1;
				if ( (_fClonePasteCount == 1) )
					SetOriginalCloneKind(ev, _fDraft, kODCloneCopy);
			}
			else if ( originalCloneKind != kODCloneCopy )
				THROW(kODErrIllegalClipboardCloneKind);
		}

	SOM_CATCH_ALL
	
	SOM_ENDTRY
}

//------------------------------------------------------------------------------
// ODClipboard::SetPlatformClipboard
//------------------------------------------------------------------------------

SOM_Scope void  SOMLINK ODClipboardSetPlatformClipboard(ODClipboard *somSelf, Environment *ev,
	ODPlatformTypeList* typeList)
{
	ODClipboardData *somThis = ODClipboardGetData(somSelf);
	ODClipboardMethodDebug("ODClipboard","SetPlatformClipboard");
	
	ODPlatformTypeList* requestedTypes = (ODPlatformTypeList*) kODNULL;
	ODVolatile(requestedTypes);
	
	short scrapCount = GetMacScrapCount();

	if ( (scrapCount == _fScrapCountLastChange) && (scrapCount == _fScrapCount) )
	{
		SOM_TRY
		

			if ( _fSU != (ODStorageUnit*) kODNULL )
			{
				if ( typeList )
					requestedTypes = _fSession->GetStorageSystem(ev)->CreatePlatformTypeList(ev, typeList);

				TRY
					somSelf->ExportPlatformTypes(ev, requestedTypes, TRUE);
				CATCH_ALL
					RERAISE;
				ENDTRY
			}

		SOM_CATCH_ALL
		
		SOM_ENDTRY

		if ( requestedTypes )
			delete requestedTypes;

		// Since the scrap has only been partially updated, or an error has
		// occurred, we still need to remember to transfer everything later [cc]
		_fScrapCountLastChange = _fScrapCount = GetMacScrapCount();
	}
}

/* METHOD OVERRIDE: ExportClipboard
 *
 * DESCRIPTION:
 *   THROWs an exception if setting the platform clipboard fails
 */
SOM_Scope void SOMLINK ODClipboardExportClipboard(
	ODClipboard		*somSelf,
	Environment		*ev)
{
	ODClipboardData *somThis 	= ODClipboardGetData(somSelf);

#ifdef NEVER // 24 apr 96 - the DR2 recipes must have been different.  We want ExportClipboard to put stuff
	     //             out on the platform clipboard, but it never will with this stuff in here. 
	/* If the OLE clipboard hasn't changed since the last time
	 * this clipboard was cleared, the OLE clipboard needs to be updated.
	 */
	if (  _fpIDataObjectLast
	   && SUCCEEDED(OleIsCurrentClipboard((IDataObject*)_fpIDataObjectLast))
	   )
#endif
		
	short count = GetMacScrapCount();	
	ODBoolean needToExport = kODFalse;
	ODBoolean needToExportToRemoveLinkSpec = kODFalse;
	if  (count == _fScrapCountLastChange )
	{
		if ( count == _fScrapCount )
		{
			needToExport = kODTrue;
			_fExportedLinkSpec = _fSU && _fSU->Exists(ev, kODPropLinkSpec, kODNULL, 0);
		}
		else if ( _fExportedLinkSpec )
		{
			needToExportToRemoveLinkSpec = _fSU && !_fSU->Exists(ev, kODPropLinkSpec, kODNULL, 0);
		}
        }	
		// Immediately after changing the clipboard, _fOriginalDraft won't be set correctly
		// because the clipboard is not informed when a part is finished writing. [cc]
	if ( needToExport && (_fOriginalDraft == kODNULL) )
	{
		ASSERT(_fDraft != kODNULL, kODErrAssertionFailed);
		_fOriginalDraft = GetOriginalDraft(ev, _fDraft);
		WASSERTMSG(_fOriginalDraft != kODNULL, "Last clipboard change did not clone", AMSG_861);
	}

		
	if ( needToExport || needToExportToRemoveLinkSpec )
	{
	   
		/* If we have a storage unit, put data to the Windows clipboard,
		 * otherwise just clear the clipboard...
		 */
		if (_fSU != (ODStorageUnit*) kODNULL)
		{
			/* Since PutClipboardOnPlatformClipboard() blows away our
			 * clipboard, we'll read the OLE clipboard back in the next
			 * time the OpenDoc clipboard is needed.
			 */
			somSelf->ExportPlatformTypes(ev,kODNULL, TRUE);				
			_fScrapCountLastChange = GetMacScrapCount();		// Exporting was successful
	        	if ( needToExportToRemoveLinkSpec )
			  _fExportedLinkSpec = kODFalse;
		}
		else
		{ // rrk 22 May 96 We really don't want to clear the clipboard here.  As a result of calling 
		  //               ExportClipboard, the clipboard document is cleared.  If ExportClipboard is
		  //               called a second time, say, by a part's container, we will find _fSU to be NULL.
		  //               The right action to take is no action, not, as here, to clear the clipboard.
#ifdef NEVER
			/* Clear the current clipboard content - we don't have anything
			 * to put up there.
			 */
			IDataObject	*pCurClipboardIDataObject	= NULL;
			HRESULT hr;
			hr	  	= OleGetClipboard(&pCurClipboardIDataObject);
			if (SUCCEEDED(hr) && pCurClipboardIDataObject)
			{
			  //		                _fUpdateIDAtLastPlatformClipboardSet = _fUpdateID + 1; // Act of setting clipboard on the next source line
		                                                                       // will cause increment of _fUpdateID.
				OleSetClipboard(NULL);
				_fbClear			= TRUE;
				pCurClipboardIDataObject->Release();
			}
#endif
		}
	}
}


//------------------------------------------------------------------------------
// ODClipboard::DraftClosing
//------------------------------------------------------------------------------
//
// Forces resolution of promises, and prevents moving content across draft closings
// via the clipboard. In order to work correctly, parts MUST use BeginClone-EndClone
// when writing to the clipboard.

SOM_Scope void  SOMLINK ODClipboardDraftClosing(ODClipboard *somSelf, Environment *ev,
		ODDraft* draft)
{
	ODClipboardData *somThis = ODClipboardGetData(somSelf);
	ODClipboardMethodDebug("ODClipboard","DraftClosing");

#if ODDebugClipboard
	PRINT("ODClipboard::DraftClosing called, draft is %x\n", draft);
#endif

	SOM_TRY

	if ( draft )
	{
		// Immediately after changing the clipboard, _fOriginalDraft won't be set correctly
		// because the clipboard is not informed when a part is finished writing.

		short scrapCount = GetMacScrapCount();
		ODBoolean needToExport = (scrapCount == _fScrapCountLastChange) && (scrapCount == _fScrapCount);
		if ( needToExport && (_fOriginalDraft == kODNULL) )
		{
			ASSERT(_fDraft != kODNULL, kODErrAssertionFailed);
			_fOriginalDraft = GetOriginalDraft(ev, _fDraft);
			WASSERTMSG(_fOriginalDraft != kODNULL, "Last clipboard change did not clone", AMSG_861);
		}
	
#if ODDebugClipboard
		PRINT("Last draft to change clipboard is %x\n", _fOriginalDraft);
#endif

		if ( _fOriginalDraft == draft )
		{
			// Last draft that wrote the clipboard is being closed.

#if ODDebugClipboard
			PRINT("ODClipboard::Draft that last wrote clipboard is closing\n", draft);
			if ( needToExport )
				PRINT("ODClipboard::Clipboard needs exporting\n");
#endif
			// If the clipboard container is open, force resolution of promises 
			// by externalizing the clipboard draft. (If there are unresolved promises, 
			// the clipboard hasn't be exported yet). Otherwise open the container.

			if ( _fContainer )
				_fDraft->Externalize(ev);
			else
				ODOpenClipboard(somSelf, ev);

			// Remove a link spec if present
			ODSURemoveProperty(ev, _fSU, kODPropLinkSpec);

			// Check to see if content was cut (not copied) to the clipboard.
			// If so, change the original clone kind to copy, so any future
			// paste won't be treated as a move.
						
			if ( GetOriginalCloneKind(ev, _fDraft) == kODCloneCut )
			{
				SetOriginalCloneKind(ev, _fDraft, kODCloneCopy);
				// Force exporting to platform clipboard
				_fScrapCountLastChange = _fScrapCount = GetMacScrapCount();

			}

			_fOriginalDraft = kODNULL;
		}
	}

	SOM_CATCH_ALL

	SOM_ENDTRY
	  return;
}

//------------------------------------------------------------------------------
// ODClipboard::DraftSaved
//------------------------------------------------------------------------------
//

SOM_Scope void  SOMLINK ODClipboardDraftSaved(ODClipboard *somSelf, Environment *ev,
		ODDraft* draft)
{
	ODClipboardData *somThis = ODClipboardGetData(somSelf);
	ODClipboardMethodDebug("ODClipboard","DraftSaved");

#if ODDebugClipboard
	PRINT("ODClipboard::DraftSaved called, draft %08x, orig %08x, fDraft %08x\n", 
		(long) draft, (long) _fOriginalDraft, (long) _fDraft);
#endif

	SOM_TRY
	
	// ODDraft* tDraft = _fDraft;
	// ODDraft* tOriginalDraft = _fOriginalDraft;
	
	if ( draft )
	{
		if ( _fOriginalDraft == kODNULL )
		{
			if (_fDraft != kODNULL)		// can be null if never accessed
				_fOriginalDraft = GetOriginalDraft(ev, _fDraft);

#if ODDebugClipboard
	PRINT("    ODClipboard::DraftSaved orig %08x\n", 
		(long) _fOriginalDraft);
#endif
		}

		if ( _fOriginalDraft == draft && _fDraft )
		{
			// Check to see if content was cut (not copied) to the clipboard.
			// If so, change the original clone kind to copy, so any future
			// paste won't be treated as a move.

#if ODDebugClipboard
			PRINT("        ODClipboard::DraftSaved try cut -> copy\n");
#endif
						
			if ( GetOriginalCloneKind(ev, _fDraft) == kODCloneCut )
			{
				SetOriginalCloneKind(ev, _fDraft, kODCloneCopy);

#if ODDebugClipboard
				PRINT("            ODClipboard::DraftSaved perform cut -> copy\n");
#endif
			}
		}
	}

	SOM_CATCH_ALL

	SOM_ENDTRY
	  return;
}

//------------------------------------------------------------------------------
// ODClipboard::ShowPasteAsDialog
//------------------------------------------------------------------------------

SOM_Scope ODBoolean  SOMLINK ODClipboardShowPasteAsDialog(ODClipboard *somSelf, Environment *ev,
		ODBoolean canPasteLink,
		ODPasteAsMergeSetting mergeSetting,
		ODFacet* facet,
		ODTypeToken viewType,
		ODPasteAsResult* result)
{
    ODClipboardData *somThis = ODClipboardGetData(somSelf);
    ODClipboardMethodDebug("ODClipboard","ShowPasteAsDialog");

	ODBoolean returnValue = kODFalse;

	SOM_TRY

	THROW_IF_NULL(facet, kODErrNullFacetInput);
	THROW_IF_NULL(result, kODErrNullPasteAsResultInput);

	ODTypeToken modalFocus = _fSession->Tokenize(ev, kODModalFocus);
	ODArbitrator* arbitrator = _fSession->GetArbitrator(ev);
	TempODFrame currentOwner = arbitrator->AcquireFocusOwner(ev, modalFocus);

	if ( arbitrator->RequestFocus(ev, modalFocus, facet->GetFrame(ev)) )
	{
		ODStorageUnit* clipContentSU = somSelf->GetContentStorageUnit(ev);
	
		ODBoolean isMove = (GetOriginalCloneKind(ev, clipContentSU->GetDraft(ev)) == kODCloneCut);

		ODWindow* activeWindow = _fSession->GetWindowState(ev)->AcquireActiveWindow(ev);
		returnValue = ShowPasteAsDialog(
								canPasteLink, 
								mergeSetting,
								isMove,
								clipContentSU,
								facet, 
								viewType, 
								/*_fSession->GetWindowSystemData(ev)->parentWindow,*/
								activeWindow->GetPlatformWindow(ev),
								result);

   		arbitrator->TransferFocus(ev, modalFocus, facet->GetFrame(ev), currentOwner);
		activeWindow->Release(ev);
		// rrk 6 sep 96
		// if there is no kODApplePart on the clipboard 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.
	        ODSURemoveProperty(ev,_fSU,kODPropEmbedAsLink);
                if (result->pasteLinkSetting) {
	          ODPlatformType platformType = _fSession->GetTranslation(ev)->GetPlatformTypeFromISOType(ev,kODApplePart);
		  if (!IsClipboardFormatAvailable((unsigned int) platformType)) {
		    result->pasteLinkSetting = kODFalse; 
	            ODSUForceFocus(ev, _fSU, kODPropEmbedAsLink, kODULong);
		  }
		}
	}
	else
	{

		Beep(440, 250);  // play an A for 1/4 second, OK?

	}


	SOM_CATCH_ALL
	  //	  return kODFalse;
	SOM_ENDTRY
	return returnValue;

}

//------------------------------------------------------------------------------
// ODClipboard::CanEmbed
//------------------------------------------------------------------------------
//

SOM_Scope ODBoolean  SOMLINK ODClipboardCanEmbed(ODClipboard *somSelf, Environment *ev)
{
	ODClipboardData *somThis = ODClipboardGetData(somSelf);
	ODClipboardMethodDebug("ODClipboard","CanEmbed");

        ODType theType; 
        ODBoolean result = kODFalse;

        ODTypeList* typeList = new ODTypeList();
	somSelf->QueryContent(ev, typeList);
	if (!typeList->Count(ev)) {
          delete typeList;
	  return kODFalse;
        }
        //  at this point we may want to look in the registry to see if we can do anything
        //  with any of these types.  For now, it is enough that there are "things" in the
        //  clipboard, so return true.
#if 1
	if (_fSU && _fSU->Exists(ev, kODPropContentFrame, kODWeakStorageUnitRef,0)) {
		ODDeleteObject(typeList);
		return kODTrue;
        }
        RegistryManager* registrationMgr;
        registrationMgr = new RegistryManager;
	ODPartHandlerRegistry* odRegistry;
	odRegistry = registrationMgr->GetODPartHandlerRegistry( ev);
	ODTypeListIterator* iter = typeList->CreateTypeListIterator(ev);
	for(theType = iter->First(ev); iter->IsNotComplete(ev); theType = iter->Next(ev))
	  {
	    if(strcmp((PSZ)theType, kODApplePart) == 0) result = kODTrue;
		else if (strcmp((PSZ)theType, "Embed Source") == 0) result = kODTrue;
		else if (strcmp((PSZ)theType, kODObjSpec) == 0) result = kODTrue;
	    else if(theType[0]) {
	      _IDL_SEQUENCE_ISOString partHandlerList = odRegistry->GetPartHandlerList( ev,theType, 0) ;
	      if ( partHandlerList. _length ) {
		result = kODTrue;
	      }
	      for ( int i = 0; i < partHandlerList. _length; i++)
		{
		  SOMFree( partHandlerList. _buffer[i]);
		}
	      SOMFree( partHandlerList. _buffer);
	    }
	    if(result) break;
	  }
   ODDeleteObject(iter);
   ODDeleteObject(typeList);
   return result;
#else
        ODDeleteObject(typeList);
        return kODTrue;
#endif
	
}

//------------------------------------------------------------------------------
// ODClipboard::QueryContent
//------------------------------------------------------------------------------
//

SOM_Scope void  SOMLINK ODClipboardQueryContent(ODClipboard *somSelf, 
						Environment *ev,
						ODTypeList *typeList)
{
	ODClipboardData *somThis = ODClipboardGetData(somSelf);
	ODClipboardMethodDebug("ODClipboard","QueryContent");

	typeList->InitTypeList(ev, kODNULL);
        ODType theISOType;

     SOM_TRY	short curScrapCount = GetMacScrapCount();

	// _fScrapCount is the platform scrap count that was last imported or in effect when the clipboard changed.
	// If "curScrapCount == _fScrapCount", the platform scrap has not changed since it was last imported.
	// _fScrapCountLastChange is the platform scrap count at the time the OpenDoc clipboard last changed.
	// if "curScrapCount == _fScrapCountLastChange", the platform scrap has not changed since the
	// OpenDoc clipboard last changed.

        // If we have a storage unit AND we are
        // in sync with the Ole clipboard, then
        // we just interrogate our clipboard SU.
     if ( _fSU && (curScrapCount == _fScrapCount) && (curScrapCount == _fScrapCountLastChange) ) { 

	  if (!ODSUExistsThenFocus(ev,_fSU,kODPropContents,kODNULL)) return;

	  ODULong count = _fSU->CountValues(ev);

	  for (ODULong index = 1; index <= count; index++) {
	    _fSU->Focus(ev, kODPropContents, kODPosUndefined, 0, index, kODPosUndefined);
	    theISOType = _fSU->GetType(ev);
	    if (theISOType != (ODType) kODNULL) {
	      typeList->AddLast(ev, theISOType);
	    }
	  }
	  return;
        }
        //  either we did not have a storage unit at all, or the Ole clipboard has changed from the
        //  state represented by that storage unit.  In either case, we need to get the types from the 
        //  platform clipboard.
        
        if (OpenClipboard(NULL)) { // note: this is the WIN32 API OpenClipboard; the local function is now ODOpenClipboard
           UINT theType, thePrevType = 0;
	   while ( (theType = EnumClipboardFormats(thePrevType)) != 0) {
	     theISOType = _fSession->GetTranslation(ev)->GetISOTypeFromPlatformType(ev,theType,kODPlatformDataType);
	     if (theISOType != (ODType) kODNULL) {
	       typeList->AddLast(ev, theISOType);
	     }
	     thePrevType = theType;
	   }
	      
           CloseClipboard(); // note: this is the WIN32 API CloseClipboard; the local function is now ODCloseClipboard
	}
     SOM_CATCH_ALL

     SOM_ENDTRY
       return;
}

//==============================================================================
// Local Functions
//==============================================================================


/* METHOD: PlatformClipboardHasType
 *
 * DESCRIPTION:
 *   Returns TRUE if the OLE clipboard currenty contains the given platform
 *   type, and FALSE otherwise
 */
SOM_Scope ODBoolean SOMLINK ODClipboardPlatformClipboardHasType(
	ODClipboard		*somSelf,
	Environment		*ev,
	void			*pDataObj,
	ODPlatformType 	type)
{
	ODClipboardData *somThis 	= ODClipboardGetData(somSelf);
	IDataObject	*pIDataObject	= (IDataObject*)pDataObj;
	FORMATETC	fe;

//	SETDEFAULTFORMATETC(fe,(CLIPFORMAT)type,TYMED_HGLOBAL);
        SETDEFAULTFORMATETC(fe,(unsigned long)type,TYMED_HGLOBAL);
	if (pDataObj)
		return	SUCCEEDED( pIDataObject->QueryGetData(&fe) );
	else
		return	IsClipboardFormatAvailable((unsigned int) type);
}

/* METHOD: ClipboardChanged
 *
 * DESCRIPTION:
 *   ClipboardMonitor is telling us that the clipboard was just altered
 */
SOM_Scope void SOMLINK ODClipboardClipboardChanged(
	ODClipboard		*somSelf,
	Environment		*ev)
{
	ODClipboardData 	*somThis 		= ODClipboardGetData(somSelf);

	/* Update our internal change ID
	 */
 	clipboardUpdateCount++;
	if (!clipboardUpdateCount)
		clipboardUpdateCount = 1;
        // if pendingClear is true, then we have cleared the clipboard and are waiting for
	// the corresponding WM_DRAWCLIPBOARD.  In ODClipboardClear, we probably updated
	// _fScrapCountLastChange and _fScrapCount before we received the WM_DRAWCLIPBOARD
	// message.  The following code is sort of a hack -- it attempts to keep the scrap
	// count data members current after a clear.
	if(pendingClear) { 
	  _fScrapCountLastChange = _fScrapCount = clipboardUpdateCount;
	  pendingClear = kODFalse;
	}
}

/* FUNCTION: RegisterClipboardMonitorClass
 *
 * DESCRIPTION:
 *   Register the ClipboardMonitor's window procedure
 */
ODBoolean RegisterClipboardMonitorClass(HINSTANCE hInst)
{
	Environment		*ev	= somGetGlobalEnvironment();
	WNDCLASS  		wc;

	wc.style			= 0;
	wc.lpfnWndProc		= ClipboardMonitorWndProc;
	wc.cbClsExtra		= 0;
	wc.cbWndExtra		= 20;
        wc.hInstance            = hInst;
	wc.hIcon			= NULL;
	wc.hCursor			= NULL;
	wc.hbrBackground	= (HBRUSH)(COLOR_APPWORKSPACE+1);
	wc.lpszMenuName		= NULL;
	wc.lpszClassName	= kODClipboardMonitorClass;

	if (!RegisterClass (&wc) )
		return kODFalse;
		
	return kODTrue;
}

/* FUNCTION: ClipboardMonitorWndProc
 *
 * DESCRIPTION:	
 *	 Monitor the clipboard viewer chain to see when the clipboard is updated.
 *   Every time it gets changed, update the internal ChangeID held within the
 *   OpenDoc clipboard object...
 */
LRESULT CALLBACK ClipboardMonitorWndProc(
	HWND 		hWnd,
	UINT 		message,
	WPARAM	 	wParam,
	LPARAM 		lParam)
{
	HWND			hWndNext, Remove, Next;
	ODClipboard		*Clipboard;
	Environment		*ev;
        LONG            result;

	switch (message)
	{
	    /* Add ourselves to the clipboard viewer chain
	     */
		case WM_CREATE:
			SetLastError(0);
			hWndNext	= SetClipboardViewer(hWnd);

			/* Store away the object pointer passed into us and the next
			 * window in the clipboard viewer chain
			 */
			SetLastError(0);
			result = SetWindowLong(hWnd,
						  kODClipboardObject,
						  (LONG)(((LPCREATESTRUCT)lParam)->lpCreateParams));

                	SetLastError(0);
			result = SetWindowLong(hWnd,
						  kODhWndNext,
						  (LONG)hWndNext);
			break;
					
		/* Remove ourselves from the clipboard viewer chain
		 */
		case WM_DESTROY:
			hWndNext	= (HWND)GetWindowLong(hWnd,kODhWndNext);
			ChangeClipboardChain(hWnd,hWndNext);
			break;

		/* Make sure hWndNext is always correct
		 */
		case WM_CHANGECBCHAIN:
			hWndNext	= (HWND)GetWindowLong(hWnd,kODhWndNext);
			Remove		= (HWND)wParam;
			Next		= (HWND)lParam;

			if (Remove == hWndNext) {
                	        SetLastError(0);
				result = SetWindowLong(hWnd,kODhWndNext,(LONG)Next);

			}
			else if (hWndNext)
				SendMessage(hWndNext,message,wParam,lParam);
			break;

		/* Tell the ODClipboard object that the system clipboard was
		 * updated, so it can update its change id.
		 */
		case WM_DRAWCLIPBOARD:
			hWndNext	= (HWND)GetWindowLong(hWnd,kODhWndNext);
			Clipboard	= (ODClipboard*)GetWindowLong(hWnd,kODClipboardObject);
			ev			= somGetGlobalEnvironment();
			if (Clipboard)
				Clipboard->ClipboardChanged(ev);
			if (hWndNext)
				SendMessage(hWndNext,message,wParam,lParam);
			break;

		default:	
			return DefWindowProc(hWnd, message, wParam, lParam);
	}
	return 0L;
}

//------------------------------------------------------------------------------
// GetMacScrapCount
//------------------------------------------------------------------------------
//
// This function returns the identification stamp of the last mac scrap change,
// NOT the count of items on the scrap!
// rrk 1 jun 96 - in the WIN32 implementation, the function actually returns a global counter
//                incremented every time the platform clipboard changes.

ODStatic short GetMacScrapCount()
{
	return clipboardUpdateCount;
}

//------------------------------------------------------------------------------
// IsBentoStream
//------------------------------------------------------------------------------
// This function is given an IDataObject and a CLIPFORMAT.  The function checks 
// see if the item in the IDataObject identified by the CLIPFORMAT is an IStorage
// containing an IStream whose name is kODBentoStreamName.  The function returns 
// TRUE if there is a kODBentoStreamName IStream, FALSE if not.

BOOL IsBentoStream(
    IDataObject*   OLEIDataObject,   // the IDataObject that holds the Embed Source or Embedded Object
    CLIPFORMAT     cfFormat,         // which one, Embed Source or Embedded Object, are we looking for
    HRESULT        *returnedHRESULT  // for drag&drop, in case GetData didn't work
) 
{
    STGMEDIUM		stm;
    FORMATETC		fe;
    HRESULT             hr = 0;
    LPSTREAM            pIStream;
    LPSTORAGE           pIStorage = NULL;

    // probably should use the IStrage's EnumElements to look for the kODBentoStreamName, 
    // but, it takes less code to just try to open the stream.

    memset(&stm,0,sizeof(stm));
    memset(&fe, 0,sizeof(fe));
    SETDEFAULTFORMATETC(fe, cfFormat, TYMED_ISTORAGE);
#if 0
    hr = OLEIDataObject->QueryGetData(&fe);
    PRINT("IsBentoStream: QueryGetData returned with hr %x.\n", hr);
#endif

#if 0
    hr = OLEIDataObject->GetData(&fe,&stm);
#else
    hr = OLEIDataObject->GetData(&fe,&stm);

    // if this failed then try to use GetDataHere,  MSWord, for example, won't respond to GetData but will to GetDataHere
    if(FAILED(hr)){

        ILockBytes  *pLB = NULL;
        hr=CreateILockBytesOnHGlobal(NULL, TRUE, &pLB);

        if (SUCCEEDED(hr)) {
                hr=StgCreateDocfileOnILockBytes(pLB,  STGM_READWRITE | STGM_CONVERT | STGM_SHARE_EXCLUSIVE ,  0, &pIStorage);
                        if (SUCCEEDED (hr)) {
                                stm.pstg = pIStorage;
                                stm.tymed = TYMED_ISTORAGE;
 
                                hr = OLEIDataObject->GetDataHere(&fe,&stm);

                                *returnedHRESULT = hr;
                                if (FAILED(hr)) {
                                       return FALSE;

                                }
    
                        } else {
				pLB->Release();
                                return FALSE;
                          }

                pLB->Release();      

       }  else {  // CreateLockBytesOnHGlobal failed
  
               return FALSE;
          }

    }


#endif

    *returnedHRESULT = hr;
    if(FAILED(hr)){
        return FALSE;
    }
    // We've seen that some IDataObject implementations return 0 from GetData, but
    // do NOT return the requested format.  In particular, we've seen TYMED_ISTREAM
    // returned from the GetData call, which causes the OpenStream call below here
    // to fail.  Check to see that we have the correct format -- return FALSE if not.

    if(stm.tymed != TYMED_ISTORAGE){
        return FALSE;
    }


    hr = stm.pstg->OpenStream(kODBentoStreamName,
                               0,
                               STGM_READWRITE | STGM_SHARE_EXCLUSIVE,
                               0,
                               &pIStream);
    if(FAILED(hr)){
        return FALSE;
    }
    if(pIStorage)
      pIStorage -> Release();    
    return TRUE;

}

//------------------------------------------------------------------------------
// CopyBentoIStreamToODClipboard
//------------------------------------------------------------------------------
// 
// This function is used to copy an "Embed Source" or "Embedded Object" format from 
// an OLE IDataObject to the OpenDoc clipboard.  It does this by getting 
// OpenDoc container that is wrapped in the IStorage/ISteam on the OLE clipboard and 
// working its way down to the draft and frame.  It then causes the draft and frame to 
// clone themselves to the OpenDoc clipboard.
// 
// If the IDataObject entry is an IStorage containing an IStream labeled 
// KODBentoStreamName and CopyBentoIstreamToODClipboard is successful in getting cloning 
// draft and frame to ODTarget, then it returns TRUE, otherwise it returns FALSE



BOOL CopyBentoIStreamToODClipboard(
    IDataObject*   OLEIDataObject,   // the IDataObject that holds the Embed Source or Embedded Object
    CLIPFORMAT     cfFormat,         // which one, Embed Source or Embedded Object, are we looking for
    ODStorageUnit* targetSU,          // the OpenDoc clipboard storage unit
    ODSession*     session
) 
{

    STGMEDIUM           stm;
    FORMATETC           fe;
    HRESULT             hr;
    LPSTREAM            pIStream;
    ODByteArray        *byteArray        = kODNULL;
    ODDraft            *targetDraft      = kODNULL;
    ODFrame            *sourceRootFrame  = kODNULL;
    ODULong             offset;
    ODULong             offsetLimit;
    ODStorageUnitView  *sourceDraftPropsView;
    ODStorageUnitRef    suRef;
    ODDraftKey          key             = 0;
    ODID                targetRootID    = 0;
    ODID                targetFrameID   = 0;
    ODStorageUnitRef    aSURef;

    Environment *ev = somGetGlobalEnvironment();

    // The IStorage contains an IStream, whose name is kODBentoStreamName, that 
    // contains a byte copy of a Bento container.  First, open the IStorage 
    // and IStream

    SETDEFAULTFORMATETC(fe, cfFormat, TYMED_ISTORAGE);
#if 0

    hr = OLEIDataObject->GetData(&fe,&stm);

#else

    hr = OLEIDataObject->GetData(&fe,&stm);

    ILockBytes  *pLB = NULL;

    // if this failed then try to use get data here    
    if(FAILED(hr)){


        hr=CreateILockBytesOnHGlobal(NULL, TRUE, &pLB);

        if (FAILED(hr)) 
            return FALSE;

        LPSTORAGE           pIStorage = NULL;

        hr=StgCreateDocfileOnILockBytes(pLB,  STGM_READWRITE | STGM_CONVERT | STGM_SHARE_EXCLUSIVE ,  0, &pIStorage);


        if (FAILED(hr)) 
            return FALSE;

        stm.pstg = pIStorage;
        stm.tymed = TYMED_ISTORAGE;
 
        hr = OLEIDataObject->GetDataHere(&fe,&stm);

        if (FAILED(hr)) {
            return FALSE;
        }



    }


#endif

    hr = stm.pstg->OpenStream(kODBentoStreamName,
                               0,
                               STGM_READWRITE | STGM_SHARE_EXCLUSIVE,
                               0,
                               &pIStream);
    if(FAILED(hr)){
        return FALSE;
    }

    try {

        // now get addressability to the source's draft, and root frame
        byteArray = CreateByteArray(&pIStream, sizeof(pIStream));
        TempODContainer   sourceContainer  = ODOLEManager::GetShell()->GetSession( ev )->
                             GetStorageSystem(ev)->AcquireContainer(ev, kODBentoIStreamContainer, byteArray);
        DisposeByteArray (byteArray);
        TempODDocument    sourceDocument   = sourceContainer->AcquireDocument(ev, kODDefaultDocument);
	//        TempODDraft       sourceDraft      = sourceDocument->AcquireBaseDraft(ev,  kODDPReadOnly);
        TempODDraft       sourceDraft      = sourceDocument->AcquireBaseDraft(ev,  kODDPExclusiveWrite);
        TempODStorageUnit sourceDraftProps = sourceDraft->AcquireDraftProperties(ev);


        if( ODSUExistsThenFocus(ev, sourceDraftProps, kODPropRootFrameList, kODStrongStorageUnitRefs))  {
            offsetLimit = sourceDraftProps->GetSize(ev);
            sourceDraftPropsView = sourceDraftProps->CreateView(ev);

            for (offset = 0; offset < offsetLimit; offset += sizeof(ODStorageUnitRef))    {
                sourceDraftPropsView->SetOffset(ev,offset);
                StorageUnitViewGetValue(sourceDraftPropsView, ev, sizeof(ODStorageUnitRef), (ODValue)&suRef);

                if (sourceDraftPropsView->IsValidStorageUnitRef(ev, suRef))    {
                    ODStorageUnitID wsuID = sourceDraftPropsView->GetIDFromStorageUnitRef(ev,suRef);
                    sourceRootFrame = sourceDraft->AcquireFrame(ev, wsuID);
                }
             }    
        }
    

        TempODPart sourcePart = sourceRootFrame->AcquirePart(ev);    

	// Before we clone we need to create a facet for our source part.  If we don't, parts
	// like the BaseContainer will not clone completely.  Contents for embedded frames is
	// not written until needed, and it is not needed unless a facet is present to display
	// the contents.
	//
	// This code is not straightfoward.  In order to create an embedded facet in our source
	// part, we need a facet to start with and we don't have one.  Here is a way of getting one;
	// it works, although it's a little ugly.  We get the frame that owns the selection focus,
	// create a facet iterator for that frame, and pick the first facet.  Note that this facet
	// is not in the same draft as the facet we are creating.  The first facet is in the draft
	// into which we are pasting.  The second facet is in the temporary draft created when we
	// unwrapped the ole IStream up above.
        // Note that under some conditions, there doesn't seem to be any selection focus owner.
        // In that case, we return false and forget about it.

	ODTypeToken selectionFocus = session->Tokenize(ev, kODSelectionFocus);
	TempODFrame targetFrame = session->GetArbitrator(ev)->AcquireFocusOwner(ev, selectionFocus);
	if (!targetFrame)
	  return kODFalse;
        ODFrameFacetIterator *facetItr = targetFrame->CreateFacetIterator(ev);
        ODFacet         *facet    = facetItr->First(ev);
	if (!facet)
	  return kODFalse;
#if ODDebugClipboard
	PRINT("CopyBentoIStreamToODClipboard: facet is %x.\n", facet);
#endif

        TempODShape sourceFrameShape = sourceRootFrame->AcquireFrameShape (ev, kODNULL);
        ODShape *clipShape = sourceFrameShape ->Copy (ev);

        ODTransform *embedTransform = sourceRootFrame->CreateTransform(ev);

        ODFacet *embedFacet = facet->CreateEmbeddedFacet (ev, sourceRootFrame,
                               clipShape,embedTransform, kODNULL, kODNULL,
                               kODNULL, kODFrameInFront);


        // Clone the source part to the target storage unit
        targetDraft = targetSU->GetDraft(ev);
        key = sourceDraft->BeginClone(ev, targetDraft, kODNULL, kODCloneCopy);


        targetRootID = sourceDraft->Clone(ev, 
                                          key, 
                                          sourcePart->GetID(ev), 
                                          targetSU->GetID(ev), 
                                          sourceRootFrame->GetID(ev));


        // Clone the source frame to the target storage unit. 
        // Must be done after cloning the part because the embedded frame strongly
        // references the part
        targetFrameID = sourceDraft->Clone(ev, key, sourceRootFrame->GetID(ev), 0, sourceRootFrame->GetID(ev));

        if(targetFrameID) {
            if (ODSUExistsThenFocus (ev, targetSU, kODPropContentFrame, kODWeakStorageUnitRef))
                 targetSU->Remove (ev);
            ODSUAddPropValue (ev, targetSU,kODPropContentFrame ,kODWeakStorageUnitRef );
            targetSU->GetWeakStorageUnitRef(ev, targetFrameID, aSURef);
            StorageUnitSetValue(targetSU, ev, sizeof(ODStorageUnitRef), aSURef);
    
            targetSU->AddProperty(ev, kODPropSuggestedFrameShape);
            TempODShape tempShape = sourceRootFrame->AcquireFrameShape(ev, kODNULL);
            tempShape->WriteShape(ev, targetSU);

        }

        sourceDraft->EndClone(ev, key);

	// need to remove the funky facet we created above...
	facet->RemoveFacet(ev, embedFacet);

        ODRelease(ev, sourceRootFrame);

	// is this the right place to release???
	if(pLB)
	  pLB->Release();

        return TRUE;


    } catch (...) { 
        // we don't attempt to recover from any errors, but exceptions in this 
        // function are not propigated up.  We just allow the copy to fail and 
        // return a FALSE return code

        ODRelease(ev, sourceRootFrame);

    }

    return FALSE;
}

