CFRAME.CPP

/* 
* CFRAME.CPP
* Sample Code Class Libraries
*
* Generic CFrame class that manages either SDI or MDI clients as
* well as typical File, Edit, Window, and Help commands.
*
* Copyright (c)1993-1996 Microsoft Corporation, All Rights Reserved
*
* Kraig Brockschmidt, Software Design Engineer
* Microsoft Systems Developer Relations
*
* Internet : kraigb@microsoft.com
* Compuserve: >INTERNET:kraigb@microsoft.com
*/


#include <windows.h>
#include <memory.h>

extern "C"
{
#include <commdlg.h>
}

#include "classlib.h"



/*
* CFrame::CFrame
* CFrame::~CFrame
*
* Constructor Parameters:
* hInst HINSTANCE from WinMain
* hInstPrev HINSTANCE from WinMain
* pszCmdLine LPSTR from WinMain
* nCmdShow int from WinMain
*/

CFrame::CFrame(HINSTANCE hInst, HINSTANCE hInstPrev
, LPSTR pszCmdLine, int nCmdShow)
: CWindow(hInst)
{
m_hInstPrev =hInstPrev;
m_nCmdShow =nCmdShow;

#ifdef WIN32
//This gives us the Unicode version if necessary
m_pszCmdLine=GetCommandLine();
#else
m_pszCmdLine=pszCmdLine;
#endif

m_fLastEnable=(BOOL)-1; //Uninitialized
m_fLastPaste =(BOOL)-1; //Uninitialized

m_phMenu=NULL;
m_hBmp =NULL;
m_pST =NULL;

m_pGB =NULL;
m_pSS =NULL;
m_pCL =NULL;
m_pAdv =NULL;
return;
}



CFrame::~CFrame(void)
{
m_fClosing=TRUE;

if (NULL!=m_pAdv)
delete m_pAdv;

//Accelerators freed automatically.

//Free the GizmoBar bitmaps
if (NULL!=m_hBmp)
DeleteObject(m_hBmp);

if (NULL!=m_pCL)
delete m_pCL;

if (NULL!=m_pSS)
delete m_pSS;

if (NULL!=m_pGB)
delete m_pGB;

//Free the menu handle array
if (NULL!=m_phMenu)
delete []((UINT *)m_phMenu);

//Free the stringtable.
if (NULL!=m_pST)
delete m_pST;

m_fClosing=FALSE;
return;
}





/*
* CFrame::FInit
*
* Purpose:
* Initializer for a CFrame object containing anything prone to
* failure.
*
* Parameters:
* pFI PFRAMEINIT containing initialization parameters.
*
* Return Value:
* BOOL TRUE if initialization succeeded, FALSE
* otherwise. If FALSE is returned, the caller must
* guarantee that the destructor is called promptly
* to insure cleanup.
*/

