#define _FRAME_C
//----------------------------------------------------------------- 
// APP.C - <description> 
// 
// MAKEMDI adaptation of Windows 3.1 SDK MAKEAPP system. 
// 
// MDI application design based on Chapter 7 of	 
// "Windows 3: A Developer's Guide" by Jeffrey Richter. 
// 
// Adaptation developed with permission of the author by  
// John F. Holliday, Technisoft Corporation 
// Telephone: (515) 472-9803, CompuServe: 71271,634
//
// [DMM]	25-Nov-1992: Fixed crashing on exit
//			Also tabified file to tabsize of 4
//
//			David M. Miller, Business Visions, Inc.
//			Telephone: (212) 747-6118
//			CompuServe: 72676,327
//			internet: dmiller@hera.sbi.com
//----------------------------------------------------------------- 
#include "makemdi.h"


BOOL Frame_Initialize(APP * papp)
{
	WNDCLASS cls;

	cls.hCursor = LoadCursor(NULL, IDC_ARROW);
	cls.hIcon = LoadIcon(papp->hinst, MAKEINTRESOURCE(IDR_FRAMEICON));
	cls.lpszMenuName = MAKEINTRESOURCE(IDR_FRAMEMENU);
	cls.hInstance = papp->hinst;
	cls.lpszClassName = CLASS_FRAME;
	cls.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1);
	cls.lpfnWndProc = Frame_WndProc;
	cls.style = CS_HREDRAW | CS_VREDRAW;
	cls.cbWndExtra = sizeof(FRAME *);
	cls.cbClsExtra = 0;

	if (!RegisterClass(&cls))
		return FALSE;

	return TRUE;
}

void Frame_Terminate(APP * papp)
{
}

LRESULT CALLBACK _export Frame_WndProc(HWND hWnd,
									   UINT msg,
									   WPARAM wParam,
									   LPARAM lParam)
{
	LRESULT lResult = 0;
	FRAME *pfrm = Frame_GetPtr(hWnd);

	if (pfrm == NULL) {
		if (msg == WM_NCCREATE) {
			pfrm = (FRAME *) LocalAlloc(LPTR, sizeof(FRAME));

			if (pfrm == NULL)
				return (LRESULT) FALSE;

			pfrm->hWnd = hWnd;
			Frame_SetPtr(hWnd, pfrm);
		}
		else
			return DefWindowProc(hWnd, msg, wParam, lParam);
	}

	if (msg == WM_NCDESTROY) {
		// [DMM] Added: save hwndClient before freeing pfrm
		HWND hWndClient = pfrm->hWndClient;

		if (pfrm->fpProcRibbon != NULL)
			FreeProcInstance((FARPROC) pfrm->fpProcRibbon);

		if (pfrm->hMenu != NULL)
			DestroyMenu(pfrm->hMenu);

		LocalFree((HLOCAL)OFFSETOF(pfrm));
		pfrm = NULL;
		Frame_SetPtr(hWnd, NULL);

		// [DMM] Added: execution ends here on NCDESTROY
		return DefFrameProc(hWnd, hWndClient, msg, wParam, lParam);
	}


	// Get the window handle of the active MDI child. 
	// This is NULL if no MDI children exist. 
	// Determine if the MDI child is maximized. 

	if (IsWindow(pfrm->hWndClient))
		lResult = SendMessage(pfrm->hWndClient, WM_MDIGETACTIVE, 0, 0);

	pfrm->hWndActiveMDIChild = (HWND) LOWORD(lResult);
	pfrm->fMDIChildIsMaximized = HIWORD(lResult);


	switch (msg) {
		// Windows messages 

		HANDLE_MSG(pfrm, WM_CREATE, Frame_OnCreate);
		HANDLE_MSG(pfrm, WM_DESTROY, Frame_OnDestroy);
		HANDLE_MSG(pfrm, WM_CLOSE, Frame_OnClose);
		HANDLE_MSG(pfrm, WM_QUERYENDSESSION, Frame_OnQueryEndSession);
		HANDLE_MSG(pfrm, WM_ENDSESSION, Frame_OnEndSession);
		HANDLE_MSG(pfrm, WM_PAINT, Frame_OnPaint);
		HANDLE_MSG(pfrm, WM_ERASEBKGND, Frame_OnEraseBkgnd);
		HANDLE_MSG(pfrm, WM_SIZE, Frame_OnSize);
		HANDLE_MSG(pfrm, WM_ACTIVATE, Frame_OnActivate);
		HANDLE_MSG(pfrm, WM_INITMENU, Frame_OnInitMenu);
		HANDLE_MSG(pfrm, WM_INITMENUPOPUP, Frame_OnInitMenuPopup);
		HANDLE_MSG(pfrm, WM_COMMAND, Frame_OnCommand);

		HANDLE_MSG(pfrm, WM_SYSCOMMAND, Frame_OnSysCommand);
		HANDLE_MSG(pfrm, WM_NCLBUTTONDBLCLK, Frame_OnNclButtonDown);
		HANDLE_MSG(pfrm, WM_MENUSELECT, Frame_OnMenuSelect);
		HANDLE_MSG(pfrm, WM_ENTERIDLE, Frame_OnEnterIdle);

		// Frame window specific messages 

		HANDLE_MSG(pfrm, FW_MDICHILDDESTROY, Frame_OnMdiChildDestroy);
		HANDLE_MSG(pfrm, FW_GETSTATBARRECT, Frame_OnGetStatBarRect);
		HANDLE_MSG(pfrm, FW_DRAWSTATUSDIVIDE, Frame_OnDrawStatusDivide);
		HANDLE_MSG(pfrm, FW_RESIZEMDICLIENT, Frame_OnResizeMdiClient);
		HANDLE_MSG(pfrm, FW_SETMENUHELP, Frame_OnSetMenuHelp);
		HANDLE_MSG(pfrm, FW_GETMENUHELP, Frame_OnGetMenuHelp);

		// Application messages 

		HANDLE_MSG(pfrm, AW_PAINTMENUHELP, Frame_OnPaintMenuHelp);

	default:
		return DefFrameProc(hWnd, pfrm->hWndClient, msg, wParam, lParam);
	}
}

