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