CCLIENT.CPP

/* 
* CCLIENT.CPP
* Sample Code Class Libraries
*
* Implementation of the CClient class that handles an SDI or MDI
* client area window.
*
* 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 "classlib.h"
#include <dbgout.h>


/*
* CClient::CClient
* CClient::~CClient
*
* Constructor Parameters:
* hInst HINSTANCE of the application.
*/

CClient::CClient(HINSTANCE hInst)
: CWindow(hInst)
{
m_pFR=NULL;
m_cDoc=0;
m_pDocLast=NULL;

return;
}


CClient::~CClient(void)
{
return;
}







/*
* CClient::FInit
*
* Purpose:
* Creates a client area window sensitive to SDI or MDI
* (compile-time).
*
* Parameters:
* hMenuWindow HWND of the Window menu on the frame.
* pRect LPRECT containing the desired window rectangle.
* pFR PCFrame to which this client belongs.
*
* Return Value:
* BOOL TRUE if the function succeeded, FALSE otherwise.
*/

BOOL CClient::FInit(HMENU hMenuWindow, LPRECT pRect, PCFrame pFR)
{
HWND hWndFrame;
#ifdef MDI
CLIENTCREATESTRUCT ccs;
#endif

m_pFR=pFR;

hWndFrame=m_pFR->Window();

#ifdef MDI

ccs.hWindowMenu =hMenuWindow;
ccs.idFirstChild=ID_MDICHILDMIN;

//Create the MDI client filling the client area
m_hWnd=CreateWindow(TEXT("mdiclient"), TEXT("mdiclient")
, MDIS_ALLCHILDSTYLES | WS_CHILD | WS_CLIPCHILDREN
| WS_VISIBLE | WS_CLIPSIBLINGS
, pRect->left, pRect->top, pRect->right-pRect->left
, pRect->bottom-pRect->top, hWndFrame, NULL, m_hInst
, &ccs);

#else

//SDI case, create out own SDI Client window
m_hWnd=CreateWindow(SZCLASSSDICLIENT, SZCLASSSDICLIENT
, WS_CHILD | WS_CLIPCHILDREN | WS_VISIBLE | WS_CLIPSIBLINGS
, pRect->left, pRect->top, pRect->right-pRect->left
, pRect->bottom-pRect->top, hWndFrame, NULL, m_hInst, 0L);

#endif

return (NULL!=m_hWnd);
}





/*
* CClient::CreateDoc (Internal)
* CClient::CreateCDocument (Virtual)
*
* Purpose:
* Constructs a new document. This function is overridable so an
* app can use the default CClient but create a derived document
* class.
*
* Parameters:
* None
*
* Return Value:
* PCDocument Pointer to the new document object.
*/

PCDocument CClient::CreateDoc(void)
{
PCDocument pDoc;

pDoc=CreateCDocument();
m_pDocLast=pDoc;

return pDoc;
}

PCDocument CClient::CreateCDocument(void)
{
return new CDocument(m_hInst, m_pFR);
}






/*
* CClient::TranslateAccelerator
*
* Purpose:
* Provides an isolated system accelerator translation as necessary
* for the client window. In MDI this calls TranslateMDISysAccel
* but is a NOP in SDI.
*
* Parameters:
* pMsg LPMSG to the message from GetMessage
*
* Return Value:
* BOOL TRUE if the accelerator was translated and
* processed, FALSE otherwise.
*/

BOOL CClient::TranslateAccelerator(LPMSG pMsg)
{
#ifdef MDI
return TranslateMDISysAccel(m_hWnd, pMsg);
#else
return FALSE;
#endif
}





/*
* CClient::DefFrameProc
*
* Purpose:
* Encapsulates which default message procedure to call for a frame
* window: SDI (DefWindowProc) or MDI (DefFrameProc).
*
* Parameters:
* hWnd HWND of the frame
* iMsg UINT of the message
* wParam WPARAM of the message
* lParam LPARAM of the message
*
* Return Value:
* LRESULT Return value for the message.
*/

LRESULT CClient::DefaultFrameProc(HWND hWnd, UINT iMsg, WPARAM wParam
, LPARAM lParam)
{
#ifdef MDI
return (DefFrameProc(hWnd, m_hWnd, iMsg, wParam, lParam));
#else
return (DefWindowProc(hWnd, iMsg, wParam, lParam));
#endif
}





/*
* CClient::OnWindowCommand
*
* Purpose:
* Handles Window menu commands for MDI situations. This is a NOP
* in SDI.
*
* Parameters:
* uCommand UINT command to execute:
* WM_MDICASCADE
* WM_MDITILE
* WM_MDIICONARRANGE
*
* uOption UINT optional parameter for WM_MDITILE, either
* MDITILE_HORIZONTAL or MDITILE_VERTICAL.
*
* Return Value:
* None
*/