HWND Frame_CreateWindow(LPCSTR lpszText,
						int x,
						int y,
						int cx,
						int cy,
						HINSTANCE hinst)
{
	return CreateWindowEx(0L,
						  CLASS_FRAME,
						  lpszText,
			WS_OVERLAPPEDWINDOW | WS_CLIPSIBLINGS | WS_CLIPCHILDREN,
						  x, y, cx, cy,
						  NULL,
						  NULL,
						  hinst,
						  NULL);
}

//-Message handlers------------------------------------------------ 

BOOL Frame_OnCreate(FRAME * pfrm, CREATESTRUCT FAR * lpCreateStruct)
{
	CLIENTCREATESTRUCT ccs;

	pfrm->hMenu = LoadMenu(lpCreateStruct->hInstance,
						   MAKEINTRESOURCE(IDR_FRAMEMENU));

	pfrm->fStatusBarOn = TRUE;

	// Create the MDICLIENT window as a child of the frame. 

	ccs.hWindowMenu = GetSubMenu(GetMenu(pfrm->hWnd), 1);
	ccs.idFirstChild = CMD_WINDOWCHILD;

	pfrm->hWndClient = CreateWindow("MDIClient", "",
						WS_CHILD | WS_CLIPCHILDREN | WS_VSCROLL | WS_HSCROLL |
									WS_VISIBLE | WS_CLIPSIBLINGS, 0, 0, 0, 0,
								  pfrm->hWnd, NULL, lpCreateStruct->hInstance,
									(LPSTR) (LPCLIENTCREATESTRUCT) & ccs);

	// Create the Ribbon dialog box with Frame as owner. 

	pfrm->fpProcRibbon = (DLGPROC) MakeProcInstance((FARPROC) Ribbon_DlgProc, g_app.hinst);

	pfrm->hdlgRibbon = CreateDialog(g_app.hinst,
									MAKEINTRESOURCE(DLG_RIBBON), pfrm->hWnd,
									pfrm->fpProcRibbon);

	if (pfrm->hdlgRibbon == NULL) {
		FreeProcInstance((FARPROC) pfrm->fpProcRibbon);
		return (0);
	}

	return pfrm->hWndClient != NULL;
}

void Frame_OnDestroy(FRAME * pfrm)
{
	PostQuitMessage(0);
}

void Frame_OnClose(FRAME * pfrm)
{
	// Before closing the application, ask the MDI children if it 
	// is ok? 

	if ((BOOL) SendMessage(pfrm->hWnd, WM_QUERYENDSESSION, 0, 0)) {
		SendMessage(pfrm->hWnd, WM_ENDSESSION, TRUE, 0);
		DefFrameProc(pfrm->hWnd, pfrm->hWndClient, WM_CLOSE, 0, 0L);
	}
}

BOOL Frame_OnQueryEndSession(FRAME * pfrm)
{
	BOOL			bResult = TRUE;
	HWND			hWndChild;

	// Get the handle of the first MDI Child. 

	hWndChild = GetWindow(pfrm->hWndClient, GW_CHILD);

	if (hWndChild != NULL) {
		// Ask each child if it is OK to terminate. 

		do {
			// Do not ask caption bars of iconic MDI Children. 

			if (GetWindow(hWndChild, GW_OWNER) != NULL)
				continue;

			bResult = FORWARD_WM_QUERYENDSESSION(hWndChild, SendMessage);

			// If the MDI Child says that it is NOT OK, don't ask the 
			// rest of the MDI Children. 

			if (bResult == FALSE)
				break;
		}
		while ((hWndChild = GetWindow(hWndChild, GW_HWNDNEXT)) != NULL);


		// If any MDI Child said NO, tell the other children that 
		// the session is NOT being terminated. 

		if (bResult == FALSE) {
			HWND			hWndTemp = hWndChild;

			hWndChild = GetWindow(pfrm->hWndClient, GW_CHILD);

			do {
				// If this child is the one that said NO, stop. 

				if (hWndTemp == hWndChild)
					break;

				// Do not send to caption bars of iconic MDI Children. 

				if (GetWindow(hWndChild, GW_OWNER) != NULL)
					continue;

				// Tell child we are not ending the session (wParam is FALSE). 

				SendMessage(hWndChild, WM_ENDSESSION, FALSE, 0);
			}
			while ((hWndChild = GetWindow(hWndChild, GW_HWNDNEXT)) != NULL);
		}

	}

	return bResult;
}

