3 * Copyright 1994, Bob Amstadt
5 * This file contains routines to support MDI features.
12 #include "nonclient.h"
16 #include "sysmetrics.h"
18 /* #define DEBUG_MDI */
21 /**********************************************************************
25 MDIRecreateMenuList(MDICLIENTINFO *ci)
31 dprintf_mdi(stddeb, "MDIRecreateMenuList: hWindowMenu %0x\n",
34 id = ci->idFirstChild;
35 while (DeleteMenu(ci->hWindowMenu, id, MF_BYCOMMAND))
38 dprintf_mdi(stddeb, "MDIRecreateMenuList: id %04x, idFirstChild %04x\n",
39 id, ci->idFirstChild);
41 if (!ci->flagMenuAltered)
43 ci->flagMenuAltered = TRUE;
44 AppendMenu(ci->hWindowMenu, MF_SEPARATOR, 0, NULL);
47 id = ci->idFirstChild;
49 for (chi = ci->infoActiveChildren; chi != NULL; chi = chi->next)
51 n = sprintf(buffer, "%d ", index++);
52 GetWindowText(chi->hwnd, buffer + n, sizeof(buffer) - n - 1);
54 dprintf_mdi(stddeb, "MDIRecreateMenuList: id %04x, '%s'\n",
57 AppendMenu(ci->hWindowMenu, MF_STRING, id++, buffer);
61 /**********************************************************************
65 MDICreateChild(WND *w, MDICLIENTINFO *ci, HWND parent, LPMDICREATESTRUCT cs)
72 cs->style &= (WS_MINIMIZE | WS_MAXIMIZE | WS_HSCROLL | WS_VSCROLL);
74 hwnd = CreateWindowEx(0, cs->szClass, cs->szTitle,
75 WS_CHILD | WS_BORDER | WS_CAPTION | WS_CLIPSIBLINGS |
76 WS_MAXIMIZEBOX | WS_MINIMIZEBOX | WS_SYSMENU |
77 WS_THICKFRAME | WS_VISIBLE | cs->style,
78 cs->x, cs->y, cs->cx, cs->cy, parent, (HMENU) 0,
79 w->hInstance, (LPSTR) cs);
83 HANDLE h = USER_HEAP_ALLOC(GMEM_MOVEABLE, sizeof(MDICHILDINFO));
84 MDICHILDINFO *child_info = USER_HEAP_ADDR(h);
91 ci->nActiveChildren++;
93 child_info->next = ci->infoActiveChildren;
94 child_info->prev = NULL;
95 child_info->hwnd = hwnd;
97 if (ci->infoActiveChildren)
98 ci->infoActiveChildren->prev = child_info;
100 ci->infoActiveChildren = child_info;
102 SendMessage(parent, WM_CHILDACTIVATE, 0, 0);
108 /**********************************************************************
112 MDIDestroyChild(WND *w_parent, MDICLIENTINFO *ci, HWND parent, HWND child,
117 chi = ci->infoActiveChildren;
118 while (chi && chi->hwnd != child)
124 chi->prev->next = chi->next;
126 chi->next->prev = chi->prev;
127 if (ci->infoActiveChildren == chi)
128 ci->infoActiveChildren = chi->next;
130 ci->nActiveChildren--;
132 if (chi->hwnd == ci->hwndActiveChild)
133 SendMessage(parent, WM_CHILDACTIVATE, 0, 0);
135 USER_HEAP_FREE((HANDLE) (LONG) chi);
138 DestroyWindow(child);
144 /**********************************************************************
147 void MDIBringChildToTop(HWND parent, WORD id, WORD by_id, BOOL send_to_bottom)
154 w = WIN_FindWndPtr(parent);
155 ci = (MDICLIENTINFO *) w->wExtra;
157 dprintf_mdi(stddeb, "MDIBringToTop: id %04x, by_id %d\n", id, by_id);
160 id -= ci->idFirstChild;
161 if (!by_id || id < ci->nActiveChildren)
163 chi = ci->infoActiveChildren;
167 for (i = 0; i < id; i++)
172 while (chi && chi->hwnd != id)
179 dprintf_mdi(stddeb, "MDIBringToTop: child %04x\n", chi->hwnd);
180 if (chi != ci->infoActiveChildren)
182 if (ci->flagChildMaximized)
184 RECT rectOldRestore, rect;
186 w = WIN_FindWndPtr(chi->hwnd);
188 rectOldRestore = ci->rectRestore;
189 GetWindowRect(chi->hwnd, &ci->rectRestore);
191 rect.top = (ci->rectMaximize.top -
192 (w->rectClient.top - w->rectWindow.top));
193 rect.bottom = (ci->rectMaximize.bottom +
194 (w->rectWindow.bottom - w->rectClient.bottom));
195 rect.left = (ci->rectMaximize.left -
196 (w->rectClient.left - w->rectWindow.left));
197 rect.right = (ci->rectMaximize.right +
198 (w->rectWindow.right - w->rectClient.right));
199 w->dwStyle |= WS_MAXIMIZE;
200 SetWindowPos(chi->hwnd, HWND_TOP, rect.left, rect.top,
201 rect.right - rect.left + 1,
202 rect.bottom - rect.top + 1, 0);
203 SendMessage(chi->hwnd, WM_SIZE, SIZE_MAXIMIZED,
204 MAKELONG(w->rectClient.right-w->rectClient.left,
205 w->rectClient.bottom-w->rectClient.top));
207 w = WIN_FindWndPtr(ci->hwndActiveChild);
208 w->dwStyle &= ~WS_MAXIMIZE;
209 SetWindowPos(ci->hwndActiveChild, HWND_BOTTOM,
210 rectOldRestore.left, rectOldRestore.top,
211 rectOldRestore.right - rectOldRestore.left + 1,
212 rectOldRestore.bottom - rectOldRestore.top + 1,
214 (send_to_bottom ? 0 : SWP_NOZORDER));
218 SetWindowPos(chi->hwnd, HWND_TOP, 0, 0, 0, 0,
219 SWP_NOMOVE | SWP_NOSIZE );
222 SetWindowPos(ci->hwndActiveChild, HWND_BOTTOM, 0, 0, 0, 0,
223 SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
228 chi->next->prev = chi->prev;
231 chi->prev->next = chi->next;
234 chi->next = ci->infoActiveChildren;
235 chi->next->prev = chi;
236 ci->infoActiveChildren = chi;
238 SendMessage(parent, WM_CHILDACTIVATE, 0, 0);
241 dprintf_mdi(stddeb, "MDIBringToTop: pos %04x, hwnd %04x\n",
246 /**********************************************************************
249 LONG MDIMaximizeChild(HWND parent, HWND child, MDICLIENTINFO *ci)
251 WND *w = WIN_FindWndPtr(child);
254 MDIBringChildToTop(parent, child, FALSE, FALSE);
255 ci->rectRestore = w->rectWindow;
257 rect.top = (ci->rectMaximize.top -
258 (w->rectClient.top - w->rectWindow.top));
259 rect.bottom = (ci->rectMaximize.bottom +
260 (w->rectWindow.bottom - w->rectClient.bottom));
261 rect.left = (ci->rectMaximize.left -
262 (w->rectClient.left - w->rectWindow.left));
263 rect.right = (ci->rectMaximize.right +
264 (w->rectWindow.right - w->rectClient.right));
265 w->dwStyle |= WS_MAXIMIZE;
266 SetWindowPos(child, 0, rect.left, rect.top,
267 rect.right - rect.left + 1, rect.bottom - rect.top + 1,
268 SWP_NOACTIVATE | SWP_NOZORDER);
270 ci->flagChildMaximized = TRUE;
272 SendMessage(child, WM_SIZE, SIZE_MAXIMIZED,
273 MAKELONG(w->rectClient.right-w->rectClient.left,
274 w->rectClient.bottom-w->rectClient.top));
275 SendMessage(GetParent(parent), WM_NCPAINT, 0, 0);
280 /**********************************************************************
283 LONG MDIRestoreChild(HWND parent, MDICLIENTINFO *ci)
287 dprintf_mdi(stddeb,"restoring mdi child\n");
289 child = ci->hwndActiveChild;
290 ci->flagChildMaximized = FALSE;
292 ShowWindow(child, SW_RESTORE); /* display the window */
293 MDIBringChildToTop(parent, child, FALSE, FALSE);
298 /**********************************************************************
301 LONG MDIChildActivated(WND *w, MDICLIENTINFO *ci, HWND parent)
308 dprintf_mdi(stddeb, "MDIChildActivate: top %04x\n", w->hwndChild);
310 chi = ci->infoActiveChildren;
313 deact_hwnd = ci->hwndActiveChild;
314 act_hwnd = chi->hwnd;
315 lParam = ((LONG) deact_hwnd << 16) | act_hwnd;
317 dprintf_mdi(stddeb, "MDIChildActivate: deact %04x, act %04x\n",
318 deact_hwnd, act_hwnd);
320 ci->hwndActiveChild = act_hwnd;
322 if (deact_hwnd != act_hwnd)
324 MDIRecreateMenuList(ci);
325 SendMessage(deact_hwnd, WM_NCACTIVATE, FALSE, 0);
326 SendMessage(deact_hwnd, WM_MDIACTIVATE, FALSE, lParam);
329 SendMessage(act_hwnd, WM_NCACTIVATE, TRUE, 0);
330 SendMessage(act_hwnd, WM_MDIACTIVATE, TRUE, lParam);
333 if (chi || ci->nActiveChildren == 0)
335 MDIRecreateMenuList(ci);
336 SendMessage(GetParent(parent), WM_NCPAINT, 0, 0);
342 /**********************************************************************
345 LONG MDICascade(HWND parent, MDICLIENTINFO *ci)
349 int spacing, xsize, ysize;
352 if (ci->flagChildMaximized)
353 MDIRestoreChild(parent, ci);
355 /* If there aren't any children, don't even bother.
357 if (ci->nActiveChildren == 0)
360 GetClientRect(parent, &rect);
361 spacing = GetSystemMetrics(SM_CYCAPTION) + GetSystemMetrics(SM_CYFRAME);
362 ysize = rect.bottom - 8 * spacing;
363 xsize = rect.right - 8 * spacing;
366 "MDICascade: Client wnd at (%d,%d) - (%d,%d), spacing %d\n",
367 rect.left, rect.top, rect.right, rect.bottom, spacing);
368 dprintf_mdi(stddeb, "MDICascade: searching for last child\n");
369 for (chi = ci->infoActiveChildren; chi->next != NULL; chi = chi->next)
372 dprintf_mdi(stddeb, "MDICascade: last child is %04x\n", chi->hwnd);
375 for ( ; chi != NULL; chi = chi->prev)
377 dprintf_mdi(stddeb, "MDICascade: move %04x to (%d,%d) size [%d,%d]\n",
378 chi->hwnd, x, y, xsize, ysize);
379 SetWindowPos(chi->hwnd, 0, x, y, xsize, ysize,
380 SWP_DRAWFRAME | SWP_NOACTIVATE | SWP_NOZORDER);
389 /**********************************************************************
392 LONG MDITile(HWND parent, MDICLIENTINFO *ci)
402 if (ci->flagChildMaximized)
403 MDIRestoreChild(parent, ci);
405 /* If there aren't any children, don't even bother.
407 if (ci->nActiveChildren == 0)
410 GetClientRect(parent, &rect);
411 rows = (int) sqrt((double) ci->nActiveChildren);
412 columns = ci->nActiveChildren / rows;
413 ysize = rect.bottom / rows;
414 xsize = rect.right / columns;
416 chi = ci->infoActiveChildren;
419 for (c = 1; c <= columns; c++)
423 rows = ci->nActiveChildren - i;
424 ysize = rect.bottom / rows;
428 for (r = 1; r <= rows; r++, i++, chi = chi->next)
430 SetWindowPos(chi->hwnd, 0, x, y, xsize, ysize,
431 SWP_DRAWFRAME | SWP_NOACTIVATE | SWP_NOZORDER);
443 /**********************************************************************
446 BOOL MDIHandleLButton(HWND hwndFrame, HWND hwndClient,
447 WORD wParam, LONG lParam)
454 w = WIN_FindWndPtr(hwndClient);
455 ci = (MDICLIENTINFO *) w->wExtra;
457 if (wParam == HTMENU && ci->flagChildMaximized)
461 NC_GetInsideRect(hwndFrame, &rect);
462 if (x < rect.left + SYSMETRICS_CXSIZE)
464 SendMessage(ci->hwndActiveChild, WM_SYSCOMMAND,
468 else if (x >= rect.right - SYSMETRICS_CXSIZE)
470 SendMessage(ci->hwndActiveChild, WM_SYSCOMMAND,
479 /**********************************************************************
482 LONG MDIPaintMaximized(HWND hwndFrame, HWND hwndClient, WORD message,
483 WORD wParam, LONG lParam)
485 static HBITMAP hbitmapClose = 0;
486 static HBITMAP hbitmapMaximized = 0;
492 WND *wndPtr = WIN_FindWndPtr(hwndFrame);
494 w = WIN_FindWndPtr(hwndClient);
495 ci = (MDICLIENTINFO *) w->wExtra;
498 "MDIPaintMaximized: frame %04x, client %04x"
499 ", max flag %d, menu %04x\n",
500 hwndFrame, hwndClient,
501 ci->flagChildMaximized, wndPtr ? wndPtr->wIDmenu : 0);
503 if (ci->flagChildMaximized && wndPtr && wndPtr->wIDmenu != 0)
505 NC_DoNCPaint( hwndFrame, wParam, TRUE);
507 hdc = GetDCEx(hwndFrame, 0, DCX_CACHE | DCX_WINDOW);
510 hdcMem = CreateCompatibleDC(hdc);
512 if (hbitmapClose == 0)
514 hbitmapClose = LoadBitmap(0, MAKEINTRESOURCE(OBM_OLD_CLOSE));
515 hbitmapMaximized = LoadBitmap(0, MAKEINTRESOURCE(OBM_RESTORE));
519 "MDIPaintMaximized: hdcMem %04x, close bitmap %04x, "
520 "maximized bitmap %04x\n",
521 hdcMem, hbitmapClose, hbitmapMaximized);
523 NC_GetInsideRect(hwndFrame, &rect);
524 rect.top += ((wndPtr->dwStyle & WS_CAPTION) ?
525 SYSMETRICS_CYSIZE + 1 : 0);
526 SelectObject(hdcMem, hbitmapClose);
527 BitBlt(hdc, rect.left, rect.top + 1,
528 SYSMETRICS_CXSIZE, SYSMETRICS_CYSIZE,
529 hdcMem, 1, 1, SRCCOPY);
531 NC_GetInsideRect(hwndFrame, &rect);
532 rect.top += ((wndPtr->dwStyle & WS_CAPTION) ?
533 SYSMETRICS_CYSIZE + 1 : 0);
534 rect.left = rect.right - SYSMETRICS_CXSIZE;
535 SelectObject(hdcMem, hbitmapMaximized);
536 BitBlt(hdc, rect.left, rect.top + 1,
537 SYSMETRICS_CXSIZE, SYSMETRICS_CYSIZE,
538 hdcMem, 1, 1, SRCCOPY);
540 NC_GetInsideRect(hwndFrame, &rect);
541 rect.top += ((wndPtr->dwStyle & WS_CAPTION) ?
542 SYSMETRICS_CYSIZE + 1 : 0);
543 rect.left += SYSMETRICS_CXSIZE;
544 rect.right -= SYSMETRICS_CXSIZE;
545 rect.bottom = rect.top + SYSMETRICS_CYMENU;
547 MENU_DrawMenuBar(hdc, &rect, hwndFrame, FALSE);
550 ReleaseDC(hwndFrame, hdc);
553 DefWindowProc(hwndFrame, message, wParam, lParam);
558 /**********************************************************************
561 * This function is the handler for all MDI requests.
564 MDIClientWndProc(HWND hwnd, WORD message, WORD wParam, LONG lParam)
567 LPCLIENTCREATESTRUCT ccs;
571 w = WIN_FindWndPtr(hwnd);
572 ci = (MDICLIENTINFO *) w->wExtra;
576 case WM_CHILDACTIVATE:
577 return MDIChildActivated(w, ci, hwnd);
580 cs = (LPCREATESTRUCT) lParam;
581 ccs = (LPCLIENTCREATESTRUCT) cs->lpCreateParams;
582 ci->hWindowMenu = ccs->hWindowMenu;
583 ci->idFirstChild = ccs->idFirstChild;
584 ci->infoActiveChildren = NULL;
585 ci->flagMenuAltered = FALSE;
586 ci->flagChildMaximized = FALSE;
587 w->dwStyle |= WS_CLIPCHILDREN;
589 GetClientRect(w->hwndParent, &ci->rectMaximize);
590 MoveWindow(hwnd, 0, 0,
591 ci->rectMaximize.right, ci->rectMaximize.bottom, 1);
596 MDIBringChildToTop(hwnd, wParam, FALSE, FALSE);
600 return MDICascade(hwnd, ci);
603 return MDICreateChild(w, ci, hwnd, (LPMDICREATESTRUCT) lParam);
606 return MDIDestroyChild(w, ci, hwnd, wParam, TRUE);
608 case WM_MDIGETACTIVE:
609 return ((LONG) ci->hwndActiveChild |
610 ((LONG) ci->flagChildMaximized << 16));
612 case WM_MDIICONARRANGE:
613 /* return MDIIconArrange(...) */
617 return MDIMaximizeChild(hwnd, wParam, ci);
620 MDIBringChildToTop(hwnd, wParam, FALSE, TRUE);
624 return MDIRestoreChild(hwnd, ci);
627 /* return MDISetMenu(...) */
631 return MDITile(hwnd, ci);
634 SendMessage(ci->hwndActiveChild, message, wParam, lParam);
637 case WM_PARENTNOTIFY:
638 if (wParam == WM_DESTROY)
639 return MDIDestroyChild(w, ci, hwnd, LOWORD(lParam), FALSE);
640 else if (wParam == WM_LBUTTONDOWN)
641 MDIBringChildToTop(hwnd, ci->hwndHitTest, FALSE, FALSE);
645 GetClientRect(w->hwndParent, &ci->rectMaximize);
650 return DefWindowProc(hwnd, message, wParam, lParam);
653 /**********************************************************************
654 * DefFrameProc (USER.445)
658 DefFrameProc(HWND hwnd, HWND hwndMDIClient, WORD message,
659 WORD wParam, LONG lParam)
666 MDIBringChildToTop(hwndMDIClient, wParam, TRUE, FALSE);
669 case WM_NCLBUTTONDOWN:
670 if (MDIHandleLButton(hwnd, hwndMDIClient, wParam, lParam))
675 SendMessage(hwndMDIClient, message, wParam, lParam);
676 return MDIPaintMaximized(hwnd, hwndMDIClient,
677 message, wParam, lParam);
680 return MDIPaintMaximized(hwnd, hwndMDIClient,
681 message, wParam, lParam);
684 SendMessage(hwndMDIClient, WM_SETFOCUS, wParam, lParam);
688 MoveWindow(hwndMDIClient, 0, 0,
689 LOWORD(lParam), HIWORD(lParam), TRUE);
694 return DefWindowProc(hwnd, message, wParam, lParam);
697 /**********************************************************************
698 * DefMDIChildProc (USER.447)
702 DefMDIChildProc(HWND hwnd, WORD message, WORD wParam, LONG lParam)
707 w = WIN_FindWndPtr(GetParent(hwnd));
708 ci = (MDICLIENTINFO *) w->wExtra;
713 ci->hwndHitTest = hwnd;
717 NC_DoNCPaint( hwnd, hwnd == ci->hwndActiveChild, FALSE );
724 return SendMessage(GetParent(hwnd), WM_MDIMAXIMIZE, hwnd, 0);
727 return SendMessage(GetParent(hwnd), WM_MDIRESTORE, hwnd, 0);
733 return DefWindowProc(hwnd, message, wParam, lParam);
736 /**********************************************************************
737 * TranslateMDISysAccel (USER.451)
740 BOOL TranslateMDISysAccel(HWND hwndClient, LPMSG msg)