4.7.4 File New and File Open

File New and File Open follow the same sequence of steps, since File Open is really File New with the extra step of loading the contents of a file. The steps are:

Allocate, initialize, and register a new document with OleRegisterServerDoc.

For File Open, load the file as necessary.

An SDI server application that was performing embedding when the user chooses File New must reconfigure itself to appear as a stand-alone server application by changing the title bar and menu back to the stand-alone interface. The new filename should appear in the title bar and the File menu should contain the standard "Save," "Exit," and "Save As..." commands in place of "Update <Client Document>," "Save Copy As...," and "Exit & Return to <Client Document>."

At this point, the server application operates as a stand-alone application, in which case you'll want it to reset any global flags that track whether or not it was started through OLE and whether it is linking or embedding.

The following code example shows how SRVRDEMO.EXE performs the File New command:

case IDM_NEW:

{

BOOL fUpdateLater;

OLESTATUS olestatus;

if (SaveChangesOption (&fUpdateLater) == IDCANCEL)

break;

else if (fUpdateLater)

SendDocMsg (OLE_CLOSED);

// We want to revoke the doc but not the server, so if

// SrvrRelease is called, do not revoke server.

fRevokeSrvrOnSrvrRelease = FALSE;

if ((olestatus = RevokeDoc()) > OLE_WAIT_FOR_RELEASE)

{

ErrorBox ("Serious Error: Cannot revoke document.");

break;

}

else if (olestatus == OLE_WAIT_FOR_RELEASE)

Wait (&fWaitingForDocRelease);

fRevokeSrvrOnSrvrRelease = TRUE;

if (!CreateNewDoc (NULL, "(Untitled)", doctypeNew))

{

ErrorBox ("Serious Error: Cannot create new document.");

break;

}

// Your application need not create a default object.

CreateNewObj (FALSE);

EmbeddingModeOff();

break;

}

/*

* 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

*/

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] = NULL;

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

*

*/

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);

}

}

/*

* SelectedObjectWindow

*

* Return a handle to the window for the currently selected

* object. The GetWindow calls returns a handle to the main

* window's first child, which is the selected object's

* window.

*

*/

HWND SelectedObjectWindow (void)

{

return GetWindow (hwndMain, GW_CHILD);

}

/*

* SendObjMsg

*

* This function sends a message to a specific object.

*

* LPOBJ lpobj - The object

* WORD wMessage - The message to send

*

*/

void SendObjMsg (LPOBJ lpobj, WORD wMessage)

{

int i;

for (i=0; i < clpoleclient; i++)

{

if (lpobj->lpoleclient[i])

{

// Call the object's Callback function.

lpobj->lpoleclient[i]->lpvtbl->CallBack

(lpobj->lpoleclient[i], wMessage, (LPOLEOBJECT)

lpobj);

}

else

break;

}

}

/**

* 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 document.

*

* 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.

*

*/

OLESTATUS RevokeDoc (void)

{

OLESTATUS olestatus;

if ((olestatus = OleRevokeServerDoc(docMain.lhdoc))

> OLE_WAIT_FOR_RELEASE)

DestroyDoc();

docMain.lhdoc = NULL; // A NULL handle indicates that the

// document has been revoked or is

// being revoked.

return olestatus;

}

/*

* 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.

*

*/

BOOL CreateNewDoc (LONG lhdoc, LPSTR lpszDoc, DOCTYPE doctype)

{

int i;

// Fill in the fields of the document structure.

if (lhdoc == NULL)

{

if (OLE_OK != OleRegisterServerDoc (srvrMain.lhsrvr,

lpszDoc, (LPOLESERVERDOC) &docMain,

(LHSERVERDOC FAR *) &docMain.lhdoc))

return FALSE;

}

else

docMain.lhdoc = lhdoc;

docMain.doctype = doctype;

docMain.oledoc.lpvtbl= &docvtbl;

// 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;

}

/*

* SetTitle

*

* Sets the main window's title bar. The format of the title

* bar is as follows:

*

* If embedded <Server App name> - <object type> in

* <client doc name>

*

* Example: "Server Demo - SrvrDemo Shape in OLECLI.DOC"

* where OLECLI.DOC is a Winword document; otherwise,

* <Server App name> - <server document name>

*

* Example: "Server Demo - OLESVR.SD"

* where OLESVR.SD is a Server demo document

*

* LPSTR lpszDoc - document name

* BOOL fEmbedded - If TRUE embedded document, else normal

* document

*

* RETURNS: OLE_OK

*

*

*/