void Frame_OnEndSession(FRAME * pfrm, BOOL fEnding)
{
	HWND			hWndChild;

	// Get handle of first MDI Child window. 

	hWndChild = GetWindow(pfrm->hWndClient, GW_CHILD);

	// If no MDI Children exist, we are done. 

	if (hWndChild != NULL) {
		// Tell each MDI Child whether or not the session is ending. 

		do {
			// Do not send to caption bars of iconic MDI Children. 

			if (GetWindow(hWndChild, GW_OWNER) != NULL)
				continue;

			FORWARD_WM_ENDSESSION(hWndChild, fEnding, SendMessage);
		}
		while ((hWndChild = GetWindow(hWndChild, GW_HWNDNEXT)) != NULL);
	}
}

void Frame_OnPaint(FRAME * pfrm)
{
	PAINTSTRUCT		ps;

	// Since the only visible portion of the Frame's client area is	 
	// the status bar when it is ON, this must mean that the status 
	// bar needs to be repainted. 

	// Set up the device context. 

	BeginPaint(pfrm->hWnd, &ps);

	SendMessage(pfrm->hWnd, FW_GETSTATBARRECT, 0, (LPARAM)((LPRECT)&ps.rcPaint));
	SetBkMode(ps.hdc, TRANSPARENT);

	// If an MDI Child exists, the status bar must be updated by it. 

	if (pfrm->hWndActiveMDIChild) {
		SendMessage(   pfrm->hWndActiveMDIChild,
					   AC_PAINTSTATBAR,
					   (WPARAM) ps.hdc,
					   (LPARAM)((LPPAINTSTRUCT)&ps));
	}
	else {
		// No MDI Child exists, the Frame can do whatever it wants here. 

		ps.rcPaint.top += (int) SendMessage(pfrm->hWnd, FW_DRAWSTATUSDIVIDE, 0,
											(LPARAM)((LPPAINTSTRUCT)&ps));

		LoadString(g_app.hinst, IDS_FRAMESTATUSBAR, g_app.szBuf, sizeof(g_app.szBuf));
		TextOut(ps.hdc, 0, ps.rcPaint.top, g_app.szBuf, lstrlen(g_app.szBuf));
	}

	EndPaint(pfrm->hWnd, &ps);
}

BOOL Frame_OnEraseBkgnd(FRAME * pfrm, HDC hdc)
{
	return FORWARD_WM_ERASEBKGND(pfrm->hWnd, hdc, DefWindowProc);
}

void Frame_OnSize(FRAME * pfrm, UINT state, int cx, int cy)
//----------------------------------------------------------------- 
// Force MDICHILD window to be resized. 
//----------------------------------------------------------------- 
{
	SendMessage(pfrm->hWnd, FW_RESIZEMDICLIENT, 0, 0);
}

void Frame_OnActivate(FRAME * pfrm, UINT state, HWND hWndActDeact, BOOL fMinimized)
{
	switch (state) {
	case WA_INACTIVE:
		break;

	case WA_ACTIVE:
		SetFocus(pfrm->hWndClient);
		break;

	case WA_CLICKACTIVE:
		break;
	}
}

void Frame_OnInitMenu(FRAME * pfrm, HMENU hMenu)
//----------------------------------------------------------------- 
// The user has entered the menu system, set any options. 
//----------------------------------------------------------------- 
{
	CheckMenuItem(hMenu, CMD_OPTIONSSTATUS, MF_BYCOMMAND |
			(pfrm->fStatusBarOn ? MF_CHECKED : MF_UNCHECKED));

	CheckMenuItem(hMenu, CMD_OPTIONSRIBBON, MF_BYCOMMAND |
			(IsWindowVisible(pfrm->hdlgRibbon) ? MF_CHECKED : MF_UNCHECKED));
}

void Frame_OnInitMenuPopup(FRAME * pfrm, HMENU hMenu, int item, BOOL fSystemMenu)
{
}

