windows-nt/Source/XPSP1/NT/com/winole/samples/srvrdemo/doc.c
2020-09-26 16:20:57 +08:00

595 lines
16 KiB
C

/*
OLE SERVER DEMO
Doc.c
This file contains document methods and various document-related support
functions.
(c) Copyright Microsoft Corp. 1990 - 1992 All Rights Reserved
*/
/*
Important Note:
No method should ever dispatch a DDE message or allow a DDE message to
be dispatched.
Therefore, no method should ever enter a message dispatch loop.
Also, a method should not show a dialog or message box, because the
processing of the dialog box messages will allow DDE messages to be
dispatched.
*/
#define SERVERONLY
#include <windows.h>
#include <ole.h>
#include "srvrdemo.h"
/* AssociateClient
* ---------------
*
* Add a client to the list of clients associated with an object.
*
* This function is necessary only because ServerDemo does not create object
* structures as they are requested, but rather has a fixed set of objects.
* When DocGetObject is called with a NULL object name, the entire
* document is requested, but ServerDemo does not currently support making
* the entire document an object, so DocGetObject returns one object.
* That object now goes by two names: NULL and its real name. Therefore
* we need to keep track of both lpoleclient's that were passed to
* DocGetObject. Ideally, DocGetObject should always create a new OBJ
* structure containing a pointer (or some reference) to the object's native
* data and also containing one lpoleclient.
*
* LPOLECLIENT lpoleclient - the client to be associated with the object.
* LPOBJ lpobj - the object
*
* RETURNS: TRUE if successful
* FALSE if out of memory
*
* CUSTOMIZATION: Server Demo specific
*
*/
static BOOL AssociateClient (LPOLECLIENT lpoleclient, LPOBJ lpobj)
{
INT i;
for (i=0; i < clpoleclient; i++)
{
if (lpobj->lpoleclient[i]==lpoleclient)
{
return TRUE;
}
if (lpobj->lpoleclient[i]==NULL)
{
lpobj->lpoleclient[i]=lpoleclient;
return TRUE;
}
}
return FALSE;
}
/* CreateNewDoc
* ------------
*
* If lhdoc == NULL then we must register the new document by calling
* OleRegisterServerDoc, which will return a new handle which will be stored
* in docMain.lhdoc.
* Also if lhdoc==NULL then this document is being created at the request of
* the user, not of the client library.
*
* LONG lhdoc - Document handle
* LPSTR lpszDoc - Title of the new document
* DOCTYPE doctype - What type of document is being created
*
* RETURNS: TRUE if successful, FALSE otherwise.
*
* CUSTOMIZATION: Re-implement
*
*/
BOOL CreateNewDoc (LONG lhdoc, LPSTR lpszDoc, DOCTYPE doctype)
{
INT i;
// Fill in the fields of the document structure.
docMain.doctype = doctype;
docMain.oledoc.lpvtbl= &docvtbl;
if (lhdoc == 0)
{
if (OLE_OK != OleRegisterServerDoc
(srvrMain.lhsrvr,
lpszDoc,
(LPOLESERVERDOC) &docMain,
(LHSERVERDOC FAR *) &docMain.lhdoc))
return FALSE;
}
else
docMain.lhdoc = lhdoc;
// Reset all the flags because no object numbers have been used.
for (i=1; i <= cfObjNums; i++)
docMain.rgfObjNums[i] = FALSE;
fDocChanged = FALSE;
SetTitle (lpszDoc, doctype == doctypeEmbedded);
return TRUE;
}
/* DestroyDoc
* ----------
*
* Free all memory that had been allocated for a document.
*
*
* CUSTOMIZATION: Re-implement. Your application will probably use some
* other method for enumerating all the objects in a document.
* ServerDemo enumerates the child windows, but if each object
* does not have its own window, this will not work.
*
*/
VOID DestroyDoc (VOID)
{
HWND hwnd;
HWND hwndNext;
// Delete all object windows.
hwnd = SelectedObjectWindow();
while (hwnd)
{
hwndNext = GetWindow (hwnd, GW_HWNDNEXT);
// Each object window frees its own memory upon receiving WM_DESTROY.
DestroyWindow (hwnd);
hwnd = hwndNext;
}
if (docMain.aName)
{
GlobalDeleteAtom (docMain.aName);
docMain.aName = '\0';
}
if (docMain.hpal)
DeleteObject (docMain.hpal);
}
/* DocClose DOCUMENT "Close" METHOD
* --------
*
* The library calls this method to unconditionally close the document.
*
* LPOLESERVERDOC lpoledoc - The server document to close
*
* RETURNS: Return value from RevokeDoc.
*
* CUSTOMIZATION: None
*
*/
OLESTATUS APIENTRY DocClose (LPOLESERVERDOC lpoledoc)
{
return RevokeDoc();
}
/* DocExecute DOCUMENT "Execute" METHOD
* ----------
*
* This application does not support the execution of DDE execution commands.
*
* LPOLESERVERDOC lpoledoc - The server document
* HANDLE hCommands - DDE execute commands
*
* RETURNS: OLE_ERROR_COMMAND
*
* CUSTOMIZATION: Re-implement if your application supports the execution of
* DDE commands.
*
*/
OLESTATUS APIENTRY DocExecute (LPOLESERVERDOC lpoledoc, HANDLE hCommands)
{
return OLE_ERROR_COMMAND;
}
/* DocGetObject DOCUMENT "GetObject" METHOD
* ------------
*
* The library uses this method to get an object's structure for the
* client. Memory needs to be allocated and initialized here for this.
* A NULL string indicates that the client has an embedded object
* which was started from Create, CreateFromTemplate, or Edit, but not Open.
*
* First see if the object name is NULL. If so, you would ordinarily
* return the entire document, but Server Demo returns the selected object.
* If the object name is not NULL, then go through the list of objects,
* searching for one with that name. Return an error if there is not one.
*
* LPOLESERVERDOC lpoledoc - The server document
* OLE_LPCSTR lpszObjectName - The name of the object to get data for
* LPOLEOBJECT FAR *lplpoleobject - The object's data is put here
* LPOLECLIENT lpoleclient - The client structure
*
* RETURNS: OLE_OK
* OLE_ERROR_NAME if object not found
* OLE_ERROR_MEMORY if no more memory to store lpoleclient
*
* CUSTOMIZATION: Re-implement.
* lpszObjectName == "" indicates that the whole document
* should be the object returned.
*
*/
OLESTATUS APIENTRY DocGetObject
(LPOLESERVERDOC lpoledoc, OLE_LPCSTR lpszObjectName,
LPOLEOBJECT FAR *lplpoleobject, LPOLECLIENT lpoleclient)
{
HWND hwnd;
ATOM aName;
LPOBJ lpobj;
if (lpszObjectName == NULL || lpszObjectName[0] == '\0')
{
// Return a new object or the selected object.
hwnd = SelectedObjectWindow();
lpobj = hwnd ? HwndToLpobj (hwnd) : CreateNewObj (FALSE);
*lplpoleobject = (LPOLEOBJECT) lpobj;
// Associate client with object.
if (!AssociateClient (lpoleclient, lpobj))
return OLE_ERROR_MEMORY;
return OLE_OK;
}
if (!(aName = GlobalFindAtom (lpszObjectName)))
return OLE_ERROR_NAME;
hwnd = SelectedObjectWindow();
// Go through all the child windows and find the window whose name
// matches the given object name.
while (hwnd)
{
lpobj = HwndToLpobj (hwnd);
if (aName == lpobj->aName)
{
// Return the object with the matching name.
*lplpoleobject = (LPOLEOBJECT) lpobj;
// Associate client with the object.
if (!AssociateClient (lpoleclient, lpobj))
return OLE_ERROR_MEMORY;
return OLE_OK;
}
hwnd = GetWindow (hwnd, GW_HWNDNEXT);
}
if (((DOCPTR)lpoledoc)->doctype == doctypeEmbedded)
{
lpobj = CreateNewObj (FALSE);
*lplpoleobject = (LPOLEOBJECT) lpobj;
// Associate client with object.
if (!AssociateClient (lpoleclient, lpobj))
return OLE_ERROR_MEMORY;
return OLE_OK;
}
// Object with name lpszObjName was not found.
return OLE_ERROR_NAME;
}
/* DocRelease DOCUMENT "Release" METHOD
* ----------
*
* The library uses this method to notify the server that a revoked
* document has finally finished all conversations, and can be
* destroyed.
* It sets fWaitingForDocRelease to FALSE so a new document can be created
* and the user can continue working.
*
* LPOLESERVERDOC lpoledoc - The server document
*
* RETURNS: OLE_OK
*
* CUSTOMIZATION: None
*
*/
OLESTATUS APIENTRY DocRelease (LPOLESERVERDOC lpoledoc)
{
fWaitingForDocRelease = FALSE;
// Free all memory that has been allocated for the document.
DestroyDoc();
return OLE_OK;
}
/* DocSave DOCUMENT "Save" METHOD
* -------
*
* Save document to a file.
*
* LPOLESERVERDOC lpoledoc - The document to save
*
* RETURNS: OLE_OK
*
* CUSTOMIZATION: None
*
*/
OLESTATUS APIENTRY DocSave (LPOLESERVERDOC lpoledoc)
{
if (docMain.doctype == doctypeFromFile)
{
// No "File Save As" dialog box will be brought up because the
// file name is already known.
return SaveDoc() ? OLE_OK : OLE_ERROR_GENERIC;
}
else
return OLE_ERROR_GENERIC;
}
/* DocSetDocDimensions DOCUMENT "SetDocDimensions" METHOD
* -------------------
*
* The library calls this method to tell the server the bounds on
* the target device for rendering the document.
* A call to this method is ignored for linked objects because the size of
* a linked document depends only on the source file.
*
* LPOLESERVERDOC lpoledoc - The server document
* CONST LPRECT lprect - The target size in MM_HIMETRIC units
*
* RETURNS: OLE_OK
*
* CUSTOMIZATION: Re-implement
* How an object is sized is application-specific. (Server Demo
* uses MoveWindow.)
*
*/
OLESTATUS APIENTRY DocSetDocDimensions
(LPOLESERVERDOC lpoledoc, OLE_CONST RECT FAR * lprect)
{
if (docMain.doctype == doctypeEmbedded)
{
RECT rect = *lprect;
// the units are in HIMETRIC
rect.right = rect.right - rect.left;
// the following was bottom - top
rect.bottom = rect.top - rect.bottom;
HiMetricToDevice ( (LPPOINT) &rect.right );
MoveWindow (SelectedObjectWindow(), 0, 0,
rect.right + 2 * GetSystemMetrics(SM_CXFRAME),
rect.bottom + 2 * GetSystemMetrics(SM_CYFRAME),
TRUE);
/* If for some reason your application needs to notify the client that
the data has changed because DocSetDocDimensions has been called,
then notify the client here.
SendDocMsg (OLE_CHANGED);
*/
}
return OLE_OK;
}
/* DocSetHostNames DOCUMENT "SetHostNames" METHOD
* ---------------
*
* The library uses this method to set the name of the document
* window.
* All this function does is change the title bar text, although it could
* do more if necesary.
* This function is only called for embedded objects; linked objects
* use their filenames for the title bar text.
*
* LPOLESERVERDOC lpoledoc - The server document
* OLE_LPCSTR lpszClient - The name of the client
* OLE_LPCSTR lpszDoc - The client's name for the document
*
* RETURNS: OLE_OK
*
* CUSTOMIZATION: None
*
*/
OLESTATUS APIENTRY DocSetHostNames
(LPOLESERVERDOC lpoledoc, OLE_LPCSTR lpszClient, OLE_LPCSTR lpszDoc)
{
SetTitle ((LPSTR)lpszDoc, TRUE);
lstrcpy ((LPSTR) szClient, lpszClient);
lstrcpy ((LPSTR) szClientDoc, Abbrev((LPSTR)lpszDoc));
UpdateFileMenu (IDM_UPDATE);
return OLE_OK;
}
/* DocSetColorScheme DOCUMENT "SetColorScheme" METHOD
* -----------------
*
* The client calls this method to suggest a color scheme (palette) for
* the server to use.
* In Server Demo the document's palette is never actually used because each
* object has its own palette. See ObjSetColorScheme.
*
* LPOLESERVERDOC lpoledoc - The server document
* CONST LOGPALETTE FAR * lppal - Suggested palette
*
* RETURNS: OLE_ERROR_PALETTE if CreatePalette fails,
* OLE_OK otherwise
*
*
* CUSTOMIZATION: If your application supports color schemes, then this
* function is a good example of how to create and store
* a palette.
*/
OLESTATUS APIENTRY DocSetColorScheme
(LPOLESERVERDOC lpoledoc, OLE_CONST LOGPALETTE FAR * lppal)
{
HPALETTE hpal = CreatePalette (lppal);
if (hpal==NULL)
return OLE_ERROR_PALETTE;
if (docMain.hpal)
{
// Delete old palette
DeleteObject (docMain.hpal);
}
// Store handle to new palette
docMain.hpal = hpal;
return OLE_OK;
}
/* RevokeDoc
* ---------
*
* Call OleRevokeServerDoc.
* If the return value is OLE_WAIT_FOR_BUSY, then set fWaitingForDocRelease
* and enter a message-dispatch loop until fWaitingForDocRelease is reset.
* As long as fWaitingForDocRelease is set, the user interface will be
* disabled so that the user will not be able to manipulate the document.
* When the DocRelease method is called, it will reset fWaitingForDocRelease,
* allowing RevokeDoc to free the document's memory and return.
*
* This is essentially a way to make an asynchronous operation synchronous.
* We need to wait until the old document is revoked before we can delete
* its data and create a new one.
*
* Note that we cannot call RevokeDoc from a method because it is illegal to
* enter a message-dispatch loop within a method.
*
* RETURNS: The return value of OleRevokeServerDoc.
*
* CUSTOMIZATION: lhdoc may need to be passed in as a parameter if your
* application does not have a global variable corresponding
* to docMain.
*
*/
OLESTATUS RevokeDoc (VOID)
{
OLESTATUS olestatus;
if ((olestatus = OleRevokeServerDoc(docMain.lhdoc)) > OLE_WAIT_FOR_RELEASE)
DestroyDoc();
docMain.lhdoc = 0; // A NULL handle indicates that the document
// has been revoked or is being revoked.
return olestatus;
}
/* SaveChangesOption
* -----------------
*
* Give the user the opportunity to save changes to the current document
* before continuing.
*
* BOOL *pfUpdateLater - Will be set to TRUE if the client does not accept
* the update and needs to be updated when the document
* is closed. In that case, OLE_CLOSED will be sent.
*
* RETURNS: IDYES, IDNO, or IDCANCEL
*
* CUSTOMIZATION: None
*
*/
INT SaveChangesOption (BOOL *pfUpdateLater)
{
INT nReply;
CHAR szBuf[cchFilenameMax];
*pfUpdateLater = FALSE;
if (fDocChanged)
{
CHAR szTmp[cchFilenameMax];
if (docMain.aName)
GlobalGetAtomName (docMain.aName, szTmp, cchFilenameMax);
else
szTmp[0] = '\0';
if (docMain.doctype == doctypeEmbedded)
wsprintf (szBuf, "The object has been changed.\n\nUpdate %s before closing the object?", Abbrev (szTmp));
else
lstrcpy (szBuf, (LPSTR) "Save changes?");
nReply = MessageBox (hwndMain, szBuf, szAppName,
MB_ICONEXCLAMATION | MB_YESNOCANCEL);
switch (nReply)
{
case IDYES:
if (docMain.doctype != doctypeEmbedded)
SaveDoc();
else
switch (OleSavedServerDoc (docMain.lhdoc))
{
case OLE_ERROR_CANT_UPDATE_CLIENT:
*pfUpdateLater = TRUE;
break;
case OLE_OK:
break;
default:
ErrorBox ("Fatal Error: Cannot update.");
}
return IDYES;
case IDNO:
return IDNO;
case IDCANCEL:
return IDCANCEL;
}
}
return TRUE;
}
/* SendDocMsg
* ----------
*
* This function sends messages to all the objects in a document when
* the document has changed.
*
* WORD wMessage - The message to send
*
* CUSTOMIZATION: The means of enumerating all the objects in a document
* is application specific.
*/
VOID SendDocMsg (WORD wMessage)
{
HWND hwnd;
// Get handle to first object window.
hwnd = SelectedObjectWindow();
// Send message to all object windows.
while (hwnd)
{
SendObjMsg (HwndToLpobj(hwnd), wMessage);
hwnd = GetWindow (hwnd, GW_HWNDNEXT);
}
}