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