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