void SetTitle (LPSTR lpszDoc, BOOL fEmbedded)

{

char szBuf[cchFilenameMax];

if (lpszDoc && lpszDoc[0])

{

// Change document name.

if (docMain.aName)

GlobalDeleteAtom (docMain.aName);

docMain.aName = GlobalAddAtom (lpszDoc);

}

if (fEmbedded)

{

//

if (lpszDoc && lpszDoc[0])

{

wsprintf (szBuf, "%s - SrvrDemo Shape in %s", (LPSTR)

szAppName, Abbrev (lpszDoc));

}

else

{

// Use name from docMain

char szDoc [cchFilenameMax];

GlobalGetAtomName (docMain.aName, szDoc, cchFilenameMax);

wsprintf (szBuf, "%s - SrvrDemo Shape in %s", (LPSTR)

szAppName, Abbrev (szDoc));

}

SetWindowText (hwndMain, (LPSTR)szBuf);

}

else if (lpszDoc && lpszDoc[0])

{

wsprintf (szBuf, "%s - %s", (LPSTR) szAppName, Abbrev(lpszDoc));

SetWindowText (hwndMain, szBuf);

}

}

/*

* CreateNewObj

*

* BOOL fDoc_Changed - The new value for the global variable

* fDocChanged.

* When initializing a new document, we need to create

* a new object without the creation counting as a

* change to the document.

*

* RETURNS: A pointer to the new object

*/

LPOBJ CreateNewObj (BOOL fDoc_Changed)

{

HANDLE hObj = NULL;

LPOBJ lpobj = NULL;

// index into an array of flags indicating if that object

// number is used.

int ifObj = 0;

if ((hObj = LocalAlloc (LMEM_MOVEABLE|LMEM_ZEROINIT,

sizeof (OBJ))) == NULL)

return NULL;

if ((lpobj = (LPOBJ) LocalLock (hObj)) == NULL)

{

LocalFree (hObj);

return NULL;

}

// Fill the fields in the object structure.

// Find an unused number.

for (ifObj=1; ifObj <= cfObjNums; ifObj++)

{

if (docMain.rgfObjNums[ifObj]==FALSE)

{

docMain.rgfObjNums[ifObj]=TRUE;

break;

}

}

if (ifObj==cfObjNums+1)

{

// Cannot create any more objects.

MessageBeep(0);

return NULL;

}

wsprintf (lpobj->native.szName, "Object %d", ifObj);

lpobj->aName = GlobalAddAtom (lpobj->native.szName);

lpobj->hObj = hObj;

lpobj->oleobject.lpvtbl = &objvtbl;

lpobj->native.idmColor = IDM_RED; // Default color

lpobj->native.version = version;

lpobj->native.nWidth = OBJECT_WIDTH; // Default size

lpobj->native.nHeight = OBJECT_HEIGHT;

SetHiMetricFields (lpobj);

// Place object in a location corresponding to its number,

// for aesthetics.

lpobj->native.nX = (ifObj - 1) * 20;

lpobj->native.nY = (ifObj - 1) * 20;

if (!CreateWindow ("ObjClass", "Obj", WS_BORDER | WS_THICKFRAME |

WS_CHILD | WS_CLIPSIBLINGS | WS_VISIBLE, lpobj->native.nX,

lpobj->native.nY, lpobj->native.nWidth, lpobj->native.nHeight,

hwndMain, NULL, hInst, (LPSTR) lpobj ))

return FALSE;

fDocChanged = fDoc_Changed;

return lpobj;

}

/*

* EmbeddingModeOff

*

* Do whatever is necessary for the application to end

* "embedding mode."

*/

void EmbeddingModeOff (void)