void Frame_OnCommand(FRAME * pfrm, int id, HWND hWndCtl, UINT code)
{
	BOOL			fCallDefProc = FALSE;
	WORD			wTemp;

	// If a child is being activated via the "Window" menu, let 
	// the DefFrameProc handle it. 

	if (id >= CMD_WINDOWCHILD)
		fCallDefProc = TRUE;
	else
		switch (id) {
		case CMD_FILEOPENSHEET:
			// Get the # of sheets already created and increment by 1. 

			wTemp = pfrm->wNumSheets + 1;
			pfrm->wNumSheets = wTemp;

			// The sheet's caption should display the sheet number. 

			wsprintf(g_app.szBuf, "Sheet%d", wTemp);

			// Create the MDI Child window. 

			Frame_CreateMDIChild(CLASS_SHEET, g_app.szBuf, 0,
				   CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
								 pfrm->hWndClient, g_app.hinst, 0);

			// Make sure the ribbon is enabled when any children exist. 

			EnableWindow(pfrm->hdlgRibbon, TRUE);
			break;

		case CMD_FILEOPENCHART:
			// Get the # of charts already created and increment by 1. 

			wTemp = pfrm->wNumCharts + 1;
			pfrm->wNumCharts = wTemp;

			// The chart's caption should display the chart number. 

			wsprintf(g_app.szBuf, "Chart%d", wTemp);

			// Create the MDI Child window. 

			Frame_CreateMDIChild(CLASS_CHART, g_app.szBuf, 0,
				   CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
								 pfrm->hWndClient, g_app.hinst, 0);

			// Make sure the ribbon is enabled when any children exist. 

			EnableWindow(pfrm->hdlgRibbon, TRUE);
			break;

		case CMD_OPTIONSSTATUS:
			// Toggle the status of the status bar, resize the MDICLIENT. 

			pfrm->fStatusBarOn = !pfrm->fStatusBarOn;
			SendMessage(pfrm->hWnd, FW_RESIZEMDICLIENT, 0, 0);
			break;

		case CMD_OPTIONSRIBBON:
			// Toggle the status of the ribbon, resize the MDICLIENT. 

			ShowWindow(pfrm->hdlgRibbon,
					   IsWindowVisible(pfrm->hdlgRibbon) ? SW_HIDE : SW_SHOW);

			SendMessage(pfrm->hWnd, FW_RESIZEMDICLIENT, 0, 0);
			break;

		case CMD_EXIT:
			SendMessage(pfrm->hWnd, WM_CLOSE, 0, 0L);
			break;

		case CMD_HELPINDEX:
		case CMD_HELPKEYBOARD:
		case CMD_HELPCOMMANDS:
		case CMD_HELPPROCEDURES:
		case CMD_HELPUSINGHELP:
			MessageBox(pfrm->hWnd, "Option not implemented.", g_app.szName, MB_OK);
			break;

			// case CMD_ABOUT: 
			// { 
			// FARPROC	fpProc; 
			// fpProc = MakeProcInstance(AboutProc, g_app.hinst); 
			// DialogBox(g_app.hinst, "About", pfrm->hWnd, fpProc); 
			// FreeProcInstance(fpProc); 
			// break; 
			// } 

		case CMD_WINDOWTILEVERT:
			// Call our own function to perform vertical tiling. 

			Frame_TileVertically(pfrm->hWndClient);
			break;

		case CMD_WINDOWTILEHORIZ:
			// Let the MDICLIENT window do the repositioning. 

			SendMessage(pfrm->hWndClient, WM_MDITILE, 0, 0);
			break;

		case CMD_WINDOWCASCADE:
			// Let the MDICLIENT window do the repositioning. 

			SendMessage(pfrm->hWndClient, WM_MDICASCADE, 0, 0);
			break;

		case CMD_WINDOWARRANGEICONS:
			// Let the MDICLIENT window do the repositioning. 

			SendMessage(pfrm->hWndClient, WM_MDIICONARRANGE, 0, 0);
			break;

		default:
			// Menu options not processed by the Frame window must 
			// be passed to the MDI Children for processing. 

			FORWARD_WM_COMMAND(pfrm->hWndActiveMDIChild,
							   id, hWndCtl, code,
							   SendMessage);
			break;
		}

	if (fCallDefProc)
		DefFrameProc(pfrm->hWnd,
					 pfrm->hWndClient, WM_COMMAND,
					 (WPARAM) id, MAKELPARAM(hWndCtl, code));
}

void Frame_OnSysCommand(FRAME * pfrm, WORD cmd, int x, int y)
//----------------------------------------------------------------- 
// Set focus to frame window.  This causes any comboboxes 
// in the ribbon to be closed. 
//----------------------------------------------------------------- 
{
	SetFocus(pfrm->hWnd);
	DefFrameProc(pfrm->hWnd,
				 pfrm->hWndClient, WM_SYSCOMMAND,
				 (WPARAM) cmd,
				 MAKELPARAM(x, y));
}