BOOL CFrame::FInit(PFRAMEINIT pFI)
{
RECT rc;
HMENU hMenu;
UINT uTemp;
TOOLDISPLAYDATA tdd;

m_fInit=TRUE;

//1. Create our stringtable
m_pST=new CStringTable(m_hInst);

if (!m_pST->FInit(pFI->idsMin, pFI->idsMax))
return FALSE;


/*
* 2. Register the classes we need for this application.
* We have our main (frame) window, document windows (for
* either MDI or SDI, and Polyline windows which are the
* editing controls. This separate virtual function allows
* applications to add additional classes.
*/
if (NULL==m_hInstPrev)
{
if (!FRegisterAllClasses())
return FALSE;
}


/*
* 3. Create the main window, the gizmobar, the StatStrip,
* and a client that owns documents.
*/
m_pCL=NULL;

m_hWnd=CreateWindow(SZCLASSFRAME, PSZ(IDS_CAPTION)
, WS_MINIMIZEBOX | WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN
, pFI->x, pFI->y, pFI->cx, pFI->cy
, NULL, NULL, m_hInst, this);

if (NULL==m_hWnd)
return FALSE;

GetClientRect(m_hWnd, &rc);

UIToolConfigureForDisplay(&tdd);
m_dxB=tdd.cxButton;
m_dyB=tdd.cyButton;
m_cyBar=tdd.cyBar;

m_pGB=new CGizmoBar(m_hInst);

if (!m_pGB->FInit(m_hWnd, ID_GIZMOBAR, m_cyBar))
return FALSE;


m_pSS=new CStatStrip(m_hInst);

if (!m_pSS->FInit(m_hWnd, ID_STATSTRIP, CYSTATSTRIP))
return FALSE;


//Initialize the StatStrip for automated WM_MENUSELECT processing
if (!m_pSS->MessageMap(m_hWnd, m_hInst, IDR_STATMESSAGEMAP
, pFI->idsStatMin, pFI->idsStatMax, CCHMESSAGEMAX
, pFI->idStatMenuMin, pFI->idStatMenuMax, ID_MESSAGEREADY
, ID_MESSAGEEMPTY, ID_MENUSYS))
return FALSE;


rc.top+=m_cyBar;

m_pCL=CreateCClient();

/*
* 4. Allocate space for the menu handle array and store the
* popup handles. Get the menu handle of the Window menu
* specifically for later processing.
*/

hMenu=GetMenu(m_hWnd);
m_phMenu=new HMENU[pFI->cMenus];

for (uTemp=0; uTemp < pFI->cMenus; uTemp++)
m_phMenu[uTemp]=GetSubMenu(hMenu, uTemp);

#ifdef MDI
//Save this for UpdateMenus. Stays NULL in SDI
m_hMenuWindow=GetSubMenu(hMenu, pFI->iPosWindowMenu);
#endif

if (!m_pCL->FInit(m_hMenuWindow, &rc, this))
return FALSE;


/*
* 5. Initialize fancy things like the gizmobar. If a derived
* class wants more gizmo images, they can copy the first
* two in the standard image set and this code will still
* load it. This code just won't reference it.
*/

m_hBmp=LoadBitmap(m_hInst, MAKEINTRESOURCE(tdd.uIDImages));

if (NULL==m_hBmp)
return FALSE;

//Create all the gizmos on the gizmobar
CreateGizmos();
UpdateGizmos();

m_hAccel=LoadAccelerators(m_hInst
, MAKEINTRESOURCE(IDR_ACCELERATORS));

if (NULL==m_hAccel)
return FALSE;



/*
* 6. Before we possibly create any new documents, create an
* AdviseSink for document notifications.
*/

m_pAdv=new CDocumentAdviseSink(this);


/*
* 7. In the default implementation FPreShowInit does not do
* anything, but is called here to allow derivations to
* hook the function and modify m_nCmdShow before we
* call ShowWindow. This is one such place where OLE affects
* a derivation, because servers will change m_nCmdShow to
* SW_HIDE if started with -Embedding.
*/
if (!FPreShowInit())
return FALSE;


/*
* 8. Handle window visibility appropriately after giving
* FPreShowInit a chance to modify it.
*/
ShowWindow(m_hWnd, m_nCmdShow);
UpdateWindow(m_hWnd);


/*
* 9. Parse the command line and take appropriate action. Again,
* this virtual function is here for apps to override.
*/
ParseCommandLine();

m_fInit=FALSE;
return TRUE;
}






/*
* CFrame::CreateCClient
*
* Purpose:
* Creates a CClient object for use in this frame. This function
* is overrided by derived classes to create a different type of
* CClient.
*
* Parameters:
* None
*
* Return Value:
* PCClient Pointer to the new CClient object.
*/

PCClient CFrame::CreateCClient()
{
return new CClient(m_hInst);
}







/*
* CFrame::FRegisterAllClasses
*
* Purpose:
* Registers all classes used in this application.
*
* Parameters:
* None
*
* Return Value:
* BOOL TRUE if registration succeeded, FALSE otherwise.
*/