{

HMENU hMenu = GetMenu(hwndMain);

// Change File menu so it contains "Save" instead of

// "Update".

ModifyMenu(hMenu, IDM_UPDATE, MF_BYCOMMAND | MF_STRING,

IDM_SAVE, "&Save");

// Change File menu so it contains "Exit & Return to

// <client doc>" instead of just "Exit"

ModifyMenu(hMenu, IDM_EXIT, MF_BYCOMMAND | MF_STRING,

IDM_EXIT, "E&xit");

// Change File menu so it contains "Save As..." instead of

// "Save Copy As..."

ModifyMenu(hMenu, IDM_SAVEAS, MF_BYCOMMAND|MF_STRING,

IDM_SAVEAS, "Save As..");

// In non-embedded mode, the user can create new objects.

EnableMenuItem(hMenu, menuposObject, MF_BYPOSITION | MF_ENABLED);

lstrcpy (szClientDoc, "Client Document");

DrawMenuBar (hwndMain);

}

Shown below is the code supporting the File Open command in SRVRDEMO.EXE (the example references code shown in the preceding code example for File New):

case IDM_OPEN:

OpenDoc();

UpdateObjMenus();

break;

/*

* OpenDoc

*

* Prompt the user for which document he wants to open

*

* RETURNS: TRUE if successful, FALSE otherwise.

*

*/

BOOL OpenDoc (void)

{

char szDoc[cchFilenameMax];

BOOL fUpdateLater;

OLESTATUS olestatus;

if (SaveChangesOption (&fUpdateLater) == IDCANCEL)

return FALSE;

if (!GetFileOpenFilename (szDoc))

{

if (fUpdateLater)

{

// The user chose the "Yes, Update" button but the

// File Open dialog box failed for some reason

// (perhaps the user chose Cancel).

// Even though the user chose "Yes, Update", there is

// no way to update a client that does not accept

// updates except when the document is closed.

}

return FALSE;

}

if (fUpdateLater)

{

// The non-standard OLE client did not accept the update

// when we requested it, so we are sending the client

// OLE_CLOSED now that we are closing the document.

SendDocMsg (OLE_CLOSED);

}

fRevokeSrvrOnSrvrRelease = FALSE;

if ((olestatus = RevokeDoc()) > OLE_WAIT_FOR_RELEASE)

return FALSE;

else if (olestatus == OLE_WAIT_FOR_RELEASE)

Wait (&fWaitingForDocRelease);

fRevokeSrvrOnSrvrRelease = TRUE;

EmbeddingModeOff();

if (!CreateDocFromFile (szDoc, NULL, doctypeFromFile))

{

MessageBox (hwndMain, "Reading from file failed.\r\nFile

may not be in proper file format.",

szAppName, MB_ICONEXCLAMATION | MB_OK);

// We already revoked the document, so give the user a

// new one to edit.

CreateNewDoc (NULL, "(Untitled)", doctypeNew);

CreateNewObj (FALSE);

return FALSE;

}

fDocChanged = FALSE;

return TRUE;

}

.

.

.

/* UpdateObjMenus

*

* Grey or Ungrey menu items depending on the existence of

* at least one object in the document.

*/

static void UpdateObjMenus (void)

{

static BOOL fObjMenusEnabled = TRUE;

BOOL fOneObjExists; // Does an object exist?

WORD wEnable;

HMENU hMenu;

fOneObjExists = (SelectedObjectWindow() != NULL);

if (fOneObjExists == fObjMenusEnabled)

{

// Nothing has changed.

return;

}

wEnable = (fOneObjExists ? MF_ENABLED : MF_GRAYED);

hMenu = GetMenu(hwndMain);

EnableMenuItem(hMenu, menuposColor, MF_BYPOSITION | wEnable);

hMenu = GetSubMenu(GetMenu(hwndMain), menuposFile);

EnableMenuItem(hMenu, IDM_SAVE, MF_BYCOMMAND | wEnable);

EnableMenuItem(hMenu, IDM_SAVEAS, MF_BYCOMMAND | wEnable);

hMenu = GetSubMenu(GetMenu(hwndMain), menuposEdit);

EnableMenuItem(hMenu, IDM_CUT, MF_BYCOMMAND | wEnable);

EnableMenuItem(hMenu, IDM_COPY, MF_BYCOMMAND | wEnable);

EnableMenuItem(hMenu, IDM_DELETE, MF_BYCOMMAND | wEnable);

hMenu = GetSubMenu(GetMenu(hwndMain), menuposObject);

EnableMenuItem(hMenu, IDM_NEXTOBJ, MF_BYCOMMAND | wEnable);

DrawMenuBar (hwndMain);

fObjMenusEnabled = fOneObjExists;

}