void Frame_OnNclButtonDown(FRAME * pfrm, BOOL fDoubleClick, int x, int y, UINT codeHitTest)
//----------------------------------------------------------------- 
// Code to allow double-clicking the MDI Child's system menu 
// to close the MDI Child window. 
//----------------------------------------------------------------- 
{
	RECT			rc;
	BITMAP			Bitmap;
	HBITMAP			hBitmap;
	DWORD			dwResult;
	LPARAM			lParam;
	BOOL			fCallDefProc = TRUE;


	if (fDoubleClick && (codeHitTest == HTMENU)) {
		// If the active child is not maximized, nothing to do. 

		dwResult = SendMessage(pfrm->hWndClient, WM_MDIGETACTIVE, 0, 0);

		if (HIWORD(dwResult) == 1) {
			// Get position and dimensions of the MDI Child's system menu in  
			// the Frame's menu bar. 

			// Get position and dimensions of the Frame window. 

			GetWindowRect(pfrm->hWnd, &rc);

			// Get handle to the CLOSE BOX bitmaps. 

			hBitmap = LoadBitmap(NULL, MAKEINTRESOURCE(OBM_CLOSE));

			// Get dimensions of the bitmaps. 

			GetObject(hBitmap, sizeof(BITMAP), (LPSTR) (LPBITMAP) & Bitmap);
			DeleteBitmap(hBitmap);

			// Adjust the rectangle. 

			rc.top += GetSystemMetrics(SM_CYCAPTION) + GetSystemMetrics(SM_CYFRAME);
			rc.bottom = rc.top + Bitmap.bmHeight;
			rc.left += GetSystemMetrics(SM_CXFRAME);

			// The close bitmap includes the Application and MDI Child CLOSE  
			// boxes.  So we only want half of the bitmap's width. 

			rc.right = rc.left + Bitmap.bmWidth / 2;

			// If the mouse cursor is within this rectangle, tell the  
			// MDI Child window to close. 

			lParam = MAKELPARAM(x, y);
			if (PtInRect(&rc, MAKEPOINT(lParam))) {
				SendMessage((HWND) LOWORD(dwResult),
							WM_SYSCOMMAND, SC_CLOSE, lParam);
				fCallDefProc = FALSE;
			}
		}
	}

	if (fCallDefProc)
		DefFrameProc(pfrm->hWnd,
					 pfrm->hWndClient,
					 (fDoubleClick) ? WM_NCLBUTTONDBLCLK : WM_NCLBUTTONDOWN,
					 (WPARAM) (UINT) codeHitTest,
					 MAKELPARAM(x, y));
}

void Frame_OnMenuSelect(FRAME * pfrm, HMENU hMenu, int item, HMENU hMenuPopup, UINT flags)
//----------------------------------------------------------------- 
// The user has highlighted a menu item. 
//----------------------------------------------------------------- 
{
	WORD			wTemp;

	if (flags == -1 && (hMenu == (HMENU) 0)) {
		// User has stopped using the menu system. 

		SendMessage(pfrm->hWnd, FW_SETMENUHELP, 0, 0);
		return;
	}

	// If wTemp == 0, at end of switch, MDI Child handled the message. 

	wTemp = 0;

	switch (flags & (MF_POPUP | MF_SYSMENU)) {
	case 0:
		// item is a menu item ID NOT on the app's system menu. 

		if (pfrm->hWndActiveMDIChild != NULL) {
			// An MDI Child exists. 

			if (pfrm->fMDIChildIsMaximized) {
				// If menu item from the MDI Child's system menu, set  
				// the MF_SYSMENU bit in the lParam parameter. 

				HMENU			hTemp = GetSubMenu(GetMenu(pfrm->hWnd), 0);

				if ((int) GetMenuState(hTemp, item, MF_BYCOMMAND) != -1)
					flags |= MF_SYSMENU;
			}

			// Make active MDI Child think that it received the	 
			// WM_MENUSELECT message. 

			FORWARD_WM_MENUSELECT(
									 pfrm->hWndActiveMDIChild,
									 hMenu, item, hMenuPopup,
									 flags,
									 SendMessage);

			wTemp = 0;					// MDI Child handled the message. 
			break;
		}

		wTemp = IDS_FRAMEMENUID + item;
		break;

	case MF_POPUP:

		if (pfrm->hWndActiveMDIChild != NULL) {
			// An MDI Child exists. 

			if (pfrm->fMDIChildIsMaximized) {
				// If popup menu is first top-level menu, it is the	 
				// MDI Child's system menu, set the MF_SYSMENU flag. 

				if (hMenuPopup == GetSubMenu(GetMenu(pfrm->hWnd), 0))
					flags |= MF_SYSMENU;
			}

			// Make active MDI Child think that it received the	 
			// WM_MENUSELECT message. 

			FORWARD_WM_MENUSELECT(
									 pfrm->hWndActiveMDIChild,
									 hMenu, item, hMenuPopup,
									 flags,
									 SendMessage);

			wTemp = 0;					// MDI Child handled the message. 
			break;
		}

		// Calculate the index of the top-level menu. 

		hMenu = GetMenu(pfrm->hWnd);
		wTemp = GetMenuItemCount(hMenu);

		while (wTemp--)
			if (GetSubMenu(hMenu, wTemp) == hMenuPopup)
				break;

		wTemp += IDS_FRAMEPOPUPID + 1;	// Jump over system menu. 
		break;

	case MF_SYSMENU:
		// item is menu item ID from system menu. 
		wTemp = IDS_FRAMEMENUID + ((item & 0x0FFF) >> 4);
		break;

	case MF_POPUP | MF_SYSMENU:
		// item is handle to app's sys menu. 
		wTemp = IDS_FRAMEPOPUPID;
		break;
	}

	// If message handled by MDI Child, nothing more to do. 

	if (wTemp != 0) {
		// Tell the Frame that the Frame window should display the	
		// help text and the identifier for the help text. 

		SendMessage(pfrm->hWnd, FW_SETMENUHELP,
					(WPARAM) pfrm->hWnd, (LPARAM) wTemp);
	}
}

