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