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