BOOL CFrame::FRegisterAllClasses(void)
{
WNDCLASS wc;

//Field that are the same for all windows.
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.hInstance = m_hInst;
wc.cbClsExtra = 0;

//Register the Frame window
wc.lpfnWndProc = FrameWndProc;
wc.cbWndExtra = CBFRAMEWNDEXTRA;
wc.hIcon = LoadIcon(m_hInst, TEXT("Icon"));
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_APPWORKSPACE+1);
wc.lpszMenuName = MAKEINTRESOURCE(IDR_MENU);
wc.lpszClassName = SZCLASSFRAME;

if (!RegisterClass(&wc))
return FALSE;

#ifndef MDI
wc.lpfnWndProc = SDIClientWndProc;
wc.cbWndExtra = CBCLIENTWNDEXTRA;
wc.hIcon = NULL;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_APPWORKSPACE+1);
wc.lpszMenuName = NULL;
wc.lpszClassName = SZCLASSSDICLIENT;

if (!RegisterClass(&wc))
return FALSE;
#endif

wc.lpfnWndProc = DocumentWndProc;
wc.cbWndExtra = CBDOCUMENTWNDEXTRA;
wc.hIcon = LoadIcon(m_hInst
, MAKEINTRESOURCE(IDR_DOCUMENTICON));
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wc.lpszMenuName = NULL;
wc.lpszClassName = SZCLASSDOCUMENT;

if (!RegisterClass(&wc))
return FALSE;

return TRUE;
}





/*
* CFrame::FPreShowInit
*
* Purpose:
* Called from FInit before intially showing the window. We do
* whatever else we want here, modifying nCmdShow as necessary
* which affects ShowWindow in FInit.
*
* Parameters:
* None
*
* Return Value:
* BOOL TRUE if this initialization succeeded, FALSE
* otherwise.
*/

BOOL CFrame::FPreShowInit(void)
{
return TRUE;
}







/*
* CFrame::ParseCommandLine
*
* Purpose:
* Allows the application to parse the command line and take action
* after the window has possibly been shown.
*
* Parameters:
* None
*
* Return Value:
* BOOL TRUE if this function succeeded, FALSE otherwise.
*/

void CFrame::ParseCommandLine(void)
{
LPTSTR psz;
UINT uTemp;
PCDocument pDoc;

psz=PszWhiteSpaceScan(m_pszCmdLine, TRUE);

#ifdef WIN32
//Skip the first argument which is the EXE name.
psz=PszWhiteSpaceScan(psz, FALSE);
psz=PszWhiteSpaceScan(psz, TRUE);
#endif

if ((TCHAR)0!=*psz)
{
pDoc=m_pCL->NewDocument(TRUE, m_pAdv);

//If loading fails, we'll just sit around with a new document
uTemp=pDoc->ULoad(TRUE, psz);
pDoc->ErrorMessage(uTemp);
}
#ifndef MDI
else
{
/*
* In SDI we always want to have an initial document visible,
* so if there was no command line we'll create a new
* untitled document here.
*/
pDoc=m_pCL->NewDocument(TRUE, m_pAdv);

if (NULL!=pDoc)
pDoc->ULoad(TRUE, NULL);
}
#endif

return;
}








/*
* CFrame::CreateGizmos
*
* Purpose:
* Procedure to create all the necessary gizmobar buttons.
*
* Parameters:
* None
*
* Return Value:
* UINT Number of gizmos added to the bar.
*/

