File:  [WindowsNT SDKs] / mstools / ole20 / samples / outline / cntrbase.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs
Thu Aug 9 18:24:38 2018 UTC (7 years, 9 months ago) by root
Branches: msft, MAIN
CVS tags: ntsdk-jul-1993, HEAD
Microsoft Windows NT Build 511 (SDK Final Release) 07-24-1993

/*************************************************************************
**
**    OLE 2 Container Sample Code
**
**    cntrbase.c
**    
**    This file contains all interfaces, methods and related support
**    functions for the basic OLE Container application. The
**    basic OLE Container application supports being a container for
**    embedded and linked objects.
**    The basic Container application includes the following
**    implementation objects: 
**    
**    ContainerDoc Object
**      no required interfaces for basic functionality
**      (see linking.c for linking related support)
**      (see clipbrd.c for clipboard related support)
**      (see dragdrop.c for drag/drop related support)
**    
**    ContainerLine Object
**    (see cntrline.c for all ContainerLine functions and interfaces)
**      exposed interfaces:
**          IOleClientSite
**          IAdviseSink
**
**    (c) Copyright Microsoft Corp. 1992 - 1993 All Rights Reserved
**
*************************************************************************/

#include "outline.h"
#include <geticon.h>    // for OleStdGetUserTypeOfClass


OLEDBGDATA


extern LPOUTLINEAPP             g_lpApp;
extern IOleUILinkContainerVtbl  g_CntrDoc_OleUILinkContainerVtbl;


// REVIEW: should use string resource for messages
char ErrMsgShowObj[] = "Could not show object server!";
char ErrMsgInsertObj[] = "Insert Object failed!";
char ErrMsgConvertObj[] = "Convert Object failed!";
char ErrMsgCantConvert[] = "Unable to convert the selection!";
char ErrMsgActivateAsObj[] = "Activate As Object failed!";

extern char ErrMsgSaving[];
extern char ErrMsgOpening[];


/* ContainerDoc_Init
 * -----------------
 *
 *  Initialize the fields of a new ContainerDoc object. The doc is initially
 *  not associated with a file or an (Untitled) document. This function sets
 *  the docInitType to DOCTYPE_UNKNOWN. After calling this function the
 *  caller should call:
 *      1.) Doc_InitNewFile to set the ContainerDoc to (Untitled)
 *      2.) Doc_LoadFromFile to associate the ContainerDoc with a file.
 *  This function creates a new window for the document.
 *
 *  NOTE: the window is initially created with a NIL size. it must be
 *        sized and positioned by the caller. also the document is initially
 *        created invisible. the caller must call Doc_ShowWindow
 *        after sizing it to make the document window visible.
 */
BOOL ContainerDoc_Init(LPCONTAINERDOC lpContainerDoc, BOOL fDataTransferDoc)
{
    LPCONTAINERAPP lpContainerApp = (LPCONTAINERAPP)g_lpApp;
    LPOUTLINEDOC lpOutlineDoc = (LPOUTLINEDOC)lpContainerDoc;

    lpOutlineDoc->m_cfSaveFormat             = lpContainerApp->m_cfCntrOutl;
    lpContainerDoc->m_nNextObjNo            = 0L;
    lpContainerDoc->m_lpStg                 = NULL;
    lpContainerDoc->m_lpNewStg              = NULL;
    lpContainerDoc->m_fEmbeddedObjectAvail  = FALSE;
    lpContainerDoc->m_clsidOleObjCopied     = CLSID_NULL;
    lpContainerDoc->m_dwAspectOleObjCopied  = DVASPECT_CONTENT;
    lpContainerDoc->m_lpSrcContainerLine    = NULL;
    lpContainerDoc->m_fShowObject           = TRUE;

#if defined( INPLACE_CNTR )
    lpContainerDoc->m_lpLastIpActiveLine    = NULL;
    lpContainerDoc->m_lpLastUIActiveLine    = NULL;
    lpContainerDoc->m_hwndUIActiveObj       = NULL;
    lpContainerDoc->m_fAddMyUI              = TRUE; // UI needs to be added
    lpContainerDoc->m_cIPActiveObjects		= 0;	
	
#if defined( INPLACE_CNTRSVR )
    lpContainerDoc->m_lpTopIPFrame          =
                    (LPOLEINPLACEUIWINDOW)&lpContainerDoc->m_OleInPlaceFrame;
    lpContainerDoc->m_lpTopIPDoc            =
                    (LPOLEINPLACEUIWINDOW)&lpContainerDoc->m_OleInPlaceDoc;
    lpContainerDoc->m_hSharedMenu           = NULL;
    lpContainerDoc->m_hOleMenu              = NULL;

#endif  // INPLACE_CNTRSVR
#endif  // INPLACE_CNTR

    INIT_INTERFACEIMPL(
            &lpContainerDoc->m_OleUILinkContainer,
            &g_CntrDoc_OleUILinkContainerVtbl,
            lpContainerDoc
    );

    return TRUE;
}


/* ContainerDoc_GetNextLink
 * ------------------------
 *
 *  Update all links in the document. A dialog box will be popped up showing
 *	the progress of the update and allow the user to quit by pushing the
 *  stop button
 */
LPCONTAINERLINE ContainerDoc_GetNextLink(
		LPCONTAINERDOC lpContainerDoc, 
		LPCONTAINERLINE lpContainerLine
)
{
    LPLINELIST lpLL = &((LPOUTLINEDOC)lpContainerDoc)->m_LineList;
    DWORD dwNextLink = 0;
    LPLINE lpLine;
    static int nIndex = 0;
	
	if (lpContainerLine==NULL)
		nIndex = 0;

    for ( ; nIndex < lpLL->m_nNumLines; nIndex++) {
        lpLine = LineList_GetLine(lpLL, nIndex);

        if (lpLine
            && (Line_GetLineType(lpLine) == CONTAINERLINETYPE)
            && ContainerLine_IsOleLink((LPCONTAINERLINE)lpLine)) {
			
			nIndex++;
			return (LPCONTAINERLINE)lpLine;
		}
	}

    return NULL;
}



/* ContainerDoc_UpdateLinks
 * ------------------------
 *
 *  Update all links in the document. A dialog box will be popped up showing
 *	the progress of the update and allow the user to quit by pushing the
 *  stop button
 */
void ContainerDoc_UpdateLinks(LPCONTAINERDOC lpContainerDoc)
{
	int				cLinks;
	HWND			hwndDoc = ((LPOUTLINEDOC)lpContainerDoc)->m_hWndDoc;
	HCURSOR			hCursor;
	LPCONTAINERLINE lpContainerLine = NULL;
	HRESULT			hrErr;
	DWORD			dwUpdateOpt;
	
	hCursor = SetCursor(LoadCursor(NULL, IDC_WAIT));
	
	/* get total number of automatic links */
	cLinks = 0;
	while (lpContainerLine = ContainerDoc_GetNextLink(
									lpContainerDoc, 
									lpContainerLine)) {
		hrErr = CntrDoc_LinkCont_GetLinkUpdateOptions(
				(LPOLEUILINKCONTAINER)&lpContainerDoc->m_OleUILinkContainer,
				(DWORD)lpContainerLine,
				(LPDWORD)&dwUpdateOpt
		);
		if (hrErr == NOERROR) {
			if (dwUpdateOpt==OLEUPDATE_ALWAYS)
				cLinks++;
		}
		else
			OleDbgOutHResult("IOleUILinkContainer::GetLinkUpdateOptions returned",hrErr);
		
	}
	
    SetCursor(hCursor);
	
	if ((cLinks > 0) && !OleUIUpdateLinks(
			(LPOLEUILINKCONTAINER)&lpContainerDoc->m_OleUILinkContainer, 
			hwndDoc, 
			(LPSTR)APPNAME, 
			cLinks)) {
		if (ID_PU_LINKS == OleUIPromptUser(
				IDD_CANNOTUPDATELINK, 
				hwndDoc,
				(LPSTR)APPNAME)) {
			ContainerDoc_EditLinksCommand(lpContainerDoc);
		}
	}
}

	

/* ContainerDoc_SetShowObjectFlag
 * ------------------------------
 *
 *  Set/Clear the ShowObject flag of ContainerDoc
 */
