xref: /reactos/win32ss/user/user32/windows/mdi.c (revision 0cfd7bde)
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 
WIN_ListChildren(HWND hWndparent)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, dwCount, 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, dwCount, 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  */
MDI_GetChildByID(HWND hwnd,UINT id,MDICLIENTINFO * ci)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 
MDI_PostUpdate(HWND hwnd,MDICLIENTINFO * ci,WORD recalc)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 
get_client_info(HWND client)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 
is_close_enabled(HWND hwnd,HMENU hSysMenu)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  */
MDI_GetWindow(MDICLIENTINFO * clientInfo,HWND hWnd,BOOL bNext,DWORD dwStyleMask)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  */
MDI_CalcDefaultChildPos(HWND hwndClient,INT total,LPPOINT lpPos,INT delta,UINT * id)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  */
MDISetMenu(HWND hwnd,HMENU hmenuFrame,HMENU hmenuWindow)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  */
MDI_RefreshMenu(MDICLIENTINFO * ci)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  */
MDI_ChildGetMinMaxInfo(HWND client,HWND hwnd,MINMAXINFO * lpMinMax)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  */
MDI_SwitchActiveChild(MDICLIENTINFO * ci,HWND hwndTo,BOOL activate)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  */
MDIDestroyChild(HWND client,MDICLIENTINFO * ci,HWND child,BOOL flagDestroy)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  */
MDI_ChildActivate(HWND client,HWND child)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  */
CreateMDIMenuBitmap(void)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  */
MDICascade(HWND client,MDICLIENTINFO * ci)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  */
MDITile(HWND client,MDICLIENTINFO * ci,WPARAM wParam)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  */
MDI_AugmentFrameMenu(HWND frame,HWND hChild)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       HDC hdc = GetDC(hChild);
923 
924       if (hdc)
925       {
926         int cx, cy;
927         cx = GetSystemMetrics(SM_CXSMICON);
928         cy = GetSystemMetrics(SM_CYSMICON);
929         hMemDC = CreateCompatibleDC(hdc);
930         hBitmap = CreateCompatibleBitmap(hdc, cx, cy);
931         hOldBitmap = SelectObject(hMemDC, hBitmap);
932         SetMapMode(hMemDC, MM_TEXT);
933         DrawIconEx(hMemDC, 0, 0, hIcon, cx, cy, 0, GetSysColorBrush(COLOR_MENU), DI_NORMAL);
934         SelectObject (hMemDC, hOldBitmap);
935         DeleteDC(hMemDC);
936         ReleaseDC(hChild, hdc);
937         hSysMenuBitmap = hBitmap;
938       }
939     }
940 
941     if( !InsertMenuA(menu,0,MF_BYPOSITION | MF_BITMAP | MF_POPUP,
942                      (UINT_PTR)hSysPopup, (LPSTR)hSysMenuBitmap))
943     {
944         TRACE("not inserted\n");
945 	DestroyMenu(hSysPopup);
946         return FALSE;
947     }
948 
949     EnableMenuItem(hSysPopup, SC_SIZE, MF_BYCOMMAND | MF_GRAYED);
950     EnableMenuItem(hSysPopup, SC_MOVE, MF_BYCOMMAND | MF_GRAYED);
951     EnableMenuItem(hSysPopup, SC_MAXIMIZE, MF_BYCOMMAND | MF_GRAYED);
952     SetMenuDefaultItem(hSysPopup, SC_CLOSE, FALSE);
953 
954     /* redraw menu */
955     DrawMenuBar(frame);
956 
957     return TRUE;
958 }
959 
960 /**********************************************************************
961  *					MDI_RestoreFrameMenu
962  */
MDI_RestoreFrameMenu(HWND frame,HWND hChild,HBITMAP hBmpClose)963 static BOOL MDI_RestoreFrameMenu( HWND frame, HWND hChild, HBITMAP hBmpClose )
964 {
965     MENUITEMINFOW menuInfo;
966     HMENU menu = GetMenu( frame );
967     INT nItems;
968     UINT iId;
969 
970     TRACE("frame %p,child %p\n",frame, hChild);
971 
972     if (!menu) return FALSE;
973 
974     /* if there is no system buttons then nothing to do */
975     nItems = GetMenuItemCount(menu) - 1;
976     iId = GetMenuItemID(menu, nItems);
977     if ( !(iId == SC_RESTORE || iId == SC_CLOSE) )
978     {
979         ERR("no system buttons then nothing to do\n");
980         return FALSE;
981     }
982 
983     /*
984      * Remove the system menu, If that menu is the icon of the window
985      * as it is in win95, we have to delete the bitmap.
986      */
987     memset(&menuInfo, 0, sizeof(menuInfo));
988     menuInfo.cbSize = sizeof(menuInfo);
989     menuInfo.fMask  = MIIM_DATA | MIIM_TYPE | MIIM_BITMAP;
990 
991     GetMenuItemInfoW(menu,
992 		     0,
993 		     TRUE,
994 		     &menuInfo);
995 
996     RemoveMenu(menu,0,MF_BYPOSITION);
997 
998     if ( (menuInfo.fType & MFT_BITMAP) &&
999 	 (menuInfo.dwTypeData != 0) &&
1000 	 (menuInfo.dwTypeData != (LPWSTR)hBmpClose) )
1001     {
1002         DeleteObject(menuInfo.dwTypeData);
1003     }
1004 
1005     if ( menuInfo.hbmpItem != 0 )
1006          DeleteObject(menuInfo.hbmpItem);
1007 
1008     /* close */
1009     DeleteMenu(menu, SC_CLOSE, MF_BYCOMMAND);
1010     /* restore */
1011     DeleteMenu(menu, SC_RESTORE, MF_BYCOMMAND);
1012     /* minimize */
1013     DeleteMenu(menu, SC_MINIMIZE, MF_BYCOMMAND);
1014 
1015     DrawMenuBar(frame);
1016 
1017     return TRUE;
1018 }
1019 
1020 
1021 /**********************************************************************
1022  *				        MDI_UpdateFrameText
1023  *
1024  * used when child window is maximized/restored
1025  *
1026  * Note: lpTitle can be NULL
1027  */
MDI_UpdateFrameText(HWND frame,HWND hClient,BOOL repaint,LPCWSTR lpTitle)1028 static void MDI_UpdateFrameText( HWND frame, HWND hClient, BOOL repaint, LPCWSTR lpTitle )
1029 {
1030     WCHAR   lpBuffer[MDI_MAXTITLELENGTH+1];
1031     MDICLIENTINFO *ci = get_client_info( hClient );
1032 
1033     TRACE("frameText %s\n", debugstr_w(lpTitle));
1034 
1035     if (!ci) return;
1036 
1037     if (!lpTitle && !ci->frameTitle)  /* first time around, get title from the frame window */
1038     {
1039         GetWindowTextW( frame, lpBuffer, sizeof(lpBuffer)/sizeof(WCHAR) );
1040         lpTitle = lpBuffer;
1041     }
1042 
1043     /* store new "default" title if lpTitle is not NULL */
1044     if (lpTitle)
1045     {
1046 	HeapFree( GetProcessHeap(), 0, ci->frameTitle );
1047 	if ((ci->frameTitle = HeapAlloc( GetProcessHeap(), 0, (strlenW(lpTitle)+1)*sizeof(WCHAR))))
1048             strcpyW( ci->frameTitle, lpTitle );
1049     }
1050 
1051     if (ci->frameTitle)
1052     {
1053 	if (ci->hwndChildMaximized)
1054 	{
1055 	    /* combine frame title and child title if possible */
1056 
1057 	    static const WCHAR lpBracket[]  = {' ','-',' ','[',0};
1058 	    static const WCHAR lpBracket2[]  = {']',0};
1059 	    int	i_frame_text_length = strlenW(ci->frameTitle);
1060 
1061 	    lstrcpynW( lpBuffer, ci->frameTitle, MDI_MAXTITLELENGTH);
1062 
1063 	    if( i_frame_text_length + 6 < MDI_MAXTITLELENGTH )
1064             {
1065 		strcatW( lpBuffer, lpBracket );
1066                 if (GetWindowTextW( ci->hwndActiveChild, lpBuffer + i_frame_text_length + 4,
1067                                     MDI_MAXTITLELENGTH - i_frame_text_length - 5 ))
1068                     strcatW( lpBuffer, lpBracket2 );
1069                 else
1070                     lpBuffer[i_frame_text_length] = 0;  /* remove bracket */
1071             }
1072 	}
1073 	else
1074 	{
1075             lstrcpynW(lpBuffer, ci->frameTitle, MDI_MAXTITLELENGTH+1 );
1076 	}
1077     }
1078     else
1079 	lpBuffer[0] = '\0';
1080 
1081     DefWindowProcW( frame, WM_SETTEXT, 0, (LPARAM)lpBuffer );
1082 
1083     if (repaint)
1084     {
1085        if (!NtUserCallTwoParam((DWORD_PTR)frame,DC_ACTIVE,TWOPARAM_ROUTINE_REDRAWTITLE))
1086         SetWindowPos( frame, 0,0,0,0,0, SWP_FRAMECHANGED |
1087                       SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER );
1088     }
1089 }
1090 
1091 
1092 /* ----------------------------- Interface ---------------------------- */
1093 
1094 
1095 /**********************************************************************
1096  *		MDIClientWndProc_common
1097  */
MDIClientWndProc_common(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam,BOOL unicode)1098 LRESULT WINAPI MDIClientWndProc_common( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam, BOOL unicode )
1099 {
1100     MDICLIENTINFO *ci = NULL;
1101 
1102     TRACE("%p %04x (%s) %08lx %08lx\n", hwnd, message, SPY_GetMsgName(message, hwnd), wParam, lParam);
1103 
1104     if (!(ci = get_client_info( hwnd )))
1105     {
1106 #ifdef __REACTOS__
1107         if (message == WM_NCCREATE)
1108         {
1109           if (!(ci = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*ci))))
1110              return FALSE;
1111            SetWindowLongPtrW( hwnd, GWLP_MDIWND, (LONG_PTR)ci );
1112            ci->hBmpClose = 0;
1113            NtUserSetWindowFNID( hwnd, FNID_MDICLIENT); // wine uses WIN_ISMDICLIENT
1114         }
1115 #else
1116         if (message == WM_NCCREATE) win_set_flags( hwnd, WIN_ISMDICLIENT, 0 );
1117 #endif
1118         return unicode ? DefWindowProcW( hwnd, message, wParam, lParam ) :
1119                          DefWindowProcA( hwnd, message, wParam, lParam );
1120     }
1121 
1122     switch (message)
1123     {
1124       case WM_CREATE:
1125       {
1126           /* Since we are using only cs->lpCreateParams, we can safely
1127            * cast to LPCREATESTRUCTA here */
1128         LPCREATESTRUCTA cs = (LPCREATESTRUCTA)lParam;
1129         LPCLIENTCREATESTRUCT ccs = (LPCLIENTCREATESTRUCT)cs->lpCreateParams;
1130 
1131         ci->hWindowMenu		= ccs->hWindowMenu;
1132         ci->idFirstChild	= ccs->idFirstChild;
1133         ci->hwndChildMaximized  = 0;
1134         ci->child = NULL;
1135 	ci->nActiveChildren	= 0;
1136 	ci->nTotalCreated	= 0;
1137 	ci->frameTitle		= NULL;
1138 	ci->mdiFlags		= 0;
1139         ci->hFrameMenu = GetMenu(cs->hwndParent);
1140 
1141 	if (!ci->hBmpClose) ci->hBmpClose = CreateMDIMenuBitmap();
1142 
1143         TRACE("Client created: hwnd %p, Window menu %p, idFirst = %04x\n",
1144               hwnd, ci->hWindowMenu, ci->idFirstChild );
1145         return 0;
1146       }
1147 
1148       case WM_DESTROY:
1149       {
1150           if( ci->hwndChildMaximized )
1151               MDI_RestoreFrameMenu(GetParent(hwnd), ci->hwndChildMaximized, ci->hBmpClose);
1152 
1153           ci->nActiveChildren = 0;
1154           MDI_RefreshMenu(ci);
1155 
1156           HeapFree( GetProcessHeap(), 0, ci->child );
1157           HeapFree( GetProcessHeap(), 0, ci->frameTitle );
1158 #ifdef __REACTOS__
1159           HeapFree( GetProcessHeap(), 0, ci );
1160           SetWindowLongPtrW( hwnd, GWLP_MDIWND, 0 );
1161 #endif
1162           return 0;
1163       }
1164 
1165 #ifdef __REACTOS__
1166       case WM_NCDESTROY:
1167       {
1168           NtUserSetWindowFNID(hwnd, FNID_DESTROY);
1169           return 0;
1170       }
1171 #endif
1172 
1173       case WM_MDIACTIVATE:
1174       {
1175         if( ci->hwndActiveChild != (HWND)wParam )
1176 	    SetWindowPos((HWND)wParam, 0,0,0,0,0, SWP_NOSIZE | SWP_NOMOVE);
1177         return 0;
1178       }
1179 
1180       case WM_MDICASCADE:
1181         return MDICascade(hwnd, ci);
1182 
1183       case WM_MDICREATE:
1184         if (lParam)
1185         {
1186             HWND child;
1187 
1188             if (unicode)
1189             {
1190                 MDICREATESTRUCTW *csW = (MDICREATESTRUCTW *)lParam;
1191                 child = CreateWindowExW(WS_EX_MDICHILD, csW->szClass,
1192                                             csW->szTitle, csW->style,
1193                                             csW->x, csW->y, csW->cx, csW->cy,
1194                                             hwnd, 0, csW->hOwner,
1195                                             (LPVOID)csW->lParam);
1196             }
1197             else
1198             {
1199                 MDICREATESTRUCTA *csA = (MDICREATESTRUCTA *)lParam;
1200                 child = CreateWindowExA(WS_EX_MDICHILD, csA->szClass,
1201                                             csA->szTitle, csA->style,
1202                                             csA->x, csA->y, csA->cx, csA->cy,
1203                                             hwnd, 0, csA->hOwner,
1204                                             (LPVOID)csA->lParam);
1205             }
1206             return (LRESULT)child;
1207         }
1208         return 0;
1209 
1210       case WM_MDIDESTROY:
1211           return MDIDestroyChild( hwnd, ci, (HWND)wParam, TRUE );
1212 
1213       case WM_MDIGETACTIVE:
1214           if (lParam) *(BOOL *)lParam = IsZoomed(ci->hwndActiveChild);
1215           return (LRESULT)ci->hwndActiveChild;
1216 
1217       case WM_MDIICONARRANGE:
1218 	ci->mdiFlags |= MDIF_NEEDUPDATE;
1219         ArrangeIconicWindows( hwnd );
1220 	ci->sbRecalc = SB_BOTH+1;
1221 #ifdef __REACTOS__
1222 	PostMessageA( hwnd, WM_MDICALCCHILDSCROLL, 0, 0 ); //// ReactOS: Post not send!
1223 #else
1224         SendMessageW( hwnd, WM_MDICALCCHILDSCROLL, 0, 0 );
1225 #endif
1226         return 0;
1227 
1228       case WM_MDIMAXIMIZE:
1229 	ShowWindow( (HWND)wParam, SW_MAXIMIZE );
1230         return 0;
1231 
1232       case WM_MDINEXT: /* lParam != 0 means previous window */
1233       {
1234         HWND hwnd = wParam ? WIN_GetFullHandle((HWND)wParam) : ci->hwndActiveChild;
1235         HWND next = MDI_GetWindow( ci, hwnd, !lParam, 0 );
1236         MDI_SwitchActiveChild( ci, next, TRUE );
1237         if(!lParam)
1238             SetWindowPos(hwnd, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE);
1239 	break;
1240       }
1241 
1242       case WM_MDIRESTORE:
1243         ShowWindow( (HWND)wParam, SW_SHOWNORMAL );
1244         return 0;
1245 
1246       case WM_MDISETMENU:
1247           return MDISetMenu( hwnd, (HMENU)wParam, (HMENU)lParam );
1248 
1249       case WM_MDIREFRESHMENU:
1250           return MDI_RefreshMenu( ci );
1251 
1252       case WM_MDITILE:
1253 	ci->mdiFlags |= MDIF_NEEDUPDATE;
1254         ShowScrollBar( hwnd, SB_BOTH, FALSE );
1255         MDITile( hwnd, ci, wParam );
1256         ci->mdiFlags &= ~MDIF_NEEDUPDATE;
1257         return 0;
1258 
1259       case WM_VSCROLL:
1260       case WM_HSCROLL:
1261 	ci->mdiFlags |= MDIF_NEEDUPDATE;
1262         ScrollChildren( hwnd, message, wParam, lParam );
1263 	ci->mdiFlags &= ~MDIF_NEEDUPDATE;
1264         return 0;
1265 
1266       case WM_SETFOCUS:
1267           if (ci->hwndActiveChild && !IsIconic( ci->hwndActiveChild ))
1268               SetFocus( ci->hwndActiveChild );
1269           return 0;
1270 
1271       case WM_NCACTIVATE:
1272         if( ci->hwndActiveChild )
1273             SendMessageW(ci->hwndActiveChild, message, wParam, lParam);
1274 	break;
1275 
1276       case WM_PARENTNOTIFY:
1277         switch (LOWORD(wParam))
1278         {
1279         case WM_CREATE:
1280             if (GetWindowLongPtrW((HWND)lParam, GWL_EXSTYLE) & WS_EX_MDICHILD)
1281             {
1282                 // ReactOS See rev 33503
1283                 if (!ci->child)
1284                     ci->child = HeapAlloc(GetProcessHeap(), 0, sizeof(HWND));
1285                 else
1286                     ci->child = HeapReAlloc(GetProcessHeap(), 0, ci->child, sizeof(HWND) * (ci->nActiveChildren + 1));
1287 
1288                 TRACE("Adding MDI child %p, # of children %d\n",
1289                       (HWND)lParam, ci->nActiveChildren);
1290 
1291                 if (ci->child != NULL)
1292                 {
1293                     ci->child[ci->nActiveChildren] = (HWND)lParam;
1294                     ci->nTotalCreated++;
1295                     ci->nActiveChildren++;
1296                 }
1297             }
1298             break;
1299 
1300         case WM_LBUTTONDOWN:
1301             {
1302             HWND child;
1303             POINT pt;
1304             pt.x = (short)LOWORD(lParam);
1305             pt.y = (short)HIWORD(lParam);
1306             child = ChildWindowFromPoint(hwnd, pt);
1307 
1308 	    TRACE("notification from %p (%li,%li)\n",child,pt.x,pt.y);
1309 
1310             if( child && child != hwnd && child != ci->hwndActiveChild )
1311                 SetWindowPos(child, 0,0,0,0,0, SWP_NOSIZE | SWP_NOMOVE );
1312             break;
1313             }
1314 
1315         case WM_DESTROY:
1316             return MDIDestroyChild( hwnd, ci, WIN_GetFullHandle( (HWND)lParam ), FALSE );
1317         }
1318         return 0;
1319 
1320       case WM_SIZE:
1321         if( ci->hwndActiveChild && IsZoomed(ci->hwndActiveChild) )
1322 	{
1323 	    RECT	rect;
1324 
1325 	    SetRect(&rect, 0, 0, LOWORD(lParam), HIWORD(lParam));
1326 	    AdjustWindowRectEx(&rect, GetWindowLongPtrA(ci->hwndActiveChild, GWL_STYLE),
1327                                0, GetWindowLongPtrA(ci->hwndActiveChild, GWL_EXSTYLE) );
1328 	    MoveWindow(ci->hwndActiveChild, rect.left, rect.top,
1329 			 rect.right - rect.left, rect.bottom - rect.top, 1);
1330 	}
1331 	else
1332             MDI_PostUpdate(hwnd, ci, SB_BOTH+1);
1333 
1334 	break;
1335 
1336       case WM_MDICALCCHILDSCROLL:
1337 	if( (ci->mdiFlags & MDIF_NEEDUPDATE) && ci->sbRecalc )
1338 	{
1339             CalcChildScroll(hwnd, ci->sbRecalc-1);
1340 	    ci->sbRecalc = 0;
1341 	    ci->mdiFlags &= ~MDIF_NEEDUPDATE;
1342 	}
1343         return 0;
1344     }
1345     return unicode ? DefWindowProcW( hwnd, message, wParam, lParam ) :
1346                      DefWindowProcA( hwnd, message, wParam, lParam );
1347 }
1348 
1349 /***********************************************************************
1350  *		MDIClientWndProcA
1351  */
MDIClientWndProcA(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam)1352 LRESULT WINAPI MDIClientWndProcA( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
1353 {
1354     if (!IsWindow(hwnd)) return 0;
1355     return MDIClientWndProc_common( hwnd, message, wParam, lParam, FALSE );
1356 }
1357 
1358 /***********************************************************************
1359  *		MDIClientWndProcW
1360  */
MDIClientWndProcW(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam)1361 LRESULT WINAPI MDIClientWndProcW( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
1362 {
1363     if (!IsWindow(hwnd)) return 0;
1364     return MDIClientWndProc_common( hwnd, message, wParam, lParam, TRUE );
1365 }
1366 
1367 /***********************************************************************
1368  *		DefFrameProcA (USER32.@)
1369  */
DefFrameProcA(HWND hwnd,HWND hwndMDIClient,UINT message,WPARAM wParam,LPARAM lParam)1370 LRESULT WINAPI DefFrameProcA( HWND hwnd, HWND hwndMDIClient,
1371                                 UINT message, WPARAM wParam, LPARAM lParam)
1372 {
1373     if (hwndMDIClient)
1374     {
1375 	switch (message)
1376 	{
1377         case WM_SETTEXT:
1378             {
1379                 DWORD len = MultiByteToWideChar( CP_ACP, 0, (LPSTR)lParam, -1, NULL, 0 );
1380                 LPWSTR text = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
1381                 if (text == NULL)
1382                     return 0;
1383                 MultiByteToWideChar( CP_ACP, 0, (LPSTR)lParam, -1, text, len );
1384                 MDI_UpdateFrameText( hwnd, hwndMDIClient, FALSE, text );
1385                 HeapFree( GetProcessHeap(), 0, text );
1386             }
1387             return 1; /* success. FIXME: check text length */
1388 
1389         case WM_COMMAND:
1390         case WM_NCACTIVATE:
1391         case WM_NEXTMENU:
1392         case WM_SETFOCUS:
1393         case WM_SIZE:
1394             return DefFrameProcW( hwnd, hwndMDIClient, message, wParam, lParam );
1395         }
1396     }
1397     return DefWindowProcA(hwnd, message, wParam, lParam);
1398 }
1399 
1400 
1401 /***********************************************************************
1402  *		DefFrameProcW (USER32.@)
1403  */
DefFrameProcW(HWND hwnd,HWND hwndMDIClient,UINT message,WPARAM wParam,LPARAM lParam)1404 LRESULT WINAPI DefFrameProcW( HWND hwnd, HWND hwndMDIClient,
1405                                 UINT message, WPARAM wParam, LPARAM lParam)
1406 {
1407     MDICLIENTINFO *ci = get_client_info( hwndMDIClient );
1408 
1409     TRACE("%p %p %04x (%s) %08lx %08lx\n", hwnd, hwndMDIClient, message, SPY_GetMsgName(message, hwnd), wParam, lParam);
1410 
1411     if (ci)
1412     {
1413 	switch (message)
1414 	{
1415         case WM_COMMAND:
1416             {
1417                 WORD id = LOWORD(wParam);
1418                 /* check for possible syscommands for maximized MDI child */
1419                 if (id <  ci->idFirstChild || id >= ci->idFirstChild + ci->nActiveChildren)
1420                 {
1421                     if( (id - 0xf000) & 0xf00f ) break;
1422                     if( !ci->hwndChildMaximized ) break;
1423                     switch( id )
1424                     {
1425                     case SC_CLOSE:
1426                         if (!is_close_enabled(ci->hwndActiveChild, 0)) break;
1427                     case SC_SIZE:
1428                     case SC_MOVE:
1429                     case SC_MINIMIZE:
1430                     case SC_MAXIMIZE:
1431                     case SC_NEXTWINDOW:
1432                     case SC_PREVWINDOW:
1433                     case SC_RESTORE:
1434                         return SendMessageW( ci->hwndChildMaximized, WM_SYSCOMMAND,
1435                                              wParam, lParam);
1436                     }
1437                 }
1438                 else
1439                 {
1440                     HWND childHwnd;
1441                     if (id - ci->idFirstChild == MDI_MOREWINDOWSLIMIT)
1442                         /* User chose "More Windows..." */
1443                         childHwnd = MDI_MoreWindowsDialog(hwndMDIClient);
1444                     else
1445                         /* User chose one of the windows listed in the "Windows" menu */
1446                         childHwnd = MDI_GetChildByID(hwndMDIClient, id, ci);
1447 
1448                     if( childHwnd )
1449                         SendMessageW( hwndMDIClient, WM_MDIACTIVATE, (WPARAM)childHwnd, 0 );
1450                 }
1451             }
1452             break;
1453 
1454         case WM_NCACTIVATE:
1455 	    SendMessageW(hwndMDIClient, message, wParam, lParam);
1456 	    break;
1457 
1458         case WM_SETTEXT:
1459             MDI_UpdateFrameText( hwnd, hwndMDIClient, FALSE, (LPWSTR)lParam );
1460 	    return 1; /* success. FIXME: check text length */
1461 
1462         case WM_SETFOCUS:
1463 	    SetFocus(hwndMDIClient);
1464 	    break;
1465 
1466         case WM_SIZE:
1467             MoveWindow(hwndMDIClient, 0, 0, LOWORD(lParam), HIWORD(lParam), TRUE);
1468             break;
1469 
1470         case WM_NEXTMENU:
1471             {
1472                 MDINEXTMENU *next_menu = (MDINEXTMENU *)lParam;
1473 
1474                 if (!IsIconic(hwnd) && ci->hwndActiveChild && !IsZoomed(ci->hwndActiveChild))
1475                 {
1476                     /* control menu is between the frame system menu and
1477                      * the first entry of menu bar */
1478 //                    WND *wndPtr = WIN_GetPtr(hwnd);
1479 
1480                     if( (wParam == VK_LEFT && GetMenu(hwnd) == next_menu->hmenuIn) ||
1481                         (wParam == VK_RIGHT && GetSubMenu(GetMenu(hwnd), 0) == next_menu->hmenuIn) )
1482                     {
1483 //                        WIN_ReleasePtr(wndPtr);
1484 //                        wndPtr = WIN_GetPtr(ci->hwndActiveChild);
1485                         next_menu->hmenuNext = GetSubMenu(GetMenu(ci->hwndActiveChild), 0);
1486                         next_menu->hwndNext = ci->hwndActiveChild;
1487                     }
1488 //                    WIN_ReleasePtr(wndPtr);
1489                 }
1490                 return 0;
1491             }
1492 	}
1493     }
1494 
1495     return DefWindowProcW( hwnd, message, wParam, lParam );
1496 }
1497 
1498 /***********************************************************************
1499  *		DefMDIChildProcA (USER32.@)
1500  */
DefMDIChildProcA(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam)1501 LRESULT WINAPI DefMDIChildProcA( HWND hwnd, UINT message,
1502                                    WPARAM wParam, LPARAM lParam )
1503 {
1504     HWND client = GetParent(hwnd);
1505     MDICLIENTINFO *ci = get_client_info( client );
1506 
1507     TRACE("%p %04x (%s) %08lx %08lx\n", hwnd, message, SPY_GetMsgName(message, hwnd), wParam, lParam);
1508 
1509     hwnd = WIN_GetFullHandle( hwnd );
1510     if (!ci) return DefWindowProcA( hwnd, message, wParam, lParam );
1511 
1512     switch (message)
1513     {
1514     case WM_SETTEXT:
1515 	DefWindowProcA(hwnd, message, wParam, lParam);
1516 	if( ci->hwndChildMaximized == hwnd )
1517 	    MDI_UpdateFrameText( GetParent(client), client, TRUE, NULL );
1518         MDI_RefreshMenu( ci );
1519         return 1; /* success. FIXME: check text length */
1520 
1521     case WM_GETMINMAXINFO:
1522     case WM_MENUCHAR:
1523     case WM_CLOSE:
1524     case WM_SETFOCUS:
1525     case WM_CHILDACTIVATE:
1526     case WM_SYSCOMMAND:
1527     case WM_SHOWWINDOW:
1528     case WM_SETVISIBLE:
1529     case WM_SIZE:
1530     case WM_NEXTMENU:
1531     case WM_SYSCHAR:
1532     case WM_DESTROY:
1533         return DefMDIChildProcW( hwnd, message, wParam, lParam );
1534     }
1535     return DefWindowProcA(hwnd, message, wParam, lParam);
1536 }
1537 
1538 
1539 /***********************************************************************
1540  *		DefMDIChildProcW (USER32.@)
1541  */
DefMDIChildProcW(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam)1542 LRESULT WINAPI DefMDIChildProcW( HWND hwnd, UINT message,
1543                                    WPARAM wParam, LPARAM lParam )
1544 {
1545     HWND client = GetParent(hwnd);
1546     MDICLIENTINFO *ci = get_client_info( client );
1547 
1548     TRACE("%p %04x (%s) %08lx %08lx\n", hwnd, message, SPY_GetMsgName(message, hwnd), wParam, lParam);
1549 
1550     hwnd = WIN_GetFullHandle( hwnd );
1551     if (!ci) return DefWindowProcW( hwnd, message, wParam, lParam );
1552 
1553     switch (message)
1554     {
1555     case WM_SETTEXT:
1556         DefWindowProcW(hwnd, message, wParam, lParam);
1557         if( ci->hwndChildMaximized == hwnd )
1558             MDI_UpdateFrameText( GetParent(client), client, TRUE, NULL );
1559         MDI_RefreshMenu( ci );
1560         return 1; /* success. FIXME: check text length */
1561 
1562     case WM_GETMINMAXINFO:
1563         MDI_ChildGetMinMaxInfo( client, hwnd, (MINMAXINFO *)lParam );
1564         return 0;
1565 
1566     case WM_MENUCHAR:
1567         return MAKELRESULT( 0, MNC_CLOSE ); /* MDI children don't have menu bars */
1568 
1569     case WM_CLOSE:
1570         SendMessageW( client, WM_MDIDESTROY, (WPARAM)hwnd, 0 );
1571         return 0;
1572 
1573     case WM_SETFOCUS:
1574         if (ci->hwndActiveChild != hwnd)
1575             MDI_ChildActivate( client, hwnd );
1576         break;
1577 
1578     case WM_CHILDACTIVATE:
1579         if (IsWindowEnabled( hwnd ))
1580             MDI_ChildActivate( client, hwnd );
1581         return 0;
1582 
1583     case WM_SYSCOMMAND:
1584         switch (wParam & 0xfff0)
1585         {
1586         case SC_MOVE:
1587             if( ci->hwndChildMaximized == hwnd )
1588                 return 0;
1589             break;
1590         case SC_RESTORE:
1591         case SC_MINIMIZE:
1592             break;
1593         case SC_MAXIMIZE:
1594             if (ci->hwndChildMaximized == hwnd)
1595                 return SendMessageW( GetParent(client), message, wParam, lParam);
1596             break;
1597         case SC_NEXTWINDOW:
1598             SendMessageW( client, WM_MDINEXT, (WPARAM)ci->hwndActiveChild, 0);
1599             return 0;
1600         case SC_PREVWINDOW:
1601             SendMessageW( client, WM_MDINEXT, (WPARAM)ci->hwndActiveChild, 1);
1602             return 0;
1603         }
1604         break;
1605 
1606     case WM_SHOWWINDOW:
1607     case WM_SETVISIBLE:
1608         //// Commented out r57663
1609         /*if (ci->hwndChildMaximized) ci->mdiFlags &= ~MDIF_NEEDUPDATE;
1610         else*/ MDI_PostUpdate(client, ci, SB_BOTH+1);
1611         break;
1612 
1613     case WM_SIZE:
1614         /* This is the only place where we switch to/from maximized state */
1615         /* do not change */
1616         TRACE("current active %p, maximized %p\n", ci->hwndActiveChild, ci->hwndChildMaximized);
1617 
1618         if( ci->hwndChildMaximized == hwnd && wParam != SIZE_MAXIMIZED )
1619         {
1620             HWND frame;
1621 
1622             ci->hwndChildMaximized = 0;
1623 
1624             frame = GetParent(client);
1625             MDI_RestoreFrameMenu( frame, hwnd, ci->hBmpClose );
1626             MDI_UpdateFrameText( frame, client, TRUE, NULL );
1627         }
1628 
1629         if( wParam == SIZE_MAXIMIZED )
1630         {
1631             HWND frame, hMaxChild = ci->hwndChildMaximized;
1632 
1633             if( hMaxChild == hwnd ) break;
1634 
1635             if( hMaxChild)
1636             {
1637                 SendMessageW( hMaxChild, WM_SETREDRAW, FALSE, 0 );
1638 
1639                 MDI_RestoreFrameMenu( GetParent(client), hMaxChild, ci->hBmpClose );
1640                 ShowWindow( hMaxChild, SW_SHOWNOACTIVATE );
1641 
1642                 SendMessageW( hMaxChild, WM_SETREDRAW, TRUE, 0 );
1643             }
1644 
1645             TRACE("maximizing child %p\n", hwnd );
1646 
1647             /* keep track of the maximized window. */
1648             ci->hwndChildMaximized = hwnd; /* !!! */
1649 
1650             frame = GetParent(client);
1651             MDI_AugmentFrameMenu( frame, hwnd );
1652             MDI_UpdateFrameText( frame, client, TRUE, NULL );
1653         }
1654 
1655         if( wParam == SIZE_MINIMIZED )
1656         {
1657             HWND switchTo = MDI_GetWindow( ci, hwnd, TRUE, WS_MINIMIZE );
1658 
1659             if (!switchTo) switchTo = hwnd;
1660             SendMessageW( switchTo, WM_CHILDACTIVATE, 0, 0 );
1661 	}
1662 
1663         MDI_PostUpdate(client, ci, SB_BOTH+1);
1664         break;
1665 
1666     case WM_NEXTMENU:
1667         {
1668             MDINEXTMENU *next_menu = (MDINEXTMENU *)lParam;
1669             HWND parent = GetParent(client);
1670 
1671             if( wParam == VK_LEFT )  /* switch to frame system menu */
1672             {
1673 //                WND *wndPtr = WIN_GetPtr( parent );
1674                 next_menu->hmenuNext = GetSubMenu( GetMenu(parent), 0 );
1675 //                WIN_ReleasePtr( wndPtr );
1676             }
1677             if( wParam == VK_RIGHT )  /* to frame menu bar */
1678             {
1679                 next_menu->hmenuNext = GetMenu(parent);
1680             }
1681             next_menu->hwndNext = parent;
1682             return 0;
1683         }
1684 
1685     case WM_SYSCHAR:
1686         if (wParam == '-')
1687         {
1688             SendMessageW( hwnd, WM_SYSCOMMAND, SC_KEYMENU, VK_SPACE);
1689             return 0;
1690         }
1691         break;
1692 
1693     case WM_DESTROY:
1694         /* Remove itself from the Window menu */
1695         MDI_RefreshMenu(ci);
1696         break;
1697     }
1698     return DefWindowProcW(hwnd, message, wParam, lParam);
1699 }
1700 
1701 /**********************************************************************
1702  *		CreateMDIWindowA (USER32.@) Creates a MDI child
1703  *
1704  * RETURNS
1705  *    Success: Handle to created window
1706  *    Failure: NULL
1707  */
CreateMDIWindowA(LPCSTR lpClassName,LPCSTR lpWindowName,DWORD dwStyle,INT X,INT Y,INT nWidth,INT nHeight,HWND hWndParent,HINSTANCE hInstance,LPARAM lParam)1708 HWND WINAPI CreateMDIWindowA(
1709     LPCSTR lpClassName,    /* [in] Pointer to registered child class name */
1710     LPCSTR lpWindowName,   /* [in] Pointer to window name */
1711     DWORD dwStyle,         /* [in] Window style */
1712     INT X,               /* [in] Horizontal position of window */
1713     INT Y,               /* [in] Vertical position of window */
1714     INT nWidth,          /* [in] Width of window */
1715     INT nHeight,         /* [in] Height of window */
1716     HWND hWndParent,     /* [in] Handle to parent window */
1717     HINSTANCE hInstance, /* [in] Handle to application instance */
1718     LPARAM lParam)         /* [in] Application-defined value */
1719 {
1720     TRACE("(%s,%s,%08lx,%d,%d,%d,%d,%p,%p,%08lx)\n",
1721           debugstr_a(lpClassName),debugstr_a(lpWindowName),dwStyle,X,Y,
1722           nWidth,nHeight,hWndParent,hInstance,lParam);
1723 
1724     return CreateWindowExA(WS_EX_MDICHILD, lpClassName, lpWindowName,
1725                            dwStyle, X, Y, nWidth, nHeight, hWndParent,
1726                            0, hInstance, (LPVOID)lParam);
1727 }
1728 
1729 /***********************************************************************
1730  *		CreateMDIWindowW (USER32.@) Creates a MDI child
1731  *
1732  * RETURNS
1733  *    Success: Handle to created window
1734  *    Failure: NULL
1735  */
CreateMDIWindowW(LPCWSTR lpClassName,LPCWSTR lpWindowName,DWORD dwStyle,INT X,INT Y,INT nWidth,INT nHeight,HWND hWndParent,HINSTANCE hInstance,LPARAM lParam)1736 HWND WINAPI CreateMDIWindowW(
1737     LPCWSTR lpClassName,    /* [in] Pointer to registered child class name */
1738     LPCWSTR lpWindowName,   /* [in] Pointer to window name */
1739     DWORD dwStyle,         /* [in] Window style */
1740     INT X,               /* [in] Horizontal position of window */
1741     INT Y,               /* [in] Vertical position of window */
1742     INT nWidth,          /* [in] Width of window */
1743     INT nHeight,         /* [in] Height of window */
1744     HWND hWndParent,     /* [in] Handle to parent window */
1745     HINSTANCE hInstance, /* [in] Handle to application instance */
1746     LPARAM lParam)         /* [in] Application-defined value */
1747 {
1748     TRACE("(%s,%s,%08lx,%d,%d,%d,%d,%p,%p,%08lx)\n",
1749           debugstr_w(lpClassName), debugstr_w(lpWindowName), dwStyle, X, Y,
1750           nWidth, nHeight, hWndParent, hInstance, lParam);
1751 
1752     return CreateWindowExW(WS_EX_MDICHILD, lpClassName, lpWindowName,
1753                            dwStyle, X, Y, nWidth, nHeight, hWndParent,
1754                            0, hInstance, (LPVOID)lParam);
1755 }
1756 
1757 /**********************************************************************
1758  *		TranslateMDISysAccel (USER32.@)
1759  */
TranslateMDISysAccel(HWND hwndClient,LPMSG msg)1760 BOOL WINAPI TranslateMDISysAccel( HWND hwndClient, LPMSG msg )
1761 {
1762     if (msg->message == WM_KEYDOWN || msg->message == WM_SYSKEYDOWN)
1763     {
1764         MDICLIENTINFO *ci = get_client_info( hwndClient );
1765         WPARAM wParam = 0;
1766 
1767         if (!ci || !IsWindowEnabled(ci->hwndActiveChild)) return 0;
1768 
1769         /* translate if the Ctrl key is down and Alt not. */
1770 
1771         if( (GetKeyState(VK_CONTROL) & 0x8000) && !(GetKeyState(VK_MENU) & 0x8000))
1772         {
1773             switch( msg->wParam )
1774             {
1775             case VK_F6:
1776             case VK_TAB:
1777                 wParam = ( GetKeyState(VK_SHIFT) & 0x8000 ) ? SC_NEXTWINDOW : SC_PREVWINDOW;
1778                 break;
1779             case VK_F4:
1780             case VK_RBUTTON:
1781                 if (is_close_enabled(ci->hwndActiveChild, 0))
1782                 {
1783                     wParam = SC_CLOSE;
1784                     break;
1785                 }
1786                 /* fall through */
1787             default:
1788                 return FALSE;
1789             }
1790             TRACE("wParam = %04lx\n", wParam);
1791             SendMessageW(ci->hwndActiveChild, WM_SYSCOMMAND, wParam, msg->wParam);
1792             return TRUE;
1793         }
1794     }
1795     return FALSE; /* failure */
1796 }
1797 
1798 /***********************************************************************
1799  *		CalcChildScroll (USER32.@)
1800  */
CalcChildScroll(HWND hwnd,INT scroll)1801 void WINAPI CalcChildScroll( HWND hwnd, INT scroll )
1802 {
1803     SCROLLINFO info;
1804     RECT childRect, clientRect;
1805     HWND *list;
1806     DWORD style;
1807     WINDOWINFO WindowInfo;
1808 
1809     GetClientRect( hwnd, &clientRect );
1810     SetRectEmpty( &childRect );
1811 
1812    /* The rectangle returned by GetClientRect always has 0,0 as top left
1813     * because it is in client coordinates. The rectangles returned by
1814     * GetWindowRect are in screen coordinates to make this complicated.
1815     *
1816     * Apparently (in ReactOS at least) the rcClient returned by GetWindowInfo
1817     * is in screen coordinates too.
1818     */
1819     WindowInfo.cbSize = sizeof(WindowInfo);
1820     if (!GetWindowInfo(hwnd, &WindowInfo))
1821     {
1822         ERR("Can't get window info\n");
1823         return;
1824     }
1825 
1826     TRACE("CalcChildScroll 1\n");
1827     if ((list = WIN_ListChildren( hwnd )))
1828     {
1829         int i;
1830         for (i = 0; list[i]; i++)
1831         {
1832             style = GetWindowLongPtrW( list[i], GWL_STYLE );
1833             if (style & WS_MAXIMIZE)
1834             {
1835                 HeapFree( GetProcessHeap(), 0, list );
1836                 ShowScrollBar( hwnd, SB_BOTH, FALSE );
1837                 ERR("CalcChildScroll 2\n");
1838                 return;
1839             }
1840             if (style & WS_VISIBLE)
1841             {
1842                 RECT rect;
1843                 GetWindowRect( list[i], &rect );
1844                 OffsetRect(&rect, -WindowInfo.rcClient.left,
1845                                   -WindowInfo.rcClient.top);
1846                 //WIN_GetRectangles( list[i], COORDS_PARENT, &rect, NULL );
1847                 TRACE("CalcChildScroll L\n");
1848                 UnionRect( &childRect, &rect, &childRect );
1849             }
1850         }
1851         HeapFree( GetProcessHeap(), 0, list );
1852     }
1853     UnionRect( &childRect, &clientRect, &childRect );
1854     TRACE("CalcChildScroll 3\n");
1855     /* set common info values */
1856     info.cbSize = sizeof(info);
1857     info.fMask = SIF_POS | SIF_RANGE | SIF_PAGE;
1858     info.nPos = 0;
1859 
1860     /* set the specific values and apply but only if window style allows */
1861     /* Note how we set nPos to 0 because we scroll the clients instead of
1862      * the window, and we set nPage to 1 bigger than the clientRect because
1863      * otherwise the scrollbar never disables. This causes a somewhat ugly
1864      * effect though while scrolling.
1865      */
1866     style = GetWindowLongW( hwnd, GWL_STYLE );
1867     switch( scroll )
1868     {
1869 	case SB_BOTH:
1870 	case SB_HORZ:
1871                         if (style & (WS_HSCROLL | WS_VSCROLL))
1872                         {
1873                             info.nMin = childRect.left;
1874                             info.nMax = childRect.right;
1875                             info.nPage = 1 + clientRect.right - clientRect.left;
1876                             //info.nMax = childRect.right - clientRect.right;
1877                             //info.nPos = clientRect.left - childRect.left;
1878                             SetScrollInfo(hwnd, SB_HORZ, &info, TRUE);
1879                         }
1880 			if (scroll == SB_HORZ) break;
1881 			/* fall through */
1882 	case SB_VERT:
1883                         if (style & (WS_HSCROLL | WS_VSCROLL))
1884                         {
1885                             info.nMin = childRect.top;
1886                             info.nMax = childRect.bottom;
1887                             info.nPage = 1 + clientRect.bottom - clientRect.top;
1888                             //info.nMax = childRect.bottom - clientRect.bottom;
1889                             //info.nPos = clientRect.top - childRect.top;
1890                             SetScrollInfo(hwnd, SB_VERT, &info, TRUE);
1891                         }
1892 			break;
1893     }
1894 }
1895 
1896 
1897 /***********************************************************************
1898  *		ScrollChildren (USER32.@)
1899  */
ScrollChildren(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam)1900 void WINAPI ScrollChildren(HWND hWnd, UINT uMsg, WPARAM wParam,
1901                              LPARAM lParam)
1902 {
1903     INT newPos = -1;
1904     INT curPos, length, minPos, maxPos, shift;
1905     RECT rect;
1906 
1907     GetClientRect( hWnd, &rect );
1908 
1909     switch(uMsg)
1910     {
1911     case WM_HSCROLL:
1912 	GetScrollRange(hWnd,SB_HORZ,&minPos,&maxPos);
1913 	curPos = GetScrollPos(hWnd,SB_HORZ);
1914 	length = (rect.right - rect.left) / 2;
1915 	shift = GetSystemMetrics(SM_CYHSCROLL);
1916         break;
1917     case WM_VSCROLL:
1918 	GetScrollRange(hWnd,SB_VERT,&minPos,&maxPos);
1919 	curPos = GetScrollPos(hWnd,SB_VERT);
1920 	length = (rect.bottom - rect.top) / 2;
1921 	shift = GetSystemMetrics(SM_CXVSCROLL);
1922         break;
1923     default:
1924         return;
1925     }
1926 
1927     switch( wParam )
1928     {
1929 	case SB_LINEUP:
1930 		        newPos = curPos - shift;
1931 			break;
1932 	case SB_LINEDOWN:
1933 			newPos = curPos + shift;
1934 			break;
1935 	case SB_PAGEUP:
1936 			newPos = curPos - length;
1937 			break;
1938 	case SB_PAGEDOWN:
1939 			newPos = curPos + length;
1940 			break;
1941 
1942 	case SB_THUMBPOSITION:
1943 			newPos = LOWORD(lParam);
1944 			break;
1945 
1946 	case SB_THUMBTRACK:
1947 			return;
1948 
1949 	case SB_TOP:
1950 			newPos = minPos;
1951 			break;
1952 	case SB_BOTTOM:
1953 			newPos = maxPos;
1954 			break;
1955 	case SB_ENDSCROLL:
1956 			CalcChildScroll(hWnd,(uMsg == WM_VSCROLL)?SB_VERT:SB_HORZ);
1957 			return;
1958     }
1959 
1960     if( newPos > maxPos )
1961 	newPos = maxPos;
1962     else
1963 	if( newPos < minPos )
1964 	    newPos = minPos;
1965 
1966     SetScrollPos(hWnd, (uMsg == WM_VSCROLL)?SB_VERT:SB_HORZ , newPos, TRUE);
1967 
1968     if( uMsg == WM_VSCROLL )
1969 	ScrollWindowEx(hWnd ,0 ,curPos - newPos, NULL, NULL, 0, NULL,
1970 			SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
1971     else
1972 	ScrollWindowEx(hWnd ,curPos - newPos, 0, NULL, NULL, 0, NULL,
1973 			SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
1974 }
1975 
1976 /******************************************************************************
1977  *		CascadeWindows (USER32.@) Cascades MDI child windows
1978  *
1979  * RETURNS
1980  *    Success: Number of cascaded windows.
1981  *    Failure: 0
1982  */
1983 
1984 typedef struct CASCADE_INFO
1985 {
1986     HWND hwndTop;
1987     UINT wFlags;
1988     HWND hwndParent;
1989     HWND hwndDesktop;
1990     HWND hTrayWnd;
1991     HWND hwndProgman;
1992     HWND *ahwnd;
1993     DWORD chwnd;
1994 } CASCADE_INFO;
1995 
1996 static BOOL CALLBACK
GetCascadeChildProc(HWND hwnd,LPARAM lParam)1997 GetCascadeChildProc(HWND hwnd, LPARAM lParam)
1998 {
1999     DWORD count, size;
2000     HWND *ahwnd;
2001     CASCADE_INFO *pInfo = (CASCADE_INFO *)lParam;
2002 
2003     if (hwnd == pInfo->hwndDesktop || hwnd == pInfo->hTrayWnd ||
2004         hwnd == pInfo->hwndProgman || hwnd == pInfo->hwndTop)
2005     {
2006         return TRUE;
2007     }
2008 
2009     if (pInfo->hwndParent && GetParent(hwnd) != pInfo->hwndParent)
2010         return TRUE;
2011 
2012     if ((pInfo->wFlags & MDITILE_SKIPDISABLED) && !IsWindowEnabled(hwnd))
2013         return TRUE;
2014 
2015     if (!IsWindowVisible(hwnd) || IsIconic(hwnd))
2016         return TRUE;
2017 
2018     count = pInfo->chwnd;
2019     size = (count + 1) * sizeof(HWND);
2020 
2021     if (count == 0 || pInfo->ahwnd == NULL)
2022     {
2023         count = 0;
2024         pInfo->ahwnd = (HWND *)HeapAlloc(GetProcessHeap(), 0, size);
2025     }
2026     else
2027     {
2028         ahwnd = (HWND *)HeapReAlloc(GetProcessHeap(), 0, pInfo->ahwnd, size);
2029         if (ahwnd == NULL)
2030         {
2031             HeapFree(GetProcessHeap(), 0, pInfo->ahwnd);
2032         }
2033         pInfo->ahwnd = ahwnd;
2034     }
2035 
2036     if (pInfo->ahwnd == NULL)
2037     {
2038         pInfo->chwnd = 0;
2039         return FALSE;
2040     }
2041 
2042     pInfo->ahwnd[count] = hwnd;
2043     pInfo->chwnd = count + 1;
2044     return TRUE;
2045 }
2046 
2047 static BOOL
QuerySizeFix(HWND hwnd,LPINT pcx,LPINT pcy)2048 QuerySizeFix(HWND hwnd, LPINT pcx, LPINT pcy)
2049 {
2050     MINMAXINFO mmi;
2051     DWORD_PTR dwResult;
2052 
2053     mmi.ptMinTrackSize.x = mmi.ptMinTrackSize.y = 0;
2054     mmi.ptMaxTrackSize.x = mmi.ptMaxTrackSize.y = MAXLONG;
2055     if (SendMessageTimeoutW(hwnd, WM_GETMINMAXINFO, 0, (LPARAM)&mmi,
2056                             SMTO_ABORTIFHUNG | SMTO_NORMAL, 1000, &dwResult))
2057     {
2058         *pcx = min(max(*pcx, mmi.ptMinTrackSize.x), mmi.ptMaxTrackSize.x);
2059         *pcy = min(max(*pcy, mmi.ptMinTrackSize.y), mmi.ptMaxTrackSize.y);
2060         return TRUE;
2061     }
2062     return FALSE;
2063 }
2064 
2065 WORD WINAPI
CascadeWindows(HWND hwndParent,UINT wFlags,LPCRECT lpRect,UINT cKids,const HWND * lpKids)2066 CascadeWindows(HWND hwndParent, UINT wFlags, LPCRECT lpRect,
2067                UINT cKids, const HWND *lpKids)
2068 {
2069     CASCADE_INFO info;
2070     HWND hwnd, hwndTop, hwndPrev;
2071     HMONITOR hMon;
2072     MONITORINFO mi;
2073     RECT rcWork, rcWnd;
2074     DWORD i, ret = 0;
2075     INT x, y, cx, cy, cxNew, cyNew, cxWork, cyWork, dx, dy;
2076     HDWP hDWP;
2077     POINT pt;
2078 
2079     TRACE("(%p,0x%08x,...,%u,...)\n", hwndParent, wFlags, cKids);
2080 
2081     hwndTop = GetTopWindow(hwndParent);
2082 
2083     ZeroMemory(&info, sizeof(info));
2084     info.hwndDesktop = GetDesktopWindow();
2085     info.hTrayWnd = FindWindowW(L"Shell_TrayWnd", NULL);
2086     info.hwndProgman = FindWindowW(L"Progman", NULL);
2087     info.hwndParent = hwndParent;
2088     info.wFlags = wFlags;
2089 
2090     if (cKids == 0 || lpKids == NULL)
2091     {
2092         info.hwndTop = hwndTop;
2093         EnumChildWindows(hwndParent, GetCascadeChildProc, (LPARAM)&info);
2094 
2095         info.hwndTop = NULL;
2096         GetCascadeChildProc(hwndTop, (LPARAM)&info);
2097     }
2098     else
2099     {
2100         info.chwnd = cKids;
2101         info.ahwnd = (HWND *)lpKids;
2102     }
2103 
2104     if (info.chwnd == 0 || info.ahwnd == NULL)
2105         return ret;
2106 
2107     if (lpRect)
2108     {
2109         rcWork = *lpRect;
2110     }
2111     else if (hwndParent)
2112     {
2113         GetClientRect(hwndParent, &rcWork);
2114     }
2115     else
2116     {
2117         pt.x = pt.y = 0;
2118         hMon = MonitorFromPoint(pt, MONITOR_DEFAULTTOPRIMARY);
2119         mi.cbSize = sizeof(mi);
2120         GetMonitorInfoW(hMon, &mi);
2121         rcWork = mi.rcWork;
2122     }
2123 
2124     hDWP = BeginDeferWindowPos(info.chwnd);
2125     if (hDWP == NULL)
2126         goto cleanup;
2127 
2128     x = rcWork.left;
2129     y = rcWork.top;
2130     dx = GetSystemMetrics(SM_CXSIZEFRAME) + GetSystemMetrics(SM_CXSIZE);
2131     dy = GetSystemMetrics(SM_CYSIZEFRAME) + GetSystemMetrics(SM_CYSIZE);
2132     cxWork = rcWork.right - rcWork.left;
2133     cyWork = rcWork.bottom - rcWork.top;
2134     hwndPrev = NULL;
2135     for (i = info.chwnd; i > 0;)    /* in reverse order */
2136     {
2137         --i;
2138         hwnd = info.ahwnd[i];
2139 
2140         if (!IsWindowVisible(hwnd) || IsIconic(hwnd))
2141             continue;
2142 
2143         if ((info.wFlags & MDITILE_SKIPDISABLED) && !IsWindowEnabled(hwnd))
2144             continue;
2145 
2146         if (IsZoomed(hwnd))
2147             ShowWindow(hwnd, SW_RESTORE | SW_SHOWNA);
2148 
2149         GetWindowRect(hwnd, &rcWnd);
2150         cxNew = cx = rcWnd.right - rcWnd.left;
2151         cyNew = cy = rcWnd.bottom - rcWnd.top;
2152 
2153         /* if we can change the window size and it is not only one */
2154         if (info.chwnd != 1 && (GetWindowLongPtrW(hwnd, GWL_STYLE) & WS_THICKFRAME))
2155         {
2156             /* check the size */
2157 #define MIN_THRESHOLD(xy) (((xy) * 4) / 7)      /* in the rate 4/7 */
2158 #define MAX_THRESHOLD(xy) (((xy) * 5) / 7)      /* in the rate 5/7 */
2159             cxNew = max(min(cxNew, MAX_THRESHOLD(cxWork)), MIN_THRESHOLD(cxWork));
2160             cyNew = max(min(cyNew, MAX_THRESHOLD(cyWork)), MIN_THRESHOLD(cyWork));
2161 #undef MIN_THRESHOLD
2162 #undef MAX_THRESHOLD
2163             if (cx != cxNew || cy != cyNew)
2164             {
2165                 /* too large. shrink if we can */
2166                 if (QuerySizeFix(hwnd, &cxNew, &cyNew))
2167                 {
2168                     cx = cxNew;
2169                     cy = cyNew;
2170                 }
2171             }
2172         }
2173 
2174         if (x + cx > rcWork.right)
2175             x = rcWork.left;
2176         if (y + cy > rcWork.bottom)
2177             y = rcWork.top;
2178 
2179         hDWP = DeferWindowPos(hDWP, hwnd, HWND_TOP, x, y, cx, cy, SWP_NOACTIVATE);
2180         if (hDWP == NULL)
2181         {
2182             ret = 0;
2183             goto cleanup;
2184         }
2185 
2186         x += dx;
2187         y += dy;
2188         hwndPrev = hwnd;
2189         ++ret;
2190     }
2191 
2192     NtUserEndDeferWindowPosEx(hDWP, TRUE);
2193 
2194     if (hwndPrev)
2195         SetForegroundWindow(hwndPrev);
2196 
2197 cleanup:
2198     if (cKids == 0 || lpKids == NULL)
2199         HeapFree(GetProcessHeap(), 0, info.ahwnd);
2200 
2201     return (WORD)ret;
2202 }
2203 
2204 
2205 /***********************************************************************
2206  *		CascadeChildWindows (USER32.@)
2207  */
CascadeChildWindows(HWND parent,UINT flags)2208 WORD WINAPI CascadeChildWindows( HWND parent, UINT flags )
2209 {
2210     return CascadeWindows( parent, flags, NULL, 0, NULL );
2211 }
2212 
2213 
2214 /******************************************************************************
2215  *		TileWindows (USER32.@) Tiles MDI child windows
2216  *
2217  * RETURNS
2218  *    Success: Number of tiled windows.
2219  *    Failure: 0
2220  */
2221 WORD WINAPI
TileWindows(HWND hwndParent,UINT wFlags,LPCRECT lpRect,UINT cKids,const HWND * lpKids)2222 TileWindows(HWND hwndParent, UINT wFlags, LPCRECT lpRect,
2223             UINT cKids, const HWND *lpKids)
2224 {
2225     HWND hwnd, hwndTop, hwndPrev;
2226     CASCADE_INFO info;
2227     RECT rcWork, rcWnd;
2228     DWORD i, iRow, iColumn, cRows, cColumns, ret = 0;
2229     INT x, y, cx, cy, cxNew, cyNew, cxWork, cyWork, cxCell, cyCell, cxMin2, cyMin3;
2230     HDWP hDWP;
2231     MONITORINFO mi;
2232     HMONITOR hMon;
2233     POINT pt;
2234 
2235     TRACE("(%p,0x%08x,...,%u,...)\n", hwndParent, wFlags, cKids);
2236 
2237     hwndTop = GetTopWindow(hwndParent);
2238 
2239     ZeroMemory(&info, sizeof(info));
2240     info.hwndDesktop = GetDesktopWindow();
2241     info.hTrayWnd = FindWindowW(L"Shell_TrayWnd", NULL);
2242     info.hwndProgman = FindWindowW(L"Progman", NULL);
2243     info.hwndParent = hwndParent;
2244     info.wFlags = wFlags;
2245 
2246     if (cKids == 0 || lpKids == NULL)
2247     {
2248         info.hwndTop = hwndTop;
2249         EnumChildWindows(hwndParent, GetCascadeChildProc, (LPARAM)&info);
2250 
2251         info.hwndTop = NULL;
2252         GetCascadeChildProc(hwndTop, (LPARAM)&info);
2253     }
2254     else
2255     {
2256         info.chwnd = cKids;
2257         info.ahwnd = (HWND *)lpKids;
2258     }
2259 
2260     if (info.chwnd == 0 || info.ahwnd == NULL)
2261         return ret;
2262 
2263     if (lpRect)
2264     {
2265         rcWork = *lpRect;
2266     }
2267     else if (hwndParent)
2268     {
2269         GetClientRect(hwndParent, &rcWork);
2270     }
2271     else
2272     {
2273         pt.x = pt.y = 0;
2274         hMon = MonitorFromPoint(pt, MONITOR_DEFAULTTOPRIMARY);
2275         mi.cbSize = sizeof(mi);
2276         GetMonitorInfoW(hMon, &mi);
2277         rcWork = mi.rcWork;
2278     }
2279 
2280     cxWork = rcWork.right - rcWork.left;
2281     cyWork = rcWork.bottom - rcWork.top;
2282 
2283     cxMin2 = GetSystemMetrics(SM_CXMIN) * 2;
2284     cyMin3 = GetSystemMetrics(SM_CYMIN) * 3;
2285 
2286     /* calculate the numbers and widths of columns and rows */
2287     if (info.wFlags & MDITILE_HORIZONTAL)
2288     {
2289         cColumns = info.chwnd;
2290         cRows = 1;
2291         for (;;)
2292         {
2293             cxCell = cxWork / cColumns;
2294             cyCell = cyWork / cRows;
2295             if (cyCell <= cyMin3 || cxCell >= cxMin2)
2296                 break;
2297 
2298             ++cRows;
2299             cColumns = (info.chwnd + cRows - 1) / cRows;
2300         }
2301     }
2302     else
2303     {
2304         cRows = info.chwnd;
2305         cColumns = 1;
2306         for (;;)
2307         {
2308             cxCell = cxWork / cColumns;
2309             cyCell = cyWork / cRows;
2310             if (cxCell <= cxMin2 || cyCell >= cyMin3)
2311                 break;
2312 
2313             ++cColumns;
2314             cRows = (info.chwnd + cColumns - 1) / cColumns;
2315         }
2316     }
2317 
2318     hDWP = BeginDeferWindowPos(info.chwnd);
2319     if (hDWP == NULL)
2320         goto cleanup;
2321 
2322     x = rcWork.left;
2323     y = rcWork.top;
2324     hwndPrev = NULL;
2325     iRow = iColumn = 0;
2326     for (i = info.chwnd; i > 0;)    /* in reverse order */
2327     {
2328         --i;
2329         hwnd = info.ahwnd[i];
2330 
2331         if (IsZoomed(hwnd))
2332             ShowWindow(hwnd, SW_RESTORE | SW_SHOWNA);
2333 
2334         GetWindowRect(hwnd, &rcWnd);
2335         cx = rcWnd.right - rcWnd.left;
2336         cy = rcWnd.bottom - rcWnd.top;
2337 
2338         /* if we can change the window size */
2339         if (GetWindowLongPtrW(hwnd, GWL_STYLE) & WS_THICKFRAME)
2340         {
2341             cxNew = cxCell;
2342             cyNew = cyCell;
2343             /* shrink if we can */
2344             if (QuerySizeFix(hwnd, &cxNew, &cyNew))
2345             {
2346                 cx = cxNew;
2347                 cy = cyNew;
2348             }
2349         }
2350 
2351         hDWP = DeferWindowPos(hDWP, hwnd, HWND_TOP, x, y, cx, cy, SWP_NOACTIVATE);
2352         if (hDWP == NULL)
2353         {
2354             ret = 0;
2355             goto cleanup;
2356         }
2357 
2358         if (info.wFlags & MDITILE_HORIZONTAL)
2359         {
2360             x += cxCell;
2361             ++iColumn;
2362             if (iColumn >= cColumns)
2363             {
2364                 iColumn = 0;
2365                 ++iRow;
2366                 x = rcWork.left;
2367                 y += cyCell;
2368             }
2369         }
2370         else
2371         {
2372             y += cyCell;
2373             ++iRow;
2374             if (iRow >= cRows)
2375             {
2376                 iRow = 0;
2377                 ++iColumn;
2378                 x += cxCell;
2379                 y = rcWork.top;
2380             }
2381         }
2382         hwndPrev = hwnd;
2383         ++ret;
2384     }
2385 
2386     NtUserEndDeferWindowPosEx(hDWP, TRUE);
2387 
2388     if (hwndPrev)
2389         SetForegroundWindow(hwndPrev);
2390 
2391 cleanup:
2392     if (cKids == 0 || lpKids == NULL)
2393         HeapFree(GetProcessHeap(), 0, info.ahwnd);
2394 
2395     return (WORD)ret;
2396 }
2397 
2398 
2399 /***********************************************************************
2400  *		TileChildWindows (USER32.@)
2401  */
TileChildWindows(HWND parent,UINT flags)2402 WORD WINAPI TileChildWindows( HWND parent, UINT flags )
2403 {
2404     return TileWindows( parent, flags, NULL, 0, NULL );
2405 }
2406 
2407 
2408 /************************************************************************
2409  *              "More Windows..." functionality
2410  */
2411 
2412 /*              MDI_MoreWindowsDlgProc
2413  *
2414  *    This function will process the messages sent to the "More Windows..."
2415  *    dialog.
2416  *    Return values:  0    = cancel pressed
2417  *                    HWND = ok pressed or double-click in the list...
2418  *
2419  */
2420 
MDI_MoreWindowsDlgProc(HWND hDlg,UINT iMsg,WPARAM wParam,LPARAM lParam)2421 static INT_PTR WINAPI MDI_MoreWindowsDlgProc (HWND hDlg, UINT iMsg, WPARAM wParam, LPARAM lParam)
2422 {
2423     switch (iMsg)
2424     {
2425        case WM_INITDIALOG:
2426        {
2427            UINT widest       = 0;
2428            UINT length;
2429            UINT i;
2430            MDICLIENTINFO *ci = get_client_info( (HWND)lParam );
2431            HWND hListBox = GetDlgItem(hDlg, MDI_IDC_LISTBOX);
2432 
2433            for (i = 0; i < ci->nActiveChildren; i++)
2434            {
2435                WCHAR buffer[MDI_MAXTITLELENGTH];
2436 
2437                if (!InternalGetWindowText( ci->child[i], buffer, sizeof(buffer)/sizeof(WCHAR) ))
2438                    continue;
2439                SendMessageW(hListBox, LB_ADDSTRING, 0, (LPARAM)buffer );
2440                SendMessageW(hListBox, LB_SETITEMDATA, i, (LPARAM)ci->child[i] );
2441                length = strlenW(buffer);  /* FIXME: should use GetTextExtentPoint */
2442                if (length > widest)
2443                    widest = length;
2444            }
2445            /* Make sure the horizontal scrollbar scrolls ok */
2446            SendMessageW(hListBox, LB_SETHORIZONTALEXTENT, widest * 6, 0);
2447 
2448            /* Set the current selection */
2449            SendMessageW(hListBox, LB_SETCURSEL, MDI_MOREWINDOWSLIMIT, 0);
2450            return TRUE;
2451        }
2452 
2453        case WM_COMMAND:
2454            switch (LOWORD(wParam))
2455            {
2456                 default:
2457                     if (HIWORD(wParam) != LBN_DBLCLK) break;
2458                     /* fall through */
2459                 case IDOK:
2460                 {
2461                     /*  windows are sorted by menu ID, so we must return the
2462                      *  window associated to the given id
2463                      */
2464                     HWND hListBox     = GetDlgItem(hDlg, MDI_IDC_LISTBOX);
2465                     UINT index        = SendMessageW(hListBox, LB_GETCURSEL, 0, 0);
2466                     LRESULT res = SendMessageW(hListBox, LB_GETITEMDATA, index, 0);
2467                     EndDialog(hDlg, res);
2468                     return TRUE;
2469                 }
2470                 case IDCANCEL:
2471                     EndDialog(hDlg, 0);
2472                     return TRUE;
2473            }
2474            break;
2475     }
2476     return FALSE;
2477 }
2478 
2479 /*
2480  *
2481  *                      MDI_MoreWindowsDialog
2482  *
2483  *     Prompts the user with a listbox containing the opened
2484  *     documents. The user can then choose a windows and click
2485  *     on OK to set the current window to the one selected, or
2486  *     CANCEL to cancel. The function returns a handle to the
2487  *     selected window.
2488  */
2489 
MDI_MoreWindowsDialog(HWND hwnd)2490 static HWND MDI_MoreWindowsDialog(HWND hwnd)
2491 {
2492     LPCVOID template;
2493     HRSRC hRes;
2494     HANDLE hDlgTmpl;
2495 
2496     hRes = FindResourceA(User32Instance, "MDI_MOREWINDOWS", (LPSTR)RT_DIALOG);
2497 
2498     if (hRes == 0)
2499         return 0;
2500 
2501     hDlgTmpl = LoadResource(User32Instance, hRes );
2502 
2503     if (hDlgTmpl == 0)
2504         return 0;
2505 
2506     template = LockResource( hDlgTmpl );
2507 
2508     if (template == 0)
2509         return 0;
2510 
2511     return (HWND) DialogBoxIndirectParamA(User32Instance, template, hwnd,
2512                                           MDI_MoreWindowsDlgProc, (LPARAM) hwnd);
2513 }
2514