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