void CClient::OnWindowCommand(UINT uCommand, UINT uOption)
{
SendMessage(m_hWnd, uCommand, (WPARAM)uOption, 0L);
return;
}







/*
* CClient::OnSize
*
* Purpose:
* Handles resizing the client window when the frame is resized.
*
* Parameters:
* x, y UINT new location of the window.
* cx, cy UINT new dimensions of the window.
*
* Return Value:
* None
*/

void CClient::OnSize(UINT x, UINT y, UINT cx, UINT cy)
{
SetWindowPos(m_hWnd, NULL, x, y, cx, cy
, SWP_NOZORDER | SWP_NOACTIVATE);
return;
}






/*
* CClient::NewDocument
*
* Purpose:
* Creates a new blank document in the client space. See
* CloseDocument for the opposite effect.
*
* Parameters:
* fVisible BOOL indicating if the document is to be visible
* or not.
* pAdv PCDocumentAdviseSink to set with the new document
* for notifications. Can be NULL.
*
* Return Value:
* PCDocument Pointer to the new document object.
*/

PCDocument CClient::NewDocument(BOOL fVisible
, PCDocumentAdviseSink pAdv)
{
MDICREATESTRUCT mcs;
HWND hWndDoc;
PCDocument pDoc;
DOCUMENTINIT di;
BOOL fCreate=TRUE;
#ifdef MDI
DWORD dw;
#endif

#ifdef MDI
//In MDI we create a new CDocument
pDoc=CreateDoc(); //This could create a derived class...
#else
//In SDI we close the one we have and create a new one.
pDoc=ActiveDocument();

if (NULL!=pDoc)
CloseDocument(pDoc);

pDoc=CreateDoc();
#endif

if (fCreate)
{
/*
* We implement this by having the client window actually
* create the windows instead of using the CDocument
* initializer. Reason being is that we ask the client to
* actually create the window using WM_MDICREATE (which works
* for our SDI client as well. Since we need to conditionally
* compile for MDI or SDI here, we create a window before
* calling the document constructor.
*/

mcs.szTitle=TEXT("");
mcs.szClass=SZCLASSDOCUMENT;
mcs.hOwner =m_hInst;
mcs.lParam =(LPARAM)pDoc;

mcs.x =CW_USEDEFAULT;
mcs.cx=CW_USEDEFAULT;
mcs.y =CW_USEDEFAULT;
mcs.cy=CW_USEDEFAULT;

/*
* Set the style of the window, controlling visiblity.
* WS_CLIPCHILDREN is important to prevent unnecessary
* flashing of document contents that we'll usually fill
* with some editor window.
*/
#ifdef MDI
mcs.style=WS_CHILD | WS_SYSMENU | WS_CAPTION
| WS_CLIPSIBLINGS| WS_THICKFRAME | WS_MINIMIZEBOX
| WS_MAXIMIZEBOX| WS_CLIPCHILDREN
| ((fVisible) ? WS_VISIBLE : 0L);

//If the current document is maxmized, maximize this one
#ifdef WIN32
dw=GetWindowLong(m_hWnd, GWL_STYLE);
mcs.style |= (dw & WS_MAXIMIZE);
#else
dw=SendMessage(m_hWnd, WM_MDIGETACTIVE, 0, 0L);

if (HIWORD(dw))
mcs.style |= WS_MAXIMIZE;
#endif //WIN32

#else
mcs.style=WS_CHILD | WS_CLIPCHILDREN
| ((fVisible) ? WS_VISIBLE : 0L);
#endif //MDI

//Tell the Client window to create the child
hWndDoc=(HWND)(UINT)SendMessage(m_hWnd, WM_MDICREATE, 0
, (LONG)&mcs);


di.idsMin=IDS_STANDARDDOCMIN;
di.idsMax=IDS_STANDARDDOCMAX;
di.hWndDoc=hWndDoc;
di.pAdv=pAdv;

if (!pDoc->FInit(&di))
{
SendMessage(m_hWnd, WM_MDIDESTROY, (WPARAM)hWndDoc, 0L);
delete pDoc;
return NULL;
}
}

m_cDoc++;

//Update menus and gizmos for the new document if visible
if (fVisible)
m_pFR->UpdateGizmos();

return pDoc;
}







/*
* CClient::ActiveDocument
*
* Purpose:
* Returns the active document window (encapsulates WM_MDIGETACTIVE)
*
* Parameters:
* None
*
* Return Value:
* PCDocument Pointer to the active document object.
*/

