Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

2002 lines
57 KiB

/*************************************************************************
**
** 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);
}