xref: /reactos/win32ss/user/user32/windows/mdi.c (revision 7115d7ba)
1 /* MDI.C
2  *
3  * Copyright 1994, Bob Amstadt
4  * Copyright 1995,1996 Alex Korobka
5  * Copyright 2018 Katayama Hirofumi MZ
6  *
7  * This file contains routines to support MDI (Multiple Document
8  * Interface) features.
9  *
10  * This library is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU Lesser General Public
12  * License as published by the Free Software Foundation; either
13  * version 2.1 of the License, or (at your option) any later version.
14  *
15  * This library is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  * Lesser General Public License for more details.
19  *
20  * You should have received a copy of the GNU Lesser General Public
21  * License along with this library; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
23  *
24  * Notes: Fairly complete implementation.
25  *        Also, Excel and WinWord do _not_ use MDI so if you're trying
26  *        to fix them look elsewhere.
27  *
28  * Notes on how the "More Windows..." is implemented:
29  *
30  *      When we have more than 9 opened windows, a "More Windows..."
31  *      option appears in the "Windows" menu. Each child window has
32  *      a WND* associated with it, accessible via the children list of
33  *      the parent window. This WND* has a wIDmenu member, which reflects
34  *      the position of the child in the window list. For example, with
35  *      9 child windows, we could have the following pattern:
36  *
37  *
38  *
39  *                Name of the child window    pWndChild->wIDmenu
40  *                     Doc1                       5000
41  *                     Doc2                       5001
42  *                     Doc3                       5002
43  *                     Doc4                       5003
44  *                     Doc5                       5004
45  *                     Doc6                       5005
46  *                     Doc7                       5006
47  *                     Doc8                       5007
48  *                     Doc9                       5008
49  *
50  *
51  *       The "Windows" menu, as the "More windows..." dialog, are constructed
52  *       in this order. If we add a child, we would have the following list:
53  *
54  *
55  *               Name of the child window    pWndChild->wIDmenu
56  *                     Doc1                       5000
57  *                     Doc2                       5001
58  *                     Doc3                       5002
59  *                     Doc4                       5003
60  *                     Doc5                       5004
61  *                     Doc6                       5005
62  *                     Doc7                       5006
63  *                     Doc8                       5007
64  *                     Doc9                       5008
65  *                     Doc10                      5009
66  *
67  *       But only 5000 to 5008 would be displayed in the "Windows" menu. We want
68  *       the last created child to be in the menu, so we swap the last child with
69  *       the 9th... Doc9 will be accessible via the "More Windows..." option.
70  *
71  *                     Doc1                       5000
72  *                     Doc2                       5001
73  *                     Doc3                       5002
74  *                     Doc4                       5003
75  *                     Doc5                       5004
76  *                     Doc6                       5005
77  *                     Doc7                       5006
78  *                     Doc8                       5007
79  *                     Doc9                       5009
80  *                     Doc10                      5008
81  *
82  */
83 
84 #include <user32.h>
85 
86 WINE_DEFAULT_DEBUG_CHANNEL(mdi);
87 
88 #define MDI_MAXTITLELENGTH      0xa1
89 
90 #define WM_MDICALCCHILDSCROLL   0x003F  /* this is exactly what Windows uses */
91 
92 /* "More Windows..." definitions */
93 #define MDI_MOREWINDOWSLIMIT    9       /* after this number of windows, a "More Windows..."
94                                            option will appear under the Windows menu */
95 #define MDI_IDC_LISTBOX         100
96 #define IDS_MDI_MOREWINDOWS     13
97 
98 #define MDIF_NEEDUPDATE		0x0001
99 
100 typedef struct
101 {
102     /* At some points, particularly when switching MDI children, active and
103      * maximized MDI children may be not the same window, so we need to track
104      * them separately.
105      * The only place where we switch to/from maximized state is DefMDIChildProc
106      * WM_SIZE/SIZE_MAXIMIZED handler. We get that notification only after the
107      * ShowWindow(SW_SHOWMAXIMIZED) request, therefore window is guaranteed to
108      * be visible at the time we get the notification, and it's safe to assume
109      * that hwndChildMaximized is always visible.
110      * If the app plays games with WS_VISIBLE, WS_MAXIMIZE or any other window
111      * states it must keep coherency with USER32 on its own. This is true for
112      * Windows as well.
113      */
114     LONG      reserved;
115     UINT      nActiveChildren;
116     HWND      hwndChildMaximized;
117     HWND      hwndActiveChild;
118     HWND      *child; /* array of tracked children */
119     HMENU     hFrameMenu;
120     HMENU     hWindowMenu;
121     UINT      idFirstChild;
122     LPWSTR    frameTitle;
123     UINT      nTotalCreated;
124     UINT      mdiFlags;
125     UINT      sbRecalc;   /* SB_xxx flags for scrollbar fixup */
126     HBITMAP   hBmpClose; /* ReactOS modification */
127 } MDICLIENTINFO;
128 
129 //static HBITMAP hBmpClose   = 0;
130 
131 /* ----------------- declarations ----------------- */
132 static void MDI_UpdateFrameText( HWND, HWND, BOOL, LPCWSTR);
133 static BOOL MDI_AugmentFrameMenu( HWND, HWND );
134 static BOOL MDI_RestoreFrameMenu( HWND, HWND, HBITMAP );
135 static LONG MDI_ChildActivate( HWND, HWND );
136 static LRESULT MDI_RefreshMenu(MDICLIENTINFO *);
137 
138 static HWND MDI_MoreWindowsDialog(HWND);
139 
140 HWND* WIN_ListChildren (HWND hWndparent)
141 {
142 
143   DWORD dwCount = 0;
144   HWND* pHwnd = NULL;
145   HANDLE hHeap;
146   NTSTATUS Status;
147 
148   Status = NtUserBuildHwndList ( NULL, hWndparent, FALSE, 0, 0, NULL, &dwCount );
149 
150   if ( !NT_SUCCESS( Status ) )
151     return 0;
152 
153   /* allocate buffer to receive HWND handles */
154   hHeap = GetProcessHeap();
155 
156   pHwnd = HeapAlloc ( hHeap, 0, sizeof(HWND)*(dwCount+1) );
157   if ( !pHwnd )
158     {
159       SetLastError ( ERROR_NOT_ENOUGH_MEMORY );
160       return 0;
161     }
162 
163   /* now call kernel again to fill the buffer this time */
164   Status = NtUserBuildHwndList (NULL, hWndparent, FALSE, 0, 0, pHwnd, &dwCount );
165 
166   if ( !NT_SUCCESS( Status ) )
167     {
168       if ( pHwnd )
169         HeapFree ( hHeap, 0, pHwnd );
170       return 0;
171     }
172 
173   pHwnd[dwCount] = (HWND) 0;
174 
175   return pHwnd;
176 }
177 
178 #ifdef __REACTOS__
179 void WINAPI ScrollChildren(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
180 void WINAPI CalcChildScroll(HWND hwnd, INT scroll);
181 #endif
182 
183 /* -------- Miscellaneous service functions ----------
184  *
185  *			MDI_GetChildByID
186  */
187 static HWND MDI_GetChildByID(HWND hwnd, UINT id, MDICLIENTINFO *ci)
188 {
189     int i;
190 
191     for (i = 0; ci->nActiveChildren; i++)
192     {
193         if (GetWindowLongPtrW( ci->child[i], GWLP_ID ) == id)
194             return ci->child[i];
195     }
196     return 0;
197 }
198 
199 static void MDI_PostUpdate(HWND hwnd, MDICLIENTINFO* ci, WORD recalc)
200 {
201     if( !(ci->mdiFlags & MDIF_NEEDUPDATE) )
202     {
203 	ci->mdiFlags |= MDIF_NEEDUPDATE;
204 	PostMessageA( hwnd, WM_MDICALCCHILDSCROLL, 0, 0);
205     }
206     ci->sbRecalc = recalc;
207 }
208 
209 
210 /*********************************************************************
211  * MDIClient class descriptor
212  */
213 const struct builtin_class_descr MDICLIENT_builtin_class =
214 {
215     L"MDIClient",            /* name */
216     0,                      /* style */
217     MDIClientWndProcA,      /* procA */
218     MDIClientWndProcW,      /* procW */
219     sizeof(MDIWND),         /* extra */
220     IDC_ARROW,              /* cursor */
221     (HBRUSH)(COLOR_APPWORKSPACE+1)    /* brush */
222 };
223 
224 
225 static MDICLIENTINFO *get_client_info( HWND client )
226 {
227 #ifdef __REACTOS__
228     return (MDICLIENTINFO *)GetWindowLongPtr(client, GWLP_MDIWND);
229 #else
230     MDICLIENTINFO *ret = NULL;
231     WND *win = WIN_GetPtr( client );
232     if (win)
233     {
234         if (win == WND_OTHER_PROCESS || win == WND_DESKTOP)
235         {
236             if (IsWindow(client)) WARN( "client %p belongs to other process\n", client );
237             return NULL;
238         }
239         if (win->flags & WIN_ISMDICLIENT)
240             ret = (MDICLIENTINFO *)win->wExtra;
241         else
242             WARN( "%p is not an MDI client\n", client );
243         WIN_ReleasePtr( win );
244     }
245     return ret;
246 #endif
247 }
248 
249 static BOOL is_close_enabled(HWND hwnd, HMENU hSysMenu)
250 {
251     if (GetClassLongPtrW(hwnd, GCL_STYLE) & CS_NOCLOSE) return FALSE;
252 
253     if (!hSysMenu) hSysMenu = GetSystemMenu(hwnd, FALSE);
254     if (hSysMenu)
255     {
256         UINT state = GetMenuState(hSysMenu, SC_CLOSE, MF_BYCOMMAND);
257         if (state == 0xFFFFFFFF || (state & (MF_DISABLED | MF_GRAYED)))
258             return FALSE;
259     }
260     return TRUE;
261 }
262 
263 /**********************************************************************
264  * 			MDI_GetWindow
265  *
266  * returns "activatable" child different from the current or zero
267  */
268 static HWND MDI_GetWindow(MDICLIENTINFO *clientInfo, HWND hWnd, BOOL bNext,
269                             DWORD dwStyleMask )
270 {
271     int i;
272     HWND *list;
273     HWND last = 0;
274 
275     dwStyleMask |= WS_DISABLED | WS_VISIBLE;
276     if( !hWnd ) hWnd = clientInfo->hwndActiveChild;
277 
278     if (!(list = WIN_ListChildren( GetParent(hWnd) ))) return 0;
279     i = 0;
280     /* start from next after hWnd */
281     while (list[i] && list[i] != hWnd) i++;
282     if (list[i]) i++;
283 
284     for ( ; list[i]; i++)
285     {
286         if (GetWindow( list[i], GW_OWNER )) continue;
287         if ((GetWindowLongPtrW( list[i], GWL_STYLE ) & dwStyleMask) != WS_VISIBLE) continue;
288         last = list[i];
289         if (bNext) goto found;
290     }
291     /* now restart from the beginning */
292     for (i = 0; list[i] && list[i] != hWnd; i++)
293     {
294         if (GetWindow( list[i], GW_OWNER )) continue;
295         if ((GetWindowLongPtrW( list[i], GWL_STYLE ) & dwStyleMask) != WS_VISIBLE) continue;
296         last = list[i];
297         if (bNext) goto found;
298     }
299  found:
300     HeapFree( GetProcessHeap(), 0, list );
301     return last;
302 }
303 
304 /**********************************************************************
305  *			MDI_CalcDefaultChildPos
306  *
307  *  It seems that the default height is about 2/3 of the client rect
308  */
309 void MDI_CalcDefaultChildPos( HWND hwndClient, INT total, LPPOINT lpPos, INT delta, UINT *id )
310 {
311     INT  nstagger;
312     RECT rect;
313     INT spacing = GetSystemMetrics(SM_CYCAPTION) + GetSystemMetrics(SM_CYFRAME) - 1;
314 
315     if (total < 0) /* we are called from CreateWindow */
316     {
317         MDICLIENTINFO *ci = get_client_info(hwndClient);
318         total = ci ? ci->nTotalCreated : 0;                     // Do not portsync wine
319         *id = ci ? ci->idFirstChild + ci->nActiveChildren : 0;  // Do not portsync wine
320         TRACE("MDI child id %04x\n", *id);
321     }
322 
323     GetClientRect( hwndClient, &rect );
324     if( rect.bottom - rect.top - delta >= spacing )
325 	rect.bottom -= delta;
326 
327     nstagger = (rect.bottom - rect.top)/(3 * spacing);
328     lpPos[1].x = (rect.right - rect.left - nstagger * spacing);
329     lpPos[1].y = (rect.bottom - rect.top - nstagger * spacing);
330     lpPos[0].x = lpPos[0].y = spacing * (total%(nstagger+1));
331 }
332 
333 /**********************************************************************
334  *            MDISetMenu
335  */
336 static LRESULT MDISetMenu( HWND hwnd, HMENU hmenuFrame,
337                            HMENU hmenuWindow)
338 {
339     MDICLIENTINFO *ci;
340     HWND hwndFrame = GetParent(hwnd);
341 
342     TRACE("%p, frame menu %p, window menu %p\n", hwnd, hmenuFrame, hmenuWindow);
343 
344     if (hmenuFrame && !IsMenu(hmenuFrame))
345     {
346 	WARN("hmenuFrame is not a menu handle\n");
347 	return 0L;
348     }
349 
350     if (hmenuWindow && !IsMenu(hmenuWindow))
351     {
352 	WARN("hmenuWindow is not a menu handle\n");
353 	return 0L;
354     }
355 
356     if (!(ci = get_client_info( hwnd ))) return 0;
357 
358     TRACE("old frame menu %p, old window menu %p\n", ci->hFrameMenu, ci->hWindowMenu);
359 
360     if (hmenuFrame)
361     {
362         if (hmenuFrame == ci->hFrameMenu) return (LRESULT)hmenuFrame;
363 
364         if (ci->hwndChildMaximized)
365             MDI_RestoreFrameMenu( hwndFrame, ci->hwndChildMaximized, ci->hBmpClose );
366     }
367 
368     if( hmenuWindow && hmenuWindow != ci->hWindowMenu )
369     {
370         /* delete menu items from ci->hWindowMenu
371          * and add them to hmenuWindow */
372         /* Agent newsreader calls this function with  ci->hWindowMenu == NULL */
373         if( ci->hWindowMenu && ci->nActiveChildren )
374         {
375             UINT nActiveChildren_old = ci->nActiveChildren;
376 
377             /* Remove all items from old Window menu */
378             ci->nActiveChildren = 0;
379             MDI_RefreshMenu(ci);
380 
381             ci->hWindowMenu = hmenuWindow;
382 
383             /* Add items to the new Window menu */
384             ci->nActiveChildren = nActiveChildren_old;
385             MDI_RefreshMenu(ci);
386         }
387         else
388             ci->hWindowMenu = hmenuWindow;
389     }
390 
391     if (hmenuFrame)
392     {
393         SetMenu(hwndFrame, hmenuFrame);
394         if( hmenuFrame != ci->hFrameMenu )
395         {
396             HMENU oldFrameMenu = ci->hFrameMenu;
397 
398             ci->hFrameMenu = hmenuFrame;
399             if (ci->hwndChildMaximized)
400                 MDI_AugmentFrameMenu( hwndFrame, ci->hwndChildMaximized );
401 
402             return (LRESULT)oldFrameMenu;
403         }
404     }
405 
406     return 0;
407 }
408 
409 /**********************************************************************
410  *            MDIRefreshMenu
411  */
412 static LRESULT MDI_RefreshMenu(MDICLIENTINFO *ci)
413 {
414     UINT i, count, visible, id;
415     WCHAR buf[MDI_MAXTITLELENGTH];
416 
417     TRACE("children %u, window menu %p\n", ci->nActiveChildren, ci->hWindowMenu);
418 
419     if (!ci->hWindowMenu)
420         return 0;
421 
422     if (!IsMenu(ci->hWindowMenu))
423     {
424         WARN("Window menu handle %p is no longer valid\n", ci->hWindowMenu);
425         return 0;
426     }
427 
428     /* Windows finds the last separator in the menu, and if after it
429      * there is a menu item with MDI magic ID removes all existing
430      * menu items after it, and then adds visible MDI children.
431      */
432     count = GetMenuItemCount(ci->hWindowMenu);
433     for (i = 0; i < count; i++)
434     {
435         MENUITEMINFOW mii;
436 
437         memset(&mii, 0, sizeof(mii));
438         mii.cbSize = sizeof(mii);
439         mii.fMask  = MIIM_TYPE;
440         if (GetMenuItemInfoW(ci->hWindowMenu, i, TRUE, &mii))
441         {
442             if (mii.fType & MF_SEPARATOR)
443             {
444                 /* Windows checks only ID of the menu item */
445                 memset(&mii, 0, sizeof(mii));
446                 mii.cbSize = sizeof(mii);
447                 mii.fMask  = MIIM_ID;
448                 if (GetMenuItemInfoW(ci->hWindowMenu, i + 1, TRUE, &mii))
449                 {
450                     if (mii.wID == ci->idFirstChild)
451                     {
452                         TRACE("removing %u items including separator\n", count - i);
453                         while (RemoveMenu(ci->hWindowMenu, i, MF_BYPOSITION))
454                             /* nothing */;
455 
456                         break;
457                     }
458                 }
459             }
460         }
461     }
462 
463     visible = 0;
464     for (i = 0; i < ci->nActiveChildren; i++)
465     {
466         if (GetWindowLongPtrW(ci->child[i], GWL_STYLE) & WS_VISIBLE)
467         {
468             id = ci->idFirstChild + visible;
469 
470             if (visible == MDI_MOREWINDOWSLIMIT)
471             {
472                 LoadStringW(User32Instance, IDS_MDI_MOREWINDOWS, buf, sizeof(buf)/sizeof(WCHAR));
473                 AppendMenuW(ci->hWindowMenu, MF_STRING, id, buf);
474                 break;
475             }
476 
477             if (!visible)
478                 /* Visio expects that separator has id 0 */
479                 AppendMenuW(ci->hWindowMenu, MF_SEPARATOR, 0, NULL);
480 
481             visible++;
482 
483             SetWindowLongPtrW(ci->child[i], GWLP_ID, id);
484 
485             buf[0] = '&';
486             buf[1] = '0' + visible;
487             buf[2] = ' ';
488             InternalGetWindowText(ci->child[i], buf + 3, sizeof(buf)/sizeof(WCHAR) - 3);
489             TRACE("Adding %p, id %u %s\n", ci->child[i], id, debugstr_w(buf));
490             AppendMenuW(ci->hWindowMenu, MF_STRING, id, buf);
491 
492             if (ci->child[i] == ci->hwndActiveChild)
493                 CheckMenuItem(ci->hWindowMenu, id, MF_CHECKED);
494         }
495         else
496             TRACE("MDI child %p is not visible, skipping\n", ci->child[i]);
497     }
498 
499     return (LRESULT)ci->hFrameMenu;
500 }
501 
502 
503 /* ------------------ MDI child window functions ---------------------- */
504 
505 /**********************************************************************
506  *			MDI_ChildGetMinMaxInfo
507  *
508  * Note: The rule here is that client rect of the maximized MDI child
509  *	 is equal to the client rect of the MDI client window.
510  */
511 static void MDI_ChildGetMinMaxInfo( HWND client, HWND hwnd, MINMAXINFO* lpMinMax )
512 {
513     RECT rect;
514 
515     GetClientRect( client, &rect );
516     AdjustWindowRectEx( &rect, GetWindowLongPtrW( hwnd, GWL_STYLE ),
517                         0, GetWindowLongPtrW( hwnd, GWL_EXSTYLE ));
518 
519     lpMinMax->ptMaxSize.x = rect.right -= rect.left;
520     lpMinMax->ptMaxSize.y = rect.bottom -= rect.top;
521 
522     lpMinMax->ptMaxPosition.x = rect.left;
523     lpMinMax->ptMaxPosition.y = rect.top;
524 
525     TRACE("max rect %s\n", wine_dbgstr_rect(&rect));
526 }
527 
528 /**********************************************************************
529  *			MDI_SwitchActiveChild
530  *
531  * Note: SetWindowPos sends WM_CHILDACTIVATE to the child window that is
532  *       being activated
533  */
534 static void MDI_SwitchActiveChild( MDICLIENTINFO *ci, HWND hwndTo, BOOL activate )
535 {
536     HWND hwndPrev;
537 
538     hwndPrev = ci->hwndActiveChild;
539 
540     TRACE("from %p, to %p\n", hwndPrev, hwndTo);
541 
542     if ( hwndTo != hwndPrev )
543     {
544         BOOL was_zoomed = IsZoomed(hwndPrev);
545 
546         if (was_zoomed)
547         {
548             /* restore old MDI child */
549             SendMessageW( hwndPrev, WM_SETREDRAW, FALSE, 0 );
550             ShowWindow( hwndPrev, SW_RESTORE );
551             SendMessageW( hwndPrev, WM_SETREDRAW, TRUE, 0 );
552 
553             /* activate new MDI child */
554             SetWindowPos( hwndTo, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE );
555             /* maximize new MDI child */
556             ShowWindow( hwndTo, SW_MAXIMIZE );
557         }
558         /* activate new MDI child */
559         SetWindowPos( hwndTo, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | (activate ? 0 : SWP_NOACTIVATE) );
560     }
561 }
562 
563 
564 /**********************************************************************
565  *                                      MDIDestroyChild
566  */
567 static LRESULT MDIDestroyChild( HWND client, MDICLIENTINFO *ci,
568                                 HWND child, BOOL flagDestroy )
569 {
570     UINT i;
571 
572     TRACE("# of managed children %u\n", ci->nActiveChildren);
573 
574     if( child == ci->hwndActiveChild )
575     {
576         HWND next = MDI_GetWindow(ci, child, TRUE, 0);
577         if (next)
578             MDI_SwitchActiveChild(ci, next, TRUE);
579         else
580         {
581             ShowWindow(child, SW_HIDE);
582             if (child == ci->hwndChildMaximized)
583             {
584                 HWND frame = GetParent(client);
585                 MDI_RestoreFrameMenu(frame, child, ci->hBmpClose);
586                 ci->hwndChildMaximized = 0;
587                 MDI_UpdateFrameText(frame, client, TRUE, NULL);
588             }
589             if (flagDestroy)
590                 MDI_ChildActivate(client, 0);
591         }
592     }
593 
594     for (i = 0; i < ci->nActiveChildren; i++)
595     {
596         if (ci->child[i] == child)
597         {
598             HWND *new_child = HeapAlloc(GetProcessHeap(), 0, (ci->nActiveChildren - 1) * sizeof(HWND));
599             if (new_child != NULL)
600             {
601                 memcpy(new_child, ci->child, i * sizeof(HWND));
602                 if (i + 1 < ci->nActiveChildren)
603                     memcpy(new_child + i, ci->child + i + 1, (ci->nActiveChildren - i - 1) * sizeof(HWND));
604                 HeapFree(GetProcessHeap(), 0, ci->child);
605                 ci->child = new_child;
606             }
607             else
608             {
609                 UINT c;
610                 for (c = i; c < ci->nActiveChildren - 1; c++)
611                 {
612                     ci->child[c] = ci->child[c+1];
613                 }
614             }
615 
616             ci->nActiveChildren--;
617             break;
618         }
619     }
620 
621     if (flagDestroy)
622     {
623         SendMessageW(client, WM_MDIREFRESHMENU, 0, 0);
624         MDI_PostUpdate(GetParent(child), ci, SB_BOTH+1);
625         DestroyWindow(child);
626     }
627 
628     TRACE("child destroyed - %p\n", child);
629     return 0;
630 }
631 
632 
633 /**********************************************************************
634  *					MDI_ChildActivate
635  *
636  * Called in response to WM_CHILDACTIVATE, or when last MDI child
637  * is being deactivated.
638  */
639 static LONG MDI_ChildActivate( HWND client, HWND child )
640 {
641     MDICLIENTINFO *clientInfo;
642     HWND prevActiveWnd, frame;
643     BOOL isActiveFrameWnd;
644 
645     clientInfo = get_client_info( client );
646 
647     if (clientInfo->hwndActiveChild == child) return 0;
648 
649     TRACE("%p\n", child);
650 
651     frame = GetParent(client);
652     isActiveFrameWnd = (GetActiveWindow() == frame);
653     prevActiveWnd = clientInfo->hwndActiveChild;
654 
655     /* deactivate prev. active child */
656     if(prevActiveWnd)
657     {
658         SendMessageW( prevActiveWnd, WM_NCACTIVATE, FALSE, 0L );
659         SendMessageW( prevActiveWnd, WM_MDIACTIVATE, (WPARAM)prevActiveWnd, (LPARAM)child);
660     }
661 
662     MDI_SwitchActiveChild( clientInfo, child, FALSE );
663     clientInfo->hwndActiveChild = child;
664 
665     MDI_RefreshMenu(clientInfo);
666 
667     if( isActiveFrameWnd )
668     {
669         SendMessageW( child, WM_NCACTIVATE, TRUE, 0L);
670         /* Let the client window manage focus for children, but if the focus
671          * is already on the client (for instance this is the 1st child) then
672          * SetFocus won't work. It appears that Windows sends WM_SETFOCUS
673          * manually in this case.
674          */
675         if (SetFocus(client) == client)
676             SendMessageW( client, WM_SETFOCUS, (WPARAM)client, 0 );
677     }
678 
679     SendMessageW( child, WM_MDIACTIVATE, (WPARAM)prevActiveWnd, (LPARAM)child );
680     return TRUE;
681 }
682 
683 /* -------------------- MDI client window functions ------------------- */
684 
685 /**********************************************************************
686  *				CreateMDIMenuBitmap
687  */
688 static HBITMAP CreateMDIMenuBitmap(void)
689 {
690  HDC 		hDCSrc  = CreateCompatibleDC(0);
691  HDC		hDCDest	= CreateCompatibleDC(hDCSrc);
692  HBITMAP	hbClose = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_OLD_CLOSE) );
693  HBITMAP	hbCopy;
694  HBITMAP	hobjSrc, hobjDest;
695 
696  hobjSrc = SelectObject(hDCSrc, hbClose);
697  hbCopy = CreateCompatibleBitmap(hDCSrc,GetSystemMetrics(SM_CXSIZE),GetSystemMetrics(SM_CYSIZE));
698  hobjDest = SelectObject(hDCDest, hbCopy);
699 
700  BitBlt(hDCDest, 0, 0, GetSystemMetrics(SM_CXSIZE), GetSystemMetrics(SM_CYSIZE),
701           hDCSrc, GetSystemMetrics(SM_CXSIZE), 0, SRCCOPY);
702 
703  SelectObject(hDCSrc, hobjSrc);
704  DeleteObject(hbClose);
705  DeleteDC(hDCSrc);
706 
707  hobjSrc = SelectObject( hDCDest, GetStockObject(BLACK_PEN) );
708 
709  MoveToEx( hDCDest, GetSystemMetrics(SM_CXSIZE) - 1, 0, NULL );
710  LineTo( hDCDest, GetSystemMetrics(SM_CXSIZE) - 1, GetSystemMetrics(SM_CYSIZE) - 1);
711 
712  SelectObject(hDCDest, hobjSrc );
713  SelectObject(hDCDest, hobjDest);
714  DeleteDC(hDCDest);
715 
716  return hbCopy;
717 }
718 
719 /**********************************************************************
720  *				MDICascade
721  */
722 static LONG MDICascade( HWND client, MDICLIENTINFO *ci )
723 {
724     HWND *win_array;
725     BOOL has_icons = FALSE;
726     int i, total;
727 
728     if (ci->hwndChildMaximized)
729         SendMessageW(client, WM_MDIRESTORE, (WPARAM)ci->hwndChildMaximized, 0);
730 
731     if (ci->nActiveChildren == 0) return 0;
732 
733     if (!(win_array = WIN_ListChildren( client ))) return 0;
734 
735     /* remove all the windows we don't want */
736     for (i = total = 0; win_array[i]; i++)
737     {
738         if (!IsWindowVisible( win_array[i] )) continue;
739         if (GetWindow( win_array[i], GW_OWNER )) continue; /* skip owned windows */
740         if (IsIconic( win_array[i] ))
741         {
742             has_icons = TRUE;
743             continue;
744         }
745         win_array[total++] = win_array[i];
746     }
747     win_array[total] = 0;
748 
749     if (total)
750     {
751         INT delta = 0, n = 0, i;
752         POINT pos[2];
753         if (has_icons) delta = GetSystemMetrics(SM_CYICONSPACING) + GetSystemMetrics(SM_CYICON);
754 
755         /* walk the list (backwards) and move windows */
756         for (i = total - 1; i >= 0; i--)
757         {
758             LONG style;
759             LONG posOptions = SWP_DRAWFRAME | SWP_NOACTIVATE | SWP_NOZORDER;
760 
761             MDI_CalcDefaultChildPos(client, n++, pos, delta, NULL);
762             TRACE("move %p to (%ld,%ld) size [%ld,%ld]\n",
763                   win_array[i], pos[0].x, pos[0].y, pos[1].x, pos[1].y);
764             style = GetWindowLongW(win_array[i], GWL_STYLE);
765 
766             if (!(style & WS_SIZEBOX)) posOptions |= SWP_NOSIZE;
767             SetWindowPos( win_array[i], 0, pos[0].x, pos[0].y, pos[1].x, pos[1].y,
768                            posOptions);
769         }
770     }
771     HeapFree( GetProcessHeap(), 0, win_array );
772 
773     if (has_icons) ArrangeIconicWindows( client );
774     return 0;
775 }
776 
777 /**********************************************************************
778  *					MDITile
779  */
780 static void MDITile( HWND client, MDICLIENTINFO *ci, WPARAM wParam )
781 {
782     HWND *win_array;
783     int i, total, rows, columns;
784     BOOL has_icons = FALSE;
785 
786     if (ci->hwndChildMaximized)
787         SendMessageW(client, WM_MDIRESTORE, (WPARAM)ci->hwndChildMaximized, 0);
788 
789     if (ci->nActiveChildren == 0) return;
790 
791     if (!(win_array = WIN_ListChildren( client ))) return;
792 
793     /* remove all the windows we don't want */
794     for (i = total = rows = 0; win_array[i]; i++)
795     {
796         if (!IsWindowVisible( win_array[i] )) continue;
797         if (GetWindow( win_array[i], GW_OWNER )) continue; /* skip owned windows (icon titles) */
798         if (IsIconic( win_array[i] ))
799         {
800             has_icons = TRUE;
801             continue;
802         }
803         if ((wParam & MDITILE_SKIPDISABLED) && !IsWindowEnabled( win_array[i] )) continue;
804         if(total == (rows * (rows + 2))) rows++; /* total+1 == (rows+1)*(rows+1) */
805         win_array[total++] = win_array[i];
806     }
807     win_array[total] = 0;
808 
809     TRACE("%u windows to tile\n", total);
810 
811     if (total)
812     {
813         HWND *pWnd = win_array;
814         RECT rect;
815         int x, y, xsize, ysize;
816         int r, c, i;
817 
818         GetClientRect(client,&rect);
819         columns = total/rows;
820         //while(total < rows*columns) rows++;
821 
822         if( wParam & MDITILE_HORIZONTAL )  /* version >= 3.1 */
823         {
824             i = rows;
825             rows = columns;  /* exchange r and c */
826             columns = i;
827         }
828 
829         if (has_icons)
830         {
831             y = rect.bottom - 2 * GetSystemMetrics(SM_CYICONSPACING) - GetSystemMetrics(SM_CYICON);
832             rect.bottom = ( y - GetSystemMetrics(SM_CYICON) < rect.top )? rect.bottom: y;
833         }
834 
835         ysize   = rect.bottom / rows;
836         xsize   = rect.right  / columns;
837 
838         for (x = i = 0, c = 1; c <= columns && *pWnd; c++)
839         {
840             if (c == columns)
841             {
842                 rows  = total - i;
843                 ysize = rect.bottom / rows;
844             }
845 
846             y = 0;
847             for (r = 1; r <= rows && *pWnd; r++, i++)
848             {
849                 LONG posOptions = SWP_DRAWFRAME | SWP_NOACTIVATE | SWP_NOZORDER;
850                 LONG style = GetWindowLongW(win_array[i], GWL_STYLE);
851                 if (!(style & WS_SIZEBOX)) posOptions |= SWP_NOSIZE;
852 
853                 SetWindowPos(*pWnd, 0, x, y, xsize, ysize, posOptions);
854                 y += ysize;
855                 pWnd++;
856             }
857             x += xsize;
858         }
859     }
860     HeapFree( GetProcessHeap(), 0, win_array );
861     if (has_icons) ArrangeIconicWindows( client );
862 }
863 
864 /* ----------------------- Frame window ---------------------------- */
865 
866 
867 /**********************************************************************
868  *					MDI_AugmentFrameMenu
869  */
870 static BOOL MDI_AugmentFrameMenu( HWND frame, HWND hChild )
871 {
872     HMENU menu = GetMenu( frame );
873     HMENU  	hSysPopup;
874     HBITMAP hSysMenuBitmap = 0;
875     HICON hIcon;
876     INT nItems;
877     UINT iId;
878 
879     TRACE("frame %p,child %p\n",frame,hChild);
880 
881     if( !menu ) return FALSE;
882 //// ReactOS start
883     /* if the system buttons already exist do not add them again */
884     nItems = GetMenuItemCount(menu) - 1;
885     iId = GetMenuItemID(menu,nItems) ;
886     if (iId == SC_RESTORE || iId == SC_CLOSE)
887     {
888         ERR("system buttons already exist\n");
889 	return FALSE;
890     }
891 //// End
892     /* create a copy of sysmenu popup and insert it into frame menu bar */
893     if (!(hSysPopup = GetSystemMenu(hChild, FALSE)))
894     {
895         TRACE("child %p doesn't have a system menu\n", hChild);
896         return FALSE;
897     }
898 
899     AppendMenuW(menu, MF_HELP | MF_BITMAP,
900                 SC_CLOSE, is_close_enabled(hChild, hSysPopup) ?
901                 (LPCWSTR)HBMMENU_MBAR_CLOSE : (LPCWSTR)HBMMENU_MBAR_CLOSE_D );
902     AppendMenuW(menu, MF_HELP | MF_BITMAP,
903                 SC_RESTORE, (LPCWSTR)HBMMENU_MBAR_RESTORE );
904     AppendMenuW(menu, MF_HELP | MF_BITMAP,
905                 SC_MINIMIZE, (LPCWSTR)HBMMENU_MBAR_MINIMIZE ) ;
906 
907     /* The system menu is replaced by the child icon */
908     hIcon = (HICON)SendMessageW(hChild, WM_GETICON, ICON_SMALL, 0);
909     if (!hIcon)
910         hIcon = (HICON)GetClassLongPtrW(hChild, GCLP_HICONSM);
911     if (!hIcon)
912         hIcon = (HICON)SendMessageW(hChild, WM_GETICON, ICON_BIG, 0);
913     if (!hIcon)
914         hIcon = (HICON)GetClassLongPtrW(hChild, GCLP_HICON);
915     if (!hIcon)
916         hIcon = LoadImageW(0, (LPWSTR)IDI_WINLOGO, IMAGE_ICON, GetSystemMetrics(SM_CXSMICON),
917                            GetSystemMetrics(SM_CYSMICON), LR_DEFAULTCOLOR);
918     if (hIcon)
919     {
920       HDC hMemDC;
921       HBITMAP hBitmap, hOldBitmap;
922       HBRUSH hBrush;
923       HDC hdc = GetDC(hChild);
924 
925       if (hdc)
926       {
927         int cx, cy;
928         cx = GetSystemMetrics(SM_CXSMICON);
929         cy = GetSystemMetrics(SM_CYSMICON);
930         hMemDC = CreateCompatibleDC(hdc);
931         hBitmap = CreateCompatibleBitmap(hdc, cx, cy);
932         hOldBitmap = SelectObject(hMemDC, hBitmap);
933         SetMapMode(hMemDC, MM_TEXT);
934         hBrush = CreateSolidBrush(GetSysColor(COLOR_MENU));
935         DrawIconEx(hMemDC, 0, 0, hIcon, cx, cy, 0, hBrush, DI_NORMAL);
936         SelectObject (hMemDC, hOldBitmap);
937         DeleteObject(hBrush);
938         DeleteDC(hMemDC);
939         ReleaseDC(hChild, hdc);
940         hSysMenuBitmap = hBitmap;
941       }
942     }
943 
944     if( !InsertMenuA(menu,0,MF_BYPOSITION | MF_BITMAP | MF_POPUP,
945                      (UINT_PTR)hSysPopup, (LPSTR)hSysMenuBitmap))
946     {
947         TRACE("not inserted\n");
948 	DestroyMenu(hSysPopup);
949         return FALSE;
950     }
951 
952     EnableMenuItem(hSysPopup, SC_SIZE, MF_BYCOMMAND | MF_GRAYED);
953     EnableMenuItem(hSysPopup, SC_MOVE, MF_BYCOMMAND | MF_GRAYED);
954     EnableMenuItem(hSysPopup, SC_MAXIMIZE, MF_BYCOMMAND | MF_GRAYED);
955     SetMenuDefaultItem(hSysPopup, SC_CLOSE, FALSE);
956 
957     /* redraw menu */
958     DrawMenuBar(frame);
959 
960     return TRUE;
961 }
962 
963 /**********************************************************************
964  *					MDI_RestoreFrameMenu
965  */
966 static BOOL MDI_RestoreFrameMenu( HWND frame, HWND hChild, HBITMAP hBmpClose )
967 {
968     MENUITEMINFOW menuInfo;
969     HMENU menu = GetMenu( frame );
970     INT nItems;
971     UINT iId;
972 
973     TRACE("frame %p,child %p\n",frame, hChild);
974 
975     if (!menu) return FALSE;
976 
977     /* if there is no system buttons then nothing to do */
978     nItems = GetMenuItemCount(menu) - 1;
979     iId = GetMenuItemID(menu, nItems);
980     if ( !(iId == SC_RESTORE || iId == SC_CLOSE) )
981     {
982         ERR("no system buttons then nothing to do\n");
983         return FALSE;
984     }
985 
986     /*
987      * Remove the system menu, If that menu is the icon of the window
988      * as it is in win95, we have to delete the bitmap.
989      */
990     memset(&menuInfo, 0, sizeof(menuInfo));
991     menuInfo.cbSize = sizeof(menuInfo);
992     menuInfo.fMask  = MIIM_DATA | MIIM_TYPE | MIIM_BITMAP;
993 
994     GetMenuItemInfoW(menu,
995 		     0,
996 		     TRUE,
997 		     &menuInfo);
998 
999     RemoveMenu(menu,0,MF_BYPOSITION);
1000 
1001     if ( (menuInfo.fType & MFT_BITMAP) &&
1002 	 (menuInfo.dwTypeData != 0) &&
1003 	 (menuInfo.dwTypeData != (LPWSTR)hBmpClose) )
1004     {
1005         DeleteObject(menuInfo.dwTypeData);
1006     }
1007 
1008     if ( menuInfo.hbmpItem != 0 )
1009          DeleteObject(menuInfo.hbmpItem);
1010 
1011     /* close */
1012     DeleteMenu(menu, SC_CLOSE, MF_BYCOMMAND);
1013     /* restore */
1014     DeleteMenu(menu, SC_RESTORE, MF_BYCOMMAND);
1015     /* minimize */
1016     DeleteMenu(menu, SC_MINIMIZE, MF_BYCOMMAND);
1017 
1018     DrawMenuBar(frame);
1019 
1020     return TRUE;
1021 }
1022 
1023 
1024 /**********************************************************************
1025  *				        MDI_UpdateFrameText
1026  *
1027  * used when child window is maximized/restored
1028  *
1029  * Note: lpTitle can be NULL
1030  */
1031 static void MDI_UpdateFrameText( HWND frame, HWND hClient, BOOL repaint, LPCWSTR lpTitle )
1032 {
1033     WCHAR   lpBuffer[MDI_MAXTITLELENGTH+1];
1034     MDICLIENTINFO *ci = get_client_info( hClient );
1035 
1036     TRACE("frameText %s\n", debugstr_w(lpTitle));
1037 
1038     if (!ci) return;
1039 
1040     if (!lpTitle && !ci->frameTitle)  /* first time around, get title from the frame window */
1041     {
1042         GetWindowTextW( frame, lpBuffer, sizeof(lpBuffer)/sizeof(WCHAR) );
1043         lpTitle = lpBuffer;
1044     }
1045 
1046     /* store new "default" title if lpTitle is not NULL */
1047     if (lpTitle)
1048     {
1049 	HeapFree( GetProcessHeap(), 0, ci->frameTitle );
1050 	if ((ci->frameTitle = HeapAlloc( GetProcessHeap(), 0, (strlenW(lpTitle)+1)*sizeof(WCHAR))))
1051             strcpyW( ci->frameTitle, lpTitle );
1052     }
1053 
1054     if (ci->frameTitle)
1055     {
1056 	if (ci->hwndChildMaximized)
1057 	{
1058 	    /* combine frame title and child title if possible */
1059 
1060 	    static const WCHAR lpBracket[]  = {' ','-',' ','[',0};
1061 	    static const WCHAR lpBracket2[]  = {']',0};
1062 	    int	i_frame_text_length = strlenW(ci->frameTitle);
1063 
1064 	    lstrcpynW( lpBuffer, ci->frameTitle, MDI_MAXTITLELENGTH);
1065 
1066 	    if( i_frame_text_length + 6 < MDI_MAXTITLELENGTH )
1067             {
1068 		strcatW( lpBuffer, lpBracket );
1069                 if (GetWindowTextW( ci->hwndActiveChild, lpBuffer + i_frame_text_length + 4,
1070                                     MDI_MAXTITLELENGTH - i_frame_text_length - 5 ))
1071                     strcatW( lpBuffer, lpBracket2 );
1072                 else
1073                     lpBuffer[i_frame_text_length] = 0;  /* remove bracket */
1074             }
1075 	}
1076 	else
1077 	{
1078             lstrcpynW(lpBuffer, ci->frameTitle, MDI_MAXTITLELENGTH+1 );
1079 	}
1080     }
1081     else
1082 	lpBuffer[0] = '\0';
1083 
1084     DefWindowProcW( frame, WM_SETTEXT, 0, (LPARAM)lpBuffer );
1085 
1086     if (repaint)
1087     {
1088        if (!NtUserCallTwoParam((DWORD_PTR)frame,DC_ACTIVE,TWOPARAM_ROUTINE_REDRAWTITLE))
1089         SetWindowPos( frame, 0,0,0,0,0, SWP_FRAMECHANGED |
1090                       SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER );
1091     }
1092 }
1093 
1094 
1095 /* ----------------------------- Interface ---------------------------- */
1096 
1097 
1098 /**********************************************************************
1099  *		MDIClientWndProc_common
1100  */
1101 LRESULT WINAPI MDIClientWndProc_common( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam, BOOL unicode )
1102 {
1103     MDICLIENTINFO *ci = NULL;
1104 
1105     TRACE("%p %04x (%s) %08lx %08lx\n", hwnd, message, SPY_GetMsgName(message, hwnd), wParam, lParam);
1106 
1107     if (!(ci = get_client_info( hwnd )))
1108     {
1109 #ifdef __REACTOS__
1110         if (message == WM_NCCREATE)
1111         {
1112           if (!(ci = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*ci))))
1113              return FALSE;
1114            SetWindowLongPtrW( hwnd, GWLP_MDIWND, (LONG_PTR)ci );
1115            ci->hBmpClose = 0;
1116            NtUserSetWindowFNID( hwnd, FNID_MDICLIENT); // wine uses WIN_ISMDICLIENT
1117         }
1118 #else
1119         if (message == WM_NCCREATE) win_set_flags( hwnd, WIN_ISMDICLIENT, 0 );
1120 #endif
1121         return unicode ? DefWindowProcW( hwnd, message, wParam, lParam ) :
1122                          DefWindowProcA( hwnd, message, wParam, lParam );
1123     }
1124 
1125     switch (message)
1126     {
1127       case WM_CREATE:
1128       {
1129           /* Since we are using only cs->lpCreateParams, we can safely
1130            * cast to LPCREATESTRUCTA here */
1131         LPCREATESTRUCTA cs = (LPCREATESTRUCTA)lParam;
1132         LPCLIENTCREATESTRUCT ccs = (LPCLIENTCREATESTRUCT)cs->lpCreateParams;
1133 
1134         ci->hWindowMenu		= ccs->hWindowMenu;
1135         ci->idFirstChild	= ccs->idFirstChild;
1136         ci->hwndChildMaximized  = 0;
1137         ci->child = NULL;
1138 	ci->nActiveChildren	= 0;
1139 	ci->nTotalCreated	= 0;
1140 	ci->frameTitle		= NULL;
1141 	ci->mdiFlags		= 0;
1142         ci->hFrameMenu = GetMenu(cs->hwndParent);
1143 
1144 	if (!ci->hBmpClose) ci->hBmpClose = CreateMDIMenuBitmap();
1145 
1146         TRACE("Client created: hwnd %p, Window menu %p, idFirst = %04x\n",
1147               hwnd, ci->hWindowMenu, ci->idFirstChild );
1148         return 0;
1149       }
1150 
1151       case WM_DESTROY:
1152       {
1153           if( ci->hwndChildMaximized )
1154               MDI_RestoreFrameMenu(GetParent(hwnd), ci->hwndChildMaximized, ci->hBmpClose);
1155 
1156           ci->nActiveChildren = 0;
1157           MDI_RefreshMenu(ci);
1158 
1159           HeapFree( GetProcessHeap(), 0, ci->child );
1160           HeapFree( GetProcessHeap(), 0, ci->frameTitle );
1161 #ifdef __REACTOS__
1162           HeapFree( GetProcessHeap(), 0, ci );
1163           SetWindowLongPtrW( hwnd, GWLP_MDIWND, 0 );
1164 #endif
1165           return 0;
1166       }
1167 
1168 #ifdef __REACTOS__
1169       case WM_NCDESTROY:
1170       {
1171           NtUserSetWindowFNID(hwnd, FNID_DESTROY);
1172           return 0;
1173       }
1174 #endif
1175 
1176       case WM_MDIACTIVATE:
1177       {
1178         if( ci->hwndActiveChild != (HWND)wParam )
1179 	    SetWindowPos((HWND)wParam, 0,0,0,0,0, SWP_NOSIZE | SWP_NOMOVE);
1180         return 0;
1181       }
1182 
1183       case WM_MDICASCADE:
1184         return MDICascade(hwnd, ci);
1185 
1186       case WM_MDICREATE:
1187         if (lParam)
1188         {
1189             HWND child;
1190 
1191             if (unicode)
1192             {
1193                 MDICREATESTRUCTW *csW = (MDICREATESTRUCTW *)lParam;
1194                 child = CreateWindowExW(WS_EX_MDICHILD, csW->szClass,
1195                                             csW->szTitle, csW->style,
1196                                             csW->x, csW->y, csW->cx, csW->cy,
1197                                             hwnd, 0, csW->hOwner,
1198                                             (LPVOID)csW->lParam);
1199             }
1200             else
1201             {
1202                 MDICREATESTRUCTA *csA = (MDICREATESTRUCTA *)lParam;
1203                 child = CreateWindowExA(WS_EX_MDICHILD, csA->szClass,
1204                                             csA->szTitle, csA->style,
1205                                             csA->x, csA->y, csA->cx, csA->cy,
1206                                             hwnd, 0, csA->hOwner,
1207                                             (LPVOID)csA->lParam);
1208             }
1209             return (LRESULT)child;
1210         }
1211         return 0;
1212 
1213       case WM_MDIDESTROY:
1214           return MDIDestroyChild( hwnd, ci, (HWND)wParam, TRUE );
1215 
1216       case WM_MDIGETACTIVE:
1217           if (lParam) *(BOOL *)lParam = IsZoomed(ci->hwndActiveChild);
1218           return (LRESULT)ci->hwndActiveChild;
1219 
1220       case WM_MDIICONARRANGE:
1221 	ci->mdiFlags |= MDIF_NEEDUPDATE;
1222         ArrangeIconicWindows( hwnd );
1223 	ci->sbRecalc = SB_BOTH+1;
1224 #ifdef __REACTOS__
1225 	PostMessageA( hwnd, WM_MDICALCCHILDSCROLL, 0, 0 ); //// ReactOS: Post not send!
1226 #else
1227         SendMessageW( hwnd, WM_MDICALCCHILDSCROLL, 0, 0 );
1228 #endif
1229         return 0;
1230 
1231       case WM_MDIMAXIMIZE:
1232 	ShowWindow( (HWND)wParam, SW_MAXIMIZE );
1233         return 0;
1234 
1235       case WM_MDINEXT: /* lParam != 0 means previous window */
1236       {
1237         HWND hwnd = wParam ? WIN_GetFullHandle((HWND)wParam) : ci->hwndActiveChild;
1238         HWND next = MDI_GetWindow( ci, hwnd, !lParam, 0 );
1239         MDI_SwitchActiveChild( ci, next, TRUE );
1240         if(!lParam)
1241             SetWindowPos(hwnd, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE);
1242 	break;
1243       }
1244 
1245       case WM_MDIRESTORE:
1246         ShowWindow( (HWND)wParam, SW_SHOWNORMAL );
1247         return 0;
1248 
1249       case WM_MDISETMENU:
1250           return MDISetMenu( hwnd, (HMENU)wParam, (HMENU)lParam );
1251 
1252       case WM_MDIREFRESHMENU:
1253           return MDI_RefreshMenu( ci );
1254 
1255       case WM_MDITILE:
1256 	ci->mdiFlags |= MDIF_NEEDUPDATE;
1257         ShowScrollBar( hwnd, SB_BOTH, FALSE );
1258         MDITile( hwnd, ci, wParam );
1259         ci->mdiFlags &= ~MDIF_NEEDUPDATE;
1260         return 0;
1261 
1262       case WM_VSCROLL:
1263       case WM_HSCROLL:
1264 	ci->mdiFlags |= MDIF_NEEDUPDATE;
1265         ScrollChildren( hwnd, message, wParam, lParam );
1266 	ci->mdiFlags &= ~MDIF_NEEDUPDATE;
1267         return 0;
1268 
1269       case WM_SETFOCUS:
1270           if (ci->hwndActiveChild && !IsIconic( ci->hwndActiveChild ))
1271               SetFocus( ci->hwndActiveChild );
1272           return 0;
1273 
1274       case WM_NCACTIVATE:
1275         if( ci->hwndActiveChild )
1276             SendMessageW(ci->hwndActiveChild, message, wParam, lParam);
1277 	break;
1278 
1279       case WM_PARENTNOTIFY:
1280         switch (LOWORD(wParam))
1281         {
1282         case WM_CREATE:
1283             if (GetWindowLongPtrW((HWND)lParam, GWL_EXSTYLE) & WS_EX_MDICHILD)
1284             {
1285                 // ReactOS See rev 33503
1286                 if (!ci->child)
1287                     ci->child = HeapAlloc(GetProcessHeap(), 0, sizeof(HWND));
1288                 else
1289                     ci->child = HeapReAlloc(GetProcessHeap(), 0, ci->child, sizeof(HWND) * (ci->nActiveChildren + 1));
1290 
1291                 TRACE("Adding MDI child %p, # of children %d\n",
1292                       (HWND)lParam, ci->nActiveChildren);
1293 
1294                 if (ci->child != NULL)
1295                 {
1296                     ci->child[ci->nActiveChildren] = (HWND)lParam;
1297                     ci->nTotalCreated++;
1298                     ci->nActiveChildren++;
1299                 }
1300             }
1301             break;
1302 
1303         case WM_LBUTTONDOWN:
1304             {
1305             HWND child;
1306             POINT pt;
1307             pt.x = (short)LOWORD(lParam);
1308             pt.y = (short)HIWORD(lParam);
1309             child = ChildWindowFromPoint(hwnd, pt);
1310 
1311 	    TRACE("notification from %p (%li,%li)\n",child,pt.x,pt.y);
1312 
1313             if( child && child != hwnd && child != ci->hwndActiveChild )
1314                 SetWindowPos(child, 0,0,0,0,0, SWP_NOSIZE | SWP_NOMOVE );
1315             break;
1316             }
1317 
1318         case WM_DESTROY:
1319             return MDIDestroyChild( hwnd, ci, WIN_GetFullHandle( (HWND)lParam ), FALSE );
1320         }
1321         return 0;
1322 
1323       case WM_SIZE:
1324         if( ci->hwndActiveChild && IsZoomed(ci->hwndActiveChild) )
1325 	{
1326 	    RECT	rect;
1327 
1328 	    SetRect(&rect, 0, 0, LOWORD(lParam), HIWORD(lParam));
1329 	    AdjustWindowRectEx(&rect, GetWindowLongPtrA(ci->hwndActiveChild, GWL_STYLE),
1330                                0, GetWindowLongPtrA(ci->hwndActiveChild, GWL_EXSTYLE) );
1331 	    MoveWindow(ci->hwndActiveChild, rect.left, rect.top,
1332 			 rect.right - rect.left, rect.bottom - rect.top, 1);
1333 	}
1334 	else
1335             MDI_PostUpdate(hwnd, ci, SB_BOTH+1);
1336 
1337 	break;
1338 
1339       case WM_MDICALCCHILDSCROLL:
1340 	if( (ci->mdiFlags & MDIF_NEEDUPDATE) && ci->sbRecalc )
1341 	{
1342             CalcChildScroll(hwnd, ci->sbRecalc-1);
1343 	    ci->sbRecalc = 0;
1344 	    ci->mdiFlags &= ~MDIF_NEEDUPDATE;
1345 	}
1346         return 0;
1347     }
1348     return unicode ? DefWindowProcW( hwnd, message, wParam, lParam ) :
1349                      DefWindowProcA( hwnd, message, wParam, lParam );
1350 }
1351 
1352 /***********************************************************************
1353  *		MDIClientWndProcA
1354  */
1355 LRESULT WINAPI MDIClientWndProcA( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
1356 {
1357     if (!IsWindow(hwnd)) return 0;
1358     return MDIClientWndProc_common( hwnd, message, wParam, lParam, FALSE );
1359 }
1360 
1361 /***********************************************************************
1362  *		MDIClientWndProcW
1363  */
1364 LRESULT WINAPI MDIClientWndProcW( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
1365 {
1366     if (!IsWindow(hwnd)) return 0;
1367     return MDIClientWndProc_common( hwnd, message, wParam, lParam, TRUE );
1368 }
1369 
1370 /***********************************************************************
1371  *		DefFrameProcA (USER32.@)
1372  */
1373 LRESULT WINAPI DefFrameProcA( HWND hwnd, HWND hwndMDIClient,
1374                                 UINT message, WPARAM wParam, LPARAM lParam)
1375 {
1376     if (hwndMDIClient)
1377     {
1378 	switch (message)
1379 	{
1380         case WM_SETTEXT:
1381             {
1382                 DWORD len = MultiByteToWideChar( CP_ACP, 0, (LPSTR)lParam, -1, NULL, 0 );
1383                 LPWSTR text = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
1384                 if (text == NULL)
1385                     return 0;
1386                 MultiByteToWideChar( CP_ACP, 0, (LPSTR)lParam, -1, text, len );
1387                 MDI_UpdateFrameText( hwnd, hwndMDIClient, FALSE, text );
1388                 HeapFree( GetProcessHeap(), 0, text );
1389             }
1390             return 1; /* success. FIXME: check text length */
1391 
1392         case WM_COMMAND:
1393         case WM_NCACTIVATE:
1394         case WM_NEXTMENU:
1395         case WM_SETFOCUS:
1396         case WM_SIZE:
1397             return DefFrameProcW( hwnd, hwndMDIClient, message, wParam, lParam );
1398         }
1399     }
1400     return DefWindowProcA(hwnd, message, wParam, lParam);
1401 }
1402 
1403 
1404 /***********************************************************************
1405  *		DefFrameProcW (USER32.@)
1406  */
1407 LRESULT WINAPI DefFrameProcW( HWND hwnd, HWND hwndMDIClient,
1408                                 UINT message, WPARAM wParam, LPARAM lParam)
1409 {
1410     MDICLIENTINFO *ci = get_client_info( hwndMDIClient );
1411 
1412     TRACE("%p %p %04x (%s) %08lx %08lx\n", hwnd, hwndMDIClient, message, SPY_GetMsgName(message, hwnd), wParam, lParam);
1413 
1414     if (ci)
1415     {
1416 	switch (message)
1417 	{
1418         case WM_COMMAND:
1419             {
1420                 WORD id = LOWORD(wParam);
1421                 /* check for possible syscommands for maximized MDI child */
1422                 if (id <  ci->idFirstChild || id >= ci->idFirstChild + ci->nActiveChildren)
1423                 {
1424                     if( (id - 0xf000) & 0xf00f ) break;
1425                     if( !ci->hwndChildMaximized ) break;
1426                     switch( id )
1427                     {
1428                     case SC_CLOSE:
1429                         if (!is_close_enabled(ci->hwndActiveChild, 0)) break;
1430                     case SC_SIZE:
1431                     case SC_MOVE:
1432                     case SC_MINIMIZE:
1433                     case SC_MAXIMIZE:
1434                     case SC_NEXTWINDOW:
1435                     case SC_PREVWINDOW:
1436                     case SC_RESTORE:
1437                         return SendMessageW( ci->hwndChildMaximized, WM_SYSCOMMAND,
1438                                              wParam, lParam);
1439                     }
1440                 }
1441                 else
1442                 {
1443                     HWND childHwnd;
1444                     if (id - ci->idFirstChild == MDI_MOREWINDOWSLIMIT)
1445                         /* User chose "More Windows..." */
1446                         childHwnd = MDI_MoreWindowsDialog(hwndMDIClient);
1447                     else
1448                         /* User chose one of the windows listed in the "Windows" menu */
1449                         childHwnd = MDI_GetChildByID(hwndMDIClient, id, ci);
1450 
1451                     if( childHwnd )
1452                         SendMessageW( hwndMDIClient, WM_MDIACTIVATE, (WPARAM)childHwnd, 0 );
1453                 }
1454             }
1455             break;
1456 
1457         case WM_NCACTIVATE:
1458 	    SendMessageW(hwndMDIClient, message, wParam, lParam);
1459 	    break;
1460 
1461         case WM_SETTEXT:
1462             MDI_UpdateFrameText( hwnd, hwndMDIClient, FALSE, (LPWSTR)lParam );
1463 	    return 1; /* success. FIXME: check text length */
1464 
1465         case WM_SETFOCUS:
1466 	    SetFocus(hwndMDIClient);
1467 	    break;
1468 
1469         case WM_SIZE:
1470             MoveWindow(hwndMDIClient, 0, 0, LOWORD(lParam), HIWORD(lParam), TRUE);
1471             break;
1472 
1473         case WM_NEXTMENU:
1474             {
1475                 MDINEXTMENU *next_menu = (MDINEXTMENU *)lParam;
1476 
1477                 if (!IsIconic(hwnd) && ci->hwndActiveChild && !IsZoomed(ci->hwndActiveChild))
1478                 {
1479                     /* control menu is between the frame system menu and
1480                      * the first entry of menu bar */
1481 //                    WND *wndPtr = WIN_GetPtr(hwnd);
1482 
1483                     if( (wParam == VK_LEFT && GetMenu(hwnd) == next_menu->hmenuIn) ||
1484                         (wParam == VK_RIGHT && GetSubMenu(GetMenu(hwnd), 0) == next_menu->hmenuIn) )
1485                     {
1486 //                        WIN_ReleasePtr(wndPtr);
1487 //                        wndPtr = WIN_GetPtr(ci->hwndActiveChild);
1488                         next_menu->hmenuNext = GetSubMenu(GetMenu(ci->hwndActiveChild), 0);
1489                         next_menu->hwndNext = ci->hwndActiveChild;
1490                     }
1491 //                    WIN_ReleasePtr(wndPtr);
1492                 }
1493                 return 0;
1494             }
1495 	}
1496     }
1497 
1498     return DefWindowProcW( hwnd, message, wParam, lParam );
1499 }
1500 
1501 /***********************************************************************
1502  *		DefMDIChildProcA (USER32.@)
1503  */
1504 LRESULT WINAPI DefMDIChildProcA( HWND hwnd, UINT message,
1505                                    WPARAM wParam, LPARAM lParam )
1506 {
1507     HWND client = GetParent(hwnd);
1508     MDICLIENTINFO *ci = get_client_info( client );
1509 
1510     TRACE("%p %04x (%s) %08lx %08lx\n", hwnd, message, SPY_GetMsgName(message, hwnd), wParam, lParam);
1511 
1512     hwnd = WIN_GetFullHandle( hwnd );
1513     if (!ci) return DefWindowProcA( hwnd, message, wParam, lParam );
1514 
1515     switch (message)
1516     {
1517     case WM_SETTEXT:
1518 	DefWindowProcA(hwnd, message, wParam, lParam);
1519 	if( ci->hwndChildMaximized == hwnd )
1520 	    MDI_UpdateFrameText( GetParent(client), client, TRUE, NULL );
1521         MDI_RefreshMenu( ci );
1522         return 1; /* success. FIXME: check text length */
1523 
1524     case WM_GETMINMAXINFO:
1525     case WM_MENUCHAR:
1526     case WM_CLOSE:
1527     case WM_SETFOCUS:
1528     case WM_CHILDACTIVATE:
1529     case WM_SYSCOMMAND:
1530     case WM_SHOWWINDOW:
1531     case WM_SETVISIBLE:
1532     case WM_SIZE:
1533     case WM_NEXTMENU:
1534     case WM_SYSCHAR:
1535     case WM_DESTROY:
1536         return DefMDIChildProcW( hwnd, message, wParam, lParam );
1537     }
1538     return DefWindowProcA(hwnd, message, wParam, lParam);
1539 }
1540 
1541 
1542 /***********************************************************************
1543  *		DefMDIChildProcW (USER32.@)
1544  */
1545 LRESULT WINAPI DefMDIChildProcW( HWND hwnd, UINT message,
1546                                    WPARAM wParam, LPARAM lParam )
1547 {
1548     HWND client = GetParent(hwnd);
1549     MDICLIENTINFO *ci = get_client_info( client );
1550 
1551     TRACE("%p %04x (%s) %08lx %08lx\n", hwnd, message, SPY_GetMsgName(message, hwnd), wParam, lParam);
1552 
1553     hwnd = WIN_GetFullHandle( hwnd );
1554     if (!ci) return DefWindowProcW( hwnd, message, wParam, lParam );
1555 
1556     switch (message)
1557     {
1558     case WM_SETTEXT:
1559         DefWindowProcW(hwnd, message, wParam, lParam);
1560         if( ci->hwndChildMaximized == hwnd )
1561             MDI_UpdateFrameText( GetParent(client), client, TRUE, NULL );
1562         MDI_RefreshMenu( ci );
1563         return 1; /* success. FIXME: check text length */
1564 
1565     case WM_GETMINMAXINFO:
1566         MDI_ChildGetMinMaxInfo( client, hwnd, (MINMAXINFO *)lParam );
1567         return 0;
1568 
1569     case WM_MENUCHAR:
1570         return MAKELRESULT( 0, MNC_CLOSE ); /* MDI children don't have menu bars */
1571 
1572     case WM_CLOSE:
1573         SendMessageW( client, WM_MDIDESTROY, (WPARAM)hwnd, 0 );
1574         return 0;
1575 
1576     case WM_SETFOCUS:
1577         if (ci->hwndActiveChild != hwnd)
1578             MDI_ChildActivate( client, hwnd );
1579         break;
1580 
1581     case WM_CHILDACTIVATE:
1582         if (IsWindowEnabled( hwnd ))
1583             MDI_ChildActivate( client, hwnd );
1584         return 0;
1585 
1586     case WM_SYSCOMMAND:
1587         switch (wParam & 0xfff0)
1588         {
1589         case SC_MOVE:
1590             if( ci->hwndChildMaximized == hwnd )
1591                 return 0;
1592             break;
1593         case SC_RESTORE:
1594         case SC_MINIMIZE:
1595             break;
1596         case SC_MAXIMIZE:
1597             if (ci->hwndChildMaximized == hwnd)
1598                 return SendMessageW( GetParent(client), message, wParam, lParam);
1599             break;
1600         case SC_NEXTWINDOW:
1601             SendMessageW( client, WM_MDINEXT, (WPARAM)ci->hwndActiveChild, 0);
1602             return 0;
1603         case SC_PREVWINDOW:
1604             SendMessageW( client, WM_MDINEXT, (WPARAM)ci->hwndActiveChild, 1);
1605             return 0;
1606         }
1607         break;
1608 
1609     case WM_SHOWWINDOW:
1610     case WM_SETVISIBLE:
1611         //// Commented out r57663
1612         /*if (ci->hwndChildMaximized) ci->mdiFlags &= ~MDIF_NEEDUPDATE;
1613         else*/ MDI_PostUpdate(client, ci, SB_BOTH+1);
1614         break;
1615 
1616     case WM_SIZE:
1617         /* This is the only place where we switch to/from maximized state */
1618         /* do not change */
1619         TRACE("current active %p, maximized %p\n", ci->hwndActiveChild, ci->hwndChildMaximized);
1620 
1621         if( ci->hwndChildMaximized == hwnd && wParam != SIZE_MAXIMIZED )
1622         {
1623             HWND frame;
1624 
1625             ci->hwndChildMaximized = 0;
1626 
1627             frame = GetParent(client);
1628             MDI_RestoreFrameMenu( frame, hwnd, ci->hBmpClose );
1629             MDI_UpdateFrameText( frame, client, TRUE, NULL );
1630         }
1631 
1632         if( wParam == SIZE_MAXIMIZED )
1633         {
1634             HWND frame, hMaxChild = ci->hwndChildMaximized;
1635 
1636             if( hMaxChild == hwnd ) break;
1637 
1638             if( hMaxChild)
1639             {
1640                 SendMessageW( hMaxChild, WM_SETREDRAW, FALSE, 0 );
1641 
1642                 MDI_RestoreFrameMenu( GetParent(client), hMaxChild, ci->hBmpClose );
1643                 ShowWindow( hMaxChild, SW_SHOWNOACTIVATE );
1644 
1645                 SendMessageW( hMaxChild, WM_SETREDRAW, TRUE, 0 );
1646             }
1647 
1648             TRACE("maximizing child %p\n", hwnd );
1649 
1650             /* keep track of the maximized window. */
1651             ci->hwndChildMaximized = hwnd; /* !!! */
1652 
1653             frame = GetParent(client);
1654             MDI_AugmentFrameMenu( frame, hwnd );
1655             MDI_UpdateFrameText( frame, client, TRUE, NULL );
1656         }
1657 
1658         if( wParam == SIZE_MINIMIZED )
1659         {
1660             HWND switchTo = MDI_GetWindow( ci, hwnd, TRUE, WS_MINIMIZE );
1661 
1662             if (!switchTo) switchTo = hwnd;
1663             SendMessageW( switchTo, WM_CHILDACTIVATE, 0, 0 );
1664 	}
1665 
1666         MDI_PostUpdate(client, ci, SB_BOTH+1);
1667         break;
1668 
1669     case WM_NEXTMENU:
1670         {
1671             MDINEXTMENU *next_menu = (MDINEXTMENU *)lParam;
1672             HWND parent = GetParent(client);
1673 
1674             if( wParam == VK_LEFT )  /* switch to frame system menu */
1675             {
1676 //                WND *wndPtr = WIN_GetPtr( parent );
1677                 next_menu->hmenuNext = GetSubMenu( GetMenu(parent), 0 );
1678 //                WIN_ReleasePtr( wndPtr );
1679             }
1680             if( wParam == VK_RIGHT )  /* to frame menu bar */
1681             {
1682                 next_menu->hmenuNext = GetMenu(parent);
1683             }
1684             next_menu->hwndNext = parent;
1685             return 0;
1686         }
1687 
1688     case WM_SYSCHAR:
1689         if (wParam == '-')
1690         {
1691             SendMessageW( hwnd, WM_SYSCOMMAND, SC_KEYMENU, VK_SPACE);
1692             return 0;
1693         }
1694         break;
1695 
1696     case WM_DESTROY:
1697         /* Remove itself from the Window menu */
1698         MDI_RefreshMenu(ci);
1699         break;
1700     }
1701     return DefWindowProcW(hwnd, message, wParam, lParam);
1702 }
1703 
1704 /**********************************************************************
1705  *		CreateMDIWindowA (USER32.@) Creates a MDI child
1706  *
1707  * RETURNS
1708  *    Success: Handle to created window
1709  *    Failure: NULL
1710  */
1711 HWND WINAPI CreateMDIWindowA(
1712     LPCSTR lpClassName,    /* [in] Pointer to registered child class name */
1713     LPCSTR lpWindowName,   /* [in] Pointer to window name */
1714     DWORD dwStyle,         /* [in] Window style */
1715     INT X,               /* [in] Horizontal position of window */
1716     INT Y,               /* [in] Vertical position of window */
1717     INT nWidth,          /* [in] Width of window */
1718     INT nHeight,         /* [in] Height of window */
1719     HWND hWndParent,     /* [in] Handle to parent window */
1720     HINSTANCE hInstance, /* [in] Handle to application instance */
1721     LPARAM lParam)         /* [in] Application-defined value */
1722 {
1723     TRACE("(%s,%s,%08lx,%d,%d,%d,%d,%p,%p,%08lx)\n",
1724           debugstr_a(lpClassName),debugstr_a(lpWindowName),dwStyle,X,Y,
1725           nWidth,nHeight,hWndParent,hInstance,lParam);
1726 
1727     return CreateWindowExA(WS_EX_MDICHILD, lpClassName, lpWindowName,
1728                            dwStyle, X, Y, nWidth, nHeight, hWndParent,
1729                            0, hInstance, (LPVOID)lParam);
1730 }
1731 
1732 /***********************************************************************
1733  *		CreateMDIWindowW (USER32.@) Creates a MDI child
1734  *
1735  * RETURNS
1736  *    Success: Handle to created window
1737  *    Failure: NULL
1738  */
1739 HWND WINAPI CreateMDIWindowW(
1740     LPCWSTR lpClassName,    /* [in] Pointer to registered child class name */
1741     LPCWSTR lpWindowName,   /* [in] Pointer to window name */
1742     DWORD dwStyle,         /* [in] Window style */
1743     INT X,               /* [in] Horizontal position of window */
1744     INT Y,               /* [in] Vertical position of window */
1745     INT nWidth,          /* [in] Width of window */
1746     INT nHeight,         /* [in] Height of window */
1747     HWND hWndParent,     /* [in] Handle to parent window */
1748     HINSTANCE hInstance, /* [in] Handle to application instance */
1749     LPARAM lParam)         /* [in] Application-defined value */
1750 {
1751     TRACE("(%s,%s,%08lx,%d,%d,%d,%d,%p,%p,%08lx)\n",
1752           debugstr_w(lpClassName), debugstr_w(lpWindowName), dwStyle, X, Y,
1753           nWidth, nHeight, hWndParent, hInstance, lParam);
1754 
1755     return CreateWindowExW(WS_EX_MDICHILD, lpClassName, lpWindowName,
1756                            dwStyle, X, Y, nWidth, nHeight, hWndParent,
1757                            0, hInstance, (LPVOID)lParam);
1758 }
1759 
1760 /**********************************************************************
1761  *		TranslateMDISysAccel (USER32.@)
1762  */
1763 BOOL WINAPI TranslateMDISysAccel( HWND hwndClient, LPMSG msg )
1764 {
1765     if (msg->message == WM_KEYDOWN || msg->message == WM_SYSKEYDOWN)
1766     {
1767         MDICLIENTINFO *ci = get_client_info( hwndClient );
1768         WPARAM wParam = 0;
1769 
1770         if (!ci || !IsWindowEnabled(ci->hwndActiveChild)) return 0;
1771 
1772         /* translate if the Ctrl key is down and Alt not. */
1773 
1774         if( (GetKeyState(VK_CONTROL) & 0x8000) && !(GetKeyState(VK_MENU) & 0x8000))
1775         {
1776             switch( msg->wParam )
1777             {
1778             case VK_F6:
1779             case VK_TAB:
1780                 wParam = ( GetKeyState(VK_SHIFT) & 0x8000 ) ? SC_NEXTWINDOW : SC_PREVWINDOW;
1781                 break;
1782             case VK_F4:
1783             case VK_RBUTTON:
1784                 if (is_close_enabled(ci->hwndActiveChild, 0))
1785                 {
1786                     wParam = SC_CLOSE;
1787                     break;
1788                 }
1789                 /* fall through */
1790             default:
1791                 return FALSE;
1792             }
1793             TRACE("wParam = %04lx\n", wParam);
1794             SendMessageW(ci->hwndActiveChild, WM_SYSCOMMAND, wParam, msg->wParam);
1795             return TRUE;
1796         }
1797     }
1798     return FALSE; /* failure */
1799 }
1800 
1801 /***********************************************************************
1802  *		CalcChildScroll (USER32.@)
1803  */
1804 void WINAPI CalcChildScroll( HWND hwnd, INT scroll )
1805 {
1806     SCROLLINFO info;
1807     RECT childRect, clientRect;
1808     HWND *list;
1809     DWORD style;
1810     WINDOWINFO WindowInfo;
1811 
1812     GetClientRect( hwnd, &clientRect );
1813     SetRectEmpty( &childRect );
1814 
1815    /* The rectangle returned by GetClientRect always has 0,0 as top left
1816     * because it is in client coordinates. The rectangles returned by
1817     * GetWindowRect are in screen coordinates to make this complicated.
1818     *
1819     * Apparently (in ReactOS at least) the rcClient returned by GetWindowInfo
1820     * is in screen coordinates too.
1821     */
1822     WindowInfo.cbSize = sizeof(WindowInfo);
1823     if (!GetWindowInfo(hwnd, &WindowInfo))
1824     {
1825         ERR("Can't get window info\n");
1826         return;
1827     }
1828 
1829     TRACE("CalcChildScroll 1\n");
1830     if ((list = WIN_ListChildren( hwnd )))
1831     {
1832         int i;
1833         for (i = 0; list[i]; i++)
1834         {
1835             style = GetWindowLongPtrW( list[i], GWL_STYLE );
1836             if (style & WS_MAXIMIZE)
1837             {
1838                 HeapFree( GetProcessHeap(), 0, list );
1839                 ShowScrollBar( hwnd, SB_BOTH, FALSE );
1840                 ERR("CalcChildScroll 2\n");
1841                 return;
1842             }
1843             if (style & WS_VISIBLE)
1844             {
1845                 RECT rect;
1846                 GetWindowRect( list[i], &rect );
1847                 OffsetRect(&rect, -WindowInfo.rcClient.left,
1848                                   -WindowInfo.rcClient.top);
1849                 //WIN_GetRectangles( list[i], COORDS_PARENT, &rect, NULL );
1850                 TRACE("CalcChildScroll L\n");
1851                 UnionRect( &childRect, &rect, &childRect );
1852             }
1853         }
1854         HeapFree( GetProcessHeap(), 0, list );
1855     }
1856     UnionRect( &childRect, &clientRect, &childRect );
1857     TRACE("CalcChildScroll 3\n");
1858     /* set common info values */
1859     info.cbSize = sizeof(info);
1860     info.fMask = SIF_POS | SIF_RANGE | SIF_PAGE;
1861     info.nPos = 0;
1862 
1863     /* set the specific values and apply but only if window style allows */
1864     /* Note how we set nPos to 0 because we scroll the clients instead of
1865      * the window, and we set nPage to 1 bigger than the clientRect because
1866      * otherwise the scrollbar never disables. This causes a somewhat ugly
1867      * effect though while scrolling.
1868      */
1869     style = GetWindowLongW( hwnd, GWL_STYLE );
1870     switch( scroll )
1871     {
1872 	case SB_BOTH:
1873 	case SB_HORZ:
1874                         if (style & (WS_HSCROLL | WS_VSCROLL))
1875                         {
1876                             info.nMin = childRect.left;
1877                             info.nMax = childRect.right;
1878                             info.nPage = 1 + clientRect.right - clientRect.left;
1879                             //info.nMax = childRect.right - clientRect.right;
1880                             //info.nPos = clientRect.left - childRect.left;
1881                             SetScrollInfo(hwnd, SB_HORZ, &info, TRUE);
1882                         }
1883 			if (scroll == SB_HORZ) break;
1884 			/* fall through */
1885 	case SB_VERT:
1886                         if (style & (WS_HSCROLL | WS_VSCROLL))
1887                         {
1888                             info.nMin = childRect.top;
1889                             info.nMax = childRect.bottom;
1890                             info.nPage = 1 + clientRect.bottom - clientRect.top;
1891                             //info.nMax = childRect.bottom - clientRect.bottom;
1892                             //info.nPos = clientRect.top - childRect.top;
1893                             SetScrollInfo(hwnd, SB_VERT, &info, TRUE);
1894                         }
1895 			break;
1896     }
1897 }
1898 
1899 
1900 /***********************************************************************
1901  *		ScrollChildren (USER32.@)
1902  */
1903 void WINAPI ScrollChildren(HWND hWnd, UINT uMsg, WPARAM wParam,
1904                              LPARAM lParam)
1905 {
1906     INT newPos = -1;
1907     INT curPos, length, minPos, maxPos, shift;
1908     RECT rect;
1909 
1910     GetClientRect( hWnd, &rect );
1911 
1912     switch(uMsg)
1913     {
1914     case WM_HSCROLL:
1915 	GetScrollRange(hWnd,SB_HORZ,&minPos,&maxPos);
1916 	curPos = GetScrollPos(hWnd,SB_HORZ);
1917 	length = (rect.right - rect.left) / 2;
1918 	shift = GetSystemMetrics(SM_CYHSCROLL);
1919         break;
1920     case WM_VSCROLL:
1921 	GetScrollRange(hWnd,SB_VERT,&minPos,&maxPos);
1922 	curPos = GetScrollPos(hWnd,SB_VERT);
1923 	length = (rect.bottom - rect.top) / 2;
1924 	shift = GetSystemMetrics(SM_CXVSCROLL);
1925         break;
1926     default:
1927         return;
1928     }
1929 
1930     switch( wParam )
1931     {
1932 	case SB_LINEUP:
1933 		        newPos = curPos - shift;
1934 			break;
1935 	case SB_LINEDOWN:
1936 			newPos = curPos + shift;
1937 			break;
1938 	case SB_PAGEUP:
1939 			newPos = curPos - length;
1940 			break;
1941 	case SB_PAGEDOWN:
1942 			newPos = curPos + length;
1943 			break;
1944 
1945 	case SB_THUMBPOSITION:
1946 			newPos = LOWORD(lParam);
1947 			break;
1948 
1949 	case SB_THUMBTRACK:
1950 			return;
1951 
1952 	case SB_TOP:
1953 			newPos = minPos;
1954 			break;
1955 	case SB_BOTTOM:
1956 			newPos = maxPos;
1957 			break;
1958 	case SB_ENDSCROLL:
1959 			CalcChildScroll(hWnd,(uMsg == WM_VSCROLL)?SB_VERT:SB_HORZ);
1960 			return;
1961     }
1962 
1963     if( newPos > maxPos )
1964 	newPos = maxPos;
1965     else
1966 	if( newPos < minPos )
1967 	    newPos = minPos;
1968 
1969     SetScrollPos(hWnd, (uMsg == WM_VSCROLL)?SB_VERT:SB_HORZ , newPos, TRUE);
1970 
1971     if( uMsg == WM_VSCROLL )
1972 	ScrollWindowEx(hWnd ,0 ,curPos - newPos, NULL, NULL, 0, NULL,
1973 			SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
1974     else
1975 	ScrollWindowEx(hWnd ,curPos - newPos, 0, NULL, NULL, 0, NULL,
1976 			SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
1977 }
1978 
1979 /******************************************************************************
1980  *		CascadeWindows (USER32.@) Cascades MDI child windows
1981  *
1982  * RETURNS
1983  *    Success: Number of cascaded windows.
1984  *    Failure: 0
1985  */
1986 
1987 typedef struct CASCADE_INFO
1988 {
1989     HWND hwndTop;
1990     UINT wFlags;
1991     HWND hwndParent;
1992     HWND hwndDesktop;
1993     HWND hTrayWnd;
1994     HWND hwndProgman;
1995     HWND *ahwnd;
1996     DWORD chwnd;
1997 } CASCADE_INFO;
1998 
1999 static BOOL CALLBACK
2000 GetCascadeChildProc(HWND hwnd, LPARAM lParam)
2001 {
2002     DWORD count, size;
2003     HWND *ahwnd;
2004     CASCADE_INFO *pInfo = (CASCADE_INFO *)lParam;
2005 
2006     if (hwnd == pInfo->hwndDesktop || hwnd == pInfo->hTrayWnd ||
2007         hwnd == pInfo->hwndProgman || hwnd == pInfo->hwndTop)
2008     {
2009         return TRUE;
2010     }
2011 
2012     if (pInfo->hwndParent && GetParent(hwnd) != pInfo->hwndParent)
2013         return TRUE;
2014 
2015     if ((pInfo->wFlags & MDITILE_SKIPDISABLED) && !IsWindowEnabled(hwnd))
2016         return TRUE;
2017 
2018     if (!IsWindowVisible(hwnd) || IsIconic(hwnd))
2019         return TRUE;
2020 
2021     count = pInfo->chwnd;
2022     size = (count + 1) * sizeof(HWND);
2023 
2024     if (count == 0 || pInfo->ahwnd == NULL)
2025     {
2026         count = 0;
2027         pInfo->ahwnd = (HWND *)HeapAlloc(GetProcessHeap(), 0, size);
2028     }
2029     else
2030     {
2031         ahwnd = (HWND *)HeapReAlloc(GetProcessHeap(), 0, pInfo->ahwnd, size);
2032         if (ahwnd == NULL)
2033         {
2034             HeapFree(GetProcessHeap(), 0, pInfo->ahwnd);
2035         }
2036         pInfo->ahwnd = ahwnd;
2037     }
2038 
2039     if (pInfo->ahwnd == NULL)
2040     {
2041         pInfo->chwnd = 0;
2042         return FALSE;
2043     }
2044 
2045     pInfo->ahwnd[count] = hwnd;
2046     pInfo->chwnd = count + 1;
2047     return TRUE;
2048 }
2049 
2050 static BOOL
2051 QuerySizeFix(HWND hwnd, LPINT pcx, LPINT pcy)
2052 {
2053     MINMAXINFO mmi;
2054     DWORD_PTR dwResult;
2055 
2056     mmi.ptMinTrackSize.x = mmi.ptMinTrackSize.y = 0;
2057     mmi.ptMaxTrackSize.x = mmi.ptMaxTrackSize.y = MAXLONG;
2058     if (SendMessageTimeoutW(hwnd, WM_GETMINMAXINFO, 0, (LPARAM)&mmi,
2059                             SMTO_ABORTIFHUNG | SMTO_NORMAL, 120, &dwResult))
2060     {
2061         *pcx = min(max(*pcx, mmi.ptMinTrackSize.x), mmi.ptMaxTrackSize.x);
2062         *pcy = min(max(*pcy, mmi.ptMinTrackSize.y), mmi.ptMaxTrackSize.y);
2063         return TRUE;
2064     }
2065     return FALSE;
2066 }
2067 
2068 WORD WINAPI
2069 CascadeWindows(HWND hwndParent, UINT wFlags, LPCRECT lpRect,
2070                UINT cKids, const HWND *lpKids)
2071 {
2072     CASCADE_INFO info;
2073     HWND hwnd, hwndTop, hwndPrev;
2074     HMONITOR hMon;
2075     MONITORINFO mi;
2076     RECT rcWork, rcWnd;
2077     DWORD i, ret = 0;
2078     INT x, y, cx, cy, cxNew, cyNew, cxWork, cyWork, dx, dy;
2079     HDWP hDWP;
2080     POINT pt;
2081 
2082     TRACE("(%p,0x%08x,...,%u,...)\n", hwndParent, wFlags, cKids);
2083 
2084     hwndTop = GetTopWindow(hwndParent);
2085 
2086     ZeroMemory(&info, sizeof(info));
2087     info.hwndDesktop = GetDesktopWindow();
2088     info.hTrayWnd = FindWindowW(L"Shell_TrayWnd", NULL);
2089     info.hwndProgman = FindWindowW(L"Progman", NULL);
2090     info.hwndParent = hwndParent;
2091     info.wFlags = wFlags;
2092 
2093     if (cKids == 0 || lpKids == NULL)
2094     {
2095         info.hwndTop = hwndTop;
2096         EnumChildWindows(hwndParent, GetCascadeChildProc, (LPARAM)&info);
2097 
2098         info.hwndTop = NULL;
2099         GetCascadeChildProc(hwndTop, (LPARAM)&info);
2100     }
2101     else
2102     {
2103         info.chwnd = cKids;
2104         info.ahwnd = (HWND *)lpKids;
2105     }
2106 
2107     if (info.chwnd == 0 || info.ahwnd == NULL)
2108         return ret;
2109 
2110     if (lpRect)
2111     {
2112         rcWork = *lpRect;
2113     }
2114     else if (hwndParent)
2115     {
2116         GetClientRect(hwndParent, &rcWork);
2117     }
2118     else
2119     {
2120         pt.x = pt.y = 0;
2121         hMon = MonitorFromPoint(pt, MONITOR_DEFAULTTOPRIMARY);
2122         mi.cbSize = sizeof(mi);
2123         GetMonitorInfoW(hMon, &mi);
2124         rcWork = mi.rcWork;
2125     }
2126 
2127     hDWP = BeginDeferWindowPos(info.chwnd);
2128     if (hDWP == NULL)
2129         goto cleanup;
2130 
2131     x = rcWork.left;
2132     y = rcWork.top;
2133     dx = GetSystemMetrics(SM_CXSIZEFRAME) + GetSystemMetrics(SM_CXSIZE);
2134     dy = GetSystemMetrics(SM_CYSIZEFRAME) + GetSystemMetrics(SM_CYSIZE);
2135     cxWork = rcWork.right - rcWork.left;
2136     cyWork = rcWork.bottom - rcWork.top;
2137     hwndPrev = NULL;
2138     for (i = info.chwnd; i > 0;)    /* in reverse order */
2139     {
2140         --i;
2141         hwnd = info.ahwnd[i];
2142 
2143         if (!IsWindowVisible(hwnd) || IsIconic(hwnd))
2144             continue;
2145 
2146         if ((info.wFlags & MDITILE_SKIPDISABLED) && !IsWindowEnabled(hwnd))
2147             continue;
2148 
2149         if (IsZoomed(hwnd))
2150             ShowWindow(hwnd, SW_RESTORE | SW_SHOWNA);
2151 
2152         GetWindowRect(hwnd, &rcWnd);
2153         cxNew = cx = rcWnd.right - rcWnd.left;
2154         cyNew = cy = rcWnd.bottom - rcWnd.top;
2155 
2156         /* if we can change the window size and it is not only one */
2157         if (info.chwnd != 1 && (GetWindowLongPtrW(hwnd, GWL_STYLE) & WS_THICKFRAME))
2158         {
2159             /* check the size */
2160 #define THRESHOLD(xy) (((xy) * 5) / 7)      /* in the rate 5/7 */
2161             cxNew = min(cxNew, THRESHOLD(cxWork));
2162             cyNew = min(cyNew, THRESHOLD(cyWork));
2163 #undef THRESHOLD
2164             if (cx != cxNew || cy != cyNew)
2165             {
2166                 /* too large. shrink if we can */
2167                 if (QuerySizeFix(hwnd, &cxNew, &cyNew))
2168                 {
2169                     cx = cxNew;
2170                     cy = cyNew;
2171                 }
2172             }
2173         }
2174 
2175         if (x + cx > rcWork.right)
2176             x = rcWork.left;
2177         if (y + cy > rcWork.bottom)
2178             y = rcWork.top;
2179 
2180         hDWP = DeferWindowPos(hDWP, hwnd, HWND_TOP, x, y, cx, cy, SWP_NOACTIVATE);
2181         if (hDWP == NULL)
2182         {
2183             ret = 0;
2184             goto cleanup;
2185         }
2186 
2187         x += dx;
2188         y += dy;
2189         hwndPrev = hwnd;
2190         ++ret;
2191     }
2192 
2193     EndDeferWindowPos(hDWP);
2194 
2195     if (hwndPrev)
2196         SetForegroundWindow(hwndPrev);
2197 
2198 cleanup:
2199     if (cKids == 0 || lpKids == NULL)
2200         HeapFree(GetProcessHeap(), 0, info.ahwnd);
2201 
2202     return (WORD)ret;
2203 }
2204 
2205 
2206 /***********************************************************************
2207  *		CascadeChildWindows (USER32.@)
2208  */
2209 WORD WINAPI CascadeChildWindows( HWND parent, UINT flags )
2210 {
2211     return CascadeWindows( parent, flags, NULL, 0, NULL );
2212 }
2213 
2214 
2215 /******************************************************************************
2216  *		TileWindows (USER32.@) Tiles MDI child windows
2217  *
2218  * RETURNS
2219  *    Success: Number of tiled windows.
2220  *    Failure: 0
2221  */
2222 WORD WINAPI
2223 TileWindows(HWND hwndParent, UINT wFlags, LPCRECT lpRect,
2224             UINT cKids, const HWND *lpKids)
2225 {
2226     HWND hwnd, hwndTop, hwndPrev;
2227     CASCADE_INFO info;
2228     RECT rcWork, rcWnd;
2229     DWORD i, iRow, iColumn, cRows, cColumns, ret = 0;
2230     INT x, y, cx, cy, cxNew, cyNew, cxWork, cyWork, cxCell, cyCell, cxMin2, cyMin3;
2231     HDWP hDWP;
2232     MONITORINFO mi;
2233     HMONITOR hMon;
2234     POINT pt;
2235 
2236     TRACE("(%p,0x%08x,...,%u,...)\n", hwndParent, wFlags, cKids);
2237 
2238     hwndTop = GetTopWindow(hwndParent);
2239 
2240     ZeroMemory(&info, sizeof(info));
2241     info.hwndDesktop = GetDesktopWindow();
2242     info.hTrayWnd = FindWindowW(L"Shell_TrayWnd", NULL);
2243     info.hwndProgman = FindWindowW(L"Progman", NULL);
2244     info.hwndParent = hwndParent;
2245     info.wFlags = wFlags;
2246 
2247     if (cKids == 0 || lpKids == NULL)
2248     {
2249         info.hwndTop = hwndTop;
2250         EnumChildWindows(hwndParent, GetCascadeChildProc, (LPARAM)&info);
2251 
2252         info.hwndTop = NULL;
2253         GetCascadeChildProc(hwndTop, (LPARAM)&info);
2254     }
2255     else
2256     {
2257         info.chwnd = cKids;
2258         info.ahwnd = (HWND *)lpKids;
2259     }
2260 
2261     if (info.chwnd == 0 || info.ahwnd == NULL)
2262         return ret;
2263 
2264     if (lpRect)
2265     {
2266         rcWork = *lpRect;
2267     }
2268     else if (hwndParent)
2269     {
2270         GetClientRect(hwndParent, &rcWork);
2271     }
2272     else
2273     {
2274         pt.x = pt.y = 0;
2275         hMon = MonitorFromPoint(pt, MONITOR_DEFAULTTOPRIMARY);
2276         mi.cbSize = sizeof(mi);
2277         GetMonitorInfoW(hMon, &mi);
2278         rcWork = mi.rcWork;
2279     }
2280 
2281     cxWork = rcWork.right - rcWork.left;
2282     cyWork = rcWork.bottom - rcWork.top;
2283 
2284     cxMin2 = GetSystemMetrics(SM_CXMIN) * 2;
2285     cyMin3 = GetSystemMetrics(SM_CYMIN) * 3;
2286 
2287     /* calculate the numbers and widths of columns and rows */
2288     if (info.wFlags & MDITILE_HORIZONTAL)
2289     {
2290         cColumns = info.chwnd;
2291         cRows = 1;
2292         for (;;)
2293         {
2294             cxCell = cxWork / cColumns;
2295             cyCell = cyWork / cRows;
2296             if (cyCell <= cyMin3 || cxCell >= cxMin2)
2297                 break;
2298 
2299             ++cRows;
2300             cColumns = (info.chwnd + cRows - 1) / cRows;
2301         }
2302     }
2303     else
2304     {
2305         cRows = info.chwnd;
2306         cColumns = 1;
2307         for (;;)
2308         {
2309             cxCell = cxWork / cColumns;
2310             cyCell = cyWork / cRows;
2311             if (cxCell <= cxMin2 || cyCell >= cyMin3)
2312                 break;
2313 
2314             ++cColumns;
2315             cRows = (info.chwnd + cColumns - 1) / cColumns;
2316         }
2317     }
2318 
2319     hDWP = BeginDeferWindowPos(info.chwnd);
2320     if (hDWP == NULL)
2321         goto cleanup;
2322 
2323     x = rcWork.left;
2324     y = rcWork.top;
2325     hwndPrev = NULL;
2326     iRow = iColumn = 0;
2327     for (i = info.chwnd; i > 0;)    /* in reverse order */
2328     {
2329         --i;
2330         hwnd = info.ahwnd[i];
2331 
2332         if (IsZoomed(hwnd))
2333             ShowWindow(hwnd, SW_RESTORE | SW_SHOWNA);
2334 
2335         GetWindowRect(hwnd, &rcWnd);
2336         cx = rcWnd.right - rcWnd.left;
2337         cy = rcWnd.bottom - rcWnd.top;
2338 
2339         /* if we can change the window size */
2340         if (GetWindowLongPtrW(hwnd, GWL_STYLE) & WS_THICKFRAME)
2341         {
2342             cxNew = cxCell;
2343             cyNew = cyCell;
2344             /* shrink if we can */
2345             if (QuerySizeFix(hwnd, &cxNew, &cyNew))
2346             {
2347                 cx = cxNew;
2348                 cy = cyNew;
2349             }
2350         }
2351 
2352         hDWP = DeferWindowPos(hDWP, hwnd, HWND_TOP, x, y, cx, cy, SWP_NOACTIVATE);
2353         if (hDWP == NULL)
2354         {
2355             ret = 0;
2356             goto cleanup;
2357         }
2358 
2359         if (info.wFlags & MDITILE_HORIZONTAL)
2360         {
2361             x += cxCell;
2362             ++iColumn;
2363             if (iColumn >= cColumns)
2364             {
2365                 iColumn = 0;
2366                 ++iRow;
2367                 x = rcWork.left;
2368                 y += cyCell;
2369             }
2370         }
2371         else
2372         {
2373             y += cyCell;
2374             ++iRow;
2375             if (iRow >= cRows)
2376             {
2377                 iRow = 0;
2378                 ++iColumn;
2379                 x += cxCell;
2380                 y = rcWork.top;
2381             }
2382         }
2383         hwndPrev = hwnd;
2384         ++ret;
2385     }
2386 
2387     EndDeferWindowPos(hDWP);
2388 
2389     if (hwndPrev)
2390         SetForegroundWindow(hwndPrev);
2391 
2392 cleanup:
2393     if (cKids == 0 || lpKids == NULL)
2394         HeapFree(GetProcessHeap(), 0, info.ahwnd);
2395 
2396     return (WORD)ret;
2397 }
2398 
2399 
2400 /***********************************************************************
2401  *		TileChildWindows (USER32.@)
2402  */
2403 WORD WINAPI TileChildWindows( HWND parent, UINT flags )
2404 {
2405     return TileWindows( parent, flags, NULL, 0, NULL );
2406 }
2407 
2408 
2409 /************************************************************************
2410  *              "More Windows..." functionality
2411  */
2412 
2413 /*              MDI_MoreWindowsDlgProc
2414  *
2415  *    This function will process the messages sent to the "More Windows..."
2416  *    dialog.
2417  *    Return values:  0    = cancel pressed
2418  *                    HWND = ok pressed or double-click in the list...
2419  *
2420  */
2421 
2422 static INT_PTR WINAPI MDI_MoreWindowsDlgProc (HWND hDlg, UINT iMsg, WPARAM wParam, LPARAM lParam)
2423 {
2424     switch (iMsg)
2425     {
2426        case WM_INITDIALOG:
2427        {
2428            UINT widest       = 0;
2429            UINT length;
2430            UINT i;
2431            MDICLIENTINFO *ci = get_client_info( (HWND)lParam );
2432            HWND hListBox = GetDlgItem(hDlg, MDI_IDC_LISTBOX);
2433 
2434            for (i = 0; i < ci->nActiveChildren; i++)
2435            {
2436                WCHAR buffer[MDI_MAXTITLELENGTH];
2437 
2438                if (!InternalGetWindowText( ci->child[i], buffer, sizeof(buffer)/sizeof(WCHAR) ))
2439                    continue;
2440                SendMessageW(hListBox, LB_ADDSTRING, 0, (LPARAM)buffer );
2441                SendMessageW(hListBox, LB_SETITEMDATA, i, (LPARAM)ci->child[i] );
2442                length = strlenW(buffer);  /* FIXME: should use GetTextExtentPoint */
2443                if (length > widest)
2444                    widest = length;
2445            }
2446            /* Make sure the horizontal scrollbar scrolls ok */
2447            SendMessageW(hListBox, LB_SETHORIZONTALEXTENT, widest * 6, 0);
2448 
2449            /* Set the current selection */
2450            SendMessageW(hListBox, LB_SETCURSEL, MDI_MOREWINDOWSLIMIT, 0);
2451            return TRUE;
2452        }
2453 
2454        case WM_COMMAND:
2455            switch (LOWORD(wParam))
2456            {
2457                 default:
2458                     if (HIWORD(wParam) != LBN_DBLCLK) break;
2459                     /* fall through */
2460                 case IDOK:
2461                 {
2462                     /*  windows are sorted by menu ID, so we must return the
2463                      *  window associated to the given id
2464                      */
2465                     HWND hListBox     = GetDlgItem(hDlg, MDI_IDC_LISTBOX);
2466                     UINT index        = SendMessageW(hListBox, LB_GETCURSEL, 0, 0);
2467                     LRESULT res = SendMessageW(hListBox, LB_GETITEMDATA, index, 0);
2468                     EndDialog(hDlg, res);
2469                     return TRUE;
2470                 }
2471                 case IDCANCEL:
2472                     EndDialog(hDlg, 0);
2473                     return TRUE;
2474            }
2475            break;
2476     }
2477     return FALSE;
2478 }
2479 
2480 /*
2481  *
2482  *                      MDI_MoreWindowsDialog
2483  *
2484  *     Prompts the user with a listbox containing the opened
2485  *     documents. The user can then choose a windows and click
2486  *     on OK to set the current window to the one selected, or
2487  *     CANCEL to cancel. The function returns a handle to the
2488  *     selected window.
2489  */
2490 
2491 static HWND MDI_MoreWindowsDialog(HWND hwnd)
2492 {
2493     LPCVOID template;
2494     HRSRC hRes;
2495     HANDLE hDlgTmpl;
2496 
2497     hRes = FindResourceA(User32Instance, "MDI_MOREWINDOWS", (LPSTR)RT_DIALOG);
2498 
2499     if (hRes == 0)
2500         return 0;
2501 
2502     hDlgTmpl = LoadResource(User32Instance, hRes );
2503 
2504     if (hDlgTmpl == 0)
2505         return 0;
2506 
2507     template = LockResource( hDlgTmpl );
2508 
2509     if (template == 0)
2510         return 0;
2511 
2512     return (HWND) DialogBoxIndirectParamA(User32Instance, template, hwnd,
2513                                           MDI_MoreWindowsDlgProc, (LPARAM) hwnd);
2514 }
2515