PCDocument CClient::ActiveDocument(void)
{
PCDocument pDoc=NULL;
HWND hWnd;

hWnd=(HWND)(UINT)SendMessage(m_hWnd, WM_MDIGETACTIVE, 0, 0L);

if (NULL!=hWnd)
pDoc=(PCDocument)SendMessage(hWnd, DOCM_PDOCUMENT, 0, 0L);
else
pDoc=m_pDocLast;

return pDoc;
}




/*
* CClient::ShowDocument
*
* Purpose:
* Shows or hides a document.
*
* Parameters:
* pDoc PCDocument to show or hide.
* fShow BOOL indicating whether to show or hide the
* document.
*
* Return Value:
* BOOL Previous shown state of the document.
*/

BOOL CClient::ShowDocument(PCDocument pDoc, BOOL fShow)
{
BOOL fRet;

if (NULL==pDoc)
return FALSE;

fRet=IsWindowVisible(pDoc->Window());

ShowWindow(pDoc->Window(), fShow ? SW_SHOW : SW_HIDE);

MDIREFRESHMENU(m_hWnd); //Macro in book1632.h
DrawMenuBar(m_pFR->Window());

SendMessage(m_hWnd, WM_MDIACTIVATE, (WPARAM)pDoc->Window(), 0L);

//Update gizmos if we're *changing* a document's visibility
if (fShow != fRet)
m_pFR->UpdateGizmos();

return fRet;
}






/*
* CClient::SDIVerify
*
* Purpose:
* In MDI, this is a NOP, but in SDI checks if the user has dirtied
* and wants to save the current document before blasting it away.
*
* Parameters:
* None
*
* Return Value:
* BOOL TRUE if the operation calling us can proceed,
* FALSE to abort the operation.
*/

BOOL CClient::SDIVerify(void)
{
#ifdef MDI
return TRUE;
#else
PCDocument pDoc;

pDoc=ActiveDocument();

/*
* In SDI, we'll erase the current, so verify if we have one
* before continuing. If we don't have any document, then we
* don't have a problem.
*/

if (NULL==pDoc)
return TRUE;

return FCleanVerify(pDoc);
#endif
}






/*
* CClient::CloseDocument
*
* Purpose:
* Closes a document created with NewDocument.
*
* Parameters:
* pDoc PCDocument of the document to close.
*
* Return Value:
* UINT New count of open documents.
*/

UINT CClient::CloseDocument(PCDocument pDoc)
{
HWND hWndT;

if (NULL==pDoc)
return m_cDoc;

/*
* Again, since the client window controlled document creation
* we destroy the document window here instead of asking the
* document to do it for us. Once we're rid of the window then
* we can use the destructor.
*/

hWndT=pDoc->Window();

//Don't delete unowned windows.
if (GetParent(hWndT)!=m_hWnd)
return m_cDoc;

m_cDoc--;

//Update window text. Maxed MDI windows handled automatically.
#ifdef MDI
m_pFR->WindowTitleSet(NULL, TRUE);
#else
m_pFR->WindowTitleSet(NULL, FALSE);
#endif

if (m_pDocLast==pDoc)
m_pDocLast=NULL;

//Let the document clean up first, then we can nuke the window.
SendMessage(m_hWnd, WM_MDIDESTROY, (WPARAM)hWndT, 0L);
delete pDoc;

return m_cDoc;
}







/*
* CClient::QueryCloseAllDocuments
*
* Purpose:
* Ask every document window we have in us if we can close them
* down. We use this when exiting the entire application.
*
* Parameters:
* fClose BOOL indicating if we should query close on the
* documents or acutally close them.
*
* Return Value:
* BOOL TRUE if we can close, FALSE otherwise.
*/

BOOL CClient::QueryCloseAllDocuments(BOOL fClose)
{
PCDocument pDoc;
HWND hWndT;
HWND hPrevClose=NULL;

hWndT=GetWindow(m_hWnd, GW_CHILD);

while (NULL!=hWndT)
{
//Cache these: hWndT might be destroyed in closing hPrevClose
HWND hWndOwner=GetWindow(hWndT, GW_OWNER);
HWND hWndNext =GetWindow(hWndT, GW_HWNDNEXT);

if (NULL!=hPrevClose)
{
pDoc=(PCDocument)SendMessage(hPrevClose, DOCM_PDOCUMENT
, 0, 0L);
CloseDocument(pDoc);
hPrevClose=NULL;
}

//Skip icon title windows when saving hPrevClose.
if (NULL==hWndOwner)
{
pDoc=(PCDocument)SendMessage(hWndT, DOCM_PDOCUMENT
, 0, 0L);

if (fClose)
{
if (!FCleanVerify(pDoc))
return FALSE;

//Delay closing this.
hPrevClose=hWndT;
}
else
{
if (0==SendMessage(hWndT, WM_QUERYENDSESSION, 0, 0L))
return FALSE;
}
}

hWndT=hWndNext;
}

//Close the last window as necessary.
if (fClose && IsWindow(hPrevClose))
{
pDoc=(PCDocument)SendMessage(hPrevClose, DOCM_PDOCUMENT
, 0, 0L);
CloseDocument(pDoc);
hPrevClose=NULL;
}

return TRUE;
}







