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