UINT CFrame::CreateGizmos(void)
{
UINT uState=GIZMO_NORMAL;
UINT utCmd =GIZMOTYPE_BUTTONCOMMAND;
UINT utEx =GIZMOTYPE_BUTTONATTRIBUTEEX;

//File New, Open, Close, Save, Import
m_pGB->Add(utCmd, 0, IDM_FILENEW, m_dxB, m_dyB, NULL, NULL
, 3, uState);
m_pGB->Add(utCmd, 1, IDM_FILEOPEN, m_dxB, m_dyB, NULL, NULL
, 4, uState);
m_pGB->Add(utCmd, 2, IDM_FILECLOSE, m_dxB, m_dyB, NULL, m_hBmp
, 0, uState);
m_pGB->Add(utCmd, 3, IDM_FILESAVE, m_dxB, m_dyB, NULL, NULL
, 5, uState);

//Separator
m_pGB->Add(GIZMOTYPE_SEPARATOR, 4, 0, 6, m_dyB, NULL, NULL
, 0, uState);

//Edit Undo, Cut, Copy, Paste
m_pGB->Add(utCmd, 5, IDM_EDITUNDO, m_dxB, m_dyB, NULL, m_hBmp
, 1, uState);
m_pGB->Add(utCmd, 6, IDM_EDITCUT, m_dxB, m_dyB, NULL, NULL
, 0, uState);
m_pGB->Add(utCmd, 7, IDM_EDITCOPY, m_dxB, m_dyB, NULL, NULL
, 1, uState);
m_pGB->Add(utCmd, 8, IDM_EDITPASTE, m_dxB, m_dyB, NULL, NULL
, 2, uState);

return 9;
}







/*
* CFrame::MessageLoop
*
* Purpose:
* Spins in a standard message loop (with accelerators) until
* WM_QUIT is found after which it returns.
*
* Parameters:
* None
*
* Return Value:
* WPARAM Contents of msg.wParam from WM_QUIT.
*/

