Fixed Filesystem documentation.
[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 features.
7  *
8  * Notes: Fairly complete implementation. Any volunteers for 
9  *        "More windows..." stuff?
10  *
11  *        Also, Excel and WinWord do _not_ use MDI so if you're trying
12  *        to fix them look elsewhere. 
13  */
14
15 #include <stdlib.h>
16 #include <string.h>
17 #include <math.h>
18 #include "winuser.h"
19 #include "win.h"
20 #include "heap.h"
21 #include "nonclient.h"
22 #include "mdi.h"
23 #include "user.h"
24 #include "menu.h"
25 #include "resource.h"
26 #include "struct32.h"
27 #include "sysmetrics.h"
28 #include "tweak.h"
29 #include "debug.h"
30
31 #define MDIF_NEEDUPDATE         0x0001
32
33 static HBITMAP16 hBmpClose   = 0;
34 static HBITMAP16 hBmpRestore = 0;
35
36 INT SCROLL_SetNCSbState(WND*,int,int,int,int,int,int);
37
38 /* ----------------- declarations ----------------- */
39 static void MDI_UpdateFrameText(WND *, HWND, BOOL, LPCSTR);
40 static BOOL MDI_AugmentFrameMenu(MDICLIENTINFO*, WND *, HWND);
41 static BOOL MDI_RestoreFrameMenu(WND *, HWND);
42
43 static LONG MDI_ChildActivate( WND*, HWND );
44
45 /* -------- Miscellaneous service functions ----------
46  *
47  *                      MDI_GetChildByID
48  */
49
50 static HWND MDI_GetChildByID(WND* wndPtr, INT id)
51 {
52     for (wndPtr = wndPtr->child; wndPtr; wndPtr = wndPtr->next)
53         if (wndPtr->wIDmenu == id) return wndPtr->hwndSelf;
54     return 0;
55 }
56
57 static void MDI_PostUpdate(HWND hwnd, MDICLIENTINFO* ci, WORD recalc)
58 {
59     if( !(ci->mdiFlags & MDIF_NEEDUPDATE) )
60     {
61         ci->mdiFlags |= MDIF_NEEDUPDATE;
62         PostMessageA( hwnd, WM_MDICALCCHILDSCROLL, 0, 0);
63     }
64     ci->sbRecalc = recalc;
65 }
66
67 /**********************************************************************
68  *                      MDI_MenuModifyItem
69  */
70 static BOOL MDI_MenuModifyItem(WND* clientWnd, HWND hWndChild )
71 {
72     char            buffer[128];
73     MDICLIENTINFO  *clientInfo = (MDICLIENTINFO*)clientWnd->wExtra;
74     WND            *wndPtr     = WIN_FindWndPtr(hWndChild);
75     UINT            n          = sprintf(buffer, "%d ",
76                                  wndPtr->wIDmenu - clientInfo->idFirstChild + 1);
77     BOOL            bRet            = 0;
78
79     if( !clientInfo->hWindowMenu ) return 0;
80
81     if (wndPtr->text) lstrcpynA(buffer + n, wndPtr->text, sizeof(buffer) - n );
82
83     n    = GetMenuState(clientInfo->hWindowMenu,wndPtr->wIDmenu ,MF_BYCOMMAND); 
84     bRet = ModifyMenuA(clientInfo->hWindowMenu , wndPtr->wIDmenu, 
85                       MF_BYCOMMAND | MF_STRING, wndPtr->wIDmenu, buffer );
86     CheckMenuItem(clientInfo->hWindowMenu ,wndPtr->wIDmenu , n & MF_CHECKED);
87     return bRet;
88 }
89
90 /**********************************************************************
91  *                      MDI_MenuDeleteItem
92  */
93 static BOOL MDI_MenuDeleteItem(WND* clientWnd, HWND hWndChild )
94 {
95     char         buffer[128];
96     MDICLIENTINFO *clientInfo = (MDICLIENTINFO*)clientWnd->wExtra;
97     WND         *wndPtr     = WIN_FindWndPtr(hWndChild);
98     UINT         index      = 0,id,n;
99
100     if( !clientInfo->nActiveChildren ||
101         !clientInfo->hWindowMenu ) return 0;
102
103     id = wndPtr->wIDmenu;
104     DeleteMenu(clientInfo->hWindowMenu,id,MF_BYCOMMAND);
105
106  /* walk the rest of MDI children to prevent gaps in the id 
107   * sequence and in the menu child list */
108
109     for( index = id+1; index <= clientInfo->nActiveChildren + 
110                                 clientInfo->idFirstChild; index++ )
111     {
112         wndPtr = WIN_FindWndPtr(MDI_GetChildByID(clientWnd,index));
113         if( !wndPtr )
114         {
115               TRACE(mdi,"no window for id=%i\n",index);
116               continue;
117         }
118     
119         /* set correct id */
120         wndPtr->wIDmenu--;
121
122         n = sprintf(buffer, "%d ",index - clientInfo->idFirstChild);
123         if (wndPtr->text)
124             lstrcpynA(buffer + n, wndPtr->text, sizeof(buffer) - n );   
125
126         /* change menu */
127         ModifyMenuA(clientInfo->hWindowMenu ,index ,MF_BYCOMMAND | MF_STRING,
128                       index - 1 , buffer ); 
129     }
130     return 1;
131 }
132
133 /**********************************************************************
134  *                      MDI_GetWindow
135  *
136  * returns "activateable" child different from the current or zero
137  */
138 static HWND MDI_GetWindow(WND *clientWnd, HWND hWnd, BOOL bNext,
139                             DWORD dwStyleMask )
140 {
141     MDICLIENTINFO *clientInfo = (MDICLIENTINFO*)clientWnd->wExtra;
142     WND *wndPtr, *pWnd, *pWndLast = NULL;
143     
144     dwStyleMask |= WS_DISABLED | WS_VISIBLE;
145     if( !hWnd ) hWnd = clientInfo->hwndActiveChild;
146
147     if( !(wndPtr = WIN_FindWndPtr(hWnd)) ) return 0;
148
149     for ( pWnd = wndPtr->next; ; pWnd = pWnd->next )
150     {
151         if (!pWnd ) pWnd = wndPtr->parent->child;
152
153         if ( pWnd == wndPtr ) break; /* went full circle */
154
155         if (!pWnd->owner && (pWnd->dwStyle & dwStyleMask) == WS_VISIBLE )
156         {
157             pWndLast = pWnd;
158             if ( bNext ) break;
159         }
160     }
161     return pWndLast ? pWndLast->hwndSelf : 0;
162 }
163
164 /**********************************************************************
165  *                      MDI_CalcDefaultChildPos
166  *
167  *  It seems that the default height is about 2/3 of the client rect
168  */
169 static void MDI_CalcDefaultChildPos( WND* w, WORD n, LPPOINT lpPos,
170                                      INT delta)
171 {
172     INT  nstagger;
173     RECT rect = w->rectClient;
174     INT  spacing = GetSystemMetrics(SM_CYCAPTION) +
175                      GetSystemMetrics(SM_CYFRAME) - 1; 
176
177     if( rect.bottom - rect.top - delta >= spacing ) 
178         rect.bottom -= delta;
179
180     nstagger = (rect.bottom - rect.top)/(3 * spacing);
181     lpPos[1].x = (rect.right - rect.left - nstagger * spacing);
182     lpPos[1].y = (rect.bottom - rect.top - nstagger * spacing);
183     lpPos[0].x = lpPos[0].y = spacing * (n%(nstagger+1));
184 }
185
186 /**********************************************************************
187  *            MDISetMenu
188  */
189 static LRESULT MDISetMenu( HWND hwnd, HMENU hmenuFrame,
190                            HMENU hmenuWindow)
191 {
192     WND *w = WIN_FindWndPtr(hwnd);
193     MDICLIENTINFO *ci;
194     HWND hwndFrame = GetParent(hwnd);
195     HMENU oldFrameMenu = GetMenu(hwndFrame);
196
197     TRACE(mdi, "%04x %04x %04x\n",
198                 hwnd, hmenuFrame, hmenuWindow);
199
200     ci = (MDICLIENTINFO *) w->wExtra;
201
202     if( ci->hwndChildMaximized && hmenuFrame && hmenuFrame!=oldFrameMenu )
203         MDI_RestoreFrameMenu(w->parent, ci->hwndChildMaximized );
204
205     if( hmenuWindow && hmenuWindow!=ci->hWindowMenu )
206     {
207         /* delete menu items from ci->hWindowMenu 
208          * and add them to hmenuWindow */
209
210         INT i = GetMenuItemCount(ci->hWindowMenu) - 1;
211         INT pos = GetMenuItemCount(hmenuWindow) + 1;
212
213         AppendMenuA( hmenuWindow, MF_SEPARATOR, 0, NULL);
214
215         if( ci->nActiveChildren )
216         {
217             INT j = i - ci->nActiveChildren + 1;
218             char buffer[100];
219             UINT id,state;
220
221             for( ; i >= j ; i-- )
222             {
223                 id = GetMenuItemID(ci->hWindowMenu,i );
224                 state = GetMenuState(ci->hWindowMenu,i,MF_BYPOSITION); 
225
226                 GetMenuStringA(ci->hWindowMenu, i, buffer, 100, MF_BYPOSITION);
227
228                 DeleteMenu(ci->hWindowMenu, i , MF_BYPOSITION);
229                 InsertMenuA(hmenuWindow, pos, MF_BYPOSITION | MF_STRING,
230                               id, buffer);
231                 CheckMenuItem(hmenuWindow ,pos , MF_BYPOSITION | (state & MF_CHECKED));
232             }
233         }
234
235         /* remove separator */
236         DeleteMenu(ci->hWindowMenu, i, MF_BYPOSITION); 
237
238         ci->hWindowMenu = hmenuWindow;
239     } 
240
241     if( hmenuFrame && hmenuFrame!=oldFrameMenu)
242     {
243         SetMenu(hwndFrame, hmenuFrame);
244         if( ci->hwndChildMaximized )
245             MDI_AugmentFrameMenu(ci, w->parent, ci->hwndChildMaximized );
246         return oldFrameMenu;
247     }
248     return 0;
249 }
250
251 /**********************************************************************
252  *            MDIRefreshMenu
253  */
254 static LRESULT MDIRefreshMenu( HWND hwnd, HMENU hmenuFrame,
255                            HMENU hmenuWindow)
256 {
257     HWND hwndFrame = GetParent(hwnd);
258     HMENU oldFrameMenu = GetMenu(hwndFrame);
259
260     TRACE(mdi, "%04x %04x %04x\n",
261                 hwnd, hmenuFrame, hmenuWindow);
262
263     FIXME(mdi,"partially function stub\n");
264
265     return oldFrameMenu;
266 }
267
268
269 /* ------------------ MDI child window functions ---------------------- */
270
271
272 /**********************************************************************
273  *                                      MDICreateChild
274  */
275 static HWND MDICreateChild( WND *w, MDICLIENTINFO *ci, HWND parent, 
276                               LPMDICREATESTRUCTA cs )
277 {
278     POINT          pos[2]; 
279     DWORD            style = cs->style | (WS_CHILD | WS_CLIPSIBLINGS);
280     HWND             hwnd, hwndMax = 0;
281     WORD             wIDmenu = ci->idFirstChild + ci->nActiveChildren;
282     char             lpstrDef[]="junk!";
283
284     TRACE(mdi, "origin %i,%i - dim %i,%i, style %08x\n", 
285                 cs->x, cs->y, cs->cx, cs->cy, (unsigned)cs->style);    
286     /* calculate placement */
287     MDI_CalcDefaultChildPos(w, ci->nTotalCreated++, pos, 0);
288
289     if (cs->cx == CW_USEDEFAULT || !cs->cx) cs->cx = pos[1].x;
290     if (cs->cy == CW_USEDEFAULT || !cs->cy) cs->cy = pos[1].y;
291
292     if( cs->x == CW_USEDEFAULT )
293     {
294         cs->x = pos[0].x;
295         cs->y = pos[0].y;
296     }
297
298     /* restore current maximized child */
299     if( style & WS_VISIBLE && ci->hwndChildMaximized )
300     {
301         if( style & WS_MAXIMIZE )
302             SendMessageA(w->hwndSelf, WM_SETREDRAW, FALSE, 0L );
303         hwndMax = ci->hwndChildMaximized;
304         ShowWindow( hwndMax, SW_SHOWNOACTIVATE );
305         if( style & WS_MAXIMIZE )
306             SendMessageA(w->hwndSelf, WM_SETREDRAW, TRUE, 0L );
307     }
308
309     /* this menu is needed to set a check mark in MDI_ChildActivate */
310     AppendMenuA(ci->hWindowMenu ,MF_STRING ,wIDmenu, lpstrDef );
311
312     ci->nActiveChildren++;
313
314     /* fix window style */
315     if( !(w->dwStyle & MDIS_ALLCHILDSTYLES) )
316     {
317         style &= (WS_CHILD | WS_CLIPSIBLINGS | WS_MINIMIZE | WS_MAXIMIZE |
318                   WS_CLIPCHILDREN | WS_DISABLED | WS_VSCROLL | WS_HSCROLL );
319         style |= (WS_VISIBLE | WS_OVERLAPPEDWINDOW);
320     }
321
322     if( w->flags & WIN_ISWIN32 )
323     {
324         hwnd = CreateWindowA( cs->szClass, cs->szTitle, style, 
325                                 cs->x, cs->y, cs->cx, cs->cy, parent, 
326                                 (HMENU16)wIDmenu, cs->hOwner, cs );
327     }
328     else
329     {
330         MDICREATESTRUCT16 *cs16;
331         LPSTR title, cls;
332
333         cs16 = SEGPTR_NEW(MDICREATESTRUCT16);
334         STRUCT32_MDICREATESTRUCT32Ato16( cs, cs16 );
335         title = SEGPTR_STRDUP( cs->szTitle );
336         cls   = SEGPTR_STRDUP( cs->szClass );
337         cs16->szTitle = SEGPTR_GET(title);
338         cs16->szClass = SEGPTR_GET(cls);
339
340         hwnd = CreateWindow16( cs->szClass, cs->szTitle, style, 
341                                cs16->x, cs16->y, cs16->cx, cs16->cy, parent, 
342                                (HMENU)wIDmenu, cs16->hOwner,
343                                (LPVOID)SEGPTR_GET(cs16) );
344         SEGPTR_FREE( title );
345         SEGPTR_FREE( cls );
346         SEGPTR_FREE( cs16 );
347     }
348
349     /* MDI windows are WS_CHILD so they won't be activated by CreateWindow */
350
351     if (hwnd)
352     {
353         WND* wnd = WIN_FindWndPtr( hwnd );
354
355         MDI_MenuModifyItem(w ,hwnd); 
356         if( wnd->dwStyle & WS_MINIMIZE && ci->hwndActiveChild )
357             ShowWindow( hwnd, SW_SHOWMINNOACTIVE );
358         else
359         {
360             /* WS_VISIBLE is clear if a) the MDI client has
361              * MDIS_ALLCHILDSTYLES style and 2) the flag is cleared in the
362              * MDICreateStruct. If so the created window is not shown nor 
363              * activated.
364              */
365             int showflag=wnd->dwStyle & WS_VISIBLE;
366             /* clear visible flag, otherwise SetWindoPos32 ignores
367              * the SWP_SHOWWINDOW command.
368              */
369             wnd->dwStyle &= ~WS_VISIBLE;
370             if(showflag){
371                 SetWindowPos( hwnd, 0, 0, 0, 0, 0, SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOMOVE );
372
373                 /* Set maximized state here in case hwnd didn't receive WM_SIZE
374                  * during CreateWindow - bad!
375                  */
376
377                 if((wnd->dwStyle & WS_MAXIMIZE) && !ci->hwndChildMaximized )
378                 {
379                     ci->hwndChildMaximized = wnd->hwndSelf;
380                     MDI_AugmentFrameMenu( ci, w->parent, hwnd );
381                     MDI_UpdateFrameText( w->parent, ci->self, MDI_REPAINTFRAME, NULL ); 
382                 }
383             }else
384                 /* needed, harmless ? */
385                 SetWindowPos( hwnd, 0, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOMOVE );
386
387         }
388         TRACE(mdi, "created child - %04x\n",hwnd);
389     }
390     else
391     {
392         ci->nActiveChildren--;
393         DeleteMenu(ci->hWindowMenu,wIDmenu,MF_BYCOMMAND);
394         if( IsWindow(hwndMax) )
395             ShowWindow(hwndMax, SW_SHOWMAXIMIZED);
396     }
397         
398     return hwnd;
399 }
400
401 /**********************************************************************
402  *                      MDI_ChildGetMinMaxInfo
403  *
404  * Note: The rule here is that client rect of the maximized MDI child 
405  *       is equal to the client rect of the MDI client window.
406  */
407 static void MDI_ChildGetMinMaxInfo( WND* clientWnd, HWND hwnd,
408                                     MINMAXINFO16* lpMinMax )
409 {
410     WND*        childWnd = WIN_FindWndPtr(hwnd);
411     RECT        rect     = clientWnd->rectClient;
412
413     MapWindowPoints( clientWnd->parent->hwndSelf, 
414                      ((MDICLIENTINFO*)clientWnd->wExtra)->self, (LPPOINT)&rect, 2);
415     AdjustWindowRectEx( &rect, childWnd->dwStyle, 0, childWnd->dwExStyle );
416
417     lpMinMax->ptMaxSize.x = rect.right -= rect.left;
418     lpMinMax->ptMaxSize.y = rect.bottom -= rect.top;
419
420     lpMinMax->ptMaxPosition.x = rect.left;
421     lpMinMax->ptMaxPosition.y = rect.top; 
422
423     TRACE(mdi,"max rect (%i,%i - %i, %i)\n", 
424                         rect.left,rect.top,rect.right,rect.bottom);
425 }
426
427 /**********************************************************************
428  *                      MDI_SwitchActiveChild
429  * 
430  * Note: SetWindowPos sends WM_CHILDACTIVATE to the child window that is
431  *       being activated 
432  */
433 static void MDI_SwitchActiveChild( HWND clientHwnd, HWND childHwnd,
434                                    BOOL bNextWindow )
435 {
436     WND           *w         = WIN_FindWndPtr(clientHwnd);
437     HWND           hwndTo    = 0;
438     HWND           hwndPrev  = 0;
439     MDICLIENTINFO *ci;
440
441     hwndTo = MDI_GetWindow(w, childHwnd, bNextWindow, 0);
442  
443     ci = (MDICLIENTINFO *) w->wExtra;
444
445     TRACE(mdi, "from %04x, to %04x\n",childHwnd,hwndTo);
446
447     if ( !hwndTo ) return; /* no window to switch to */
448
449     hwndPrev = ci->hwndActiveChild;
450
451     if ( hwndTo != hwndPrev )
452     {
453         BOOL bOptimize = 0;
454
455         if( ci->hwndChildMaximized )
456         {
457             bOptimize = 1; 
458             w->dwStyle &= ~WS_VISIBLE;
459         }
460
461         SetWindowPos( hwndTo, HWND_TOP, 0, 0, 0, 0, 
462                         SWP_NOMOVE | SWP_NOSIZE );
463
464         if( bNextWindow && hwndPrev )
465             SetWindowPos( hwndPrev, HWND_BOTTOM, 0, 0, 0, 0, 
466                             SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE );
467         if( bOptimize )
468             ShowWindow( clientHwnd, SW_SHOW );
469     }
470 }
471
472             
473 /**********************************************************************
474  *                                      MDIDestroyChild
475  */
476 static LRESULT MDIDestroyChild( WND *w_parent, MDICLIENTINFO *ci,
477                                 HWND parent, HWND child,
478                                 BOOL flagDestroy )
479 {
480     WND         *childPtr = WIN_FindWndPtr(child);
481
482     if( childPtr )
483     {
484         if( child == ci->hwndActiveChild )
485         {
486             MDI_SwitchActiveChild(parent, child, TRUE);
487
488             if( child == ci->hwndActiveChild )
489             {
490                 ShowWindow( child, SW_HIDE);
491                 if( child == ci->hwndChildMaximized )
492                 {
493                     MDI_RestoreFrameMenu(w_parent->parent, child);
494                     ci->hwndChildMaximized = 0;
495                     MDI_UpdateFrameText(w_parent->parent,parent,TRUE,NULL);
496                 }
497
498                 MDI_ChildActivate(w_parent, 0);
499             }
500             MDI_MenuDeleteItem(w_parent, child);
501         }
502         
503         ci->nActiveChildren--;
504
505         TRACE(mdi,"child destroyed - %04x\n",child);
506
507         if (flagDestroy)
508         {
509             MDI_PostUpdate(GetParent(child), ci, SB_BOTH+1);
510             DestroyWindow(child);
511         }
512     }
513
514     return 0;
515 }
516
517
518 /**********************************************************************
519  *                                      MDI_ChildActivate
520  *
521  * Note: hWndChild is NULL when last child is being destroyed
522  */
523 static LONG MDI_ChildActivate( WND *clientPtr, HWND hWndChild )
524 {
525     MDICLIENTINFO       *clientInfo = (MDICLIENTINFO*)clientPtr->wExtra; 
526     HWND               prevActiveWnd = clientInfo->hwndActiveChild;
527     WND                 *wndPtr = WIN_FindWndPtr( hWndChild );
528     WND                 *wndPrev = WIN_FindWndPtr( prevActiveWnd );
529     BOOL                 isActiveFrameWnd = 0;   
530
531     if( hWndChild == prevActiveWnd ) return 0L;
532
533     if( wndPtr )
534         if( wndPtr->dwStyle & WS_DISABLED ) return 0L;
535
536     TRACE(mdi,"%04x\n", hWndChild);
537
538     if( GetActiveWindow() == clientPtr->parent->hwndSelf )
539         isActiveFrameWnd = TRUE;
540         
541     /* deactivate prev. active child */
542     if( wndPrev )
543     {
544         wndPrev->dwStyle |= WS_SYSMENU;
545         SendMessageA( prevActiveWnd, WM_NCACTIVATE, FALSE, 0L );
546         SendMessageA( prevActiveWnd, WM_MDIACTIVATE, (WPARAM)prevActiveWnd,
547                         (LPARAM)hWndChild);
548         /* uncheck menu item */
549         if( clientInfo->hWindowMenu )
550                 CheckMenuItem( clientInfo->hWindowMenu,
551                                  wndPrev->wIDmenu, 0);
552     }
553
554     /* set appearance */
555     if( clientInfo->hwndChildMaximized )
556     {
557       if( clientInfo->hwndChildMaximized != hWndChild ) {
558         if( hWndChild ) {
559                   clientInfo->hwndActiveChild = hWndChild;
560                   ShowWindow( hWndChild, SW_SHOWMAXIMIZED);
561         } else
562                 ShowWindow( clientInfo->hwndActiveChild, SW_SHOWNORMAL );
563       }
564     }
565
566     clientInfo->hwndActiveChild = hWndChild;
567
568     /* check if we have any children left */
569     if( !hWndChild )
570     {
571         if( isActiveFrameWnd )
572             SetFocus( clientInfo->self );
573         return 0;
574     }
575         
576     /* check menu item */
577     if( clientInfo->hWindowMenu )
578               CheckMenuItem( clientInfo->hWindowMenu,
579                                wndPtr->wIDmenu, MF_CHECKED);
580
581     /* bring active child to the top */
582     SetWindowPos( hWndChild, 0,0,0,0,0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE);
583
584     if( isActiveFrameWnd )
585     {
586             SendMessageA( hWndChild, WM_NCACTIVATE, TRUE, 0L);
587             if( GetFocus() == clientInfo->self )
588                 SendMessageA( clientInfo->self, WM_SETFOCUS, 
589                                 (WPARAM)clientInfo->self, 0L );
590             else
591                 SetFocus( clientInfo->self );
592     }
593     SendMessageA( hWndChild, WM_MDIACTIVATE, (WPARAM)prevActiveWnd,
594                     (LPARAM)hWndChild );
595     return 1;
596 }
597
598 /* -------------------- MDI client window functions ------------------- */
599
600 /**********************************************************************
601  *                              CreateMDIMenuBitmap
602  */
603 static HBITMAP16 CreateMDIMenuBitmap(void)
604 {
605  HDC            hDCSrc  = CreateCompatibleDC(0);
606  HDC            hDCDest = CreateCompatibleDC(hDCSrc);
607  HBITMAP16      hbClose = LoadBitmap16(0, MAKEINTRESOURCE16(OBM_CLOSE) );
608  HBITMAP16      hbCopy;
609  HANDLE16       hobjSrc, hobjDest;
610
611  hobjSrc = SelectObject(hDCSrc, hbClose);
612  hbCopy = CreateCompatibleBitmap(hDCSrc,SYSMETRICS_CXSIZE,SYSMETRICS_CYSIZE);
613  hobjDest = SelectObject(hDCDest, hbCopy);
614
615  BitBlt(hDCDest, 0, 0, SYSMETRICS_CXSIZE, SYSMETRICS_CYSIZE,
616           hDCSrc, SYSMETRICS_CXSIZE, 0, SRCCOPY);
617   
618  SelectObject(hDCSrc, hobjSrc);
619  DeleteObject(hbClose);
620  DeleteDC(hDCSrc);
621
622  hobjSrc = SelectObject( hDCDest, GetStockObject(BLACK_PEN) );
623
624  MoveToEx( hDCDest, SYSMETRICS_CXSIZE - 1, 0, NULL );
625  LineTo( hDCDest, SYSMETRICS_CXSIZE - 1, SYSMETRICS_CYSIZE - 1);
626
627  SelectObject(hDCDest, hobjSrc );
628  SelectObject(hDCDest, hobjDest);
629  DeleteDC(hDCDest);
630
631  return hbCopy;
632 }
633
634 /**********************************************************************
635  *                              MDICascade
636  */
637 static LONG MDICascade(WND* clientWnd, MDICLIENTINFO *ci)
638 {
639     WND**       ppWnd;
640     UINT        total;
641   
642     if (ci->hwndChildMaximized)
643         SendMessageA( clientWnd->hwndSelf, WM_MDIRESTORE,
644                         (WPARAM)ci->hwndChildMaximized, 0);
645
646     if (ci->nActiveChildren == 0) return 0;
647
648     if ((ppWnd = WIN_BuildWinArray(clientWnd, BWA_SKIPHIDDEN | BWA_SKIPOWNED | 
649                                               BWA_SKIPICONIC, &total)))
650     {
651         WND**   heapPtr = ppWnd;
652         if( total )
653         {
654             INT delta = 0, n = 0;
655             POINT       pos[2];
656             if( total < ci->nActiveChildren )
657                 delta = SYSMETRICS_CYICONSPACING + SYSMETRICS_CYICON;
658
659             /* walk the list (backwards) and move windows */
660             while (*ppWnd) ppWnd++;
661             while (ppWnd != heapPtr)
662             {
663                 ppWnd--;
664                 TRACE(mdi, "move %04x to (%ld,%ld) size [%ld,%ld]\n", 
665                             (*ppWnd)->hwndSelf, pos[0].x, pos[0].y, pos[1].x, pos[1].y);
666
667                 MDI_CalcDefaultChildPos(clientWnd, n++, pos, delta);
668                 SetWindowPos( (*ppWnd)->hwndSelf, 0, pos[0].x, pos[0].y,
669                                 pos[1].x, pos[1].y,
670                                 SWP_DRAWFRAME | SWP_NOACTIVATE | SWP_NOZORDER);
671             }
672         }
673         HeapFree( SystemHeap, 0, heapPtr );
674     }
675
676     if( total < ci->nActiveChildren )
677         ArrangeIconicWindows( clientWnd->hwndSelf );
678     return 0;
679 }
680
681 /**********************************************************************
682  *                                      MDITile
683  */
684 static void MDITile( WND* wndClient, MDICLIENTINFO *ci, WPARAM wParam )
685 {
686     WND**       ppWnd;
687     UINT        total = 0;
688
689     if (ci->hwndChildMaximized)
690         SendMessageA( wndClient->hwndSelf, WM_MDIRESTORE,
691                         (WPARAM)ci->hwndChildMaximized, 0);
692
693     if (ci->nActiveChildren == 0) return;
694
695     ppWnd = WIN_BuildWinArray(wndClient, BWA_SKIPHIDDEN | BWA_SKIPOWNED | BWA_SKIPICONIC |
696             ((wParam & MDITILE_SKIPDISABLED)? BWA_SKIPDISABLED : 0), &total );
697
698     TRACE(mdi,"%u windows to tile\n", total);
699
700     if( ppWnd )
701     {
702         WND**   heapPtr = ppWnd;
703
704         if( total )
705         {
706             RECT        rect;
707             int         x, y, xsize, ysize;
708             int         rows, columns, r, c, i;
709
710             rect    = wndClient->rectClient;
711             rows    = (int) sqrt((double)total);
712             columns = total / rows;
713
714             if( wParam & MDITILE_HORIZONTAL )  /* version >= 3.1 */
715             {
716                 i = rows;
717                 rows = columns;  /* exchange r and c */
718                 columns = i;
719             }
720
721             if( total != ci->nActiveChildren)
722             {
723                 y = rect.bottom - 2 * SYSMETRICS_CYICONSPACING - SYSMETRICS_CYICON;
724                 rect.bottom = ( y - SYSMETRICS_CYICON < rect.top )? rect.bottom: y;
725             }
726
727             ysize   = rect.bottom / rows;
728             xsize   = rect.right  / columns;
729     
730             for (x = i = 0, c = 1; c <= columns && *ppWnd; c++)
731             {
732                 if (c == columns)
733                 {
734                     rows  = total - i;
735                     ysize = rect.bottom / rows;
736                 }
737
738                 y = 0;
739                 for (r = 1; r <= rows && *ppWnd; r++, i++)
740                 {
741                     SetWindowPos((*ppWnd)->hwndSelf, 0, x, y, xsize, ysize, 
742                                    SWP_DRAWFRAME | SWP_NOACTIVATE | SWP_NOZORDER);
743                     y += ysize;
744                     ppWnd++;
745                 }
746                 x += xsize;
747             }
748         }
749         HeapFree( SystemHeap, 0, heapPtr );
750     }
751   
752     if( total < ci->nActiveChildren ) ArrangeIconicWindows( wndClient->hwndSelf );
753 }
754
755 /* ----------------------- Frame window ---------------------------- */
756
757
758 /**********************************************************************
759  *                                      MDI_AugmentFrameMenu
760  */
761 static BOOL MDI_AugmentFrameMenu( MDICLIENTINFO* ci, WND *frame,
762                                     HWND hChild )
763 {
764     WND*        child = WIN_FindWndPtr(hChild);
765     HMENU       hSysPopup = 0;
766
767     TRACE(mdi,"frame %p,child %04x\n",frame,hChild);
768
769     if( !frame->wIDmenu || !child->hSysMenu ) return 0; 
770
771     /* create a copy of sysmenu popup and insert it into frame menu bar */
772
773     if (!(hSysPopup = LoadMenuIndirectA(SYSRES_GetResPtr(SYSRES_MENU_SYSMENU))))
774         return 0;
775  
776     TRACE(mdi,"\tgot popup %04x in sysmenu %04x\n", 
777                 hSysPopup, child->hSysMenu);
778  
779     AppendMenuA(frame->wIDmenu,MF_HELP | MF_BITMAP,
780                    SC_MINIMIZE, (LPSTR)(DWORD)MAGIC_REDUCE ) ;
781     AppendMenuA(frame->wIDmenu,MF_HELP | MF_BITMAP,
782                    SC_RESTORE, (LPSTR)(DWORD)MAGIC_RESTORE );
783
784     if( !InsertMenuA(frame->wIDmenu,0,MF_BYPOSITION | MF_BITMAP | MF_POPUP,
785                     hSysPopup, (LPSTR)(DWORD)hBmpClose ))
786     {  
787         TRACE(mdi,"not inserted\n");
788         DestroyMenu(hSysPopup); 
789         return 0; 
790     }
791
792      // The close button is only present in Win 95 look
793     if(TWEAK_WineLook > WIN31_LOOK)
794     {
795         AppendMenuA(frame->wIDmenu,MF_HELP | MF_BITMAP,
796                        SC_CLOSE, (LPSTR)(DWORD)MAGIC_CLOSE );
797     }
798
799     EnableMenuItem(hSysPopup, SC_SIZE, MF_BYCOMMAND | MF_GRAYED);
800     EnableMenuItem(hSysPopup, SC_MOVE, MF_BYCOMMAND | MF_GRAYED);
801     EnableMenuItem(hSysPopup, SC_MAXIMIZE, MF_BYCOMMAND | MF_GRAYED);
802
803     /* redraw menu */
804     DrawMenuBar(frame->hwndSelf);
805
806     return 1;
807 }
808
809 /**********************************************************************
810  *                                      MDI_RestoreFrameMenu
811  */
812 static BOOL MDI_RestoreFrameMenu( WND *frameWnd, HWND hChild )
813 {
814     INT nItems = GetMenuItemCount(frameWnd->wIDmenu) - 1;
815     UINT iId = GetMenuItemID(frameWnd->wIDmenu,nItems) ;
816
817     TRACE(mdi,"frameWnd %p,child %04x\n",frameWnd,hChild);
818
819     if(!(iId == SC_RESTORE || iId == SC_CLOSE) )
820         return 0; 
821
822     // app button
823     RemoveMenu(frameWnd->wIDmenu,0,MF_BYPOSITION);
824
825     if(TWEAK_WineLook > WIN31_LOOK)
826     {
827         // close
828     DeleteMenu(frameWnd->wIDmenu,GetMenuItemCount(frameWnd->wIDmenu) - 1,MF_BYPOSITION);
829     }
830     // restore
831     DeleteMenu(frameWnd->wIDmenu,GetMenuItemCount(frameWnd->wIDmenu) - 1,MF_BYPOSITION);
832     // minimize
833     DeleteMenu(frameWnd->wIDmenu,GetMenuItemCount(frameWnd->wIDmenu) - 1,MF_BYPOSITION);
834
835     DrawMenuBar(frameWnd->hwndSelf);
836
837     return 1;
838 }
839
840
841 /**********************************************************************
842  *                                      MDI_UpdateFrameText
843  *
844  * used when child window is maximized/restored 
845  *
846  * Note: lpTitle can be NULL
847  */
848 static void MDI_UpdateFrameText( WND *frameWnd, HWND hClient,
849                                  BOOL repaint, LPCSTR lpTitle )
850 {
851     char   lpBuffer[MDI_MAXTITLELENGTH+1];
852     WND*   clientWnd = WIN_FindWndPtr(hClient);
853     MDICLIENTINFO *ci = (MDICLIENTINFO *) clientWnd->wExtra;
854
855     TRACE(mdi, "repaint %i, frameText %s\n", repaint, (lpTitle)?lpTitle:"NULL");
856
857     if (!clientWnd)
858            return;
859
860     if (!ci)
861            return;
862
863     /* store new "default" title if lpTitle is not NULL */
864     if (lpTitle) 
865     {
866         if (ci->frameTitle) HeapFree( SystemHeap, 0, ci->frameTitle );
867         ci->frameTitle = HEAP_strdupA( SystemHeap, 0, lpTitle );
868     }
869
870     if (ci->frameTitle)
871     {
872         WND* childWnd = WIN_FindWndPtr( ci->hwndChildMaximized );     
873
874         if( childWnd && childWnd->text )
875         {
876             /* combine frame title and child title if possible */
877
878             LPCSTR lpBracket  = " - [";
879             int i_frame_text_length = strlen(ci->frameTitle);
880             int i_child_text_length = strlen(childWnd->text);
881
882             lstrcpynA( lpBuffer, ci->frameTitle, MDI_MAXTITLELENGTH);
883
884             if( i_frame_text_length + 6 < MDI_MAXTITLELENGTH )
885             {
886                 strcat( lpBuffer, lpBracket );
887
888                 if( i_frame_text_length + i_child_text_length + 6 < MDI_MAXTITLELENGTH )
889                 {
890                     strcat( lpBuffer, childWnd->text );
891                     strcat( lpBuffer, "]" );
892                 }
893                 else
894                 {
895                     lstrcpynA( lpBuffer + i_frame_text_length + 4, 
896                                  childWnd->text, MDI_MAXTITLELENGTH - i_frame_text_length - 5 );
897                     strcat( lpBuffer, "]" );
898                 }
899             }
900         }
901         else
902         {
903             strncpy(lpBuffer, ci->frameTitle, MDI_MAXTITLELENGTH );
904             lpBuffer[MDI_MAXTITLELENGTH]='\0';
905         }
906     }
907     else
908         lpBuffer[0] = '\0';
909
910     DEFWND_SetText( frameWnd, lpBuffer );
911     if( repaint == MDI_REPAINTFRAME)
912         SetWindowPos( frameWnd->hwndSelf, 0,0,0,0,0, SWP_FRAMECHANGED |
913                         SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER );
914 }
915
916
917 /* ----------------------------- Interface ---------------------------- */
918
919
920 /**********************************************************************
921  *                                      MDIClientWndProc
922  *
923  * This function handles all MDI requests.
924  */
925 LRESULT WINAPI MDIClientWndProc( HWND hwnd, UINT message, WPARAM wParam,
926                                  LPARAM lParam )
927 {
928     LPCREATESTRUCTA    cs;
929     MDICLIENTINFO       *ci;
930     RECT                 rect;
931     WND                 *w        = WIN_FindWndPtr(hwnd);
932     WND                 *frameWnd = w->parent;
933     INT nItems;
934     
935     ci = (MDICLIENTINFO *) w->wExtra;
936     
937     switch (message)
938     {
939       case WM_CREATE:
940
941         cs = (LPCREATESTRUCTA)lParam;
942
943         /* Translation layer doesn't know what's in the cs->lpCreateParams
944          * so we have to keep track of what environment we're in. */
945
946         if( w->flags & WIN_ISWIN32 )
947         {
948 #define ccs ((LPCLIENTCREATESTRUCT)cs->lpCreateParams)
949             ci->hWindowMenu     = ccs->hWindowMenu;
950             ci->idFirstChild    = ccs->idFirstChild;
951 #undef ccs
952         }
953         else    
954         {
955             LPCLIENTCREATESTRUCT16 ccs = (LPCLIENTCREATESTRUCT16) 
956                                    PTR_SEG_TO_LIN(cs->lpCreateParams);
957             ci->hWindowMenu     = ccs->hWindowMenu;
958             ci->idFirstChild    = ccs->idFirstChild;
959         }
960
961         ci->hwndChildMaximized  = 0;
962         ci->nActiveChildren     = 0;
963         ci->nTotalCreated       = 0;
964         ci->frameTitle          = NULL;
965         ci->mdiFlags            = 0;
966         ci->self                = hwnd;
967         w->dwStyle             |= WS_CLIPCHILDREN;
968
969         if (!hBmpClose)
970         {
971             hBmpClose = CreateMDIMenuBitmap();
972             hBmpRestore = LoadBitmap16( 0, MAKEINTRESOURCE16(OBM_RESTORE) );
973         }
974         MDI_UpdateFrameText(frameWnd, hwnd, MDI_NOFRAMEREPAINT,frameWnd->text);
975
976         AppendMenuA( ci->hWindowMenu, MF_SEPARATOR, 0, NULL );
977
978         GetClientRect(frameWnd->hwndSelf, &rect);
979         NC_HandleNCCalcSize( w, &rect );
980         w->rectClient = rect;
981
982         TRACE(mdi,"Client created - hwnd = %04x, idFirst = %u\n",
983                            hwnd, ci->idFirstChild );
984
985         return 0;
986       
987       case WM_DESTROY:
988         if( ci->hwndChildMaximized ) MDI_RestoreFrameMenu(w, frameWnd->hwndSelf);
989         if((nItems = GetMenuItemCount(ci->hWindowMenu)) > 0) 
990         {
991             ci->idFirstChild = nItems - 1;
992             ci->nActiveChildren++;              /* to delete a separator */
993             while( ci->nActiveChildren-- )
994                 DeleteMenu(ci->hWindowMenu,MF_BYPOSITION,ci->idFirstChild--);
995         }
996         return 0;
997
998       case WM_MDIACTIVATE:
999         if( ci->hwndActiveChild != (HWND)wParam )
1000             SetWindowPos((HWND)wParam, 0,0,0,0,0, SWP_NOSIZE | SWP_NOMOVE);
1001         return 0;
1002
1003       case WM_MDICASCADE:
1004         return MDICascade(w, ci);
1005
1006       case WM_MDICREATE:
1007         if (lParam) return MDICreateChild( w, ci, hwnd,
1008                                            (MDICREATESTRUCTA*)lParam );
1009         return 0;
1010
1011       case WM_MDIDESTROY:
1012         return MDIDestroyChild( w, ci, hwnd, (HWND)wParam, TRUE );
1013
1014       case WM_MDIGETACTIVE:
1015           if (lParam) *(BOOL *)lParam = (ci->hwndChildMaximized > 0);
1016           return ci->hwndActiveChild;
1017
1018       case WM_MDIICONARRANGE:
1019         ci->mdiFlags |= MDIF_NEEDUPDATE;
1020         ArrangeIconicWindows(hwnd);
1021         ci->sbRecalc = SB_BOTH+1;
1022         SendMessageA(hwnd, WM_MDICALCCHILDSCROLL, 0, 0L);
1023         return 0;
1024         
1025       case WM_MDIMAXIMIZE:
1026         ShowWindow( (HWND)wParam, SW_MAXIMIZE );
1027         return 0;
1028
1029       case WM_MDINEXT: /* lParam != 0 means previous window */
1030         MDI_SwitchActiveChild(hwnd, (HWND)wParam, (lParam)? FALSE : TRUE );
1031         break;
1032         
1033       case WM_MDIRESTORE:
1034         SendMessageA( (HWND)wParam, WM_SYSCOMMAND, SC_RESTORE, 0);
1035         return 0;
1036
1037       case WM_MDISETMENU:
1038           return MDISetMenu( hwnd, (HMENU)wParam, (HMENU)lParam );
1039         
1040       case WM_MDIREFRESHMENU:
1041           return MDIRefreshMenu( hwnd, (HMENU)wParam, (HMENU)lParam );
1042
1043       case WM_MDITILE:
1044         ci->mdiFlags |= MDIF_NEEDUPDATE;
1045         ShowScrollBar(hwnd,SB_BOTH,FALSE);
1046         MDITile(w, ci, wParam);
1047         ci->mdiFlags &= ~MDIF_NEEDUPDATE;
1048         return 0;
1049
1050       case WM_VSCROLL:
1051       case WM_HSCROLL:
1052         ci->mdiFlags |= MDIF_NEEDUPDATE;
1053         ScrollChildren(hwnd, message, wParam, lParam);
1054         ci->mdiFlags &= ~MDIF_NEEDUPDATE;
1055         return 0;
1056
1057       case WM_SETFOCUS:
1058         if( ci->hwndActiveChild )
1059         {
1060            w = WIN_FindWndPtr( ci->hwndActiveChild );
1061            if( !(w->dwStyle & WS_MINIMIZE) )
1062                SetFocus( ci->hwndActiveChild );
1063         } 
1064         return 0;
1065         
1066       case WM_NCACTIVATE:
1067         if( ci->hwndActiveChild )
1068              SendMessageA(ci->hwndActiveChild, message, wParam, lParam);
1069         break;
1070         
1071       case WM_PARENTNOTIFY:
1072         if (LOWORD(wParam) == WM_LBUTTONDOWN)
1073         {
1074             POINT16  pt = MAKEPOINT16(lParam);
1075             HWND16 child = ChildWindowFromPoint16(hwnd, pt);
1076
1077             TRACE(mdi,"notification from %04x (%i,%i)\n",child,pt.x,pt.y);
1078
1079             if( child && child != hwnd && child != ci->hwndActiveChild )
1080                 SetWindowPos(child, 0,0,0,0,0, SWP_NOSIZE | SWP_NOMOVE );
1081         }
1082         return 0;
1083
1084       case WM_SIZE:
1085         if( IsWindow(ci->hwndChildMaximized) )
1086         {
1087             WND*        child = WIN_FindWndPtr(ci->hwndChildMaximized);
1088             RECT        rect  = { 0, 0, LOWORD(lParam), HIWORD(lParam) };
1089
1090             AdjustWindowRectEx(&rect, child->dwStyle, 0, child->dwExStyle);
1091             MoveWindow(ci->hwndChildMaximized, rect.left, rect.top,
1092                          rect.right - rect.left, rect.bottom - rect.top, 1);
1093         }
1094         else
1095             MDI_PostUpdate(hwnd, ci, SB_BOTH+1);
1096
1097         break;
1098
1099       case WM_MDICALCCHILDSCROLL:
1100         if( (ci->mdiFlags & MDIF_NEEDUPDATE) && ci->sbRecalc )
1101         {
1102             CalcChildScroll16(hwnd, ci->sbRecalc-1);
1103             ci->sbRecalc = 0;
1104             ci->mdiFlags &= ~MDIF_NEEDUPDATE;
1105         }
1106         return 0;
1107     }
1108     
1109     return DefWindowProcA( hwnd, message, wParam, lParam );
1110 }
1111
1112
1113 /***********************************************************************
1114  *           DefFrameProc16   (USER.445)
1115  */
1116 LRESULT WINAPI DefFrameProc16( HWND16 hwnd, HWND16 hwndMDIClient,
1117                                UINT16 message, WPARAM16 wParam, LPARAM lParam )
1118 {
1119     HWND16               childHwnd;
1120     MDICLIENTINFO*       ci;
1121     WND*                 wndPtr;
1122
1123     if (hwndMDIClient)
1124     {
1125         switch (message)
1126         {
1127           case WM_COMMAND:
1128             wndPtr = WIN_FindWndPtr(hwndMDIClient);
1129             ci     = (MDICLIENTINFO*)wndPtr->wExtra;
1130
1131             /* check for possible syscommands for maximized MDI child */
1132
1133             if( ci && (
1134                 wParam <  ci->idFirstChild || 
1135                 wParam >= ci->idFirstChild + ci->nActiveChildren
1136             )){
1137                 if( (wParam - 0xF000) & 0xF00F ) break;
1138                 switch( wParam )
1139                   {
1140                     case SC_SIZE:
1141                     case SC_MOVE:
1142                     case SC_MINIMIZE:
1143                     case SC_MAXIMIZE:
1144                     case SC_NEXTWINDOW:
1145                     case SC_PREVWINDOW:
1146                     case SC_CLOSE:
1147                     case SC_RESTORE:
1148                        if( ci->hwndChildMaximized )
1149                            return SendMessage16( ci->hwndChildMaximized, WM_SYSCOMMAND,
1150                                                wParam, lParam);
1151                   }
1152               }
1153             else
1154               {
1155                 childHwnd = MDI_GetChildByID( WIN_FindWndPtr(hwndMDIClient),
1156                                           wParam );
1157                 if( childHwnd )
1158                     SendMessage16(hwndMDIClient, WM_MDIACTIVATE,
1159                                   (WPARAM16)childHwnd , 0L);
1160               }
1161             break;
1162
1163           case WM_NCACTIVATE:
1164             SendMessage16(hwndMDIClient, message, wParam, lParam);
1165             break;
1166
1167           case WM_SETTEXT:
1168             MDI_UpdateFrameText(WIN_FindWndPtr(hwnd), hwndMDIClient, 
1169                                       MDI_REPAINTFRAME, 
1170                                      (LPCSTR)PTR_SEG_TO_LIN(lParam));
1171             return 0;
1172         
1173           case WM_SETFOCUS:
1174             SetFocus(hwndMDIClient);
1175             break;
1176
1177           case WM_SIZE:
1178             MoveWindow16(hwndMDIClient, 0, 0, 
1179                          LOWORD(lParam), HIWORD(lParam), TRUE);
1180             break;
1181
1182           case WM_NEXTMENU:
1183
1184             wndPtr = WIN_FindWndPtr(hwndMDIClient);
1185             ci     = (MDICLIENTINFO*)wndPtr->wExtra;
1186
1187             if( !(wndPtr->parent->dwStyle & WS_MINIMIZE) 
1188                 && ci->hwndActiveChild && !ci->hwndChildMaximized )
1189             {
1190                 /* control menu is between the frame system menu and 
1191                  * the first entry of menu bar */
1192
1193                 if( (wParam == VK_LEFT &&
1194                      wndPtr->parent->wIDmenu == LOWORD(lParam)) ||
1195                     (wParam == VK_RIGHT &&
1196                      GetSubMenu16(wndPtr->parent->hSysMenu, 0) == LOWORD(lParam)) )
1197                 {
1198                     wndPtr = WIN_FindWndPtr(ci->hwndActiveChild);
1199                     return MAKELONG( GetSubMenu16(wndPtr->hSysMenu, 0), 
1200                                                   ci->hwndActiveChild);
1201                 }
1202             }
1203             break;
1204         }
1205     }
1206     
1207     return DefWindowProc16(hwnd, message, wParam, lParam);
1208 }
1209
1210
1211 /***********************************************************************
1212  *           DefFrameProc32A   (USER32.122)
1213  */
1214 LRESULT WINAPI DefFrameProcA( HWND hwnd, HWND hwndMDIClient,
1215                                 UINT message, WPARAM wParam, LPARAM lParam)
1216 {
1217     if (hwndMDIClient)
1218     {
1219         switch (message)
1220         {
1221           case WM_COMMAND:
1222               return DefFrameProc16( hwnd, hwndMDIClient, message,
1223                                      (WPARAM16)wParam,
1224                               MAKELPARAM( (HWND16)lParam, HIWORD(wParam) ) );
1225
1226           case WM_NCACTIVATE:
1227             SendMessageA(hwndMDIClient, message, wParam, lParam);
1228             break;
1229
1230           case WM_SETTEXT: {
1231                 LRESULT ret;
1232                 LPSTR   segstr = SEGPTR_STRDUP((LPSTR)lParam);
1233
1234                 ret = DefFrameProc16(hwnd, hwndMDIClient, message,
1235                                      wParam, (LPARAM)SEGPTR_GET(segstr) );
1236                 SEGPTR_FREE(segstr);
1237                 return ret;
1238           }
1239         
1240           case WM_NEXTMENU:
1241           case WM_SETFOCUS:
1242           case WM_SIZE:
1243               return DefFrameProc16( hwnd, hwndMDIClient, message,
1244                                      wParam, lParam );
1245         }
1246     }
1247     
1248     return DefWindowProcA(hwnd, message, wParam, lParam);
1249 }
1250
1251
1252 /***********************************************************************
1253  *           DefFrameProc32W   (USER32.123)
1254  */
1255 LRESULT WINAPI DefFrameProcW( HWND hwnd, HWND hwndMDIClient,
1256                                 UINT message, WPARAM wParam, LPARAM lParam)
1257 {
1258     if (hwndMDIClient)
1259     {
1260         switch (message)
1261         {
1262           case WM_COMMAND:
1263               return DefFrameProc16( hwnd, hwndMDIClient, message,
1264                                      (WPARAM16)wParam,
1265                               MAKELPARAM( (HWND16)lParam, HIWORD(wParam) ) );
1266
1267           case WM_NCACTIVATE:
1268             SendMessageW(hwndMDIClient, message, wParam, lParam);
1269             break;
1270
1271           case WM_SETTEXT: 
1272           {
1273               LPSTR txt = HEAP_strdupWtoA(GetProcessHeap(),0,(LPWSTR)lParam);
1274               LRESULT ret = DefFrameProcA( hwnd, hwndMDIClient, message,
1275                                      wParam, (DWORD)txt );
1276               HeapFree(GetProcessHeap(),0,txt);
1277               return ret;
1278           }
1279           case WM_NEXTMENU:
1280           case WM_SETFOCUS:
1281           case WM_SIZE:
1282               return DefFrameProcA( hwnd, hwndMDIClient, message,
1283                                       wParam, lParam );
1284         }
1285     }
1286     
1287     return DefWindowProcW( hwnd, message, wParam, lParam );
1288 }
1289
1290
1291 /***********************************************************************
1292  *           DefMDIChildProc16   (USER.447)
1293  */
1294 LRESULT WINAPI DefMDIChildProc16( HWND16 hwnd, UINT16 message,
1295                                   WPARAM16 wParam, LPARAM lParam )
1296 {
1297     MDICLIENTINFO       *ci;
1298     WND                 *clientWnd;
1299
1300     clientWnd  = WIN_FindWndPtr(GetParent16(hwnd));
1301     ci         = (MDICLIENTINFO *) clientWnd->wExtra;
1302
1303     switch (message)
1304     {
1305       case WM_SETTEXT:
1306         DefWindowProc16(hwnd, message, wParam, lParam);
1307         MDI_MenuModifyItem(clientWnd,hwnd);
1308         if( ci->hwndChildMaximized == hwnd )
1309             MDI_UpdateFrameText( clientWnd->parent, ci->self,
1310                                  MDI_REPAINTFRAME, NULL );
1311         return 0;
1312
1313       case WM_CLOSE:
1314         SendMessage16(ci->self,WM_MDIDESTROY,(WPARAM16)hwnd,0L);
1315         return 0;
1316
1317       case WM_SETFOCUS:
1318         if( ci->hwndActiveChild != hwnd )
1319             MDI_ChildActivate(clientWnd, hwnd);
1320         break;
1321
1322       case WM_CHILDACTIVATE:
1323         MDI_ChildActivate(clientWnd, hwnd);
1324         return 0;
1325
1326       case WM_NCPAINT:
1327         TRACE(mdi,"WM_NCPAINT for %04x, active %04x\n",
1328                                              hwnd, ci->hwndActiveChild );
1329         break;
1330
1331       case WM_SYSCOMMAND:
1332         switch( wParam )
1333         {
1334                 case SC_MOVE:
1335                      if( ci->hwndChildMaximized == hwnd) return 0;
1336                      break;
1337                 case SC_RESTORE:
1338                 case SC_MINIMIZE:
1339                      WIN_FindWndPtr(hwnd)->dwStyle |= WS_SYSMENU;
1340                      break;
1341                 case SC_MAXIMIZE:
1342                      if( ci->hwndChildMaximized == hwnd) 
1343                          return SendMessage16( clientWnd->parent->hwndSelf,
1344                                              message, wParam, lParam);
1345                      WIN_FindWndPtr(hwnd)->dwStyle &= ~WS_SYSMENU;
1346                      break;
1347                 case SC_NEXTWINDOW:
1348                      SendMessage16( ci->self, WM_MDINEXT, 0, 0);
1349                      return 0;
1350                 case SC_PREVWINDOW:
1351                      SendMessage16( ci->self, WM_MDINEXT, 0, 1);
1352                      return 0;
1353         }
1354         break;
1355         
1356       case WM_GETMINMAXINFO:
1357         MDI_ChildGetMinMaxInfo(clientWnd, hwnd, (MINMAXINFO16*) PTR_SEG_TO_LIN(lParam));
1358         return 0;
1359
1360       case WM_SETVISIBLE:
1361          if( ci->hwndChildMaximized) ci->mdiFlags &= ~MDIF_NEEDUPDATE;
1362          else
1363             MDI_PostUpdate(clientWnd->hwndSelf, ci, SB_BOTH+1);
1364         break;
1365
1366       case WM_SIZE:
1367         /* do not change */
1368
1369         if( ci->hwndActiveChild == hwnd && wParam != SIZE_MAXIMIZED )
1370         {
1371             ci->hwndChildMaximized = 0;
1372             
1373             MDI_RestoreFrameMenu( clientWnd->parent, hwnd);
1374             MDI_UpdateFrameText( clientWnd->parent, ci->self,
1375                                  MDI_REPAINTFRAME, NULL );
1376         }
1377
1378         if( wParam == SIZE_MAXIMIZED )
1379         {
1380             HWND16 hMaxChild = ci->hwndChildMaximized;
1381
1382             if( hMaxChild == hwnd ) break;
1383
1384             if( hMaxChild)
1385             {       
1386                 SendMessage16( hMaxChild, WM_SETREDRAW, FALSE, 0L );
1387
1388                 MDI_RestoreFrameMenu( clientWnd->parent, hMaxChild);
1389                 ShowWindow16( hMaxChild, SW_SHOWNOACTIVATE);
1390
1391                 SendMessage16( hMaxChild, WM_SETREDRAW, TRUE, 0L );
1392             }
1393
1394             TRACE(mdi,"maximizing child %04x\n", hwnd );
1395
1396             ci->hwndChildMaximized = hwnd; /* !!! */
1397             ci->hwndActiveChild = hwnd;
1398
1399             MDI_AugmentFrameMenu( ci, clientWnd->parent, hwnd);
1400             MDI_UpdateFrameText( clientWnd->parent, ci->self,
1401                                  MDI_REPAINTFRAME, NULL ); 
1402         }
1403
1404         if( wParam == SIZE_MINIMIZED )
1405         {
1406             HWND16 switchTo = MDI_GetWindow(clientWnd, hwnd, TRUE, WS_MINIMIZE);
1407
1408             if( switchTo )
1409                 SendMessage16( switchTo, WM_CHILDACTIVATE, 0, 0L);
1410         }
1411          
1412         MDI_PostUpdate(clientWnd->hwndSelf, ci, SB_BOTH+1);
1413         break;
1414
1415       case WM_MENUCHAR:
1416
1417         /* MDI children don't have menu bars */
1418         PostMessage16( clientWnd->parent->hwndSelf, WM_SYSCOMMAND, 
1419                        (WPARAM16)SC_KEYMENU, (LPARAM)wParam);
1420         return 0x00010000L;
1421
1422       case WM_NEXTMENU:
1423
1424         if( wParam == VK_LEFT )         /* switch to frame system menu */
1425           return MAKELONG( GetSubMenu16(clientWnd->parent->hSysMenu, 0), 
1426                            clientWnd->parent->hwndSelf );
1427         if( wParam == VK_RIGHT )        /* to frame menu bar */
1428           return MAKELONG( clientWnd->parent->wIDmenu,
1429                            clientWnd->parent->hwndSelf );
1430
1431         break;  
1432
1433       case WM_SYSCHAR:
1434            if (wParam == '-')
1435            {
1436                 SendMessage16(hwnd,WM_SYSCOMMAND,
1437                         (WPARAM16)SC_KEYMENU, (LPARAM)(DWORD)VK_SPACE);
1438                 return 0;
1439            }
1440     }
1441         
1442     return DefWindowProc16(hwnd, message, wParam, lParam);
1443 }
1444
1445
1446 /***********************************************************************
1447  *           DefMDIChildProc32A   (USER32.124)
1448  */
1449 LRESULT WINAPI DefMDIChildProcA( HWND hwnd, UINT message,
1450                                    WPARAM wParam, LPARAM lParam )
1451 {
1452     MDICLIENTINFO       *ci;
1453     WND                 *clientWnd;
1454
1455     clientWnd  = WIN_FindWndPtr(WIN_FindWndPtr(hwnd)->parent->hwndSelf);
1456     ci         = (MDICLIENTINFO *) clientWnd->wExtra;
1457
1458     switch (message)
1459     {
1460       case WM_SETTEXT:
1461         DefWindowProcA(hwnd, message, wParam, lParam);
1462         MDI_MenuModifyItem(clientWnd,hwnd);
1463         if( ci->hwndChildMaximized == hwnd )
1464             MDI_UpdateFrameText( clientWnd->parent, ci->self,
1465                                  MDI_REPAINTFRAME, NULL );
1466         return 0;
1467
1468       case WM_GETMINMAXINFO:
1469         {
1470             MINMAXINFO16 mmi;
1471             STRUCT32_MINMAXINFO32to16( (MINMAXINFO *)lParam, &mmi );
1472             MDI_ChildGetMinMaxInfo( clientWnd, hwnd, &mmi );
1473             STRUCT32_MINMAXINFO16to32( &mmi, (MINMAXINFO *)lParam );
1474         }
1475         return 0;
1476
1477       case WM_MENUCHAR:
1478
1479         /* MDI children don't have menu bars */
1480         PostMessage16( clientWnd->parent->hwndSelf, WM_SYSCOMMAND, 
1481                        (WPARAM16)SC_KEYMENU, (LPARAM)LOWORD(wParam) );
1482         return 0x00010000L;
1483
1484       case WM_CLOSE:
1485       case WM_SETFOCUS:
1486       case WM_CHILDACTIVATE:
1487       case WM_NCPAINT:
1488       case WM_SYSCOMMAND:
1489       case WM_SETVISIBLE:
1490       case WM_SIZE:
1491       case WM_NEXTMENU:
1492           return DefMDIChildProc16( hwnd, message, (WPARAM16)wParam, lParam );
1493
1494       case WM_SYSCHAR:
1495            if (wParam == '-')
1496            {
1497                 SendMessageA(hwnd,WM_SYSCOMMAND,
1498                         (WPARAM)SC_KEYMENU, (LPARAM)(DWORD)VK_SPACE);
1499                 return 0;
1500            }
1501     }
1502     return DefWindowProcA(hwnd, message, wParam, lParam);
1503 }
1504
1505
1506 /***********************************************************************
1507  *           DefMDIChildProc32W   (USER32.125)
1508  */
1509 LRESULT WINAPI DefMDIChildProcW( HWND hwnd, UINT message,
1510                                    WPARAM wParam, LPARAM lParam )
1511 {
1512     MDICLIENTINFO       *ci;
1513     WND                 *clientWnd;
1514
1515     clientWnd  = WIN_FindWndPtr(GetParent16(hwnd));
1516     ci         = (MDICLIENTINFO *) clientWnd->wExtra;
1517
1518     switch (message)
1519     {
1520       case WM_SETTEXT:
1521         DefWindowProcW(hwnd, message, wParam, lParam);
1522         MDI_MenuModifyItem(clientWnd,hwnd);
1523         if( ci->hwndChildMaximized == hwnd )
1524             MDI_UpdateFrameText( clientWnd->parent, ci->self,
1525                                  MDI_REPAINTFRAME, NULL );
1526         return 0;
1527
1528       case WM_GETMINMAXINFO:
1529       case WM_MENUCHAR:
1530       case WM_CLOSE:
1531       case WM_SETFOCUS:
1532       case WM_CHILDACTIVATE:
1533       case WM_NCPAINT:
1534       case WM_SYSCOMMAND:
1535       case WM_SETVISIBLE:
1536       case WM_SIZE:
1537       case WM_NEXTMENU:
1538           return DefMDIChildProcA( hwnd, message, (WPARAM16)wParam, lParam );
1539
1540       case WM_SYSCHAR:
1541            if (wParam == '-')
1542            {
1543                 SendMessageW(hwnd,WM_SYSCOMMAND,
1544                         (WPARAM)SC_KEYMENU, (LPARAM)(DWORD)VK_SPACE);
1545                 return 0;
1546            }
1547     }
1548     return DefWindowProcW(hwnd, message, wParam, lParam);
1549 }
1550
1551
1552 /**********************************************************************
1553  * CreateMDIWindowA [USER32.79] Creates a MDI child in new thread
1554  * FIXME: its in the same thread now
1555  *
1556  * RETURNS
1557  *    Success: Handle to created window
1558  *    Failure: NULL
1559  */
1560 HWND WINAPI CreateMDIWindowA(
1561     LPCSTR lpClassName,    /* [in] Pointer to registered child class name */
1562     LPCSTR lpWindowName,   /* [in] Pointer to window name */
1563     DWORD dwStyle,         /* [in] Window style */
1564     INT X,               /* [in] Horizontal position of window */
1565     INT Y,               /* [in] Vertical position of window */
1566     INT nWidth,          /* [in] Width of window */
1567     INT nHeight,         /* [in] Height of window */
1568     HWND hWndParent,     /* [in] Handle to parent window */
1569     HINSTANCE hInstance, /* [in] Handle to application instance */
1570     LPARAM lParam)         /* [in] Application-defined value */
1571 {
1572     WARN(mdi,"is only single threaded!\n");
1573     return MDI_CreateMDIWindowA(lpClassName, lpWindowName, dwStyle, X, Y, 
1574             nWidth, nHeight, hWndParent, hInstance, lParam);
1575 }
1576  
1577 /**********************************************************************
1578  * MDI_CreateMDIWindowA 
1579  * single threaded version of CreateMDIWindowA
1580  * called by CreateWindowEx32A
1581  */
1582 HWND MDI_CreateMDIWindowA(
1583     LPCSTR lpClassName,
1584     LPCSTR lpWindowName,
1585     DWORD dwStyle,
1586     INT X,
1587     INT Y,
1588     INT nWidth,
1589     INT nHeight,
1590     HWND hWndParent,
1591     HINSTANCE hInstance,
1592     LPARAM lParam)
1593 {
1594     MDICLIENTINFO* pCi;
1595     MDICREATESTRUCTA cs;
1596     WND *pWnd=WIN_FindWndPtr(hWndParent);
1597
1598     TRACE(mdi, "(%s,%s,%ld,%d,%d,%d,%d,%x,%d,%ld)\n",
1599           debugstr_a(lpClassName),debugstr_a(lpWindowName),dwStyle,X,Y,
1600           nWidth,nHeight,hWndParent,hInstance,lParam);
1601
1602     if(!pWnd){
1603         ERR(mdi," bad hwnd for MDI-client: %d\n",hWndParent);
1604         return 0;
1605     }
1606     cs.szClass=lpClassName;
1607     cs.szTitle=lpWindowName;
1608     cs.hOwner=hInstance;
1609     cs.x=X;
1610     cs.y=Y;
1611     cs.cx=nWidth;
1612     cs.cy=nHeight;
1613     cs.style=dwStyle;
1614     cs.lParam=lParam;
1615
1616     pCi=(MDICLIENTINFO *)pWnd->wExtra;
1617     
1618     return MDICreateChild(pWnd,pCi,hWndParent,&cs);
1619 }
1620
1621 /***************************************
1622  * CreateMDIWindow32W [USER32.80] Creates a MDI child in new thread
1623  *
1624  * RETURNS
1625  *    Success: Handle to created window
1626  *    Failure: NULL
1627  */
1628 HWND WINAPI CreateMDIWindowW(
1629     LPCWSTR lpClassName,    /* [in] Pointer to registered child class name */
1630     LPCWSTR lpWindowName,   /* [in] Pointer to window name */
1631     DWORD dwStyle,         /* [in] Window style */
1632     INT X,               /* [in] Horizontal position of window */
1633     INT Y,               /* [in] Vertical position of window */
1634     INT nWidth,          /* [in] Width of window */
1635     INT nHeight,         /* [in] Height of window */
1636     HWND hWndParent,     /* [in] Handle to parent window */
1637     HINSTANCE hInstance, /* [in] Handle to application instance */
1638     LPARAM lParam)         /* [in] Application-defined value */
1639 {
1640     FIXME(mdi, "(%s,%s,%ld,%d,%d,%d,%d,%x,%d,%ld): stub\n",
1641           debugstr_w(lpClassName),debugstr_w(lpWindowName),dwStyle,X,Y,
1642           nWidth,nHeight,hWndParent,hInstance,lParam);
1643     return (HWND)NULL;
1644 }
1645
1646
1647 /******************************************************************************
1648  * CreateMDIWindow32W [USER32.80]  Creates a MDI child window
1649  * single threaded version of CreateMDIWindow
1650  * called by CreateWindowEx32W(). 
1651  */
1652 HWND MDI_CreateMDIWindowW(
1653     LPCWSTR lpClassName,   /* [in] Pointer to registered child class name */
1654     LPCWSTR lpWindowName,  /* [in] Pointer to window name */
1655     DWORD dwStyle,         /* [in] Window style */
1656     INT X,               /* [in] Horizontal position of window */
1657     INT Y,               /* [in] Vertical position of window */
1658     INT nWidth,          /* [in] Width of window */
1659     INT nHeight,         /* [in] Height of window */
1660     HWND hWndParent,     /* [in] Handle to parent window */
1661     HINSTANCE hInstance, /* [in] Handle to application instance */
1662     LPARAM lParam)         /* [in] Application-defined value */
1663 {
1664     FIXME(mdi, "(%s,%s,%ld,%d,%d,%d,%d,%x,%d,%ld): stub\n",
1665           debugstr_w(lpClassName),debugstr_w(lpWindowName),dwStyle,X,Y,
1666           nWidth,nHeight,hWndParent,hInstance,lParam);
1667     return (HWND)NULL;
1668 }
1669
1670
1671 /**********************************************************************
1672  *             TranslateMDISysAccel32   (USER32.555)
1673  */
1674 BOOL WINAPI TranslateMDISysAccel( HWND hwndClient, LPMSG msg )
1675 {
1676     MSG16 msg16;
1677  
1678     STRUCT32_MSG32to16(msg,&msg16);
1679     /* MDICLIENTINFO is still the same for win32 and win16 ... */
1680     return TranslateMDISysAccel16(hwndClient,&msg16);
1681 }
1682
1683
1684 /**********************************************************************
1685  *             TranslateMDISysAccel16   (USER.451)
1686  */
1687 BOOL16 WINAPI TranslateMDISysAccel16( HWND16 hwndClient, LPMSG16 msg )
1688 {
1689     WND* clientWnd = WIN_FindWndPtr( hwndClient);
1690
1691     if( clientWnd && (msg->message == WM_KEYDOWN || msg->message == WM_SYSKEYDOWN))
1692     {
1693         MDICLIENTINFO   *ci = NULL;
1694         WND*            wnd;
1695
1696         ci = (MDICLIENTINFO*) clientWnd->wExtra;
1697         wnd = WIN_FindWndPtr(ci->hwndActiveChild);
1698         if( wnd && !(wnd->dwStyle & WS_DISABLED) )
1699         {
1700             WPARAM16    wParam = 0;
1701
1702             /* translate if the Ctrl key is down and Alt not. */
1703   
1704             if( (GetKeyState(VK_CONTROL) & 0x8000) && 
1705                !(GetKeyState(VK_MENU) & 0x8000))
1706             {
1707                 switch( msg->wParam )
1708                 {
1709                     case VK_F6:
1710                     case VK_TAB:
1711                          wParam = ( GetKeyState(VK_SHIFT) & 0x8000 )
1712                                   ? SC_NEXTWINDOW : SC_PREVWINDOW;
1713                          break;
1714                     case VK_F4:
1715                     case VK_RBUTTON:
1716                          wParam = SC_CLOSE; 
1717                          break;
1718                     default:
1719                          return 0;
1720                 }
1721                 TRACE(mdi,"wParam = %04x\n", wParam);
1722                 SendMessage16( ci->hwndActiveChild, WM_SYSCOMMAND, 
1723                                         wParam, (LPARAM)msg->wParam);
1724                 return 1;
1725             }
1726         }
1727     }
1728     return 0; /* failure */
1729 }
1730
1731
1732 /***********************************************************************
1733  *           CalcChildScroll   (USER.462)
1734  */
1735 void WINAPI CalcChildScroll16( HWND16 hwnd, WORD scroll )
1736 {
1737     SCROLLINFO info;
1738     RECT childRect, clientRect;
1739     INT  vmin, vmax, hmin, hmax, vpos, hpos;
1740     WND *pWnd, *Wnd;
1741
1742     if (!(Wnd = pWnd = WIN_FindWndPtr( hwnd ))) return;
1743     GetClientRect( hwnd, &clientRect );
1744     SetRectEmpty( &childRect );
1745
1746     for ( pWnd = pWnd->child; pWnd; pWnd = pWnd->next )
1747     {
1748           if( pWnd->dwStyle & WS_MAXIMIZE )
1749           {
1750               ShowScrollBar(hwnd, SB_BOTH, FALSE);
1751               return;
1752           }
1753           UnionRect( &childRect, &pWnd->rectWindow, &childRect );
1754     } 
1755     UnionRect( &childRect, &clientRect, &childRect );
1756
1757     hmin = childRect.left; hmax = childRect.right - clientRect.right;
1758     hpos = clientRect.left - childRect.left;
1759     vmin = childRect.top; vmax = childRect.bottom - clientRect.bottom;
1760     vpos = clientRect.top - childRect.top;
1761
1762     switch( scroll )
1763     {
1764         case SB_HORZ:
1765                         vpos = hpos; vmin = hmin; vmax = hmax;
1766         case SB_VERT:
1767                         info.cbSize = sizeof(info);
1768                         info.nMax = vmax; info.nMin = vmin; info.nPos = vpos;
1769                         info.fMask = SIF_POS | SIF_RANGE;
1770                         SetScrollInfo(hwnd, scroll, &info, TRUE);
1771                         break;
1772         case SB_BOTH:
1773                         SCROLL_SetNCSbState( Wnd, vmin, vmax, vpos,
1774                                                   hmin, hmax, hpos);
1775     }    
1776 }
1777
1778
1779 /***********************************************************************
1780  *           ScrollChildren16   (USER.463)
1781  */
1782 void WINAPI ScrollChildren16(HWND16 hWnd, UINT16 uMsg, WPARAM16 wParam, LPARAM lParam)
1783 {
1784     return ScrollChildren( hWnd, uMsg, wParam, lParam );
1785 }
1786
1787
1788 /***********************************************************************
1789  *           ScrollChildren32   (USER32.448)
1790  */
1791 void WINAPI ScrollChildren(HWND hWnd, UINT uMsg, WPARAM wParam,
1792                              LPARAM lParam)
1793 {
1794     WND *wndPtr = WIN_FindWndPtr(hWnd);
1795     INT newPos = -1;
1796     INT curPos, length, minPos, maxPos, shift;
1797
1798     if( !wndPtr ) return;
1799
1800     if( uMsg == WM_HSCROLL )
1801     {
1802         GetScrollRange(hWnd,SB_HORZ,&minPos,&maxPos);
1803         curPos = GetScrollPos(hWnd,SB_HORZ);
1804         length = (wndPtr->rectClient.right - wndPtr->rectClient.left)/2;
1805         shift = SYSMETRICS_CYHSCROLL;
1806     }
1807     else if( uMsg == WM_VSCROLL )
1808     {
1809         GetScrollRange(hWnd,SB_VERT,&minPos,&maxPos);
1810         curPos = GetScrollPos(hWnd,SB_VERT);
1811         length = (wndPtr->rectClient.bottom - wndPtr->rectClient.top)/2;
1812         shift = SYSMETRICS_CXVSCROLL;
1813     }
1814     else return;
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                         CalcChildScroll16(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.21] 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 LPRECT lpRect,
1875                 UINT cKids, const HWND *lpKids)
1876 {
1877     FIXME (mdi, "(0x%08x,0x%08x,...,%u,...): stub\n",
1878            hwndParent, wFlags, cKids);
1879
1880     return 0;
1881 }
1882
1883
1884 /******************************************************************************
1885  * TileWindows [USER32.545] Tiles MDI child windows
1886  *
1887  * RETURNS
1888  *    Success: Number of tiled windows.
1889  *    Failure: 0
1890  */
1891 WORD WINAPI
1892 TileWindows (HWND hwndParent, UINT wFlags, const LPRECT lpRect,
1893              UINT cKids, const HWND *lpKids)
1894 {
1895     FIXME (mdi, "(0x%08x,0x%08x,...,%u,...): stub\n",
1896            hwndParent, wFlags, cKids);
1897
1898     return 0;
1899 }
1900