void ContainerDoc_SetShowObjectFlag(LPCONTAINERDOC lpContainerDoc, BOOL fShow)
{
    if (!lpContainerDoc)
        return;

    lpContainerDoc->m_fShowObject = fShow;
}


/* ContainerDoc_GetShowObjectFlag
 * ------------------------------
 *
 *  Get the ShowObject flag of ContainerDoc
 */
BOOL ContainerDoc_GetShowObjectFlag(LPCONTAINERDOC lpContainerDoc)
{
    if (!lpContainerDoc)
        return FALSE;

    return lpContainerDoc->m_fShowObject;
}


/* ContainerDoc_InsertOleObjectCommand
 * -----------------------------------
 *
 * Insert a new OLE object in the ContainerDoc.
 */
void ContainerDoc_InsertOleObjectCommand(LPCONTAINERDOC lpContainerDoc)
{
    LPLINELIST              lpLL =&((LPOUTLINEDOC)lpContainerDoc)->m_LineList;
    LPLINE                  lpLine = NULL;
    HDC                     hDC;
    int                     nTab = 0;
    int                     nIndex = LineList_GetFocusLineIndex(lpLL);
    LPCONTAINERLINE         lpContainerLine=NULL;
    char                    szStgName[CWCSTORAGENAME];
    UINT                    uRet;
    OLEUIINSERTOBJECT       io;
    char                    szFile[OLEUI_CCHPATHMAX];
    DWORD                   dwOleCreateType;
    BOOL                    fDisplayAsIcon;
    HCURSOR                 hPrevCursor;

    //String initially NULL.
    _fmemset((LPSTR)szFile, 0, OLEUI_CCHPATHMAX);

    _fmemset((LPOLEUIINSERTOBJECT)&io, 0, sizeof(io));
    io.cbStruct=sizeof(io);
    io.dwFlags=IOF_SELECTCREATENEW | IOF_SHOWHELP;
    io.hWndOwner=((LPOUTLINEDOC)lpContainerDoc)->m_hWndDoc;

    io.lpszFile=(LPSTR)szFile;
    io.cchFile=sizeof(szFile);

    OLEDBG_BEGIN3("OleUIInsertObject called\r\n")
    uRet=OleUIInsertObject((LPOLEUIINSERTOBJECT)&io);
    OLEDBG_END3

    if (OLEUI_OK != uRet)
        return;     // user canceled dialog

    // this may take a while, put up hourglass cursor
    hPrevCursor = SetCursor(LoadCursor(NULL, IDC_WAIT));

    fDisplayAsIcon = (io.dwFlags & IOF_CHECKDISPLAYASICON ? TRUE : FALSE);

    // make up a storage name for the OLE object
    ContainerDoc_GetNextStgName(lpContainerDoc, szStgName, sizeof(szStgName));

    /* default the new line to have the same indent as previous line */
    lpLine = LineList_GetLine(lpLL, nIndex);
    if (lpLine)
        nTab = Line_GetTabLevel(lpLine);

    hDC = LineList_GetDC(lpLL);

    //Only try to create new objects IF Create New was selected
    if ((io.dwFlags & IOF_SELECTCREATENEW))
        dwOleCreateType = IOF_SELECTCREATENEW;
    else
        {
        if ((io.dwFlags & IOF_SELECTCREATEFROMFILE))
            dwOleCreateType = IOF_SELECTCREATEFROMFILE;

        if ((io.dwFlags & IOF_CHECKLINK))
            dwOleCreateType = IOF_CHECKLINK;
        }

    lpContainerLine = ContainerLine_Create(
            dwOleCreateType,
            hDC,
            nTab,
            lpContainerDoc,
            &io.clsid,
            (LPSTR)szFile,
            fDisplayAsIcon,
            io.hMetaPict,
            szStgName
    );

    if (!lpContainerLine)
        goto error;         // creation of OLE object FAILED

    if (io.hMetaPict) {
        OleUIMetafilePictIconFree(io.hMetaPict);    // clean up metafile
    }

    /* add a ContainerLine object to the document's LineList. The
    **    ContainerLine manages the rectangle on the screen occupied by
    **    the OLE object. later when the app is updated to support
    **    extended layout, there could be more than one Line associated
    **    with the OLE object.
    */

    LineList_AddLine(lpLL, (LPLINE)lpContainerLine, nIndex);

    /* before calling DoVerb(OLEIVERB_SHOW), check to see if the object
    **    has any initial extents.
    */
    ContainerLine_UpdateExtent(lpContainerLine, NULL);

    /* If a new embedded object was created, tell the object server to
    **    make itself visible (show itself).
    */
    if (dwOleCreateType == IOF_SELECTCREATENEW) {
        if (! ContainerLine_DoVerb(lpContainerLine, OLEIVERB_SHOW, TRUE, 
				TRUE)) {
            OutlineApp_ErrorMessage(g_lpApp, ErrMsgShowObj);
        }

        /* OLE2NOTE: we will immediately force a save of the object
        **    to guarantee that a valid initial object is saved
        **    with our document. if the object is a OLE 1.0 object,
        **    then it may exit without update. by forcing this
        **    initial save we consistently always have a valid
        **    object even if it is a OLE 1.0 object that exited
        **    without saving. if we did NOT do this save here, then
        **    we would have to worry about deleting the object if
        **    it was a OLE 1.0 object that closed without saving.
        **    the OLE 2.0 User Model dictates that the object
        **    should always be valid after CreateNew performed. the
        **    user must explicitly delete it.
        */
        ContainerLine_SaveOleObject(
                lpContainerLine, 
                lpContainerLine->m_lpStg, 
                TRUE,	/* fSameAsLoad */
                TRUE,	/* fRemember */
                TRUE	/* fForceUpdate */
   		);
    }

    OutlineDoc_SetModified((LPOUTLINEDOC)lpContainerDoc, TRUE, TRUE, TRUE);

    LineList_ReleaseDC(lpLL, hDC);

    SetCursor(hPrevCursor);     // restore original cursor

    return;

error:
    // NOTE: if ContainerLine_Create failed
    LineList_ReleaseDC(lpLL, hDC);

    if (OLEUI_OK == uRet && io.hMetaPict)
        OleUIMetafilePictIconFree(io.hMetaPict);    // clean up metafile

    SetCursor(hPrevCursor);     // restore original cursor
    OutlineApp_ErrorMessage(g_lpApp, ErrMsgInsertObj);
}



void ContainerDoc_EditLinksCommand(LPCONTAINERDOC lpContainerDoc)
{
    UINT        uRet;
    OLEUIEDITLINKS      el;
    LPCONTAINERLINE lpContainerLine = NULL;
    LPLINELIST lpLL = &((LPOUTLINEDOC)lpContainerDoc)->m_LineList;

    _fmemset((LPOLEUIEDITLINKS)&el,0,sizeof(el));
    el.cbStruct=sizeof(el);
    el.dwFlags=ELF_SHOWHELP;  //what else?
    el.hWndOwner=((LPOUTLINEDOC)lpContainerDoc)->m_hWndDoc;
    el.lpOleUILinkContainer =
            (LPOLEUILINKCONTAINER)&lpContainerDoc->m_OleUILinkContainer;

    OLEDBG_BEGIN3("OleUIEditLinks called\r\n")
    uRet=OleUIEditLinks((LPOLEUIEDITLINKS)&el);
    OLEDBG_END3

    OleDbgAssert((uRet==1) || (uRet==OLEUI_CANCEL));

}


/* Convert command - brings up the "Convert" dialog
 */
