#define _FRAME_C //----------------------------------------------------------------- // APP.C - // // 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); }