WPARAM CFrame::MessageLoop(void)
{
MSG msg;

while (GetMessage(&msg, NULL, 0,0 ))
{
if (!m_pCL->TranslateAccelerator(&msg))
{
if (!TranslateAccelerator(m_hWnd, m_hAccel, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
}

return msg.wParam;
}






/*
* CFrame::FMessageHook
*
* Purpose:
* Provides a derivation of the base CFrame class to hook all
* messages to the window procedure for special processing.
* WM_COMMAND is NOT sent here as that goes through OnCommand
* instead.
*
* Parameters:
* <WndProc Parameters>
* pLRes LRESULT * in which to store the return value
* for the message.
*
* Return Value:
* BOOL TRUE to prevent further processing, FALSE
* otherwise.
*/

BOOL CFrame::FMessageHook(HWND hWnd, UINT iMsg, WPARAM wParam
, LPARAM lParam, LRESULT *pLRes)
{
*pLRes=0;
return FALSE;
}






/*
* CFrame::OnCommand
*
* Purpose:
* WM_COMMAND handler for the frame window so derivations can
* process their messages and then pass the standard commands (like
* file open and save) on to the base class.
*
* Parameters:
* hWnd HWND of the frame window.
* wParam WPARAM of the message.
* lParam LPARAM of the message.
*
* Return Value:
* LRESULT Return value for the message.
*/

LRESULT CFrame::OnCommand(HWND hWnd, WPARAM wParam, LPARAM lParam)
{
PCDocument pDoc;
TCHAR szFile[CCHPATHMAX];
BOOL fOK;
UINT uTemp;
UINT uType;
PCHourglass pHour;

COMMANDPARAMS(wID, wCode, hWndMsg);

/*
* Don't bother with anything during first initialization,
* skipping many GizmoBar notifications.
*/
if (m_fInit)
return 0L;

pDoc=m_pCL->ActiveDocument();


switch (wID)
{
case IDM_FILENEW:
if (!m_pCL->SDIVerify())
break;

pHour=new CHourglass;
pDoc=m_pCL->NewDocument(TRUE, m_pAdv);

//Insure the document is untitled.
if (NULL!=pDoc)
pDoc->ULoad(TRUE, NULL);

delete pHour;
break;


case IDM_FILEOPEN:
if (!m_pCL->SDIVerify())
break;

szFile[0]=0;
fOK=FSaveOpenDialog(szFile, CCHPATHMAX, IDS_FILEOPEN
, TRUE, &uType);

if (!fOK)
return 0L;

pDoc=m_pCL->NewDocument(FALSE, m_pAdv);

if (NULL==pDoc)
return 0L;

pHour=new CHourglass;
uTemp=pDoc->ULoad(TRUE, szFile);
delete pHour;

pDoc->ErrorMessage(uTemp);

//Close the new doc on failure, show on success.
if (DOCERR_NONE!=uTemp)
m_pCL->CloseDocument(pDoc);
else
m_pCL->ShowDocument(pDoc, TRUE);

return (DOCERR_NONE==uTemp);


case IDM_FILECLOSE:
//Ask if you want to save.
if (!m_pCL->FCleanVerify(pDoc))
return 0L;

m_pCL->CloseDocument(pDoc);
UpdateGizmos();
break;


case IDM_FILESAVE:
//If we get this from the gizmobar, we may need Save As
fOK=pDoc->FQuerySave();

if (fOK)
{
//Save using current document name and version
pHour=new CHourglass;
uTemp=pDoc->USave(0, NULL);
delete pHour;

pDoc->ErrorMessage(uTemp);

//This return code is for CFrame::FAskAndSave
return (DOCERR_NONE==uTemp);
}

//FALL through to File/Save As for no name documents.


case IDM_FILESAVEAS:
//Go get a filename, then save it.
pDoc->FilenameGet(szFile, CCHPATHMAX);

fOK=FSaveOpenDialog(szFile, CCHPATHMAX, IDS_FILESAVEAS
, FALSE, &uType);

if (!fOK)
return 0L;

uTemp=pDoc->USave(uType, szFile);
pDoc->ErrorMessage(uTemp);

return (DOCERR_NONE==uTemp);


case IDM_FILEEXIT:
PostMessage(hWnd, WM_CLOSE, 0, 0L);
break;


case IDM_EDITCUT:
case IDM_EDITCOPY:
pDoc->FClip(hWnd, (IDM_EDITCUT==wID));

//Update the gizmobar as appropriate.
m_pGB->Enable(IDM_EDITPASTE, pDoc->FQueryPaste());
break;


case IDM_EDITUNDO:
pDoc->Undo();
break;

case IDM_EDITPASTE:
pDoc->FPaste(hWnd);
break;


//These commands don't happen in SDI builds; not on the menu
case IDM_WINDOWCASCADE:
m_pCL->OnWindowCommand(WM_MDICASCADE, 0);
break;

case IDM_WINDOWTILEHORZ:
m_pCL->OnWindowCommand(WM_MDITILE, MDITILE_HORIZONTAL);
break;

case IDM_WINDOWTILEVERT:
m_pCL->OnWindowCommand(WM_MDITILE, MDITILE_VERTICAL);
break;

case IDM_WINDOWICONS:
m_pCL->OnWindowCommand(WM_MDIICONARRANGE, 0);
break;

case IDM_HELPABOUT:
DialogBox(m_hInst, MAKEINTRESOURCE(IDD_ABOUT)
, m_hWnd, AboutProc);
break;

default:
return m_pCL->DefaultFrameProc(hWnd, WM_COMMAND, wParam
, lParam);
}

return 0L;
}





/*
* CFrame::OnDocumentDataChange
*
* Purpose:
* Most of the CDocumentAdviseSink notifications we get are fairly
* standard implementations. When data in the document changes,
* however, there's probably more special things we can do, so we
* keep this simple case simple with this hook, not forcing a
* derived class to reimplement CDocumentAdviseSink.
*
* Parameters:
* pDoc PCDocument notifying the sink.
*
* Return Value:
* None
*/

void CFrame::OnDocumentDataChange(PCDocument pDoc)
{
return;
}





/*
* CFrame::OnDocumentActivate
*
* Purpose:
* Most of the CDocumentAdviseSink notifications we get are fairly
* standard implementations. When the current document changes,
* however, there's probably more UI that frame derivations need
* to do, so we allow the hook here.
*
* Parameters:
* pDoc PCDocument notifying the sink.
*
* Return Value:
* None
*/

void CFrame::OnDocumentActivate(PCDocument pDoc)
{
return;
}






/*
* CFrame::FSaveOpenDialog
*
* Purpose:
* Invokes the COMMDLG.DLL GetOpenFileName dialog and retrieves
* a filename for saving or opening.
*
* Parameters:
* pszFile LPTSTR buffer to receive the entered filename.
* cchFile UINT length of pszFile
* idsCaption UINT of string to use in the caption bar.
* fOpen BOOL indicating if we want file open or save.
* puType UINT * in which we store the selected type.
* Can be NULL.
*
* Return Value:
* BOOL TRUE if the function retrieved a filename,
* FALSE if the user pressed CANCEL.
*/

BOOL CFrame::FSaveOpenDialog(LPTSTR pszFile, UINT cchFile
, UINT idsCaption, BOOL fOpen, UINT *puType)
{
OPENFILENAME ofn;
TCHAR szFilter[80];
UINT cch;
BOOL fRet;
#ifdef DEBUG
DWORD dwErr;
#endif

if (NULL==pszFile)
return FALSE;

memset(&ofn, 0, sizeof(OPENFILENAME));
ofn.lStructSize =sizeof(OPENFILENAME);
ofn.hwndOwner =m_hWnd;

if (fOpen)
lstrcpy(szFilter, PSZ(IDS_FILEOPENFILTER));
else
lstrcpy(szFilter, PSZ(IDS_FILESAVEFILTER));

cch=lstrlen(szFilter);
ReplaceCharWithNull(szFilter, szFilter[cch-1]);

ofn.lpstrFilter =szFilter;
ofn.nFilterIndex =1L;

ofn.lpstrTitle =PSZ(idsCaption);
ofn.lpstrFile =pszFile;
ofn.nMaxFile =cchFile;

ofn.lpstrDefExt =PSZ(IDS_DEFEXT);

if (fOpen)
{
ofn.Flags=OFN_HIDEREADONLY | OFN_FILEMUSTEXIST;
fRet=GetOpenFileName(&ofn);
}
else
{
ofn.Flags=OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT;
fRet=GetSaveFileName(&ofn);
}

if (fRet || NULL!=puType)
*puType=(UINT)ofn.nFilterIndex;

#ifdef DEBUG
dwErr=CommDlgExtendedError();
#endif
return fRet;
}






/*
* CFrame::ReplaceCharWithNull
*
* Purpose:
* Walks a null-terminated string and replaces a given character
* with a zero for use with the FSaveOpenDialog member.
*
* Parameters:
* psz LPTSTR to the string to process.
* ch int character to replace.
*
* Return Value:
* int Number of characters replaced. -1 if psz is NULL
*/

UINT CFrame::ReplaceCharWithNull(LPTSTR psz, int ch)
{
UINT cChanged=0;

if (NULL==psz)
return 0;

while ((TCHAR)0!=*psz)
{
if (ch==*psz)
{
*psz=0;
cChanged++;
}

psz++;
}

return cChanged;
}






/*
* CFrame::PszWhiteSpaceScan
*
* Purpose:
* Skips characters in a string until a whitespace or non-whitespace
* character is seen. Whitespace is defined as \n, \r, \t, or ' '.
*
* Parameters:
* psz LPTSTR to string to manipulate
* fSkip BOOL TRUE if we want to skip whitespace.
* FALSE if we want to skip anything but whitespace.
*
* Return Value:
* LPTSTR Pointer to first character in the string that
* either non-whitespace (fSkip=TRUE) or
* whitespace (fSkip=FALSE), which may be the
* null terminator.
*/

LPTSTR CFrame::PszWhiteSpaceScan(LPTSTR psz, BOOL fSkip)
{
TCHAR ch;
BOOL fWhite;

while (ch=*psz)
{
//Not too sure how this localizes...
fWhite=(TEXT('\n')==ch || TEXT('\r')==ch
|| TEXT('\t')==ch || TEXT(' ')==ch);

//Too bad C doesn't have a logical XOR (^^) operator.
if ((fSkip && !fWhite) || (!fSkip && fWhite))
break;

psz++;
}

return psz;
}






//PUBLIC FUNCTIONS




/*
* CFrame::FAskAndSave
*
* Purpose:
* If a document is closed and is dirty the client window we own
* will get the document's filename and call us here. We will
* ask the user if they want to save that file and if so, send a
* message to our frame window to execute the Save command.
*
* Parameters:
* pszDoc LPTSTR name of the document. If the first
* character is 0 then we use (Untitled).
*
* Return Value:
* BOOL TRUE if the file was saved, FALSE otherwise.
*/

BOOL CFrame::FAskAndSave(LPTSTR pszDoc)
{
BOOL fRet=TRUE;
UINT uRet;
TCHAR szTemp[CCHFILENAMEMAX+100];

if (NULL==pszDoc)
return FALSE;

if ((TCHAR)0==*pszDoc)
pszDoc=PSZ(IDS_UNTITLED);
else
{
GetFileTitle(pszDoc, szTemp, CCHFILENAMEMAX);
lstrcpy(pszDoc, szTemp);
}

wsprintf(szTemp, PSZ(IDS_FILEDIRTY), pszDoc);

uRet=MessageBox(m_hWnd, szTemp, PSZ(IDS_CAPTION)
, MB_YESNOCANCEL | MB_ICONEXCLAMATION);

/*
* If the user wants to save, tell the window to execute the
* command.
*/
if (IDYES==uRet)
return (BOOL)SendCommand(m_hWnd, IDM_FILESAVE, 0, 0);

//TRUE for No, False for Cancel
return (IDCANCEL!=uRet);
}





/*
* CFrame::UpdateMenus
*
* Purpose:
* Handles the WM_INITMENU message for the frame window. Depending
* on the existence of an active window, menu items are selectively
* enabled and disabled.
*
* Parameters:
* hMenu HMENU of the menu to intialize
* iMenu UINT position of the menu.
*
* Return Value:
* None
*/

void CFrame::UpdateMenus(HMENU hMenu, UINT iMenu)
{
PCDocument pDoc;
BOOL fOK=FALSE;
UINT uTemp;
UINT uTempE;
UINT uTempD;

pDoc=m_pCL->ActiveDocument();

uTempE=MF_ENABLED | MF_BYCOMMAND;
uTempD=MF_DISABLED | MF_GRAYED | MF_BYCOMMAND;
uTemp=((NULL!=pDoc) ? uTempE : uTempD);

/*
* File menu: If there is no current document window, disable
* Close, Save, and Save As. If there is a document but
* it doesn't have a filename, disable save.
*/
if (m_phMenu[0]==hMenu)
{
EnableMenuItem(hMenu, IDM_FILECLOSE, uTemp);
EnableMenuItem(hMenu, IDM_FILESAVE, uTemp);
EnableMenuItem(hMenu, IDM_FILESAVEAS, uTemp);

if (NULL!=pDoc)
fOK=pDoc->FQuerySave();

EnableMenuItem(hMenu, IDM_FILESAVE, (fOK) ? uTempE : uTempD);
}


/*
* Edit menus: If there's no document, disable all of it.
* If there's a document but no clipboard format available,
* disable paste only.
*/
if (m_phMenu[1]==hMenu)
{
EnableMenuItem(hMenu, IDM_EDITUNDO, uTemp);
EnableMenuItem(hMenu, IDM_EDITCOPY, uTemp);
EnableMenuItem(hMenu, IDM_EDITCUT, uTemp);

/*
* Paste has two dependencies; format available and an open
* document
*/
if (NULL!=pDoc)
fOK=pDoc->FQueryPaste();

fOK &=(uTemp==uTempE);
EnableMenuItem(hMenu,IDM_EDITPASTE, (fOK) ? uTempE : uTempD);
}


#ifdef MDI
//Window menu: no document, no commands
if (m_hMenuWindow==hMenu)
{
EnableMenuItem(hMenu, IDM_WINDOWCASCADE, uTemp);
EnableMenuItem(hMenu, IDM_WINDOWTILEHORZ, uTemp);
EnableMenuItem(hMenu, IDM_WINDOWTILEVERT, uTemp);
EnableMenuItem(hMenu, IDM_WINDOWICONS, uTemp);
}
#endif

return;
}









/*
* CFrame::UpdateGizmos
*
* Purpose:
* Enables and disables gizmos depending on whether we have
* a document or not and depending on clipboard format availability.
*
* Parameters:
* None
*
* Return Value:
* None
*/

void CFrame::UpdateGizmos(void)
{
PCDocument pDoc;
BOOL fEnable;

pDoc=m_pCL->ActiveDocument();
fEnable=(NULL!=pDoc);

if (m_fLastEnable!=fEnable)
{
m_fLastEnable=fEnable;

//No document, disable just about everything
m_pGB->Enable(IDM_FILECLOSE, fEnable);
m_pGB->Enable(IDM_FILESAVE, fEnable);

m_pGB->Enable(IDM_EDITUNDO, fEnable);
m_pGB->Enable(IDM_EDITCUT, fEnable);
m_pGB->Enable(IDM_EDITCOPY, fEnable);
}

//Special handling: clipboard format available, enable paste
if (NULL!=pDoc)
fEnable &= pDoc->FQueryPaste();

//Paste is not enabled unless there's a document too
if (m_fLastPaste!=fEnable)
{
m_fLastPaste=fEnable;
m_pGB->Enable(IDM_EDITPASTE, fEnable);
}

return;
}








/*
* CFrame::WindowTitleSet
*
* Purpose:
* Handles changing the caption bar of a document window.
*
* Parameters:
* pDoc PCDocument of the document affected. If NULL,
* then we remove titles.

*  fDocTitle       BOOL indicating to set the document or the main 
* window captions. In MDI we may do either, this
* is ignored in SDI.
*
* Return Value:
* None
*/

void CFrame::WindowTitleSet(PCDocument pDoc, BOOL fDocTitle)
{
TCHAR szTitle[CCHPATHMAX];
TCHAR szFile[CCHPATHMAX];

/*
* Go grab the filename. If we get an emtpy file back, then
* we'll use (Untitled). If we have a NULL document, then we'll
* just set the title of the app back to having no filename.
*/
if (NULL!=pDoc)
{
pDoc->FilenameGet(szTitle, CCHPATHMAX);

if ((TCHAR)0==szTitle[0])
lstrcpy(szFile, PSZ(IDS_UNTITLED));
else
{
GetFileTitle(szTitle, szFile, sizeof(szFile));
#ifndef WIN32
AnsiUpper(szFile);
#endif
}
}
else
szFile[0]=0;

#ifndef MDI
//SDI always titles the application window
fDocTitle=FALSE;
#endif

if (fDocTitle)
{
if (NULL!=pDoc)
SetWindowText(pDoc->Window(), szFile);
}
else
{
if ((TCHAR)0!=szFile[0])
{
wsprintf(szTitle, TEXT("%s - %s"), PSZ(IDS_CAPTION)
, (LPTSTR)szFile);
SetWindowText(m_hWnd, szTitle);
}
else
SetWindowText(m_hWnd, PSZ(IDS_CAPTION));
}

return;
}




/*
* CFrame::GetStatusControl (inline)
*
* Purpose:
* Returns the StatStrip object pointer to the caller so they can
* display status messages.
*
* Parameters:
* None
*
*
* Return Value:
* PCStatStrip Pointer to the status strip object.
*/

PCStatStrip inline CFrame::GetStatusControl(void)
{
return m_pSS;
}