/* MDI.C * * Copyright 1994, Bob Amstadt * * This file contains routines to support MDI features. */ #include #include #include #include "windows.h" #include "win.h" #include "mdi.h" #include "user.h" #include "sysmetrics.h" #define DEBUG_MDI /* */ extern WORD MENU_DrawMenuBar( HDC hDC, LPRECT lprect, HMENU hmenu, BOOL suppress_draw ); /* menu.c */ /********************************************************************** * MDIRecreateMenuList */ void MDIRecreateMenuList(MDICLIENTINFO *ci) { MDICHILDINFO *chi; char buffer[128]; int id, n, index; #ifdef DEBUG_MDI fprintf(stderr, "MDIRecreateMenuList: hWindowMenu %04.4x\n", ci->hWindowMenu); #endif id = ci->idFirstChild; while (DeleteMenu(ci->hWindowMenu, id, MF_BYCOMMAND)) id++; #ifdef DEBUG_MDI fprintf(stderr, "MDIRecreateMenuList: id %04.4x, idFirstChild %04.4x\n", id, ci->idFirstChild); #endif if (!ci->flagMenuAltered) { ci->flagMenuAltered = TRUE; AppendMenu(ci->hWindowMenu, MF_SEPARATOR, 0, NULL); } id = ci->idFirstChild; index = 1; for (chi = ci->infoActiveChildren; chi != NULL; chi = chi->next) { n = sprintf(buffer, "%d ", index++); GetWindowText(chi->hwnd, buffer + n, sizeof(buffer) - n - 1); #ifdef DEBUG_MDI fprintf(stderr, "MDIRecreateMenuList: id %04.4x, '%s'\n", id, buffer); #endif AppendMenu(ci->hWindowMenu, MF_STRING, id++, buffer); } } /********************************************************************** * MDICreateChild */ HWND MDICreateChild(WND *w, MDICLIENTINFO *ci, HWND parent, LPMDICREATESTRUCT cs) { HWND hwnd; /* * Create child window */ cs->style &= (WS_MINIMIZE | WS_MAXIMIZE | WS_HSCROLL | WS_VSCROLL); hwnd = CreateWindowEx(0, cs->szClass, cs->szTitle, WS_CHILD | WS_BORDER | WS_CAPTION | WS_CLIPSIBLINGS | WS_MAXIMIZEBOX | WS_MINIMIZEBOX | WS_SYSMENU | WS_THICKFRAME | WS_VISIBLE | cs->style, cs->x, cs->y, cs->cx, cs->cy, parent, (HMENU) 0, w->hInstance, (LPSTR) cs); if (hwnd) { HANDLE h = USER_HEAP_ALLOC(GMEM_MOVEABLE, sizeof(MDICHILDINFO)); MDICHILDINFO *child_info = USER_HEAP_ADDR(h); if (!h) { DestroyWindow(hwnd); return 0; } ci->nActiveChildren++; child_info->next = ci->infoActiveChildren; child_info->prev = NULL; child_info->hwnd = hwnd; if (ci->infoActiveChildren) ci->infoActiveChildren->prev = child_info; ci->infoActiveChildren = child_info; SendMessage(parent, WM_CHILDACTIVATE, 0, 0); } return hwnd; } /********************************************************************** * MDIDestroyChild */ HWND MDIDestroyChild(WND *w_parent, MDICLIENTINFO *ci, HWND parent, HWND child, BOOL flagDestroy) { MDICHILDINFO *chi; chi = ci->infoActiveChildren; while (chi && chi->hwnd != child) chi = chi->next; if (chi) { if (chi->prev) chi->prev->next = chi->next; if (chi->next) chi->next->prev = chi->prev; if (ci->infoActiveChildren == chi) ci->infoActiveChildren = chi->next; ci->nActiveChildren--; if (chi->hwnd == ci->hwndActiveChild) SendMessage(parent, WM_CHILDACTIVATE, 0, 0); USER_HEAP_FREE((HANDLE) (LONG) chi); if (flagDestroy) DestroyWindow(child); } return 0; } /********************************************************************** * MDIBringChildToTop */ void MDIBringChildToTop(HWND parent, WORD id, WORD by_id, BOOL send_to_bottom) { MDICHILDINFO *chi; MDICLIENTINFO *ci; WND *w; int i; w = WIN_FindWndPtr(parent); ci = (MDICLIENTINFO *) w->wExtra; #ifdef DEBUG_MDI fprintf(stderr, "MDIBringToTop: id %04.4x, by_id %d\n", id, by_id); #endif if (by_id) id -= ci->idFirstChild; if (!by_id || id < ci->nActiveChildren) { chi = ci->infoActiveChildren; if (by_id) { for (i = 0; i < id; i++) chi = chi->next; } else { while (chi && chi->hwnd != id) chi = chi->next; } if (!chi) return; #ifdef DEBUG_MDI fprintf(stderr, "MDIBringToTop: child %04.4x\n", chi->hwnd); #endif if (chi != ci->infoActiveChildren) { if (ci->flagChildMaximized) { RECT rectOldRestore, rect; w = WIN_FindWndPtr(chi->hwnd); rectOldRestore = ci->rectRestore; GetWindowRect(chi->hwnd, &ci->rectRestore); rect.top = (ci->rectMaximize.top - (w->rectClient.top - w->rectWindow.top)); rect.bottom = (ci->rectMaximize.bottom + (w->rectWindow.bottom - w->rectClient.bottom)); rect.left = (ci->rectMaximize.left - (w->rectClient.left - w->rectWindow.left)); rect.right = (ci->rectMaximize.right + (w->rectWindow.right - w->rectClient.right)); w->dwStyle |= WS_MAXIMIZE; SetWindowPos(chi->hwnd, HWND_TOP, rect.left, rect.top, rect.right - rect.left + 1, rect.bottom - rect.top + 1, 0); SendMessage(chi->hwnd, WM_SIZE, SIZE_MAXIMIZED, MAKELONG(w->rectClient.right-w->rectClient.left, w->rectClient.bottom-w->rectClient.top)); w = WIN_FindWndPtr(ci->hwndActiveChild); w->dwStyle &= ~WS_MAXIMIZE; SetWindowPos(ci->hwndActiveChild, HWND_BOTTOM, rectOldRestore.left, rectOldRestore.top, rectOldRestore.right - rectOldRestore.left + 1, rectOldRestore.bottom - rectOldRestore.top + 1, SWP_NOACTIVATE | (send_to_bottom ? 0 : SWP_NOZORDER)); } else { SetWindowPos(chi->hwnd, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE ); if (send_to_bottom) { SetWindowPos(ci->hwndActiveChild, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE); } } if (chi->next) chi->next->prev = chi->prev; if (chi->prev) chi->prev->next = chi->next; chi->prev = NULL; chi->next = ci->infoActiveChildren; chi->next->prev = chi; ci->infoActiveChildren = chi; SendMessage(parent, WM_CHILDACTIVATE, 0, 0); } #ifdef DEBUG_MDI fprintf(stderr, "MDIBringToTop: pos %04.4x, hwnd %04.4x\n", id, chi->hwnd); #endif } } /********************************************************************** * MDIMaximizeChild */ LONG MDIMaximizeChild(HWND parent, HWND child, MDICLIENTINFO *ci) { WND *w = WIN_FindWndPtr(child); RECT rect; MDIBringChildToTop(parent, child, FALSE, FALSE); ci->rectRestore = w->rectWindow; rect.top = (ci->rectMaximize.top - (w->rectClient.top - w->rectWindow.top)); rect.bottom = (ci->rectMaximize.bottom + (w->rectWindow.bottom - w->rectClient.bottom)); rect.left = (ci->rectMaximize.left - (w->rectClient.left - w->rectWindow.left)); rect.right = (ci->rectMaximize.right + (w->rectWindow.right - w->rectClient.right)); w->dwStyle |= WS_MAXIMIZE; SetWindowPos(child, 0, rect.left, rect.top, rect.right - rect.left + 1, rect.bottom - rect.top + 1, SWP_NOACTIVATE | SWP_NOZORDER); ci->flagChildMaximized = TRUE; SendMessage(child, WM_SIZE, SIZE_MAXIMIZED, MAKELONG(w->rectClient.right-w->rectClient.left, w->rectClient.bottom-w->rectClient.top)); SendMessage(GetParent(parent), WM_NCPAINT, 1, 0); return 0; } /********************************************************************** * MDIRestoreChild */ LONG MDIRestoreChild(HWND parent, MDICLIENTINFO *ci) { HWND child; WND *w = WIN_FindWndPtr(child); LPRECT lprect = &ci->rectRestore; printf("restoring mdi child\n"); child = ci->hwndActiveChild; w->dwStyle &= ~WS_MAXIMIZE; SetWindowPos(child, 0, lprect->left, lprect->top, lprect->right - lprect->left + 1, lprect->bottom - lprect->top + 1, SWP_NOACTIVATE | SWP_NOZORDER); ci->flagChildMaximized = FALSE; SendMessage(GetParent(parent), WM_NCPAINT, 1, 0); MDIBringChildToTop(parent, child, FALSE, FALSE); return 0; } /********************************************************************** * MDIChildActivated */ LONG MDIChildActivated(WND *w, MDICLIENTINFO *ci, HWND parent) { MDICHILDINFO *chi; HWND deact_hwnd; HWND act_hwnd; LONG lParam; #ifdef DEBUG_MDI fprintf(stderr, "MDIChildActivate: top %04.4x\n", w->hwndChild); #endif chi = ci->infoActiveChildren; if (chi) { deact_hwnd = ci->hwndActiveChild; act_hwnd = chi->hwnd; lParam = ((LONG) deact_hwnd << 16) | act_hwnd; #ifdef DEBUG_MDI fprintf(stderr, "MDIChildActivate: deact %04.4x, act %04.4x\n", deact_hwnd, act_hwnd); #endif ci->hwndActiveChild = act_hwnd; if (deact_hwnd != act_hwnd) { MDIRecreateMenuList(ci); SendMessage(deact_hwnd, WM_NCACTIVATE, FALSE, 0); SendMessage(deact_hwnd, WM_MDIACTIVATE, FALSE, lParam); } SendMessage(act_hwnd, WM_NCACTIVATE, TRUE, 0); SendMessage(act_hwnd, WM_MDIACTIVATE, TRUE, lParam); } if (chi || ci->nActiveChildren == 0) { MDIRecreateMenuList(ci); SendMessage(GetParent(parent), WM_NCPAINT, 0, 0); } return 0; } /********************************************************************** * MDICascade */ LONG MDICascade(HWND parent, MDICLIENTINFO *ci) { MDICHILDINFO *chi; RECT rect; int spacing, xsize, ysize; int x, y; if (ci->flagChildMaximized) MDIRestoreChild(parent, ci); GetClientRect(parent, &rect); spacing = GetSystemMetrics(SM_CYCAPTION) + GetSystemMetrics(SM_CYFRAME); ysize = rect.bottom - 8 * spacing; xsize = rect.right - 8 * spacing; #ifdef DEBUG_MDI fprintf(stderr, "MDICascade: Client wnd at (%d,%d) - (%d,%d), spacing %d\n", rect.left, rect.top, rect.right, rect.bottom, spacing); fprintf(stderr, "MDICascade: searching for last child\n"); #endif for (chi = ci->infoActiveChildren; chi->next != NULL; chi = chi->next) ; #ifdef DEBUG_MDI fprintf(stderr, "MDICascade: last child is %04.4x\n", chi->hwnd); #endif x = 0; y = 0; for ( ; chi != NULL; chi = chi->prev) { #ifdef DEBUG_MDI fprintf(stderr, "MDICascade: move %04.4x to (%d,%d) size [%d,%d]\n", chi->hwnd, x, y, xsize, ysize); #endif SetWindowPos(chi->hwnd, 0, x, y, xsize, ysize, SWP_DRAWFRAME | SWP_NOACTIVATE | SWP_NOZORDER); x += spacing; y += spacing; } return 0; } /********************************************************************** * MDITile */ LONG MDITile(HWND parent, MDICLIENTINFO *ci) { MDICHILDINFO *chi; RECT rect; int xsize, ysize; int x, y; int rows, columns; int r, c; int i; if (ci->flagChildMaximized) MDIRestoreChild(parent, ci); GetClientRect(parent, &rect); rows = (int) sqrt((double) ci->nActiveChildren); columns = ci->nActiveChildren / rows; ysize = rect.bottom / rows; xsize = rect.right / columns; chi = ci->infoActiveChildren; x = 0; i = 0; for (c = 1; c <= columns; c++) { if (c == columns) { rows = ci->nActiveChildren - i; ysize = rect.bottom / rows; } y = 0; for (r = 1; r <= rows; r++, i++, chi = chi->next) { SetWindowPos(chi->hwnd, 0, x, y, xsize, ysize, SWP_DRAWFRAME | SWP_NOACTIVATE | SWP_NOZORDER); y += ysize; } x += xsize; } return 0; } /********************************************************************** * MDIHandleLButton */ BOOL MDIHandleLButton(HWND hwndFrame, HWND hwndClient, WORD wParam, LONG lParam) { MDICLIENTINFO *ci; WND *w; RECT rect; WORD x; w = WIN_FindWndPtr(hwndClient); ci = (MDICLIENTINFO *) w->wExtra; if (wParam == HTMENU && ci->flagChildMaximized) { x = LOWORD(lParam); NC_GetInsideRect(hwndFrame, &rect); if (x < rect.left + SYSMETRICS_CXSIZE) { SendMessage(ci->hwndActiveChild, WM_SYSCOMMAND, SC_CLOSE, lParam); return TRUE; } else if (x >= rect.right - SYSMETRICS_CXSIZE) { SendMessage(ci->hwndActiveChild, WM_SYSCOMMAND, SC_RESTORE, lParam); return TRUE; } } return FALSE; } /********************************************************************** * MDIPaintMaximized */ LONG MDIPaintMaximized(HWND hwndFrame, HWND hwndClient, WORD message, WORD wParam, LONG lParam) { static HBITMAP hbitmapClose = 0; static HBITMAP hbitmapMaximized = 0; MDICLIENTINFO *ci; WND *w; LONG rv; HDC hdc, hdcMem; RECT rect; WND *wndPtr = WIN_FindWndPtr(hwndFrame); w = WIN_FindWndPtr(hwndClient); ci = (MDICLIENTINFO *) w->wExtra; #ifdef DEBUG_MDI fprintf(stderr, "MDIPaintMaximized: frame %04x, client %04x" ", max flag %d, menu %04x\n", hwndFrame, hwndClient, ci->flagChildMaximized, wndPtr ? wndPtr->wIDmenu : 0); #endif if (ci->flagChildMaximized && wndPtr && wndPtr->wIDmenu != 0) { rv = NC_DoNCPaint( hwndFrame, (HRGN) 1, wParam, TRUE); hdc = GetDCEx(hwndFrame, 0, DCX_CACHE | DCX_WINDOW); if (!hdc) return rv; hdcMem = CreateCompatibleDC(hdc); if (hbitmapClose == 0) { hbitmapClose = LoadBitmap(0, MAKEINTRESOURCE(OBM_OLD_CLOSE)); hbitmapMaximized = LoadBitmap(0, MAKEINTRESOURCE(OBM_RESTORE)); } #ifdef DEBUG_MDI fprintf(stderr, "MDIPaintMaximized: hdcMem %04x, close bitmap %04x, " "maximized bitmap %04x\n", hdcMem, hbitmapClose, hbitmapMaximized); #endif NC_GetInsideRect(hwndFrame, &rect); rect.top += ((wndPtr->dwStyle & WS_CAPTION) ? SYSMETRICS_CYSIZE + 1 : 0); SelectObject(hdcMem, hbitmapClose); BitBlt(hdc, rect.left, rect.top + 1, SYSMETRICS_CXSIZE, SYSMETRICS_CYSIZE, hdcMem, 1, 1, SRCCOPY); NC_GetInsideRect(hwndFrame, &rect); rect.top += ((wndPtr->dwStyle & WS_CAPTION) ? SYSMETRICS_CYSIZE + 1 : 0); rect.left = rect.right - SYSMETRICS_CXSIZE; SelectObject(hdcMem, hbitmapMaximized); BitBlt(hdc, rect.left, rect.top + 1, SYSMETRICS_CXSIZE, SYSMETRICS_CYSIZE, hdcMem, 1, 1, SRCCOPY); NC_GetInsideRect(hwndFrame, &rect); rect.top += ((wndPtr->dwStyle & WS_CAPTION) ? SYSMETRICS_CYSIZE + 1 : 0); rect.left += SYSMETRICS_CXSIZE; rect.right -= SYSMETRICS_CXSIZE; rect.bottom = rect.top + SYSMETRICS_CYMENU; MENU_DrawMenuBar(hdc, &rect, wndPtr->wIDmenu, FALSE); DeleteDC(hdcMem); ReleaseDC(hwndFrame, hdc); } else DefWindowProc(hwndFrame, message, wParam, lParam); return rv; } /********************************************************************** * MDIClientWndProc * * This function is the handler for all MDI requests. */ LONG MDIClientWndProc(HWND hwnd, WORD message, WORD wParam, LONG lParam) { LPCREATESTRUCT cs; LPCLIENTCREATESTRUCT ccs; MDICLIENTINFO *ci; WND *w; w = WIN_FindWndPtr(hwnd); ci = (MDICLIENTINFO *) w->wExtra; switch (message) { case WM_CHILDACTIVATE: return MDIChildActivated(w, ci, hwnd); case WM_CREATE: cs = (LPCREATESTRUCT) lParam; ccs = (LPCLIENTCREATESTRUCT) cs->lpCreateParams; ci->hWindowMenu = ccs->hWindowMenu; ci->idFirstChild = ccs->idFirstChild; ci->infoActiveChildren = NULL; ci->flagMenuAltered = FALSE; ci->flagChildMaximized = FALSE; w->dwStyle |= WS_CLIPCHILDREN; GetClientRect(w->hwndParent, &ci->rectMaximize); MoveWindow(hwnd, 0, 0, ci->rectMaximize.right, ci->rectMaximize.bottom, 1); return 0; case WM_MDIACTIVATE: MDIBringChildToTop(hwnd, wParam, FALSE, FALSE); return 0; case WM_MDICASCADE: return MDICascade(hwnd, ci); case WM_MDICREATE: return MDICreateChild(w, ci, hwnd, (LPMDICREATESTRUCT) lParam); case WM_MDIDESTROY: return MDIDestroyChild(w, ci, hwnd, wParam, TRUE); case WM_MDIGETACTIVE: return ((LONG) ci->hwndActiveChild | ((LONG) ci->flagChildMaximized << 16)); case WM_MDIICONARRANGE: /* return MDIIconArrange(...) */ break; case WM_MDIMAXIMIZE: return MDIMaximizeChild(hwnd, wParam, ci); case WM_MDINEXT: MDIBringChildToTop(hwnd, wParam, FALSE, TRUE); break; case WM_MDIRESTORE: return MDIRestoreChild(hwnd, ci); case WM_MDISETMENU: /* return MDISetMenu(...) */ break; case WM_MDITILE: return MDITile(hwnd, ci); case WM_NCACTIVATE: SendMessage(ci->hwndActiveChild, message, wParam, lParam); break; case WM_PARENTNOTIFY: if (wParam == WM_DESTROY) return MDIDestroyChild(w, ci, hwnd, LOWORD(lParam), FALSE); else if (wParam == WM_LBUTTONDOWN) MDIBringChildToTop(hwnd, ci->hwndHitTest, FALSE, FALSE); break; case WM_SIZE: GetClientRect(w->hwndParent, &ci->rectMaximize); break; } return DefWindowProc(hwnd, message, wParam, lParam); } /********************************************************************** * DefFrameProc (USER.445) * */ LONG DefFrameProc(HWND hwnd, HWND hwndMDIClient, WORD message, WORD wParam, LONG lParam) { if (hwndMDIClient) { switch (message) { case WM_COMMAND: MDIBringChildToTop(hwndMDIClient, wParam, TRUE, FALSE); break; case WM_NCLBUTTONDOWN: if (MDIHandleLButton(hwnd, hwndMDIClient, wParam, lParam)) return 0; break; case WM_NCACTIVATE: SendMessage(hwndMDIClient, message, wParam, lParam); return MDIPaintMaximized(hwnd, hwndMDIClient, message, wParam, lParam); case WM_NCPAINT: return MDIPaintMaximized(hwnd, hwndMDIClient, message, wParam, lParam); case WM_SETFOCUS: SendMessage(hwndMDIClient, WM_SETFOCUS, wParam, lParam); break; case WM_SIZE: MoveWindow(hwndMDIClient, 0, 0, LOWORD(lParam), HIWORD(lParam), TRUE); break; } } return DefWindowProc(hwnd, message, wParam, lParam); } /********************************************************************** * DefMDIChildProc (USER.447) * */ LONG DefMDIChildProc(HWND hwnd, WORD message, WORD wParam, LONG lParam) { MDICLIENTINFO *ci; WND *w; w = WIN_FindWndPtr(GetParent(hwnd)); ci = (MDICLIENTINFO *) w->wExtra; switch (message) { case WM_NCHITTEST: ci->hwndHitTest = hwnd; break; case WM_NCPAINT: return NC_DoNCPaint(hwnd, (HRGN)1, hwnd == ci->hwndActiveChild); case WM_SYSCOMMAND: switch (wParam) { case SC_MAXIMIZE: return SendMessage(GetParent(hwnd), WM_MDIMAXIMIZE, hwnd, 0); case SC_RESTORE: if (IsIconic(hwnd)) ICON_Deiconify(hwnd); return SendMessage(GetParent(hwnd), WM_MDIRESTORE, hwnd, 0); } break; } return DefWindowProc(hwnd, message, wParam, lParam); } /********************************************************************** * TranslateMDISysAccel (USER.451) * */ BOOL TranslateMDISysAccel(HWND hwndClient, LPMSG msg) { return 0; }