3 * Copyright 1994, Bob Amstadt
5 * This file contains routines to support MDI features.
12 #include "nonclient.h"
16 #include "sysmetrics.h"
20 /**********************************************************************
23 void MDIRecreateMenuList(MDICLIENTINFO *ci)
30 dprintf_mdi(stddeb, "MDIRecreateMenuList: hWindowMenu %0x\n",
33 id = ci->idFirstChild;
34 while (DeleteMenu(ci->hWindowMenu, id, MF_BYCOMMAND))
37 dprintf_mdi(stddeb, "MDIRecreateMenuList: id %04x, idFirstChild %04x\n",
38 id, ci->idFirstChild);
40 if (!ci->flagMenuAltered)
42 ci->flagMenuAltered = TRUE;
43 AppendMenu(ci->hWindowMenu, MF_SEPARATOR, 0, NULL);
46 id = ci->idFirstChild;
48 for (hinfo = ci->infoActiveChildren; hinfo != 0;)
50 MDICHILDINFO *chi = USER_HEAP_LIN_ADDR(hinfo);
52 n = sprintf(buffer, "%d ", index++);
53 GetWindowText(chi->hwnd, buffer + n, sizeof(buffer) - n - 1);
55 dprintf_mdi(stddeb, "MDIRecreateMenuList: id %04x, '%s'\n",
58 AppendMenu(ci->hWindowMenu, MF_STRING, id++, buffer);
63 /**********************************************************************
65 * FIXME: This is not complete.
67 HMENU MDISetMenu(HWND hwnd, BOOL fRefresh, HMENU hmenuFrame, HMENU hmenuWindow)
69 dprintf_mdi(stddeb, "WM_MDISETMENU: %04x %04x %04x %04x\n", hwnd, fRefresh, hmenuFrame, hmenuWindow);
71 HWND hwndFrame = GetParent(hwnd);
72 HMENU oldFrameMenu = GetMenu(hwndFrame);
73 SetMenu(hwndFrame, hmenuFrame);
79 /**********************************************************************
82 WORD MDIIconArrange(HWND parent)
84 return ArrangeIconicWindows(parent); /* Any reason why the */
85 /* existing icon arrange */
86 /* can't be used here? */
90 /**********************************************************************
93 HWND MDICreateChild(WND *w, MDICLIENTINFO *ci, HWND parent, LPARAM lParam )
95 MDICREATESTRUCT *cs = (MDICREATESTRUCT *)PTR_SEG_TO_LIN(lParam);
100 * Create child window
102 cs->style &= (WS_MINIMIZE | WS_MAXIMIZE | WS_HSCROLL | WS_VSCROLL);
104 /* The child windows should probably */
105 /* stagger, shouldn't they? -DRP */
106 spacing = GetSystemMetrics(SM_CYCAPTION) + GetSystemMetrics(SM_CYFRAME);
107 cs->x = ci->nActiveChildren * spacing;
108 cs->y = ci->nActiveChildren * spacing;
110 hwnd = CreateWindowEx(0, PTR_SEG_TO_LIN(cs->szClass),
111 PTR_SEG_TO_LIN(cs->szTitle),
112 WS_CHILD | WS_BORDER | WS_CAPTION | WS_CLIPSIBLINGS |
113 WS_MAXIMIZEBOX | WS_MINIMIZEBOX | WS_SYSMENU |
114 WS_THICKFRAME | WS_VISIBLE | cs->style,
115 cs->x, cs->y, cs->cx, cs->cy, parent, (HMENU) 0,
116 w->hInstance, lParam);
120 HANDLE h = USER_HEAP_ALLOC( sizeof(MDICHILDINFO) );
121 MDICHILDINFO *child_info = USER_HEAP_LIN_ADDR(h);
129 ci->nActiveChildren++;
131 child_info->next = ci->infoActiveChildren;
132 child_info->prev = 0;
133 child_info->hwnd = hwnd;
135 if (ci->infoActiveChildren) {
136 MDICHILDINFO *nextinfo = USER_HEAP_LIN_ADDR(ci->infoActiveChildren);
140 ci->infoActiveChildren = h;
142 SendMessage(parent, WM_CHILDACTIVATE, 0, 0);
148 /**********************************************************************
151 HWND MDIDestroyChild(WND *w_parent, MDICLIENTINFO *ci, HWND parent,
152 HWND child, BOOL flagDestroy)
157 hinfo = ci->infoActiveChildren;
159 chi = (MDICHILDINFO *)USER_HEAP_LIN_ADDR(hinfo);
160 if (chi->hwnd == child) break;
167 ((MDICHILDINFO *)USER_HEAP_LIN_ADDR(chi->prev))->next = chi->next;
169 ((MDICHILDINFO *)USER_HEAP_LIN_ADDR(chi->next))->prev = chi->prev;
170 if (ci->infoActiveChildren == hinfo)
171 ci->infoActiveChildren = chi->next;
173 ci->nActiveChildren--;
175 if (chi->hwnd == ci->hwndActiveChild)
176 SendMessage(parent, WM_CHILDACTIVATE, 0, 0);
178 USER_HEAP_FREE(hinfo);
181 DestroyWindow(child);
187 /**********************************************************************
190 void MDIBringChildToTop(HWND parent, WORD id, WORD by_id, BOOL send_to_bottom)
198 w = WIN_FindWndPtr(parent);
199 ci = (MDICLIENTINFO *) w->wExtra;
201 dprintf_mdi(stddeb, "MDIBringToTop: id %04x, by_id %d\n", id, by_id);
204 id -= ci->idFirstChild;
205 if (!by_id || id < ci->nActiveChildren)
207 hinfo = ci->infoActiveChildren;
211 for (i = 0; i < id; i++)
212 hinfo = ((MDICHILDINFO *)USER_HEAP_LIN_ADDR(hinfo))->next;
213 chi = USER_HEAP_LIN_ADDR(hinfo);
218 chi = (MDICHILDINFO *)USER_HEAP_LIN_ADDR(hinfo);
219 if (chi->hwnd == id) break;
227 dprintf_mdi(stddeb, "MDIBringToTop: child %04x\n", chi->hwnd);
228 if (hinfo != ci->infoActiveChildren)
230 if (ci->flagChildMaximized)
232 RECT rectOldRestore, rect;
234 w = WIN_FindWndPtr(chi->hwnd);
236 rectOldRestore = ci->rectRestore;
237 GetWindowRect(chi->hwnd, &ci->rectRestore);
239 rect.top = (ci->rectMaximize.top -
240 (w->rectClient.top - w->rectWindow.top));
241 rect.bottom = (ci->rectMaximize.bottom +
242 (w->rectWindow.bottom - w->rectClient.bottom));
243 rect.left = (ci->rectMaximize.left -
244 (w->rectClient.left - w->rectWindow.left));
245 rect.right = (ci->rectMaximize.right +
246 (w->rectWindow.right - w->rectClient.right));
247 w->dwStyle |= WS_MAXIMIZE;
248 SetWindowPos(chi->hwnd, HWND_TOP, rect.left, rect.top,
249 rect.right - rect.left + 1,
250 rect.bottom - rect.top + 1, 0);
251 SendMessage(chi->hwnd, WM_SIZE, SIZE_MAXIMIZED,
252 MAKELONG(w->rectClient.right-w->rectClient.left,
253 w->rectClient.bottom-w->rectClient.top));
255 w = WIN_FindWndPtr(ci->hwndActiveChild);
256 w->dwStyle &= ~WS_MAXIMIZE;
257 SetWindowPos(ci->hwndActiveChild, HWND_BOTTOM,
258 rectOldRestore.left, rectOldRestore.top,
259 rectOldRestore.right - rectOldRestore.left + 1,
260 rectOldRestore.bottom - rectOldRestore.top + 1,
262 (send_to_bottom ? 0 : SWP_NOZORDER));
266 SetWindowPos(chi->hwnd, HWND_TOP, 0, 0, 0, 0,
267 SWP_NOMOVE | SWP_NOSIZE );
270 SetWindowPos(ci->hwndActiveChild, HWND_BOTTOM, 0, 0, 0, 0,
271 SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
276 ((MDICHILDINFO *)USER_HEAP_LIN_ADDR(chi->next))->prev = chi->prev;
279 ((MDICHILDINFO *)USER_HEAP_LIN_ADDR(chi->prev))->next = chi->next;
282 chi->next = ci->infoActiveChildren;
283 ((MDICHILDINFO *)USER_HEAP_LIN_ADDR(chi->next))->prev = hinfo;
284 ci->infoActiveChildren = hinfo;
286 SendMessage(parent, WM_CHILDACTIVATE, 0, 0);
289 dprintf_mdi(stddeb, "MDIBringToTop: pos %04x, hwnd %04x\n",
294 /**********************************************************************
297 LONG MDIMaximizeChild(HWND parent, HWND child, MDICLIENTINFO *ci)
299 WND *w = WIN_FindWndPtr(child);
302 MDIBringChildToTop(parent, child, FALSE, FALSE);
303 ci->rectRestore = w->rectWindow;
305 rect.top = (ci->rectMaximize.top -
306 (w->rectClient.top - w->rectWindow.top));
307 rect.bottom = (ci->rectMaximize.bottom +
308 (w->rectWindow.bottom - w->rectClient.bottom));
309 rect.left = (ci->rectMaximize.left -
310 (w->rectClient.left - w->rectWindow.left));
311 rect.right = (ci->rectMaximize.right +
312 (w->rectWindow.right - w->rectClient.right));
313 w->dwStyle |= WS_MAXIMIZE;
314 SetWindowPos(child, 0, rect.left, rect.top,
315 rect.right - rect.left + 1, rect.bottom - rect.top + 1,
316 SWP_NOACTIVATE | SWP_NOZORDER);
318 ci->flagChildMaximized = TRUE;
320 SendMessage(child, WM_SIZE, SIZE_MAXIMIZED,
321 MAKELONG(w->rectClient.right-w->rectClient.left,
322 w->rectClient.bottom-w->rectClient.top));
323 SendMessage(GetParent(parent), WM_NCPAINT, 0, 0);
328 /**********************************************************************
331 LONG MDIRestoreChild(HWND parent, MDICLIENTINFO *ci)
335 dprintf_mdi(stddeb,"restoring mdi child\n");
337 child = ci->hwndActiveChild;
338 ci->flagChildMaximized = FALSE;
340 ShowWindow(child, SW_RESTORE); /* display the window */
341 MDIBringChildToTop(parent, child, FALSE, FALSE);
342 SendMessage(GetParent(parent), WM_NCPAINT, 0, 0);
347 /**********************************************************************
350 LONG MDIChildActivated(WND *w, MDICLIENTINFO *ci, HWND parent)
358 dprintf_mdi(stddeb, "MDIChildActivate: top %04x\n", w->hwndChild);
360 hinfo = ci->infoActiveChildren;
363 chi = (MDICHILDINFO *)USER_HEAP_LIN_ADDR(hinfo);
364 deact_hwnd = ci->hwndActiveChild;
365 act_hwnd = chi->hwnd;
366 lParam = ((LONG) deact_hwnd << 16) | act_hwnd;
368 dprintf_mdi(stddeb, "MDIChildActivate: deact %04x, act %04x\n",
369 deact_hwnd, act_hwnd);
371 ci->hwndActiveChild = act_hwnd;
373 if (deact_hwnd != act_hwnd)
375 MDIRecreateMenuList(ci);
376 SendMessage(deact_hwnd, WM_NCACTIVATE, FALSE, 0);
377 SendMessage(deact_hwnd, WM_MDIACTIVATE, FALSE, lParam);
380 SendMessage(act_hwnd, WM_NCACTIVATE, TRUE, 0);
381 SendMessage(act_hwnd, WM_MDIACTIVATE, TRUE, lParam);
384 if (hinfo || ci->nActiveChildren == 0)
386 MDIRecreateMenuList(ci);
387 SendMessage(GetParent(parent), WM_NCPAINT, 0, 0);
393 /**********************************************************************
396 LONG MDICascade(HWND parent, MDICLIENTINFO *ci)
401 int spacing, xsize, ysize;
404 if (ci->flagChildMaximized)
405 MDIRestoreChild(parent, ci);
407 /* If there aren't any children, don't even bother.
409 if (ci->nActiveChildren == 0)
412 GetClientRect(parent, &rect);
413 spacing = GetSystemMetrics(SM_CYCAPTION) + GetSystemMetrics(SM_CYFRAME);
414 ysize = rect.bottom - 8 * spacing;
415 xsize = rect.right - 8 * spacing;
418 "MDICascade: Client wnd at (%d,%d) - (%d,%d), spacing %d\n",
419 rect.left, rect.top, rect.right, rect.bottom, spacing);
420 dprintf_mdi(stddeb, "MDICascade: searching for last child\n");
421 hinfo = ci->infoActiveChildren;
423 chi = USER_HEAP_LIN_ADDR(hinfo);
424 if (chi->next == 0) break;
428 dprintf_mdi(stddeb, "MDICascade: last child is %04x\n", chi->hwnd);
433 chi = USER_HEAP_LIN_ADDR(hinfo);
434 dprintf_mdi(stddeb, "MDICascade: move %04x to (%d,%d) size [%d,%d]\n",
435 chi->hwnd, x, y, xsize, ysize);
436 if (IsIconic(chi->hwnd)) continue;
437 SetWindowPos(chi->hwnd, 0, x, y, xsize, ysize,
438 SWP_DRAWFRAME | SWP_NOACTIVATE | SWP_NOZORDER);
449 /**********************************************************************
452 LONG MDITile(HWND parent, MDICLIENTINFO *ci)
463 if (ci->flagChildMaximized)
464 MDIRestoreChild(parent, ci);
466 /* If there aren't any children, don't even bother.
468 if (ci->nActiveChildren == 0)
471 GetClientRect(parent, &rect);
472 rows = (int) sqrt((double) ci->nActiveChildren);
473 columns = ci->nActiveChildren / rows;
474 ysize = rect.bottom / rows;
475 xsize = rect.right / columns;
477 hinfo = ci->infoActiveChildren;
480 for (c = 1; c <= columns; c++)
484 rows = ci->nActiveChildren - i;
485 ysize = rect.bottom / rows;
489 for (r = 1; r <= rows; r++, i++)
491 chi = (MDICHILDINFO *)USER_HEAP_LIN_ADDR(hinfo);
492 SetWindowPos(chi->hwnd, 0, x, y, xsize, ysize,
493 SWP_DRAWFRAME | SWP_NOACTIVATE | SWP_NOZORDER);
506 /**********************************************************************
509 BOOL MDIHandleLButton(HWND hwndFrame, HWND hwndClient,
510 WORD wParam, LONG lParam)
517 w = WIN_FindWndPtr(hwndClient);
518 ci = (MDICLIENTINFO *) w->wExtra;
520 if (wParam == HTMENU && ci->flagChildMaximized)
524 NC_GetInsideRect(hwndFrame, &rect);
525 if (x < rect.left + SYSMETRICS_CXSIZE)
527 SendMessage(ci->hwndActiveChild, WM_SYSCOMMAND,
531 else if (x >= rect.right - SYSMETRICS_CXSIZE)
533 SendMessage(ci->hwndActiveChild, WM_SYSCOMMAND,
542 /**********************************************************************
545 LONG MDIPaintMaximized(HWND hwndFrame, HWND hwndClient, WORD message,
546 WORD wParam, LONG lParam)
548 static HBITMAP hbitmapClose = 0;
549 static HBITMAP hbitmapMaximized = 0;
555 WND *wndPtr = WIN_FindWndPtr(hwndFrame);
557 w = WIN_FindWndPtr(hwndClient);
558 ci = (MDICLIENTINFO *) w->wExtra;
560 dprintf_mdi(stddeb, "MDIPaintMaximized: frame %04x, client %04x"
561 ", max flag %d, menu %04x\n", hwndFrame, hwndClient,
562 ci->flagChildMaximized, wndPtr ? wndPtr->wIDmenu : 0);
564 if (ci->flagChildMaximized && wndPtr && wndPtr->wIDmenu != 0)
566 NC_DoNCPaint(hwndFrame, wParam, TRUE);
568 hdc = GetDCEx(hwndFrame, 0, DCX_CACHE | DCX_WINDOW);
571 hdcMem = CreateCompatibleDC(hdc);
573 if (hbitmapClose == 0)
575 hbitmapClose = LoadBitmap(0, MAKEINTRESOURCE(OBM_OLD_CLOSE));
576 hbitmapMaximized = LoadBitmap(0, MAKEINTRESOURCE(OBM_RESTORE));
580 "MDIPaintMaximized: hdcMem %04x, close bitmap %04x, "
581 "maximized bitmap %04x\n",
582 hdcMem, hbitmapClose, hbitmapMaximized);
584 NC_GetInsideRect(hwndFrame, &rect);
585 rect.top += (wndPtr->dwStyle & WS_CAPTION) ? SYSMETRICS_CYSIZE + 1 : 0;
586 SelectObject(hdcMem, hbitmapClose);
587 BitBlt(hdc, rect.left, rect.top + 1,
588 SYSMETRICS_CXSIZE, SYSMETRICS_CYSIZE,
589 hdcMem, 1, 1, SRCCOPY);
591 NC_GetInsideRect(hwndFrame, &rect);
592 rect.top += (wndPtr->dwStyle & WS_CAPTION) ? SYSMETRICS_CYSIZE + 1 : 0;
593 rect.left = rect.right - SYSMETRICS_CXSIZE;
594 SelectObject(hdcMem, hbitmapMaximized);
595 BitBlt(hdc, rect.left, rect.top + 1,
596 SYSMETRICS_CXSIZE, SYSMETRICS_CYSIZE,
597 hdcMem, 1, 1, SRCCOPY);
599 NC_GetInsideRect(hwndFrame, &rect);
600 rect.top += (wndPtr->dwStyle & WS_CAPTION) ? SYSMETRICS_CYSIZE + 1 : 0;
601 rect.left += SYSMETRICS_CXSIZE;
602 rect.right -= SYSMETRICS_CXSIZE;
603 rect.bottom = rect.top + SYSMETRICS_CYMENU;
605 MENU_DrawMenuBar(hdc, &rect, hwndFrame, FALSE);
608 ReleaseDC(hwndFrame, hdc);
611 return DefWindowProc(hwndFrame, message, wParam, lParam);
616 /**********************************************************************
619 * This function is the handler for all MDI requests.
621 LONG MDIClientWndProc(HWND hwnd, WORD message, WORD wParam, LONG lParam)
624 LPCLIENTCREATESTRUCT ccs;
628 w = WIN_FindWndPtr(hwnd);
629 ci = (MDICLIENTINFO *) w->wExtra;
633 case WM_CHILDACTIVATE:
634 return MDIChildActivated(w, ci, hwnd);
637 cs = (LPCREATESTRUCT) PTR_SEG_TO_LIN(lParam);
638 ccs = (LPCLIENTCREATESTRUCT) PTR_SEG_TO_LIN(cs->lpCreateParams);
639 ci->hWindowMenu = ccs->hWindowMenu;
640 ci->idFirstChild = ccs->idFirstChild;
641 ci->infoActiveChildren = 0;
642 ci->flagMenuAltered = FALSE;
643 ci->flagChildMaximized = FALSE;
644 w->dwStyle |= WS_CLIPCHILDREN;
646 GetClientRect(w->hwndParent, &ci->rectMaximize);
647 MoveWindow(hwnd, 0, 0,
648 ci->rectMaximize.right, ci->rectMaximize.bottom, 1);
653 MDIBringChildToTop(hwnd, wParam, FALSE, FALSE);
657 return MDICascade(hwnd, ci);
660 return MDICreateChild(w, ci, hwnd, lParam );
663 return MDIDestroyChild(w, ci, hwnd, wParam, TRUE);
665 case WM_MDIGETACTIVE:
666 return ((LONG) ci->hwndActiveChild |
667 ((LONG) ci->flagChildMaximized << 16));
669 case WM_MDIICONARRANGE:
670 return MDIIconArrange(hwnd);
673 return MDIMaximizeChild(hwnd, wParam, ci);
676 MDIBringChildToTop(hwnd, wParam, FALSE, TRUE);
680 return MDIRestoreChild(hwnd, ci);
683 return MDISetMenu(hwnd, wParam, LOWORD(lParam), HIWORD(lParam));
686 return MDITile(hwnd, ci);
689 SendMessage(ci->hwndActiveChild, message, wParam, lParam);
692 case WM_PARENTNOTIFY:
693 if (wParam == WM_DESTROY)
694 return MDIDestroyChild(w, ci, hwnd, LOWORD(lParam), FALSE);
695 else if (wParam == WM_LBUTTONDOWN)
696 MDIBringChildToTop(hwnd, ci->hwndHitTest, FALSE, FALSE);
700 GetClientRect(w->hwndParent, &ci->rectMaximize);
705 return DefWindowProc(hwnd, message, wParam, lParam);
708 /**********************************************************************
709 * DefFrameProc (USER.445)
712 LONG DefFrameProc(HWND hwnd, HWND hwndMDIClient, WORD message,
713 WORD wParam, LONG lParam)
720 MDIBringChildToTop(hwndMDIClient, wParam, TRUE, FALSE);
723 case WM_NCLBUTTONDOWN:
724 if (MDIHandleLButton(hwnd, hwndMDIClient, wParam, lParam))
729 SendMessage(hwndMDIClient, message, wParam, lParam);
730 return MDIPaintMaximized(hwnd, hwndMDIClient,
731 message, wParam, lParam);
734 return MDIPaintMaximized(hwnd, hwndMDIClient,
735 message, wParam, lParam);
738 SendMessage(hwndMDIClient, WM_SETFOCUS, wParam, lParam);
742 MoveWindow(hwndMDIClient, 0, 0,
743 LOWORD(lParam), HIWORD(lParam), TRUE);
748 return DefWindowProc(hwnd, message, wParam, lParam);
751 /**********************************************************************
752 * DefMDIChildProc (USER.447)
755 LONG DefMDIChildProc(HWND hwnd, WORD message, WORD wParam, LONG lParam)
760 w = WIN_FindWndPtr(GetParent(hwnd));
761 ci = (MDICLIENTINFO *) w->wExtra;
766 ci->hwndHitTest = hwnd;
770 NC_DoNCPaint( hwnd, hwnd == ci->hwndActiveChild, FALSE );
777 return SendMessage(GetParent(hwnd), WM_MDIMAXIMIZE, hwnd, 0);
780 return SendMessage(GetParent(hwnd), WM_MDIRESTORE, hwnd, 0);
786 return DefWindowProc(hwnd, message, wParam, lParam);
789 /**********************************************************************
790 * TranslateMDISysAccel (USER.451)
793 BOOL TranslateMDISysAccel(HWND hwndClient, LPMSG msg)
799 /***********************************************************************
800 * CalcChildScroll (USER.462)
802 void CalcChildScroll( HWND hwnd, WORD scroll )
804 RECT childRect, clientRect;
807 GetClientRect( hwnd, &clientRect );
808 SetRectEmpty( &childRect );
809 hwndChild = GetWindow( hwnd, GW_CHILD );
812 WND *wndPtr = WIN_FindWndPtr( hwndChild );
813 UnionRect( &childRect, &wndPtr->rectWindow, &childRect );
814 hwndChild = wndPtr->hwndNext;
816 UnionRect( &childRect, &clientRect, &childRect );
818 if ((scroll == SB_HORZ) || (scroll == SB_BOTH))
820 SetScrollRange( hwnd, SB_HORZ, childRect.left,
821 childRect.right - clientRect.right, FALSE );
822 SetScrollPos( hwnd, SB_HORZ, clientRect.left - childRect.left, TRUE );
824 if ((scroll == SB_VERT) || (scroll == SB_BOTH))
826 SetScrollRange( hwnd, SB_VERT, childRect.top,
827 childRect.bottom - clientRect.bottom, FALSE );
828 SetScrollPos( hwnd, SB_HORZ, clientRect.top - childRect.top, TRUE );