void ContainerDoc_ConvertCommand(
        LPCONTAINERDOC      lpContainerDoc, 
        BOOL                fServerNotRegistered
)
{
    LPOUTLINEDOC lpOutlineDoc = (LPOUTLINEDOC)lpContainerDoc;
    OLEUICONVERT ct;
    UINT         uRet;
    LPDATAOBJECT  lpDataObj;
    LPLINELIST lpLL = &((LPOUTLINEDOC)lpContainerDoc)->m_LineList;
    LPCONTAINERLINE lpContainerLine = NULL;
    BOOL         fSelIsOleObject;
    int          nIndex;
    STGMEDIUM    medium;
    LPSTR        lpErrMsg = NULL;
    HRESULT      hrErr;
    HCURSOR      hPrevCursor;
	BOOL		 fRunServer=FALSE;
    BOOL         fMustRun = FALSE;
    BOOL         fObjConverted = FALSE;
    BOOL         fDisplayChanged = FALSE;
    BOOL         fHaveCLSID = FALSE;
    BOOL         fHaveFmtUserType = FALSE;
    char         szUserType[128];
    BOOL         fMustActivate;

    /* OLE2NOTE: if we came to the Convert dialog because the user
    **    activated a non-registered object, then we should activate
    **    the object after the user has converted it or setup an
    **    ActivateAs server.
    */
    fMustActivate = fServerNotRegistered;

    _fmemset((LPOLEUICONVERT)&ct,0,sizeof(ct));

    fSelIsOleObject = ContainerDoc_IsSelAnOleObject(
            (LPCONTAINERDOC)lpContainerDoc,
            &IID_IDataObject,
            (LPUNKNOWN FAR*)&lpDataObj,
            &nIndex,
            (LPCONTAINERLINE FAR*)&lpContainerLine
    );

    lpErrMsg = ErrMsgCantConvert;

    if (! fSelIsOleObject) 
        goto error;     // can NOT do Convert.

    if (! lpContainerLine) {
        OleStdRelease((LPUNKNOWN)lpDataObj);
        goto error;     // can NOT do Convert.
    }

    ct.cbStruct  = sizeof(OLEUICONVERT);
    ct.dwFlags   = CF_SHOWHELPBUTTON;
    ct.hWndOwner = lpContainerDoc->m_OleDoc.m_OutlineDoc.m_hWndDoc;
    ct.lpszCaption = (LPSTR)NULL;
    ct.lpfnHook  = NULL;
    ct.lCustData = 0;
    ct.hInstance = NULL;
    ct.lpszTemplate = NULL;
    ct.hResource = 0;
    ct.fIsLinkedObject = ContainerLine_IsOleLink(lpContainerLine);
    ct.dvAspect = lpContainerLine->m_dwDrawAspect;

    if (! ct.fIsLinkedObject) {
        /* OLE2NOTE: the object is an embedded object. we should first
        **    attempt to read the actual object CLSID, file data
        **    format, and full user type name that is written inside of
        **    the object's storage as this should be the most
        **    definitive information. if this fails we will ask the
        **    object what its class is and attempt to get the rest of
        **    the information out of the REGDB.
        */
        hrErr=ReadClassStg(lpContainerLine->m_lpStg,(CLSID FAR*)&(ct.clsid));
        if (hrErr == NOERROR) 
            fHaveCLSID = TRUE;
        else {
            OleDbgOutHResult("ReadClassStg returned", hrErr);
        }
        hrErr = ReadFmtUserTypeStg(
                lpContainerLine->m_lpStg, 
                (CLIPFORMAT FAR*)&ct.wFormat, 
                (LPSTR FAR*)&ct.lpszUserType);
        if (hrErr == NOERROR) 
            fHaveFmtUserType = TRUE;
        else {
            OleDbgOutHResult("ReadFmtUserTypeStg returned", hrErr);
        }
    }

    if (! fHaveCLSID) {
        hrErr = lpContainerLine->m_lpOleObj->lpVtbl->GetUserClassID(
                lpContainerLine->m_lpOleObj,
                (CLSID FAR*)&ct.clsid
        );
        if (hrErr != NOERROR) 
            ct.clsid = CLSID_NULL;
    }
    if (! fHaveFmtUserType) {
        ct.wFormat = 0;
        if (OleStdGetUserTypeOfClass(
                (CLSID FAR*)&ct.clsid,szUserType,sizeof(szUserType),NULL)) {
            ct.lpszUserType = OleStdCopyString(szUserType, NULL);
        } else {
            ct.lpszUserType = NULL;
        }
    }

    if (lpContainerLine->m_dwDrawAspect == DVASPECT_ICON) {
        ct.hMetaPict = OleStdGetData(
                lpDataObj,
                CF_METAFILEPICT,
                NULL,
                DVASPECT_ICON,
                (LPSTGMEDIUM)&medium
        );
    } else {
        ct.hMetaPict = NULL;
    }
    OleStdRelease((LPUNKNOWN)lpDataObj);

    OLEDBG_BEGIN3("OleUIConvert called\r\n")
    uRet = OleUIConvert(&ct);
    OLEDBG_END3

    // this may take a while, put up hourglass cursor
    hPrevCursor = SetCursor(LoadCursor(NULL, IDC_WAIT));

    if (uRet == OLEUI_OK) {

        /*****************************************************************
        **  OLE2NOTE: the convert dialog actually allows the user to
        **    change two orthogonal properties of the object: the
        **    object's type/server and the object's display aspect.
        **    first we will execute the ConvertTo/ActivateAs action and
        **    then we will deal with any display aspect change. we want
        **    to be careful to only call IOleObject::Update once
        **    because this is an expensive operation; it results in
        **    launching the object's server.
        *****************************************************************/

        if (ct.dwFlags & CF_SELECTCONVERTTO &&
                ! IsEqualCLSID(&ct.clsid, &ct.clsidNew)) {

            /* user selected CONVERT.
            **
            ** OLE2NOTE: to achieve the "Convert To" at this point we
            **    need to take the following steps:
            **    1. unload the object.
            **    2. write the NEW CLSID and NEW user type name
            **       string into the storage of the object,
            **       BUT write the OLD format tag.
            **    3. force an update to force the actual conversion of
            **       the data bits.
            */
            lpErrMsg = ErrMsgConvertObj; // setup correct msg in case of error

            ContainerLine_UnloadOleObject(lpContainerLine);

            OLEDBG_BEGIN2("OleStdDoConvert called \r\n")
            hrErr = OleStdDoConvert(
                    lpContainerLine->m_lpStg, (REFCLSID)&ct.clsidNew);
            OLEDBG_END2
            if (hrErr != NOERROR) 
                goto error;

            // Reload the object
            ContainerLine_LoadOleObject(lpContainerLine);
            
            /* we need to force the object to run to complete the
            **    conversion. set flag to force OleRun to be called at 
            **    end of function. 
            */
            fMustRun = TRUE;
            fObjConverted = TRUE;

        } else if (ct.dwFlags & CF_SELECTACTIVATEAS) {
            /* user selected ACTIVATE AS.
            **
            ** OLE2NOTE: to achieve the "Activate As" at this point we
            **    need to take the following steps:
            **    1. unload ALL objects of the OLD class that app knows about
            **    2. add the TreatAs tag in the registration database
            **    by calling CoTreatAsClass().
            **    3. lazily it can reload the objects; when the objects
            **    are reloaded the TreatAs will take effect.
            */
            lpErrMsg = ErrMsgActivateAsObj; // setup msg in case of error

            ContainerDoc_UnloadAllOleObjectsOfClass(
                    lpContainerDoc, (REFCLSID)&ct.clsid);

            OLEDBG_BEGIN2("OleStdDoTreatAsClass called \r\n")
			hrErr = OleStdDoTreatAsClass(ct.lpszUserType, (REFCLSID)&ct.clsid,
					(REFCLSID)&ct.clsidNew);
            OLEDBG_END2

            // Reload the object
            ContainerLine_LoadOleObject(lpContainerLine);

            fMustActivate = TRUE;   // we should activate this object
        }
        
        /*****************************************************************
        **  OLE2NOTE: now we will try to change the display if
        **    necessary. 
        *****************************************************************/

        if (lpContainerLine->m_lpOleObj && 
                ct.dvAspect != lpContainerLine->m_dwDrawAspect) {
            /* user has selected to change display aspect between icon
            **    aspect and content aspect.
            **    
            ** OLE2NOTE: if we got here because the server was not
            **    registered, then we will NOT delete the object's
            **    original display aspect. because we do not have the
            **    original server, we can NEVER get it back. this is a
            **    safety precaution.
            */
            
            hrErr = OleStdSwitchDisplayAspect(
                    lpContainerLine->m_lpOleObj,
                    &lpContainerLine->m_dwDrawAspect,
                    ct.dvAspect,
                    ct.hMetaPict,
                    !fServerNotRegistered,   /* fDeleteOldAspect */
                    TRUE,                    /* fSetupViewAdvise */
                    (LPADVISESINK)&lpContainerLine->m_AdviseSink,
                    (BOOL FAR*)&fMustRun
            );

            if (hrErr == NOERROR) 
                fDisplayChanged = TRUE;

#if defined( INPLACE_CNTR )
                ContainerDoc_UpdateInPlaceObjectRects(
                        lpContainerLine->m_lpDoc, nIndex);
#endif
            
        } else if (ct.dvAspect == DVASPECT_ICON && ct.fObjectsIconChanged) {
            hrErr = OleStdSetIconInCache(
                    lpContainerLine->m_lpOleObj,
                    ct.hMetaPict
            );

            if (hrErr == NOERROR) 
                fDisplayChanged = TRUE;
        }

		/* we deliberately run the object so that the update won't shut
		** the server down. 
		*/
		if ((fMustActivate || fMustRun) 
                && !OleIsRunning(lpContainerLine->m_lpOleObj)) {
			ContainerLine_RunOleObject(lpContainerLine);
			fRunServer = TRUE;
		}

        if (fDisplayChanged) {
            /* the Object's display was changed, force a repaint of
            **    the line. note the extents of the object may have
            **    changed.
            */
            ContainerLine_UpdateExtent(lpContainerLine, NULL);
            LineList_ForceLineRedraw(lpLL, nIndex, TRUE);
        }
        
        if (fDisplayChanged || fObjConverted) {
            /* mark ContainerDoc as now dirty. if display changed, then
            **    the extents of the object may have changed.
            */
            OutlineDoc_SetModified(lpOutlineDoc, TRUE, TRUE, fDisplayChanged);
        }
		
		if (fMustActivate)
			ContainerLine_DoVerb(lpContainerLine, OLEIVERB_SHOW, FALSE,FALSE);
        else if (fRunServer) {
            /* if we launched the server in order to get an update of
            **    the presentation, then we must shut the server down
            */
            ContainerLine_CloseOleObject(lpContainerLine);
        }
    }


    if (ct.hMetaPict)
        OleUIMetafilePictIconFree(ct.hMetaPict);    // clean up metafile

    SetCursor(hPrevCursor);     // restore original cursor

    return;

error:
	if (fRunServer)
		ContainerLine_CloseOleObject(lpContainerLine);

    if (ct.lpszUserType)
        OleStdFreeString(ct.lpszUserType, NULL);

    if (ct.hMetaPict)
        OleUIMetafilePictIconFree(ct.hMetaPict);    // clean up metafile

    SetCursor(hPrevCursor);     // restore original cursor
    if (lpErrMsg) 
        OutlineApp_ErrorMessage(g_lpApp, lpErrMsg);
	
}


