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