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.

594 lines
16 KiB

  1. /*
  2. OLE SERVER DEMO
  3. Doc.c
  4. This file contains document methods and various document-related support
  5. functions.
  6. (c) Copyright Microsoft Corp. 1990 - 1992 All Rights Reserved
  7. */
  8. /*
  9. Important Note:
  10. No method should ever dispatch a DDE message or allow a DDE message to
  11. be dispatched.
  12. Therefore, no method should ever enter a message dispatch loop.
  13. Also, a method should not show a dialog or message box, because the
  14. processing of the dialog box messages will allow DDE messages to be
  15. dispatched.
  16. */
  17. #define SERVERONLY
  18. #include <windows.h>
  19. #include <ole.h>
  20. #include "srvrdemo.h"
  21. /* AssociateClient
  22. * ---------------
  23. *
  24. * Add a client to the list of clients associated with an object.
  25. *
  26. * This function is necessary only because ServerDemo does not create object
  27. * structures as they are requested, but rather has a fixed set of objects.
  28. * When DocGetObject is called with a NULL object name, the entire
  29. * document is requested, but ServerDemo does not currently support making
  30. * the entire document an object, so DocGetObject returns one object.
  31. * That object now goes by two names: NULL and its real name. Therefore
  32. * we need to keep track of both lpoleclient's that were passed to
  33. * DocGetObject. Ideally, DocGetObject should always create a new OBJ
  34. * structure containing a pointer (or some reference) to the object's native
  35. * data and also containing one lpoleclient.
  36. *
  37. * LPOLECLIENT lpoleclient - the client to be associated with the object.
  38. * LPOBJ lpobj - the object
  39. *
  40. * RETURNS: TRUE if successful
  41. * FALSE if out of memory
  42. *
  43. * CUSTOMIZATION: Server Demo specific
  44. *
  45. */
  46. static BOOL AssociateClient (LPOLECLIENT lpoleclient, LPOBJ lpobj)
  47. {
  48. INT i;
  49. for (i=0; i < clpoleclient; i++)
  50. {
  51. if (lpobj->lpoleclient[i]==lpoleclient)
  52. {
  53. return TRUE;
  54. }
  55. if (lpobj->lpoleclient[i]==NULL)
  56. {
  57. lpobj->lpoleclient[i]=lpoleclient;
  58. return TRUE;
  59. }
  60. }
  61. return FALSE;
  62. }
  63. /* CreateNewDoc
  64. * ------------
  65. *
  66. * If lhdoc == NULL then we must register the new document by calling
  67. * OleRegisterServerDoc, which will return a new handle which will be stored
  68. * in docMain.lhdoc.
  69. * Also if lhdoc==NULL then this document is being created at the request of
  70. * the user, not of the client library.
  71. *
  72. * LONG lhdoc - Document handle
  73. * LPSTR lpszDoc - Title of the new document
  74. * DOCTYPE doctype - What type of document is being created
  75. *
  76. * RETURNS: TRUE if successful, FALSE otherwise.
  77. *
  78. * CUSTOMIZATION: Re-implement
  79. *
  80. */
  81. BOOL CreateNewDoc (LONG lhdoc, LPSTR lpszDoc, DOCTYPE doctype)
  82. {
  83. INT i;
  84. // Fill in the fields of the document structure.
  85. docMain.doctype = doctype;
  86. docMain.oledoc.lpvtbl= &docvtbl;
  87. if (lhdoc == 0)
  88. {
  89. if (OLE_OK != OleRegisterServerDoc
  90. (srvrMain.lhsrvr,
  91. lpszDoc,
  92. (LPOLESERVERDOC) &docMain,
  93. (LHSERVERDOC FAR *) &docMain.lhdoc))
  94. return FALSE;
  95. }
  96. else
  97. docMain.lhdoc = lhdoc;
  98. // Reset all the flags because no object numbers have been used.
  99. for (i=1; i <= cfObjNums; i++)
  100. docMain.rgfObjNums[i] = FALSE;
  101. fDocChanged = FALSE;
  102. SetTitle (lpszDoc, doctype == doctypeEmbedded);
  103. return TRUE;
  104. }
  105. /* DestroyDoc
  106. * ----------
  107. *
  108. * Free all memory that had been allocated for a document.
  109. *
  110. *
  111. * CUSTOMIZATION: Re-implement. Your application will probably use some
  112. * other method for enumerating all the objects in a document.
  113. * ServerDemo enumerates the child windows, but if each object
  114. * does not have its own window, this will not work.
  115. *
  116. */
  117. VOID DestroyDoc (VOID)
  118. {
  119. HWND hwnd;
  120. HWND hwndNext;
  121. // Delete all object windows.
  122. hwnd = SelectedObjectWindow();
  123. while (hwnd)
  124. {
  125. hwndNext = GetWindow (hwnd, GW_HWNDNEXT);
  126. // Each object window frees its own memory upon receiving WM_DESTROY.
  127. DestroyWindow (hwnd);
  128. hwnd = hwndNext;
  129. }
  130. if (docMain.aName)
  131. {
  132. GlobalDeleteAtom (docMain.aName);
  133. docMain.aName = '\0';
  134. }
  135. if (docMain.hpal)
  136. DeleteObject (docMain.hpal);
  137. }
  138. /* DocClose DOCUMENT "Close" METHOD
  139. * --------
  140. *
  141. * The library calls this method to unconditionally close the document.
  142. *
  143. * LPOLESERVERDOC lpoledoc - The server document to close
  144. *
  145. * RETURNS: Return value from RevokeDoc.
  146. *
  147. * CUSTOMIZATION: None
  148. *
  149. */
  150. OLESTATUS APIENTRY DocClose (LPOLESERVERDOC lpoledoc)
  151. {
  152. return RevokeDoc();
  153. }
  154. /* DocExecute DOCUMENT "Execute" METHOD
  155. * ----------
  156. *
  157. * This application does not support the execution of DDE execution commands.
  158. *
  159. * LPOLESERVERDOC lpoledoc - The server document
  160. * HANDLE hCommands - DDE execute commands
  161. *
  162. * RETURNS: OLE_ERROR_COMMAND
  163. *
  164. * CUSTOMIZATION: Re-implement if your application supports the execution of
  165. * DDE commands.
  166. *
  167. */
  168. OLESTATUS APIENTRY DocExecute (LPOLESERVERDOC lpoledoc, HANDLE hCommands)
  169. {
  170. return OLE_ERROR_COMMAND;
  171. }
  172. /* DocGetObject DOCUMENT "GetObject" METHOD
  173. * ------------
  174. *
  175. * The library uses this method to get an object's structure for the
  176. * client. Memory needs to be allocated and initialized here for this.
  177. * A NULL string indicates that the client has an embedded object
  178. * which was started from Create, CreateFromTemplate, or Edit, but not Open.
  179. *
  180. * First see if the object name is NULL. If so, you would ordinarily
  181. * return the entire document, but Server Demo returns the selected object.
  182. * If the object name is not NULL, then go through the list of objects,
  183. * searching for one with that name. Return an error if there is not one.
  184. *
  185. * LPOLESERVERDOC lpoledoc - The server document
  186. * OLE_LPCSTR lpszObjectName - The name of the object to get data for
  187. * LPOLEOBJECT FAR *lplpoleobject - The object's data is put here
  188. * LPOLECLIENT lpoleclient - The client structure
  189. *
  190. * RETURNS: OLE_OK
  191. * OLE_ERROR_NAME if object not found
  192. * OLE_ERROR_MEMORY if no more memory to store lpoleclient
  193. *
  194. * CUSTOMIZATION: Re-implement.
  195. * lpszObjectName == "" indicates that the whole document
  196. * should be the object returned.
  197. *
  198. */
  199. OLESTATUS APIENTRY DocGetObject
  200. (LPOLESERVERDOC lpoledoc, OLE_LPCSTR lpszObjectName,
  201. LPOLEOBJECT FAR *lplpoleobject, LPOLECLIENT lpoleclient)
  202. {
  203. HWND hwnd;
  204. ATOM aName;
  205. LPOBJ lpobj;
  206. if (lpszObjectName == NULL || lpszObjectName[0] == '\0')
  207. {
  208. // Return a new object or the selected object.
  209. hwnd = SelectedObjectWindow();
  210. lpobj = hwnd ? HwndToLpobj (hwnd) : CreateNewObj (FALSE);
  211. *lplpoleobject = (LPOLEOBJECT) lpobj;
  212. // Associate client with object.
  213. if (!AssociateClient (lpoleclient, lpobj))
  214. return OLE_ERROR_MEMORY;
  215. return OLE_OK;
  216. }
  217. if (!(aName = GlobalFindAtom (lpszObjectName)))
  218. return OLE_ERROR_NAME;
  219. hwnd = SelectedObjectWindow();
  220. // Go through all the child windows and find the window whose name
  221. // matches the given object name.
  222. while (hwnd)
  223. {
  224. lpobj = HwndToLpobj (hwnd);
  225. if (aName == lpobj->aName)
  226. {
  227. // Return the object with the matching name.
  228. *lplpoleobject = (LPOLEOBJECT) lpobj;
  229. // Associate client with the object.
  230. if (!AssociateClient (lpoleclient, lpobj))
  231. return OLE_ERROR_MEMORY;
  232. return OLE_OK;
  233. }
  234. hwnd = GetWindow (hwnd, GW_HWNDNEXT);
  235. }
  236. if (((DOCPTR)lpoledoc)->doctype == doctypeEmbedded)
  237. {
  238. lpobj = CreateNewObj (FALSE);
  239. *lplpoleobject = (LPOLEOBJECT) lpobj;
  240. // Associate client with object.
  241. if (!AssociateClient (lpoleclient, lpobj))
  242. return OLE_ERROR_MEMORY;
  243. return OLE_OK;
  244. }
  245. // Object with name lpszObjName was not found.
  246. return OLE_ERROR_NAME;
  247. }
  248. /* DocRelease DOCUMENT "Release" METHOD
  249. * ----------
  250. *
  251. * The library uses this method to notify the server that a revoked
  252. * document has finally finished all conversations, and can be
  253. * destroyed.
  254. * It sets fWaitingForDocRelease to FALSE so a new document can be created
  255. * and the user can continue working.
  256. *
  257. * LPOLESERVERDOC lpoledoc - The server document
  258. *
  259. * RETURNS: OLE_OK
  260. *
  261. * CUSTOMIZATION: None
  262. *
  263. */
  264. OLESTATUS APIENTRY DocRelease (LPOLESERVERDOC lpoledoc)
  265. {
  266. fWaitingForDocRelease = FALSE;
  267. // Free all memory that has been allocated for the document.
  268. DestroyDoc();
  269. return OLE_OK;
  270. }
  271. /* DocSave DOCUMENT "Save" METHOD
  272. * -------
  273. *
  274. * Save document to a file.
  275. *
  276. * LPOLESERVERDOC lpoledoc - The document to save
  277. *
  278. * RETURNS: OLE_OK
  279. *
  280. * CUSTOMIZATION: None
  281. *
  282. */
  283. OLESTATUS APIENTRY DocSave (LPOLESERVERDOC lpoledoc)
  284. {
  285. if (docMain.doctype == doctypeFromFile)
  286. {
  287. // No "File Save As" dialog box will be brought up because the
  288. // file name is already known.
  289. return SaveDoc() ? OLE_OK : OLE_ERROR_GENERIC;
  290. }
  291. else
  292. return OLE_ERROR_GENERIC;
  293. }
  294. /* DocSetDocDimensions DOCUMENT "SetDocDimensions" METHOD
  295. * -------------------
  296. *
  297. * The library calls this method to tell the server the bounds on
  298. * the target device for rendering the document.
  299. * A call to this method is ignored for linked objects because the size of
  300. * a linked document depends only on the source file.
  301. *
  302. * LPOLESERVERDOC lpoledoc - The server document
  303. * CONST LPRECT lprect - The target size in MM_HIMETRIC units
  304. *
  305. * RETURNS: OLE_OK
  306. *
  307. * CUSTOMIZATION: Re-implement
  308. * How an object is sized is application-specific. (Server Demo
  309. * uses MoveWindow.)
  310. *
  311. */
  312. OLESTATUS APIENTRY DocSetDocDimensions
  313. (LPOLESERVERDOC lpoledoc, OLE_CONST RECT FAR * lprect)
  314. {
  315. if (docMain.doctype == doctypeEmbedded)
  316. {
  317. RECT rect = *lprect;
  318. // the units are in HIMETRIC
  319. rect.right = rect.right - rect.left;
  320. // the following was bottom - top
  321. rect.bottom = rect.top - rect.bottom;
  322. HiMetricToDevice ( (LPPOINT) &rect.right );
  323. MoveWindow (SelectedObjectWindow(), 0, 0,
  324. rect.right + 2 * GetSystemMetrics(SM_CXFRAME),
  325. rect.bottom + 2 * GetSystemMetrics(SM_CYFRAME),
  326. TRUE);
  327. /* If for some reason your application needs to notify the client that
  328. the data has changed because DocSetDocDimensions has been called,
  329. then notify the client here.
  330. SendDocMsg (OLE_CHANGED);
  331. */
  332. }
  333. return OLE_OK;
  334. }
  335. /* DocSetHostNames DOCUMENT "SetHostNames" METHOD
  336. * ---------------
  337. *
  338. * The library uses this method to set the name of the document
  339. * window.
  340. * All this function does is change the title bar text, although it could
  341. * do more if necesary.
  342. * This function is only called for embedded objects; linked objects
  343. * use their filenames for the title bar text.
  344. *
  345. * LPOLESERVERDOC lpoledoc - The server document
  346. * OLE_LPCSTR lpszClient - The name of the client
  347. * OLE_LPCSTR lpszDoc - The client's name for the document
  348. *
  349. * RETURNS: OLE_OK
  350. *
  351. * CUSTOMIZATION: None
  352. *
  353. */
  354. OLESTATUS APIENTRY DocSetHostNames
  355. (LPOLESERVERDOC lpoledoc, OLE_LPCSTR lpszClient, OLE_LPCSTR lpszDoc)
  356. {
  357. SetTitle ((LPSTR)lpszDoc, TRUE);
  358. lstrcpy ((LPSTR) szClient, lpszClient);
  359. lstrcpy ((LPSTR) szClientDoc, Abbrev((LPSTR)lpszDoc));
  360. UpdateFileMenu (IDM_UPDATE);
  361. return OLE_OK;
  362. }
  363. /* DocSetColorScheme DOCUMENT "SetColorScheme" METHOD
  364. * -----------------
  365. *
  366. * The client calls this method to suggest a color scheme (palette) for
  367. * the server to use.
  368. * In Server Demo the document's palette is never actually used because each
  369. * object has its own palette. See ObjSetColorScheme.
  370. *
  371. * LPOLESERVERDOC lpoledoc - The server document
  372. * CONST LOGPALETTE FAR * lppal - Suggested palette
  373. *
  374. * RETURNS: OLE_ERROR_PALETTE if CreatePalette fails,
  375. * OLE_OK otherwise
  376. *
  377. *
  378. * CUSTOMIZATION: If your application supports color schemes, then this
  379. * function is a good example of how to create and store
  380. * a palette.
  381. */
  382. OLESTATUS APIENTRY DocSetColorScheme
  383. (LPOLESERVERDOC lpoledoc, OLE_CONST LOGPALETTE FAR * lppal)
  384. {
  385. HPALETTE hpal = CreatePalette (lppal);
  386. if (hpal==NULL)
  387. return OLE_ERROR_PALETTE;
  388. if (docMain.hpal)
  389. {
  390. // Delete old palette
  391. DeleteObject (docMain.hpal);
  392. }
  393. // Store handle to new palette
  394. docMain.hpal = hpal;
  395. return OLE_OK;
  396. }
  397. /* RevokeDoc
  398. * ---------
  399. *
  400. * Call OleRevokeServerDoc.
  401. * If the return value is OLE_WAIT_FOR_BUSY, then set fWaitingForDocRelease
  402. * and enter a message-dispatch loop until fWaitingForDocRelease is reset.
  403. * As long as fWaitingForDocRelease is set, the user interface will be
  404. * disabled so that the user will not be able to manipulate the document.
  405. * When the DocRelease method is called, it will reset fWaitingForDocRelease,
  406. * allowing RevokeDoc to free the document's memory and return.
  407. *
  408. * This is essentially a way to make an asynchronous operation synchronous.
  409. * We need to wait until the old document is revoked before we can delete
  410. * its data and create a new one.
  411. *
  412. * Note that we cannot call RevokeDoc from a method because it is illegal to
  413. * enter a message-dispatch loop within a method.
  414. *
  415. * RETURNS: The return value of OleRevokeServerDoc.
  416. *
  417. * CUSTOMIZATION: lhdoc may need to be passed in as a parameter if your
  418. * application does not have a global variable corresponding
  419. * to docMain.
  420. *
  421. */
  422. OLESTATUS RevokeDoc (VOID)
  423. {
  424. OLESTATUS olestatus;
  425. if ((olestatus = OleRevokeServerDoc(docMain.lhdoc)) > OLE_WAIT_FOR_RELEASE)
  426. DestroyDoc();
  427. docMain.lhdoc = 0; // A NULL handle indicates that the document
  428. // has been revoked or is being revoked.
  429. return olestatus;
  430. }
  431. /* SaveChangesOption
  432. * -----------------
  433. *
  434. * Give the user the opportunity to save changes to the current document
  435. * before continuing.
  436. *
  437. * BOOL *pfUpdateLater - Will be set to TRUE if the client does not accept
  438. * the update and needs to be updated when the document
  439. * is closed. In that case, OLE_CLOSED will be sent.
  440. *
  441. * RETURNS: IDYES, IDNO, or IDCANCEL
  442. *
  443. * CUSTOMIZATION: None
  444. *
  445. */
  446. INT SaveChangesOption (BOOL *pfUpdateLater)
  447. {
  448. INT nReply;
  449. CHAR szBuf[cchFilenameMax];
  450. *pfUpdateLater = FALSE;
  451. if (fDocChanged)
  452. {
  453. CHAR szTmp[cchFilenameMax];
  454. if (docMain.aName)
  455. GlobalGetAtomName (docMain.aName, szTmp, cchFilenameMax);
  456. else
  457. szTmp[0] = '\0';
  458. if (docMain.doctype == doctypeEmbedded)
  459. wsprintf (szBuf, "The object has been changed.\n\nUpdate %s before closing the object?", Abbrev (szTmp));
  460. else
  461. lstrcpy (szBuf, (LPSTR) "Save changes?");
  462. nReply = MessageBox (hwndMain, szBuf, szAppName,
  463. MB_ICONEXCLAMATION | MB_YESNOCANCEL);
  464. switch (nReply)
  465. {
  466. case IDYES:
  467. if (docMain.doctype != doctypeEmbedded)
  468. SaveDoc();
  469. else
  470. switch (OleSavedServerDoc (docMain.lhdoc))
  471. {
  472. case OLE_ERROR_CANT_UPDATE_CLIENT:
  473. *pfUpdateLater = TRUE;
  474. break;
  475. case OLE_OK:
  476. break;
  477. default:
  478. ErrorBox ("Fatal Error: Cannot update.");
  479. }
  480. return IDYES;
  481. case IDNO:
  482. return IDNO;
  483. case IDCANCEL:
  484. return IDCANCEL;
  485. }
  486. }
  487. return TRUE;
  488. }
  489. /* SendDocMsg
  490. * ----------
  491. *
  492. * This function sends messages to all the objects in a document when
  493. * the document has changed.
  494. *
  495. * WORD wMessage - The message to send
  496. *
  497. * CUSTOMIZATION: The means of enumerating all the objects in a document
  498. * is application specific.
  499. */
  500. VOID SendDocMsg (WORD wMessage)
  501. {
  502. HWND hwnd;
  503. // Get handle to first object window.
  504. hwnd = SelectedObjectWindow();
  505. // Send message to all object windows.
  506. while (hwnd)
  507. {
  508. SendObjMsg (HwndToLpobj(hwnd), wMessage);
  509. hwnd = GetWindow (hwnd, GW_HWNDNEXT);
  510. }
  511. }