[USER32] MDIClientWndProc_common(): Properly sync to old Wine (#2843)
[reactos.git] / win32ss / user / user32 / windows / mdi.c
1 /* MDI.C
2 *
3 * Copyright 1994, Bob Amstadt
4 * Copyright 1995,1996 Alex Korobka
5 * Copyright 2018 Katayama Hirofumi MZ
6 *
7 * This file contains routines to support MDI (Multiple Document
8 * Interface) features.
9 *
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2.1 of the License, or (at your option) any later version.
14 *
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
19 *
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
23 *
24 * Notes: Fairly complete implementation.
25 * Also, Excel and WinWord do _not_ use MDI so if you're trying
26 * to fix them look elsewhere.
27 *
28 * Notes on how the "More Windows..." is implemented:
29 *
30 * When we have more than 9 opened windows, a "More Windows..."
31 * option appears in the "Windows" menu. Each child window has
32 * a WND* associated with it, accessible via the children list of
33 * the parent window. This WND* has a wIDmenu member, which reflects
34 * the position of the child in the window list. For example, with
35 * 9 child windows, we could have the following pattern:
36 *
37 *
38 *
39 * Name of the child window pWndChild->wIDmenu
40 * Doc1 5000
41 * Doc2 5001
42 * Doc3 5002
43 * Doc4 5003
44 * Doc5 5004
45 * Doc6 5005
46 * Doc7 5006
47 * Doc8 5007
48 * Doc9 5008
49 *
50 *
51 * The "Windows" menu, as the "More windows..." dialog, are constructed
52 * in this order. If we add a child, we would have the following list:
53 *
54 *
55 * Name of the child window pWndChild->wIDmenu
56 * Doc1 5000
57 * Doc2 5001
58 * Doc3 5002
59 * Doc4 5003
60 * Doc5 5004
61 * Doc6 5005
62 * Doc7 5006
63 * Doc8 5007
64 * Doc9 5008
65 * Doc10 5009
66 *
67 * But only 5000 to 5008 would be displayed in the "Windows" menu. We want
68 * the last created child to be in the menu, so we swap the last child with
69 * the 9th... Doc9 will be accessible via the "More Windows..." option.
70 *
71 * Doc1 5000
72 * Doc2 5001
73 * Doc3 5002
74 * Doc4 5003
75 * Doc5 5004
76 * Doc6 5005
77 * Doc7 5006
78 * Doc8 5007
79 * Doc9 5009
80 * Doc10 5008
81 *
82 */
83
84 #include <user32.h>
85
86 WINE_DEFAULT_DEBUG_CHANNEL(mdi);
87
88 #define MDI_MAXTITLELENGTH 0xa1
89
90 #define WM_MDICALCCHILDSCROLL 0x003F /* this is exactly what Windows uses */
91
92 /* "More Windows..." definitions */
93 #define MDI_MOREWINDOWSLIMIT 9 /* after this number of windows, a "More Windows..."
94 option will appear under the Windows menu */
95 #define MDI_IDC_LISTBOX 100
96 #define IDS_MDI_MOREWINDOWS 13
97
98 #define MDIF_NEEDUPDATE 0x0001
99
100 typedef struct
101 {
102 /* At some points, particularly when switching MDI children, active and
103 * maximized MDI children may be not the same window, so we need to track
104 * them separately.
105 * The only place where we switch to/from maximized state is DefMDIChildProc
106 * WM_SIZE/SIZE_MAXIMIZED handler. We get that notification only after the
107 * ShowWindow(SW_SHOWMAXIMIZED) request, therefore window is guaranteed to
108 * be visible at the time we get the notification, and it's safe to assume
109 * that hwndChildMaximized is always visible.
110 * If the app plays games with WS_VISIBLE, WS_MAXIMIZE or any other window
111 * states it must keep coherency with USER32 on its own. This is true for
112 * Windows as well.
113 */
114 LONG reserved;
115 UINT nActiveChildren;
116 HWND hwndChildMaximized;
117 HWND hwndActiveChild;
118 HWND *child; /* array of tracked children */
119 HMENU hFrameMenu;
120 HMENU hWindowMenu;
121 UINT idFirstChild;
122 LPWSTR frameTitle;
123 UINT nTotalCreated;
124 UINT mdiFlags;
125 UINT sbRecalc; /* SB_xxx flags for scrollbar fixup */
126 HBITMAP hBmpClose; /* ReactOS modification */
127 } MDICLIENTINFO;
128
129 //static HBITMAP hBmpClose = 0;
130
131 /* ----------------- declarations ----------------- */
132 static void MDI_UpdateFrameText( HWND, HWND, BOOL, LPCWSTR);
133 static BOOL MDI_AugmentFrameMenu( HWND, HWND );
134 static BOOL MDI_RestoreFrameMenu( HWND, HWND, HBITMAP );
135 static LONG MDI_ChildActivate( HWND, HWND );
136 static LRESULT MDI_RefreshMenu(MDICLIENTINFO *);
137
138 static HWND MDI_MoreWindowsDialog(HWND);
139
140 HWND* WIN_ListChildren (HWND hWndparent)
141 {
142
143 DWORD dwCount = 0;
144 HWND* pHwnd = NULL;
145 HANDLE hHeap;
146 NTSTATUS Status;
147
148 Status = NtUserBuildHwndList ( NULL, hWndparent, FALSE, 0, 0, NULL, &dwCount );
149
150 if ( !NT_SUCCESS( Status ) )
151 return 0;
152
153 /* allocate buffer to receive HWND handles */
154 hHeap = GetProcessHeap();
155
156 pHwnd = HeapAlloc ( hHeap, 0, sizeof(HWND)*(dwCount+1) );
157 if ( !pHwnd )
158 {
159 SetLastError ( ERROR_NOT_ENOUGH_MEMORY );
160 return 0;
161 }
162
163 /* now call kernel again to fill the buffer this time */
164 Status = NtUserBuildHwndList (NULL, hWndparent, FALSE, 0, 0, pHwnd, &dwCount );
165
166 if ( !NT_SUCCESS( Status ) )
167 {
168 if ( pHwnd )
169 HeapFree ( hHeap, 0, pHwnd );
170 return 0;
171 }
172
173 pHwnd[dwCount] = (HWND) 0;
174
175 return pHwnd;
176 }
177
178 #ifdef __REACTOS__
179 void WINAPI ScrollChildren(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
180 void WINAPI CalcChildScroll(HWND hwnd, INT scroll);
181 #endif
182
183 /* -------- Miscellaneous service functions ----------
184 *
185 * MDI_GetChildByID
186 */
187 static HWND MDI_GetChildByID(HWND hwnd, UINT id, MDICLIENTINFO *ci)
188 {
189 int i;
190
191 for (i = 0; ci->nActiveChildren; i++)
192 {
193 if (GetWindowLongPtrW( ci->child[i], GWLP_ID ) == id)
194 return ci->child[i];
195 }
196 return 0;
197 }
198
199 static void MDI_PostUpdate(HWND hwnd, MDICLIENTINFO* ci, WORD recalc)
200 {
201 if( !(ci->mdiFlags & MDIF_NEEDUPDATE) )
202 {
203 ci->mdiFlags |= MDIF_NEEDUPDATE;
204 PostMessageA( hwnd, WM_MDICALCCHILDSCROLL, 0, 0);
205 }
206 ci->sbRecalc = recalc;
207 }
208
209
210 /*********************************************************************
211 * MDIClient class descriptor
212 */
213 const struct builtin_class_descr MDICLIENT_builtin_class =
214 {
215 L"MDIClient", /* name */
216 0, /* style */
217 MDIClientWndProcA, /* procA */
218 MDIClientWndProcW, /* procW */
219 sizeof(MDIWND), /* extra */
220 IDC_ARROW, /* cursor */
221 (HBRUSH)(COLOR_APPWORKSPACE+1) /* brush */
222 };
223
224
225 static MDICLIENTINFO *get_client_info( HWND client )
226 {
227 #ifdef __REACTOS__
228 return (MDICLIENTINFO *)GetWindowLongPtr(client, GWLP_MDIWND);
229 #else
230 MDICLIENTINFO *ret = NULL;
231 WND *win = WIN_GetPtr( client );
232 if (win)
233 {
234 if (win == WND_OTHER_PROCESS || win == WND_DESKTOP)
235 {
236 if (IsWindow(client)) WARN( "client %p belongs to other process\n", client );
237 return NULL;
238 }
239 if (win->flags & WIN_ISMDICLIENT)
240 ret = (MDICLIENTINFO *)win->wExtra;
241 else
242 WARN( "%p is not an MDI client\n", client );
243 WIN_ReleasePtr( win );
244 }
245 return ret;
246 #endif
247 }
248
249 static BOOL is_close_enabled(HWND hwnd, HMENU hSysMenu)
250 {
251 if (GetClassLongPtrW(hwnd, GCL_STYLE) & CS_NOCLOSE) return FALSE;
252
253 if (!hSysMenu) hSysMenu = GetSystemMenu(hwnd, FALSE);
254 if (hSysMenu)
255 {
256 UINT state = GetMenuState(hSysMenu, SC_CLOSE, MF_BYCOMMAND);
257 if (state == 0xFFFFFFFF || (state & (MF_DISABLED | MF_GRAYED)))
258 return FALSE;
259 }
260 return TRUE;
261 }
262
263 /**********************************************************************
264 * MDI_GetWindow
265 *
266 * returns "activatable" child different from the current or zero
267 */
268 static HWND MDI_GetWindow(MDICLIENTINFO *clientInfo, HWND hWnd, BOOL bNext,
269 DWORD dwStyleMask )
270 {
271 int i;
272 HWND *list;
273 HWND last = 0;
274
275 dwStyleMask |= WS_DISABLED | WS_VISIBLE;
276 if( !hWnd ) hWnd = clientInfo->hwndActiveChild;
277
278 if (!(list = WIN_ListChildren( GetParent(hWnd) ))) return 0;
279 i = 0;
280 /* start from next after hWnd */
281 while (list[i] && list[i] != hWnd) i++;
282 if (list[i]) i++;
283
284 for ( ; list[i]; i++)
285 {
286 if (GetWindow( list[i], GW_OWNER )) continue;
287 if ((GetWindowLongPtrW( list[i], GWL_STYLE ) & dwStyleMask) != WS_VISIBLE) continue;
288 last = list[i];
289 if (bNext) goto found;
290 }
291 /* now restart from the beginning */
292 for (i = 0; list[i] && list[i] != hWnd; i++)
293 {
294 if (GetWindow( list[i], GW_OWNER )) continue;
295 if ((GetWindowLongPtrW( list[i], GWL_STYLE ) & dwStyleMask) != WS_VISIBLE) continue;
296 last = list[i];
297 if (bNext) goto found;
298 }
299 found:
300 HeapFree( GetProcessHeap(), 0, list );
301 return last;
302 }
303
304 /**********************************************************************
305 * MDI_CalcDefaultChildPos
306 *
307 * It seems that the default height is about 2/3 of the client rect
308 */
309 void MDI_CalcDefaultChildPos( HWND hwndClient, INT total, LPPOINT lpPos, INT delta, UINT *id )
310 {
311 INT nstagger;
312 RECT rect;
313 INT spacing = GetSystemMetrics(SM_CYCAPTION) + GetSystemMetrics(SM_CYFRAME) - 1;
314
315 if (total < 0) /* we are called from CreateWindow */
316 {
317 MDICLIENTINFO *ci = get_client_info(hwndClient);
318 total = ci ? ci->nTotalCreated : 0; // Do not portsync wine
319 *id = ci ? ci->idFirstChild + ci->nActiveChildren : 0; // Do not portsync wine
320 TRACE("MDI child id %04x\n", *id);
321 }
322
323 GetClientRect( hwndClient, &rect );
324 if( rect.bottom - rect.top - delta >= spacing )
325 rect.bottom -= delta;
326
327 nstagger = (rect.bottom - rect.top)/(3 * spacing);
328 lpPos[1].x = (rect.right - rect.left - nstagger * spacing);
329 lpPos[1].y = (rect.bottom - rect.top - nstagger * spacing);
330 lpPos[0].x = lpPos[0].y = spacing * (total%(nstagger+1));
331 }
332
333 /**********************************************************************
334 * MDISetMenu
335 */
336 static LRESULT MDISetMenu( HWND hwnd, HMENU hmenuFrame,
337 HMENU hmenuWindow)
338 {
339 MDICLIENTINFO *ci;
340 HWND hwndFrame = GetParent(hwnd);
341
342 TRACE("%p, frame menu %p, window menu %p\n", hwnd, hmenuFrame, hmenuWindow);
343
344 if (hmenuFrame && !IsMenu(hmenuFrame))
345 {
346 WARN("hmenuFrame is not a menu handle\n");
347 return 0L;
348 }
349
350 if (hmenuWindow && !IsMenu(hmenuWindow))
351 {
352 WARN("hmenuWindow is not a menu handle\n");
353 return 0L;
354 }
355
356 if (!(ci = get_client_info( hwnd ))) return 0;
357
358 TRACE("old frame menu %p, old window menu %p\n", ci->hFrameMenu, ci->hWindowMenu);
359
360 if (hmenuFrame)
361 {
362 if (hmenuFrame == ci->hFrameMenu) return (LRESULT)hmenuFrame;
363
364 if (ci->hwndChildMaximized)
365 MDI_RestoreFrameMenu( hwndFrame, ci->hwndChildMaximized, ci->hBmpClose );
366 }
367
368 if( hmenuWindow && hmenuWindow != ci->hWindowMenu )
369 {
370 /* delete menu items from ci->hWindowMenu
371 * and add them to hmenuWindow */
372 /* Agent newsreader calls this function with ci->hWindowMenu == NULL */
373 if( ci->hWindowMenu && ci->nActiveChildren )
374 {
375 UINT nActiveChildren_old = ci->nActiveChildren;
376
377 /* Remove all items from old Window menu */
378 ci->nActiveChildren = 0;
379 MDI_RefreshMenu(ci);
380
381 ci->hWindowMenu = hmenuWindow;
382
383 /* Add items to the new Window menu */
384 ci->nActiveChildren = nActiveChildren_old;
385 MDI_RefreshMenu(ci);
386 }
387 else
388 ci->hWindowMenu = hmenuWindow;
389 }
390
391 if (hmenuFrame)
392 {
393 SetMenu(hwndFrame, hmenuFrame);
394 if( hmenuFrame != ci->hFrameMenu )
395 {
396 HMENU oldFrameMenu = ci->hFrameMenu;
397
398 ci->hFrameMenu = hmenuFrame;
399 if (ci->hwndChildMaximized)
400 MDI_AugmentFrameMenu( hwndFrame, ci->hwndChildMaximized );
401
402 return (LRESULT)oldFrameMenu;
403 }
404 }
405
406 return 0;
407 }
408
409 /**********************************************************************
410 * MDIRefreshMenu
411 */
412 static LRESULT MDI_RefreshMenu(MDICLIENTINFO *ci)
413 {
414 UINT i, count, visible, id;
415 WCHAR buf[MDI_MAXTITLELENGTH];
416
417 TRACE("children %u, window menu %p\n", ci->nActiveChildren, ci->hWindowMenu);
418
419 if (!ci->hWindowMenu)
420 return 0;
421
422 if (!IsMenu(ci->hWindowMenu))
423 {
424 WARN("Window menu handle %p is no longer valid\n", ci->hWindowMenu);
425 return 0;
426 }
427
428 /* Windows finds the last separator in the menu, and if after it
429 * there is a menu item with MDI magic ID removes all existing
430 * menu items after it, and then adds visible MDI children.
431 */
432 count = GetMenuItemCount(ci->hWindowMenu);
433 for (i = 0; i < count; i++)
434 {
435 MENUITEMINFOW mii;
436
437 memset(&mii, 0, sizeof(mii));
438 mii.cbSize = sizeof(mii);
439 mii.fMask = MIIM_TYPE;
440 if (GetMenuItemInfoW(ci->hWindowMenu, i, TRUE, &mii))
441 {
442 if (mii.fType & MF_SEPARATOR)
443 {
444 /* Windows checks only ID of the menu item */
445 memset(&mii, 0, sizeof(mii));
446 mii.cbSize = sizeof(mii);
447 mii.fMask = MIIM_ID;
448 if (GetMenuItemInfoW(ci->hWindowMenu, i + 1, TRUE, &mii))
449 {
450 if (mii.wID == ci->idFirstChild)
451 {
452 TRACE("removing %u items including separator\n", count - i);
453 while (RemoveMenu(ci->hWindowMenu, i, MF_BYPOSITION))
454 /* nothing */;
455
456 break;
457 }
458 }
459 }
460 }
461 }
462
463 visible = 0;
464 for (i = 0; i < ci->nActiveChildren; i++)
465 {
466 if (GetWindowLongPtrW(ci->child[i], GWL_STYLE) & WS_VISIBLE)
467 {
468 id = ci->idFirstChild + visible;
469
470 if (visible == MDI_MOREWINDOWSLIMIT)
471 {
472 LoadStringW(User32Instance, IDS_MDI_MOREWINDOWS, buf, sizeof(buf)/sizeof(WCHAR));
473 AppendMenuW(ci->hWindowMenu, MF_STRING, id, buf);
474 break;
475 }
476
477 if (!visible)
478 /* Visio expects that separator has id 0 */
479 AppendMenuW(ci->hWindowMenu, MF_SEPARATOR, 0, NULL);
480
481 visible++;
482
483 SetWindowLongPtrW(ci->child[i], GWLP_ID, id);
484
485 buf[0] = '&';
486 buf[1] = '0' + visible;
487 buf[2] = ' ';
488 InternalGetWindowText(ci->child[i], buf + 3, sizeof(buf)/sizeof(WCHAR) - 3);
489 TRACE("Adding %p, id %u %s\n", ci->child[i], id, debugstr_w(buf));
490 AppendMenuW(ci->hWindowMenu, MF_STRING, id, buf);
491
492 if (ci->child[i] == ci->hwndActiveChild)
493 CheckMenuItem(ci->hWindowMenu, id, MF_CHECKED);
494 }
495 else
496 TRACE("MDI child %p is not visible, skipping\n", ci->child[i]);
497 }
498
499 return (LRESULT)ci->hFrameMenu;
500 }
501
502
503 /* ------------------ MDI child window functions ---------------------- */
504
505 /**********************************************************************
506 * MDI_ChildGetMinMaxInfo
507 *
508 * Note: The rule here is that client rect of the maximized MDI child
509 * is equal to the client rect of the MDI client window.
510 */
511 static void MDI_ChildGetMinMaxInfo( HWND client, HWND hwnd, MINMAXINFO* lpMinMax )
512 {
513 RECT rect;
514
515 GetClientRect( client, &rect );
516 AdjustWindowRectEx( &rect, GetWindowLongPtrW( hwnd, GWL_STYLE ),
517 0, GetWindowLongPtrW( hwnd, GWL_EXSTYLE ));
518
519 lpMinMax->ptMaxSize.x = rect.right -= rect.left;
520 lpMinMax->ptMaxSize.y = rect.bottom -= rect.top;
521
522 lpMinMax->ptMaxPosition.x = rect.left;
523 lpMinMax->ptMaxPosition.y = rect.top;
524
525 TRACE("max rect %s\n", wine_dbgstr_rect(&rect));
526 }
527
528 /**********************************************************************
529 * MDI_SwitchActiveChild
530 *
531 * Note: SetWindowPos sends WM_CHILDACTIVATE to the child window that is
532 * being activated
533 */
534 static void MDI_SwitchActiveChild( MDICLIENTINFO *ci, HWND hwndTo, BOOL activate )
535 {
536 HWND hwndPrev;
537
538 hwndPrev = ci->hwndActiveChild;
539
540 TRACE("from %p, to %p\n", hwndPrev, hwndTo);
541
542 if ( hwndTo != hwndPrev )
543 {
544 BOOL was_zoomed = IsZoomed(hwndPrev);
545
546 if (was_zoomed)
547 {
548 /* restore old MDI child */
549 SendMessageW( hwndPrev, WM_SETREDRAW, FALSE, 0 );
550 ShowWindow( hwndPrev, SW_RESTORE );
551 SendMessageW( hwndPrev, WM_SETREDRAW, TRUE, 0 );
552
553 /* activate new MDI child */
554 SetWindowPos( hwndTo, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE );
555 /* maximize new MDI child */
556 ShowWindow( hwndTo, SW_MAXIMIZE );
557 }
558 /* activate new MDI child */
559 SetWindowPos( hwndTo, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | (activate ? 0 : SWP_NOACTIVATE) );
560 }
561 }
562
563
564 /**********************************************************************
565 * MDIDestroyChild
566 */
567 static LRESULT MDIDestroyChild( HWND client, MDICLIENTINFO *ci,
568 HWND child, BOOL flagDestroy )
569 {
570 UINT i;
571
572 TRACE("# of managed children %u\n", ci->nActiveChildren);
573
574 if( child == ci->hwndActiveChild )
575 {
576 HWND next = MDI_GetWindow(ci, child, TRUE, 0);
577 if (next)
578 MDI_SwitchActiveChild(ci, next, TRUE);
579 else
580 {
581 ShowWindow(child, SW_HIDE);
582 if (child == ci->hwndChildMaximized)
583 {
584 HWND frame = GetParent(client);
585 MDI_RestoreFrameMenu(frame, child, ci->hBmpClose);
586 ci->hwndChildMaximized = 0;
587 MDI_UpdateFrameText(frame, client, TRUE, NULL);
588 }
589 if (flagDestroy)
590 MDI_ChildActivate(client, 0);
591 }
592 }
593
594 for (i = 0; i < ci->nActiveChildren; i++)
595 {
596 if (ci->child[i] == child)
597 {
598 HWND *new_child = HeapAlloc(GetProcessHeap(), 0, (ci->nActiveChildren - 1) * sizeof(HWND));
599 if (new_child != NULL)
600 {
601 memcpy(new_child, ci->child, i * sizeof(HWND));
602 if (i + 1 < ci->nActiveChildren)
603 memcpy(new_child + i, ci->child + i + 1, (ci->nActiveChildren - i - 1) * sizeof(HWND));
604 HeapFree(GetProcessHeap(), 0, ci->child);
605 ci->child = new_child;
606 }
607 else
608 {
609 UINT c;
610 for (c = i; c < ci->nActiveChildren - 1; c++)
611 {
612 ci->child[c] = ci->child[c+1];
613 }
614 }
615
616 ci->nActiveChildren--;
617 break;
618 }
619 }
620
621 if (flagDestroy)
622 {
623 SendMessageW(client, WM_MDIREFRESHMENU, 0, 0);
624 MDI_PostUpdate(GetParent(child), ci, SB_BOTH+1);
625 DestroyWindow(child);
626 }
627
628 TRACE("child destroyed - %p\n", child);
629 return 0;
630 }
631
632
633 /**********************************************************************
634 * MDI_ChildActivate
635 *
636 * Called in response to WM_CHILDACTIVATE, or when last MDI child
637 * is being deactivated.
638 */
639 static LONG MDI_ChildActivate( HWND client, HWND child )
640 {
641 MDICLIENTINFO *clientInfo;
642 HWND prevActiveWnd, frame;
643 BOOL isActiveFrameWnd;
644
645 clientInfo = get_client_info( client );
646
647 if (clientInfo->hwndActiveChild == child) return 0;
648
649 TRACE("%p\n", child);
650
651 frame = GetParent(client);
652 isActiveFrameWnd = (GetActiveWindow() == frame);
653 prevActiveWnd = clientInfo->hwndActiveChild;
654
655 /* deactivate prev. active child */
656 if(prevActiveWnd)
657 {
658 SendMessageW( prevActiveWnd, WM_NCACTIVATE, FALSE, 0L );
659 SendMessageW( prevActiveWnd, WM_MDIACTIVATE, (WPARAM)prevActiveWnd, (LPARAM)child);
660 }
661
662 MDI_SwitchActiveChild( clientInfo, child, FALSE );
663 clientInfo->hwndActiveChild = child;
664
665 MDI_RefreshMenu(clientInfo);
666
667 if( isActiveFrameWnd )
668 {
669 SendMessageW( child, WM_NCACTIVATE, TRUE, 0L);
670 /* Let the client window manage focus for children, but if the focus
671 * is already on the client (for instance this is the 1st child) then
672 * SetFocus won't work. It appears that Windows sends WM_SETFOCUS
673 * manually in this case.
674 */
675 if (SetFocus(client) == client)
676 SendMessageW( client, WM_SETFOCUS, (WPARAM)client, 0 );
677 }
678
679 SendMessageW( child, WM_MDIACTIVATE, (WPARAM)prevActiveWnd, (LPARAM)child );
680 return TRUE;
681 }
682
683 /* -------------------- MDI client window functions ------------------- */
684
685 /**********************************************************************
686 * CreateMDIMenuBitmap
687 */
688 static HBITMAP CreateMDIMenuBitmap(void)
689 {
690 HDC hDCSrc = CreateCompatibleDC(0);
691 HDC hDCDest = CreateCompatibleDC(hDCSrc);
692 HBITMAP hbClose = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_OLD_CLOSE) );
693 HBITMAP hbCopy;
694 HBITMAP hobjSrc, hobjDest;
695
696 hobjSrc = SelectObject(hDCSrc, hbClose);
697 hbCopy = CreateCompatibleBitmap(hDCSrc,GetSystemMetrics(SM_CXSIZE),GetSystemMetrics(SM_CYSIZE));
698 hobjDest = SelectObject(hDCDest, hbCopy);
699
700 BitBlt(hDCDest, 0, 0, GetSystemMetrics(SM_CXSIZE), GetSystemMetrics(SM_CYSIZE),
701 hDCSrc, GetSystemMetrics(SM_CXSIZE), 0, SRCCOPY);
702
703 SelectObject(hDCSrc, hobjSrc);
704 DeleteObject(hbClose);
705 DeleteDC(hDCSrc);
706
707 hobjSrc = SelectObject( hDCDest, GetStockObject(BLACK_PEN) );
708
709 MoveToEx( hDCDest, GetSystemMetrics(SM_CXSIZE) - 1, 0, NULL );
710 LineTo( hDCDest, GetSystemMetrics(SM_CXSIZE) - 1, GetSystemMetrics(SM_CYSIZE) - 1);
711
712 SelectObject(hDCDest, hobjSrc );
713 SelectObject(hDCDest, hobjDest);
714 DeleteDC(hDCDest);
715
716 return hbCopy;
717 }
718
719 /**********************************************************************
720 * MDICascade
721 */
722 static LONG MDICascade( HWND client, MDICLIENTINFO *ci )
723 {
724 HWND *win_array;
725 BOOL has_icons = FALSE;
726 int i, total;
727
728 if (ci->hwndChildMaximized)
729 SendMessageW(client, WM_MDIRESTORE, (WPARAM)ci->hwndChildMaximized, 0);
730
731 if (ci->nActiveChildren == 0) return 0;
732
733 if (!(win_array = WIN_ListChildren( client ))) return 0;
734
735 /* remove all the windows we don't want */
736 for (i = total = 0; win_array[i]; i++)
737 {
738 if (!IsWindowVisible( win_array[i] )) continue;
739 if (GetWindow( win_array[i], GW_OWNER )) continue; /* skip owned windows */
740 if (IsIconic( win_array[i] ))
741 {
742 has_icons = TRUE;
743 continue;
744 }
745 win_array[total++] = win_array[i];
746 }
747 win_array[total] = 0;
748
749 if (total)
750 {
751 INT delta = 0, n = 0, i;
752 POINT pos[2];
753 if (has_icons) delta = GetSystemMetrics(SM_CYICONSPACING) + GetSystemMetrics(SM_CYICON);
754
755 /* walk the list (backwards) and move windows */
756 for (i = total - 1; i >= 0; i--)
757 {
758 LONG style;
759 LONG posOptions = SWP_DRAWFRAME | SWP_NOACTIVATE | SWP_NOZORDER;
760
761 MDI_CalcDefaultChildPos(client, n++, pos, delta, NULL);
762 TRACE("move %p to (%ld,%ld) size [%ld,%ld]\n",
763 win_array[i], pos[0].x, pos[0].y, pos[1].x, pos[1].y);
764 style = GetWindowLongW(win_array[i], GWL_STYLE);
765
766 if (!(style & WS_SIZEBOX)) posOptions |= SWP_NOSIZE;
767 SetWindowPos( win_array[i], 0, pos[0].x, pos[0].y, pos[1].x, pos[1].y,
768 posOptions);
769 }
770 }
771 HeapFree( GetProcessHeap(), 0, win_array );
772
773 if (has_icons) ArrangeIconicWindows( client );
774 return 0;
775 }
776
777 /**********************************************************************
778 * MDITile
779 */
780 static void MDITile( HWND client, MDICLIENTINFO *ci, WPARAM wParam )
781 {
782 HWND *win_array;
783 int i, total, rows, columns;
784 BOOL has_icons = FALSE;
785
786 if (ci->hwndChildMaximized)
787 SendMessageW(client, WM_MDIRESTORE, (WPARAM)ci->hwndChildMaximized, 0);
788
789 if (ci->nActiveChildren == 0) return;
790
791 if (!(win_array = WIN_ListChildren( client ))) return;
792
793 /* remove all the windows we don't want */
794 for (i = total = rows = 0; win_array[i]; i++)
795 {
796 if (!IsWindowVisible( win_array[i] )) continue;
797 if (GetWindow( win_array[i], GW_OWNER )) continue; /* skip owned windows (icon titles) */
798 if (IsIconic( win_array[i] ))
799 {
800 has_icons = TRUE;
801 continue;
802 }
803 if ((wParam & MDITILE_SKIPDISABLED) && !IsWindowEnabled( win_array[i] )) continue;
804 if(total == (rows * (rows + 2))) rows++; /* total+1 == (rows+1)*(rows+1) */
805 win_array[total++] = win_array[i];
806 }
807 win_array[total] = 0;
808
809 TRACE("%u windows to tile\n", total);
810
811 if (total)
812 {
813 HWND *pWnd = win_array;
814 RECT rect;
815 int x, y, xsize, ysize;
816 int r, c, i;
817
818 GetClientRect(client,&rect);
819 columns = total/rows;
820 //while(total < rows*columns) rows++;
821
822 if( wParam & MDITILE_HORIZONTAL ) /* version >= 3.1 */
823 {
824 i = rows;
825 rows = columns; /* exchange r and c */
826 columns = i;
827 }
828
829 if (has_icons)
830 {
831 y = rect.bottom - 2 * GetSystemMetrics(SM_CYICONSPACING) - GetSystemMetrics(SM_CYICON);
832 rect.bottom = ( y - GetSystemMetrics(SM_CYICON) < rect.top )? rect.bottom: y;
833 }
834
835 ysize = rect.bottom / rows;
836 xsize = rect.right / columns;
837
838 for (x = i = 0, c = 1; c <= columns && *pWnd; c++)
839 {
840 if (c == columns)
841 {
842 rows = total - i;
843 ysize = rect.bottom / rows;
844 }
845
846 y = 0;
847 for (r = 1; r <= rows && *pWnd; r++, i++)
848 {
849 LONG posOptions = SWP_DRAWFRAME | SWP_NOACTIVATE | SWP_NOZORDER;
850 LONG style = GetWindowLongW(win_array[i], GWL_STYLE);
851 if (!(style & WS_SIZEBOX)) posOptions |= SWP_NOSIZE;
852
853 SetWindowPos(*pWnd, 0, x, y, xsize, ysize, posOptions);
854 y += ysize;
855 pWnd++;
856 }
857 x += xsize;
858 }
859 }
860 HeapFree( GetProcessHeap(), 0, win_array );
861 if (has_icons) ArrangeIconicWindows( client );
862 }
863
864 /* ----------------------- Frame window ---------------------------- */
865
866
867 /**********************************************************************
868 * MDI_AugmentFrameMenu
869 */
870 static BOOL MDI_AugmentFrameMenu( HWND frame, HWND hChild )
871 {
872 HMENU menu = GetMenu( frame );
873 HMENU hSysPopup;
874 HBITMAP hSysMenuBitmap = 0;
875 HICON hIcon;
876 INT nItems;
877 UINT iId;
878
879 TRACE("frame %p,child %p\n",frame,hChild);
880
881 if( !menu ) return FALSE;
882 //// ReactOS start
883 /* if the system buttons already exist do not add them again */
884 nItems = GetMenuItemCount(menu) - 1;
885 iId = GetMenuItemID(menu,nItems) ;
886 if (iId == SC_RESTORE || iId == SC_CLOSE)
887 {
888 ERR("system buttons already exist\n");
889 return FALSE;
890 }
891 //// End
892 /* create a copy of sysmenu popup and insert it into frame menu bar */
893 if (!(hSysPopup = GetSystemMenu(hChild, FALSE)))
894 {
895 TRACE("child %p doesn't have a system menu\n", hChild);
896 return FALSE;
897 }
898
899 AppendMenuW(menu, MF_HELP | MF_BITMAP,
900 SC_CLOSE, is_close_enabled(hChild, hSysPopup) ?
901 (LPCWSTR)HBMMENU_MBAR_CLOSE : (LPCWSTR)HBMMENU_MBAR_CLOSE_D );
902 AppendMenuW(menu, MF_HELP | MF_BITMAP,
903 SC_RESTORE, (LPCWSTR)HBMMENU_MBAR_RESTORE );
904 AppendMenuW(menu, MF_HELP | MF_BITMAP,
905 SC_MINIMIZE, (LPCWSTR)HBMMENU_MBAR_MINIMIZE ) ;
906
907 /* The system menu is replaced by the child icon */
908 hIcon = (HICON)SendMessageW(hChild, WM_GETICON, ICON_SMALL, 0);
909 if (!hIcon)
910 hIcon = (HICON)GetClassLongPtrW(hChild, GCLP_HICONSM);
911 if (!hIcon)
912 hIcon = (HICON)SendMessageW(hChild, WM_GETICON, ICON_BIG, 0);
913 if (!hIcon)
914 hIcon = (HICON)GetClassLongPtrW(hChild, GCLP_HICON);
915 if (!hIcon)
916 hIcon = LoadImageW(0, (LPWSTR)IDI_WINLOGO, IMAGE_ICON, GetSystemMetrics(SM_CXSMICON),
917 GetSystemMetrics(SM_CYSMICON), LR_DEFAULTCOLOR);
918 if (hIcon)
919 {
920 HDC hMemDC;
921 HBITMAP hBitmap, hOldBitmap;
922 HBRUSH hBrush;
923 HDC hdc = GetDC(hChild);
924
925 if (hdc)
926 {
927 int cx, cy;
928 cx = GetSystemMetrics(SM_CXSMICON);
929 cy = GetSystemMetrics(SM_CYSMICON);
930 hMemDC = CreateCompatibleDC(hdc);
931 hBitmap = CreateCompatibleBitmap(hdc, cx, cy);
932 hOldBitmap = SelectObject(hMemDC, hBitmap);
933 SetMapMode(hMemDC, MM_TEXT);
934 hBrush = CreateSolidBrush(GetSysColor(COLOR_MENU));
935 DrawIconEx(hMemDC, 0, 0, hIcon, cx, cy, 0, hBrush, DI_NORMAL);
936 SelectObject (hMemDC, hOldBitmap);
937 DeleteObject(hBrush);
938 DeleteDC(hMemDC);
939 ReleaseDC(hChild, hdc);
940 hSysMenuBitmap = hBitmap;
941 }
942 }
943
944 if( !InsertMenuA(menu,0,MF_BYPOSITION | MF_BITMAP | MF_POPUP,
945 (UINT_PTR)hSysPopup, (LPSTR)hSysMenuBitmap))
946 {
947 TRACE("not inserted\n");
948 DestroyMenu(hSysPopup);
949 return FALSE;
950 }
951
952 EnableMenuItem(hSysPopup, SC_SIZE, MF_BYCOMMAND | MF_GRAYED);
953 EnableMenuItem(hSysPopup, SC_MOVE, MF_BYCOMMAND | MF_GRAYED);
954 EnableMenuItem(hSysPopup, SC_MAXIMIZE, MF_BYCOMMAND | MF_GRAYED);
955 SetMenuDefaultItem(hSysPopup, SC_CLOSE, FALSE);
956
957 /* redraw menu */
958 DrawMenuBar(frame);
959
960 return TRUE;
961 }
962
963 /**********************************************************************
964 * MDI_RestoreFrameMenu
965 */
966 static BOOL MDI_RestoreFrameMenu( HWND frame, HWND hChild, HBITMAP hBmpClose )
967 {
968 MENUITEMINFOW menuInfo;
969 HMENU menu = GetMenu( frame );
970 INT nItems;
971 UINT iId;
972
973 TRACE("frame %p,child %p\n",frame, hChild);
974
975 if (!menu) return FALSE;
976
977 /* if there is no system buttons then nothing to do */
978 nItems = GetMenuItemCount(menu) - 1;
979 iId = GetMenuItemID(menu, nItems);
980 if ( !(iId == SC_RESTORE || iId == SC_CLOSE) )
981 {
982 ERR("no system buttons then nothing to do\n");
983 return FALSE;
984 }
985
986 /*
987 * Remove the system menu, If that menu is the icon of the window
988 * as it is in win95, we have to delete the bitmap.
989 */
990 memset(&menuInfo, 0, sizeof(menuInfo));
991 menuInfo.cbSize = sizeof(menuInfo);
992 menuInfo.fMask = MIIM_DATA | MIIM_TYPE | MIIM_BITMAP;
993
994 GetMenuItemInfoW(menu,
995 0,
996 TRUE,
997 &menuInfo);
998
999 RemoveMenu(menu,0,MF_BYPOSITION);
1000
1001 if ( (menuInfo.fType & MFT_BITMAP) &&
1002 (menuInfo.dwTypeData != 0) &&
1003 (menuInfo.dwTypeData != (LPWSTR)hBmpClose) )
1004 {
1005 DeleteObject(menuInfo.dwTypeData);
1006 }
1007
1008 if ( menuInfo.hbmpItem != 0 )
1009 DeleteObject(menuInfo.hbmpItem);
1010
1011 /* close */
1012 DeleteMenu(menu, SC_CLOSE, MF_BYCOMMAND);
1013 /* restore */
1014 DeleteMenu(menu, SC_RESTORE, MF_BYCOMMAND);
1015 /* minimize */
1016 DeleteMenu(menu, SC_MINIMIZE, MF_BYCOMMAND);
1017
1018 DrawMenuBar(frame);
1019
1020 return TRUE;
1021 }
1022
1023
1024 /**********************************************************************
1025 * MDI_UpdateFrameText
1026 *
1027 * used when child window is maximized/restored
1028 *
1029 * Note: lpTitle can be NULL
1030 */
1031 static void MDI_UpdateFrameText( HWND frame, HWND hClient, BOOL repaint, LPCWSTR lpTitle )
1032 {
1033 WCHAR lpBuffer[MDI_MAXTITLELENGTH+1];
1034 MDICLIENTINFO *ci = get_client_info( hClient );
1035
1036 TRACE("frameText %s\n", debugstr_w(lpTitle));
1037
1038 if (!ci) return;
1039
1040 if (!lpTitle && !ci->frameTitle) /* first time around, get title from the frame window */
1041 {
1042 GetWindowTextW( frame, lpBuffer, sizeof(lpBuffer)/sizeof(WCHAR) );
1043 lpTitle = lpBuffer;
1044 }
1045
1046 /* store new "default" title if lpTitle is not NULL */
1047 if (lpTitle)
1048 {
1049 HeapFree( GetProcessHeap(), 0, ci->frameTitle );
1050 if ((ci->frameTitle = HeapAlloc( GetProcessHeap(), 0, (strlenW(lpTitle)+1)*sizeof(WCHAR))))
1051 strcpyW( ci->frameTitle, lpTitle );
1052 }
1053
1054 if (ci->frameTitle)
1055 {
1056 if (ci->hwndChildMaximized)
1057 {
1058 /* combine frame title and child title if possible */
1059
1060 static const WCHAR lpBracket[] = {' ','-',' ','[',0};
1061 static const WCHAR lpBracket2[] = {']',0};
1062 int i_frame_text_length = strlenW(ci->frameTitle);
1063
1064 lstrcpynW( lpBuffer, ci->frameTitle, MDI_MAXTITLELENGTH);
1065
1066 if( i_frame_text_length + 6 < MDI_MAXTITLELENGTH )
1067 {
1068 strcatW( lpBuffer, lpBracket );
1069 if (GetWindowTextW( ci->hwndActiveChild, lpBuffer + i_frame_text_length + 4,
1070 MDI_MAXTITLELENGTH - i_frame_text_length - 5 ))
1071 strcatW( lpBuffer, lpBracket2 );
1072 else
1073 lpBuffer[i_frame_text_length] = 0; /* remove bracket */
1074 }
1075 }
1076 else
1077 {
1078 lstrcpynW(lpBuffer, ci->frameTitle, MDI_MAXTITLELENGTH+1 );
1079 }
1080 }
1081 else
1082 lpBuffer[0] = '\0';
1083
1084 DefWindowProcW( frame, WM_SETTEXT, 0, (LPARAM)lpBuffer );
1085
1086 if (repaint)
1087 {
1088 if (!NtUserCallTwoParam((DWORD_PTR)frame,DC_ACTIVE,TWOPARAM_ROUTINE_REDRAWTITLE))
1089 SetWindowPos( frame, 0,0,0,0,0, SWP_FRAMECHANGED |
1090 SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER );
1091 }
1092 }
1093
1094
1095 /* ----------------------------- Interface ---------------------------- */
1096
1097
1098 /**********************************************************************
1099 * MDIClientWndProc_common
1100 */
1101 LRESULT WINAPI MDIClientWndProc_common( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam, BOOL unicode )
1102 {
1103 MDICLIENTINFO *ci = NULL;
1104
1105 TRACE("%p %04x (%s) %08lx %08lx\n", hwnd, message, SPY_GetMsgName(message, hwnd), wParam, lParam);
1106
1107 if (!(ci = get_client_info( hwnd )))
1108 {
1109 #ifdef __REACTOS__
1110 if (message == WM_NCCREATE)
1111 {
1112 if (!(ci = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*ci))))
1113 return FALSE;
1114 SetWindowLongPtrW( hwnd, GWLP_MDIWND, (LONG_PTR)ci );
1115 ci->hBmpClose = 0;
1116 NtUserSetWindowFNID( hwnd, FNID_MDICLIENT); // wine uses WIN_ISMDICLIENT
1117 }
1118 #else
1119 if (message == WM_NCCREATE) win_set_flags( hwnd, WIN_ISMDICLIENT, 0 );
1120 #endif
1121 return unicode ? DefWindowProcW( hwnd, message, wParam, lParam ) :
1122 DefWindowProcA( hwnd, message, wParam, lParam );
1123 }
1124
1125 switch (message)
1126 {
1127 case WM_CREATE:
1128 {
1129 /* Since we are using only cs->lpCreateParams, we can safely
1130 * cast to LPCREATESTRUCTA here */
1131 LPCREATESTRUCTA cs = (LPCREATESTRUCTA)lParam;
1132 LPCLIENTCREATESTRUCT ccs = (LPCLIENTCREATESTRUCT)cs->lpCreateParams;
1133
1134 ci->hWindowMenu = ccs->hWindowMenu;
1135 ci->idFirstChild = ccs->idFirstChild;
1136 ci->hwndChildMaximized = 0;
1137 ci->child = NULL;
1138 ci->nActiveChildren = 0;
1139 ci->nTotalCreated = 0;
1140 ci->frameTitle = NULL;
1141 ci->mdiFlags = 0;
1142 ci->hFrameMenu = GetMenu(cs->hwndParent);
1143
1144 if (!ci->hBmpClose) ci->hBmpClose = CreateMDIMenuBitmap();
1145
1146 TRACE("Client created: hwnd %p, Window menu %p, idFirst = %04x\n",
1147 hwnd, ci->hWindowMenu, ci->idFirstChild );
1148 return 0;
1149 }
1150
1151 case WM_DESTROY:
1152 {
1153 if( ci->hwndChildMaximized )
1154 MDI_RestoreFrameMenu(GetParent(hwnd), ci->hwndChildMaximized, ci->hBmpClose);
1155
1156 ci->nActiveChildren = 0;
1157 MDI_RefreshMenu(ci);
1158
1159 HeapFree( GetProcessHeap(), 0, ci->child );
1160 HeapFree( GetProcessHeap(), 0, ci->frameTitle );
1161 #ifdef __REACTOS__
1162 HeapFree( GetProcessHeap(), 0, ci );
1163 SetWindowLongPtrW( hwnd, GWLP_MDIWND, 0 );
1164 #endif
1165 return 0;
1166 }
1167
1168 #ifdef __REACTOS__
1169 case WM_NCDESTROY:
1170 {
1171 NtUserSetWindowFNID(hwnd, FNID_DESTROY);
1172 return 0;
1173 }
1174 #endif
1175
1176 case WM_MDIACTIVATE:
1177 {
1178 if( ci->hwndActiveChild != (HWND)wParam )
1179 SetWindowPos((HWND)wParam, 0,0,0,0,0, SWP_NOSIZE | SWP_NOMOVE);
1180 return 0;
1181 }
1182
1183 case WM_MDICASCADE:
1184 return MDICascade(hwnd, ci);
1185
1186 case WM_MDICREATE:
1187 if (lParam)
1188 {
1189 HWND child;
1190
1191 if (unicode)
1192 {
1193 MDICREATESTRUCTW *csW = (MDICREATESTRUCTW *)lParam;
1194 child = CreateWindowExW(WS_EX_MDICHILD, csW->szClass,
1195 csW->szTitle, csW->style,
1196 csW->x, csW->y, csW->cx, csW->cy,
1197 hwnd, 0, csW->hOwner,
1198 (LPVOID)csW->lParam);
1199 }
1200 else
1201 {
1202 MDICREATESTRUCTA *csA = (MDICREATESTRUCTA *)lParam;
1203 child = CreateWindowExA(WS_EX_MDICHILD, csA->szClass,
1204 csA->szTitle, csA->style,
1205 csA->x, csA->y, csA->cx, csA->cy,
1206 hwnd, 0, csA->hOwner,
1207 (LPVOID)csA->lParam);
1208 }
1209 return (LRESULT)child;
1210 }
1211 return 0;
1212
1213 case WM_MDIDESTROY:
1214 return MDIDestroyChild( hwnd, ci, (HWND)wParam, TRUE );
1215
1216 case WM_MDIGETACTIVE:
1217 if (lParam) *(BOOL *)lParam = IsZoomed(ci->hwndActiveChild);
1218 return (LRESULT)ci->hwndActiveChild;
1219
1220 case WM_MDIICONARRANGE:
1221 ci->mdiFlags |= MDIF_NEEDUPDATE;
1222 ArrangeIconicWindows( hwnd );
1223 ci->sbRecalc = SB_BOTH+1;
1224 #ifdef __REACTOS__
1225 PostMessageA( hwnd, WM_MDICALCCHILDSCROLL, 0, 0 ); //// ReactOS: Post not send!
1226 #else
1227 SendMessageW( hwnd, WM_MDICALCCHILDSCROLL, 0, 0 );
1228 #endif
1229 return 0;
1230
1231 case WM_MDIMAXIMIZE:
1232 ShowWindow( (HWND)wParam, SW_MAXIMIZE );
1233 return 0;
1234
1235 case WM_MDINEXT: /* lParam != 0 means previous window */
1236 {
1237 HWND hwnd = wParam ? WIN_GetFullHandle((HWND)wParam) : ci->hwndActiveChild;
1238 HWND next = MDI_GetWindow( ci, hwnd, !lParam, 0 );
1239 MDI_SwitchActiveChild( ci, next, TRUE );
1240 if(!lParam)
1241 SetWindowPos(hwnd, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE);
1242 break;
1243 }
1244
1245 case WM_MDIRESTORE:
1246 ShowWindow( (HWND)wParam, SW_SHOWNORMAL );
1247 return 0;
1248
1249 case WM_MDISETMENU:
1250 return MDISetMenu( hwnd, (HMENU)wParam, (HMENU)lParam );
1251
1252 case WM_MDIREFRESHMENU:
1253 return MDI_RefreshMenu( ci );
1254
1255 case WM_MDITILE:
1256 ci->mdiFlags |= MDIF_NEEDUPDATE;
1257 ShowScrollBar( hwnd, SB_BOTH, FALSE );
1258 MDITile( hwnd, ci, wParam );
1259 ci->mdiFlags &= ~MDIF_NEEDUPDATE;
1260 return 0;
1261
1262 case WM_VSCROLL:
1263 case WM_HSCROLL:
1264 ci->mdiFlags |= MDIF_NEEDUPDATE;
1265 ScrollChildren( hwnd, message, wParam, lParam );
1266 ci->mdiFlags &= ~MDIF_NEEDUPDATE;
1267 return 0;
1268
1269 case WM_SETFOCUS:
1270 if (ci->hwndActiveChild && !IsIconic( ci->hwndActiveChild ))
1271 SetFocus( ci->hwndActiveChild );
1272 return 0;
1273
1274 case WM_NCACTIVATE:
1275 if( ci->hwndActiveChild )
1276 SendMessageW(ci->hwndActiveChild, message, wParam, lParam);
1277 break;
1278
1279 case WM_PARENTNOTIFY:
1280 switch (LOWORD(wParam))
1281 {
1282 case WM_CREATE:
1283 if (GetWindowLongPtrW((HWND)lParam, GWL_EXSTYLE) & WS_EX_MDICHILD)
1284 {
1285 // ReactOS See rev 33503
1286 if (!ci->child)
1287 ci->child = HeapAlloc(GetProcessHeap(), 0, sizeof(HWND));
1288 else
1289 ci->child = HeapReAlloc(GetProcessHeap(), 0, ci->child, sizeof(HWND) * (ci->nActiveChildren + 1));
1290
1291 TRACE("Adding MDI child %p, # of children %d\n",
1292 (HWND)lParam, ci->nActiveChildren);
1293
1294 if (ci->child != NULL)
1295 {
1296 ci->child[ci->nActiveChildren] = (HWND)lParam;
1297 ci->nTotalCreated++;
1298 ci->nActiveChildren++;
1299 }
1300 }
1301 break;
1302
1303 case WM_LBUTTONDOWN:
1304 {
1305 HWND child;
1306 POINT pt;
1307 pt.x = (short)LOWORD(lParam);
1308 pt.y = (short)HIWORD(lParam);
1309 child = ChildWindowFromPoint(hwnd, pt);
1310
1311 TRACE("notification from %p (%li,%li)\n",child,pt.x,pt.y);
1312
1313 if( child && child != hwnd && child != ci->hwndActiveChild )
1314 SetWindowPos(child, 0,0,0,0,0, SWP_NOSIZE | SWP_NOMOVE );
1315 break;
1316 }
1317
1318 case WM_DESTROY:
1319 return MDIDestroyChild( hwnd, ci, WIN_GetFullHandle( (HWND)lParam ), FALSE );
1320 }
1321 return 0;
1322
1323 case WM_SIZE:
1324 if( ci->hwndActiveChild && IsZoomed(ci->hwndActiveChild) )
1325 {
1326 RECT rect;
1327
1328 SetRect(&rect, 0, 0, LOWORD(lParam), HIWORD(lParam));
1329 AdjustWindowRectEx(&rect, GetWindowLongPtrA(ci->hwndActiveChild, GWL_STYLE),
1330 0, GetWindowLongPtrA(ci->hwndActiveChild, GWL_EXSTYLE) );
1331 MoveWindow(ci->hwndActiveChild, rect.left, rect.top,
1332 rect.right - rect.left, rect.bottom - rect.top, 1);
1333 }
1334 else
1335 MDI_PostUpdate(hwnd, ci, SB_BOTH+1);
1336
1337 break;
1338
1339 case WM_MDICALCCHILDSCROLL:
1340 if( (ci->mdiFlags & MDIF_NEEDUPDATE) && ci->sbRecalc )
1341 {
1342 CalcChildScroll(hwnd, ci->sbRecalc-1);
1343 ci->sbRecalc = 0;
1344 ci->mdiFlags &= ~MDIF_NEEDUPDATE;
1345 }
1346 return 0;
1347 }
1348 return unicode ? DefWindowProcW( hwnd, message, wParam, lParam ) :
1349 DefWindowProcA( hwnd, message, wParam, lParam );
1350 }
1351
1352 /***********************************************************************
1353 * MDIClientWndProcA
1354 */
1355 LRESULT WINAPI MDIClientWndProcA( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
1356 {
1357 if (!IsWindow(hwnd)) return 0;
1358 return MDIClientWndProc_common( hwnd, message, wParam, lParam, FALSE );
1359 }
1360
1361 /***********************************************************************
1362 * MDIClientWndProcW
1363 */
1364 LRESULT WINAPI MDIClientWndProcW( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
1365 {
1366 if (!IsWindow(hwnd)) return 0;
1367 return MDIClientWndProc_common( hwnd, message, wParam, lParam, TRUE );
1368 }
1369
1370 /***********************************************************************
1371 * DefFrameProcA (USER32.@)
1372 */
1373 LRESULT WINAPI DefFrameProcA( HWND hwnd, HWND hwndMDIClient,
1374 UINT message, WPARAM wParam, LPARAM lParam)
1375 {
1376 if (hwndMDIClient)
1377 {
1378 switch (message)
1379 {
1380 case WM_SETTEXT:
1381 {
1382 DWORD len = MultiByteToWideChar( CP_ACP, 0, (LPSTR)lParam, -1, NULL, 0 );
1383 LPWSTR text = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
1384 if (text == NULL)
1385 return 0;
1386 MultiByteToWideChar( CP_ACP, 0, (LPSTR)lParam, -1, text, len );
1387 MDI_UpdateFrameText( hwnd, hwndMDIClient, FALSE, text );
1388 HeapFree( GetProcessHeap(), 0, text );
1389 }
1390 return 1; /* success. FIXME: check text length */
1391
1392 case WM_COMMAND:
1393 case WM_NCACTIVATE:
1394 case WM_NEXTMENU:
1395 case WM_SETFOCUS:
1396 case WM_SIZE:
1397 return DefFrameProcW( hwnd, hwndMDIClient, message, wParam, lParam );
1398 }
1399 }
1400 return DefWindowProcA(hwnd, message, wParam, lParam);
1401 }
1402
1403
1404 /***********************************************************************
1405 * DefFrameProcW (USER32.@)
1406 */
1407 LRESULT WINAPI DefFrameProcW( HWND hwnd, HWND hwndMDIClient,
1408 UINT message, WPARAM wParam, LPARAM lParam)
1409 {
1410 MDICLIENTINFO *ci = get_client_info( hwndMDIClient );
1411
1412 TRACE("%p %p %04x (%s) %08lx %08lx\n", hwnd, hwndMDIClient, message, SPY_GetMsgName(message, hwnd), wParam, lParam);
1413
1414 if (ci)
1415 {
1416 switch (message)
1417 {
1418 case WM_COMMAND:
1419 {
1420 WORD id = LOWORD(wParam);
1421 /* check for possible syscommands for maximized MDI child */
1422 if (id < ci->idFirstChild || id >= ci->idFirstChild + ci->nActiveChildren)
1423 {
1424 if( (id - 0xf000) & 0xf00f ) break;
1425 if( !ci->hwndChildMaximized ) break;
1426 switch( id )
1427 {
1428 case SC_CLOSE:
1429 if (!is_close_enabled(ci->hwndActiveChild, 0)) break;
1430 case SC_SIZE:
1431 case SC_MOVE:
1432 case SC_MINIMIZE:
1433 case SC_MAXIMIZE:
1434 case SC_NEXTWINDOW:
1435 case SC_PREVWINDOW:
1436 case SC_RESTORE:
1437 return SendMessageW( ci->hwndChildMaximized, WM_SYSCOMMAND,
1438 wParam, lParam);
1439 }
1440 }
1441 else
1442 {
1443 HWND childHwnd;
1444 if (id - ci->idFirstChild == MDI_MOREWINDOWSLIMIT)
1445 /* User chose "More Windows..." */
1446 childHwnd = MDI_MoreWindowsDialog(hwndMDIClient);
1447 else
1448 /* User chose one of the windows listed in the "Windows" menu */
1449 childHwnd = MDI_GetChildByID(hwndMDIClient, id, ci);
1450
1451 if( childHwnd )
1452 SendMessageW( hwndMDIClient, WM_MDIACTIVATE, (WPARAM)childHwnd, 0 );
1453 }
1454 }
1455 break;
1456
1457 case WM_NCACTIVATE:
1458 SendMessageW(hwndMDIClient, message, wParam, lParam);
1459 break;
1460
1461 case WM_SETTEXT:
1462 MDI_UpdateFrameText( hwnd, hwndMDIClient, FALSE, (LPWSTR)lParam );
1463 return 1; /* success. FIXME: check text length */
1464
1465 case WM_SETFOCUS:
1466 SetFocus(hwndMDIClient);
1467 break;
1468
1469 case WM_SIZE:
1470 MoveWindow(hwndMDIClient, 0, 0, LOWORD(lParam), HIWORD(lParam), TRUE);
1471 break;
1472
1473 case WM_NEXTMENU:
1474 {
1475 MDINEXTMENU *next_menu = (MDINEXTMENU *)lParam;
1476
1477 if (!IsIconic(hwnd) && ci->hwndActiveChild && !IsZoomed(ci->hwndActiveChild))
1478 {
1479 /* control menu is between the frame system menu and
1480 * the first entry of menu bar */
1481 // WND *wndPtr = WIN_GetPtr(hwnd);
1482
1483 if( (wParam == VK_LEFT && GetMenu(hwnd) == next_menu->hmenuIn) ||
1484 (wParam == VK_RIGHT && GetSubMenu(GetMenu(hwnd), 0) == next_menu->hmenuIn) )
1485 {
1486 // WIN_ReleasePtr(wndPtr);
1487 // wndPtr = WIN_GetPtr(ci->hwndActiveChild);
1488 next_menu->hmenuNext = GetSubMenu(GetMenu(ci->hwndActiveChild), 0);
1489 next_menu->hwndNext = ci->hwndActiveChild;
1490 }
1491 // WIN_ReleasePtr(wndPtr);
1492 }
1493 return 0;
1494 }
1495 }
1496 }
1497
1498 return DefWindowProcW( hwnd, message, wParam, lParam );
1499 }
1500
1501 /***********************************************************************
1502 * DefMDIChildProcA (USER32.@)
1503 */
1504 LRESULT WINAPI DefMDIChildProcA( HWND hwnd, UINT message,
1505 WPARAM wParam, LPARAM lParam )
1506 {
1507 HWND client = GetParent(hwnd);
1508 MDICLIENTINFO *ci = get_client_info( client );
1509
1510 TRACE("%p %04x (%s) %08lx %08lx\n", hwnd, message, SPY_GetMsgName(message, hwnd), wParam, lParam);
1511
1512 hwnd = WIN_GetFullHandle( hwnd );
1513 if (!ci) return DefWindowProcA( hwnd, message, wParam, lParam );
1514
1515 switch (message)
1516 {
1517 case WM_SETTEXT:
1518 DefWindowProcA(hwnd, message, wParam, lParam);
1519 if( ci->hwndChildMaximized == hwnd )
1520 MDI_UpdateFrameText( GetParent(client), client, TRUE, NULL );
1521 MDI_RefreshMenu( ci );
1522 return 1; /* success. FIXME: check text length */
1523
1524 case WM_GETMINMAXINFO:
1525 case WM_MENUCHAR:
1526 case WM_CLOSE:
1527 case WM_SETFOCUS:
1528 case WM_CHILDACTIVATE:
1529 case WM_SYSCOMMAND:
1530 case WM_SHOWWINDOW:
1531 case WM_SETVISIBLE:
1532 case WM_SIZE:
1533 case WM_NEXTMENU:
1534 case WM_SYSCHAR:
1535 case WM_DESTROY:
1536 return DefMDIChildProcW( hwnd, message, wParam, lParam );
1537 }
1538 return DefWindowProcA(hwnd, message, wParam, lParam);
1539 }
1540
1541
1542 /***********************************************************************
1543 * DefMDIChildProcW (USER32.@)
1544 */
1545 LRESULT WINAPI DefMDIChildProcW( HWND hwnd, UINT message,
1546 WPARAM wParam, LPARAM lParam )
1547 {
1548 HWND client = GetParent(hwnd);
1549 MDICLIENTINFO *ci = get_client_info( client );
1550
1551 TRACE("%p %04x (%s) %08lx %08lx\n", hwnd, message, SPY_GetMsgName(message, hwnd), wParam, lParam);
1552
1553 hwnd = WIN_GetFullHandle( hwnd );
1554 if (!ci) return DefWindowProcW( hwnd, message, wParam, lParam );
1555
1556 switch (message)
1557 {
1558 case WM_SETTEXT:
1559 DefWindowProcW(hwnd, message, wParam, lParam);
1560 if( ci->hwndChildMaximized == hwnd )
1561 MDI_UpdateFrameText( GetParent(client), client, TRUE, NULL );
1562 MDI_RefreshMenu( ci );
1563 return 1; /* success. FIXME: check text length */
1564
1565 case WM_GETMINMAXINFO:
1566 MDI_ChildGetMinMaxInfo( client, hwnd, (MINMAXINFO *)lParam );
1567 return 0;
1568
1569 case WM_MENUCHAR:
1570 return MAKELRESULT( 0, MNC_CLOSE ); /* MDI children don't have menu bars */
1571
1572 case WM_CLOSE:
1573 SendMessageW( client, WM_MDIDESTROY, (WPARAM)hwnd, 0 );
1574 return 0;
1575
1576 case WM_SETFOCUS:
1577 if (ci->hwndActiveChild != hwnd)
1578 MDI_ChildActivate( client, hwnd );
1579 break;
1580
1581 case WM_CHILDACTIVATE:
1582 if (IsWindowEnabled( hwnd ))
1583 MDI_ChildActivate( client, hwnd );
1584 return 0;
1585
1586 case WM_SYSCOMMAND:
1587 switch (wParam & 0xfff0)
1588 {
1589 case SC_MOVE:
1590 if( ci->hwndChildMaximized == hwnd )
1591 return 0;
1592 break;
1593 case SC_RESTORE:
1594 case SC_MINIMIZE:
1595 break;
1596 case SC_MAXIMIZE:
1597 if (ci->hwndChildMaximized == hwnd)
1598 return SendMessageW( GetParent(client), message, wParam, lParam);
1599 break;
1600 case SC_NEXTWINDOW:
1601 SendMessageW( client, WM_MDINEXT, (WPARAM)ci->hwndActiveChild, 0);
1602 return 0;
1603 case SC_PREVWINDOW:
1604 SendMessageW( client, WM_MDINEXT, (WPARAM)ci->hwndActiveChild, 1);
1605 return 0;
1606 }
1607 break;
1608
1609 case WM_SHOWWINDOW:
1610 case WM_SETVISIBLE:
1611 //// Commented out r57663
1612 /*if (ci->hwndChildMaximized) ci->mdiFlags &= ~MDIF_NEEDUPDATE;
1613 else*/ MDI_PostUpdate(client, ci, SB_BOTH+1);
1614 break;
1615
1616 case WM_SIZE:
1617 /* This is the only place where we switch to/from maximized state */
1618 /* do not change */
1619 TRACE("current active %p, maximized %p\n", ci->hwndActiveChild, ci->hwndChildMaximized);
1620
1621 if( ci->hwndChildMaximized == hwnd && wParam != SIZE_MAXIMIZED )
1622 {
1623 HWND frame;
1624
1625 ci->hwndChildMaximized = 0;
1626
1627 frame = GetParent(client);
1628 MDI_RestoreFrameMenu( frame, hwnd, ci->hBmpClose );
1629 MDI_UpdateFrameText( frame, client, TRUE, NULL );
1630 }
1631
1632 if( wParam == SIZE_MAXIMIZED )
1633 {
1634 HWND frame, hMaxChild = ci->hwndChildMaximized;
1635
1636 if( hMaxChild == hwnd ) break;
1637
1638 if( hMaxChild)
1639 {
1640 SendMessageW( hMaxChild, WM_SETREDRAW, FALSE, 0 );
1641
1642 MDI_RestoreFrameMenu( GetParent(client), hMaxChild, ci->hBmpClose );
1643 ShowWindow( hMaxChild, SW_SHOWNOACTIVATE );
1644
1645 SendMessageW( hMaxChild, WM_SETREDRAW, TRUE, 0 );
1646 }
1647
1648 TRACE("maximizing child %p\n", hwnd );
1649
1650 /* keep track of the maximized window. */
1651 ci->hwndChildMaximized = hwnd; /* !!! */
1652
1653 frame = GetParent(client);
1654 MDI_AugmentFrameMenu( frame, hwnd );
1655 MDI_UpdateFrameText( frame, client, TRUE, NULL );
1656 }
1657
1658 if( wParam == SIZE_MINIMIZED )
1659 {
1660 HWND switchTo = MDI_GetWindow( ci, hwnd, TRUE, WS_MINIMIZE );
1661
1662 if (!switchTo) switchTo = hwnd;
1663 SendMessageW( switchTo, WM_CHILDACTIVATE, 0, 0 );
1664 }
1665
1666 MDI_PostUpdate(client, ci, SB_BOTH+1);
1667 break;
1668
1669 case WM_NEXTMENU:
1670 {
1671 MDINEXTMENU *next_menu = (MDINEXTMENU *)lParam;
1672 HWND parent = GetParent(client);
1673
1674 if( wParam == VK_LEFT ) /* switch to frame system menu */
1675 {
1676 // WND *wndPtr = WIN_GetPtr( parent );
1677 next_menu->hmenuNext = GetSubMenu( GetMenu(parent), 0 );
1678 // WIN_ReleasePtr( wndPtr );
1679 }
1680 if( wParam == VK_RIGHT ) /* to frame menu bar */
1681 {
1682 next_menu->hmenuNext = GetMenu(parent);
1683 }
1684 next_menu->hwndNext = parent;
1685 return 0;
1686 }
1687
1688 case WM_SYSCHAR:
1689 if (wParam == '-')
1690 {
1691 SendMessageW( hwnd, WM_SYSCOMMAND, SC_KEYMENU, VK_SPACE);
1692 return 0;
1693 }
1694 break;
1695
1696 case WM_DESTROY:
1697 /* Remove itself from the Window menu */
1698 MDI_RefreshMenu(ci);
1699 break;
1700 }
1701 return DefWindowProcW(hwnd, message, wParam, lParam);
1702 }
1703
1704 /**********************************************************************
1705 * CreateMDIWindowA (USER32.@) Creates a MDI child
1706 *
1707 * RETURNS
1708 * Success: Handle to created window
1709 * Failure: NULL
1710 */
1711 HWND WINAPI CreateMDIWindowA(
1712 LPCSTR lpClassName, /* [in] Pointer to registered child class name */
1713 LPCSTR lpWindowName, /* [in] Pointer to window name */
1714 DWORD dwStyle, /* [in] Window style */
1715 INT X, /* [in] Horizontal position of window */
1716 INT Y, /* [in] Vertical position of window */
1717 INT nWidth, /* [in] Width of window */
1718 INT nHeight, /* [in] Height of window */
1719 HWND hWndParent, /* [in] Handle to parent window */
1720 HINSTANCE hInstance, /* [in] Handle to application instance */
1721 LPARAM lParam) /* [in] Application-defined value */
1722 {
1723 TRACE("(%s,%s,%08lx,%d,%d,%d,%d,%p,%p,%08lx)\n",
1724 debugstr_a(lpClassName),debugstr_a(lpWindowName),dwStyle,X,Y,
1725 nWidth,nHeight,hWndParent,hInstance,lParam);
1726
1727 return CreateWindowExA(WS_EX_MDICHILD, lpClassName, lpWindowName,
1728 dwStyle, X, Y, nWidth, nHeight, hWndParent,
1729 0, hInstance, (LPVOID)lParam);
1730 }
1731
1732 /***********************************************************************
1733 * CreateMDIWindowW (USER32.@) Creates a MDI child
1734 *
1735 * RETURNS
1736 * Success: Handle to created window
1737 * Failure: NULL
1738 */
1739 HWND WINAPI CreateMDIWindowW(
1740 LPCWSTR lpClassName, /* [in] Pointer to registered child class name */
1741 LPCWSTR lpWindowName, /* [in] Pointer to window name */
1742 DWORD dwStyle, /* [in] Window style */
1743 INT X, /* [in] Horizontal position of window */
1744 INT Y, /* [in] Vertical position of window */
1745 INT nWidth, /* [in] Width of window */
1746 INT nHeight, /* [in] Height of window */
1747 HWND hWndParent, /* [in] Handle to parent window */
1748 HINSTANCE hInstance, /* [in] Handle to application instance */
1749 LPARAM lParam) /* [in] Application-defined value */
1750 {
1751 TRACE("(%s,%s,%08lx,%d,%d,%d,%d,%p,%p,%08lx)\n",
1752 debugstr_w(lpClassName), debugstr_w(lpWindowName), dwStyle, X, Y,
1753 nWidth, nHeight, hWndParent, hInstance, lParam);
1754
1755 return CreateWindowExW(WS_EX_MDICHILD, lpClassName, lpWindowName,
1756 dwStyle, X, Y, nWidth, nHeight, hWndParent,
1757 0, hInstance, (LPVOID)lParam);
1758 }
1759
1760 /**********************************************************************
1761 * TranslateMDISysAccel (USER32.@)
1762 */
1763 BOOL WINAPI TranslateMDISysAccel( HWND hwndClient, LPMSG msg )
1764 {
1765 if (msg->message == WM_KEYDOWN || msg->message == WM_SYSKEYDOWN)
1766 {
1767 MDICLIENTINFO *ci = get_client_info( hwndClient );
1768 WPARAM wParam = 0;
1769
1770 if (!ci || !IsWindowEnabled(ci->hwndActiveChild)) return 0;
1771
1772 /* translate if the Ctrl key is down and Alt not. */
1773
1774 if( (GetKeyState(VK_CONTROL) & 0x8000) && !(GetKeyState(VK_MENU) & 0x8000))
1775 {
1776 switch( msg->wParam )
1777 {
1778 case VK_F6:
1779 case VK_TAB:
1780 wParam = ( GetKeyState(VK_SHIFT) & 0x8000 ) ? SC_NEXTWINDOW : SC_PREVWINDOW;
1781 break;
1782 case VK_F4:
1783 case VK_RBUTTON:
1784 if (is_close_enabled(ci->hwndActiveChild, 0))
1785 {
1786 wParam = SC_CLOSE;
1787 break;
1788 }
1789 /* fall through */
1790 default:
1791 return FALSE;
1792 }
1793 TRACE("wParam = %04lx\n", wParam);
1794 SendMessageW(ci->hwndActiveChild, WM_SYSCOMMAND, wParam, msg->wParam);
1795 return TRUE;
1796 }
1797 }
1798 return FALSE; /* failure */
1799 }
1800
1801 /***********************************************************************
1802 * CalcChildScroll (USER32.@)
1803 */
1804 void WINAPI CalcChildScroll( HWND hwnd, INT scroll )
1805 {
1806 SCROLLINFO info;
1807 RECT childRect, clientRect;
1808 HWND *list;
1809 DWORD style;
1810 WINDOWINFO WindowInfo;
1811
1812 GetClientRect( hwnd, &clientRect );
1813 SetRectEmpty( &childRect );
1814
1815 /* The rectangle returned by GetClientRect always has 0,0 as top left
1816 * because it is in client coordinates. The rectangles returned by
1817 * GetWindowRect are in screen coordinates to make this complicated.
1818 *
1819 * Apparently (in ReactOS at least) the rcClient returned by GetWindowInfo
1820 * is in screen coordinates too.
1821 */
1822 WindowInfo.cbSize = sizeof(WindowInfo);
1823 if (!GetWindowInfo(hwnd, &WindowInfo))
1824 {
1825 ERR("Can't get window info\n");
1826 return;
1827 }
1828
1829 TRACE("CalcChildScroll 1\n");
1830 if ((list = WIN_ListChildren( hwnd )))
1831 {
1832 int i;
1833 for (i = 0; list[i]; i++)
1834 {
1835 style = GetWindowLongPtrW( list[i], GWL_STYLE );
1836 if (style & WS_MAXIMIZE)
1837 {
1838 HeapFree( GetProcessHeap(), 0, list );
1839 ShowScrollBar( hwnd, SB_BOTH, FALSE );
1840 ERR("CalcChildScroll 2\n");
1841 return;
1842 }
1843 if (style & WS_VISIBLE)
1844 {
1845 RECT rect;
1846 GetWindowRect( list[i], &rect );
1847 OffsetRect(&rect, -WindowInfo.rcClient.left,
1848 -WindowInfo.rcClient.top);
1849 //WIN_GetRectangles( list[i], COORDS_PARENT, &rect, NULL );
1850 TRACE("CalcChildScroll L\n");
1851 UnionRect( &childRect, &rect, &childRect );
1852 }
1853 }
1854 HeapFree( GetProcessHeap(), 0, list );
1855 }
1856 UnionRect( &childRect, &clientRect, &childRect );
1857 TRACE("CalcChildScroll 3\n");
1858 /* set common info values */
1859 info.cbSize = sizeof(info);
1860 info.fMask = SIF_POS | SIF_RANGE | SIF_PAGE;
1861 info.nPos = 0;
1862
1863 /* set the specific values and apply but only if window style allows */
1864 /* Note how we set nPos to 0 because we scroll the clients instead of
1865 * the window, and we set nPage to 1 bigger than the clientRect because
1866 * otherwise the scrollbar never disables. This causes a somewhat ugly
1867 * effect though while scrolling.
1868 */
1869 style = GetWindowLongW( hwnd, GWL_STYLE );
1870 switch( scroll )
1871 {
1872 case SB_BOTH:
1873 case SB_HORZ:
1874 if (style & (WS_HSCROLL | WS_VSCROLL))
1875 {
1876 info.nMin = childRect.left;
1877 info.nMax = childRect.right;
1878 info.nPage = 1 + clientRect.right - clientRect.left;
1879 //info.nMax = childRect.right - clientRect.right;
1880 //info.nPos = clientRect.left - childRect.left;
1881 SetScrollInfo(hwnd, SB_HORZ, &info, TRUE);
1882 }
1883 if (scroll == SB_HORZ) break;
1884 /* fall through */
1885 case SB_VERT:
1886 if (style & (WS_HSCROLL | WS_VSCROLL))
1887 {
1888 info.nMin = childRect.top;
1889 info.nMax = childRect.bottom;
1890 info.nPage = 1 + clientRect.bottom - clientRect.top;
1891 //info.nMax = childRect.bottom - clientRect.bottom;
1892 //info.nPos = clientRect.top - childRect.top;
1893 SetScrollInfo(hwnd, SB_VERT, &info, TRUE);
1894 }
1895 break;
1896 }
1897 }
1898
1899
1900 /***********************************************************************
1901 * ScrollChildren (USER32.@)
1902 */
1903 void WINAPI ScrollChildren(HWND hWnd, UINT uMsg, WPARAM wParam,
1904 LPARAM lParam)
1905 {
1906 INT newPos = -1;
1907 INT curPos, length, minPos, maxPos, shift;
1908 RECT rect;
1909
1910 GetClientRect( hWnd, &rect );
1911
1912 switch(uMsg)
1913 {
1914 case WM_HSCROLL:
1915 GetScrollRange(hWnd,SB_HORZ,&minPos,&maxPos);
1916 curPos = GetScrollPos(hWnd,SB_HORZ);
1917 length = (rect.right - rect.left) / 2;
1918 shift = GetSystemMetrics(SM_CYHSCROLL);
1919 break;
1920 case WM_VSCROLL:
1921 GetScrollRange(hWnd,SB_VERT,&minPos,&maxPos);
1922 curPos = GetScrollPos(hWnd,SB_VERT);
1923 length = (rect.bottom - rect.top) / 2;
1924 shift = GetSystemMetrics(SM_CXVSCROLL);
1925 break;
1926 default:
1927 return;
1928 }
1929
1930 switch( wParam )
1931 {
1932 case SB_LINEUP:
1933 newPos = curPos - shift;
1934 break;
1935 case SB_LINEDOWN:
1936 newPos = curPos + shift;
1937 break;
1938 case SB_PAGEUP:
1939 newPos = curPos - length;
1940 break;
1941 case SB_PAGEDOWN:
1942 newPos = curPos + length;
1943 break;
1944
1945 case SB_THUMBPOSITION:
1946 newPos = LOWORD(lParam);
1947 break;
1948
1949 case SB_THUMBTRACK:
1950 return;
1951
1952 case SB_TOP:
1953 newPos = minPos;
1954 break;
1955 case SB_BOTTOM:
1956 newPos = maxPos;
1957 break;
1958 case SB_ENDSCROLL:
1959 CalcChildScroll(hWnd,(uMsg == WM_VSCROLL)?SB_VERT:SB_HORZ);
1960 return;
1961 }
1962
1963 if( newPos > maxPos )
1964 newPos = maxPos;
1965 else
1966 if( newPos < minPos )
1967 newPos = minPos;
1968
1969 SetScrollPos(hWnd, (uMsg == WM_VSCROLL)?SB_VERT:SB_HORZ , newPos, TRUE);
1970
1971 if( uMsg == WM_VSCROLL )
1972 ScrollWindowEx(hWnd ,0 ,curPos - newPos, NULL, NULL, 0, NULL,
1973 SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
1974 else
1975 ScrollWindowEx(hWnd ,curPos - newPos, 0, NULL, NULL, 0, NULL,
1976 SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
1977 }
1978
1979 /******************************************************************************
1980 * CascadeWindows (USER32.@) Cascades MDI child windows
1981 *
1982 * RETURNS
1983 * Success: Number of cascaded windows.
1984 * Failure: 0
1985 */
1986
1987 typedef struct CASCADE_INFO
1988 {
1989 HWND hwndTop;
1990 UINT wFlags;
1991 HWND hwndParent;
1992 HWND hwndDesktop;
1993 HWND hTrayWnd;
1994 HWND hwndProgman;
1995 HWND *ahwnd;
1996 DWORD chwnd;
1997 } CASCADE_INFO;
1998
1999 static BOOL CALLBACK
2000 GetCascadeChildProc(HWND hwnd, LPARAM lParam)
2001 {
2002 DWORD count, size;
2003 HWND *ahwnd;
2004 CASCADE_INFO *pInfo = (CASCADE_INFO *)lParam;
2005
2006 if (hwnd == pInfo->hwndDesktop || hwnd == pInfo->hTrayWnd ||
2007 hwnd == pInfo->hwndProgman || hwnd == pInfo->hwndTop)
2008 {
2009 return TRUE;
2010 }
2011
2012 if (pInfo->hwndParent && GetParent(hwnd) != pInfo->hwndParent)
2013 return TRUE;
2014
2015 if ((pInfo->wFlags & MDITILE_SKIPDISABLED) && !IsWindowEnabled(hwnd))
2016 return TRUE;
2017
2018 if (!IsWindowVisible(hwnd) || IsIconic(hwnd))
2019 return TRUE;
2020
2021 count = pInfo->chwnd;
2022 size = (count + 1) * sizeof(HWND);
2023
2024 if (count == 0 || pInfo->ahwnd == NULL)
2025 {
2026 count = 0;
2027 pInfo->ahwnd = (HWND *)HeapAlloc(GetProcessHeap(), 0, size);
2028 }
2029 else
2030 {
2031 ahwnd = (HWND *)HeapReAlloc(GetProcessHeap(), 0, pInfo->ahwnd, size);
2032 if (ahwnd == NULL)
2033 {
2034 HeapFree(GetProcessHeap(), 0, pInfo->ahwnd);
2035 }
2036 pInfo->ahwnd = ahwnd;
2037 }
2038
2039 if (pInfo->ahwnd == NULL)
2040 {
2041 pInfo->chwnd = 0;
2042 return FALSE;
2043 }
2044
2045 pInfo->ahwnd[count] = hwnd;
2046 pInfo->chwnd = count + 1;
2047 return TRUE;
2048 }
2049
2050 static BOOL
2051 QuerySizeFix(HWND hwnd, LPINT pcx, LPINT pcy)
2052 {
2053 MINMAXINFO mmi;
2054 DWORD_PTR dwResult;
2055
2056 mmi.ptMinTrackSize.x = mmi.ptMinTrackSize.y = 0;
2057 mmi.ptMaxTrackSize.x = mmi.ptMaxTrackSize.y = MAXLONG;
2058 if (SendMessageTimeoutW(hwnd, WM_GETMINMAXINFO, 0, (LPARAM)&mmi,
2059 SMTO_ABORTIFHUNG | SMTO_NORMAL, 120, &dwResult))
2060 {
2061 *pcx = min(max(*pcx, mmi.ptMinTrackSize.x), mmi.ptMaxTrackSize.x);
2062 *pcy = min(max(*pcy, mmi.ptMinTrackSize.y), mmi.ptMaxTrackSize.y);
2063 return TRUE;
2064 }
2065 return FALSE;
2066 }
2067
2068 WORD WINAPI
2069 CascadeWindows(HWND hwndParent, UINT wFlags, LPCRECT lpRect,
2070 UINT cKids, const HWND *lpKids)
2071 {
2072 CASCADE_INFO info;
2073 HWND hwnd, hwndTop, hwndPrev;
2074 HMONITOR hMon;
2075 MONITORINFO mi;
2076 RECT rcWork, rcWnd;
2077 DWORD i, ret = 0;
2078 INT x, y, cx, cy, cxNew, cyNew, cxWork, cyWork, dx, dy;
2079 HDWP hDWP;
2080 POINT pt;
2081
2082 TRACE("(%p,0x%08x,...,%u,...)\n", hwndParent, wFlags, cKids);
2083
2084 hwndTop = GetTopWindow(hwndParent);
2085
2086 ZeroMemory(&info, sizeof(info));
2087 info.hwndDesktop = GetDesktopWindow();
2088 info.hTrayWnd = FindWindowW(L"Shell_TrayWnd", NULL);
2089 info.hwndProgman = FindWindowW(L"Progman", NULL);
2090 info.hwndParent = hwndParent;
2091 info.wFlags = wFlags;
2092
2093 if (cKids == 0 || lpKids == NULL)
2094 {
2095 info.hwndTop = hwndTop;
2096 EnumChildWindows(hwndParent, GetCascadeChildProc, (LPARAM)&info);
2097
2098 info.hwndTop = NULL;
2099 GetCascadeChildProc(hwndTop, (LPARAM)&info);
2100 }
2101 else
2102 {
2103 info.chwnd = cKids;
2104 info.ahwnd = (HWND *)lpKids;
2105 }
2106
2107 if (info.chwnd == 0 || info.ahwnd == NULL)
2108 return ret;
2109
2110 if (lpRect)
2111 {
2112 rcWork = *lpRect;
2113 }
2114 else if (hwndParent)
2115 {
2116 GetClientRect(hwndParent, &rcWork);
2117 }
2118 else
2119 {
2120 pt.x = pt.y = 0;
2121 hMon = MonitorFromPoint(pt, MONITOR_DEFAULTTOPRIMARY);
2122 mi.cbSize = sizeof(mi);
2123 GetMonitorInfoW(hMon, &mi);
2124 rcWork = mi.rcWork;
2125 }
2126
2127 hDWP = BeginDeferWindowPos(info.chwnd);
2128 if (hDWP == NULL)
2129 goto cleanup;
2130
2131 x = rcWork.left;
2132 y = rcWork.top;
2133 dx = GetSystemMetrics(SM_CXSIZEFRAME) + GetSystemMetrics(SM_CXSIZE);
2134 dy = GetSystemMetrics(SM_CYSIZEFRAME) + GetSystemMetrics(SM_CYSIZE);
2135 cxWork = rcWork.right - rcWork.left;
2136 cyWork = rcWork.bottom - rcWork.top;
2137 hwndPrev = NULL;
2138 for (i = info.chwnd; i > 0;) /* in reverse order */
2139 {
2140 --i;
2141 hwnd = info.ahwnd[i];
2142
2143 if (!IsWindowVisible(hwnd) || IsIconic(hwnd))
2144 continue;
2145
2146 if ((info.wFlags & MDITILE_SKIPDISABLED) && !IsWindowEnabled(hwnd))
2147 continue;
2148
2149 if (IsZoomed(hwnd))
2150 ShowWindow(hwnd, SW_RESTORE | SW_SHOWNA);
2151
2152 GetWindowRect(hwnd, &rcWnd);
2153 cxNew = cx = rcWnd.right - rcWnd.left;
2154 cyNew = cy = rcWnd.bottom - rcWnd.top;
2155
2156 /* if we can change the window size and it is not only one */
2157 if (info.chwnd != 1 && (GetWindowLongPtrW(hwnd, GWL_STYLE) & WS_THICKFRAME))
2158 {
2159 /* check the size */
2160 #define THRESHOLD(xy) (((xy) * 5) / 7) /* in the rate 5/7 */
2161 cxNew = min(cxNew, THRESHOLD(cxWork));
2162 cyNew = min(cyNew, THRESHOLD(cyWork));
2163 #undef THRESHOLD
2164 if (cx != cxNew || cy != cyNew)
2165 {
2166 /* too large. shrink if we can */
2167 if (QuerySizeFix(hwnd, &cxNew, &cyNew))
2168 {
2169 cx = cxNew;
2170 cy = cyNew;
2171 }
2172 }
2173 }
2174
2175 if (x + cx > rcWork.right)
2176 x = rcWork.left;
2177 if (y + cy > rcWork.bottom)
2178 y = rcWork.top;
2179
2180 hDWP = DeferWindowPos(hDWP, hwnd, HWND_TOP, x, y, cx, cy, SWP_NOACTIVATE);
2181 if (hDWP == NULL)
2182 {
2183 ret = 0;
2184 goto cleanup;
2185 }
2186
2187 x += dx;
2188 y += dy;
2189 hwndPrev = hwnd;
2190 ++ret;
2191 }
2192
2193 EndDeferWindowPos(hDWP);
2194
2195 if (hwndPrev)
2196 SetForegroundWindow(hwndPrev);
2197
2198 cleanup:
2199 if (cKids == 0 || lpKids == NULL)
2200 HeapFree(GetProcessHeap(), 0, info.ahwnd);
2201
2202 return (WORD)ret;
2203 }
2204
2205
2206 /***********************************************************************
2207 * CascadeChildWindows (USER32.@)
2208 */
2209 WORD WINAPI CascadeChildWindows( HWND parent, UINT flags )
2210 {
2211 return CascadeWindows( parent, flags, NULL, 0, NULL );
2212 }
2213
2214
2215 /******************************************************************************
2216 * TileWindows (USER32.@) Tiles MDI child windows
2217 *
2218 * RETURNS
2219 * Success: Number of tiled windows.
2220 * Failure: 0
2221 */
2222 WORD WINAPI
2223 TileWindows(HWND hwndParent, UINT wFlags, LPCRECT lpRect,
2224 UINT cKids, const HWND *lpKids)
2225 {
2226 HWND hwnd, hwndTop, hwndPrev;
2227 CASCADE_INFO info;
2228 RECT rcWork, rcWnd;
2229 DWORD i, iRow, iColumn, cRows, cColumns, ret = 0;
2230 INT x, y, cx, cy, cxNew, cyNew, cxWork, cyWork, cxCell, cyCell, cxMin2, cyMin3;
2231 HDWP hDWP;
2232 MONITORINFO mi;
2233 HMONITOR hMon;
2234 POINT pt;
2235
2236 TRACE("(%p,0x%08x,...,%u,...)\n", hwndParent, wFlags, cKids);
2237
2238 hwndTop = GetTopWindow(hwndParent);
2239
2240 ZeroMemory(&info, sizeof(info));
2241 info.hwndDesktop = GetDesktopWindow();
2242 info.hTrayWnd = FindWindowW(L"Shell_TrayWnd", NULL);
2243 info.hwndProgman = FindWindowW(L"Progman", NULL);
2244 info.hwndParent = hwndParent;
2245 info.wFlags = wFlags;
2246
2247 if (cKids == 0 || lpKids == NULL)
2248 {
2249 info.hwndTop = hwndTop;
2250 EnumChildWindows(hwndParent, GetCascadeChildProc, (LPARAM)&info);
2251
2252 info.hwndTop = NULL;
2253 GetCascadeChildProc(hwndTop, (LPARAM)&info);
2254 }
2255 else
2256 {
2257 info.chwnd = cKids;
2258 info.ahwnd = (HWND *)lpKids;
2259 }
2260
2261 if (info.chwnd == 0 || info.ahwnd == NULL)
2262 return ret;
2263
2264 if (lpRect)
2265 {
2266 rcWork = *lpRect;
2267 }
2268 else if (hwndParent)
2269 {
2270 GetClientRect(hwndParent, &rcWork);
2271 }
2272 else
2273 {
2274 pt.x = pt.y = 0;
2275 hMon = MonitorFromPoint(pt, MONITOR_DEFAULTTOPRIMARY);
2276 mi.cbSize = sizeof(mi);
2277 GetMonitorInfoW(hMon, &mi);
2278 rcWork = mi.rcWork;
2279 }
2280
2281 cxWork = rcWork.right - rcWork.left;
2282 cyWork = rcWork.bottom - rcWork.top;
2283
2284 cxMin2 = GetSystemMetrics(SM_CXMIN) * 2;
2285 cyMin3 = GetSystemMetrics(SM_CYMIN) * 3;
2286
2287 /* calculate the numbers and widths of columns and rows */
2288 if (info.wFlags & MDITILE_HORIZONTAL)
2289 {
2290 cColumns = info.chwnd;
2291 cRows = 1;
2292 for (;;)
2293 {
2294 cxCell = cxWork / cColumns;
2295 cyCell = cyWork / cRows;
2296 if (cyCell <= cyMin3 || cxCell >= cxMin2)
2297 break;
2298
2299 ++cRows;
2300 cColumns = (info.chwnd + cRows - 1) / cRows;
2301 }
2302 }
2303 else
2304 {
2305 cRows = info.chwnd;
2306 cColumns = 1;
2307 for (;;)
2308 {
2309 cxCell = cxWork / cColumns;
2310 cyCell = cyWork / cRows;
2311 if (cxCell <= cxMin2 || cyCell >= cyMin3)
2312 break;
2313
2314 ++cColumns;
2315 cRows = (info.chwnd + cColumns - 1) / cColumns;
2316 }
2317 }
2318
2319 hDWP = BeginDeferWindowPos(info.chwnd);
2320 if (hDWP == NULL)
2321 goto cleanup;
2322
2323 x = rcWork.left;
2324 y = rcWork.top;
2325 hwndPrev = NULL;
2326 iRow = iColumn = 0;
2327 for (i = info.chwnd; i > 0;) /* in reverse order */
2328 {
2329 --i;
2330 hwnd = info.ahwnd[i];
2331
2332 if (IsZoomed(hwnd))
2333 ShowWindow(hwnd, SW_RESTORE | SW_SHOWNA);
2334
2335 GetWindowRect(hwnd, &rcWnd);
2336 cx = rcWnd.right - rcWnd.left;
2337 cy = rcWnd.bottom - rcWnd.top;
2338
2339 /* if we can change the window size */
2340 if (GetWindowLongPtrW(hwnd, GWL_STYLE) & WS_THICKFRAME)
2341 {
2342 cxNew = cxCell;
2343 cyNew = cyCell;
2344 /* shrink if we can */
2345 if (QuerySizeFix(hwnd, &cxNew, &cyNew))
2346 {
2347 cx = cxNew;
2348 cy = cyNew;
2349 }
2350 }
2351
2352 hDWP = DeferWindowPos(hDWP, hwnd, HWND_TOP, x, y, cx, cy, SWP_NOACTIVATE);
2353 if (hDWP == NULL)
2354 {
2355 ret = 0;
2356 goto cleanup;
2357 }
2358
2359 if (info.wFlags & MDITILE_HORIZONTAL)
2360 {
2361 x += cxCell;
2362 ++iColumn;
2363 if (iColumn >= cColumns)
2364 {
2365 iColumn = 0;
2366 ++iRow;
2367 x = rcWork.left;
2368 y += cyCell;
2369 }
2370 }
2371 else
2372 {
2373 y += cyCell;
2374 ++iRow;
2375 if (iRow >= cRows)
2376 {
2377 iRow = 0;
2378 ++iColumn;
2379 x += cxCell;
2380 y = rcWork.top;
2381 }
2382 }
2383 hwndPrev = hwnd;
2384 ++ret;
2385 }
2386
2387 EndDeferWindowPos(hDWP);
2388
2389 if (hwndPrev)
2390 SetForegroundWindow(hwndPrev);
2391
2392 cleanup:
2393 if (cKids == 0 || lpKids == NULL)
2394 HeapFree(GetProcessHeap(), 0, info.ahwnd);
2395
2396 return (WORD)ret;
2397 }
2398
2399
2400 /***********************************************************************
2401 * TileChildWindows (USER32.@)
2402 */
2403 WORD WINAPI TileChildWindows( HWND parent, UINT flags )
2404 {
2405 return TileWindows( parent, flags, NULL, 0, NULL );
2406 }
2407
2408
2409 /************************************************************************
2410 * "More Windows..." functionality
2411 */
2412
2413 /* MDI_MoreWindowsDlgProc
2414 *
2415 * This function will process the messages sent to the "More Windows..."
2416 * dialog.
2417 * Return values: 0 = cancel pressed
2418 * HWND = ok pressed or double-click in the list...
2419 *
2420 */
2421
2422 static INT_PTR WINAPI MDI_MoreWindowsDlgProc (HWND hDlg, UINT iMsg, WPARAM wParam, LPARAM lParam)
2423 {
2424 switch (iMsg)
2425 {
2426 case WM_INITDIALOG:
2427 {
2428 UINT widest = 0;
2429 UINT length;
2430 UINT i;
2431 MDICLIENTINFO *ci = get_client_info( (HWND)lParam );
2432 HWND hListBox = GetDlgItem(hDlg, MDI_IDC_LISTBOX);
2433
2434 for (i = 0; i < ci->nActiveChildren; i++)
2435 {
2436 WCHAR buffer[MDI_MAXTITLELENGTH];
2437
2438 if (!InternalGetWindowText( ci->child[i], buffer, sizeof(buffer)/sizeof(WCHAR) ))
2439 continue;
2440 SendMessageW(hListBox, LB_ADDSTRING, 0, (LPARAM)buffer );
2441 SendMessageW(hListBox, LB_SETITEMDATA, i, (LPARAM)ci->child[i] );
2442 length = strlenW(buffer); /* FIXME: should use GetTextExtentPoint */
2443 if (length > widest)
2444 widest = length;
2445 }
2446 /* Make sure the horizontal scrollbar scrolls ok */
2447 SendMessageW(hListBox, LB_SETHORIZONTALEXTENT, widest * 6, 0);
2448
2449 /* Set the current selection */
2450 SendMessageW(hListBox, LB_SETCURSEL, MDI_MOREWINDOWSLIMIT, 0);
2451 return TRUE;
2452 }
2453
2454 case WM_COMMAND:
2455 switch (LOWORD(wParam))
2456 {
2457 default:
2458 if (HIWORD(wParam) != LBN_DBLCLK) break;
2459 /* fall through */
2460 case IDOK:
2461 {
2462 /* windows are sorted by menu ID, so we must return the
2463 * window associated to the given id
2464 */
2465 HWND hListBox = GetDlgItem(hDlg, MDI_IDC_LISTBOX);
2466 UINT index = SendMessageW(hListBox, LB_GETCURSEL, 0, 0);
2467 LRESULT res = SendMessageW(hListBox, LB_GETITEMDATA, index, 0);
2468 EndDialog(hDlg, res);
2469 return TRUE;
2470 }
2471 case IDCANCEL:
2472 EndDialog(hDlg, 0);
2473 return TRUE;
2474 }
2475 break;
2476 }
2477 return FALSE;
2478 }
2479
2480 /*
2481 *
2482 * MDI_MoreWindowsDialog
2483 *
2484 * Prompts the user with a listbox containing the opened
2485 * documents. The user can then choose a windows and click
2486 * on OK to set the current window to the one selected, or
2487 * CANCEL to cancel. The function returns a handle to the
2488 * selected window.
2489 */
2490
2491 static HWND MDI_MoreWindowsDialog(HWND hwnd)
2492 {
2493 LPCVOID template;
2494 HRSRC hRes;
2495 HANDLE hDlgTmpl;
2496
2497 hRes = FindResourceA(User32Instance, "MDI_MOREWINDOWS", (LPSTR)RT_DIALOG);
2498
2499 if (hRes == 0)
2500 return 0;
2501
2502 hDlgTmpl = LoadResource(User32Instance, hRes );
2503
2504 if (hDlgTmpl == 0)
2505 return 0;
2506
2507 template = LockResource( hDlgTmpl );
2508
2509 if (template == 0)
2510 return 0;
2511
2512 return (HWND) DialogBoxIndirectParamA(User32Instance, template, hwnd,
2513 MDI_MoreWindowsDlgProc, (LPARAM) hwnd);
2514 }