void Frame_OnEnterIdle(FRAME * pfrm, WORD source, HWND hWndSource)
{
	RECT			rc;
	PAINTSTRUCT		ps;

	if (source == MSGF_MENU) {
		// User has stopped scrolling through menu items. 

		// If Menu help already displayed, nothing more to do. 
		// This is signaled by hWndMenu help being -1. 

		if (pfrm->hWndMenuHelp != (HWND) - 1) {
			// Display new menu help, invalidate the status bar. 

			SendMessage(pfrm->hWnd, FW_GETSTATBARRECT, 0, (LPARAM)((LPRECT)&rc));
			InvalidateRect(pfrm->hWnd, &rc, TRUE);

			// BeginPaint is OK because an invalid rectangle must exist because 
			// of the call to InvalidateRect above.	 This causes the background 
			// for the Frame's client area to be drawn correctly. 

			BeginPaint(pfrm->hWnd, &ps);

			// Set up the device context. 

			SetBkMode(ps.hdc, TRANSPARENT);

			// Send message to window that last received a WM_MENUSELECT 
			// message to tell it to paint the status bar with the	
			// appropriate menu help text. 

			SendMessage(pfrm->hWndMenuHelp, AW_PAINTMENUHELP, 0,
						(LPARAM)((LPPAINTSTRUCT)&ps));

			EndPaint(pfrm->hWnd, &ps);

			// Set flag notifying this message that the most recently selected 
			// menu item has had its help text painted.	 This stops unsightly 
			// screen flicker. 

			pfrm->hWndMenuHelp = (HWND) - 1;
		}
	}
}


void Frame_OnMdiChildDestroy(FRAME * pfrm)
//----------------------------------------------------------------- 
// Message is posted by an MDI Child just before it is destroyed. 
//----------------------------------------------------------------- 
{
	// If another MDI Child exists, nothing to do. 

	if (pfrm->hWndActiveMDIChild != NULL)
		return;

	// Set the menu bar and accelerator table to the Frame's defaults. 

	Frame_ChangeMDIMenu(   pfrm->hWnd,
						   pfrm->hWndClient,
						   pfrm->hMenu,
						   CMD_WINDOWTILEVERT);

	g_app.hAccelTable = NULL;

	// Force the status bar to be updated. 

	InvalidateRect(pfrm->hWnd, NULL, TRUE);

	// Disable the Ribbon. 

	EnableWindow(pfrm->hdlgRibbon, FALSE);
}

void Frame_OnGetStatBarRect(FRAME * pfrm, LPRECT lpRect)
{
	// Get the client area of the Frame window. 

	GetClientRect(pfrm->hWnd, lpRect);

	// If the status bar is OFF, set the status bar to have no height. 

	if (pfrm->fStatusBarOn) {
		// Change the dimensions so that the status bar is the height of  
		// one line of text plus a small border. 

		HDC				hdc;
		TEXTMETRIC		tm;

		hdc = GetDC(pfrm->hWnd);
		GetTextMetrics(hdc, &tm);
		ReleaseDC(pfrm->hWnd, hdc);

		lpRect->top = lpRect->bottom - tm.tmHeight -
				GetSystemMetrics(SM_CYBORDER);
	}
	else
		lpRect->top = lpRect->bottom;
}

void Frame_OnDrawStatusDivide(FRAME * pfrm, LPPAINTSTRUCT ps)
{
	HPEN			hPen;
	DWORD			dwResult = GetSystemMetrics(SM_CYBORDER);

	// Draw a line separating the status bar from the MDICLIENT window. 

	hPen = CreatePen(PS_SOLID, (int) dwResult, RGB(0, 0, 0));
	hPen = SelectPen(ps->hdc, hPen);

	MoveTo(ps->hdc, 0, ps->rcPaint.top);
	LineTo(ps->hdc, ps->rcPaint.right, ps->rcPaint.top);
	DeletePen(SelectPen(ps->hdc, hPen));
}