/* ContainerDoc_CloseAllOleObjects
** -------------------------------
**    Close all OLE objects. This forces all OLE objects to transition
**    from the running state to the loaded state.
**
**    Returns TRUE if all objects closed successfully  
**            FALSE if any object could not be closed.
*/
BOOL ContainerDoc_CloseAllOleObjects(LPCONTAINERDOC lpContainerDoc)
{
    LPLINELIST  lpLL = &((LPOUTLINEDOC)lpContainerDoc)->m_LineList;
    int         i;
    LPLINE      lpLine;
    BOOL        fStatus = TRUE;

    for (i = 0; i < lpLL->m_nNumLines; i++) {
        lpLine=LineList_GetLine(lpLL, i);

        if (lpLine && (Line_GetLineType(lpLine)==CONTAINERLINETYPE))
            if (! ContainerLine_CloseOleObject((LPCONTAINERLINE)lpLine))
                fStatus = FALSE;
    }
    
    return fStatus;
}


/* ContainerDoc_UnloadAllOleObjectsOfClass
** ---------------------------------------
**    Unload all OLE objects of a particular class. this is necessary
**    when a class level "ActivateAs" (aka. TreatAs) is setup. the user
**    can do this with the Convert dialog. for the TreatAs to take
**    effect, all objects of the class have to loaded and reloaded.
*/
void ContainerDoc_UnloadAllOleObjectsOfClass(
        LPCONTAINERDOC      lpContainerDoc,
        REFCLSID            rClsid
)
{
    LPLINELIST  lpLL = &((LPOUTLINEDOC)lpContainerDoc)->m_LineList;
    int         i;
    LPLINE      lpLine;
    CLSID       clsid;
    HRESULT     hrErr;

    for (i = 0; i < lpLL->m_nNumLines; i++) {
        lpLine=LineList_GetLine(lpLL, i);

        if (lpLine && (Line_GetLineType(lpLine)==CONTAINERLINETYPE)) {
            LPCONTAINERLINE lpContainerLine = (LPCONTAINERLINE)lpLine;
            
            if (! lpContainerLine->m_lpOleObj) 
                continue;       // this object is NOT loaded

            hrErr = lpContainerLine->m_lpOleObj->lpVtbl->GetUserClassID(
                    lpContainerLine->m_lpOleObj,
                    (CLSID FAR*)&clsid
            );
            if (hrErr == NOERROR && 
                    ( IsEqualCLSID((CLSID FAR*)&clsid,rClsid) 
                      || IsEqualCLSID(rClsid,&CLSID_NULL) ) ) {
                ContainerLine_UnloadOleObject(lpContainerLine);
            }
        }
    }
}


/* ContainerDoc_UpdateExtentOfAllOleObjects
** ----------------------------------------
**    Update the extents of any OLE object that is marked that its size
**    may  have changed. when an IAdviseSink::OnViewChange notification
**    is received, the corresponding ContainerLine is marked
**    (m_fDoGetExtent==TRUE) and a message (WM_U_UPDATEOBJECTEXTENT) is
**    posted to the document indicating that there are dirty objects.
**    when this message is received, this function is called.
*/
void ContainerDoc_UpdateExtentOfAllOleObjects(LPCONTAINERDOC lpContainerDoc)
{
    LPLINELIST  lpLL = &((LPOUTLINEDOC)lpContainerDoc)->m_LineList;
    int         i;
    LPLINE      lpLine;
    BOOL        fStatus = TRUE;

    for (i = 0; i < lpLL->m_nNumLines; i++) {
        lpLine=LineList_GetLine(lpLL, i);

        if (lpLine && (Line_GetLineType(lpLine)==CONTAINERLINETYPE)) {
            LPCONTAINERLINE lpContainerLine = (LPCONTAINERLINE)lpLine;
            
            if (lpContainerLine->m_fDoGetExtent)
				ContainerLine_UpdateExtent(lpContainerLine, NULL);
        }
    }
}


