|
|
/*************************************************************************
** ** 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 <olethunk.h>
OLEDBGDATA
extern LPOUTLINEAPP g_lpApp; extern IOleUILinkContainerVtbl g_CntrDoc_OleUILinkContainerVtbl;
#if defined( INPLACE_CNTR )
extern BOOL g_fInsideOutContainer; #endif // INPLACE_CNTR
// 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_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; lpContainerApp->m_fMenuHelpMode = FALSE; // F1 pressed in menu
#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++; ContainerLine_LoadOleObject((LPCONTAINERLINE)lpLine); 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; BOOL fAllLinksUpToDate = TRUE; HWND hwndDoc = ((LPOUTLINEDOC)lpContainerDoc)->m_hWndDoc; HCURSOR hCursor; LPCONTAINERLINE lpContainerLine = NULL; HRESULT hrErr; DWORD dwUpdateOpt; LPOLEAPP lpOleApp = (LPOLEAPP)g_lpApp; BOOL fPrevEnable1; BOOL fPrevEnable2;
hCursor = SetCursor(LoadCursor(NULL, IDC_WAIT));
/* OLE2NOTE: we do not want to ever give the Busy/NotResponding
** dialogs when we are updating automatic links as part of ** opening a document. even if the link source of data is busy, ** we do not want put up the busy dialog. thus we will disable ** the dialog and later re-enable them */ OleApp_DisableBusyDialogs(lpOleApp, &fPrevEnable1, &fPrevEnable2);
/* 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++; if (fAllLinksUpToDate) { OLEDBG_BEGIN2("IOleObject::IsUpToDate called\r\n") hrErr = lpContainerLine->m_lpOleObj->lpVtbl->IsUpToDate( lpContainerLine->m_lpOleObj); OLEDBG_END2 if (hrErr != NOERROR) fAllLinksUpToDate = FALSE; } } } #if defined( _DEBUG )
else OleDbgOutHResult("IOleUILinkContainer::GetLinkUpdateOptions returned",hrErr); #endif
}
if (fAllLinksUpToDate) goto done; // don't bother user if all links are up-to-date
SetCursor(hCursor);
if ((cLinks > 0) && !OleUIUpdateLinks( (LPOLEUILINKCONTAINER)&lpContainerDoc->m_OleUILinkContainer, hwndDoc, (LPSTR)APPNAME, cLinks)) { if (ID_PU_LINKS == OleUIPromptUser( (WORD)IDD_CANNOTUPDATELINK, hwndDoc, (LPSTR)APPNAME)) { ContainerDoc_EditLinksCommand(lpContainerDoc); } }
done: // re-enable the Busy/NotResponding dialogs
OleApp_EnableBusyDialogs(lpOleApp, fPrevEnable1, fPrevEnable2); }
/* 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;
_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); _fmemset((LPSTR)szFile, 0, OLEUI_CCHPATHMAX);
#if defined( OLE_VERSION )
OleApp_PreModalDialog((LPOLEAPP)g_lpApp, (LPOLEDOC)lpContainerDoc); #endif
OLEDBG_BEGIN3("OleUIInsertObject called\r\n") uRet=OleUIInsertObject((LPOLEUIINSERTOBJECT)&io); OLEDBG_END3
#if defined( OLE_VERSION )
OleApp_PostModalDialog((LPOLEAPP)g_lpApp, (LPOLEDOC)lpContainerDoc); #endif
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);
if ((io.dwFlags & IOF_SELECTCREATENEW)) dwOleCreateType = IOF_SELECTCREATENEW; else if ((io.dwFlags & IOF_CHECKLINK)) dwOleCreateType = IOF_CHECKLINK; else dwOleCreateType = IOF_SELECTCREATEFROMFILE;
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. */
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). ** OLE2NOTE: the standard OLE 2 User Model is to only call ** IOleObject::DoVerb(OLEIVERB_SHOW...) if a new object is ** created. specifically, it should NOT be calld if the object ** is created from file or link to file. */ if (dwOleCreateType == IOF_SELECTCREATENEW) { if (! ContainerLine_DoVerb( lpContainerLine, OLEIVERB_SHOW, NULL, 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_SaveOleObjectToStg( lpContainerLine, lpContainerLine->m_lpStg, lpContainerLine->m_lpStg, TRUE /* fRemember */ ); } #if defined( INPLACE_CNTR )
else if (dwOleCreateType == IOF_SELECTCREATEFROMFILE) { /* OLE2NOTE: an inside-out container should check if the object
** created from file is an inside-out and prefers to be ** activated when visible type of object. if so, the object ** should be immediately activated in-place, BUT NOT UIActived. */ if (g_fInsideOutContainer && lpContainerLine->m_dwDrawAspect == DVASPECT_CONTENT && lpContainerLine->m_fInsideOutObj ) { HWND hWndDoc = OutlineDoc_GetWindow((LPOUTLINEDOC)lpContainerDoc);
ContainerLine_DoVerb( lpContainerLine,OLEIVERB_INPLACEACTIVATE,NULL,FALSE,FALSE);
/* OLE2NOTE: following this DoVerb(INPLACEACTIVATE) the
** object may have taken focus. but because the ** object is NOT UIActive it should NOT have focus. ** we will make sure our document has focus. */ SetFocus(hWndDoc); } } #endif // INPLACE_CNTR
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; el.hWndOwner=((LPOUTLINEDOC)lpContainerDoc)->m_hWndDoc; el.lpOleUILinkContainer = (LPOLEUILINKCONTAINER)&lpContainerDoc->m_OleUILinkContainer;
#if defined( OLE_VERSION )
OleApp_PreModalDialog((LPOLEAPP)g_lpApp, (LPOLEDOC)lpContainerDoc); #endif
OLEDBG_BEGIN3("OleUIEditLinks called\r\n") uRet=OleUIEditLinks((LPOLEUIEDITLINKS)&el); OLEDBG_END3
#if defined( OLE_VERSION )
OleApp_PostModalDialog((LPOLEAPP)g_lpApp, (LPOLEDOC)lpContainerDoc); #endif
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 fMustRun = FALSE; BOOL fMustClose = 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; ct.cClsidExclude = 0; ct.lpClsidExclude = NULL;
if (! ct.fIsLinkedObject || !lpContainerLine->m_lpOleLink) { /* 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 = ReadFmtUserTypeStgA( lpContainerLine->m_lpStg, (CLIPFORMAT FAR*)&ct.wFormat, &ct.lpszUserType);
if (hrErr == NOERROR) fHaveFmtUserType = TRUE; else { OleDbgOutHResult("ReadFmtUserTypeStg returned", hrErr); } } else { /* OLE2NOTE: the object is a linked object. we should give the
** DisplayName of the link source as the default icon label. */ OLEDBG_BEGIN2("IOleLink::GetSourceDisplayName called\r\n")
hrErr = CallIOleLinkGetSourceDisplayNameA( lpContainerLine->m_lpOleLink, &ct.lpszDefLabel);
OLEDBG_END2 }
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);
#if defined( OLE_VERSION )
OleApp_PreModalDialog((LPOLEAPP)g_lpApp, (LPOLEDOC)lpContainerDoc); #endif
OLEDBG_BEGIN3("OleUIConvert called\r\n") uRet = OleUIConvert(&ct); OLEDBG_END3
#if defined( OLE_VERSION )
OleApp_PostModalDialog((LPOLEAPP)g_lpApp, (LPOLEDOC)lpContainerDoc); #endif
// 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, OLECLOSE_SAVEIFDIRTY);
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, OLECLOSE_SAVEIFDIRTY );
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) {
/* if we force the object to run, then shut it down after
** the update. do NOT force the object to close if we ** want to activate the object or if the object was ** already running. */ if (!fMustActivate && !OleIsRunning(lpContainerLine->m_lpOleObj)) fMustClose = TRUE; // shutdown after update
hrErr = ContainerLine_RunOleObject(lpContainerLine);
if (fObjConverted && FAILED(hrErr) && GetScode(hrErr)!=OLE_E_STATIC) {
// ERROR: convert of the object failed.
// revert the storage to restore the original link.
// (OLE2NOTE: static object always return OLE_E_STATIC
// when told to run; this is NOT an error here.
// the OLE2 libraries have built in handlers for
// the static objects that do the conversion.
ContainerLine_UnloadOleObject( lpContainerLine, OLECLOSE_NOSAVE); lpContainerLine->m_lpStg->lpVtbl->Revert( lpContainerLine->m_lpStg); goto error;
} else if (fObjConverted) { FORMATETC FmtEtc; DWORD dwNewConnection; LPOLECACHE lpOleCache = (LPOLECACHE)OleStdQueryInterface ((LPUNKNOWN)lpContainerLine->m_lpOleObj,&IID_IOleCache);
/* OLE2NOTE: we need to force the converted object to
** setup a new OLERENDER_DRAW cache. it is possible ** that the new object needs to cache different data ** in order to support drawing than the old object. */ if (lpOleCache && lpContainerLine->m_dwDrawAspect == DVASPECT_CONTENT) { FmtEtc.cfFormat = 0; // whatever is needed for Draw
FmtEtc.ptd = NULL; FmtEtc.dwAspect = DVASPECT_CONTENT; FmtEtc.lindex = -1; FmtEtc.tymed = TYMED_NULL;
OLEDBG_BEGIN2("IOleCache::Cache called\r\n") hrErr = lpOleCache->lpVtbl->Cache( lpOleCache, (LPFORMATETC)&FmtEtc, ADVF_PRIMEFIRST, (LPDWORD)&dwNewConnection ); OLEDBG_END2 #if defined( _DEBUG )
if (! SUCCEEDED(hrErr)) OleDbgOutHResult("IOleCache::Cache returned", hrErr); #endif
OleStdRelease((LPUNKNOWN)lpOleCache); }
// Close and force object to save; this will commit the stg
ContainerLine_CloseOleObject( lpContainerLine, OLECLOSE_SAVEIFDIRTY); fMustClose = FALSE; // we already closed the object
} if (fMustClose) ContainerLine_CloseOleObject(lpContainerLine,OLECLOSE_NOSAVE); }
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_PRIMARY, NULL, FALSE,FALSE); } }
if (ct.lpszUserType) OleStdFreeString(ct.lpszUserType, NULL);
if (ct.lpszDefLabel) OleStdFreeString(ct.lpszDefLabel, NULL);
if (ct.hMetaPict) OleUIMetafilePictIconFree(ct.hMetaPict); // clean up metafile
SetCursor(hPrevCursor); // restore original cursor
return;
error: 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, DWORD dwSaveOption ) { 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,dwSaveOption)) 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, DWORD dwSaveOption ) { 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, dwSaveOption); } } } }
/* 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; #if defined( INPLACE_CNTR )
int nFirstUpdate = -1; #endif
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); #if defined( INPLACE_CNTR )
if (nFirstUpdate == -1) nFirstUpdate = i; #endif
} } }
#if defined( INPLACE_CNTR )
/* OLE2NOTE: after changing the extents of any line, we need to
** update the PosRect of the In-Place active ** objects (if any) that follow the first modified line. */ if (nFirstUpdate != -1) ContainerDoc_UpdateInPlaceObjectRects(lpContainerDoc, nFirstUpdate+1); #endif
}
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 defined( OPTIONAL )
FILETIME filetimeBeforeSave; #endif
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 : lpOleDoc->m_lpStg);
#if defined( OPTIONAL )
/* OLE2NOTE: an automatic link to an embedded object within the
** same container document (that uses ItemMonikers) will ** always be considered "out-of-date' by OLE. if a container ** application REALLY wants to fix this it can do one of the ** following: ** 1. implement a new moniker better than ItemMonikers ** that look into the objects storage to find the real last ** change time (rather then defaulting to that of the outer ** container file). ** or 2. using item monikers it is possible to fix the case ** where the container document is saved while the embedded ** object is running but it will NOT fix the case when the ** document is saved when the embedded object was only ** loaded. the fix is to: ** a. remember the time (T) before the save operation starts ** b. call IRunningObjectTable::NoteChangeTime(lpDoc, T) ** c. do the saving and commit the file ** d. call StgSetTimes to reset the file time to T ** e. remember time T in document structure and when the ** root storage is finally released reset the file time ** again to T (closing the file on DOS sets the time). */ CoFileTimeNow( &filetimeBeforeSave ); if (lpOleDoc->m_dwRegROT != 0) { LPRUNNINGOBJECTTABLE lprot;
if (GetRunningObjectTable(0,&lprot) == NOERROR) { OleDbgOut2("IRunningObjectTable::NoteChangeTime called\r\n"); lprot->lpVtbl->NoteChangeTime( lprot, lpOleDoc->m_dwRegROT, &filetimeBeforeSave ); lprot->lpVtbl->Release(lprot); } } #endif
} 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 = StgCreateDocfileA( lpszFileName, STGM_READWRITE|STGM_DIRECT|STGM_SHARE_EXCLUSIVE|STGM_CREATE, 0, &lpDestStg );
OleDbgAssertSz(hrErr == NOERROR, "Could not create Docfile"); if (hrErr != NOERROR) { OleDbgOutHResult("StgCreateDocfile returned", hrErr); 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, FALSE, // fSameAsLoad
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)lpOleDoc->m_lpStg); // free old stg
lpOleDoc->m_lpStg = lpContainerDoc->m_lpNewStg; // save new stg
lpContainerDoc->m_lpNewStg = NULL; } if (! fStatus) goto error;
OutlineDoc_SetModified(lpOutlineDoc, FALSE, FALSE, FALSE);
#if defined( OPTIONAL )
/* reset time of file on disk to be time just prior to saving.
** NOTE: it would also be necessary to remember ** filetimeBeforeSave in the document structure and when the ** root storage is finally released reset the file time ** again to this value (closing the file on DOS sets the time). */ StgSetTimesA(lpOutlineDoc->m_szFileName, NULL, NULL, &filetimeBeforeSave); #endif
}
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, NULL, 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 ((LPOLEDOC)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 = lpContainerLine->m_lpOleLink; SCODE sc = S_OK; HRESULT hrErr;
OLEDBG_BEGIN2("CntrDoc_LinkCont_SetLinkUpdateOptions\r\n")
OleDbgAssert(lpContainerLine);
if (! lpOleLink) { sc = E_FAIL; goto error; }
OLEDBG_BEGIN2("IOleLink::SetUpdateOptions called\r\n") hrErr = lpOleLink->lpVtbl->SetUpdateOptions( lpOleLink, dwUpdateOpt ); OLEDBG_END2
// save new link type update option
lpContainerLine->m_dwLinkType = dwUpdateOpt;
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 = lpContainerLine->m_lpOleLink; SCODE sc = S_OK; HRESULT hrErr;
OLEDBG_BEGIN2("CntrDoc_LinkCont_GetLinkUpdateOptions\r\n")
OleDbgAssert(lpContainerLine);
if (! lpOleLink) { sc = E_FAIL; goto error; }
OLEDBG_BEGIN2("IOleLink::GetUpdateOptions called\r\n") hrErr = lpOleLink->lpVtbl->GetUpdateOptions( lpOleLink, lpdwUpdateOpt ); OLEDBG_END2
// reset saved link type to ensure it is correct
lpContainerLine->m_dwLinkType = *lpdwUpdateOpt;
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 = lpContainerLine->m_lpOleLink; LPBC lpbc = NULL; LPMONIKER lpmk = NULL; LPOLEOBJECT lpLinkSrcOleObj = NULL; CLSID clsid = CLSID_NULL; CLSID clsidOld = 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; }
// Get class of orignial link source if it is available
if (lpContainerLine->m_lpOleObj) {
OLEDBG_BEGIN2("IOleObject::GetUserClassID called\r\n") hrErr = lpContainerLine->m_lpOleObj->lpVtbl->GetUserClassID( lpContainerLine->m_lpOleObj, (CLSID FAR*)&clsidOld); OLEDBG_END2 if (hrErr != NOERROR) clsidOld = CLSID_NULL; }
hrErr = OleStdMkParseDisplayName( &clsidOld,lpbc,lpszDisplayName,lpchEaten,(LPMONIKER FAR*)&lpmk);
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) { OLEDBG_BEGIN2("IOleObject::GetUserClassID called\r\n") hrErr = lpLinkSrcOleObj->lpVtbl->GetUserClassID( lpLinkSrcOleObj, (CLSID FAR*)&clsid); OLEDBG_END2 lpContainerLine->m_fLinkUnavailable = FALSE;
/* get the short user type name of the link because it may
** have changed. we cache this name and must update our ** cache. this name is used all the time when we have to ** build the object verb menu. we cache this information ** to make it quicker to build the verb menu. */ if (lpContainerLine->m_lpszShortType) { OleStdFree(lpContainerLine->m_lpszShortType); lpContainerLine->m_lpszShortType = NULL; } OLEDBG_BEGIN2("IOleObject::GetUserType called\r\n")
CallIOleObjectGetUserTypeA( lpContainerLine->m_lpOleObj, USERCLASSTYPE_SHORT, (LPSTR FAR*)&lpContainerLine->m_lpszShortType );
OLEDBG_END2 } else lpContainerLine->m_fLinkUnavailable = TRUE; } else { LPMONIKER lpmkFile = NULL; LPMONIKER lpmkItem = NULL; char szDelim[2]; LPSTR lpszName;
szDelim[0] = lpszDisplayName[(int)lenFileName]; szDelim[1] = '\0'; lpszDisplayName[(int)lenFileName] = '\0';
OLEDBG_BEGIN2("CreateFileMoniker called\r\n")
CreateFileMonikerA(lpszDisplayName, (LPMONIKER FAR*)&lpmkFile);
OLEDBG_END2
lpszDisplayName[(int)lenFileName] = szDelim[0];
if (!lpmkFile) goto cleanup;
if (lstrlen(lpszDisplayName) > (int)lenFileName) { // have item name
lpszName = lpszDisplayName + lenFileName + 1;
OLEDBG_BEGIN2("CreateItemMoniker called\r\n")
CreateItemMonikerA( szDelim, lpszName, (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; }
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 = CallIOleLinkSetSourceDisplayNameA( lpOleLink, lpszDisplayName);
OLEDBG_END2
if (hrErr != NOERROR) { OleDbgOutHResult("IOleLink::SetSourceDisplayName returned",hrErr); sc = GetScode(hrErr); goto cleanup; } }
cleanup: 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 = lpContainerLine->m_lpOleLink; 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);
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) { CallIOleObjectGetUserTypeA( lpOleObj, USERCLASSTYPE_FULL, lplpszFullLinkType );
CallIOleObjectGetUserTypeA( lpOleObj, USERCLASSTYPE_SHORT, lplpszShortLinkType );
OleStdRelease((LPUNKNOWN)lpOleObj); } *lplenFileName = OleStdGetLenFilePrefixOfMoniker(lpmk); lpmk->lpVtbl->Release(lpmk); }
OLEDBG_BEGIN2("IOleLink::GetSourceDisplayName called\r\n")
hrErr = CallIOleLinkGetSourceDisplayNameA( lpOleLink, lplpszDisplayName );
OLEDBG_END2
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, NULL, TRUE, FALSE)) { 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; /* Default to update of the link */ HRESULT hrErr = S_FALSE;
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 }
/* OLE2NOTE: If IOleObject::Update on the Link object returned
** OLE_E_CLASSDIFF because the link source is no longer ** the expected class, then the link should be re-created with ** the new link source. thus the link will be updated with the ** new link source. */ if (GetScode(hrErr) == OLE_E_CLASSDIFF) hrErr = ContainerLine_ReCreateLinkBecauseClassDiff(lpContainerLine);
lpContainerLine->m_fLinkUnavailable = (hrErr != NOERROR);
if (hrErr != NOERROR) { OleDbgOutHResult("IOleObject::Update returned", hrErr); sc = GetScode(hrErr); if (fErrorMessage) { ContainerLine_ProcessOleRunError( lpContainerLine,hrErr,fErrorAction,FALSE/*fMenuInvoked*/); } } /* 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 = lpContainerLine->m_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. */ if (lpOleLink) { lpContainerLine->m_dwLinkType = 0; OLEDBG_BEGIN2("IOleLink::SetSourceMoniker called\r\n") lpOleLink->lpVtbl->SetSourceMoniker( lpOleLink, NULL, (REFCLSID)&CLSID_NULL); OLEDBG_END2 }
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 */ (lpContainerLine->m_dwDrawAspect == DVASPECT_ICON), 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); }
|