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