BOOL ContainerDoc_SaveToFile(
        LPCONTAINERDOC          lpContainerDoc,
        LPCSTR                  lpszFileName,
        UINT                    uFormat,
        BOOL                    fRemember
)
{
    LPOLEAPP lpOleApp = (LPOLEAPP)g_lpApp;
    LPOUTLINEDOC lpOutlineDoc = (LPOUTLINEDOC)lpContainerDoc;
    LPOLEDOC lpOleDoc = (LPOLEDOC)lpContainerDoc;
    LPSTORAGE lpDestStg;
    BOOL fStatus;
    BOOL fMustRelDestStg = FALSE;
    HRESULT hrErr;

    if (fRemember) {
        if (lpszFileName) {
            fStatus = OutlineDoc_SetFileName(
                    lpOutlineDoc, (LPSTR)lpszFileName, NULL);
            if (! fStatus) goto error;
        }

        /* The ContainerDoc keeps its storage open at all times. it is not
        **    necessary to reopen the file.
        ** if SaveAs is pending, then lpNewStg is the new destination for
        **    the save operation, else the existing storage is the dest.
        */
        lpDestStg = (lpContainerDoc->m_lpNewStg ?
                        lpContainerDoc->m_lpNewStg : lpContainerDoc->m_lpStg);
    } else {
        if (! lpszFileName)
            goto error;

        /* OLE2NOTE: since we are preforming a SaveCopyAs operation, we
        **    do not need to have the DocFile open in STGM_TRANSACTED mode.
        **    there is less overhead to use STGM_DIRECT mode.
        */
        hrErr = StgCreateDocfile(
                lpszFileName,
                STGM_READWRITE|STGM_DIRECT|STGM_SHARE_DENY_WRITE|STGM_CREATE,
                0,
                &lpDestStg
        );
        if (! OleDbgVerifySz(hrErr == NOERROR, "Could not create Docfile"))
            goto error;
        fMustRelDestStg = TRUE;
    }

    /*  OLE2NOTE: we must be sure to write our class ID into our
    **    storage. this information is used by OLE to determine the
    **    class of the data stored in our storage. Even for top
    **    "file-level" objects this information should be written to
    **    the file.
    */
    hrErr = WriteClassStg(lpDestStg, &CLSID_APP);
    if(hrErr != NOERROR) goto error;

    fStatus = OutlineDoc_SaveSelToStg(
            lpOutlineDoc,
            NULL,           // save all lines
            uFormat,
            lpDestStg,
            TRUE            // remember this stg
        );

    if (fStatus)
        fStatus = OleStdCommitStorage(lpDestStg);

    if (fRemember) {
        /* if SaveAs was pending, then release the old storage and remember
        **    the new storage as the active current storage. all data from
        **    the old storage has been copied into the new storage.
        */
        if (lpContainerDoc->m_lpNewStg) {
            OleStdRelease((LPUNKNOWN)lpContainerDoc->m_lpStg);  // free old stg
            lpContainerDoc->m_lpStg = lpContainerDoc->m_lpNewStg;   // save new stg
            lpContainerDoc->m_lpNewStg = NULL;
        }
        if (! fStatus) goto error;

        OutlineDoc_SetModified(lpOutlineDoc, FALSE, FALSE, FALSE);

        /* OLE2NOTE: we must note the time this File-Based object has
        **    been saved in the RunningObjectTable. this is necessary
        **    for Container applications which support linking to
        **    embedded objects. These change times are used as the
        **    basis for IOleObject::IsUpToDate.  It is important to set
        **    the time of the file-based object following a save
        **    operation to exactly the time of the saved file. this
        **    helps IOleObject::IsUpToDate to give the correct answer
        **    after a file has been saved. 
        */
        OleStdNoteFileChangeTime(
                lpOutlineDoc->m_szFileName, lpOleDoc->m_dwRegROT);
    }

    if (fMustRelDestStg)
        OleStdRelease((LPUNKNOWN)lpDestStg);
    return TRUE;

error:
    if (fMustRelDestStg)
        OleStdRelease((LPUNKNOWN)lpDestStg);
    OutlineApp_ErrorMessage(g_lpApp, ErrMsgSaving);
    return FALSE;
}


/* ContainerDoc_ContainerLineDoVerbCommand
** ---------------------------------------
**    Execute a verb of the OLE object in the current focus line.
*/
void ContainerDoc_ContainerLineDoVerbCommand(
        LPCONTAINERDOC          lpContainerDoc,
        LONG                    iVerb
)
{
    LPLINELIST lpLL = &((LPOUTLINEDOC)lpContainerDoc)->m_LineList;
    int nIndex = LineList_GetFocusLineIndex(lpLL);
    LPLINE lpLine = LineList_GetLine(lpLL, nIndex);
    HCURSOR                 hPrevCursor;

    if (! lpLine || (Line_GetLineType(lpLine) != CONTAINERLINETYPE) ) return;

    // this may take a while, put up hourglass cursor
    hPrevCursor = SetCursor(LoadCursor(NULL, IDC_WAIT));

    ContainerLine_DoVerb((LPCONTAINERLINE) lpLine, iVerb, TRUE, TRUE);

    SetCursor(hPrevCursor);     // restore original cursor
}


/* ContainerDoc_GetNextStgName
** ---------------------------
**    Generate the next unused name for a sub-storage to be used by an
**    OLE object. The ContainerDoc keeps a counter. The storages for
**    OLE objects are simply numbered (eg. Obj 0, Obj 1). A "long"
**    integer worth of storage names should be more than enough than we
**    will ever need.
**
**    NOTE: when an OLE object is transfered via drag/drop or the
**    clipboard, we attempt to keep the currently assigned name for the
**    object (if not currently in use). thus it is possible that an
**    object with a the next default name (eg. "Obj 5") already exists
**    in the current document if an object with this name was privously
**    transfered (pasted or dropped). we therefore loop until we find
**    the next lowest unused name.
*/
void ContainerDoc_GetNextStgName(
        LPCONTAINERDOC          lpContainerDoc,
        LPSTR                   lpszStgName,
        int                     nLen
)
{
    wsprintf(lpszStgName, "%s %ld",
            (LPSTR)DEFOBJNAMEPREFIX,
            ++(lpContainerDoc->m_nNextObjNo)
    );

    while (ContainerDoc_IsStgNameUsed(lpContainerDoc, lpszStgName) == TRUE) {
        wsprintf(lpszStgName, "%s %ld",
                (LPSTR)DEFOBJNAMEPREFIX,
                ++(lpContainerDoc->m_nNextObjNo)
        );
    }
}


/* ContainerDoc_IsStgNameUsed
** --------------------------
**    Check if a given StgName is already in use.
*/
BOOL ContainerDoc_IsStgNameUsed(
        LPCONTAINERDOC          lpContainerDoc,
        LPSTR                   lpszStgName
)
{
    LPLINELIST lpLL = &((LPOUTLINEDOC)lpContainerDoc)->m_LineList;
    int i;
    LPLINE lpLine;

    for (i = 0; i < lpLL->m_nNumLines; i++) {
        lpLine=LineList_GetLine(lpLL, i);

        if (lpLine && (Line_GetLineType(lpLine)==CONTAINERLINETYPE)) {
            if (lstrcmp(lpszStgName,
                    ((LPCONTAINERLINE)lpLine)->m_szStgName) == 0) {
                return TRUE;    // Match FOUND!
            }
        }
    }
    return FALSE;   // if we get here, then NO match was found.
}


LPSTORAGE ContainerDoc_GetStg(LPCONTAINERDOC lpContainerDoc)
{
    return lpContainerDoc->m_lpStg;
}


/* ContainerDoc_GetSingleOleObject
** -------------------------------
**    If the entire document contains a single OLE object, then
**    return the desired interface of the object.
**
**    Returns NULL if there is are multiple lines in the document or
**    the single line is not a ContainerLine.
*/
LPUNKNOWN ContainerDoc_GetSingleOleObject(
        LPCONTAINERDOC          lpContainerDoc,
        REFIID                  riid,
        LPCONTAINERLINE FAR*    lplpContainerLine
)
{
    LPLINELIST lpLL = &((LPOUTLINEDOC)lpContainerDoc)->m_LineList;
    LPLINE lpLine;
    LPUNKNOWN lpObj = NULL;

    if (lplpContainerLine)
        *lplpContainerLine = NULL;

    if (lpLL->m_nNumLines != 1)
        return NULL;    // doc does NOT contain a single line

    lpLine=LineList_GetLine(lpLL, 0);

    if (lpLine && (Line_GetLineType(lpLine)==CONTAINERLINETYPE))
        lpObj = ContainerLine_GetOleObject((LPCONTAINERLINE)lpLine, riid);

    if (lplpContainerLine)
        *lplpContainerLine = (LPCONTAINERLINE)lpLine;

    return lpObj;
}


/* ContainerDoc_IsSelAnOleObject
** -----------------------------
**    Check if the selection is a single selection of an OLE object.
**    if so, then optionally return the desired interface of the object
**    and/or index of the ContainerLine containing the OLE object.
**
**    Returns FALSE if there is a multiple selection or the single
**    selection is not a ContainerLine.
*/
BOOL ContainerDoc_IsSelAnOleObject(
        LPCONTAINERDOC          lpContainerDoc,
        REFIID                  riid,
        LPUNKNOWN FAR*          lplpvObj,
        int FAR*                lpnIndex,
        LPCONTAINERLINE FAR*    lplpContainerLine
)
{
    LPLINELIST lpLL = &((LPOUTLINEDOC)lpContainerDoc)->m_LineList;
    LINERANGE lrSel;
    int nNumSel;
    LPLINE lpLine;

    if (lplpvObj) *lplpvObj = NULL;
    if (lpnIndex) *lpnIndex = -1;
    if (lplpContainerLine) *lplpContainerLine = NULL;

    nNumSel = LineList_GetSel(lpLL, (LPLINERANGE)&lrSel);
    if (nNumSel != 1)
        return FALSE;   // selection is not a single line

    lpLine = LineList_GetLine(lpLL, lrSel.m_nStartLine);

    if (lpLine && (Line_GetLineType(lpLine)==CONTAINERLINETYPE)) {
        if (lpnIndex)
            *lpnIndex = lrSel.m_nStartLine;
        if (lplpContainerLine) 
            *lplpContainerLine = (LPCONTAINERLINE)lpLine;
        if (riid) {
            *lplpvObj = ContainerLine_GetOleObject(
                    (LPCONTAINERLINE)lpLine,
                    riid
            );
        }

        return (*lplpvObj ? TRUE : FALSE);
    }

    return FALSE;
}


