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