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

  1. /*************************************************************************
  2. **
  3. ** OLE 2 Container Sample Code
  4. **
  5. ** cntrbase.c
  6. **
  7. ** This file contains all interfaces, methods and related support
  8. ** functions for the basic OLE Container application. The
  9. ** basic OLE Container application supports being a container for
  10. ** embedded and linked objects.
  11. ** The basic Container application includes the following
  12. ** implementation objects:
  13. **
  14. ** ContainerDoc Object
  15. ** no required interfaces for basic functionality
  16. ** (see linking.c for linking related support)
  17. ** (see clipbrd.c for clipboard related support)
  18. ** (see dragdrop.c for drag/drop related support)
  19. **
  20. ** ContainerLine Object
  21. ** (see cntrline.c for all ContainerLine functions and interfaces)
  22. ** exposed interfaces:
  23. ** IOleClientSite
  24. ** IAdviseSink
  25. **
  26. ** (c) Copyright Microsoft Corp. 1992 - 1993 All Rights Reserved
  27. **
  28. *************************************************************************/
  29. #include "outline.h"
  30. #include <olethunk.h>
  31. OLEDBGDATA
  32. extern LPOUTLINEAPP g_lpApp;
  33. extern IOleUILinkContainerVtbl g_CntrDoc_OleUILinkContainerVtbl;
  34. #if defined( INPLACE_CNTR )
  35. extern BOOL g_fInsideOutContainer;
  36. #endif // INPLACE_CNTR
  37. // REVIEW: should use string resource for messages
  38. char ErrMsgShowObj[] = "Could not show object server!";
  39. char ErrMsgInsertObj[] = "Insert Object failed!";
  40. char ErrMsgConvertObj[] = "Convert Object failed!";
  41. char ErrMsgCantConvert[] = "Unable to convert the selection!";
  42. char ErrMsgActivateAsObj[] = "Activate As Object failed!";
  43. extern char ErrMsgSaving[];
  44. extern char ErrMsgOpening[];
  45. /* ContainerDoc_Init
  46. * -----------------
  47. *
  48. * Initialize the fields of a new ContainerDoc object. The doc is initially
  49. * not associated with a file or an (Untitled) document. This function sets
  50. * the docInitType to DOCTYPE_UNKNOWN. After calling this function the
  51. * caller should call:
  52. * 1.) Doc_InitNewFile to set the ContainerDoc to (Untitled)
  53. * 2.) Doc_LoadFromFile to associate the ContainerDoc with a file.
  54. * This function creates a new window for the document.
  55. *
  56. * NOTE: the window is initially created with a NIL size. it must be
  57. * sized and positioned by the caller. also the document is initially
  58. * created invisible. the caller must call Doc_ShowWindow
  59. * after sizing it to make the document window visible.
  60. */
  61. BOOL ContainerDoc_Init(LPCONTAINERDOC lpContainerDoc, BOOL fDataTransferDoc)
  62. {
  63. LPCONTAINERAPP lpContainerApp = (LPCONTAINERAPP)g_lpApp;
  64. LPOUTLINEDOC lpOutlineDoc = (LPOUTLINEDOC)lpContainerDoc;
  65. lpOutlineDoc->m_cfSaveFormat = lpContainerApp->m_cfCntrOutl;
  66. lpContainerDoc->m_nNextObjNo = 0L;
  67. lpContainerDoc->m_lpNewStg = NULL;
  68. lpContainerDoc->m_fEmbeddedObjectAvail = FALSE;
  69. lpContainerDoc->m_clsidOleObjCopied = CLSID_NULL;
  70. lpContainerDoc->m_dwAspectOleObjCopied = DVASPECT_CONTENT;
  71. lpContainerDoc->m_lpSrcContainerLine = NULL;
  72. lpContainerDoc->m_fShowObject = TRUE;
  73. #if defined( INPLACE_CNTR )
  74. lpContainerDoc->m_lpLastIpActiveLine = NULL;
  75. lpContainerDoc->m_lpLastUIActiveLine = NULL;
  76. lpContainerDoc->m_hWndUIActiveObj = NULL;
  77. lpContainerDoc->m_fAddMyUI = TRUE; // UI needs to be added
  78. lpContainerDoc->m_cIPActiveObjects = 0;
  79. lpContainerApp->m_fMenuHelpMode = FALSE; // F1 pressed in menu
  80. #if defined( INPLACE_CNTRSVR )
  81. lpContainerDoc->m_lpTopIPFrame =
  82. (LPOLEINPLACEUIWINDOW)&lpContainerDoc->m_OleInPlaceFrame;
  83. lpContainerDoc->m_lpTopIPDoc =
  84. (LPOLEINPLACEUIWINDOW)&lpContainerDoc->m_OleInPlaceDoc;
  85. lpContainerDoc->m_hSharedMenu = NULL;
  86. lpContainerDoc->m_hOleMenu = NULL;
  87. #endif // INPLACE_CNTRSVR
  88. #endif // INPLACE_CNTR
  89. INIT_INTERFACEIMPL(
  90. &lpContainerDoc->m_OleUILinkContainer,
  91. &g_CntrDoc_OleUILinkContainerVtbl,
  92. lpContainerDoc
  93. );
  94. return TRUE;
  95. }
  96. /* ContainerDoc_GetNextLink
  97. * ------------------------
  98. *
  99. * Update all links in the document. A dialog box will be popped up showing
  100. * the progress of the update and allow the user to quit by pushing the
  101. * stop button
  102. */
  103. LPCONTAINERLINE ContainerDoc_GetNextLink(
  104. LPCONTAINERDOC lpContainerDoc,
  105. LPCONTAINERLINE lpContainerLine
  106. )
  107. {
  108. LPLINELIST lpLL = &((LPOUTLINEDOC)lpContainerDoc)->m_LineList;
  109. DWORD dwNextLink = 0;
  110. LPLINE lpLine;
  111. static int nIndex = 0;
  112. if (lpContainerLine==NULL)
  113. nIndex = 0;
  114. for ( ; nIndex < lpLL->m_nNumLines; nIndex++) {
  115. lpLine = LineList_GetLine(lpLL, nIndex);
  116. if (lpLine
  117. && (Line_GetLineType(lpLine) == CONTAINERLINETYPE)
  118. && ContainerLine_IsOleLink((LPCONTAINERLINE)lpLine)) {
  119. nIndex++;
  120. ContainerLine_LoadOleObject((LPCONTAINERLINE)lpLine);
  121. return (LPCONTAINERLINE)lpLine;
  122. }
  123. }
  124. return NULL;
  125. }
  126. /* ContainerDoc_UpdateLinks
  127. * ------------------------
  128. *
  129. * Update all links in the document. A dialog box will be popped up showing
  130. * the progress of the update and allow the user to quit by pushing the
  131. * stop button
  132. */
  133. void ContainerDoc_UpdateLinks(LPCONTAINERDOC lpContainerDoc)
  134. {
  135. int cLinks;
  136. BOOL fAllLinksUpToDate = TRUE;
  137. HWND hwndDoc = ((LPOUTLINEDOC)lpContainerDoc)->m_hWndDoc;
  138. HCURSOR hCursor;
  139. LPCONTAINERLINE lpContainerLine = NULL;
  140. HRESULT hrErr;
  141. DWORD dwUpdateOpt;
  142. LPOLEAPP lpOleApp = (LPOLEAPP)g_lpApp;
  143. BOOL fPrevEnable1;
  144. BOOL fPrevEnable2;
  145. hCursor = SetCursor(LoadCursor(NULL, IDC_WAIT));
  146. /* OLE2NOTE: we do not want to ever give the Busy/NotResponding
  147. ** dialogs when we are updating automatic links as part of
  148. ** opening a document. even if the link source of data is busy,
  149. ** we do not want put up the busy dialog. thus we will disable
  150. ** the dialog and later re-enable them
  151. */
  152. OleApp_DisableBusyDialogs(lpOleApp, &fPrevEnable1, &fPrevEnable2);
  153. /* get total number of automatic links */
  154. cLinks = 0;
  155. while (lpContainerLine = ContainerDoc_GetNextLink(
  156. lpContainerDoc,
  157. lpContainerLine)) {
  158. hrErr = CntrDoc_LinkCont_GetLinkUpdateOptions(
  159. (LPOLEUILINKCONTAINER)&lpContainerDoc->m_OleUILinkContainer,
  160. (DWORD)lpContainerLine,
  161. (LPDWORD)&dwUpdateOpt
  162. );
  163. if (hrErr == NOERROR) {
  164. if (dwUpdateOpt==OLEUPDATE_ALWAYS) {
  165. cLinks++;
  166. if (fAllLinksUpToDate) {
  167. OLEDBG_BEGIN2("IOleObject::IsUpToDate called\r\n")
  168. hrErr = lpContainerLine->m_lpOleObj->lpVtbl->IsUpToDate(
  169. lpContainerLine->m_lpOleObj);
  170. OLEDBG_END2
  171. if (hrErr != NOERROR)
  172. fAllLinksUpToDate = FALSE;
  173. }
  174. }
  175. }
  176. #if defined( _DEBUG )
  177. else
  178. OleDbgOutHResult("IOleUILinkContainer::GetLinkUpdateOptions returned",hrErr);
  179. #endif
  180. }
  181. if (fAllLinksUpToDate)
  182. goto done; // don't bother user if all links are up-to-date
  183. SetCursor(hCursor);
  184. if ((cLinks > 0) && !OleUIUpdateLinks(
  185. (LPOLEUILINKCONTAINER)&lpContainerDoc->m_OleUILinkContainer,
  186. hwndDoc,
  187. (LPSTR)APPNAME,
  188. cLinks)) {
  189. if (ID_PU_LINKS == OleUIPromptUser(
  190. (WORD)IDD_CANNOTUPDATELINK,
  191. hwndDoc,
  192. (LPSTR)APPNAME)) {
  193. ContainerDoc_EditLinksCommand(lpContainerDoc);
  194. }
  195. }
  196. done:
  197. // re-enable the Busy/NotResponding dialogs
  198. OleApp_EnableBusyDialogs(lpOleApp, fPrevEnable1, fPrevEnable2);
  199. }
  200. /* ContainerDoc_SetShowObjectFlag
  201. * ------------------------------
  202. *
  203. * Set/Clear the ShowObject flag of ContainerDoc
  204. */
  205. void ContainerDoc_SetShowObjectFlag(LPCONTAINERDOC lpContainerDoc, BOOL fShow)
  206. {
  207. if (!lpContainerDoc)
  208. return;
  209. lpContainerDoc->m_fShowObject = fShow;
  210. }
  211. /* ContainerDoc_GetShowObjectFlag
  212. * ------------------------------
  213. *
  214. * Get the ShowObject flag of ContainerDoc
  215. */
  216. BOOL ContainerDoc_GetShowObjectFlag(LPCONTAINERDOC lpContainerDoc)
  217. {
  218. if (!lpContainerDoc)
  219. return FALSE;
  220. return lpContainerDoc->m_fShowObject;
  221. }
  222. /* ContainerDoc_InsertOleObjectCommand
  223. * -----------------------------------
  224. *
  225. * Insert a new OLE object in the ContainerDoc.
  226. */
  227. void ContainerDoc_InsertOleObjectCommand(LPCONTAINERDOC lpContainerDoc)
  228. {
  229. LPLINELIST lpLL =&((LPOUTLINEDOC)lpContainerDoc)->m_LineList;
  230. LPLINE lpLine = NULL;
  231. HDC hDC;
  232. int nTab = 0;
  233. int nIndex = LineList_GetFocusLineIndex(lpLL);
  234. LPCONTAINERLINE lpContainerLine=NULL;
  235. char szStgName[CWCSTORAGENAME];
  236. UINT uRet;
  237. OLEUIINSERTOBJECT io;
  238. char szFile[OLEUI_CCHPATHMAX];
  239. DWORD dwOleCreateType;
  240. BOOL fDisplayAsIcon;
  241. HCURSOR hPrevCursor;
  242. _fmemset((LPOLEUIINSERTOBJECT)&io, 0, sizeof(io));
  243. io.cbStruct=sizeof(io);
  244. io.dwFlags=IOF_SELECTCREATENEW | IOF_SHOWHELP;
  245. io.hWndOwner=((LPOUTLINEDOC)lpContainerDoc)->m_hWndDoc;
  246. io.lpszFile=(LPSTR)szFile;
  247. io.cchFile=sizeof(szFile);
  248. _fmemset((LPSTR)szFile, 0, OLEUI_CCHPATHMAX);
  249. #if defined( OLE_VERSION )
  250. OleApp_PreModalDialog((LPOLEAPP)g_lpApp, (LPOLEDOC)lpContainerDoc);
  251. #endif
  252. OLEDBG_BEGIN3("OleUIInsertObject called\r\n")
  253. uRet=OleUIInsertObject((LPOLEUIINSERTOBJECT)&io);
  254. OLEDBG_END3
  255. #if defined( OLE_VERSION )
  256. OleApp_PostModalDialog((LPOLEAPP)g_lpApp, (LPOLEDOC)lpContainerDoc);
  257. #endif
  258. if (OLEUI_OK != uRet)
  259. return; // user canceled dialog
  260. // this may take a while, put up hourglass cursor
  261. hPrevCursor = SetCursor(LoadCursor(NULL, IDC_WAIT));
  262. fDisplayAsIcon = (io.dwFlags & IOF_CHECKDISPLAYASICON ? TRUE : FALSE);
  263. // make up a storage name for the OLE object
  264. ContainerDoc_GetNextStgName(lpContainerDoc, szStgName, sizeof(szStgName));
  265. /* default the new line to have the same indent as previous line */
  266. lpLine = LineList_GetLine(lpLL, nIndex);
  267. if (lpLine)
  268. nTab = Line_GetTabLevel(lpLine);
  269. hDC = LineList_GetDC(lpLL);
  270. if ((io.dwFlags & IOF_SELECTCREATENEW))
  271. dwOleCreateType = IOF_SELECTCREATENEW;
  272. else if ((io.dwFlags & IOF_CHECKLINK))
  273. dwOleCreateType = IOF_CHECKLINK;
  274. else
  275. dwOleCreateType = IOF_SELECTCREATEFROMFILE;
  276. lpContainerLine = ContainerLine_Create(
  277. dwOleCreateType,
  278. hDC,
  279. nTab,
  280. lpContainerDoc,
  281. &io.clsid,
  282. (LPSTR)szFile,
  283. fDisplayAsIcon,
  284. io.hMetaPict,
  285. szStgName
  286. );
  287. if (!lpContainerLine)
  288. goto error; // creation of OLE object FAILED
  289. if (io.hMetaPict) {
  290. OleUIMetafilePictIconFree(io.hMetaPict); // clean up metafile
  291. }
  292. /* add a ContainerLine object to the document's LineList. The
  293. ** ContainerLine manages the rectangle on the screen occupied by
  294. ** the OLE object.
  295. */
  296. LineList_AddLine(lpLL, (LPLINE)lpContainerLine, nIndex);
  297. /* before calling DoVerb(OLEIVERB_SHOW), check to see if the object
  298. ** has any initial extents.
  299. */
  300. ContainerLine_UpdateExtent(lpContainerLine, NULL);
  301. /* If a new embedded object was created, tell the object server to
  302. ** make itself visible (show itself).
  303. ** OLE2NOTE: the standard OLE 2 User Model is to only call
  304. ** IOleObject::DoVerb(OLEIVERB_SHOW...) if a new object is
  305. ** created. specifically, it should NOT be calld if the object
  306. ** is created from file or link to file.
  307. */
  308. if (dwOleCreateType == IOF_SELECTCREATENEW) {
  309. if (! ContainerLine_DoVerb(
  310. lpContainerLine, OLEIVERB_SHOW, NULL, TRUE, TRUE)) {
  311. OutlineApp_ErrorMessage(g_lpApp, ErrMsgShowObj);
  312. }
  313. /* OLE2NOTE: we will immediately force a save of the object
  314. ** to guarantee that a valid initial object is saved
  315. ** with our document. if the object is a OLE 1.0 object,
  316. ** then it may exit without update. by forcing this
  317. ** initial save we consistently always have a valid
  318. ** object even if it is a OLE 1.0 object that exited
  319. ** without saving. if we did NOT do this save here, then
  320. ** we would have to worry about deleting the object if
  321. ** it was a OLE 1.0 object that closed without saving.
  322. ** the OLE 2.0 User Model dictates that the object
  323. ** should always be valid after CreateNew performed. the
  324. ** user must explicitly delete it.
  325. */
  326. ContainerLine_SaveOleObjectToStg(
  327. lpContainerLine,
  328. lpContainerLine->m_lpStg,
  329. lpContainerLine->m_lpStg,
  330. TRUE /* fRemember */
  331. );
  332. }
  333. #if defined( INPLACE_CNTR )
  334. else if (dwOleCreateType == IOF_SELECTCREATEFROMFILE) {
  335. /* OLE2NOTE: an inside-out container should check if the object
  336. ** created from file is an inside-out and prefers to be
  337. ** activated when visible type of object. if so, the object
  338. ** should be immediately activated in-place, BUT NOT UIActived.
  339. */
  340. if (g_fInsideOutContainer &&
  341. lpContainerLine->m_dwDrawAspect == DVASPECT_CONTENT &&
  342. lpContainerLine->m_fInsideOutObj ) {
  343. HWND hWndDoc = OutlineDoc_GetWindow((LPOUTLINEDOC)lpContainerDoc);
  344. ContainerLine_DoVerb(
  345. lpContainerLine,OLEIVERB_INPLACEACTIVATE,NULL,FALSE,FALSE);
  346. /* OLE2NOTE: following this DoVerb(INPLACEACTIVATE) the
  347. ** object may have taken focus. but because the
  348. ** object is NOT UIActive it should NOT have focus.
  349. ** we will make sure our document has focus.
  350. */
  351. SetFocus(hWndDoc);
  352. }
  353. }
  354. #endif // INPLACE_CNTR
  355. OutlineDoc_SetModified((LPOUTLINEDOC)lpContainerDoc, TRUE, TRUE, TRUE);
  356. LineList_ReleaseDC(lpLL, hDC);
  357. SetCursor(hPrevCursor); // restore original cursor
  358. return;
  359. error:
  360. // NOTE: if ContainerLine_Create failed
  361. LineList_ReleaseDC(lpLL, hDC);
  362. if (OLEUI_OK == uRet && io.hMetaPict)
  363. OleUIMetafilePictIconFree(io.hMetaPict); // clean up metafile
  364. SetCursor(hPrevCursor); // restore original cursor
  365. OutlineApp_ErrorMessage(g_lpApp, ErrMsgInsertObj);
  366. }
  367. void ContainerDoc_EditLinksCommand(LPCONTAINERDOC lpContainerDoc)
  368. {
  369. UINT uRet;
  370. OLEUIEDITLINKS el;
  371. LPCONTAINERLINE lpContainerLine = NULL;
  372. LPLINELIST lpLL = &((LPOUTLINEDOC)lpContainerDoc)->m_LineList;
  373. _fmemset((LPOLEUIEDITLINKS)&el,0,sizeof(el));
  374. el.cbStruct=sizeof(el);
  375. el.dwFlags=ELF_SHOWHELP;
  376. el.hWndOwner=((LPOUTLINEDOC)lpContainerDoc)->m_hWndDoc;
  377. el.lpOleUILinkContainer =
  378. (LPOLEUILINKCONTAINER)&lpContainerDoc->m_OleUILinkContainer;
  379. #if defined( OLE_VERSION )
  380. OleApp_PreModalDialog((LPOLEAPP)g_lpApp, (LPOLEDOC)lpContainerDoc);
  381. #endif
  382. OLEDBG_BEGIN3("OleUIEditLinks called\r\n")
  383. uRet=OleUIEditLinks((LPOLEUIEDITLINKS)&el);
  384. OLEDBG_END3
  385. #if defined( OLE_VERSION )
  386. OleApp_PostModalDialog((LPOLEAPP)g_lpApp, (LPOLEDOC)lpContainerDoc);
  387. #endif
  388. OleDbgAssert((uRet==1) || (uRet==OLEUI_CANCEL));
  389. }
  390. /* Convert command - brings up the "Convert" dialog
  391. */
  392. void ContainerDoc_ConvertCommand(
  393. LPCONTAINERDOC lpContainerDoc,
  394. BOOL fServerNotRegistered
  395. )
  396. {
  397. LPOUTLINEDOC lpOutlineDoc = (LPOUTLINEDOC)lpContainerDoc;
  398. OLEUICONVERT ct;
  399. UINT uRet;
  400. LPDATAOBJECT lpDataObj;
  401. LPLINELIST lpLL = &((LPOUTLINEDOC)lpContainerDoc)->m_LineList;
  402. LPCONTAINERLINE lpContainerLine = NULL;
  403. BOOL fSelIsOleObject;
  404. int nIndex;
  405. STGMEDIUM medium;
  406. LPSTR lpErrMsg = NULL;
  407. HRESULT hrErr;
  408. HCURSOR hPrevCursor;
  409. BOOL fMustRun = FALSE;
  410. BOOL fMustClose = FALSE;
  411. BOOL fObjConverted = FALSE;
  412. BOOL fDisplayChanged = FALSE;
  413. BOOL fHaveCLSID = FALSE;
  414. BOOL fHaveFmtUserType = FALSE;
  415. char szUserType[128];
  416. BOOL fMustActivate;
  417. /* OLE2NOTE: if we came to the Convert dialog because the user
  418. ** activated a non-registered object, then we should activate
  419. ** the object after the user has converted it or setup an
  420. ** ActivateAs server.
  421. */
  422. fMustActivate = fServerNotRegistered;
  423. _fmemset((LPOLEUICONVERT)&ct,0,sizeof(ct));
  424. fSelIsOleObject = ContainerDoc_IsSelAnOleObject(
  425. (LPCONTAINERDOC)lpContainerDoc,
  426. &IID_IDataObject,
  427. (LPUNKNOWN FAR*)&lpDataObj,
  428. &nIndex,
  429. (LPCONTAINERLINE FAR*)&lpContainerLine
  430. );
  431. lpErrMsg = ErrMsgCantConvert;
  432. if (! fSelIsOleObject)
  433. goto error; // can NOT do Convert.
  434. if (! lpContainerLine) {
  435. OleStdRelease((LPUNKNOWN)lpDataObj);
  436. goto error; // can NOT do Convert.
  437. }
  438. ct.cbStruct = sizeof(OLEUICONVERT);
  439. ct.dwFlags = CF_SHOWHELPBUTTON;
  440. ct.hWndOwner = lpContainerDoc->m_OleDoc.m_OutlineDoc.m_hWndDoc;
  441. ct.lpszCaption = (LPSTR)NULL;
  442. ct.lpfnHook = NULL;
  443. ct.lCustData = 0;
  444. ct.hInstance = NULL;
  445. ct.lpszTemplate = NULL;
  446. ct.hResource = 0;
  447. ct.fIsLinkedObject = ContainerLine_IsOleLink(lpContainerLine);
  448. ct.dvAspect = lpContainerLine->m_dwDrawAspect;
  449. ct.cClsidExclude = 0;
  450. ct.lpClsidExclude = NULL;
  451. if (! ct.fIsLinkedObject || !lpContainerLine->m_lpOleLink) {
  452. /* OLE2NOTE: the object is an embedded object. we should first
  453. ** attempt to read the actual object CLSID, file data
  454. ** format, and full user type name that is written inside of
  455. ** the object's storage as this should be the most
  456. ** definitive information. if this fails we will ask the
  457. ** object what its class is and attempt to get the rest of
  458. ** the information out of the REGDB.
  459. */
  460. hrErr=ReadClassStg(lpContainerLine->m_lpStg,(CLSID FAR*)&(ct.clsid));
  461. if (hrErr == NOERROR)
  462. fHaveCLSID = TRUE;
  463. else {
  464. OleDbgOutHResult("ReadClassStg returned", hrErr);
  465. }
  466. hrErr = ReadFmtUserTypeStgA(
  467. lpContainerLine->m_lpStg,
  468. (CLIPFORMAT FAR*)&ct.wFormat,
  469. &ct.lpszUserType);
  470. if (hrErr == NOERROR)
  471. fHaveFmtUserType = TRUE;
  472. else {
  473. OleDbgOutHResult("ReadFmtUserTypeStg returned", hrErr);
  474. }
  475. } else {
  476. /* OLE2NOTE: the object is a linked object. we should give the
  477. ** DisplayName of the link source as the default icon label.
  478. */
  479. OLEDBG_BEGIN2("IOleLink::GetSourceDisplayName called\r\n")
  480. hrErr = CallIOleLinkGetSourceDisplayNameA(
  481. lpContainerLine->m_lpOleLink, &ct.lpszDefLabel);
  482. OLEDBG_END2
  483. }
  484. if (! fHaveCLSID) {
  485. hrErr = lpContainerLine->m_lpOleObj->lpVtbl->GetUserClassID(
  486. lpContainerLine->m_lpOleObj,
  487. (CLSID FAR*)&ct.clsid
  488. );
  489. if (hrErr != NOERROR)
  490. ct.clsid = CLSID_NULL;
  491. }
  492. if (! fHaveFmtUserType) {
  493. ct.wFormat = 0;
  494. if (OleStdGetUserTypeOfClass(
  495. (CLSID FAR*)&ct.clsid,szUserType,sizeof(szUserType),NULL)) {
  496. ct.lpszUserType = OleStdCopyString(szUserType, NULL);
  497. } else {
  498. ct.lpszUserType = NULL;
  499. }
  500. }
  501. if (lpContainerLine->m_dwDrawAspect == DVASPECT_ICON) {
  502. ct.hMetaPict = OleStdGetData(
  503. lpDataObj,
  504. CF_METAFILEPICT,
  505. NULL,
  506. DVASPECT_ICON,
  507. (LPSTGMEDIUM)&medium
  508. );
  509. } else {
  510. ct.hMetaPict = NULL;
  511. }
  512. OleStdRelease((LPUNKNOWN)lpDataObj);
  513. #if defined( OLE_VERSION )
  514. OleApp_PreModalDialog((LPOLEAPP)g_lpApp, (LPOLEDOC)lpContainerDoc);
  515. #endif
  516. OLEDBG_BEGIN3("OleUIConvert called\r\n")
  517. uRet = OleUIConvert(&ct);
  518. OLEDBG_END3
  519. #if defined( OLE_VERSION )
  520. OleApp_PostModalDialog((LPOLEAPP)g_lpApp, (LPOLEDOC)lpContainerDoc);
  521. #endif
  522. // this may take a while, put up hourglass cursor
  523. hPrevCursor = SetCursor(LoadCursor(NULL, IDC_WAIT));
  524. if (uRet == OLEUI_OK) {
  525. /*****************************************************************
  526. ** OLE2NOTE: the convert dialog actually allows the user to
  527. ** change two orthogonal properties of the object: the
  528. ** object's type/server and the object's display aspect.
  529. ** first we will execute the ConvertTo/ActivateAs action and
  530. ** then we will deal with any display aspect change. we want
  531. ** to be careful to only call IOleObject::Update once
  532. ** because this is an expensive operation; it results in
  533. ** launching the object's server.
  534. *****************************************************************/
  535. if (ct.dwFlags & CF_SELECTCONVERTTO &&
  536. ! IsEqualCLSID(&ct.clsid, &ct.clsidNew)) {
  537. /* user selected CONVERT.
  538. **
  539. ** OLE2NOTE: to achieve the "Convert To" at this point we
  540. ** need to take the following steps:
  541. ** 1. unload the object.
  542. ** 2. write the NEW CLSID and NEW user type name
  543. ** string into the storage of the object,
  544. ** BUT write the OLD format tag.
  545. ** 3. force an update to force the actual conversion of
  546. ** the data bits.
  547. */
  548. lpErrMsg = ErrMsgConvertObj; // setup correct msg in case of error
  549. ContainerLine_UnloadOleObject(lpContainerLine, OLECLOSE_SAVEIFDIRTY);
  550. OLEDBG_BEGIN2("OleStdDoConvert called \r\n")
  551. hrErr = OleStdDoConvert(
  552. lpContainerLine->m_lpStg, (REFCLSID)&ct.clsidNew);
  553. OLEDBG_END2
  554. if (hrErr != NOERROR)
  555. goto error;
  556. // Reload the object
  557. ContainerLine_LoadOleObject(lpContainerLine);
  558. /* we need to force the object to run to complete the
  559. ** conversion. set flag to force OleRun to be called at
  560. ** end of function.
  561. */
  562. fMustRun = TRUE;
  563. fObjConverted = TRUE;
  564. } else if (ct.dwFlags & CF_SELECTACTIVATEAS) {
  565. /* user selected ACTIVATE AS.
  566. **
  567. ** OLE2NOTE: to achieve the "Activate As" at this point we
  568. ** need to take the following steps:
  569. ** 1. unload ALL objects of the OLD class that app knows about
  570. ** 2. add the TreatAs tag in the registration database
  571. ** by calling CoTreatAsClass().
  572. ** 3. lazily it can reload the objects; when the objects
  573. ** are reloaded the TreatAs will take effect.
  574. */
  575. lpErrMsg = ErrMsgActivateAsObj; // setup msg in case of error
  576. ContainerDoc_UnloadAllOleObjectsOfClass(
  577. lpContainerDoc,
  578. (REFCLSID)&ct.clsid,
  579. OLECLOSE_SAVEIFDIRTY
  580. );
  581. OLEDBG_BEGIN2("OleStdDoTreatAsClass called \r\n")
  582. hrErr = OleStdDoTreatAsClass(ct.lpszUserType, (REFCLSID)&ct.clsid,
  583. (REFCLSID)&ct.clsidNew);
  584. OLEDBG_END2
  585. // Reload the object
  586. ContainerLine_LoadOleObject(lpContainerLine);
  587. fMustActivate = TRUE; // we should activate this object
  588. }
  589. /*****************************************************************
  590. ** OLE2NOTE: now we will try to change the display if
  591. ** necessary.
  592. *****************************************************************/
  593. if (lpContainerLine->m_lpOleObj &&
  594. ct.dvAspect != lpContainerLine->m_dwDrawAspect) {
  595. /* user has selected to change display aspect between icon
  596. ** aspect and content aspect.
  597. **
  598. ** OLE2NOTE: if we got here because the server was not
  599. ** registered, then we will NOT delete the object's
  600. ** original display aspect. because we do not have the
  601. ** original server, we can NEVER get it back. this is a
  602. ** safety precaution.
  603. */
  604. hrErr = OleStdSwitchDisplayAspect(
  605. lpContainerLine->m_lpOleObj,
  606. &lpContainerLine->m_dwDrawAspect,
  607. ct.dvAspect,
  608. ct.hMetaPict,
  609. !fServerNotRegistered, /* fDeleteOldAspect */
  610. TRUE, /* fSetupViewAdvise */
  611. (LPADVISESINK)&lpContainerLine->m_AdviseSink,
  612. (BOOL FAR*)&fMustRun
  613. );
  614. if (hrErr == NOERROR)
  615. fDisplayChanged = TRUE;
  616. #if defined( INPLACE_CNTR )
  617. ContainerDoc_UpdateInPlaceObjectRects(
  618. lpContainerLine->m_lpDoc, nIndex);
  619. #endif
  620. } else if (ct.dvAspect == DVASPECT_ICON && ct.fObjectsIconChanged) {
  621. hrErr = OleStdSetIconInCache(
  622. lpContainerLine->m_lpOleObj,
  623. ct.hMetaPict
  624. );
  625. if (hrErr == NOERROR)
  626. fDisplayChanged = TRUE;
  627. }
  628. /* we deliberately run the object so that the update won't shut
  629. ** the server down.
  630. */
  631. if (fMustActivate || fMustRun) {
  632. /* if we force the object to run, then shut it down after
  633. ** the update. do NOT force the object to close if we
  634. ** want to activate the object or if the object was
  635. ** already running.
  636. */
  637. if (!fMustActivate && !OleIsRunning(lpContainerLine->m_lpOleObj))
  638. fMustClose = TRUE; // shutdown after update
  639. hrErr = ContainerLine_RunOleObject(lpContainerLine);
  640. if (fObjConverted &&
  641. FAILED(hrErr) && GetScode(hrErr)!=OLE_E_STATIC) {
  642. // ERROR: convert of the object failed.
  643. // revert the storage to restore the original link.
  644. // (OLE2NOTE: static object always return OLE_E_STATIC
  645. // when told to run; this is NOT an error here.
  646. // the OLE2 libraries have built in handlers for
  647. // the static objects that do the conversion.
  648. ContainerLine_UnloadOleObject(
  649. lpContainerLine, OLECLOSE_NOSAVE);
  650. lpContainerLine->m_lpStg->lpVtbl->Revert(
  651. lpContainerLine->m_lpStg);
  652. goto error;
  653. } else if (fObjConverted) {
  654. FORMATETC FmtEtc;
  655. DWORD dwNewConnection;
  656. LPOLECACHE lpOleCache = (LPOLECACHE)OleStdQueryInterface
  657. ((LPUNKNOWN)lpContainerLine->m_lpOleObj,&IID_IOleCache);
  658. /* OLE2NOTE: we need to force the converted object to
  659. ** setup a new OLERENDER_DRAW cache. it is possible
  660. ** that the new object needs to cache different data
  661. ** in order to support drawing than the old object.
  662. */
  663. if (lpOleCache &&
  664. lpContainerLine->m_dwDrawAspect == DVASPECT_CONTENT) {
  665. FmtEtc.cfFormat = 0; // whatever is needed for Draw
  666. FmtEtc.ptd = NULL;
  667. FmtEtc.dwAspect = DVASPECT_CONTENT;
  668. FmtEtc.lindex = -1;
  669. FmtEtc.tymed = TYMED_NULL;
  670. OLEDBG_BEGIN2("IOleCache::Cache called\r\n")
  671. hrErr = lpOleCache->lpVtbl->Cache(
  672. lpOleCache,
  673. (LPFORMATETC)&FmtEtc,
  674. ADVF_PRIMEFIRST,
  675. (LPDWORD)&dwNewConnection
  676. );
  677. OLEDBG_END2
  678. #if defined( _DEBUG )
  679. if (! SUCCEEDED(hrErr))
  680. OleDbgOutHResult("IOleCache::Cache returned", hrErr);
  681. #endif
  682. OleStdRelease((LPUNKNOWN)lpOleCache);
  683. }
  684. // Close and force object to save; this will commit the stg
  685. ContainerLine_CloseOleObject(
  686. lpContainerLine, OLECLOSE_SAVEIFDIRTY);
  687. fMustClose = FALSE; // we already closed the object
  688. }
  689. if (fMustClose)
  690. ContainerLine_CloseOleObject(lpContainerLine,OLECLOSE_NOSAVE);
  691. }
  692. if (fDisplayChanged) {
  693. /* the Object's display was changed, force a repaint of
  694. ** the line. note the extents of the object may have
  695. ** changed.
  696. */
  697. ContainerLine_UpdateExtent(lpContainerLine, NULL);
  698. LineList_ForceLineRedraw(lpLL, nIndex, TRUE);
  699. }
  700. if (fDisplayChanged || fObjConverted) {
  701. /* mark ContainerDoc as now dirty. if display changed, then
  702. ** the extents of the object may have changed.
  703. */
  704. OutlineDoc_SetModified(lpOutlineDoc, TRUE, TRUE, fDisplayChanged);
  705. }
  706. if (fMustActivate) {
  707. ContainerLine_DoVerb(
  708. lpContainerLine, OLEIVERB_PRIMARY, NULL, FALSE,FALSE);
  709. }
  710. }
  711. if (ct.lpszUserType)
  712. OleStdFreeString(ct.lpszUserType, NULL);
  713. if (ct.lpszDefLabel)
  714. OleStdFreeString(ct.lpszDefLabel, NULL);
  715. if (ct.hMetaPict)
  716. OleUIMetafilePictIconFree(ct.hMetaPict); // clean up metafile
  717. SetCursor(hPrevCursor); // restore original cursor
  718. return;
  719. error:
  720. if (ct.lpszUserType)
  721. OleStdFreeString(ct.lpszUserType, NULL);
  722. if (ct.hMetaPict)
  723. OleUIMetafilePictIconFree(ct.hMetaPict); // clean up metafile
  724. SetCursor(hPrevCursor); // restore original cursor
  725. if (lpErrMsg)
  726. OutlineApp_ErrorMessage(g_lpApp, lpErrMsg);
  727. }
  728. /* ContainerDoc_CloseAllOleObjects
  729. ** -------------------------------
  730. ** Close all OLE objects. This forces all OLE objects to transition
  731. ** from the running state to the loaded state.
  732. **
  733. ** Returns TRUE if all objects closed successfully
  734. ** FALSE if any object could not be closed.
  735. */
  736. BOOL ContainerDoc_CloseAllOleObjects(
  737. LPCONTAINERDOC lpContainerDoc,
  738. DWORD dwSaveOption
  739. )
  740. {
  741. LPLINELIST lpLL = &((LPOUTLINEDOC)lpContainerDoc)->m_LineList;
  742. int i;
  743. LPLINE lpLine;
  744. BOOL fStatus = TRUE;
  745. for (i = 0; i < lpLL->m_nNumLines; i++) {
  746. lpLine=LineList_GetLine(lpLL, i);
  747. if (lpLine && (Line_GetLineType(lpLine)==CONTAINERLINETYPE))
  748. if (! ContainerLine_CloseOleObject(
  749. (LPCONTAINERLINE)lpLine,dwSaveOption))
  750. fStatus = FALSE;
  751. }
  752. return fStatus;
  753. }
  754. /* ContainerDoc_UnloadAllOleObjectsOfClass
  755. ** ---------------------------------------
  756. ** Unload all OLE objects of a particular class. this is necessary
  757. ** when a class level "ActivateAs" (aka. TreatAs) is setup. the user
  758. ** can do this with the Convert dialog. for the TreatAs to take
  759. ** effect, all objects of the class have to loaded and reloaded.
  760. */
  761. void ContainerDoc_UnloadAllOleObjectsOfClass(
  762. LPCONTAINERDOC lpContainerDoc,
  763. REFCLSID rClsid,
  764. DWORD dwSaveOption
  765. )
  766. {
  767. LPLINELIST lpLL = &((LPOUTLINEDOC)lpContainerDoc)->m_LineList;
  768. int i;
  769. LPLINE lpLine;
  770. CLSID clsid;
  771. HRESULT hrErr;
  772. for (i = 0; i < lpLL->m_nNumLines; i++) {
  773. lpLine=LineList_GetLine(lpLL, i);
  774. if (lpLine && (Line_GetLineType(lpLine)==CONTAINERLINETYPE)) {
  775. LPCONTAINERLINE lpContainerLine = (LPCONTAINERLINE)lpLine;
  776. if (! lpContainerLine->m_lpOleObj)
  777. continue; // this object is NOT loaded
  778. hrErr = lpContainerLine->m_lpOleObj->lpVtbl->GetUserClassID(
  779. lpContainerLine->m_lpOleObj,
  780. (CLSID FAR*)&clsid
  781. );
  782. if (hrErr == NOERROR &&
  783. ( IsEqualCLSID((CLSID FAR*)&clsid,rClsid)
  784. || IsEqualCLSID(rClsid,&CLSID_NULL) ) ) {
  785. ContainerLine_UnloadOleObject(lpContainerLine, dwSaveOption);
  786. }
  787. }
  788. }
  789. }
  790. /* ContainerDoc_UpdateExtentOfAllOleObjects
  791. ** ----------------------------------------
  792. ** Update the extents of any OLE object that is marked that its size
  793. ** may have changed. when an IAdviseSink::OnViewChange notification
  794. ** is received, the corresponding ContainerLine is marked
  795. ** (m_fDoGetExtent==TRUE) and a message (WM_U_UPDATEOBJECTEXTENT) is
  796. ** posted to the document indicating that there are dirty objects.
  797. ** when this message is received, this function is called.
  798. */
  799. void ContainerDoc_UpdateExtentOfAllOleObjects(LPCONTAINERDOC lpContainerDoc)
  800. {
  801. LPLINELIST lpLL = &((LPOUTLINEDOC)lpContainerDoc)->m_LineList;
  802. int i;
  803. LPLINE lpLine;
  804. BOOL fStatus = TRUE;
  805. #if defined( INPLACE_CNTR )
  806. int nFirstUpdate = -1;
  807. #endif
  808. for (i = 0; i < lpLL->m_nNumLines; i++) {
  809. lpLine=LineList_GetLine(lpLL, i);
  810. if (lpLine && (Line_GetLineType(lpLine)==CONTAINERLINETYPE)) {
  811. LPCONTAINERLINE lpContainerLine = (LPCONTAINERLINE)lpLine;
  812. if (lpContainerLine->m_fDoGetExtent) {
  813. ContainerLine_UpdateExtent(lpContainerLine, NULL);
  814. #if defined( INPLACE_CNTR )
  815. if (nFirstUpdate == -1)
  816. nFirstUpdate = i;
  817. #endif
  818. }
  819. }
  820. }
  821. #if defined( INPLACE_CNTR )
  822. /* OLE2NOTE: after changing the extents of any line, we need to
  823. ** update the PosRect of the In-Place active
  824. ** objects (if any) that follow the first modified line.
  825. */
  826. if (nFirstUpdate != -1)
  827. ContainerDoc_UpdateInPlaceObjectRects(lpContainerDoc, nFirstUpdate+1);
  828. #endif
  829. }
  830. BOOL ContainerDoc_SaveToFile(
  831. LPCONTAINERDOC lpContainerDoc,
  832. LPCSTR lpszFileName,
  833. UINT uFormat,
  834. BOOL fRemember
  835. )
  836. {
  837. LPOLEAPP lpOleApp = (LPOLEAPP)g_lpApp;
  838. LPOUTLINEDOC lpOutlineDoc = (LPOUTLINEDOC)lpContainerDoc;
  839. LPOLEDOC lpOleDoc = (LPOLEDOC)lpContainerDoc;
  840. LPSTORAGE lpDestStg;
  841. BOOL fStatus;
  842. BOOL fMustRelDestStg = FALSE;
  843. HRESULT hrErr;
  844. #if defined( OPTIONAL )
  845. FILETIME filetimeBeforeSave;
  846. #endif
  847. if (fRemember) {
  848. if (lpszFileName) {
  849. fStatus = OutlineDoc_SetFileName(
  850. lpOutlineDoc, (LPSTR)lpszFileName, NULL);
  851. if (! fStatus) goto error;
  852. }
  853. /* The ContainerDoc keeps its storage open at all times. it is not
  854. ** necessary to reopen the file.
  855. ** if SaveAs is pending, then lpNewStg is the new destination for
  856. ** the save operation, else the existing storage is the dest.
  857. */
  858. lpDestStg = (lpContainerDoc->m_lpNewStg ?
  859. lpContainerDoc->m_lpNewStg : lpOleDoc->m_lpStg);
  860. #if defined( OPTIONAL )
  861. /* OLE2NOTE: an automatic link to an embedded object within the
  862. ** same container document (that uses ItemMonikers) will
  863. ** always be considered "out-of-date' by OLE. if a container
  864. ** application REALLY wants to fix this it can do one of the
  865. ** following:
  866. ** 1. implement a new moniker better than ItemMonikers
  867. ** that look into the objects storage to find the real last
  868. ** change time (rather then defaulting to that of the outer
  869. ** container file).
  870. ** or 2. using item monikers it is possible to fix the case
  871. ** where the container document is saved while the embedded
  872. ** object is running but it will NOT fix the case when the
  873. ** document is saved when the embedded object was only
  874. ** loaded. the fix is to:
  875. ** a. remember the time (T) before the save operation starts
  876. ** b. call IRunningObjectTable::NoteChangeTime(lpDoc, T)
  877. ** c. do the saving and commit the file
  878. ** d. call StgSetTimes to reset the file time to T
  879. ** e. remember time T in document structure and when the
  880. ** root storage is finally released reset the file time
  881. ** again to T (closing the file on DOS sets the time).
  882. */
  883. CoFileTimeNow( &filetimeBeforeSave );
  884. if (lpOleDoc->m_dwRegROT != 0) {
  885. LPRUNNINGOBJECTTABLE lprot;
  886. if (GetRunningObjectTable(0,&lprot) == NOERROR)
  887. {
  888. OleDbgOut2("IRunningObjectTable::NoteChangeTime called\r\n");
  889. lprot->lpVtbl->NoteChangeTime(
  890. lprot, lpOleDoc->m_dwRegROT, &filetimeBeforeSave );
  891. lprot->lpVtbl->Release(lprot);
  892. }
  893. }
  894. #endif
  895. } else {
  896. if (! lpszFileName)
  897. goto error;
  898. /* OLE2NOTE: since we are preforming a SaveCopyAs operation, we
  899. ** do not need to have the DocFile open in STGM_TRANSACTED mode.
  900. ** there is less overhead to use STGM_DIRECT mode.
  901. */
  902. hrErr = StgCreateDocfileA(
  903. lpszFileName,
  904. STGM_READWRITE|STGM_DIRECT|STGM_SHARE_EXCLUSIVE|STGM_CREATE,
  905. 0,
  906. &lpDestStg
  907. );
  908. OleDbgAssertSz(hrErr == NOERROR, "Could not create Docfile");
  909. if (hrErr != NOERROR) {
  910. OleDbgOutHResult("StgCreateDocfile returned", hrErr);
  911. goto error;
  912. }
  913. fMustRelDestStg = TRUE;
  914. }
  915. /* OLE2NOTE: we must be sure to write our class ID into our
  916. ** storage. this information is used by OLE to determine the
  917. ** class of the data stored in our storage. Even for top
  918. ** "file-level" objects this information should be written to
  919. ** the file.
  920. */
  921. hrErr = WriteClassStg(lpDestStg, &CLSID_APP);
  922. if(hrErr != NOERROR) goto error;
  923. fStatus = OutlineDoc_SaveSelToStg(
  924. lpOutlineDoc,
  925. NULL, // save all lines
  926. uFormat,
  927. lpDestStg,
  928. FALSE, // fSameAsLoad
  929. TRUE // remember this stg
  930. );
  931. if (fStatus)
  932. fStatus = OleStdCommitStorage(lpDestStg);
  933. if (fRemember) {
  934. /* if SaveAs was pending, then release the old storage and remember
  935. ** the new storage as the active current storage. all data from
  936. ** the old storage has been copied into the new storage.
  937. */
  938. if (lpContainerDoc->m_lpNewStg) {
  939. OleStdRelease((LPUNKNOWN)lpOleDoc->m_lpStg); // free old stg
  940. lpOleDoc->m_lpStg = lpContainerDoc->m_lpNewStg; // save new stg
  941. lpContainerDoc->m_lpNewStg = NULL;
  942. }
  943. if (! fStatus) goto error;
  944. OutlineDoc_SetModified(lpOutlineDoc, FALSE, FALSE, FALSE);
  945. #if defined( OPTIONAL )
  946. /* reset time of file on disk to be time just prior to saving.
  947. ** NOTE: it would also be necessary to remember
  948. ** filetimeBeforeSave in the document structure and when the
  949. ** root storage is finally released reset the file time
  950. ** again to this value (closing the file on DOS sets the time).
  951. */
  952. StgSetTimesA(lpOutlineDoc->m_szFileName,
  953. NULL, NULL, &filetimeBeforeSave);
  954. #endif
  955. }
  956. if (fMustRelDestStg)
  957. OleStdRelease((LPUNKNOWN)lpDestStg);
  958. return TRUE;
  959. error:
  960. if (fMustRelDestStg)
  961. OleStdRelease((LPUNKNOWN)lpDestStg);
  962. OutlineApp_ErrorMessage(g_lpApp, ErrMsgSaving);
  963. return FALSE;
  964. }
  965. /* ContainerDoc_ContainerLineDoVerbCommand
  966. ** ---------------------------------------
  967. ** Execute a verb of the OLE object in the current focus line.
  968. */
  969. void ContainerDoc_ContainerLineDoVerbCommand(
  970. LPCONTAINERDOC lpContainerDoc,
  971. LONG iVerb
  972. )
  973. {
  974. LPLINELIST lpLL = &((LPOUTLINEDOC)lpContainerDoc)->m_LineList;
  975. int nIndex = LineList_GetFocusLineIndex(lpLL);
  976. LPLINE lpLine = LineList_GetLine(lpLL, nIndex);
  977. HCURSOR hPrevCursor;
  978. if (! lpLine || (Line_GetLineType(lpLine) != CONTAINERLINETYPE) ) return;
  979. // this may take a while, put up hourglass cursor
  980. hPrevCursor = SetCursor(LoadCursor(NULL, IDC_WAIT));
  981. ContainerLine_DoVerb((LPCONTAINERLINE) lpLine, iVerb, NULL, TRUE, TRUE);
  982. SetCursor(hPrevCursor); // restore original cursor
  983. }
  984. /* ContainerDoc_GetNextStgName
  985. ** ---------------------------
  986. ** Generate the next unused name for a sub-storage to be used by an
  987. ** OLE object. The ContainerDoc keeps a counter. The storages for
  988. ** OLE objects are simply numbered (eg. Obj 0, Obj 1). A "long"
  989. ** integer worth of storage names should be more than enough than we
  990. ** will ever need.
  991. **
  992. ** NOTE: when an OLE object is transfered via drag/drop or the
  993. ** clipboard, we attempt to keep the currently assigned name for the
  994. ** object (if not currently in use). thus it is possible that an
  995. ** object with a the next default name (eg. "Obj 5") already exists
  996. ** in the current document if an object with this name was privously
  997. ** transfered (pasted or dropped). we therefore loop until we find
  998. ** the next lowest unused name.
  999. */
  1000. void ContainerDoc_GetNextStgName(
  1001. LPCONTAINERDOC lpContainerDoc,
  1002. LPSTR lpszStgName,
  1003. int nLen
  1004. )
  1005. {
  1006. wsprintf(lpszStgName, "%s %ld",
  1007. (LPSTR)DEFOBJNAMEPREFIX,
  1008. ++(lpContainerDoc->m_nNextObjNo)
  1009. );
  1010. while (ContainerDoc_IsStgNameUsed(lpContainerDoc, lpszStgName) == TRUE) {
  1011. wsprintf(lpszStgName, "%s %ld",
  1012. (LPSTR)DEFOBJNAMEPREFIX,
  1013. ++(lpContainerDoc->m_nNextObjNo)
  1014. );
  1015. }
  1016. }
  1017. /* ContainerDoc_IsStgNameUsed
  1018. ** --------------------------
  1019. ** Check if a given StgName is already in use.
  1020. */
  1021. BOOL ContainerDoc_IsStgNameUsed(
  1022. LPCONTAINERDOC lpContainerDoc,
  1023. LPSTR lpszStgName
  1024. )
  1025. {
  1026. LPLINELIST lpLL = &((LPOUTLINEDOC)lpContainerDoc)->m_LineList;
  1027. int i;
  1028. LPLINE lpLine;
  1029. for (i = 0; i < lpLL->m_nNumLines; i++) {
  1030. lpLine=LineList_GetLine(lpLL, i);
  1031. if (lpLine && (Line_GetLineType(lpLine)==CONTAINERLINETYPE)) {
  1032. if (lstrcmp(lpszStgName,
  1033. ((LPCONTAINERLINE)lpLine)->m_szStgName) == 0) {
  1034. return TRUE; // Match FOUND!
  1035. }
  1036. }
  1037. }
  1038. return FALSE; // if we get here, then NO match was found.
  1039. }
  1040. LPSTORAGE ContainerDoc_GetStg(LPCONTAINERDOC lpContainerDoc)
  1041. {
  1042. return ((LPOLEDOC)lpContainerDoc)->m_lpStg;
  1043. }
  1044. /* ContainerDoc_GetSingleOleObject
  1045. ** -------------------------------
  1046. ** If the entire document contains a single OLE object, then
  1047. ** return the desired interface of the object.
  1048. **
  1049. ** Returns NULL if there is are multiple lines in the document or
  1050. ** the single line is not a ContainerLine.
  1051. */
  1052. LPUNKNOWN ContainerDoc_GetSingleOleObject(
  1053. LPCONTAINERDOC lpContainerDoc,
  1054. REFIID riid,
  1055. LPCONTAINERLINE FAR* lplpContainerLine
  1056. )
  1057. {
  1058. LPLINELIST lpLL = &((LPOUTLINEDOC)lpContainerDoc)->m_LineList;
  1059. LPLINE lpLine;
  1060. LPUNKNOWN lpObj = NULL;
  1061. if (lplpContainerLine)
  1062. *lplpContainerLine = NULL;
  1063. if (lpLL->m_nNumLines != 1)
  1064. return NULL; // doc does NOT contain a single line
  1065. lpLine=LineList_GetLine(lpLL, 0);
  1066. if (lpLine && (Line_GetLineType(lpLine)==CONTAINERLINETYPE))
  1067. lpObj = ContainerLine_GetOleObject((LPCONTAINERLINE)lpLine, riid);
  1068. if (lplpContainerLine)
  1069. *lplpContainerLine = (LPCONTAINERLINE)lpLine;
  1070. return lpObj;
  1071. }
  1072. /* ContainerDoc_IsSelAnOleObject
  1073. ** -----------------------------
  1074. ** Check if the selection is a single selection of an OLE object.
  1075. ** if so, then optionally return the desired interface of the object
  1076. ** and/or index of the ContainerLine containing the OLE object.
  1077. **
  1078. ** Returns FALSE if there is a multiple selection or the single
  1079. ** selection is not a ContainerLine.
  1080. */
  1081. BOOL ContainerDoc_IsSelAnOleObject(
  1082. LPCONTAINERDOC lpContainerDoc,
  1083. REFIID riid,
  1084. LPUNKNOWN FAR* lplpvObj,
  1085. int FAR* lpnIndex,
  1086. LPCONTAINERLINE FAR* lplpContainerLine
  1087. )
  1088. {
  1089. LPLINELIST lpLL = &((LPOUTLINEDOC)lpContainerDoc)->m_LineList;
  1090. LINERANGE lrSel;
  1091. int nNumSel;
  1092. LPLINE lpLine;
  1093. if (lplpvObj) *lplpvObj = NULL;
  1094. if (lpnIndex) *lpnIndex = -1;
  1095. if (lplpContainerLine) *lplpContainerLine = NULL;
  1096. nNumSel = LineList_GetSel(lpLL, (LPLINERANGE)&lrSel);
  1097. if (nNumSel != 1)
  1098. return FALSE; // selection is not a single line
  1099. lpLine = LineList_GetLine(lpLL, lrSel.m_nStartLine);
  1100. if (lpLine && (Line_GetLineType(lpLine)==CONTAINERLINETYPE)) {
  1101. if (lpnIndex)
  1102. *lpnIndex = lrSel.m_nStartLine;
  1103. if (lplpContainerLine)
  1104. *lplpContainerLine = (LPCONTAINERLINE)lpLine;
  1105. if (riid) {
  1106. *lplpvObj = ContainerLine_GetOleObject(
  1107. (LPCONTAINERLINE)lpLine,
  1108. riid
  1109. );
  1110. }
  1111. return (*lplpvObj ? TRUE : FALSE);
  1112. }
  1113. return FALSE;
  1114. }
  1115. /*************************************************************************
  1116. ** ContainerDoc::IOleUILinkContainer interface implementation
  1117. *************************************************************************/
  1118. STDMETHODIMP CntrDoc_LinkCont_QueryInterface(
  1119. LPOLEUILINKCONTAINER lpThis,
  1120. REFIID riid,
  1121. LPVOID FAR* lplpvObj
  1122. )
  1123. {
  1124. LPOLEDOC lpOleDoc = (LPOLEDOC)
  1125. ((struct CDocOleUILinkContainerImpl FAR*)lpThis)->lpContainerDoc;
  1126. return OleDoc_QueryInterface(lpOleDoc, riid, lplpvObj);
  1127. }
  1128. STDMETHODIMP_(ULONG) CntrDoc_LinkCont_AddRef(LPOLEUILINKCONTAINER lpThis)
  1129. {
  1130. LPOLEDOC lpOleDoc = (LPOLEDOC)
  1131. ((struct CDocOleUILinkContainerImpl FAR*)lpThis)->lpContainerDoc;
  1132. OleDbgAddRefMethod(lpThis, "IOleUILinkContainer");
  1133. return OleDoc_AddRef(lpOleDoc);
  1134. }
  1135. STDMETHODIMP_(ULONG) CntrDoc_LinkCont_Release(LPOLEUILINKCONTAINER lpThis)
  1136. {
  1137. LPOLEDOC lpOleDoc = (LPOLEDOC)
  1138. ((struct CDocOleUILinkContainerImpl FAR*)lpThis)->lpContainerDoc;
  1139. OleDbgReleaseMethod(lpThis, "IOleUILinkContainer");
  1140. return OleDoc_Release(lpOleDoc);
  1141. }
  1142. STDMETHODIMP_(DWORD) CntrDoc_LinkCont_GetNextLink(
  1143. LPOLEUILINKCONTAINER lpThis,
  1144. DWORD dwLink
  1145. )
  1146. {
  1147. LPCONTAINERDOC lpContainerDoc =
  1148. ((struct CDocOleUILinkContainerImpl FAR*)lpThis)->lpContainerDoc;
  1149. LPCONTAINERLINE lpContainerLine = NULL;
  1150. OLEDBG_BEGIN2("CntrDoc_LinkCont_GetNextLink\r\n")
  1151. lpContainerLine = ContainerDoc_GetNextLink(
  1152. lpContainerDoc,
  1153. (LPCONTAINERLINE)dwLink
  1154. );
  1155. OLEDBG_END2
  1156. return (DWORD)lpContainerLine;
  1157. }
  1158. STDMETHODIMP CntrDoc_LinkCont_SetLinkUpdateOptions(
  1159. LPOLEUILINKCONTAINER lpThis,
  1160. DWORD dwLink,
  1161. DWORD dwUpdateOpt
  1162. )
  1163. {
  1164. LPCONTAINERDOC lpContainerDoc =
  1165. ((struct CDocOleUILinkContainerImpl FAR*)lpThis)->lpContainerDoc;
  1166. LPCONTAINERLINE lpContainerLine = (LPCONTAINERLINE)dwLink;
  1167. LPOLELINK lpOleLink = lpContainerLine->m_lpOleLink;
  1168. SCODE sc = S_OK;
  1169. HRESULT hrErr;
  1170. OLEDBG_BEGIN2("CntrDoc_LinkCont_SetLinkUpdateOptions\r\n")
  1171. OleDbgAssert(lpContainerLine);
  1172. if (! lpOleLink) {
  1173. sc = E_FAIL;
  1174. goto error;
  1175. }
  1176. OLEDBG_BEGIN2("IOleLink::SetUpdateOptions called\r\n")
  1177. hrErr = lpOleLink->lpVtbl->SetUpdateOptions(
  1178. lpOleLink,
  1179. dwUpdateOpt
  1180. );
  1181. OLEDBG_END2
  1182. // save new link type update option
  1183. lpContainerLine->m_dwLinkType = dwUpdateOpt;
  1184. if (hrErr != NOERROR) {
  1185. OleDbgOutHResult("IOleLink::SetUpdateOptions returned", hrErr);
  1186. sc = GetScode(hrErr);
  1187. goto error;
  1188. }
  1189. OLEDBG_END2
  1190. return ResultFromScode(sc);
  1191. error:
  1192. OLEDBG_END2
  1193. return ResultFromScode(sc);
  1194. }
  1195. STDMETHODIMP CntrDoc_LinkCont_GetLinkUpdateOptions(
  1196. LPOLEUILINKCONTAINER lpThis,
  1197. DWORD dwLink,
  1198. DWORD FAR* lpdwUpdateOpt
  1199. )
  1200. {
  1201. LPCONTAINERDOC lpContainerDoc =
  1202. ((struct CDocOleUILinkContainerImpl FAR*)lpThis)->lpContainerDoc;
  1203. LPCONTAINERLINE lpContainerLine = (LPCONTAINERLINE)dwLink;
  1204. LPOLELINK lpOleLink = lpContainerLine->m_lpOleLink;
  1205. SCODE sc = S_OK;
  1206. HRESULT hrErr;
  1207. OLEDBG_BEGIN2("CntrDoc_LinkCont_GetLinkUpdateOptions\r\n")
  1208. OleDbgAssert(lpContainerLine);
  1209. if (! lpOleLink) {
  1210. sc = E_FAIL;
  1211. goto error;
  1212. }
  1213. OLEDBG_BEGIN2("IOleLink::GetUpdateOptions called\r\n")
  1214. hrErr = lpOleLink->lpVtbl->GetUpdateOptions(
  1215. lpOleLink,
  1216. lpdwUpdateOpt
  1217. );
  1218. OLEDBG_END2
  1219. // reset saved link type to ensure it is correct
  1220. lpContainerLine->m_dwLinkType = *lpdwUpdateOpt;
  1221. if (hrErr != NOERROR) {
  1222. OleDbgOutHResult("IOleLink::GetUpdateOptions returned", hrErr);
  1223. sc = GetScode(hrErr);
  1224. goto error;
  1225. }
  1226. OLEDBG_END2
  1227. return ResultFromScode(sc);
  1228. error:
  1229. OLEDBG_END2
  1230. return ResultFromScode(sc);
  1231. }
  1232. STDMETHODIMP CntrDoc_LinkCont_SetLinkSource(
  1233. LPOLEUILINKCONTAINER lpThis,
  1234. DWORD dwLink,
  1235. LPSTR lpszDisplayName,
  1236. ULONG lenFileName,
  1237. ULONG FAR* lpchEaten,
  1238. BOOL fValidateSource
  1239. )
  1240. {
  1241. LPCONTAINERDOC lpContainerDoc =
  1242. ((struct CDocOleUILinkContainerImpl FAR*)lpThis)->lpContainerDoc;
  1243. LPCONTAINERLINE lpContainerLine = (LPCONTAINERLINE)dwLink;
  1244. SCODE sc = S_OK;
  1245. HRESULT hrErr;
  1246. LPOLELINK lpOleLink = lpContainerLine->m_lpOleLink;
  1247. LPBC lpbc = NULL;
  1248. LPMONIKER lpmk = NULL;
  1249. LPOLEOBJECT lpLinkSrcOleObj = NULL;
  1250. CLSID clsid = CLSID_NULL;
  1251. CLSID clsidOld = CLSID_NULL;
  1252. OLEDBG_BEGIN2("CntrDoc_LinkCont_SetLinkSource\r\n")
  1253. OleDbgAssert(lpContainerLine);
  1254. lpContainerLine->m_fLinkUnavailable = TRUE;
  1255. if (fValidateSource) {
  1256. /* OLE2NOTE: validate the link source by parsing the string
  1257. ** into a Moniker. if this is successful, then the string is
  1258. ** valid.
  1259. */
  1260. hrErr = CreateBindCtx(0, (LPBC FAR*)&lpbc);
  1261. if (hrErr != NOERROR) {
  1262. sc = GetScode(hrErr); // ERROR: OOM
  1263. goto cleanup;
  1264. }
  1265. // Get class of orignial link source if it is available
  1266. if (lpContainerLine->m_lpOleObj) {
  1267. OLEDBG_BEGIN2("IOleObject::GetUserClassID called\r\n")
  1268. hrErr = lpContainerLine->m_lpOleObj->lpVtbl->GetUserClassID(
  1269. lpContainerLine->m_lpOleObj, (CLSID FAR*)&clsidOld);
  1270. OLEDBG_END2
  1271. if (hrErr != NOERROR) clsidOld = CLSID_NULL;
  1272. }
  1273. hrErr = OleStdMkParseDisplayName(
  1274. &clsidOld,lpbc,lpszDisplayName,lpchEaten,(LPMONIKER FAR*)&lpmk);
  1275. if (hrErr != NOERROR) {
  1276. sc = GetScode(hrErr); // ERROR in parsing moniker!
  1277. goto cleanup;
  1278. }
  1279. /* OLE2NOTE: the link source was validated; it successfully
  1280. ** parsed into a Moniker. we can set the source of the link
  1281. ** directly with this Moniker. if we want the link to be
  1282. ** able to know the correct class for the new link source,
  1283. ** we must bind to the moniker and get the CLSID. if we do
  1284. ** not do this then methods like IOleObject::GetUserType
  1285. ** will return nothing (NULL strings).
  1286. */
  1287. hrErr = lpmk->lpVtbl->BindToObject(
  1288. lpmk,lpbc,NULL,&IID_IOleObject,(LPVOID FAR*)&lpLinkSrcOleObj);
  1289. if (hrErr == NOERROR) {
  1290. OLEDBG_BEGIN2("IOleObject::GetUserClassID called\r\n")
  1291. hrErr = lpLinkSrcOleObj->lpVtbl->GetUserClassID(
  1292. lpLinkSrcOleObj, (CLSID FAR*)&clsid);
  1293. OLEDBG_END2
  1294. lpContainerLine->m_fLinkUnavailable = FALSE;
  1295. /* get the short user type name of the link because it may
  1296. ** have changed. we cache this name and must update our
  1297. ** cache. this name is used all the time when we have to
  1298. ** build the object verb menu. we cache this information
  1299. ** to make it quicker to build the verb menu.
  1300. */
  1301. if (lpContainerLine->m_lpszShortType) {
  1302. OleStdFree(lpContainerLine->m_lpszShortType);
  1303. lpContainerLine->m_lpszShortType = NULL;
  1304. }
  1305. OLEDBG_BEGIN2("IOleObject::GetUserType called\r\n")
  1306. CallIOleObjectGetUserTypeA(
  1307. lpContainerLine->m_lpOleObj,
  1308. USERCLASSTYPE_SHORT,
  1309. (LPSTR FAR*)&lpContainerLine->m_lpszShortType
  1310. );
  1311. OLEDBG_END2
  1312. }
  1313. else
  1314. lpContainerLine->m_fLinkUnavailable = TRUE;
  1315. }
  1316. else {
  1317. LPMONIKER lpmkFile = NULL;
  1318. LPMONIKER lpmkItem = NULL;
  1319. char szDelim[2];
  1320. LPSTR lpszName;
  1321. szDelim[0] = lpszDisplayName[(int)lenFileName];
  1322. szDelim[1] = '\0';
  1323. lpszDisplayName[(int)lenFileName] = '\0';
  1324. OLEDBG_BEGIN2("CreateFileMoniker called\r\n")
  1325. CreateFileMonikerA(lpszDisplayName, (LPMONIKER FAR*)&lpmkFile);
  1326. OLEDBG_END2
  1327. lpszDisplayName[(int)lenFileName] = szDelim[0];
  1328. if (!lpmkFile)
  1329. goto cleanup;
  1330. if (lstrlen(lpszDisplayName) > (int)lenFileName) { // have item name
  1331. lpszName = lpszDisplayName + lenFileName + 1;
  1332. OLEDBG_BEGIN2("CreateItemMoniker called\r\n")
  1333. CreateItemMonikerA(
  1334. szDelim, lpszName, (LPMONIKER FAR*)&lpmkItem);
  1335. OLEDBG_END2
  1336. if (!lpmkItem) {
  1337. OleStdRelease((LPUNKNOWN)lpmkFile);
  1338. goto cleanup;
  1339. }
  1340. OLEDBG_BEGIN2("CreateGenericComposite called\r\n")
  1341. CreateGenericComposite(lpmkFile, lpmkItem, (LPMONIKER FAR*)&lpmk);
  1342. OLEDBG_END2
  1343. if (lpmkFile)
  1344. OleStdRelease((LPUNKNOWN)lpmkFile);
  1345. if (lpmkItem)
  1346. OleStdRelease((LPUNKNOWN)lpmkItem);
  1347. if (!lpmk)
  1348. goto cleanup;
  1349. }
  1350. else
  1351. lpmk = lpmkFile;
  1352. }
  1353. if (! lpOleLink) {
  1354. OleDbgAssert(lpOleLink != NULL);
  1355. sc = E_FAIL;
  1356. goto cleanup;
  1357. }
  1358. if (lpmk) {
  1359. OLEDBG_BEGIN2("IOleLink::SetSourceMoniker called\r\n")
  1360. hrErr = lpOleLink->lpVtbl->SetSourceMoniker(
  1361. lpOleLink, lpmk, (REFCLSID)&clsid);
  1362. OLEDBG_END2
  1363. if (FAILED(GetScode(hrErr))) {
  1364. OleDbgOutHResult("IOleLink::SetSourceMoniker returned",hrErr);
  1365. sc = GetScode(hrErr);
  1366. goto cleanup;
  1367. }
  1368. /* OLE2NOTE: above we forced the link source moniker to bind.
  1369. ** because we deliberately hold on to the bind context
  1370. ** (lpbc) the link source object will not shut down. during
  1371. ** the call to IOleLink::SetSourceMoniker, the link will
  1372. ** connect to the running link source (the link internally
  1373. ** calls BindIfRunning). it is important to initially allow
  1374. ** the link to bind to the running object so that it can get
  1375. ** an update of the presentation for its cache. we do not
  1376. ** want the connection from our link to the link source be
  1377. ** the only reason the link source stays running. thus we
  1378. ** deliberately for the link to release (unbind) the source
  1379. ** object, we then release the bind context, and then we
  1380. ** allow the link to rebind to the link source if it is
  1381. ** running anyway.
  1382. */
  1383. if (lpbc && lpmk->lpVtbl->IsRunning(lpmk,lpbc,NULL,NULL) == NOERROR) {
  1384. OLEDBG_BEGIN2("IOleLink::Update called\r\n")
  1385. hrErr = lpOleLink->lpVtbl->Update(lpOleLink, NULL);
  1386. OLEDBG_END2
  1387. #if defined( _DEBUG )
  1388. if (FAILED(GetScode(hrErr)))
  1389. OleDbgOutHResult("IOleLink::Update returned",hrErr);
  1390. #endif
  1391. OLEDBG_BEGIN2("IOleLink::UnbindSource called\r\n")
  1392. hrErr = lpOleLink->lpVtbl->UnbindSource(lpOleLink);
  1393. OLEDBG_END2
  1394. #if defined( _DEBUG )
  1395. if (FAILED(GetScode(hrErr)))
  1396. OleDbgOutHResult("IOleLink::UnbindSource returned",hrErr);
  1397. #endif
  1398. if (lpLinkSrcOleObj) {
  1399. OleStdRelease((LPUNKNOWN)lpLinkSrcOleObj);
  1400. lpLinkSrcOleObj = NULL;
  1401. }
  1402. if (lpbc) {
  1403. OleStdRelease((LPUNKNOWN)lpbc);
  1404. lpbc = NULL;
  1405. }
  1406. OLEDBG_BEGIN2("IOleLink::BindIfRunning called\r\n")
  1407. hrErr = lpOleLink->lpVtbl->BindIfRunning(lpOleLink);
  1408. OLEDBG_END2
  1409. #if defined( _DEBUG )
  1410. if (FAILED(GetScode(hrErr)))
  1411. OleDbgOutHResult("IOleLink::BindIfRunning returned",hrErr);
  1412. #endif
  1413. }
  1414. } else {
  1415. /* OLE2NOTE: the link source was NOT validated; it was NOT
  1416. ** successfully parsed into a Moniker. we can only set the
  1417. ** display name string as the source of the link. this link
  1418. ** is not able to bind.
  1419. */
  1420. OLEDBG_BEGIN2("IOleLink::SetSourceDisplayName called\r\n")
  1421. hrErr = CallIOleLinkSetSourceDisplayNameA(
  1422. lpOleLink, lpszDisplayName);
  1423. OLEDBG_END2
  1424. if (hrErr != NOERROR) {
  1425. OleDbgOutHResult("IOleLink::SetSourceDisplayName returned",hrErr);
  1426. sc = GetScode(hrErr);
  1427. goto cleanup;
  1428. }
  1429. }
  1430. cleanup:
  1431. if (lpLinkSrcOleObj)
  1432. OleStdRelease((LPUNKNOWN)lpLinkSrcOleObj);
  1433. if (lpmk)
  1434. OleStdRelease((LPUNKNOWN)lpmk);
  1435. if (lpbc)
  1436. OleStdRelease((LPUNKNOWN)lpbc);
  1437. OLEDBG_END2
  1438. return ResultFromScode(sc);
  1439. }
  1440. STDMETHODIMP CntrDoc_LinkCont_GetLinkSource(
  1441. LPOLEUILINKCONTAINER lpThis,
  1442. DWORD dwLink,
  1443. LPSTR FAR* lplpszDisplayName,
  1444. ULONG FAR* lplenFileName,
  1445. LPSTR FAR* lplpszFullLinkType,
  1446. LPSTR FAR* lplpszShortLinkType,
  1447. BOOL FAR* lpfSourceAvailable,
  1448. BOOL FAR* lpfIsSelected
  1449. )
  1450. {
  1451. LPCONTAINERDOC lpContainerDoc =
  1452. ((struct CDocOleUILinkContainerImpl FAR*)lpThis)->lpContainerDoc;
  1453. LPCONTAINERLINE lpContainerLine = (LPCONTAINERLINE)dwLink;
  1454. LPOLELINK lpOleLink = lpContainerLine->m_lpOleLink;
  1455. LPOLEOBJECT lpOleObj = NULL;
  1456. LPMONIKER lpmk = NULL;
  1457. LPMONIKER lpmkFirst = NULL;
  1458. LPBC lpbc = NULL;
  1459. SCODE sc = S_OK;
  1460. HRESULT hrErr;
  1461. OLEDBG_BEGIN2("CntrDoc_LinkCont_GetLinkSource\r\n")
  1462. /* OLE2NOTE: we must make sure to set all out parameters to NULL. */
  1463. *lplpszDisplayName = NULL;
  1464. *lplpszFullLinkType = NULL;
  1465. *lplpszShortLinkType= NULL;
  1466. *lplenFileName = 0;
  1467. *lpfSourceAvailable = !lpContainerLine->m_fLinkUnavailable;
  1468. OleDbgAssert(lpContainerLine);
  1469. if (! lpOleLink) {
  1470. OLEDBG_END2
  1471. return ResultFromScode(E_FAIL);
  1472. }
  1473. OLEDBG_BEGIN2("IOleLink::GetSourceMoniker called\r\n")
  1474. hrErr = lpOleLink->lpVtbl->GetSourceMoniker(
  1475. lpOleLink,
  1476. (LPMONIKER FAR*)&lpmk
  1477. );
  1478. OLEDBG_END2
  1479. if (hrErr == NOERROR) {
  1480. /* OLE2NOTE: the link has the Moniker form of the link source;
  1481. ** this is therefore a validated link source. if the first
  1482. ** part of the Moniker is a FileMoniker, then we need to
  1483. ** return the length of the filename string. we need to
  1484. ** return the ProgID associated with the link source as the
  1485. ** "lpszShortLinkType". we need to return the
  1486. ** FullUserTypeName associated with the link source as the
  1487. ** "lpszFullLinkType".
  1488. */
  1489. lpOleObj = (LPOLEOBJECT)OleStdQueryInterface(
  1490. (LPUNKNOWN)lpOleLink, &IID_IOleObject);
  1491. if (lpOleObj) {
  1492. CallIOleObjectGetUserTypeA(
  1493. lpOleObj,
  1494. USERCLASSTYPE_FULL,
  1495. lplpszFullLinkType
  1496. );
  1497. CallIOleObjectGetUserTypeA(
  1498. lpOleObj,
  1499. USERCLASSTYPE_SHORT,
  1500. lplpszShortLinkType
  1501. );
  1502. OleStdRelease((LPUNKNOWN)lpOleObj);
  1503. }
  1504. *lplenFileName = OleStdGetLenFilePrefixOfMoniker(lpmk);
  1505. lpmk->lpVtbl->Release(lpmk);
  1506. }
  1507. OLEDBG_BEGIN2("IOleLink::GetSourceDisplayName called\r\n")
  1508. hrErr = CallIOleLinkGetSourceDisplayNameA(
  1509. lpOleLink,
  1510. lplpszDisplayName
  1511. );
  1512. OLEDBG_END2
  1513. if (hrErr != NOERROR) {
  1514. OleDbgOutHResult("IOleLink::GetSourceDisplayName returned", hrErr);
  1515. OLEDBG_END2
  1516. return hrErr;
  1517. }
  1518. OLEDBG_END2
  1519. if (lpfIsSelected)
  1520. *lpfIsSelected = Line_IsSelected((LPLINE)lpContainerLine);
  1521. return NOERROR;
  1522. }
  1523. STDMETHODIMP CntrDoc_LinkCont_OpenLinkSource(
  1524. LPOLEUILINKCONTAINER lpThis,
  1525. DWORD dwLink
  1526. )
  1527. {
  1528. LPCONTAINERDOC lpContainerDoc =
  1529. ((struct CDocOleUILinkContainerImpl FAR*)lpThis)->lpContainerDoc;
  1530. LPCONTAINERLINE lpContainerLine = (LPCONTAINERLINE)dwLink;
  1531. SCODE sc = S_OK;
  1532. OLEDBG_BEGIN2("CntrDoc_LinkCont_OpenLinkSource\r\n")
  1533. OleDbgAssert(lpContainerLine);
  1534. if (! ContainerLine_DoVerb(
  1535. lpContainerLine, OLEIVERB_SHOW, NULL, TRUE, FALSE)) {
  1536. sc = E_FAIL;
  1537. }
  1538. lpContainerLine->m_fLinkUnavailable = (sc != S_OK);
  1539. OLEDBG_END2
  1540. return ResultFromScode(sc);
  1541. }
  1542. STDMETHODIMP CntrDoc_LinkCont_UpdateLink(
  1543. LPOLEUILINKCONTAINER lpThis,
  1544. DWORD dwLink,
  1545. BOOL fErrorMessage,
  1546. BOOL fErrorAction // ignore if fErrorMessage
  1547. // is FALSE
  1548. )
  1549. {
  1550. LPCONTAINERDOC lpContainerDoc =
  1551. ((struct CDocOleUILinkContainerImpl FAR*)lpThis)->lpContainerDoc;
  1552. LPCONTAINERLINE lpContainerLine = (LPCONTAINERLINE)dwLink;
  1553. SCODE sc = S_OK;
  1554. /* Default to update of the link */
  1555. HRESULT hrErr = S_FALSE;
  1556. OLEDBG_BEGIN2("CntrDoc_LinkCont_UpdateLink\r\n")
  1557. OleDbgAssert(lpContainerLine);
  1558. if (! lpContainerLine->m_lpOleObj)
  1559. ContainerLine_LoadOleObject(lpContainerLine);
  1560. if (!fErrorMessage) {
  1561. OLEDBG_BEGIN2("IOleObject::IsUpToDate called\r\n")
  1562. hrErr = lpContainerLine->m_lpOleObj->lpVtbl->IsUpToDate(
  1563. lpContainerLine->m_lpOleObj
  1564. );
  1565. OLEDBG_END2
  1566. }
  1567. if (hrErr != NOERROR) {
  1568. OLEDBG_BEGIN2("IOleObject::Update called\r\n")
  1569. hrErr = lpContainerLine->m_lpOleObj->lpVtbl->Update(
  1570. lpContainerLine->m_lpOleObj
  1571. );
  1572. OLEDBG_END2
  1573. }
  1574. /* OLE2NOTE: If IOleObject::Update on the Link object returned
  1575. ** OLE_E_CLASSDIFF because the link source is no longer
  1576. ** the expected class, then the link should be re-created with
  1577. ** the new link source. thus the link will be updated with the
  1578. ** new link source.
  1579. */
  1580. if (GetScode(hrErr) == OLE_E_CLASSDIFF)
  1581. hrErr = ContainerLine_ReCreateLinkBecauseClassDiff(lpContainerLine);
  1582. lpContainerLine->m_fLinkUnavailable = (hrErr != NOERROR);
  1583. if (hrErr != NOERROR) {
  1584. OleDbgOutHResult("IOleObject::Update returned", hrErr);
  1585. sc = GetScode(hrErr);
  1586. if (fErrorMessage) {
  1587. ContainerLine_ProcessOleRunError(
  1588. lpContainerLine,hrErr,fErrorAction,FALSE/*fMenuInvoked*/);
  1589. }
  1590. }
  1591. /* OLE2NOTE: if the update of the object requires us to update our
  1592. ** display, then we will automatically be sent a OnViewChange
  1593. ** advise. thus we do not need to take any action here to force
  1594. ** a repaint.
  1595. */
  1596. OLEDBG_END2
  1597. return ResultFromScode(sc);
  1598. }
  1599. /* CntrDoc_LinkCont_CancelLink
  1600. ** ---------------------------
  1601. ** Convert the link to a static picture.
  1602. **
  1603. ** OLE2NOTE: OleCreateStaticFromData can be used to create a static
  1604. ** picture object.
  1605. */
  1606. STDMETHODIMP CntrDoc_LinkCont_CancelLink(
  1607. LPOLEUILINKCONTAINER lpThis,
  1608. DWORD dwLink
  1609. )
  1610. {
  1611. LPCONTAINERDOC lpContainerDoc =
  1612. ((struct CDocOleUILinkContainerImpl FAR*)lpThis)->lpContainerDoc;
  1613. LPCONTAINERLINE lpContainerLine = (LPCONTAINERLINE)dwLink;
  1614. LPLINELIST lpLL = &((LPOUTLINEDOC)lpContainerDoc)->m_LineList;
  1615. LPLINE lpLine = NULL;
  1616. HDC hDC;
  1617. int nTab = 0;
  1618. char szStgName[CWCSTORAGENAME];
  1619. LPCONTAINERLINE lpNewContainerLine = NULL;
  1620. LPDATAOBJECT lpSrcDataObj;
  1621. LPOLELINK lpOleLink = lpContainerLine->m_lpOleLink;
  1622. int nIndex = LineList_GetLineIndex(lpLL, (LPLINE)lpContainerLine);
  1623. OLEDBG_BEGIN2("CntrDoc_LinkCont_CancelLink\r\n")
  1624. /* we will first break the connection of the link to its source. */
  1625. if (lpOleLink) {
  1626. lpContainerLine->m_dwLinkType = 0;
  1627. OLEDBG_BEGIN2("IOleLink::SetSourceMoniker called\r\n")
  1628. lpOleLink->lpVtbl->SetSourceMoniker(
  1629. lpOleLink, NULL, (REFCLSID)&CLSID_NULL);
  1630. OLEDBG_END2
  1631. }
  1632. lpSrcDataObj = (LPDATAOBJECT)ContainerLine_GetOleObject(
  1633. lpContainerLine,&IID_IDataObject);
  1634. if (! lpSrcDataObj)
  1635. goto error;
  1636. ContainerDoc_GetNextStgName(lpContainerDoc, szStgName, sizeof(szStgName));
  1637. nTab = Line_GetTabLevel((LPLINE)lpContainerLine);
  1638. hDC = LineList_GetDC(lpLL);
  1639. lpNewContainerLine = ContainerLine_CreateFromData(
  1640. hDC,
  1641. nTab,
  1642. lpContainerDoc,
  1643. lpSrcDataObj,
  1644. OLECREATEFROMDATA_STATIC,
  1645. 0, /* no special cfFormat required */
  1646. (lpContainerLine->m_dwDrawAspect == DVASPECT_ICON),
  1647. NULL, /* hMetaPict */
  1648. szStgName
  1649. );
  1650. LineList_ReleaseDC(lpLL, hDC);
  1651. OleStdRelease((LPUNKNOWN)lpSrcDataObj);
  1652. if (! lpNewContainerLine)
  1653. goto error;
  1654. OutlineDoc_SetModified((LPOUTLINEDOC)lpContainerDoc, TRUE, TRUE, FALSE);
  1655. LineList_ReplaceLine(lpLL, (LPLINE)lpNewContainerLine, nIndex);
  1656. OLEDBG_END2
  1657. return ResultFromScode(NOERROR);
  1658. error:
  1659. OutlineApp_ErrorMessage(g_lpApp, "Could not break the link.");
  1660. OLEDBG_END2
  1661. return ResultFromScode(E_FAIL);
  1662. }