/*************************************************************************
** ContainerDoc::IOleUILinkContainer interface implementation
*************************************************************************/

STDMETHODIMP CntrDoc_LinkCont_QueryInterface(
        LPOLEUILINKCONTAINER    lpThis,
        REFIID                  riid,
        LPVOID FAR*             lplpvObj
)
{
    LPOLEDOC lpOleDoc = (LPOLEDOC)
            ((struct CDocOleUILinkContainerImpl FAR*)lpThis)->lpContainerDoc;

    return OleDoc_QueryInterface(lpOleDoc, riid, lplpvObj);
}


STDMETHODIMP_(ULONG) CntrDoc_LinkCont_AddRef(LPOLEUILINKCONTAINER lpThis)
{
    LPOLEDOC lpOleDoc = (LPOLEDOC)
            ((struct CDocOleUILinkContainerImpl FAR*)lpThis)->lpContainerDoc;

    OleDbgAddRefMethod(lpThis, "IOleUILinkContainer");

    return OleDoc_AddRef(lpOleDoc);
}


STDMETHODIMP_(ULONG) CntrDoc_LinkCont_Release(LPOLEUILINKCONTAINER lpThis)
{
    LPOLEDOC lpOleDoc = (LPOLEDOC)
            ((struct CDocOleUILinkContainerImpl FAR*)lpThis)->lpContainerDoc;

    OleDbgReleaseMethod(lpThis, "IOleUILinkContainer");

    return OleDoc_Release(lpOleDoc);
}


STDMETHODIMP_(DWORD) CntrDoc_LinkCont_GetNextLink(
        LPOLEUILINKCONTAINER    lpThis,
        DWORD                   dwLink
)
{
    LPCONTAINERDOC lpContainerDoc =
            ((struct CDocOleUILinkContainerImpl FAR*)lpThis)->lpContainerDoc;
	LPCONTAINERLINE lpContainerLine = NULL;

    OLEDBG_BEGIN2("CntrDoc_LinkCont_GetNextLink\r\n")

	lpContainerLine = ContainerDoc_GetNextLink(
			lpContainerDoc, 
			(LPCONTAINERLINE)dwLink
	);

    OLEDBG_END2
    return (DWORD)lpContainerLine;
}


STDMETHODIMP CntrDoc_LinkCont_SetLinkUpdateOptions(
        LPOLEUILINKCONTAINER    lpThis,
        DWORD                   dwLink,
        DWORD                   dwUpdateOpt
)
{
    LPCONTAINERDOC lpContainerDoc =
            ((struct CDocOleUILinkContainerImpl FAR*)lpThis)->lpContainerDoc;
    LPCONTAINERLINE lpContainerLine = (LPCONTAINERLINE)dwLink;
    LPOLELINK lpOleLink;
    SCODE sc = S_OK;
    HRESULT hrErr;

    OLEDBG_BEGIN2("CntrDoc_LinkCont_SetLinkUpdateOptions\r\n")

    OleDbgAssert(lpContainerLine);

    lpOleLink = (LPOLELINK)ContainerLine_GetOleObject(
            lpContainerLine,
            &IID_IOleLink
    );

    if (! lpOleLink) {
        sc = E_FAIL;
        goto error;
    }

    OLEDBG_BEGIN2("IOleLink::SetUpdateOptions called\r\n")
    hrErr = lpOleLink->lpVtbl->SetUpdateOptions(
            lpOleLink,
            dwUpdateOpt
    );
    OLEDBG_END2

    OleStdRelease((LPUNKNOWN)lpOleLink);

    if (hrErr != NOERROR) {
        OleDbgOutHResult("IOleLink::SetUpdateOptions returned", hrErr);
        sc = GetScode(hrErr);
        goto error;
    }

    OLEDBG_END2
    return ResultFromScode(sc);

error:
    OLEDBG_END2
    return ResultFromScode(sc);
}


STDMETHODIMP CntrDoc_LinkCont_GetLinkUpdateOptions(
        LPOLEUILINKCONTAINER    lpThis,
        DWORD                   dwLink,
        DWORD FAR*              lpdwUpdateOpt
)
{
    LPCONTAINERDOC lpContainerDoc =
            ((struct CDocOleUILinkContainerImpl FAR*)lpThis)->lpContainerDoc;
    LPCONTAINERLINE lpContainerLine = (LPCONTAINERLINE)dwLink;
    LPOLELINK lpOleLink;
    SCODE sc = S_OK;
    HRESULT hrErr;

    OLEDBG_BEGIN2("CntrDoc_LinkCont_GetLinkUpdateOptions\r\n")

    OleDbgAssert(lpContainerLine);

    lpOleLink = (LPOLELINK)ContainerLine_GetOleObject(
            lpContainerLine,
            &IID_IOleLink
    );

    if (! lpOleLink) {
        sc = E_FAIL;
        goto error;
    }

    OLEDBG_BEGIN2("IOleLink::GetUpdateOptions called\r\n")
    hrErr = lpOleLink->lpVtbl->GetUpdateOptions(
            lpOleLink,
            lpdwUpdateOpt
    );
    OLEDBG_END2

    OleStdRelease((LPUNKNOWN)lpOleLink);

    if (hrErr != NOERROR) {
        OleDbgOutHResult("IOleLink::GetUpdateOptions returned", hrErr);
        sc = GetScode(hrErr);
        goto error;
    }

    OLEDBG_END2
    return ResultFromScode(sc);

error:
    OLEDBG_END2
    return ResultFromScode(sc);
}


