Make message spy thread safe.
[wine] / windows / mdi.c
1 /* MDI.C
2  *
3  * Copyright 1994, Bob Amstadt
4  *           1995,1996 Alex Korobka
5  *
6  * This file contains routines to support MDI (Multiple Document
7  * Interface) features .
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 2.1 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with this library; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22  *
23  * Notes: Fairly complete implementation.
24  *        Also, Excel and WinWord do _not_ use MDI so if you're trying
25  *        to fix them look elsewhere.
26  *
27  * Notes on how the "More Windows..." is implemented:
28  *
29  *      When we have more than 9 opened windows, a "More Windows..."
30  *      option appears in the "Windows" menu. Each child window has
31  *      a WND* associated with it, accesible via the children list of
32  *      the parent window. This WND* has a wIDmenu member, which reflects
33  *      the position of the child in the window list. For example, with
34  *      9 child windows, we could have the following pattern:
35  *
36  *
37  *
38  *                Name of the child window    pWndChild->wIDmenu
39  *                     Doc1                       5000
40  *                     Doc2                       5001
41  *                     Doc3                       5002
42  *                     Doc4                       5003
43  *                     Doc5                       5004
44  *                     Doc6                       5005
45  *                     Doc7                       5006
46  *                     Doc8                       5007
47  *                     Doc9                       5008
48  *
49  *
50  *       The "Windows" menu, as the "More windows..." dialog, are constructed
51  *       in this order. If we add a child, we would have the following list:
52  *
53  *
54  *               Name of the child window    pWndChild->wIDmenu
55  *                     Doc1                       5000
56  *                     Doc2                       5001
57  *                     Doc3                       5002
58  *                     Doc4                       5003
59  *                     Doc5                       5004
60  *                     Doc6                       5005
61  *                     Doc7                       5006
62  *                     Doc8                       5007
63  *                     Doc9                       5008
64  *                     Doc10                      5009
65  *
66  *       But only 5000 to 5008 would be displayed in the "Windows" menu. We want
67  *       the last created child to be in the menu, so we swap the last child with
68  *       the 9th... Doc9 will be accessible via the "More Windows..." option.
69  *
70  *                     Doc1                       5000
71  *                     Doc2                       5001
72  *                     Doc3                       5002
73  *                     Doc4                       5003
74  *                     Doc5                       5004
75  *                     Doc6                       5005
76  *                     Doc7                       5006
77  *                     Doc8                       5007
78  *                     Doc9                       5009
79  *                     Doc10                      5008
80  *
81  */
82
83 #include <stdlib.h>
84 #include <stdarg.h>
85 #include <stdio.h>
86 #include <string.h>
87 #include <math.h>
88
89 #include "windef.h"
90 #include "winbase.h"
91 #include "wingdi.h"
92 #include "winuser.h"
93 #include "wownt32.h"
94 #include "wine/winuser16.h"
95 #include "wine/unicode.h"
96 #include "win.h"
97 #include "nonclient.h"
98 #include "controls.h"
99 #include "message.h"
100 #include "user.h"
101 #include "wine/debug.h"
102 #include "dlgs.h"
103
104 WINE_DEFAULT_DEBUG_CHANNEL(mdi);
105
106 #define MDI_MAXTITLELENGTH      0xa1
107
108 #define WM_MDICALCCHILDSCROLL   0x10ac /* this is exactly what Windows uses */
109
110 /* "More Windows..." definitions */
111 #define MDI_MOREWINDOWSLIMIT    9       /* after this number of windows, a "More Windows..."
112                                            option will appear under the Windows menu */
113 #define MDI_IDC_LISTBOX         100
114 #define IDS_MDI_MOREWINDOWS     13
115
116 #define MDIF_NEEDUPDATE         0x0001
117
118 typedef struct
119 {
120     UINT      nActiveChildren;
121     HWND      hwndActiveChild;
122     HWND      *child; /* array of tracked children */
123     HMENU     hFrameMenu;
124     HMENU     hWindowMenu;
125     UINT      idFirstChild;
126     LPWSTR    frameTitle;
127     UINT      nTotalCreated;
128     UINT      mdiFlags;
129     UINT      sbRecalc;   /* SB_xxx flags for scrollbar fixup */
130 } MDICLIENTINFO;
131
132 static HBITMAP hBmpClose   = 0;
133
134 /* ----------------- declarations ----------------- */
135 static void MDI_UpdateFrameText( HWND, HWND, LPCWSTR);
136 static BOOL MDI_AugmentFrameMenu( HWND, HWND );
137 static BOOL MDI_RestoreFrameMenu( HWND, HWND );
138 static LONG MDI_ChildActivate( HWND, HWND );
139 static LRESULT MDI_RefreshMenu(MDICLIENTINFO *);
140
141 static HWND MDI_MoreWindowsDialog(HWND);
142 static LRESULT WINAPI MDIClientWndProcA( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam );
143 static LRESULT WINAPI MDIClientWndProcW( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam );
144
145 /* -------- Miscellaneous service functions ----------
146  *
147  *                      MDI_GetChildByID
148  */
149 static HWND MDI_GetChildByID(HWND hwnd, UINT id, MDICLIENTINFO *ci)
150 {
151     int i;
152
153     for (i = 0; ci->nActiveChildren; i++)
154     {
155         if (GetWindowLongPtrW( ci->child[i], GWLP_ID ) == id)
156             return ci->child[i];
157     }
158     return 0;
159 }
160
161 static void MDI_PostUpdate(HWND hwnd, MDICLIENTINFO* ci, WORD recalc)
162 {
163     if( !(ci->mdiFlags & MDIF_NEEDUPDATE) )
164     {
165         ci->mdiFlags |= MDIF_NEEDUPDATE;
166         PostMessageA( hwnd, WM_MDICALCCHILDSCROLL, 0, 0);
167     }
168     ci->sbRecalc = recalc;
169 }
170
171
172 /*********************************************************************
173  * MDIClient class descriptor
174  */
175 const struct builtin_class_descr MDICLIENT_builtin_class =
176 {
177     "MDIClient",            /* name */
178     0,                      /* style */
179     MDIClientWndProcA,      /* procA */
180     MDIClientWndProcW,      /* procW */
181     sizeof(MDICLIENTINFO),  /* extra */
182     IDC_ARROW,              /* cursor */
183     (HBRUSH)(COLOR_APPWORKSPACE+1)    /* brush */
184 };
185
186
187 static MDICLIENTINFO *get_client_info( HWND client )
188 {
189     MDICLIENTINFO *ret = NULL;
190     WND *win = WIN_GetPtr( client );
191     if (win)
192     {
193         if (win == WND_OTHER_PROCESS)
194         {
195             ERR( "client %p belongs to other process\n", client );
196             return NULL;
197         }
198         if (win->cbWndExtra < sizeof(MDICLIENTINFO)) WARN( "%p is not an MDI client\n", client );
199         else ret = (MDICLIENTINFO *)win->wExtra;
200         WIN_ReleasePtr( win );
201     }
202     return ret;
203 }
204
205 /**********************************************************************
206  *                      MDI_GetWindow
207  *
208  * returns "activateable" child different from the current or zero
209  */
210 static HWND MDI_GetWindow(MDICLIENTINFO *clientInfo, HWND hWnd, BOOL bNext,
211                             DWORD dwStyleMask )
212 {
213     int i;
214     HWND *list;
215     HWND last = 0;
216
217     dwStyleMask |= WS_DISABLED | WS_VISIBLE;
218     if( !hWnd ) hWnd = clientInfo->hwndActiveChild;
219
220     if (!(list = WIN_ListChildren( GetParent(hWnd) ))) return 0;
221     i = 0;
222     /* start from next after hWnd */
223     while (list[i] && list[i] != hWnd) i++;
224     if (list[i]) i++;
225
226     for ( ; list[i]; i++)
227     {
228         if (GetWindow( list[i], GW_OWNER )) continue;
229         if ((GetWindowLongW( list[i], GWL_STYLE ) & dwStyleMask) != WS_VISIBLE) continue;
230         last = list[i];
231         if (bNext) goto found;
232     }
233     /* now restart from the beginning */
234     for (i = 0; list[i] && list[i] != hWnd; i++)
235     {
236         if (GetWindow( list[i], GW_OWNER )) continue;
237         if ((GetWindowLongW( list[i], GWL_STYLE ) & dwStyleMask) != WS_VISIBLE) continue;
238         last = list[i];
239         if (bNext) goto found;
240     }
241  found:
242     HeapFree( GetProcessHeap(), 0, list );
243     return last;
244 }
245
246 /**********************************************************************
247  *                      MDI_CalcDefaultChildPos
248  *
249  *  It seems that the default height is about 2/3 of the client rect
250  */
251 void MDI_CalcDefaultChildPos( HWND hwndClient, INT total, LPPOINT lpPos, INT delta )
252 {
253     INT  nstagger;
254     RECT rect;
255     INT  spacing = GetSystemMetrics(SM_CYCAPTION) +
256                      GetSystemMetrics(SM_CYFRAME) - 1;
257
258     if (total < 0)
259     {
260         MDICLIENTINFO *ci = get_client_info(hwndClient);
261         total = ci ? ci->nTotalCreated : 0;
262     }
263
264     GetClientRect( hwndClient, &rect );
265     if( rect.bottom - rect.top - delta >= spacing )
266         rect.bottom -= delta;
267
268     nstagger = (rect.bottom - rect.top)/(3 * spacing);
269     lpPos[1].x = (rect.right - rect.left - nstagger * spacing);
270     lpPos[1].y = (rect.bottom - rect.top - nstagger * spacing);
271     lpPos[0].x = lpPos[0].y = spacing * (total%(nstagger+1));
272 }
273
274 /**********************************************************************
275  *            MDISetMenu
276  */
277 static LRESULT MDISetMenu( HWND hwnd, HMENU hmenuFrame,
278                            HMENU hmenuWindow)
279 {
280     MDICLIENTINFO *ci;
281     HWND hwndFrame = GetParent(hwnd);
282
283     TRACE("%p %p %p\n", hwnd, hmenuFrame, hmenuWindow);
284
285     if (hmenuFrame && !IsMenu(hmenuFrame))
286     {
287         WARN("hmenuFrame is not a menu handle\n");
288         return 0L;
289     }
290
291     if (hmenuWindow && !IsMenu(hmenuWindow))
292     {
293         WARN("hmenuWindow is not a menu handle\n");
294         return 0L;
295     }
296
297     if (!(ci = get_client_info( hwnd ))) return 0;
298
299     if (hmenuFrame)
300     {
301         if (hmenuFrame == ci->hFrameMenu) return (LRESULT)hmenuFrame;
302
303         if( IsZoomed(ci->hwndActiveChild) && hmenuFrame != ci->hFrameMenu )
304             MDI_RestoreFrameMenu( hwndFrame, ci->hwndActiveChild );
305     }
306
307     if( hmenuWindow && hmenuWindow != ci->hWindowMenu )
308     {
309         /* delete menu items from ci->hWindowMenu
310          * and add them to hmenuWindow */
311         /* Agent newsreader calls this function with  ci->hWindowMenu == NULL */
312         if( ci->hWindowMenu && ci->nActiveChildren )
313         {
314             UINT nActiveChildren_old = ci->nActiveChildren;
315
316             /* Remove all items from old Window menu */
317             ci->nActiveChildren = 0;
318             MDI_RefreshMenu(ci);
319
320             ci->hWindowMenu = hmenuWindow;
321
322             /* Add items to the new Window menu */
323             ci->nActiveChildren = nActiveChildren_old;
324             MDI_RefreshMenu(ci);
325         }
326         else
327             ci->hWindowMenu = hmenuWindow;
328     }
329
330     if (hmenuFrame)
331     {
332         SetMenu(hwndFrame, hmenuFrame);
333         if( hmenuFrame != ci->hFrameMenu )
334         {
335             HMENU oldFrameMenu = ci->hFrameMenu;
336
337             ci->hFrameMenu = hmenuFrame;
338             if( IsZoomed(ci->hwndActiveChild) )
339                 MDI_AugmentFrameMenu( hwndFrame, ci->hwndActiveChild );
340
341             return (LRESULT)oldFrameMenu;
342         }
343     }
344     else
345     {
346         /* SetMenu() may already have been called, meaning that this window
347          * already has its menu. But they may have done a SetMenu() on
348          * an MDI window, and called MDISetMenu() after the fact, meaning
349          * that the "if" to this "else" wouldn't catch the need to
350          * augment the frame menu.
351          */
352         if( IsZoomed(ci->hwndActiveChild) )
353             MDI_AugmentFrameMenu( hwndFrame, ci->hwndActiveChild );
354     }
355
356     return 0;
357 }
358
359 /**********************************************************************
360  *            MDIRefreshMenu
361  */
362 static LRESULT MDI_RefreshMenu(MDICLIENTINFO *ci)
363 {
364     UINT i, count, visible, id;
365     WCHAR buf[MDI_MAXTITLELENGTH];
366
367     TRACE("children %u, window menu %p\n", ci->nActiveChildren, ci->hWindowMenu);
368
369     if (!ci->hWindowMenu)
370         return 0;
371
372     if (!IsMenu(ci->hWindowMenu))
373     {
374         WARN("Window menu handle %p is no more valid\n", ci->hWindowMenu);
375         return 0;
376     }
377
378     /* Windows finds the last separator in the menu, and if after it
379      * there is a menu item with MDI magic ID removes all existing
380      * menu items after it, and then adds visible MDI children.
381      */
382     count = GetMenuItemCount(ci->hWindowMenu);
383     for (i = 0; i < count; i++)
384     {
385         MENUITEMINFOW mii;
386
387         memset(&mii, 0, sizeof(mii));
388         mii.cbSize = sizeof(mii);
389         mii.fMask  = MIIM_TYPE;
390         if (GetMenuItemInfoW(ci->hWindowMenu, i, TRUE, &mii))
391         {
392             if (mii.fType & MF_SEPARATOR)
393             {
394                 /* Windows checks only ID of the menu item */
395                 memset(&mii, 0, sizeof(mii));
396                 mii.cbSize = sizeof(mii);
397                 mii.fMask  = MIIM_ID;
398                 if (GetMenuItemInfoW(ci->hWindowMenu, i + 1, TRUE, &mii))
399                 {
400                     if (mii.wID == ci->idFirstChild)
401                     {
402                         TRACE("removing %u items including separator\n", count - i);
403                         while (RemoveMenu(ci->hWindowMenu, i, MF_BYPOSITION))
404                             /* nothing */;
405
406                         break;
407                     }
408                 }
409             }
410         }
411     }
412
413     visible = 0;
414     for (i = 0; i < ci->nActiveChildren; i++)
415     {
416         if (GetWindowLongW(ci->child[i], GWL_STYLE) & WS_VISIBLE)
417         {
418             id = ci->idFirstChild + visible;
419
420             if (visible == MDI_MOREWINDOWSLIMIT)
421             {
422                 LoadStringW(user32_module, IDS_MDI_MOREWINDOWS, buf, sizeof(buf)/sizeof(WCHAR));
423                 AppendMenuW(ci->hWindowMenu, MF_STRING, id, buf);
424                 break;
425             }
426
427             if (!visible)
428                 /* Visio expects that separator has id 0 */
429                 AppendMenuW(ci->hWindowMenu, MF_SEPARATOR, 0, NULL);
430
431             visible++;
432
433             SetWindowLongPtrW(ci->child[i], GWLP_ID, id);
434
435             buf[0] = '&';
436             buf[1] = '0' + visible;
437             buf[2] = ' ';
438             InternalGetWindowText(ci->child[i], buf + 3, sizeof(buf)/sizeof(WCHAR) - 3);
439             TRACE("Adding %p, id %u %s\n", ci->child[i], id, debugstr_w(buf));
440             AppendMenuW(ci->hWindowMenu, MF_STRING, id, buf);
441
442             if (ci->child[i] == ci->hwndActiveChild)
443                 CheckMenuItem(ci->hWindowMenu, id, MF_CHECKED);
444         }
445         else
446             TRACE("MDI child %p is not visible, skipping\n", ci->child[i]);
447     }
448
449     return (LRESULT)ci->hFrameMenu;
450 }
451
452
453 /* ------------------ MDI child window functions ---------------------- */
454
455 /**********************************************************************
456  *                      MDI_ChildGetMinMaxInfo
457  *
458  * Note: The rule here is that client rect of the maximized MDI child
459  *       is equal to the client rect of the MDI client window.
460  */
461 static void MDI_ChildGetMinMaxInfo( HWND client, HWND hwnd, MINMAXINFO* lpMinMax )
462 {
463     RECT rect;
464
465     GetClientRect( client, &rect );
466     AdjustWindowRectEx( &rect, GetWindowLongW( hwnd, GWL_STYLE ),
467                         0, GetWindowLongW( hwnd, GWL_EXSTYLE ));
468
469     lpMinMax->ptMaxSize.x = rect.right -= rect.left;
470     lpMinMax->ptMaxSize.y = rect.bottom -= rect.top;
471
472     lpMinMax->ptMaxPosition.x = rect.left;
473     lpMinMax->ptMaxPosition.y = rect.top;
474
475     TRACE("max rect (%ld,%ld - %ld, %ld)\n",
476                         rect.left,rect.top,rect.right,rect.bottom);
477 }
478
479 /**********************************************************************
480  *                      MDI_SwitchActiveChild
481  *
482  * Note: SetWindowPos sends WM_CHILDACTIVATE to the child window that is
483  *       being activated
484  */
485 static void MDI_SwitchActiveChild( MDICLIENTINFO *ci, HWND hwndTo )
486 {
487     HWND hwndPrev;
488
489     hwndPrev = ci->hwndActiveChild;
490
491     TRACE("from %p, to %p\n", hwndPrev, hwndTo);
492
493     if ( hwndTo != hwndPrev )
494     {
495         BOOL was_zoomed = IsZoomed(hwndPrev);
496
497         if (was_zoomed)
498         {
499             /* restore old MDI child */
500             SendMessageW( hwndPrev, WM_SETREDRAW, FALSE, 0 );
501             ShowWindow( hwndPrev, SW_RESTORE );
502             SendMessageW( hwndPrev, WM_SETREDRAW, TRUE, 0 );
503
504             /* activate new MDI child */
505             SetWindowPos( hwndTo, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE );
506             /* maximize new MDI child */
507             ShowWindow( hwndTo, SW_MAXIMIZE );
508         }
509         /* activate new MDI child */
510         SetWindowPos( hwndTo, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE );
511     }
512 }
513
514
515 /**********************************************************************
516  *                                      MDIDestroyChild
517  */
518 static LRESULT MDIDestroyChild( HWND parent, MDICLIENTINFO *ci,
519                                 HWND child, BOOL flagDestroy )
520 {
521     UINT i;
522
523     TRACE("# of managed children %u\n", ci->nActiveChildren);
524
525     if( child == ci->hwndActiveChild )
526     {
527         HWND next = MDI_GetWindow(ci, child, TRUE, 0);
528         if (next)
529             MDI_SwitchActiveChild(ci, next);
530         else
531             ci->hwndActiveChild = 0; /* nothing to activate */
532     }
533
534     for (i = 0; i < ci->nActiveChildren; i++)
535     {
536         if (ci->child[i] == child)
537         {
538             HWND *new_child = HeapAlloc(GetProcessHeap(), 0, (ci->nActiveChildren - 1) * sizeof(HWND));
539             memcpy(new_child, ci->child, i * sizeof(HWND));
540             if (i + 1 < ci->nActiveChildren)
541                 memcpy(new_child + i, ci->child + i + 1, (ci->nActiveChildren - i - 1) * sizeof(HWND));
542             HeapFree(GetProcessHeap(), 0, ci->child);
543             ci->child = new_child;
544
545             ci->nActiveChildren--;
546             break;
547         }
548     }
549
550     if (flagDestroy)
551     {
552         MDI_PostUpdate(GetParent(child), ci, SB_BOTH+1);
553         DestroyWindow(child);
554     }
555
556     TRACE("child destroyed - %p\n", child);
557     return 0;
558 }
559
560
561 /**********************************************************************
562  *                                      MDI_ChildActivate
563  *
564  * Called in response to WM_CHILDACTIVATE
565  */
566 static LONG MDI_ChildActivate( HWND client, HWND child )
567 {
568     MDICLIENTINFO *clientInfo;
569     HWND prevActiveWnd, frame;
570     BOOL isActiveFrameWnd;
571
572     clientInfo = get_client_info( client );
573
574     if (clientInfo->hwndActiveChild == child) return 0;
575
576     TRACE("%p\n", child);
577
578     frame = GetParent(client);
579     isActiveFrameWnd = (GetActiveWindow() == frame);
580     prevActiveWnd = clientInfo->hwndActiveChild;
581
582     /* deactivate prev. active child */
583     if(prevActiveWnd)
584     {
585         SendMessageW( prevActiveWnd, WM_NCACTIVATE, FALSE, 0L );
586         SendMessageW( prevActiveWnd, WM_MDIACTIVATE, (WPARAM)prevActiveWnd, (LPARAM)child);
587     }
588
589     clientInfo->hwndActiveChild = child;
590
591     MDI_RefreshMenu(clientInfo);
592
593     if( isActiveFrameWnd )
594     {
595         SendMessageW( child, WM_NCACTIVATE, TRUE, 0L);
596         SetFocus(child);
597     }
598
599     SendMessageW( child, WM_MDIACTIVATE, (WPARAM)prevActiveWnd, (LPARAM)child );
600     return TRUE;
601 }
602
603 /* -------------------- MDI client window functions ------------------- */
604
605 /**********************************************************************
606  *                              CreateMDIMenuBitmap
607  */
608 static HBITMAP CreateMDIMenuBitmap(void)
609 {
610  HDC            hDCSrc  = CreateCompatibleDC(0);
611  HDC            hDCDest = CreateCompatibleDC(hDCSrc);
612  HBITMAP        hbClose = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_OLD_CLOSE) );
613  HBITMAP        hbCopy;
614  HBITMAP        hobjSrc, hobjDest;
615
616  hobjSrc = SelectObject(hDCSrc, hbClose);
617  hbCopy = CreateCompatibleBitmap(hDCSrc,GetSystemMetrics(SM_CXSIZE),GetSystemMetrics(SM_CYSIZE));
618  hobjDest = SelectObject(hDCDest, hbCopy);
619
620  BitBlt(hDCDest, 0, 0, GetSystemMetrics(SM_CXSIZE), GetSystemMetrics(SM_CYSIZE),
621           hDCSrc, GetSystemMetrics(SM_CXSIZE), 0, SRCCOPY);
622
623  SelectObject(hDCSrc, hobjSrc);
624  DeleteObject(hbClose);
625  DeleteDC(hDCSrc);
626
627  hobjSrc = SelectObject( hDCDest, GetStockObject(BLACK_PEN) );
628
629  MoveToEx( hDCDest, GetSystemMetrics(SM_CXSIZE) - 1, 0, NULL );
630  LineTo( hDCDest, GetSystemMetrics(SM_CXSIZE) - 1, GetSystemMetrics(SM_CYSIZE) - 1);
631
632  SelectObject(hDCDest, hobjSrc );
633  SelectObject(hDCDest, hobjDest);
634  DeleteDC(hDCDest);
635
636  return hbCopy;
637 }
638
639 /**********************************************************************
640  *                              MDICascade
641  */
642 static LONG MDICascade( HWND client, MDICLIENTINFO *ci )
643 {
644     HWND *win_array;
645     BOOL has_icons = FALSE;
646     int i, total;
647
648     if (IsZoomed(ci->hwndActiveChild))
649         SendMessageA(client, WM_MDIRESTORE, (WPARAM)ci->hwndActiveChild, 0);
650
651     if (ci->nActiveChildren == 0) return 0;
652
653     if (!(win_array = WIN_ListChildren( client ))) return 0;
654
655     /* remove all the windows we don't want */
656     for (i = total = 0; win_array[i]; i++)
657     {
658         if (!IsWindowVisible( win_array[i] )) continue;
659         if (GetWindow( win_array[i], GW_OWNER )) continue; /* skip owned windows */
660         if (IsIconic( win_array[i] ))
661         {
662             has_icons = TRUE;
663             continue;
664         }
665         win_array[total++] = win_array[i];
666     }
667     win_array[total] = 0;
668
669     if (total)
670     {
671         INT delta = 0, n = 0, i;
672         POINT pos[2];
673         if (has_icons) delta = GetSystemMetrics(SM_CYICONSPACING) + GetSystemMetrics(SM_CYICON);
674
675         /* walk the list (backwards) and move windows */
676         for (i = total - 1; i >= 0; i--)
677         {
678             TRACE("move %p to (%ld,%ld) size [%ld,%ld]\n",
679                   win_array[i], pos[0].x, pos[0].y, pos[1].x, pos[1].y);
680
681             MDI_CalcDefaultChildPos(client, n++, pos, delta);
682             SetWindowPos( win_array[i], 0, pos[0].x, pos[0].y, pos[1].x, pos[1].y,
683                           SWP_DRAWFRAME | SWP_NOACTIVATE | SWP_NOZORDER);
684         }
685     }
686     HeapFree( GetProcessHeap(), 0, win_array );
687
688     if (has_icons) ArrangeIconicWindows( client );
689     return 0;
690 }
691
692 /**********************************************************************
693  *                                      MDITile
694  */
695 static void MDITile( HWND client, MDICLIENTINFO *ci, WPARAM wParam )
696 {
697     HWND *win_array;
698     int i, total;
699     BOOL has_icons = FALSE;
700
701     if (IsZoomed(ci->hwndActiveChild))
702         SendMessageA(client, WM_MDIRESTORE, (WPARAM)ci->hwndActiveChild, 0);
703
704     if (ci->nActiveChildren == 0) return;
705
706     if (!(win_array = WIN_ListChildren( client ))) return;
707
708     /* remove all the windows we don't want */
709     for (i = total = 0; win_array[i]; i++)
710     {
711         if (!IsWindowVisible( win_array[i] )) continue;
712         if (GetWindow( win_array[i], GW_OWNER )) continue; /* skip owned windows (icon titles) */
713         if (IsIconic( win_array[i] ))
714         {
715             has_icons = TRUE;
716             continue;
717         }
718         if ((wParam & MDITILE_SKIPDISABLED) && !IsWindowEnabled( win_array[i] )) continue;
719         win_array[total++] = win_array[i];
720     }
721     win_array[total] = 0;
722
723     TRACE("%u windows to tile\n", total);
724
725     if (total)
726     {
727         HWND *pWnd = win_array;
728         RECT rect;
729         int x, y, xsize, ysize;
730         int rows, columns, r, c, i;
731
732         GetClientRect(client,&rect);
733         rows    = (int) sqrt((double)total);
734         columns = total / rows;
735
736         if( wParam & MDITILE_HORIZONTAL )  /* version >= 3.1 */
737         {
738             i = rows;
739             rows = columns;  /* exchange r and c */
740             columns = i;
741         }
742
743         if (has_icons)
744         {
745             y = rect.bottom - 2 * GetSystemMetrics(SM_CYICONSPACING) - GetSystemMetrics(SM_CYICON);
746             rect.bottom = ( y - GetSystemMetrics(SM_CYICON) < rect.top )? rect.bottom: y;
747         }
748
749         ysize   = rect.bottom / rows;
750         xsize   = rect.right  / columns;
751
752         for (x = i = 0, c = 1; c <= columns && *pWnd; c++)
753         {
754             if (c == columns)
755             {
756                 rows  = total - i;
757                 ysize = rect.bottom / rows;
758             }
759
760             y = 0;
761             for (r = 1; r <= rows && *pWnd; r++, i++)
762             {
763                 SetWindowPos(*pWnd, 0, x, y, xsize, ysize,
764                              SWP_DRAWFRAME | SWP_NOACTIVATE | SWP_NOZORDER);
765                 y += ysize;
766                 pWnd++;
767             }
768             x += xsize;
769         }
770     }
771     HeapFree( GetProcessHeap(), 0, win_array );
772     if (has_icons) ArrangeIconicWindows( client );
773 }
774
775 /* ----------------------- Frame window ---------------------------- */
776
777
778 /**********************************************************************
779  *                                      MDI_AugmentFrameMenu
780  */
781 static BOOL MDI_AugmentFrameMenu( HWND frame, HWND hChild )
782 {
783     HMENU menu = GetMenu( frame );
784     HMENU       hSysPopup = 0;
785     HBITMAP hSysMenuBitmap = 0;
786     INT nItems;
787     UINT iId;
788     HICON hIcon;
789
790     TRACE("frame %p,child %p\n",frame,hChild);
791
792     if( !menu ) return 0;
793
794     /* if the system buttons already exist do not add them again */
795     nItems = GetMenuItemCount(menu) - 1;
796     iId = GetMenuItemID(menu,nItems) ;
797     if (iId == SC_RESTORE || iId == SC_CLOSE)
798         return 0;
799
800     /* create a copy of sysmenu popup and insert it into frame menu bar */
801     if (!(hSysPopup = GetSystemMenu(hChild, FALSE)))
802         return 0;
803
804     AppendMenuA(menu,MF_HELP | MF_BITMAP,
805                    SC_MINIMIZE, (LPSTR)(DWORD)HBMMENU_MBAR_MINIMIZE ) ;
806     AppendMenuA(menu,MF_HELP | MF_BITMAP,
807                    SC_RESTORE, (LPSTR)(DWORD)HBMMENU_MBAR_RESTORE );
808
809     AppendMenuA(menu,MF_HELP | MF_BITMAP,
810                    SC_CLOSE, (LPSTR)(DWORD)HBMMENU_MBAR_CLOSE );
811
812     /* The system menu is replaced by the child icon */
813     hIcon = (HICON)GetClassLongW(hChild, GCL_HICONSM);
814     if (!hIcon)
815         hIcon = (HICON)GetClassLongW(hChild, GCL_HICON);
816     if (!hIcon)
817         hIcon = LoadImageW(0, MAKEINTRESOURCEW(IDI_WINLOGO), IMAGE_ICON, 0, 0, LR_DEFAULTCOLOR);
818     if (hIcon)
819     {
820       HDC hMemDC;
821       HBITMAP hBitmap, hOldBitmap;
822       HBRUSH hBrush;
823       HDC hdc = GetDC(hChild);
824
825       if (hdc)
826       {
827         int cx, cy;
828         cx = GetSystemMetrics(SM_CXSMICON);
829         cy = GetSystemMetrics(SM_CYSMICON);
830         hMemDC = CreateCompatibleDC(hdc);
831         hBitmap = CreateCompatibleBitmap(hdc, cx, cy);
832         hOldBitmap = SelectObject(hMemDC, hBitmap);
833         SetMapMode(hMemDC, MM_TEXT);
834         hBrush = CreateSolidBrush(GetSysColor(COLOR_MENU));
835         DrawIconEx(hMemDC, 0, 0, hIcon, cx, cy, 0, hBrush, DI_NORMAL);
836         SelectObject (hMemDC, hOldBitmap);
837         DeleteObject(hBrush);
838         DeleteDC(hMemDC);
839         ReleaseDC(hChild, hdc);
840         hSysMenuBitmap = hBitmap;
841       }
842     }
843
844     if( !InsertMenuA(menu,0,MF_BYPOSITION | MF_BITMAP | MF_POPUP,
845                      (UINT_PTR)hSysPopup, (LPSTR)hSysMenuBitmap))
846     {
847         TRACE("not inserted\n");
848         DestroyMenu(hSysPopup);
849         return 0;
850     }
851
852     EnableMenuItem(hSysPopup, SC_SIZE, MF_BYCOMMAND | MF_GRAYED);
853     EnableMenuItem(hSysPopup, SC_MOVE, MF_BYCOMMAND | MF_GRAYED);
854     EnableMenuItem(hSysPopup, SC_MAXIMIZE, MF_BYCOMMAND | MF_GRAYED);
855     SetMenuDefaultItem(hSysPopup, SC_CLOSE, FALSE);
856
857     /* redraw menu */
858     DrawMenuBar(frame);
859
860     return 1;
861 }
862
863 /**********************************************************************
864  *                                      MDI_RestoreFrameMenu
865  */
866 static BOOL MDI_RestoreFrameMenu( HWND frame, HWND hChild )
867 {
868     MENUITEMINFOW menuInfo;
869     HMENU menu = GetMenu( frame );
870     INT nItems = GetMenuItemCount(menu) - 1;
871     UINT iId = GetMenuItemID(menu,nItems) ;
872
873     TRACE("frame %p,child %p,nIt=%d,iId=%d\n",frame,hChild,nItems,iId);
874
875     /* if there is no system buttons then nothing to do */
876     if(!(iId == SC_RESTORE || iId == SC_CLOSE) )
877         return 0;
878
879     /*
880      * Remove the system menu, If that menu is the icon of the window
881      * as it is in win95, we have to delete the bitmap.
882      */
883     memset(&menuInfo, 0, sizeof(menuInfo));
884     menuInfo.cbSize = sizeof(menuInfo);
885     menuInfo.fMask  = MIIM_DATA | MIIM_TYPE;
886
887     GetMenuItemInfoW(menu,
888                      0,
889                      TRUE,
890                      &menuInfo);
891
892     RemoveMenu(menu,0,MF_BYPOSITION);
893
894     if ( (menuInfo.fType & MFT_BITMAP)           &&
895          (LOWORD(menuInfo.dwTypeData)!=0)        &&
896          (LOWORD(menuInfo.dwTypeData)!=HBITMAP_16(hBmpClose)) )
897     {
898         DeleteObject(HBITMAP_32(LOWORD(menuInfo.dwTypeData)));
899     }
900
901     /* close */
902     DeleteMenu(menu, SC_CLOSE, MF_BYCOMMAND);
903     /* restore */
904     DeleteMenu(menu, SC_RESTORE, MF_BYCOMMAND);
905     /* minimize */
906     DeleteMenu(menu, SC_MINIMIZE, MF_BYCOMMAND);
907
908     DrawMenuBar(frame);
909
910     return 1;
911 }
912
913
914 /**********************************************************************
915  *                                      MDI_UpdateFrameText
916  *
917  * used when child window is maximized/restored
918  *
919  * Note: lpTitle can be NULL
920  */
921 static void MDI_UpdateFrameText( HWND frame, HWND hClient, LPCWSTR lpTitle )
922 {
923     WCHAR   lpBuffer[MDI_MAXTITLELENGTH+1];
924     MDICLIENTINFO *ci = get_client_info( hClient );
925
926     TRACE("frameText %s\n", debugstr_w(lpTitle));
927
928     if (!ci) return;
929
930     if (!lpTitle && !ci->frameTitle)  /* first time around, get title from the frame window */
931     {
932         GetWindowTextW( frame, lpBuffer, sizeof(lpBuffer)/sizeof(WCHAR) );
933         lpTitle = lpBuffer;
934     }
935
936     /* store new "default" title if lpTitle is not NULL */
937     if (lpTitle)
938     {
939         if (ci->frameTitle) HeapFree( GetProcessHeap(), 0, ci->frameTitle );
940         if ((ci->frameTitle = HeapAlloc( GetProcessHeap(), 0, (strlenW(lpTitle)+1)*sizeof(WCHAR))))
941             strcpyW( ci->frameTitle, lpTitle );
942     }
943
944     if (ci->frameTitle)
945     {
946         if (IsZoomed(ci->hwndActiveChild) && IsWindowVisible(ci->hwndActiveChild))
947         {
948             /* combine frame title and child title if possible */
949
950             static const WCHAR lpBracket[]  = {' ','-',' ','[',0};
951             static const WCHAR lpBracket2[]  = {']',0};
952             int i_frame_text_length = strlenW(ci->frameTitle);
953
954             lstrcpynW( lpBuffer, ci->frameTitle, MDI_MAXTITLELENGTH);
955
956             if( i_frame_text_length + 6 < MDI_MAXTITLELENGTH )
957             {
958                 strcatW( lpBuffer, lpBracket );
959                 if (GetWindowTextW( ci->hwndActiveChild, lpBuffer + i_frame_text_length + 4,
960                                     MDI_MAXTITLELENGTH - i_frame_text_length - 5 ))
961                     strcatW( lpBuffer, lpBracket2 );
962                 else
963                     lpBuffer[i_frame_text_length] = 0;  /* remove bracket */
964             }
965         }
966         else
967         {
968             lstrcpynW(lpBuffer, ci->frameTitle, MDI_MAXTITLELENGTH+1 );
969         }
970     }
971     else
972         lpBuffer[0] = '\0';
973
974     DefWindowProcW( frame, WM_SETTEXT, 0, (LPARAM)lpBuffer );
975 }
976
977
978 /* ----------------------------- Interface ---------------------------- */
979
980
981 /**********************************************************************
982  *              MDIClientWndProc_common
983  */
984 static LRESULT MDIClientWndProc_common( HWND hwnd, UINT message,
985                                         WPARAM wParam, LPARAM lParam, BOOL unicode )
986 {
987     MDICLIENTINFO *ci;
988
989     TRACE("%p %04x (%s) %08x %08lx\n", hwnd, message, SPY_GetMsgName(message, hwnd), wParam, lParam);
990
991     if (!(ci = get_client_info( hwnd ))) return 0;
992
993     switch (message)
994     {
995       case WM_CREATE:
996       {
997           /* Since we are using only cs->lpCreateParams, we can safely
998            * cast to LPCREATESTRUCTA here */
999           LPCREATESTRUCTA cs = (LPCREATESTRUCTA)lParam;
1000           WND *wndPtr = WIN_GetPtr( hwnd );
1001
1002           wndPtr->flags |= WIN_ISMDICLIENT;
1003
1004         /* Translation layer doesn't know what's in the cs->lpCreateParams
1005          * so we have to keep track of what environment we're in. */
1006
1007         if( wndPtr->flags & WIN_ISWIN32 )
1008         {
1009             LPCLIENTCREATESTRUCT ccs = cs->lpCreateParams;
1010             ci->hWindowMenu     = ccs->hWindowMenu;
1011             ci->idFirstChild    = ccs->idFirstChild;
1012         }
1013         else
1014         {
1015             LPCLIENTCREATESTRUCT16 ccs = MapSL((SEGPTR)cs->lpCreateParams);
1016             ci->hWindowMenu     = HMENU_32(ccs->hWindowMenu);
1017             ci->idFirstChild    = ccs->idFirstChild;
1018         }
1019         WIN_ReleasePtr( wndPtr );
1020
1021         ci->child = NULL;
1022         ci->nActiveChildren     = 0;
1023         ci->nTotalCreated       = 0;
1024         ci->frameTitle          = NULL;
1025         ci->mdiFlags            = 0;
1026         ci->hFrameMenu = GetMenu(cs->hwndParent);
1027
1028         if (!hBmpClose) hBmpClose = CreateMDIMenuBitmap();
1029
1030         TRACE("Client created: hwnd %p, Window menu %p, idFirst = %u\n",
1031               hwnd, ci->hWindowMenu, ci->idFirstChild );
1032         return 0;
1033       }
1034
1035       case WM_DESTROY:
1036       {
1037           if( IsZoomed(ci->hwndActiveChild) )
1038               MDI_RestoreFrameMenu(GetParent(hwnd), ci->hwndActiveChild);
1039
1040           ci->nActiveChildren = 0;
1041           MDI_RefreshMenu(ci);
1042
1043           if (ci->child) HeapFree( GetProcessHeap(), 0, ci->child );
1044           if (ci->frameTitle) HeapFree( GetProcessHeap(), 0, ci->frameTitle );
1045
1046           return 0;
1047       }
1048
1049       case WM_MDIACTIVATE:
1050       {
1051         MDI_SwitchActiveChild( ci, (HWND)wParam );
1052         return 0;
1053       }
1054
1055       case WM_MDICASCADE:
1056         return MDICascade(hwnd, ci);
1057
1058       case WM_MDICREATE:
1059         if (lParam)
1060         {
1061             if (unicode)
1062             {
1063                 MDICREATESTRUCTW *csW = (MDICREATESTRUCTW *)lParam;
1064                 return (LRESULT)CreateWindowExW(WS_EX_MDICHILD, csW->szClass,
1065                                             csW->szTitle, csW->style,
1066                                             csW->x, csW->y, csW->cx, csW->cy,
1067                                             hwnd, 0, csW->hOwner,
1068                                             (LPVOID)csW->lParam);
1069             }
1070             else
1071             {
1072                 MDICREATESTRUCTA *csA = (MDICREATESTRUCTA *)lParam;
1073                 return (LRESULT)CreateWindowExA(WS_EX_MDICHILD, csA->szClass,
1074                                             csA->szTitle, csA->style,
1075                                             csA->x, csA->y, csA->cx, csA->cy,
1076                                             hwnd, 0, csA->hOwner,
1077                                             (LPVOID)csA->lParam);
1078             }
1079         }
1080         return 0;
1081
1082       case WM_MDIDESTROY:
1083           return MDIDestroyChild( hwnd, ci, WIN_GetFullHandle( (HWND)wParam ), TRUE );
1084
1085       case WM_MDIGETACTIVE:
1086           if (lParam) *(BOOL *)lParam = IsZoomed(ci->hwndActiveChild);
1087           return (LRESULT)ci->hwndActiveChild;
1088
1089       case WM_MDIICONARRANGE:
1090         ci->mdiFlags |= MDIF_NEEDUPDATE;
1091         ArrangeIconicWindows( hwnd );
1092         ci->sbRecalc = SB_BOTH+1;
1093         SendMessageW( hwnd, WM_MDICALCCHILDSCROLL, 0, 0 );
1094         return 0;
1095
1096       case WM_MDIMAXIMIZE:
1097         ShowWindow( (HWND)wParam, SW_MAXIMIZE );
1098         return 0;
1099
1100       case WM_MDINEXT: /* lParam != 0 means previous window */
1101       {
1102         HWND next = MDI_GetWindow( ci, WIN_GetFullHandle( (HWND)wParam ), !lParam, 0 );
1103         MDI_SwitchActiveChild( ci, next );
1104         break;
1105       }
1106
1107       case WM_MDIRESTORE:
1108         SendMessageW( (HWND)wParam, WM_SYSCOMMAND, SC_RESTORE, 0);
1109         return 0;
1110
1111       case WM_MDISETMENU:
1112           return MDISetMenu( hwnd, (HMENU)wParam, (HMENU)lParam );
1113
1114       case WM_MDIREFRESHMENU:
1115           return MDI_RefreshMenu( ci );
1116
1117       case WM_MDITILE:
1118         ci->mdiFlags |= MDIF_NEEDUPDATE;
1119         ShowScrollBar( hwnd, SB_BOTH, FALSE );
1120         MDITile( hwnd, ci, wParam );
1121         ci->mdiFlags &= ~MDIF_NEEDUPDATE;
1122         return 0;
1123
1124       case WM_VSCROLL:
1125       case WM_HSCROLL:
1126         ci->mdiFlags |= MDIF_NEEDUPDATE;
1127         ScrollChildren( hwnd, message, wParam, lParam );
1128         ci->mdiFlags &= ~MDIF_NEEDUPDATE;
1129         return 0;
1130
1131       case WM_SETFOCUS:
1132           if (ci->hwndActiveChild && !IsIconic( ci->hwndActiveChild ))
1133               SetFocus( ci->hwndActiveChild );
1134           return 0;
1135
1136       case WM_NCACTIVATE:
1137         if( ci->hwndActiveChild )
1138             SendMessageW(ci->hwndActiveChild, message, wParam, lParam);
1139         break;
1140
1141       case WM_PARENTNOTIFY:
1142         switch (LOWORD(wParam))
1143         {
1144         case WM_CREATE:
1145             if (GetWindowLongW((HWND)lParam, GWL_EXSTYLE) & WS_EX_MDICHILD)
1146             {
1147                 ci->nTotalCreated++;
1148                 ci->nActiveChildren++;
1149
1150                 if (!ci->child)
1151                     ci->child = HeapAlloc(GetProcessHeap(), 0, sizeof(HWND));
1152                 else
1153                     ci->child = HeapReAlloc(GetProcessHeap(), 0, ci->child, sizeof(HWND) * ci->nActiveChildren);
1154
1155                 TRACE("Adding MDI child %p, # of children %d\n",
1156                       (HWND)lParam, ci->nActiveChildren);
1157
1158                 ci->child[ci->nActiveChildren - 1] = (HWND)lParam;
1159             }
1160             break;
1161
1162         case WM_LBUTTONDOWN:
1163             {
1164             HWND child;
1165             POINT pt;
1166             pt.x = (short)LOWORD(lParam);
1167             pt.y = (short)HIWORD(lParam);
1168             child = ChildWindowFromPoint(hwnd, pt);
1169
1170             TRACE("notification from %p (%li,%li)\n",child,pt.x,pt.y);
1171
1172             if( child && child != hwnd && child != ci->hwndActiveChild )
1173                 SetWindowPos(child, 0,0,0,0,0, SWP_NOSIZE | SWP_NOMOVE );
1174             break;
1175             }
1176         }
1177         return 0;
1178
1179       case WM_SIZE:
1180         if( IsWindow(ci->hwndActiveChild) && IsZoomed(ci->hwndActiveChild) &&
1181             IsWindowVisible(ci->hwndActiveChild) )
1182         {
1183             RECT        rect;
1184
1185             rect.left = 0;
1186             rect.top = 0;
1187             rect.right = LOWORD(lParam);
1188             rect.bottom = HIWORD(lParam);
1189
1190             AdjustWindowRectEx(&rect, GetWindowLongA(ci->hwndActiveChild, GWL_STYLE),
1191                                0, GetWindowLongA(ci->hwndActiveChild, GWL_EXSTYLE) );
1192             MoveWindow(ci->hwndActiveChild, rect.left, rect.top,
1193                          rect.right - rect.left, rect.bottom - rect.top, 1);
1194         }
1195         else
1196             MDI_PostUpdate(hwnd, ci, SB_BOTH+1);
1197
1198         break;
1199
1200       case WM_MDICALCCHILDSCROLL:
1201         if( (ci->mdiFlags & MDIF_NEEDUPDATE) && ci->sbRecalc )
1202         {
1203             CalcChildScroll(hwnd, ci->sbRecalc-1);
1204             ci->sbRecalc = 0;
1205             ci->mdiFlags &= ~MDIF_NEEDUPDATE;
1206         }
1207         return 0;
1208     }
1209     return unicode ? DefWindowProcW( hwnd, message, wParam, lParam ) :
1210                      DefWindowProcA( hwnd, message, wParam, lParam );
1211 }
1212
1213 /***********************************************************************
1214  *              MDIClientWndProcA
1215  */
1216 static LRESULT WINAPI MDIClientWndProcA( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
1217 {
1218     if (!IsWindow(hwnd)) return 0;
1219     return MDIClientWndProc_common( hwnd, message, wParam, lParam, FALSE );
1220 }
1221
1222 /***********************************************************************
1223  *              MDIClientWndProcW
1224  */
1225 static LRESULT WINAPI MDIClientWndProcW( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
1226 {
1227     if (!IsWindow(hwnd)) return 0;
1228     return MDIClientWndProc_common( hwnd, message, wParam, lParam, TRUE );
1229 }
1230
1231 /***********************************************************************
1232  *              DefFrameProcA (USER32.@)
1233  */
1234 LRESULT WINAPI DefFrameProcA( HWND hwnd, HWND hwndMDIClient,
1235                                 UINT message, WPARAM wParam, LPARAM lParam)
1236 {
1237     if (hwndMDIClient)
1238     {
1239         switch (message)
1240         {
1241         case WM_SETTEXT:
1242             {
1243                 DWORD len = MultiByteToWideChar( CP_ACP, 0, (LPSTR)lParam, -1, NULL, 0 );
1244                 LPWSTR text = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
1245                 MultiByteToWideChar( CP_ACP, 0, (LPSTR)lParam, -1, text, len );
1246                 MDI_UpdateFrameText( hwnd, hwndMDIClient, text );
1247                 HeapFree( GetProcessHeap(), 0, text );
1248             }
1249             return 1; /* success. FIXME: check text length */
1250
1251         case WM_COMMAND:
1252         case WM_NCACTIVATE:
1253         case WM_NEXTMENU:
1254         case WM_SETFOCUS:
1255         case WM_SIZE:
1256             return DefFrameProcW( hwnd, hwndMDIClient, message, wParam, lParam );
1257         }
1258     }
1259     return DefWindowProcA(hwnd, message, wParam, lParam);
1260 }
1261
1262
1263 /***********************************************************************
1264  *              DefFrameProcW (USER32.@)
1265  */
1266 LRESULT WINAPI DefFrameProcW( HWND hwnd, HWND hwndMDIClient,
1267                                 UINT message, WPARAM wParam, LPARAM lParam)
1268 {
1269     MDICLIENTINFO *ci = get_client_info( hwndMDIClient );
1270
1271     TRACE("%p %p %04x (%s) %08x %08lx\n", hwnd, hwndMDIClient, message, SPY_GetMsgName(message, hwnd), wParam, lParam);
1272
1273     if (ci)
1274     {
1275         switch (message)
1276         {
1277         case WM_COMMAND:
1278             {
1279                 WORD id = LOWORD(wParam);
1280                 /* check for possible syscommands for maximized MDI child */
1281                 if (id <  ci->idFirstChild || id >= ci->idFirstChild + ci->nActiveChildren)
1282                 {
1283                     if( (id - 0xf000) & 0xf00f ) break;
1284                     if( !IsZoomed(ci->hwndActiveChild) ) break;
1285                     switch( id )
1286                     {
1287                     case SC_SIZE:
1288                     case SC_MOVE:
1289                     case SC_MINIMIZE:
1290                     case SC_MAXIMIZE:
1291                     case SC_NEXTWINDOW:
1292                     case SC_PREVWINDOW:
1293                     case SC_CLOSE:
1294                     case SC_RESTORE:
1295                         return SendMessageW( ci->hwndActiveChild, WM_SYSCOMMAND,
1296                                              wParam, lParam);
1297                     }
1298                 }
1299                 else
1300                 {
1301                     HWND childHwnd;
1302                     if (id - ci->idFirstChild == MDI_MOREWINDOWSLIMIT)
1303                         /* User chose "More Windows..." */
1304                         childHwnd = MDI_MoreWindowsDialog(hwndMDIClient);
1305                     else
1306                         /* User chose one of the windows listed in the "Windows" menu */
1307                         childHwnd = MDI_GetChildByID(hwndMDIClient, id, ci);
1308
1309                     if( childHwnd )
1310                         SendMessageW( hwndMDIClient, WM_MDIACTIVATE, (WPARAM)childHwnd, 0 );
1311                 }
1312             }
1313             break;
1314
1315         case WM_NCACTIVATE:
1316             SendMessageW(hwndMDIClient, message, wParam, lParam);
1317             break;
1318
1319         case WM_SETTEXT:
1320             MDI_UpdateFrameText( hwnd, hwndMDIClient, (LPWSTR)lParam );
1321             return 1; /* success. FIXME: check text length */
1322
1323         case WM_SETFOCUS:
1324             SetFocus(hwndMDIClient);
1325             break;
1326
1327         case WM_SIZE:
1328             MoveWindow(hwndMDIClient, 0, 0, LOWORD(lParam), HIWORD(lParam), TRUE);
1329             break;
1330
1331         case WM_NEXTMENU:
1332             {
1333                 MDINEXTMENU *next_menu = (MDINEXTMENU *)lParam;
1334
1335                 if (!IsIconic(hwnd) && ci->hwndActiveChild && !IsZoomed(ci->hwndActiveChild))
1336                 {
1337                     /* control menu is between the frame system menu and
1338                      * the first entry of menu bar */
1339                     WND *wndPtr = WIN_GetPtr(hwnd);
1340
1341                     if( (wParam == VK_LEFT && GetMenu(hwnd) == next_menu->hmenuIn) ||
1342                         (wParam == VK_RIGHT && GetSubMenu(wndPtr->hSysMenu, 0) == next_menu->hmenuIn) )
1343                     {
1344                         WIN_ReleasePtr(wndPtr);
1345                         wndPtr = WIN_GetPtr(ci->hwndActiveChild);
1346                         next_menu->hmenuNext = GetSubMenu(wndPtr->hSysMenu, 0);
1347                         next_menu->hwndNext = ci->hwndActiveChild;
1348                     }
1349                     WIN_ReleasePtr(wndPtr);
1350                 }
1351                 return 0;
1352             }
1353         }
1354     }
1355
1356     return DefWindowProcW( hwnd, message, wParam, lParam );
1357 }
1358
1359 /***********************************************************************
1360  *              DefMDIChildProcA (USER32.@)
1361  */
1362 LRESULT WINAPI DefMDIChildProcA( HWND hwnd, UINT message,
1363                                    WPARAM wParam, LPARAM lParam )
1364 {
1365     HWND client = GetParent(hwnd);
1366     MDICLIENTINFO *ci = get_client_info( client );
1367
1368     TRACE("%p %04x (%s) %08x %08lx\n", hwnd, message, SPY_GetMsgName(message, hwnd), wParam, lParam);
1369
1370     hwnd = WIN_GetFullHandle( hwnd );
1371     if (!ci) return DefWindowProcA( hwnd, message, wParam, lParam );
1372
1373     switch (message)
1374     {
1375     case WM_SETTEXT:
1376         DefWindowProcA(hwnd, message, wParam, lParam);
1377         if( ci->hwndActiveChild == hwnd && IsZoomed(ci->hwndActiveChild) )
1378             MDI_UpdateFrameText( GetParent(client), client, NULL );
1379         return 1; /* success. FIXME: check text length */
1380
1381     case WM_GETMINMAXINFO:
1382     case WM_MENUCHAR:
1383     case WM_CLOSE:
1384     case WM_SETFOCUS:
1385     case WM_CHILDACTIVATE:
1386     case WM_SYSCOMMAND:
1387     case WM_SHOWWINDOW:
1388     case WM_SETVISIBLE:
1389     case WM_SIZE:
1390     case WM_NEXTMENU:
1391     case WM_SYSCHAR:
1392     case WM_DESTROY:
1393         return DefMDIChildProcW( hwnd, message, wParam, lParam );
1394     }
1395     return DefWindowProcA(hwnd, message, wParam, lParam);
1396 }
1397
1398
1399 /***********************************************************************
1400  *              DefMDIChildProcW (USER32.@)
1401  */
1402 LRESULT WINAPI DefMDIChildProcW( HWND hwnd, UINT message,
1403                                    WPARAM wParam, LPARAM lParam )
1404 {
1405     HWND client = GetParent(hwnd);
1406     MDICLIENTINFO *ci = get_client_info( client );
1407
1408     TRACE("%p %04x (%s) %08x %08lx\n", hwnd, message, SPY_GetMsgName(message, hwnd), wParam, lParam);
1409
1410     hwnd = WIN_GetFullHandle( hwnd );
1411     if (!ci) return DefWindowProcW( hwnd, message, wParam, lParam );
1412
1413     switch (message)
1414     {
1415     case WM_SETTEXT:
1416         DefWindowProcW(hwnd, message, wParam, lParam);
1417         if( ci->hwndActiveChild == hwnd && IsZoomed(ci->hwndActiveChild) )
1418             MDI_UpdateFrameText( GetParent(client), client, NULL );
1419         return 1; /* success. FIXME: check text length */
1420
1421     case WM_GETMINMAXINFO:
1422         MDI_ChildGetMinMaxInfo( client, hwnd, (MINMAXINFO *)lParam );
1423         return 0;
1424
1425     case WM_MENUCHAR:
1426         return 0x00010000; /* MDI children don't have menu bars */
1427
1428     case WM_CLOSE:
1429         SendMessageW( client, WM_MDIDESTROY, (WPARAM)hwnd, 0 );
1430         return 0;
1431
1432     case WM_CHILDACTIVATE:
1433         MDI_ChildActivate( client, hwnd );
1434         return 0;
1435
1436     case WM_SYSCOMMAND:
1437         switch( wParam )
1438         {
1439         case SC_MOVE:
1440             if( ci->hwndActiveChild == hwnd && IsZoomed(ci->hwndActiveChild))
1441                 return 0;
1442             break;
1443         case SC_RESTORE:
1444         case SC_MINIMIZE:
1445             break;
1446         case SC_MAXIMIZE:
1447             if (ci->hwndActiveChild == hwnd && IsZoomed(ci->hwndActiveChild))
1448                 return SendMessageW( GetParent(client), message, wParam, lParam);
1449             break;
1450         case SC_NEXTWINDOW:
1451             SendMessageW( client, WM_MDINEXT, 0, 0);
1452             return 0;
1453         case SC_PREVWINDOW:
1454             SendMessageW( client, WM_MDINEXT, 0, 1);
1455             return 0;
1456         }
1457         break;
1458
1459     case WM_SHOWWINDOW:
1460     case WM_SETVISIBLE:
1461         if (IsZoomed(ci->hwndActiveChild)) ci->mdiFlags &= ~MDIF_NEEDUPDATE;
1462         else MDI_PostUpdate(client, ci, SB_BOTH+1);
1463         break;
1464
1465     case WM_SIZE:
1466         if( hwnd == ci->hwndActiveChild )
1467         {
1468             if( wParam == SIZE_MAXIMIZED )
1469             {
1470                 TRACE("maximizing child %p\n", hwnd );
1471
1472                 MDI_AugmentFrameMenu( GetParent(client), hwnd );
1473             }
1474             else
1475                 MDI_RestoreFrameMenu( GetParent(client), hwnd );
1476         }
1477
1478         MDI_UpdateFrameText( GetParent(client), client, NULL );
1479         MDI_RefreshMenu(ci);
1480         MDI_PostUpdate(client, ci, SB_BOTH+1);
1481         break;
1482
1483     case WM_NEXTMENU:
1484         {
1485             MDINEXTMENU *next_menu = (MDINEXTMENU *)lParam;
1486             HWND parent = GetParent(client);
1487
1488             if( wParam == VK_LEFT )  /* switch to frame system menu */
1489             {
1490                 WND *wndPtr = WIN_GetPtr( parent );
1491                 next_menu->hmenuNext = GetSubMenu( wndPtr->hSysMenu, 0 );
1492                 WIN_ReleasePtr( wndPtr );
1493             }
1494             if( wParam == VK_RIGHT )  /* to frame menu bar */
1495             {
1496                 next_menu->hmenuNext = GetMenu(parent);
1497             }
1498             next_menu->hwndNext = parent;
1499             return 0;
1500         }
1501
1502     case WM_SYSCHAR:
1503         if (wParam == '-')
1504         {
1505             SendMessageW( hwnd, WM_SYSCOMMAND, (WPARAM)SC_KEYMENU, (DWORD)VK_SPACE);
1506             return 0;
1507         }
1508         break;
1509
1510     case WM_DESTROY:
1511         /* Remove itself from the Window menu */
1512         MDI_RefreshMenu(ci);
1513         break;
1514     }
1515     return DefWindowProcW(hwnd, message, wParam, lParam);
1516 }
1517
1518 /**********************************************************************
1519  *              CreateMDIWindowA (USER32.@) Creates a MDI child
1520  *
1521  * RETURNS
1522  *    Success: Handle to created window
1523  *    Failure: NULL
1524  */
1525 HWND WINAPI CreateMDIWindowA(
1526     LPCSTR lpClassName,    /* [in] Pointer to registered child class name */
1527     LPCSTR lpWindowName,   /* [in] Pointer to window name */
1528     DWORD dwStyle,         /* [in] Window style */
1529     INT X,               /* [in] Horizontal position of window */
1530     INT Y,               /* [in] Vertical position of window */
1531     INT nWidth,          /* [in] Width of window */
1532     INT nHeight,         /* [in] Height of window */
1533     HWND hWndParent,     /* [in] Handle to parent window */
1534     HINSTANCE hInstance, /* [in] Handle to application instance */
1535     LPARAM lParam)         /* [in] Application-defined value */
1536 {
1537     TRACE("(%s,%s,%08lx,%d,%d,%d,%d,%p,%p,%08lx)\n",
1538           debugstr_a(lpClassName),debugstr_a(lpWindowName),dwStyle,X,Y,
1539           nWidth,nHeight,hWndParent,hInstance,lParam);
1540
1541     return CreateWindowExA(WS_EX_MDICHILD, lpClassName, lpWindowName,
1542                            dwStyle, X, Y, nWidth, nHeight, hWndParent,
1543                            0, hInstance, (LPVOID)lParam);
1544 }
1545
1546 /***********************************************************************
1547  *              CreateMDIWindowW (USER32.@) Creates a MDI child
1548  *
1549  * RETURNS
1550  *    Success: Handle to created window
1551  *    Failure: NULL
1552  */
1553 HWND WINAPI CreateMDIWindowW(
1554     LPCWSTR lpClassName,    /* [in] Pointer to registered child class name */
1555     LPCWSTR lpWindowName,   /* [in] Pointer to window name */
1556     DWORD dwStyle,         /* [in] Window style */
1557     INT X,               /* [in] Horizontal position of window */
1558     INT Y,               /* [in] Vertical position of window */
1559     INT nWidth,          /* [in] Width of window */
1560     INT nHeight,         /* [in] Height of window */
1561     HWND hWndParent,     /* [in] Handle to parent window */
1562     HINSTANCE hInstance, /* [in] Handle to application instance */
1563     LPARAM lParam)         /* [in] Application-defined value */
1564 {
1565     TRACE("(%s,%s,%08lx,%d,%d,%d,%d,%p,%p,%08lx)\n",
1566           debugstr_w(lpClassName), debugstr_w(lpWindowName), dwStyle, X, Y,
1567           nWidth, nHeight, hWndParent, hInstance, lParam);
1568
1569     return CreateWindowExW(WS_EX_MDICHILD, lpClassName, lpWindowName,
1570                            dwStyle, X, Y, nWidth, nHeight, hWndParent,
1571                            0, hInstance, (LPVOID)lParam);
1572 }
1573
1574 /**********************************************************************
1575  *              TranslateMDISysAccel (USER32.@)
1576  */
1577 BOOL WINAPI TranslateMDISysAccel( HWND hwndClient, LPMSG msg )
1578 {
1579     if (msg->message == WM_KEYDOWN || msg->message == WM_SYSKEYDOWN)
1580     {
1581         MDICLIENTINFO *ci = get_client_info( hwndClient );
1582         WPARAM wParam = 0;
1583
1584         if (!ci || !IsWindowEnabled(ci->hwndActiveChild)) return 0;
1585
1586         /* translate if the Ctrl key is down and Alt not. */
1587
1588         if( (GetKeyState(VK_CONTROL) & 0x8000) && !(GetKeyState(VK_MENU) & 0x8000))
1589         {
1590             switch( msg->wParam )
1591             {
1592             case VK_F6:
1593             case VK_TAB:
1594                 wParam = ( GetKeyState(VK_SHIFT) & 0x8000 ) ? SC_NEXTWINDOW : SC_PREVWINDOW;
1595                 break;
1596             case VK_F4:
1597             case VK_RBUTTON:
1598                 wParam = SC_CLOSE;
1599                 break;
1600             default:
1601                 return 0;
1602             }
1603             TRACE("wParam = %04x\n", wParam);
1604             SendMessageW(ci->hwndActiveChild, WM_SYSCOMMAND, wParam, (LPARAM)msg->wParam);
1605             return 1;
1606         }
1607     }
1608     return 0; /* failure */
1609 }
1610
1611 /***********************************************************************
1612  *              CalcChildScroll (USER32.@)
1613  */
1614 void WINAPI CalcChildScroll( HWND hwnd, INT scroll )
1615 {
1616     SCROLLINFO info;
1617     RECT childRect, clientRect;
1618     HWND *list;
1619
1620     GetClientRect( hwnd, &clientRect );
1621     SetRectEmpty( &childRect );
1622
1623     if ((list = WIN_ListChildren( hwnd )))
1624     {
1625         int i;
1626         for (i = 0; list[i]; i++)
1627         {
1628             DWORD style = GetWindowLongW( list[i], GWL_STYLE );
1629             if (style & WS_MAXIMIZE)
1630             {
1631                 HeapFree( GetProcessHeap(), 0, list );
1632                 ShowScrollBar( hwnd, SB_BOTH, FALSE );
1633                 return;
1634             }
1635             if (style & WS_VISIBLE)
1636             {
1637                 WND *pWnd = WIN_FindWndPtr( list[i] );
1638                 UnionRect( &childRect, &pWnd->rectWindow, &childRect );
1639                 WIN_ReleaseWndPtr( pWnd );
1640             }
1641         }
1642         HeapFree( GetProcessHeap(), 0, list );
1643     }
1644     UnionRect( &childRect, &clientRect, &childRect );
1645
1646     /* set common info values */
1647     info.cbSize = sizeof(info);
1648     info.fMask = SIF_POS | SIF_RANGE;
1649
1650     /* set the specific */
1651     switch( scroll )
1652     {
1653         case SB_BOTH:
1654         case SB_HORZ:
1655                         info.nMin = childRect.left;
1656                         info.nMax = childRect.right - clientRect.right;
1657                         info.nPos = clientRect.left - childRect.left;
1658                         SetScrollInfo(hwnd, scroll, &info, TRUE);
1659                         if (scroll == SB_HORZ) break;
1660                         /* fall through */
1661         case SB_VERT:
1662                         info.nMin = childRect.top;
1663                         info.nMax = childRect.bottom - clientRect.bottom;
1664                         info.nPos = clientRect.top - childRect.top;
1665                         SetScrollInfo(hwnd, scroll, &info, TRUE);
1666                         break;
1667     }
1668 }
1669
1670
1671 /***********************************************************************
1672  *              ScrollChildren (USER32.@)
1673  */
1674 void WINAPI ScrollChildren(HWND hWnd, UINT uMsg, WPARAM wParam,
1675                              LPARAM lParam)
1676 {
1677     INT newPos = -1;
1678     INT curPos, length, minPos, maxPos, shift;
1679     RECT rect;
1680
1681     GetClientRect( hWnd, &rect );
1682
1683     switch(uMsg)
1684     {
1685     case WM_HSCROLL:
1686         GetScrollRange(hWnd,SB_HORZ,&minPos,&maxPos);
1687         curPos = GetScrollPos(hWnd,SB_HORZ);
1688         length = (rect.right - rect.left) / 2;
1689         shift = GetSystemMetrics(SM_CYHSCROLL);
1690         break;
1691     case WM_VSCROLL:
1692         GetScrollRange(hWnd,SB_VERT,&minPos,&maxPos);
1693         curPos = GetScrollPos(hWnd,SB_VERT);
1694         length = (rect.bottom - rect.top) / 2;
1695         shift = GetSystemMetrics(SM_CXVSCROLL);
1696         break;
1697     default:
1698         return;
1699     }
1700
1701     switch( wParam )
1702     {
1703         case SB_LINEUP:
1704                         newPos = curPos - shift;
1705                         break;
1706         case SB_LINEDOWN:
1707                         newPos = curPos + shift;
1708                         break;
1709         case SB_PAGEUP:
1710                         newPos = curPos - length;
1711                         break;
1712         case SB_PAGEDOWN:
1713                         newPos = curPos + length;
1714                         break;
1715
1716         case SB_THUMBPOSITION:
1717                         newPos = LOWORD(lParam);
1718                         break;
1719
1720         case SB_THUMBTRACK:
1721                         return;
1722
1723         case SB_TOP:
1724                         newPos = minPos;
1725                         break;
1726         case SB_BOTTOM:
1727                         newPos = maxPos;
1728                         break;
1729         case SB_ENDSCROLL:
1730                         CalcChildScroll(hWnd,(uMsg == WM_VSCROLL)?SB_VERT:SB_HORZ);
1731                         return;
1732     }
1733
1734     if( newPos > maxPos )
1735         newPos = maxPos;
1736     else
1737         if( newPos < minPos )
1738             newPos = minPos;
1739
1740     SetScrollPos(hWnd, (uMsg == WM_VSCROLL)?SB_VERT:SB_HORZ , newPos, TRUE);
1741
1742     if( uMsg == WM_VSCROLL )
1743         ScrollWindowEx(hWnd ,0 ,curPos - newPos, NULL, NULL, 0, NULL,
1744                         SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
1745     else
1746         ScrollWindowEx(hWnd ,curPos - newPos, 0, NULL, NULL, 0, NULL,
1747                         SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
1748 }
1749
1750
1751 /******************************************************************************
1752  *              CascadeWindows (USER32.@) Cascades MDI child windows
1753  *
1754  * RETURNS
1755  *    Success: Number of cascaded windows.
1756  *    Failure: 0
1757  */
1758 WORD WINAPI
1759 CascadeWindows (HWND hwndParent, UINT wFlags, const LPRECT lpRect,
1760                 UINT cKids, const HWND *lpKids)
1761 {
1762     FIXME("(%p,0x%08x,...,%u,...): stub\n", hwndParent, wFlags, cKids);
1763     return 0;
1764 }
1765
1766
1767 /******************************************************************************
1768  *              TileWindows (USER32.@) Tiles MDI child windows
1769  *
1770  * RETURNS
1771  *    Success: Number of tiled windows.
1772  *    Failure: 0
1773  */
1774 WORD WINAPI
1775 TileWindows (HWND hwndParent, UINT wFlags, const LPRECT lpRect,
1776              UINT cKids, const HWND *lpKids)
1777 {
1778     FIXME("(%p,0x%08x,...,%u,...): stub\n", hwndParent, wFlags, cKids);
1779     return 0;
1780 }
1781
1782 /************************************************************************
1783  *              "More Windows..." functionality
1784  */
1785
1786 /*              MDI_MoreWindowsDlgProc
1787  *
1788  *    This function will process the messages sent to the "More Windows..."
1789  *    dialog.
1790  *    Return values:  0    = cancel pressed
1791  *                    HWND = ok pressed or double-click in the list...
1792  *
1793  */
1794
1795 static INT_PTR WINAPI MDI_MoreWindowsDlgProc (HWND hDlg, UINT iMsg, WPARAM wParam, LPARAM lParam)
1796 {
1797     switch (iMsg)
1798     {
1799        case WM_INITDIALOG:
1800        {
1801            UINT widest       = 0;
1802            UINT length;
1803            UINT i;
1804            MDICLIENTINFO *ci = get_client_info( (HWND)lParam );
1805            HWND hListBox = GetDlgItem(hDlg, MDI_IDC_LISTBOX);
1806
1807            for (i = 0; i < ci->nActiveChildren; i++)
1808            {
1809                WCHAR buffer[MDI_MAXTITLELENGTH];
1810
1811                if (!InternalGetWindowText( ci->child[i], buffer, sizeof(buffer)/sizeof(WCHAR) ))
1812                    continue;
1813                SendMessageW(hListBox, LB_ADDSTRING, 0, (LPARAM)buffer );
1814                SendMessageW(hListBox, LB_SETITEMDATA, i, (LPARAM)ci->child[i] );
1815                length = strlenW(buffer);  /* FIXME: should use GetTextExtentPoint */
1816                if (length > widest)
1817                    widest = length;
1818            }
1819            /* Make sure the horizontal scrollbar scrolls ok */
1820            SendMessageW(hListBox, LB_SETHORIZONTALEXTENT, widest * 6, 0);
1821
1822            /* Set the current selection */
1823            SendMessageW(hListBox, LB_SETCURSEL, MDI_MOREWINDOWSLIMIT, 0);
1824            return TRUE;
1825        }
1826
1827        case WM_COMMAND:
1828            switch (LOWORD(wParam))
1829            {
1830                 default:
1831                     if (HIWORD(wParam) != LBN_DBLCLK) break;
1832                     /* fall through */
1833                 case IDOK:
1834                 {
1835                     /*  windows are sorted by menu ID, so we must return the
1836                      *  window associated to the given id
1837                      */
1838                     HWND hListBox     = GetDlgItem(hDlg, MDI_IDC_LISTBOX);
1839                     UINT index        = SendMessageW(hListBox, LB_GETCURSEL, 0, 0);
1840                     LRESULT res = SendMessageW(hListBox, LB_GETITEMDATA, index, 0);
1841                     EndDialog(hDlg, res);
1842                     return TRUE;
1843                 }
1844                 case IDCANCEL:
1845                     EndDialog(hDlg, 0);
1846                     return TRUE;
1847            }
1848            break;
1849     }
1850     return FALSE;
1851 }
1852
1853 /*
1854  *
1855  *                      MDI_MoreWindowsDialog
1856  *
1857  *     Prompts the user with a listbox containing the opened
1858  *     documents. The user can then choose a windows and click
1859  *     on OK to set the current window to the one selected, or
1860  *     CANCEL to cancel. The function returns a handle to the
1861  *     selected window.
1862  */
1863
1864 static HWND MDI_MoreWindowsDialog(HWND hwnd)
1865 {
1866     LPCVOID template;
1867     HRSRC hRes;
1868     HANDLE hDlgTmpl;
1869
1870     hRes = FindResourceA(user32_module, "MDI_MOREWINDOWS", (LPSTR)RT_DIALOG);
1871
1872     if (hRes == 0)
1873         return 0;
1874
1875     hDlgTmpl = LoadResource(user32_module, hRes );
1876
1877     if (hDlgTmpl == 0)
1878         return 0;
1879
1880     template = LockResource( hDlgTmpl );
1881
1882     if (template == 0)
1883         return 0;
1884
1885     return (HWND) DialogBoxIndirectParamA(user32_module,
1886                                           (LPDLGTEMPLATEA) template,
1887                                           hwnd, MDI_MoreWindowsDlgProc, (LPARAM) hwnd);
1888 }