1 /* MDI.C 2 * 3 * Copyright 1994, Bob Amstadt 4 * Copyright 1995,1996 Alex Korobka 5 * Copyright 2018 Katayama Hirofumi MZ 6 * 7 * This file contains routines to support MDI (Multiple Document 8 * Interface) features. 9 * 10 * This library is free software; you can redistribute it and/or 11 * modify it under the terms of the GNU Lesser General Public 12 * License as published by the Free Software Foundation; either 13 * version 2.1 of the License, or (at your option) any later version. 14 * 15 * This library is distributed in the hope that it will be useful, 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 18 * Lesser General Public License for more details. 19 * 20 * You should have received a copy of the GNU Lesser General Public 21 * License along with this library; if not, write to the Free Software 22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 23 * 24 * Notes: Fairly complete implementation. 25 * Also, Excel and WinWord do _not_ use MDI so if you're trying 26 * to fix them look elsewhere. 27 * 28 * Notes on how the "More Windows..." is implemented: 29 * 30 * When we have more than 9 opened windows, a "More Windows..." 31 * option appears in the "Windows" menu. Each child window has 32 * a WND* associated with it, accessible via the children list of 33 * the parent window. This WND* has a wIDmenu member, which reflects 34 * the position of the child in the window list. For example, with 35 * 9 child windows, we could have the following pattern: 36 * 37 * 38 * 39 * Name of the child window pWndChild->wIDmenu 40 * Doc1 5000 41 * Doc2 5001 42 * Doc3 5002 43 * Doc4 5003 44 * Doc5 5004 45 * Doc6 5005 46 * Doc7 5006 47 * Doc8 5007 48 * Doc9 5008 49 * 50 * 51 * The "Windows" menu, as the "More windows..." dialog, are constructed 52 * in this order. If we add a child, we would have the following list: 53 * 54 * 55 * Name of the child window pWndChild->wIDmenu 56 * Doc1 5000 57 * Doc2 5001 58 * Doc3 5002 59 * Doc4 5003 60 * Doc5 5004 61 * Doc6 5005 62 * Doc7 5006 63 * Doc8 5007 64 * Doc9 5008 65 * Doc10 5009 66 * 67 * But only 5000 to 5008 would be displayed in the "Windows" menu. We want 68 * the last created child to be in the menu, so we swap the last child with 69 * the 9th... Doc9 will be accessible via the "More Windows..." option. 70 * 71 * Doc1 5000 72 * Doc2 5001 73 * Doc3 5002 74 * Doc4 5003 75 * Doc5 5004 76 * Doc6 5005 77 * Doc7 5006 78 * Doc8 5007 79 * Doc9 5009 80 * Doc10 5008 81 * 82 */ 83 84 #include <user32.h> 85 86 WINE_DEFAULT_DEBUG_CHANNEL(mdi); 87 88 #define MDI_MAXTITLELENGTH 0xa1 89 90 #define WM_MDICALCCHILDSCROLL 0x003F /* this is exactly what Windows uses */ 91 92 /* "More Windows..." definitions */ 93 #define MDI_MOREWINDOWSLIMIT 9 /* after this number of windows, a "More Windows..." 94 option will appear under the Windows menu */ 95 #define MDI_IDC_LISTBOX 100 96 #define IDS_MDI_MOREWINDOWS 13 97 98 #define MDIF_NEEDUPDATE 0x0001 99 100 typedef struct 101 { 102 /* At some points, particularly when switching MDI children, active and 103 * maximized MDI children may be not the same window, so we need to track 104 * them separately. 105 * The only place where we switch to/from maximized state is DefMDIChildProc 106 * WM_SIZE/SIZE_MAXIMIZED handler. We get that notification only after the 107 * ShowWindow(SW_SHOWMAXIMIZED) request, therefore window is guaranteed to 108 * be visible at the time we get the notification, and it's safe to assume 109 * that hwndChildMaximized is always visible. 110 * If the app plays games with WS_VISIBLE, WS_MAXIMIZE or any other window 111 * states it must keep coherency with USER32 on its own. This is true for 112 * Windows as well. 113 */ 114 LONG reserved; 115 UINT nActiveChildren; 116 HWND hwndChildMaximized; 117 HWND hwndActiveChild; 118 HWND *child; /* array of tracked children */ 119 HMENU hFrameMenu; 120 HMENU hWindowMenu; 121 UINT idFirstChild; 122 LPWSTR frameTitle; 123 UINT nTotalCreated; 124 UINT mdiFlags; 125 UINT sbRecalc; /* SB_xxx flags for scrollbar fixup */ 126 HBITMAP hBmpClose; /* ReactOS modification */ 127 } MDICLIENTINFO; 128 129 //static HBITMAP hBmpClose = 0; 130 131 /* ----------------- declarations ----------------- */ 132 static void MDI_UpdateFrameText( HWND, HWND, BOOL, LPCWSTR); 133 static BOOL MDI_AugmentFrameMenu( HWND, HWND ); 134 static BOOL MDI_RestoreFrameMenu( HWND, HWND, HBITMAP ); 135 static LONG MDI_ChildActivate( HWND, HWND ); 136 static LRESULT MDI_RefreshMenu(MDICLIENTINFO *); 137 138 static HWND MDI_MoreWindowsDialog(HWND); 139 140 HWND* WIN_ListChildren (HWND hWndparent) 141 { 142 143 DWORD dwCount = 0; 144 HWND* pHwnd = NULL; 145 HANDLE hHeap; 146 NTSTATUS Status; 147 148 Status = NtUserBuildHwndList(NULL, hWndparent, FALSE, 0, 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 */ 187 static HWND MDI_GetChildByID(HWND hwnd, UINT id, MDICLIENTINFO *ci) 188 { 189 int i; 190 191 for (i = 0; ci->nActiveChildren; i++) 192 { 193 if (GetWindowLongPtrW( ci->child[i], GWLP_ID ) == id) 194 return ci->child[i]; 195 } 196 return 0; 197 } 198 199 static void MDI_PostUpdate(HWND hwnd, MDICLIENTINFO* ci, WORD recalc) 200 { 201 if( !(ci->mdiFlags & MDIF_NEEDUPDATE) ) 202 { 203 ci->mdiFlags |= MDIF_NEEDUPDATE; 204 PostMessageA( hwnd, WM_MDICALCCHILDSCROLL, 0, 0); 205 } 206 ci->sbRecalc = recalc; 207 } 208 209 210 /********************************************************************* 211 * MDIClient class descriptor 212 */ 213 const struct builtin_class_descr MDICLIENT_builtin_class = 214 { 215 L"MDIClient", /* name */ 216 0, /* style */ 217 MDIClientWndProcA, /* procA */ 218 MDIClientWndProcW, /* procW */ 219 sizeof(MDIWND), /* extra */ 220 IDC_ARROW, /* cursor */ 221 (HBRUSH)(COLOR_APPWORKSPACE+1) /* brush */ 222 }; 223 224 225 static MDICLIENTINFO *get_client_info( HWND client ) 226 { 227 #ifdef __REACTOS__ 228 return (MDICLIENTINFO *)GetWindowLongPtr(client, GWLP_MDIWND); 229 #else 230 MDICLIENTINFO *ret = NULL; 231 WND *win = WIN_GetPtr( client ); 232 if (win) 233 { 234 if (win == WND_OTHER_PROCESS || win == WND_DESKTOP) 235 { 236 if (IsWindow(client)) WARN( "client %p belongs to other process\n", client ); 237 return NULL; 238 } 239 if (win->flags & WIN_ISMDICLIENT) 240 ret = (MDICLIENTINFO *)win->wExtra; 241 else 242 WARN( "%p is not an MDI client\n", client ); 243 WIN_ReleasePtr( win ); 244 } 245 return ret; 246 #endif 247 } 248 249 static BOOL is_close_enabled(HWND hwnd, HMENU hSysMenu) 250 { 251 if (GetClassLongPtrW(hwnd, GCL_STYLE) & CS_NOCLOSE) return FALSE; 252 253 if (!hSysMenu) hSysMenu = GetSystemMenu(hwnd, FALSE); 254 if (hSysMenu) 255 { 256 UINT state = GetMenuState(hSysMenu, SC_CLOSE, MF_BYCOMMAND); 257 if (state == 0xFFFFFFFF || (state & (MF_DISABLED | MF_GRAYED))) 258 return FALSE; 259 } 260 return TRUE; 261 } 262 263 /********************************************************************** 264 * MDI_GetWindow 265 * 266 * returns "activatable" child different from the current or zero 267 */ 268 static HWND MDI_GetWindow(MDICLIENTINFO *clientInfo, HWND hWnd, BOOL bNext, 269 DWORD dwStyleMask ) 270 { 271 int i; 272 HWND *list; 273 HWND last = 0; 274 275 dwStyleMask |= WS_DISABLED | WS_VISIBLE; 276 if( !hWnd ) hWnd = clientInfo->hwndActiveChild; 277 278 if (!(list = WIN_ListChildren( GetParent(hWnd) ))) return 0; 279 i = 0; 280 /* start from next after hWnd */ 281 while (list[i] && list[i] != hWnd) i++; 282 if (list[i]) i++; 283 284 for ( ; list[i]; i++) 285 { 286 if (GetWindow( list[i], GW_OWNER )) continue; 287 if ((GetWindowLongPtrW( list[i], GWL_STYLE ) & dwStyleMask) != WS_VISIBLE) continue; 288 last = list[i]; 289 if (bNext) goto found; 290 } 291 /* now restart from the beginning */ 292 for (i = 0; list[i] && list[i] != hWnd; i++) 293 { 294 if (GetWindow( list[i], GW_OWNER )) continue; 295 if ((GetWindowLongPtrW( list[i], GWL_STYLE ) & dwStyleMask) != WS_VISIBLE) continue; 296 last = list[i]; 297 if (bNext) goto found; 298 } 299 found: 300 HeapFree( GetProcessHeap(), 0, list ); 301 return last; 302 } 303 304 /********************************************************************** 305 * MDI_CalcDefaultChildPos 306 * 307 * It seems that the default height is about 2/3 of the client rect 308 */ 309 void MDI_CalcDefaultChildPos( HWND hwndClient, INT total, LPPOINT lpPos, INT delta, UINT *id ) 310 { 311 INT nstagger; 312 RECT rect; 313 INT spacing = GetSystemMetrics(SM_CYCAPTION) + GetSystemMetrics(SM_CYFRAME) - 1; 314 315 if (total < 0) /* we are called from CreateWindow */ 316 { 317 MDICLIENTINFO *ci = get_client_info(hwndClient); 318 total = ci ? ci->nTotalCreated : 0; // Do not portsync wine 319 *id = ci ? ci->idFirstChild + ci->nActiveChildren : 0; // Do not portsync wine 320 TRACE("MDI child id %04x\n", *id); 321 } 322 323 GetClientRect( hwndClient, &rect ); 324 if( rect.bottom - rect.top - delta >= spacing ) 325 rect.bottom -= delta; 326 327 nstagger = (rect.bottom - rect.top)/(3 * spacing); 328 lpPos[1].x = (rect.right - rect.left - nstagger * spacing); 329 lpPos[1].y = (rect.bottom - rect.top - nstagger * spacing); 330 lpPos[0].x = lpPos[0].y = spacing * (total%(nstagger+1)); 331 } 332 333 /********************************************************************** 334 * MDISetMenu 335 */ 336 static LRESULT MDISetMenu( HWND hwnd, HMENU hmenuFrame, 337 HMENU hmenuWindow) 338 { 339 MDICLIENTINFO *ci; 340 HWND hwndFrame = GetParent(hwnd); 341 342 TRACE("%p, frame menu %p, window menu %p\n", hwnd, hmenuFrame, hmenuWindow); 343 344 if (hmenuFrame && !IsMenu(hmenuFrame)) 345 { 346 WARN("hmenuFrame is not a menu handle\n"); 347 return 0L; 348 } 349 350 if (hmenuWindow && !IsMenu(hmenuWindow)) 351 { 352 WARN("hmenuWindow is not a menu handle\n"); 353 return 0L; 354 } 355 356 if (!(ci = get_client_info( hwnd ))) return 0; 357 358 TRACE("old frame menu %p, old window menu %p\n", ci->hFrameMenu, ci->hWindowMenu); 359 360 if (hmenuFrame) 361 { 362 if (hmenuFrame == ci->hFrameMenu) return (LRESULT)hmenuFrame; 363 364 if (ci->hwndChildMaximized) 365 MDI_RestoreFrameMenu( hwndFrame, ci->hwndChildMaximized, ci->hBmpClose ); 366 } 367 368 if( hmenuWindow && hmenuWindow != ci->hWindowMenu ) 369 { 370 /* delete menu items from ci->hWindowMenu 371 * and add them to hmenuWindow */ 372 /* Agent newsreader calls this function with ci->hWindowMenu == NULL */ 373 if( ci->hWindowMenu && ci->nActiveChildren ) 374 { 375 UINT nActiveChildren_old = ci->nActiveChildren; 376 377 /* Remove all items from old Window menu */ 378 ci->nActiveChildren = 0; 379 MDI_RefreshMenu(ci); 380 381 ci->hWindowMenu = hmenuWindow; 382 383 /* Add items to the new Window menu */ 384 ci->nActiveChildren = nActiveChildren_old; 385 MDI_RefreshMenu(ci); 386 } 387 else 388 ci->hWindowMenu = hmenuWindow; 389 } 390 391 if (hmenuFrame) 392 { 393 SetMenu(hwndFrame, hmenuFrame); 394 if( hmenuFrame != ci->hFrameMenu ) 395 { 396 HMENU oldFrameMenu = ci->hFrameMenu; 397 398 ci->hFrameMenu = hmenuFrame; 399 if (ci->hwndChildMaximized) 400 MDI_AugmentFrameMenu( hwndFrame, ci->hwndChildMaximized ); 401 402 return (LRESULT)oldFrameMenu; 403 } 404 } 405 406 return 0; 407 } 408 409 /********************************************************************** 410 * MDIRefreshMenu 411 */ 412 static LRESULT MDI_RefreshMenu(MDICLIENTINFO *ci) 413 { 414 UINT i, count, visible, id; 415 WCHAR buf[MDI_MAXTITLELENGTH]; 416 417 TRACE("children %u, window menu %p\n", ci->nActiveChildren, ci->hWindowMenu); 418 419 if (!ci->hWindowMenu) 420 return 0; 421 422 if (!IsMenu(ci->hWindowMenu)) 423 { 424 WARN("Window menu handle %p is no longer valid\n", ci->hWindowMenu); 425 return 0; 426 } 427 428 /* Windows finds the last separator in the menu, and if after it 429 * there is a menu item with MDI magic ID removes all existing 430 * menu items after it, and then adds visible MDI children. 431 */ 432 count = GetMenuItemCount(ci->hWindowMenu); 433 for (i = 0; i < count; i++) 434 { 435 MENUITEMINFOW mii; 436 437 memset(&mii, 0, sizeof(mii)); 438 mii.cbSize = sizeof(mii); 439 mii.fMask = MIIM_TYPE; 440 if (GetMenuItemInfoW(ci->hWindowMenu, i, TRUE, &mii)) 441 { 442 if (mii.fType & MF_SEPARATOR) 443 { 444 /* Windows checks only ID of the menu item */ 445 memset(&mii, 0, sizeof(mii)); 446 mii.cbSize = sizeof(mii); 447 mii.fMask = MIIM_ID; 448 if (GetMenuItemInfoW(ci->hWindowMenu, i + 1, TRUE, &mii)) 449 { 450 if (mii.wID == ci->idFirstChild) 451 { 452 TRACE("removing %u items including separator\n", count - i); 453 while (RemoveMenu(ci->hWindowMenu, i, MF_BYPOSITION)) 454 /* nothing */; 455 456 break; 457 } 458 } 459 } 460 } 461 } 462 463 visible = 0; 464 for (i = 0; i < ci->nActiveChildren; i++) 465 { 466 if (GetWindowLongPtrW(ci->child[i], GWL_STYLE) & WS_VISIBLE) 467 { 468 id = ci->idFirstChild + visible; 469 470 if (visible == MDI_MOREWINDOWSLIMIT) 471 { 472 LoadStringW(User32Instance, IDS_MDI_MOREWINDOWS, buf, sizeof(buf)/sizeof(WCHAR)); 473 AppendMenuW(ci->hWindowMenu, MF_STRING, id, buf); 474 break; 475 } 476 477 if (!visible) 478 /* Visio expects that separator has id 0 */ 479 AppendMenuW(ci->hWindowMenu, MF_SEPARATOR, 0, NULL); 480 481 visible++; 482 483 SetWindowLongPtrW(ci->child[i], GWLP_ID, id); 484 485 buf[0] = '&'; 486 buf[1] = '0' + visible; 487 buf[2] = ' '; 488 InternalGetWindowText(ci->child[i], buf + 3, sizeof(buf)/sizeof(WCHAR) - 3); 489 TRACE("Adding %p, id %u %s\n", ci->child[i], id, debugstr_w(buf)); 490 AppendMenuW(ci->hWindowMenu, MF_STRING, id, buf); 491 492 if (ci->child[i] == ci->hwndActiveChild) 493 CheckMenuItem(ci->hWindowMenu, id, MF_CHECKED); 494 } 495 else 496 TRACE("MDI child %p is not visible, skipping\n", ci->child[i]); 497 } 498 499 return (LRESULT)ci->hFrameMenu; 500 } 501 502 503 /* ------------------ MDI child window functions ---------------------- */ 504 505 /********************************************************************** 506 * MDI_ChildGetMinMaxInfo 507 * 508 * Note: The rule here is that client rect of the maximized MDI child 509 * is equal to the client rect of the MDI client window. 510 */ 511 static void MDI_ChildGetMinMaxInfo( HWND client, HWND hwnd, MINMAXINFO* lpMinMax ) 512 { 513 RECT rect; 514 515 GetClientRect( client, &rect ); 516 AdjustWindowRectEx( &rect, GetWindowLongPtrW( hwnd, GWL_STYLE ), 517 0, GetWindowLongPtrW( hwnd, GWL_EXSTYLE )); 518 519 lpMinMax->ptMaxSize.x = rect.right -= rect.left; 520 lpMinMax->ptMaxSize.y = rect.bottom -= rect.top; 521 522 lpMinMax->ptMaxPosition.x = rect.left; 523 lpMinMax->ptMaxPosition.y = rect.top; 524 525 TRACE("max rect %s\n", wine_dbgstr_rect(&rect)); 526 } 527 528 /********************************************************************** 529 * MDI_SwitchActiveChild 530 * 531 * Note: SetWindowPos sends WM_CHILDACTIVATE to the child window that is 532 * being activated 533 */ 534 static void MDI_SwitchActiveChild( MDICLIENTINFO *ci, HWND hwndTo, BOOL activate ) 535 { 536 HWND hwndPrev; 537 538 hwndPrev = ci->hwndActiveChild; 539 540 TRACE("from %p, to %p\n", hwndPrev, hwndTo); 541 542 if ( hwndTo != hwndPrev ) 543 { 544 BOOL was_zoomed = IsZoomed(hwndPrev); 545 546 if (was_zoomed) 547 { 548 /* restore old MDI child */ 549 SendMessageW( hwndPrev, WM_SETREDRAW, FALSE, 0 ); 550 ShowWindow( hwndPrev, SW_RESTORE ); 551 SendMessageW( hwndPrev, WM_SETREDRAW, TRUE, 0 ); 552 553 /* activate new MDI child */ 554 SetWindowPos( hwndTo, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE ); 555 /* maximize new MDI child */ 556 ShowWindow( hwndTo, SW_MAXIMIZE ); 557 } 558 /* activate new MDI child */ 559 SetWindowPos( hwndTo, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | (activate ? 0 : SWP_NOACTIVATE) ); 560 } 561 } 562 563 564 /********************************************************************** 565 * MDIDestroyChild 566 */ 567 static LRESULT MDIDestroyChild( HWND client, MDICLIENTINFO *ci, 568 HWND child, BOOL flagDestroy ) 569 { 570 UINT i; 571 572 TRACE("# of managed children %u\n", ci->nActiveChildren); 573 574 if( child == ci->hwndActiveChild ) 575 { 576 HWND next = MDI_GetWindow(ci, child, TRUE, 0); 577 if (next) 578 MDI_SwitchActiveChild(ci, next, TRUE); 579 else 580 { 581 ShowWindow(child, SW_HIDE); 582 if (child == ci->hwndChildMaximized) 583 { 584 HWND frame = GetParent(client); 585 MDI_RestoreFrameMenu(frame, child, ci->hBmpClose); 586 ci->hwndChildMaximized = 0; 587 MDI_UpdateFrameText(frame, client, TRUE, NULL); 588 } 589 if (flagDestroy) 590 MDI_ChildActivate(client, 0); 591 } 592 } 593 594 for (i = 0; i < ci->nActiveChildren; i++) 595 { 596 if (ci->child[i] == child) 597 { 598 HWND *new_child = HeapAlloc(GetProcessHeap(), 0, (ci->nActiveChildren - 1) * sizeof(HWND)); 599 if (new_child != NULL) 600 { 601 memcpy(new_child, ci->child, i * sizeof(HWND)); 602 if (i + 1 < ci->nActiveChildren) 603 memcpy(new_child + i, ci->child + i + 1, (ci->nActiveChildren - i - 1) * sizeof(HWND)); 604 HeapFree(GetProcessHeap(), 0, ci->child); 605 ci->child = new_child; 606 } 607 else 608 { 609 UINT c; 610 for (c = i; c < ci->nActiveChildren - 1; c++) 611 { 612 ci->child[c] = ci->child[c+1]; 613 } 614 } 615 616 ci->nActiveChildren--; 617 break; 618 } 619 } 620 621 if (flagDestroy) 622 { 623 SendMessageW(client, WM_MDIREFRESHMENU, 0, 0); 624 MDI_PostUpdate(GetParent(child), ci, SB_BOTH+1); 625 DestroyWindow(child); 626 } 627 628 TRACE("child destroyed - %p\n", child); 629 return 0; 630 } 631 632 633 /********************************************************************** 634 * MDI_ChildActivate 635 * 636 * Called in response to WM_CHILDACTIVATE, or when last MDI child 637 * is being deactivated. 638 */ 639 static LONG MDI_ChildActivate( HWND client, HWND child ) 640 { 641 MDICLIENTINFO *clientInfo; 642 HWND prevActiveWnd, frame; 643 BOOL isActiveFrameWnd; 644 645 clientInfo = get_client_info( client ); 646 647 if (clientInfo->hwndActiveChild == child) return 0; 648 649 TRACE("%p\n", child); 650 651 frame = GetParent(client); 652 isActiveFrameWnd = (GetActiveWindow() == frame); 653 prevActiveWnd = clientInfo->hwndActiveChild; 654 655 /* deactivate prev. active child */ 656 if(prevActiveWnd) 657 { 658 SendMessageW( prevActiveWnd, WM_NCACTIVATE, FALSE, 0L ); 659 SendMessageW( prevActiveWnd, WM_MDIACTIVATE, (WPARAM)prevActiveWnd, (LPARAM)child); 660 } 661 662 MDI_SwitchActiveChild( clientInfo, child, FALSE ); 663 clientInfo->hwndActiveChild = child; 664 665 MDI_RefreshMenu(clientInfo); 666 667 if( isActiveFrameWnd ) 668 { 669 SendMessageW( child, WM_NCACTIVATE, TRUE, 0L); 670 /* Let the client window manage focus for children, but if the focus 671 * is already on the client (for instance this is the 1st child) then 672 * SetFocus won't work. It appears that Windows sends WM_SETFOCUS 673 * manually in this case. 674 */ 675 if (SetFocus(client) == client) 676 SendMessageW( client, WM_SETFOCUS, (WPARAM)client, 0 ); 677 } 678 679 SendMessageW( child, WM_MDIACTIVATE, (WPARAM)prevActiveWnd, (LPARAM)child ); 680 return TRUE; 681 } 682 683 /* -------------------- MDI client window functions ------------------- */ 684 685 /********************************************************************** 686 * CreateMDIMenuBitmap 687 */ 688 static HBITMAP CreateMDIMenuBitmap(void) 689 { 690 HDC hDCSrc = CreateCompatibleDC(0); 691 HDC hDCDest = CreateCompatibleDC(hDCSrc); 692 HBITMAP hbClose = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_OLD_CLOSE) ); 693 HBITMAP hbCopy; 694 HBITMAP hobjSrc, hobjDest; 695 696 hobjSrc = SelectObject(hDCSrc, hbClose); 697 hbCopy = CreateCompatibleBitmap(hDCSrc,GetSystemMetrics(SM_CXSIZE),GetSystemMetrics(SM_CYSIZE)); 698 hobjDest = SelectObject(hDCDest, hbCopy); 699 700 BitBlt(hDCDest, 0, 0, GetSystemMetrics(SM_CXSIZE), GetSystemMetrics(SM_CYSIZE), 701 hDCSrc, GetSystemMetrics(SM_CXSIZE), 0, SRCCOPY); 702 703 SelectObject(hDCSrc, hobjSrc); 704 DeleteObject(hbClose); 705 DeleteDC(hDCSrc); 706 707 hobjSrc = SelectObject( hDCDest, GetStockObject(BLACK_PEN) ); 708 709 MoveToEx( hDCDest, GetSystemMetrics(SM_CXSIZE) - 1, 0, NULL ); 710 LineTo( hDCDest, GetSystemMetrics(SM_CXSIZE) - 1, GetSystemMetrics(SM_CYSIZE) - 1); 711 712 SelectObject(hDCDest, hobjSrc ); 713 SelectObject(hDCDest, hobjDest); 714 DeleteDC(hDCDest); 715 716 return hbCopy; 717 } 718 719 /********************************************************************** 720 * MDICascade 721 */ 722 static LONG MDICascade( HWND client, MDICLIENTINFO *ci ) 723 { 724 HWND *win_array; 725 BOOL has_icons = FALSE; 726 int i, total; 727 728 if (ci->hwndChildMaximized) 729 SendMessageW(client, WM_MDIRESTORE, (WPARAM)ci->hwndChildMaximized, 0); 730 731 if (ci->nActiveChildren == 0) return 0; 732 733 if (!(win_array = WIN_ListChildren( client ))) return 0; 734 735 /* remove all the windows we don't want */ 736 for (i = total = 0; win_array[i]; i++) 737 { 738 if (!IsWindowVisible( win_array[i] )) continue; 739 if (GetWindow( win_array[i], GW_OWNER )) continue; /* skip owned windows */ 740 if (IsIconic( win_array[i] )) 741 { 742 has_icons = TRUE; 743 continue; 744 } 745 win_array[total++] = win_array[i]; 746 } 747 win_array[total] = 0; 748 749 if (total) 750 { 751 INT delta = 0, n = 0, i; 752 POINT pos[2]; 753 if (has_icons) delta = GetSystemMetrics(SM_CYICONSPACING) + GetSystemMetrics(SM_CYICON); 754 755 /* walk the list (backwards) and move windows */ 756 for (i = total - 1; i >= 0; i--) 757 { 758 LONG style; 759 LONG posOptions = SWP_DRAWFRAME | SWP_NOACTIVATE | SWP_NOZORDER; 760 761 MDI_CalcDefaultChildPos(client, n++, pos, delta, NULL); 762 TRACE("move %p to (%ld,%ld) size [%ld,%ld]\n", 763 win_array[i], pos[0].x, pos[0].y, pos[1].x, pos[1].y); 764 style = GetWindowLongW(win_array[i], GWL_STYLE); 765 766 if (!(style & WS_SIZEBOX)) posOptions |= SWP_NOSIZE; 767 SetWindowPos( win_array[i], 0, pos[0].x, pos[0].y, pos[1].x, pos[1].y, 768 posOptions); 769 } 770 } 771 HeapFree( GetProcessHeap(), 0, win_array ); 772 773 if (has_icons) ArrangeIconicWindows( client ); 774 return 0; 775 } 776 777 /********************************************************************** 778 * MDITile 779 */ 780 static void MDITile( HWND client, MDICLIENTINFO *ci, WPARAM wParam ) 781 { 782 HWND *win_array; 783 int i, total, rows, columns; 784 BOOL has_icons = FALSE; 785 786 if (ci->hwndChildMaximized) 787 SendMessageW(client, WM_MDIRESTORE, (WPARAM)ci->hwndChildMaximized, 0); 788 789 if (ci->nActiveChildren == 0) return; 790 791 if (!(win_array = WIN_ListChildren( client ))) return; 792 793 /* remove all the windows we don't want */ 794 for (i = total = rows = 0; win_array[i]; i++) 795 { 796 if (!IsWindowVisible( win_array[i] )) continue; 797 if (GetWindow( win_array[i], GW_OWNER )) continue; /* skip owned windows (icon titles) */ 798 if (IsIconic( win_array[i] )) 799 { 800 has_icons = TRUE; 801 continue; 802 } 803 if ((wParam & MDITILE_SKIPDISABLED) && !IsWindowEnabled( win_array[i] )) continue; 804 if(total == (rows * (rows + 2))) rows++; /* total+1 == (rows+1)*(rows+1) */ 805 win_array[total++] = win_array[i]; 806 } 807 win_array[total] = 0; 808 809 TRACE("%u windows to tile\n", total); 810 811 if (total) 812 { 813 HWND *pWnd = win_array; 814 RECT rect; 815 int x, y, xsize, ysize; 816 int r, c, i; 817 818 GetClientRect(client,&rect); 819 columns = total/rows; 820 //while(total < rows*columns) rows++; 821 822 if( wParam & MDITILE_HORIZONTAL ) /* version >= 3.1 */ 823 { 824 i = rows; 825 rows = columns; /* exchange r and c */ 826 columns = i; 827 } 828 829 if (has_icons) 830 { 831 y = rect.bottom - 2 * GetSystemMetrics(SM_CYICONSPACING) - GetSystemMetrics(SM_CYICON); 832 rect.bottom = ( y - GetSystemMetrics(SM_CYICON) < rect.top )? rect.bottom: y; 833 } 834 835 ysize = rect.bottom / rows; 836 xsize = rect.right / columns; 837 838 for (x = i = 0, c = 1; c <= columns && *pWnd; c++) 839 { 840 if (c == columns) 841 { 842 rows = total - i; 843 ysize = rect.bottom / rows; 844 } 845 846 y = 0; 847 for (r = 1; r <= rows && *pWnd; r++, i++) 848 { 849 LONG posOptions = SWP_DRAWFRAME | SWP_NOACTIVATE | SWP_NOZORDER; 850 LONG style = GetWindowLongW(win_array[i], GWL_STYLE); 851 if (!(style & WS_SIZEBOX)) posOptions |= SWP_NOSIZE; 852 853 SetWindowPos(*pWnd, 0, x, y, xsize, ysize, posOptions); 854 y += ysize; 855 pWnd++; 856 } 857 x += xsize; 858 } 859 } 860 HeapFree( GetProcessHeap(), 0, win_array ); 861 if (has_icons) ArrangeIconicWindows( client ); 862 } 863 864 /* ----------------------- Frame window ---------------------------- */ 865 866 867 /********************************************************************** 868 * MDI_AugmentFrameMenu 869 */ 870 static BOOL MDI_AugmentFrameMenu( HWND frame, HWND hChild ) 871 { 872 HMENU menu = GetMenu( frame ); 873 HMENU hSysPopup; 874 HBITMAP hSysMenuBitmap = 0; 875 HICON hIcon; 876 INT nItems; 877 UINT iId; 878 879 TRACE("frame %p,child %p\n",frame,hChild); 880 881 if( !menu ) return FALSE; 882 //// ReactOS start 883 /* if the system buttons already exist do not add them again */ 884 nItems = GetMenuItemCount(menu) - 1; 885 iId = GetMenuItemID(menu,nItems) ; 886 if (iId == SC_RESTORE || iId == SC_CLOSE) 887 { 888 ERR("system buttons already exist\n"); 889 return FALSE; 890 } 891 //// End 892 /* create a copy of sysmenu popup and insert it into frame menu bar */ 893 if (!(hSysPopup = GetSystemMenu(hChild, FALSE))) 894 { 895 TRACE("child %p doesn't have a system menu\n", hChild); 896 return FALSE; 897 } 898 899 AppendMenuW(menu, MF_HELP | MF_BITMAP, 900 SC_CLOSE, is_close_enabled(hChild, hSysPopup) ? 901 (LPCWSTR)HBMMENU_MBAR_CLOSE : (LPCWSTR)HBMMENU_MBAR_CLOSE_D ); 902 AppendMenuW(menu, MF_HELP | MF_BITMAP, 903 SC_RESTORE, (LPCWSTR)HBMMENU_MBAR_RESTORE ); 904 AppendMenuW(menu, MF_HELP | MF_BITMAP, 905 SC_MINIMIZE, (LPCWSTR)HBMMENU_MBAR_MINIMIZE ) ; 906 907 /* The system menu is replaced by the child icon */ 908 hIcon = (HICON)SendMessageW(hChild, WM_GETICON, ICON_SMALL, 0); 909 if (!hIcon) 910 hIcon = (HICON)GetClassLongPtrW(hChild, GCLP_HICONSM); 911 if (!hIcon) 912 hIcon = (HICON)SendMessageW(hChild, WM_GETICON, ICON_BIG, 0); 913 if (!hIcon) 914 hIcon = (HICON)GetClassLongPtrW(hChild, GCLP_HICON); 915 if (!hIcon) 916 hIcon = LoadImageW(0, (LPWSTR)IDI_WINLOGO, IMAGE_ICON, GetSystemMetrics(SM_CXSMICON), 917 GetSystemMetrics(SM_CYSMICON), LR_DEFAULTCOLOR); 918 if (hIcon) 919 { 920 HDC hMemDC; 921 HBITMAP hBitmap, hOldBitmap; 922 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 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 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 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 */ 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 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 */ 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 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 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