void Frame_OnResizeMdiClient(FRAME * pfrm)
{
	RECT			rc, rcTemp;

	// Sent when the Frame window is resized or when the status bar	 
	// and ribbon are toggled. 

	GetClientRect(pfrm->hWnd, &rc);

	if (IsWindow(pfrm->hdlgRibbon) && IsWindowVisible(pfrm->hdlgRibbon)) {
		// Ribbon is displayed, adjust rectangle. 

		GetClientRect(pfrm->hdlgRibbon, &rcTemp);
		rc.top += rcTemp.bottom;
		rc.bottom -= rcTemp.bottom;
	}

	// Get the dimensions of the status bar rectangle and adjust the  
	// dimensions of the MDICLIENT window. 

	SendMessage(pfrm->hWnd, FW_GETSTATBARRECT, 0, (LPARAM)((LPRECT)&rcTemp));
	rc.bottom -= rcTemp.bottom - rcTemp.top;
	MoveWindow(pfrm->hWndClient, 0, rc.top, rc.right, rc.bottom, TRUE);
}

void Frame_OnSetMenuHelp(FRAME * pfrm, HWND hWndMenuHelp, DWORD dwMenuHelp)
//----------------------------------------------------------------- 
// Called by the Frame and MDI Children whenever a	
// WM_MENUSELECT message is received. 
//----------------------------------------------------------------- 
{
	// Save the handle of the window sending the message. 

	pfrm->hWndMenuHelp = hWndMenuHelp;

	// Save the menu help code that the window sent too. 

	pfrm->dwMenuHelp = dwMenuHelp;

	// When the Frame or MDI Child receive a WM_MENUSELECT message 
	// specifying that the menu system is closed  
	// (dwMenuHelp == MAKELONG(-1, 0)), the menu help should disappear and 
	// be replaced by the proper information on the status bar. 

	if (hWndMenuHelp == NULL) {
		RECT			rc;

		SendMessage(pfrm->hWnd, FW_GETSTATBARRECT, 0, (LPARAM)((LPRECT)&rc));

		// Force status bar to be updated. 

		InvalidateRect(pfrm->hWnd, &rc, TRUE);
	}
}

DWORD Frame_OnGetMenuHelp(FRAME * pfrm)
//----------------------------------------------------------------- 
// Sent by the Frame or MDI Child when they	 
// receive a AW_PAINTMENUHELP message. 
//----------------------------------------------------------------- 
{
	return pfrm->dwMenuHelp;
}

void Frame_OnPaintMenuHelp(FRAME * pfrm, LPPAINTSTRUCT psStatus)
//----------------------------------------------------------------- 
// Message sent from Frame window to notify Frame that it should 
// paint the status bar text for the last highlighted menu item. 
//----------------------------------------------------------------- 
{
	LRESULT			lResult;

	// Ask the Frame window what the last selected menu ID was. 
	// This value was sent to the frame by this window during the  
	// processing for the WM_MENUSELECT message. 

	lResult = SendMessage(pfrm->hWnd, FW_GETMENUHELP, 0, 0);

	// Draw the horizontal dividing line separating the Status bar 
	// from the MDICLIENT window. 

	psStatus->rcPaint.top += (int)
			SendMessage(pfrm->hWnd, FW_DRAWSTATUSDIVIDE, 0, (LPARAM) psStatus);

	// Construct the string that is to be displayed. 

	LoadString(g_app.hinst, LOWORD(lResult), g_app.szBuf, sizeof(g_app.szBuf));

	// Paint the menu help text in the status bar. 

	TextOut(psStatus->hdc, 0, psStatus->rcPaint.top, g_app.szBuf, lstrlen(g_app.szBuf));
}

//-Utility routines------------------------------------------------ 

HWND WINAPI Frame_CreateMDIChild(LPSTR szClassName,
								 LPSTR szWindowName,
								 DWORD dwStyle,
								 short x,
								 short y,
								 short nWidth,
								 short nHeight,
								 HWND hWndMDIClient,
								 HANDLE hInstance,
								 LONG lParam)
{
	MDICREATESTRUCT cs;
	HWND			hWndChild;

	cs.szClass = szClassName;
	cs.szTitle = szWindowName;
	cs.hOwner = hInstance;
	cs.x = x;
	cs.y = y;
	cs.cx = nWidth;
	cs.cy = nHeight;
	cs.style = dwStyle;
	cs.lParam = lParam;

	hWndChild = (HWND) SendMessage(hWndMDIClient, WM_MDICREATE,
			0, (LPARAM)((LPMDICREATESTRUCT)&cs));
	return (hWndChild);
}

// Function to change the menu in the Frame window whenever a  
// new MDI Child becomes active. 

void WINAPI Frame_ChangeMDIMenu(HWND hWndFrame,
								HWND hWndClient,
								HMENU hMenuNew,
								WORD wMenuID)
{
	WORD			wCount;
	HMENU			hSubMenu = 0;

	// Get number of top-level menu items in the menu used by the window 
	// being activated. 

	wCount = GetMenuItemCount(hMenuNew);

	// Locate the POPUP menu that contains the menu option with the	 
	// 'wMenuID' identifier in it.	This must be an identifier for an option 
	// in the new menu's "Window" popup menu. 

	while (wCount) {
		hSubMenu = GetSubMenu(hMenuNew, wCount - 1);
		if ((int) GetMenuState(hSubMenu, wMenuID, MF_BYCOMMAND) != -1)
			break;
		wCount--;
	}

	// Tell the MDICLIENT window to setup the new menu. 

	SendMessage(hWndClient, WM_MDISETMENU, 0, MAKELONG(hMenuNew, hSubMenu));
	DrawMenuBar(hWndFrame);
}