/*
* CClient::FCleanVerify
*
* Purpose:
* Checks if the document under scrutiny is dirty or not. If so,
* then we ask the user if they want to save it. If not, then we
* just return TRUE.
*
* Parameters:
* pDoc PCDocument under consideration.
*
* Return Value:
* BOOL TRUE if the document is clean, saved, or the user
* doesn't want to save it. FALSE if the user
* presses Cancel or the document was not saved.
*/

BOOL CClient::FCleanVerify(PCDocument pDoc)
{
TCHAR szFile[CCHPATHMAX];
LPTSTR psz=szFile;

if (NULL==pDoc)
return TRUE;

//Nothing to do if we're clean.
if (!pDoc->FDirtyGet())
return TRUE;

//Get the filename and send to the frame for asking the question.
*psz=0;
pDoc->FilenameGet(psz, CCHPATHMAX);

return m_pFR->FAskAndSave(psz);
}






/*
* SDIClientWndProc
*
* Purpose:
* SDI client window class that will create one document for us,
* emulating the functions of an MDI client but with only one doc.
*/

LRESULT APIENTRY SDIClientWndProc(HWND hWnd, UINT iMsg
, WPARAM wParam, LPARAM lParam)
{
HWND hWndDoc;
LPMDICREATESTRUCT pMCS;
RECT rc;

hWndDoc=(HWND)GetWindowLong(hWnd, CLIENTWL_HWNDDOC);

switch (iMsg)
{
case WM_CREATE:
SetWindowLong(hWnd, CLIENTWL_HWNDDOC, 0L);
break;

case WM_SIZE:
if (NULL!=hWndDoc)
{
//Change the document window to match
SetWindowPos(hWndDoc, NULL, 0, 0, LOWORD(lParam)
, HIWORD(lParam), SWP_NOMOVE | SWP_NOZORDER
| SWP_NOACTIVATE);
}
break;


case WM_MDICREATE:
pMCS=(LPMDICREATESTRUCT)lParam;

//We only ate one *visible* document in SDI cases.
if (NULL!=hWndDoc && (WS_VISIBLE & pMCS->style))
return (LONG)(UINT)hWndDoc;

/*
* For our one visible window, we set this as the active
* window. For hidden windows, we return their window
* handle but don't change the 'active' window we store.
*
* Note that we force SDI documents to fill the client
* area.
*/

GetClientRect(hWnd, &rc);

hWndDoc=CreateWindowEx(WS_EX_NOPARENTNOTIFY
, pMCS->szClass, pMCS->szTitle, pMCS->style
, rc.left, rc.top, rc.right-rc.left, rc.bottom-rc.top
, hWnd, (HMENU)ID_DOCUMENT, (HINSTANCE)pMCS->hOwner
, pMCS);


if (WS_VISIBLE & pMCS->style)
{
ShowWindow(hWndDoc, SW_SHOW);
SetWindowLong(hWnd, CLIENTWL_HWNDDOC
, (LONG)(UINT)hWndDoc);
}

return (LONG)(UINT)hWndDoc;


case WM_MDIACTIVATE:
/*
* Make the new window the active one. The NEWMDIACTIVE
* macro is wParam in Win16, lParam in Win32.
*/
SetWindowLong(hWnd, CLIENTWL_HWNDDOC,(LONG)NEWMDIACTIVE);
break;


case WM_MDIGETACTIVE:
return (LONG)(UINT)hWndDoc;


case WM_MDIDESTROY:
//The only windows we should destroy are children of us.
if (GetParent((HWND)wParam)==hWnd)
{
DestroyWindow((HWND)wParam);

/*
* If this is the visible window, clear out the
* window word.
*/
if ((HWND)wParam==hWndDoc)
SetWindowLong(hWnd, CLIENTWL_HWNDDOC, 0L);
}
break;

default:
return DefWindowProc(hWnd, iMsg, wParam, lParam);
}

return 0L;
}