STDMETHODIMP CntrDoc_LinkCont_SetLinkSource(
        LPOLEUILINKCONTAINER	lpThis,
		DWORD					dwLink,
        LPSTR					lpszDisplayName,
		ULONG					lenFileName,		
        ULONG FAR*				lpchEaten,
		BOOL					fValidateSource
)
{
    LPCONTAINERDOC lpContainerDoc =
            ((struct CDocOleUILinkContainerImpl FAR*)lpThis)->lpContainerDoc;
    LPCONTAINERLINE lpContainerLine = (LPCONTAINERLINE)dwLink;
    SCODE       sc = S_OK;
    HRESULT     hrErr;
    LPOLELINK   lpOleLink = NULL;
    LPBC        lpbc = NULL;
    LPMONIKER   lpmk = NULL;
	LPOLEOBJECT lpLinkSrcOleObj = NULL;
	CLSID       clsid = CLSID_NULL;


    OLEDBG_BEGIN2("CntrDoc_LinkCont_SetLinkSource\r\n")

    OleDbgAssert(lpContainerLine);

	lpContainerLine->m_fLinkUnavailable = TRUE;
	
	if (fValidateSource) {

        /* OLE2NOTE: validate the link source by parsing the string
        **    into a Moniker. if this is successful, then the string is
        **    valid.
        */
        hrErr = CreateBindCtx(0, (LPBC FAR*)&lpbc);
        if (hrErr != NOERROR) {
            sc = GetScode(hrErr);   // ERROR: OOM
            goto cleanup;
        }

		OLEDBG_BEGIN2("MkParseDisplayName called\r\n")
		hrErr = MkParseDisplayName(
				lpbc, lpszDisplayName, lpchEaten, (LPMONIKER FAR*)&lpmk);
		OLEDBG_END2

		if (hrErr != NOERROR) {
			sc = GetScode(hrErr);	// ERROR in parsing moniker!
			goto cleanup;
		}
        /* OLE2NOTE: the link source was validated; it successfully
        **    parsed into a Moniker. we can set the source of the link
        **    directly with this Moniker. if we want the link to be
        **    able to know the correct class for the new link source,
        **    we must bind to the moniker and get the CLSID. if we do
        **    not do this then methods like IOleObject::GetUserType
        **    will return nothing (NULL strings).
        */
        
        hrErr = lpmk->lpVtbl->BindToObject(
                lpmk,lpbc,NULL,&IID_IOleObject,(LPVOID FAR*)&lpLinkSrcOleObj);
		if (hrErr == NOERROR) {
			hrErr = lpLinkSrcOleObj->lpVtbl->GetUserClassID(
							lpLinkSrcOleObj,
							(CLSID FAR*)&clsid);
			lpContainerLine->m_fLinkUnavailable = FALSE;			
		}
		else
			lpContainerLine->m_fLinkUnavailable = TRUE;
	}
	else {
		LPMONIKER	lpmkFile = NULL;
		LPMONIKER	lpmkItem = NULL;
		char		szName[OLEUI_CCHPATHMAX];
		
		lstrcpyn((LPSTR)szName, lpszDisplayName, (int)lenFileName + 1);
		
		OLEDBG_BEGIN2("CreateFileMoniker called\r\n")		
		CreateFileMoniker((LPSTR)szName, (LPMONIKER FAR*)&lpmkFile);
		OLEDBG_END2
			
		if (!lpmkFile)
			goto cleanup;

        if (lstrlen(lpszDisplayName) > (int)lenFileName) {	// have item name
            lstrcpy((LPSTR)szName, lpszDisplayName + lenFileName + 1);
		
            OLEDBG_BEGIN2("CreateItemMoniker called\r\n")
            CreateItemMoniker(
					OLESTDDELIM, (LPSTR)szName, (LPMONIKER FAR*)&lpmkItem);
            OLEDBG_END2
			
            if (!lpmkItem) {
                OleStdRelease((LPUNKNOWN)lpmkFile);
                goto cleanup;
            }
			
			OLEDBG_BEGIN2("CreateGenericComposite called\r\n")
			CreateGenericComposite(lpmkFile, lpmkItem, (LPMONIKER FAR*)&lpmk);
			OLEDBG_END2
	
			if (lpmkFile)
				OleStdRelease((LPUNKNOWN)lpmkFile);
			if (lpmkItem)
				OleStdRelease((LPUNKNOWN)lpmkItem);
		
			if (!lpmk)
				goto cleanup;
		}
		else
			lpmk = lpmkFile;
	}
	
    lpOleLink = (LPOLELINK)ContainerLine_GetOleObject(
            lpContainerLine,
            &IID_IOleLink
    );

    if (! lpOleLink) {
        OleDbgAssert(lpOleLink != NULL);
        sc = E_FAIL;
        goto cleanup;
    }
    
    if (lpmk) {
        
        OLEDBG_BEGIN2("IOleLink::SetSourceMoniker called\r\n")
        hrErr = lpOleLink->lpVtbl->SetSourceMoniker(
                lpOleLink, lpmk, (REFCLSID)&clsid);
        OLEDBG_END2

        if (FAILED(GetScode(hrErr))) {
            OleDbgOutHResult("IOleLink::SetSourceMoniker returned",hrErr);
            sc = GetScode(hrErr);
            goto cleanup;
        }

        /* OLE2NOTE: above we forced the link source moniker to bind.
        **    because we deliberately hold on to the bind context
        **    (lpbc) the link source object will not shut down. during
        **    the call to IOleLink::SetSourceMoniker, the link will
        **    connect to the running link source (the link internally
        **    calls BindIfRunning). it is important to initially allow
        **    the link to bind to the running object so that it can get
        **    an update of the presentation for its cache. we do not
        **    want the connection from our link to the link source be
        **    the only reason the link source stays running. thus we
        **    deliberately for the link to release (unbind) the source
        **    object, we then release the bind context, and then we
        **    allow the link to rebind to the link source if it is
        **    running anyway.
        */
        if (lpbc && lpmk->lpVtbl->IsRunning(lpmk,lpbc,NULL,NULL) == NOERROR) {

            OLEDBG_BEGIN2("IOleLink::Update called\r\n")
            hrErr = lpOleLink->lpVtbl->Update(lpOleLink, NULL);
            OLEDBG_END2

#if defined( _DEBUG )
            if (FAILED(GetScode(hrErr))) 
                OleDbgOutHResult("IOleLink::Update returned",hrErr);
#endif

            OLEDBG_BEGIN2("IOleLink::UnbindSource called\r\n")
            hrErr = lpOleLink->lpVtbl->UnbindSource(lpOleLink);
            OLEDBG_END2

#if defined( _DEBUG )
            if (FAILED(GetScode(hrErr))) 
                OleDbgOutHResult("IOleLink::UnbindSource returned",hrErr);
#endif

            if (lpLinkSrcOleObj) {
                OleStdRelease((LPUNKNOWN)lpLinkSrcOleObj);
                lpLinkSrcOleObj = NULL;
            }

            if (lpbc) {
                OleStdRelease((LPUNKNOWN)lpbc);
                lpbc = NULL;
            }

            OLEDBG_BEGIN2("IOleLink::BindIfRunning called\r\n")
            hrErr = lpOleLink->lpVtbl->BindIfRunning(lpOleLink);
            OLEDBG_END2

#if defined( _DEBUG )
            if (FAILED(GetScode(hrErr))) 
                OleDbgOutHResult("IOleLink::BindIfRunning returned",hrErr);
#endif
        }        
    } else {
        /* OLE2NOTE: the link source was NOT validated; it was NOT
        **    successfully parsed into a Moniker. we can only set the
        **    display name string as the source of the link. this link
        **    is not able to bind.
        */
        OLEDBG_BEGIN2("IOleLink::SetSourceDisplayName called\r\n")
        hrErr = lpOleLink->lpVtbl->SetSourceDisplayName(
                lpOleLink, (LPCSTR)lpszDisplayName);
        OLEDBG_END2

        if (hrErr != NOERROR) {
            OleDbgOutHResult("IOleLink::SetSourceDisplayName returned",hrErr);
            sc = GetScode(hrErr);
            goto cleanup;
        }
    }

cleanup:
    if (lpOleLink)
        OleStdRelease((LPUNKNOWN)lpOleLink);
    if (lpLinkSrcOleObj) 
        OleStdRelease((LPUNKNOWN)lpLinkSrcOleObj);
    if (lpmk)
        OleStdRelease((LPUNKNOWN)lpmk);
    if (lpbc)
        OleStdRelease((LPUNKNOWN)lpbc);

    OLEDBG_END2
    return ResultFromScode(sc);
}


STDMETHODIMP CntrDoc_LinkCont_GetLinkSource(
        LPOLEUILINKCONTAINER    lpThis,
        DWORD                   dwLink,
        LPSTR FAR*              lplpszDisplayName,
        ULONG FAR*              lplenFileName,
        LPSTR FAR*              lplpszFullLinkType,
        LPSTR FAR*              lplpszShortLinkType,
        BOOL FAR*               lpfSourceAvailable,
        BOOL FAR*               lpfIsSelected
)
{
    LPCONTAINERDOC lpContainerDoc =
            ((struct CDocOleUILinkContainerImpl FAR*)lpThis)->lpContainerDoc;
    LPCONTAINERLINE lpContainerLine = (LPCONTAINERLINE)dwLink;
    LPOLELINK       lpOleLink = NULL;
    LPOLEOBJECT     lpOleObj = NULL;
    LPMONIKER       lpmk = NULL;
    LPMONIKER       lpmkFirst = NULL;
    LPBC            lpbc = NULL;
    SCODE           sc = S_OK;
    HRESULT         hrErr;

    OLEDBG_BEGIN2("CntrDoc_LinkCont_GetLinkSource\r\n")

    /* OLE2NOTE: we must make sure to set all out parameters to NULL. */
    *lplpszDisplayName  = NULL;
    *lplpszFullLinkType = NULL;
    *lplpszShortLinkType= NULL;
    *lplenFileName      = 0;
    *lpfSourceAvailable = !lpContainerLine->m_fLinkUnavailable;

    OleDbgAssert(lpContainerLine);

    lpOleLink = (LPOLELINK)ContainerLine_GetOleObject(
            lpContainerLine,
            &IID_IOleLink
    );

    if (! lpOleLink) {
        OLEDBG_END2
        return ResultFromScode(E_FAIL);
    }

    OLEDBG_BEGIN2("IOleLink::GetSourceMoniker called\r\n")
    hrErr = lpOleLink->lpVtbl->GetSourceMoniker(
            lpOleLink,
            (LPMONIKER FAR*)&lpmk
    );
    OLEDBG_END2

    if (hrErr == NOERROR) {
        /* OLE2NOTE: the link has the Moniker form of the link source;
        **    this is therefore a validated link source. if the first
        **    part of the Moniker is a FileMoniker, then we need to
        **    return the length of the filename string. we need to
        **    return the ProgID associated with the link source as the
        **    "lpszShortLinkType". we need to return the
        **    FullUserTypeName associated with the link source as the
        **    "lpszFullLinkType".
        */
		
        lpOleObj = (LPOLEOBJECT)OleStdQueryInterface(
                (LPUNKNOWN)lpOleLink, &IID_IOleObject);
        if (lpOleObj) {
            lpOleObj->lpVtbl->GetUserType(
                    lpOleObj,
                    USERCLASSTYPE_FULL,
                    lplpszFullLinkType
            );
            lpOleObj->lpVtbl->GetUserType(
                    lpOleObj,
                    USERCLASSTYPE_SHORT,
                    lplpszShortLinkType
            );
            OleStdRelease((LPUNKNOWN)lpOleObj);
        }
        *lplenFileName = OleStdGetLenFilePrefixOfMoniker(lpmk);
        lpmk->lpVtbl->Release(lpmk);
    }

    OLEDBG_BEGIN2("IOleLink::GetSourceDisplayName called\r\n")
    hrErr = lpOleLink->lpVtbl->GetSourceDisplayName(
            lpOleLink,
            lplpszDisplayName
    );
    OLEDBG_END2

    OleStdRelease((LPUNKNOWN)lpOleLink);

    if (hrErr != NOERROR) {
        OleDbgOutHResult("IOleLink::GetSourceDisplayName returned", hrErr);
        OLEDBG_END2
        return hrErr;
    }

    OLEDBG_END2
        
    if (lpfIsSelected)
        *lpfIsSelected = Line_IsSelected((LPLINE)lpContainerLine);

    return NOERROR;
}