void WINAPI Frame_TileVertically(HWND hWndMDIClient)
{
	int	nNumWndsOnRow, nOpenMDIChildren = 0, nTopOfBottomIconRow = 0;
	int	nCrntCol, nColWidth, nCrntRow, nNumRows, nRowHeight, nMinWndHeight;
	HWND			hWndChild;
	HANDLE			hWinPosInfo;
	RECT			rc;
	POINT			Point;
	DWORD			dwChildInfo;

	// Assume that scrollbars will be off after windows are tiled. 
	// By forcing them off now, GetClientRect will return the correct size. 

	ShowScrollBar(hWndMDIClient, SB_BOTH, 0);

	// The WM_MDICASCADE and WM_MDITILE messages cause the icons to be	
	// arranged.  So we will too.  In fact, this is necessary to locate	 
	// the top of the bottom icon row in the next step of this function. 

	SendMessage(hWndMDIClient, WM_MDIICONARRANGE, 0, 0);

	// Get handle to first MDI Child window. 

	hWndChild = GetWindow(hWndMDIClient, GW_CHILD);

	do {
		if (IsIconic(hWndChild) && GetWindow(hWndChild, GW_OWNER) == NULL) {
			// Window is iconic and window is NOT an icon's caption. 

			// Get client area of the icon window. 

			GetWindowRect(hWndChild, &rc);

			// rc.top is in screen coordinates. 

			nTopOfBottomIconRow = max(nTopOfBottomIconRow, rc.top);
		}

		if (!IsIconic(hWndChild) && GetWindow(hWndChild, GW_OWNER) == NULL)
			++nOpenMDIChildren;

	}
	while ((hWndChild = GetWindow(hWndChild, GW_HWNDNEXT)) != NULL);


	// All MDI Children are icons, no tiling is necessary. 

	if (nOpenMDIChildren == 0)
		return;


	// Find height of usable client area for tiling. 

	GetClientRect(hWndMDIClient, &rc);

	if (nTopOfBottomIconRow) {
		// At least one MDI Child is iconic. 

		// Convert coordinates from screen to client. 

		Point.x = 0;
		Point.y = nTopOfBottomIconRow;
		ScreenToClient(hWndMDIClient, &Point);

		// Point.y is top of bottom icon row in client coordinates. 

		rc.bottom = Point.y;
	}


	// Restore the active MDI child if it's maximized 

	dwChildInfo = SendMessage(hWndMDIClient, WM_MDIGETACTIVE, 0, 0);

	if (HIWORD(dwChildInfo) == 1)
		ShowWindow((HWND) LOWORD(dwChildInfo), SW_RESTORE);

	// Calculate the minimum desired height of each MDI Child. 

	nMinWndHeight = max(1, rc.bottom / (5 * GetSystemMetrics(SM_CYCAPTION)));

	// Calculate the number of rows that will be tiled. 

	nNumRows = min(nOpenMDIChildren, nMinWndHeight);

	// Calculate the height of each row. 

	nRowHeight = rc.bottom / nNumRows;

	// Get the handle to the first MDI Child window. 

	hWndChild = GetWindow(hWndMDIClient, GW_CHILD);

	// Prime the storage of positioning information. 

	hWinPosInfo = BeginDeferWindowPos(nOpenMDIChildren);

	// Execute the loop for each row. 

	for (nCrntRow = 0; nCrntRow < nNumRows; nCrntRow++) {
		// Calculate the number of MDI Children that will appear on this row. 

		nNumWndsOnRow = nOpenMDIChildren / nNumRows +
				((nOpenMDIChildren % nNumRows > (nNumRows - (nCrntRow + 1))) ? 1 : 0);

		// Calculate the width of each of these children. 

		nColWidth = rc.right / nNumWndsOnRow;

		// Fill each column with an MDI Child window. 

		for (nCrntCol = 0; nCrntCol < nNumWndsOnRow;) {
			if (!IsIconic(hWndChild) && GetWindow(hWndChild, GW_OWNER) == NULL) {
				// Child is NOT iconic and not an icon's caption bar. 

				// Tell windows what the new position and dimensions of this  
				// MDI Child should be. 

				hWinPosInfo = DeferWindowPos(hWinPosInfo, hWndChild, NULL,
					   nCrntCol * nColWidth, nCrntRow * nRowHeight, nColWidth,
								   nRowHeight, SWP_NOACTIVATE | SWP_NOZORDER);

				// Go to the next column. 

				nCrntCol++;
			}

			// Get handle to the next MDI Child window. 

			hWndChild = GetWindow(hWndChild, GW_HWNDNEXT);
		}
	}

	// All of the positioning has been set.	 Now, tell Windows to update 
	// all of the windows at once. 

	EndDeferWindowPos(hWinPosInfo);
}