STDMETHODIMP CntrDoc_LinkCont_OpenLinkSource(
        LPOLEUILINKCONTAINER    lpThis,
        DWORD                   dwLink
)
{
    LPCONTAINERDOC lpContainerDoc =
            ((struct CDocOleUILinkContainerImpl FAR*)lpThis)->lpContainerDoc;
    LPCONTAINERLINE lpContainerLine = (LPCONTAINERLINE)dwLink;
    SCODE sc = S_OK;

    OLEDBG_BEGIN2("CntrDoc_LinkCont_OpenLinkSource\r\n")

    OleDbgAssert(lpContainerLine);

    if (! ContainerLine_DoVerb(lpContainerLine, OLEIVERB_SHOW, TRUE, FALSE)) {
        OutlineApp_ErrorMessage(g_lpApp, ErrMsgShowObj);
        sc = E_FAIL;
    }
	
	lpContainerLine->m_fLinkUnavailable = (sc != S_OK);

    OLEDBG_END2
    return ResultFromScode(sc);
}


STDMETHODIMP CntrDoc_LinkCont_UpdateLink(
        LPOLEUILINKCONTAINER    lpThis,
        DWORD                   dwLink,
		BOOL					fErrorMessage,
		BOOL					fErrorAction		// ignore if fErrorMessage
													//		is FALSE
)
{
    LPCONTAINERDOC lpContainerDoc =
            ((struct CDocOleUILinkContainerImpl FAR*)lpThis)->lpContainerDoc;
    LPCONTAINERLINE lpContainerLine = (LPCONTAINERLINE)dwLink;
    SCODE sc = S_OK;
    HRESULT hrErr;

    OLEDBG_BEGIN2("CntrDoc_LinkCont_UpdateLink\r\n")

    OleDbgAssert(lpContainerLine);

    if (! lpContainerLine->m_lpOleObj)
        ContainerLine_LoadOleObject(lpContainerLine);

	if (!fErrorMessage) {
		OLEDBG_BEGIN2("IOleObject::IsUpToDate called\r\n")
		hrErr = lpContainerLine->m_lpOleObj->lpVtbl->IsUpToDate(
				lpContainerLine->m_lpOleObj
		);
		OLEDBG_END2
	}	
	
	if (hrErr != NOERROR) {
		OLEDBG_BEGIN2("IOleObject::Update called\r\n")
		hrErr = lpContainerLine->m_lpOleObj->lpVtbl->Update(
				lpContainerLine->m_lpOleObj
		);
		OLEDBG_END2
	}

	lpContainerLine->m_fLinkUnavailable = (hrErr != NOERROR);
		
    if (hrErr != NOERROR) {
        OleDbgOutHResult("IOleObject::Update returned", hrErr);
        sc = GetScode(hrErr);
		if (fErrorMessage)
			ProcessError(hrErr, lpContainerLine, fErrorAction);
    }
    /* OLE2NOTE: if the update of the object requires us to update our
    **    display, then we will automatically be sent a OnViewChange
    **    advise. thus we do not need to take any action here to force
    **    a repaint.
    */

    OLEDBG_END2
    return ResultFromScode(sc);
}


/* CntrDoc_LinkCont_CancelLink
** ---------------------------
**    Convert the link to a static picture.
**    
**    OLE2NOTE: OleCreateStaticFromData can be used to create a static
**    picture object.
*/
STDMETHODIMP CntrDoc_LinkCont_CancelLink(
        LPOLEUILINKCONTAINER    lpThis,
        DWORD                   dwLink
)
{
    LPCONTAINERDOC lpContainerDoc =
            ((struct CDocOleUILinkContainerImpl FAR*)lpThis)->lpContainerDoc;
	LPCONTAINERLINE lpContainerLine = (LPCONTAINERLINE)dwLink;
    LPLINELIST          lpLL = &((LPOUTLINEDOC)lpContainerDoc)->m_LineList;
    LPLINE              lpLine = NULL;
    HDC                 hDC;
    int                 nTab = 0;
    char                szStgName[CWCSTORAGENAME];
    LPCONTAINERLINE     lpNewContainerLine = NULL;
    LPDATAOBJECT        lpSrcDataObj;
    LPOLELINK           lpOleLink;
    int nIndex = LineList_GetLineIndex(lpLL, (LPLINE)lpContainerLine);

    OLEDBG_BEGIN2("CntrDoc_LinkCont_CancelLink\r\n")

    /* we will first break the connection of the link to its source. */
    lpOleLink = (LPOLELINK)ContainerLine_GetOleObject(
            lpContainerLine,
            &IID_IOleLink
    );
    if (lpOleLink) {
        OLEDBG_BEGIN2("IOleLink::SetSourceMoniker called\r\n")
        lpOleLink->lpVtbl->SetSourceMoniker(
                lpOleLink, NULL, (REFCLSID)&CLSID_NULL);
        OLEDBG_END2
        OleStdRelease((LPUNKNOWN)lpOleLink);
    }
        
    lpSrcDataObj = (LPDATAOBJECT)ContainerLine_GetOleObject(
            lpContainerLine,&IID_IDataObject);
    if (! lpSrcDataObj) 
        goto error;

    ContainerDoc_GetNextStgName(lpContainerDoc, szStgName, sizeof(szStgName));
    nTab = Line_GetTabLevel((LPLINE)lpContainerLine);
    hDC = LineList_GetDC(lpLL);

    lpNewContainerLine = ContainerLine_CreateFromData(
            hDC,
            nTab,
            lpContainerDoc,
            lpSrcDataObj,
            OLECREATEFROMDATA_STATIC,
            0,   /* no special cfFormat required */
            FALSE,  /* fDisplayAsIcon */
            NULL,   /* hMetaPict */
            szStgName
    );
    LineList_ReleaseDC(lpLL, hDC);

    OleStdRelease((LPUNKNOWN)lpSrcDataObj);

    if (! lpNewContainerLine)
        goto error;
	
    OutlineDoc_SetModified((LPOUTLINEDOC)lpContainerDoc, TRUE, TRUE, FALSE);

    LineList_ReplaceLine(lpLL, (LPLINE)lpNewContainerLine, nIndex);

    OLEDBG_END2
	return ResultFromScode(NOERROR);

error:
    OutlineApp_ErrorMessage(g_lpApp, "Could not break the link.");
    OLEDBG_END2
    return ResultFromScode(E_FAIL);
}

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.