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, 0, 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, 0, 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 HBRUSH hBrush; 923 HDC hdc = GetDC(hChild); 924 925 if (hdc) 926 { 927 int cx, cy; 928 cx = GetSystemMetrics(SM_CXSMICON); 929 cy = GetSystemMetrics(SM_CYSMICON); 930 hMemDC = CreateCompatibleDC(hdc); 931 hBitmap = CreateCompatibleBitmap(hdc, cx, cy); 932 hOldBitmap = SelectObject(hMemDC, hBitmap); 933 SetMapMode(hMemDC, MM_TEXT); 934 hBrush = CreateSolidBrush(GetSysColor(COLOR_MENU)); 935 DrawIconEx(hMemDC, 0, 0, hIcon, cx, cy, 0, hBrush, DI_NORMAL); 936 SelectObject (hMemDC, hOldBitmap); 937 DeleteObject(hBrush); 938 DeleteDC(hMemDC); 939 ReleaseDC(hChild, hdc); 940 hSysMenuBitmap = hBitmap; 941 } 942 } 943 944 if( !InsertMenuA(menu,0,MF_BYPOSITION | MF_BITMAP | MF_POPUP, 945 (UINT_PTR)hSysPopup, (LPSTR)hSysMenuBitmap)) 946 { 947 TRACE("not inserted\n"); 948 DestroyMenu(hSysPopup); 949 return FALSE; 950 } 951 952 EnableMenuItem(hSysPopup, SC_SIZE, MF_BYCOMMAND | MF_GRAYED); 953 EnableMenuItem(hSysPopup, SC_MOVE, MF_BYCOMMAND | MF_GRAYED); 954 EnableMenuItem(hSysPopup, SC_MAXIMIZE, MF_BYCOMMAND | MF_GRAYED); 955 SetMenuDefaultItem(hSysPopup, SC_CLOSE, FALSE); 956 957 /* redraw menu */ 958 DrawMenuBar(frame); 959 960 return TRUE; 961 } 962 963 /********************************************************************** 964 * MDI_RestoreFrameMenu 965 */ 966 static BOOL MDI_RestoreFrameMenu( HWND frame, HWND hChild, HBITMAP hBmpClose ) 967 { 968 MENUITEMINFOW menuInfo; 969 HMENU menu = GetMenu( frame ); 970 INT nItems; 971 UINT iId; 972 973 TRACE("frame %p,child %p\n",frame, hChild); 974 975 if (!menu) return FALSE; 976 977 /* if there is no system buttons then nothing to do */ 978 nItems = GetMenuItemCount(menu) - 1; 979 iId = GetMenuItemID(menu, nItems); 980 if ( !(iId == SC_RESTORE || iId == SC_CLOSE) ) 981 { 982 ERR("no system buttons then nothing to do\n"); 983 return FALSE; 984 } 985 986 /* 987 * Remove the system menu, If that menu is the icon of the window 988 * as it is in win95, we have to delete the bitmap. 989 */ 990 memset(&menuInfo, 0, sizeof(menuInfo)); 991 menuInfo.cbSize = sizeof(menuInfo); 992 menuInfo.fMask = MIIM_DATA | MIIM_TYPE | MIIM_BITMAP; 993 994 GetMenuItemInfoW(menu, 995 0, 996 TRUE, 997 &menuInfo); 998 999 RemoveMenu(menu,0,MF_BYPOSITION); 1000 1001 if ( (menuInfo.fType & MFT_BITMAP) && 1002 (menuInfo.dwTypeData != 0) && 1003 (menuInfo.dwTypeData != (LPWSTR)hBmpClose) ) 1004 { 1005 DeleteObject(menuInfo.dwTypeData); 1006 } 1007 1008 if ( menuInfo.hbmpItem != 0 ) 1009 DeleteObject(menuInfo.hbmpItem); 1010 1011 /* close */ 1012 DeleteMenu(menu, SC_CLOSE, MF_BYCOMMAND); 1013 /* restore */ 1014 DeleteMenu(menu, SC_RESTORE, MF_BYCOMMAND); 1015 /* minimize */ 1016 DeleteMenu(menu, SC_MINIMIZE, MF_BYCOMMAND); 1017 1018 DrawMenuBar(frame); 1019 1020 return TRUE; 1021 } 1022 1023 1024 /********************************************************************** 1025 * MDI_UpdateFrameText 1026 * 1027 * used when child window is maximized/restored 1028 * 1029 * Note: lpTitle can be NULL 1030 */ 1031 static void MDI_UpdateFrameText( HWND frame, HWND hClient, BOOL repaint, LPCWSTR lpTitle ) 1032 { 1033 WCHAR lpBuffer[MDI_MAXTITLELENGTH+1]; 1034 MDICLIENTINFO *ci = get_client_info( hClient ); 1035 1036 TRACE("frameText %s\n", debugstr_w(lpTitle)); 1037 1038 if (!ci) return; 1039 1040 if (!lpTitle && !ci->frameTitle) /* first time around, get title from the frame window */ 1041 { 1042 GetWindowTextW( frame, lpBuffer, sizeof(lpBuffer)/sizeof(WCHAR) ); 1043 lpTitle = lpBuffer; 1044 } 1045 1046 /* store new "default" title if lpTitle is not NULL */ 1047 if (lpTitle) 1048 { 1049 HeapFree( GetProcessHeap(), 0, ci->frameTitle ); 1050 if ((ci->frameTitle = HeapAlloc( GetProcessHeap(), 0, (strlenW(lpTitle)+1)*sizeof(WCHAR)))) 1051 strcpyW( ci->frameTitle, lpTitle ); 1052 } 1053 1054 if (ci->frameTitle) 1055 { 1056 if (ci->hwndChildMaximized) 1057 { 1058 /* combine frame title and child title if possible */ 1059 1060 static const WCHAR lpBracket[] = {' ','-',' ','[',0}; 1061 static const WCHAR lpBracket2[] = {']',0}; 1062 int i_frame_text_length = strlenW(ci->frameTitle); 1063 1064 lstrcpynW( lpBuffer, ci->frameTitle, MDI_MAXTITLELENGTH); 1065 1066 if( i_frame_text_length + 6 < MDI_MAXTITLELENGTH ) 1067 { 1068 strcatW( lpBuffer, lpBracket ); 1069 if (GetWindowTextW( ci->hwndActiveChild, lpBuffer + i_frame_text_length + 4, 1070 MDI_MAXTITLELENGTH - i_frame_text_length - 5 )) 1071 strcatW( lpBuffer, lpBracket2 ); 1072 else 1073 lpBuffer[i_frame_text_length] = 0; /* remove bracket */ 1074 } 1075 } 1076 else 1077 { 1078 lstrcpynW(lpBuffer, ci->frameTitle, MDI_MAXTITLELENGTH+1 ); 1079 } 1080 } 1081 else 1082 lpBuffer[0] = '\0'; 1083 1084 DefWindowProcW( frame, WM_SETTEXT, 0, (LPARAM)lpBuffer ); 1085 1086 if (repaint) 1087 { 1088 if (!NtUserCallTwoParam((DWORD_PTR)frame,DC_ACTIVE,TWOPARAM_ROUTINE_REDRAWTITLE)) 1089 SetWindowPos( frame, 0,0,0,0,0, SWP_FRAMECHANGED | 1090 SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER ); 1091 } 1092 } 1093 1094 1095 /* ----------------------------- Interface ---------------------------- */ 1096 1097 1098 /********************************************************************** 1099 * MDIClientWndProc_common 1100 */ 1101 LRESULT WINAPI MDIClientWndProc_common( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam, BOOL unicode ) 1102 { 1103 MDICLIENTINFO *ci = NULL; 1104 1105 TRACE("%p %04x (%s) %08lx %08lx\n", hwnd, message, SPY_GetMsgName(message, hwnd), wParam, lParam); 1106 1107 if (!(ci = get_client_info( hwnd ))) 1108 { 1109 #ifdef __REACTOS__ 1110 if (message == WM_NCCREATE) 1111 { 1112 if (!(ci = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*ci)))) 1113 return FALSE; 1114 SetWindowLongPtrW( hwnd, GWLP_MDIWND, (LONG_PTR)ci ); 1115 ci->hBmpClose = 0; 1116 NtUserSetWindowFNID( hwnd, FNID_MDICLIENT); // wine uses WIN_ISMDICLIENT 1117 } 1118 #else 1119 if (message == WM_NCCREATE) win_set_flags( hwnd, WIN_ISMDICLIENT, 0 ); 1120 #endif 1121 return unicode ? DefWindowProcW( hwnd, message, wParam, lParam ) : 1122 DefWindowProcA( hwnd, message, wParam, lParam ); 1123 } 1124 1125 switch (message) 1126 { 1127 case WM_CREATE: 1128 { 1129 /* Since we are using only cs->lpCreateParams, we can safely 1130 * cast to LPCREATESTRUCTA here */ 1131 LPCREATESTRUCTA cs = (LPCREATESTRUCTA)lParam; 1132 LPCLIENTCREATESTRUCT ccs = (LPCLIENTCREATESTRUCT)cs->lpCreateParams; 1133 1134 ci->hWindowMenu = ccs->hWindowMenu; 1135 ci->idFirstChild = ccs->idFirstChild; 1136 ci->hwndChildMaximized = 0; 1137 ci->child = NULL; 1138 ci->nActiveChildren = 0; 1139 ci->nTotalCreated = 0; 1140 ci->frameTitle = NULL; 1141 ci->mdiFlags = 0; 1142 ci->hFrameMenu = GetMenu(cs->hwndParent); 1143 1144 if (!ci->hBmpClose) ci->hBmpClose = CreateMDIMenuBitmap(); 1145 1146 TRACE("Client created: hwnd %p, Window menu %p, idFirst = %04x\n", 1147 hwnd, ci->hWindowMenu, ci->idFirstChild ); 1148 return 0; 1149 } 1150 1151 case WM_DESTROY: 1152 { 1153 if( ci->hwndChildMaximized ) 1154 MDI_RestoreFrameMenu(GetParent(hwnd), ci->hwndChildMaximized, ci->hBmpClose); 1155 1156 ci->nActiveChildren = 0; 1157 MDI_RefreshMenu(ci); 1158 1159 HeapFree( GetProcessHeap(), 0, ci->child ); 1160 HeapFree( GetProcessHeap(), 0, ci->frameTitle ); 1161 #ifdef __REACTOS__ 1162 HeapFree( GetProcessHeap(), 0, ci ); 1163 SetWindowLongPtrW( hwnd, GWLP_MDIWND, 0 ); 1164 #endif 1165 return 0; 1166 } 1167 1168 #ifdef __REACTOS__ 1169 case WM_NCDESTROY: 1170 { 1171 NtUserSetWindowFNID(hwnd, FNID_DESTROY); 1172 return 0; 1173 } 1174 #endif 1175 1176 case WM_MDIACTIVATE: 1177 { 1178 if( ci->hwndActiveChild != (HWND)wParam ) 1179 SetWindowPos((HWND)wParam, 0,0,0,0,0, SWP_NOSIZE | SWP_NOMOVE); 1180 return 0; 1181 } 1182 1183 case WM_MDICASCADE: 1184 return MDICascade(hwnd, ci); 1185 1186 case WM_MDICREATE: 1187 if (lParam) 1188 { 1189 HWND child; 1190 1191 if (unicode) 1192 { 1193 MDICREATESTRUCTW *csW = (MDICREATESTRUCTW *)lParam; 1194 child = CreateWindowExW(WS_EX_MDICHILD, csW->szClass, 1195 csW->szTitle, csW->style, 1196 csW->x, csW->y, csW->cx, csW->cy, 1197 hwnd, 0, csW->hOwner, 1198 (LPVOID)csW->lParam); 1199 } 1200 else 1201 { 1202 MDICREATESTRUCTA *csA = (MDICREATESTRUCTA *)lParam; 1203 child = CreateWindowExA(WS_EX_MDICHILD, csA->szClass, 1204 csA->szTitle, csA->style, 1205 csA->x, csA->y, csA->cx, csA->cy, 1206 hwnd, 0, csA->hOwner, 1207 (LPVOID)csA->lParam); 1208 } 1209 return (LRESULT)child; 1210 } 1211 return 0; 1212 1213 case WM_MDIDESTROY: 1214 return MDIDestroyChild( hwnd, ci, (HWND)wParam, TRUE ); 1215 1216 case WM_MDIGETACTIVE: 1217 if (lParam) *(BOOL *)lParam = IsZoomed(ci->hwndActiveChild); 1218 return (LRESULT)ci->hwndActiveChild; 1219 1220 case WM_MDIICONARRANGE: 1221 ci->mdiFlags |= MDIF_NEEDUPDATE; 1222 ArrangeIconicWindows( hwnd ); 1223 ci->sbRecalc = SB_BOTH+1; 1224 #ifdef __REACTOS__ 1225 PostMessageA( hwnd, WM_MDICALCCHILDSCROLL, 0, 0 ); //// ReactOS: Post not send! 1226 #else 1227 SendMessageW( hwnd, WM_MDICALCCHILDSCROLL, 0, 0 ); 1228 #endif 1229 return 0; 1230 1231 case WM_MDIMAXIMIZE: 1232 ShowWindow( (HWND)wParam, SW_MAXIMIZE ); 1233 return 0; 1234 1235 case WM_MDINEXT: /* lParam != 0 means previous window */ 1236 { 1237 HWND hwnd = wParam ? WIN_GetFullHandle((HWND)wParam) : ci->hwndActiveChild; 1238 HWND next = MDI_GetWindow( ci, hwnd, !lParam, 0 ); 1239 MDI_SwitchActiveChild( ci, next, TRUE ); 1240 if(!lParam) 1241 SetWindowPos(hwnd, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE); 1242 break; 1243 } 1244 1245 case WM_MDIRESTORE: 1246 ShowWindow( (HWND)wParam, SW_SHOWNORMAL ); 1247 return 0; 1248 1249 case WM_MDISETMENU: 1250 return MDISetMenu( hwnd, (HMENU)wParam, (HMENU)lParam ); 1251 1252 case WM_MDIREFRESHMENU: 1253 return MDI_RefreshMenu( ci ); 1254 1255 case WM_MDITILE: 1256 ci->mdiFlags |= MDIF_NEEDUPDATE; 1257 ShowScrollBar( hwnd, SB_BOTH, FALSE ); 1258 MDITile( hwnd, ci, wParam ); 1259 ci->mdiFlags &= ~MDIF_NEEDUPDATE; 1260 return 0; 1261 1262 case WM_VSCROLL: 1263 case WM_HSCROLL: 1264 ci->mdiFlags |= MDIF_NEEDUPDATE; 1265 ScrollChildren( hwnd, message, wParam, lParam ); 1266 ci->mdiFlags &= ~MDIF_NEEDUPDATE; 1267 return 0; 1268 1269 case WM_SETFOCUS: 1270 if (ci->hwndActiveChild && !IsIconic( ci->hwndActiveChild )) 1271 SetFocus( ci->hwndActiveChild ); 1272 return 0; 1273 1274 case WM_NCACTIVATE: 1275 if( ci->hwndActiveChild ) 1276 SendMessageW(ci->hwndActiveChild, message, wParam, lParam); 1277 break; 1278 1279 case WM_PARENTNOTIFY: 1280 switch (LOWORD(wParam)) 1281 { 1282 case WM_CREATE: 1283 if (GetWindowLongPtrW((HWND)lParam, GWL_EXSTYLE) & WS_EX_MDICHILD) 1284 { 1285 // ReactOS See rev 33503 1286 if (!ci->child) 1287 ci->child = HeapAlloc(GetProcessHeap(), 0, sizeof(HWND)); 1288 else 1289 ci->child = HeapReAlloc(GetProcessHeap(), 0, ci->child, sizeof(HWND) * (ci->nActiveChildren + 1)); 1290 1291 TRACE("Adding MDI child %p, # of children %d\n", 1292 (HWND)lParam, ci->nActiveChildren); 1293 1294 if (ci->child != NULL) 1295 { 1296 ci->child[ci->nActiveChildren] = (HWND)lParam; 1297 ci->nTotalCreated++; 1298 ci->nActiveChildren++; 1299 } 1300 } 1301 break; 1302 1303 case WM_LBUTTONDOWN: 1304 { 1305 HWND child; 1306 POINT pt; 1307 pt.x = (short)LOWORD(lParam); 1308 pt.y = (short)HIWORD(lParam); 1309 child = ChildWindowFromPoint(hwnd, pt); 1310 1311 TRACE("notification from %p (%li,%li)\n",child,pt.x,pt.y); 1312 1313 if( child && child != hwnd && child != ci->hwndActiveChild ) 1314 SetWindowPos(child, 0,0,0,0,0, SWP_NOSIZE | SWP_NOMOVE ); 1315 break; 1316 } 1317 1318 case WM_DESTROY: 1319 return MDIDestroyChild( hwnd, ci, WIN_GetFullHandle( (HWND)lParam ), FALSE ); 1320 } 1321 return 0; 1322 1323 case WM_SIZE: 1324 if( ci->hwndActiveChild && IsZoomed(ci->hwndActiveChild) ) 1325 { 1326 RECT rect; 1327 1328 SetRect(&rect, 0, 0, LOWORD(lParam), HIWORD(lParam)); 1329 AdjustWindowRectEx(&rect, GetWindowLongPtrA(ci->hwndActiveChild, GWL_STYLE), 1330 0, GetWindowLongPtrA(ci->hwndActiveChild, GWL_EXSTYLE) ); 1331 MoveWindow(ci->hwndActiveChild, rect.left, rect.top, 1332 rect.right - rect.left, rect.bottom - rect.top, 1); 1333 } 1334 else 1335 MDI_PostUpdate(hwnd, ci, SB_BOTH+1); 1336 1337 break; 1338 1339 case WM_MDICALCCHILDSCROLL: 1340 if( (ci->mdiFlags & MDIF_NEEDUPDATE) && ci->sbRecalc ) 1341 { 1342 CalcChildScroll(hwnd, ci->sbRecalc-1); 1343 ci->sbRecalc = 0; 1344 ci->mdiFlags &= ~MDIF_NEEDUPDATE; 1345 } 1346 return 0; 1347 } 1348 return unicode ? DefWindowProcW( hwnd, message, wParam, lParam ) : 1349 DefWindowProcA( hwnd, message, wParam, lParam ); 1350 } 1351 1352 /*********************************************************************** 1353 * MDIClientWndProcA 1354 */ 1355 LRESULT WINAPI MDIClientWndProcA( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam ) 1356 { 1357 if (!IsWindow(hwnd)) return 0; 1358 return MDIClientWndProc_common( hwnd, message, wParam, lParam, FALSE ); 1359 } 1360 1361 /*********************************************************************** 1362 * MDIClientWndProcW 1363 */ 1364 LRESULT WINAPI MDIClientWndProcW( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam ) 1365 { 1366 if (!IsWindow(hwnd)) return 0; 1367 return MDIClientWndProc_common( hwnd, message, wParam, lParam, TRUE ); 1368 } 1369 1370 /*********************************************************************** 1371 * DefFrameProcA (USER32.@) 1372 */ 1373 LRESULT WINAPI DefFrameProcA( HWND hwnd, HWND hwndMDIClient, 1374 UINT message, WPARAM wParam, LPARAM lParam) 1375 { 1376 if (hwndMDIClient) 1377 { 1378 switch (message) 1379 { 1380 case WM_SETTEXT: 1381 { 1382 DWORD len = MultiByteToWideChar( CP_ACP, 0, (LPSTR)lParam, -1, NULL, 0 ); 1383 LPWSTR text = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) ); 1384 if (text == NULL) 1385 return 0; 1386 MultiByteToWideChar( CP_ACP, 0, (LPSTR)lParam, -1, text, len ); 1387 MDI_UpdateFrameText( hwnd, hwndMDIClient, FALSE, text ); 1388 HeapFree( GetProcessHeap(), 0, text ); 1389 } 1390 return 1; /* success. FIXME: check text length */ 1391 1392 case WM_COMMAND: 1393 case WM_NCACTIVATE: 1394 case WM_NEXTMENU: 1395 case WM_SETFOCUS: 1396 case WM_SIZE: 1397 return DefFrameProcW( hwnd, hwndMDIClient, message, wParam, lParam ); 1398 } 1399 } 1400 return DefWindowProcA(hwnd, message, wParam, lParam); 1401 } 1402 1403 1404 /*********************************************************************** 1405 * DefFrameProcW (USER32.@) 1406 */ 1407 LRESULT WINAPI DefFrameProcW( HWND hwnd, HWND hwndMDIClient, 1408 UINT message, WPARAM wParam, LPARAM lParam) 1409 { 1410 MDICLIENTINFO *ci = get_client_info( hwndMDIClient ); 1411 1412 TRACE("%p %p %04x (%s) %08lx %08lx\n", hwnd, hwndMDIClient, message, SPY_GetMsgName(message, hwnd), wParam, lParam); 1413 1414 if (ci) 1415 { 1416 switch (message) 1417 { 1418 case WM_COMMAND: 1419 { 1420 WORD id = LOWORD(wParam); 1421 /* check for possible syscommands for maximized MDI child */ 1422 if (id < ci->idFirstChild || id >= ci->idFirstChild + ci->nActiveChildren) 1423 { 1424 if( (id - 0xf000) & 0xf00f ) break; 1425 if( !ci->hwndChildMaximized ) break; 1426 switch( id ) 1427 { 1428 case SC_CLOSE: 1429 if (!is_close_enabled(ci->hwndActiveChild, 0)) break; 1430 case SC_SIZE: 1431 case SC_MOVE: 1432 case SC_MINIMIZE: 1433 case SC_MAXIMIZE: 1434 case SC_NEXTWINDOW: 1435 case SC_PREVWINDOW: 1436 case SC_RESTORE: 1437 return SendMessageW( ci->hwndChildMaximized, WM_SYSCOMMAND, 1438 wParam, lParam); 1439 } 1440 } 1441 else 1442 { 1443 HWND childHwnd; 1444 if (id - ci->idFirstChild == MDI_MOREWINDOWSLIMIT) 1445 /* User chose "More Windows..." */ 1446 childHwnd = MDI_MoreWindowsDialog(hwndMDIClient); 1447 else 1448 /* User chose one of the windows listed in the "Windows" menu */ 1449 childHwnd = MDI_GetChildByID(hwndMDIClient, id, ci); 1450 1451 if( childHwnd ) 1452 SendMessageW( hwndMDIClient, WM_MDIACTIVATE, (WPARAM)childHwnd, 0 ); 1453 } 1454 } 1455 break; 1456 1457 case WM_NCACTIVATE: 1458 SendMessageW(hwndMDIClient, message, wParam, lParam); 1459 break; 1460 1461 case WM_SETTEXT: 1462 MDI_UpdateFrameText( hwnd, hwndMDIClient, FALSE, (LPWSTR)lParam ); 1463 return 1; /* success. FIXME: check text length */ 1464 1465 case WM_SETFOCUS: 1466 SetFocus(hwndMDIClient); 1467 break; 1468 1469 case WM_SIZE: 1470 MoveWindow(hwndMDIClient, 0, 0, LOWORD(lParam), HIWORD(lParam), TRUE); 1471 break; 1472 1473 case WM_NEXTMENU: 1474 { 1475 MDINEXTMENU *next_menu = (MDINEXTMENU *)lParam; 1476 1477 if (!IsIconic(hwnd) && ci->hwndActiveChild && !IsZoomed(ci->hwndActiveChild)) 1478 { 1479 /* control menu is between the frame system menu and 1480 * the first entry of menu bar */ 1481 // WND *wndPtr = WIN_GetPtr(hwnd); 1482 1483 if( (wParam == VK_LEFT && GetMenu(hwnd) == next_menu->hmenuIn) || 1484 (wParam == VK_RIGHT && GetSubMenu(GetMenu(hwnd), 0) == next_menu->hmenuIn) ) 1485 { 1486 // WIN_ReleasePtr(wndPtr); 1487 // wndPtr = WIN_GetPtr(ci->hwndActiveChild); 1488 next_menu->hmenuNext = GetSubMenu(GetMenu(ci->hwndActiveChild), 0); 1489 next_menu->hwndNext = ci->hwndActiveChild; 1490 } 1491 // WIN_ReleasePtr(wndPtr); 1492 } 1493 return 0; 1494 } 1495 } 1496 } 1497 1498 return DefWindowProcW( hwnd, message, wParam, lParam ); 1499 } 1500 1501 /*********************************************************************** 1502 * DefMDIChildProcA (USER32.@) 1503 */ 1504 LRESULT WINAPI DefMDIChildProcA( HWND hwnd, UINT message, 1505 WPARAM wParam, LPARAM lParam ) 1506 { 1507 HWND client = GetParent(hwnd); 1508 MDICLIENTINFO *ci = get_client_info( client ); 1509 1510 TRACE("%p %04x (%s) %08lx %08lx\n", hwnd, message, SPY_GetMsgName(message, hwnd), wParam, lParam); 1511 1512 hwnd = WIN_GetFullHandle( hwnd ); 1513 if (!ci) return DefWindowProcA( hwnd, message, wParam, lParam ); 1514 1515 switch (message) 1516 { 1517 case WM_SETTEXT: 1518 DefWindowProcA(hwnd, message, wParam, lParam); 1519 if( ci->hwndChildMaximized == hwnd ) 1520 MDI_UpdateFrameText( GetParent(client), client, TRUE, NULL ); 1521 MDI_RefreshMenu( ci ); 1522 return 1; /* success. FIXME: check text length */ 1523 1524 case WM_GETMINMAXINFO: 1525 case WM_MENUCHAR: 1526 case WM_CLOSE: 1527 case WM_SETFOCUS: 1528 case WM_CHILDACTIVATE: 1529 case WM_SYSCOMMAND: 1530 case WM_SHOWWINDOW: 1531 case WM_SETVISIBLE: 1532 case WM_SIZE: 1533 case WM_NEXTMENU: 1534 case WM_SYSCHAR: 1535 case WM_DESTROY: 1536 return DefMDIChildProcW( hwnd, message, wParam, lParam ); 1537 } 1538 return DefWindowProcA(hwnd, message, wParam, lParam); 1539 } 1540 1541 1542 /*********************************************************************** 1543 * DefMDIChildProcW (USER32.@) 1544 */ 1545 LRESULT WINAPI DefMDIChildProcW( HWND hwnd, UINT message, 1546 WPARAM wParam, LPARAM lParam ) 1547 { 1548 HWND client = GetParent(hwnd); 1549 MDICLIENTINFO *ci = get_client_info( client ); 1550 1551 TRACE("%p %04x (%s) %08lx %08lx\n", hwnd, message, SPY_GetMsgName(message, hwnd), wParam, lParam); 1552 1553 hwnd = WIN_GetFullHandle( hwnd ); 1554 if (!ci) return DefWindowProcW( hwnd, message, wParam, lParam ); 1555 1556 switch (message) 1557 { 1558 case WM_SETTEXT: 1559 DefWindowProcW(hwnd, message, wParam, lParam); 1560 if( ci->hwndChildMaximized == hwnd ) 1561 MDI_UpdateFrameText( GetParent(client), client, TRUE, NULL ); 1562 MDI_RefreshMenu( ci ); 1563 return 1; /* success. FIXME: check text length */ 1564 1565 case WM_GETMINMAXINFO: 1566 MDI_ChildGetMinMaxInfo( client, hwnd, (MINMAXINFO *)lParam ); 1567 return 0; 1568 1569 case WM_MENUCHAR: 1570 return MAKELRESULT( 0, MNC_CLOSE ); /* MDI children don't have menu bars */ 1571 1572 case WM_CLOSE: 1573 SendMessageW( client, WM_MDIDESTROY, (WPARAM)hwnd, 0 ); 1574 return 0; 1575 1576 case WM_SETFOCUS: 1577 if (ci->hwndActiveChild != hwnd) 1578 MDI_ChildActivate( client, hwnd ); 1579 break; 1580 1581 case WM_CHILDACTIVATE: 1582 if (IsWindowEnabled( hwnd )) 1583 MDI_ChildActivate( client, hwnd ); 1584 return 0; 1585 1586 case WM_SYSCOMMAND: 1587 switch (wParam & 0xfff0) 1588 { 1589 case SC_MOVE: 1590 if( ci->hwndChildMaximized == hwnd ) 1591 return 0; 1592 break; 1593 case SC_RESTORE: 1594 case SC_MINIMIZE: 1595 break; 1596 case SC_MAXIMIZE: 1597 if (ci->hwndChildMaximized == hwnd) 1598 return SendMessageW( GetParent(client), message, wParam, lParam); 1599 break; 1600 case SC_NEXTWINDOW: 1601 SendMessageW( client, WM_MDINEXT, (WPARAM)ci->hwndActiveChild, 0); 1602 return 0; 1603 case SC_PREVWINDOW: 1604 SendMessageW( client, WM_MDINEXT, (WPARAM)ci->hwndActiveChild, 1); 1605 return 0; 1606 } 1607 break; 1608 1609 case WM_SHOWWINDOW: 1610 case WM_SETVISIBLE: 1611 //// Commented out r57663 1612 /*if (ci->hwndChildMaximized) ci->mdiFlags &= ~MDIF_NEEDUPDATE; 1613 else*/ MDI_PostUpdate(client, ci, SB_BOTH+1); 1614 break; 1615 1616 case WM_SIZE: 1617 /* This is the only place where we switch to/from maximized state */ 1618 /* do not change */ 1619 TRACE("current active %p, maximized %p\n", ci->hwndActiveChild, ci->hwndChildMaximized); 1620 1621 if( ci->hwndChildMaximized == hwnd && wParam != SIZE_MAXIMIZED ) 1622 { 1623 HWND frame; 1624 1625 ci->hwndChildMaximized = 0; 1626 1627 frame = GetParent(client); 1628 MDI_RestoreFrameMenu( frame, hwnd, ci->hBmpClose ); 1629 MDI_UpdateFrameText( frame, client, TRUE, NULL ); 1630 } 1631 1632 if( wParam == SIZE_MAXIMIZED ) 1633 { 1634 HWND frame, hMaxChild = ci->hwndChildMaximized; 1635 1636 if( hMaxChild == hwnd ) break; 1637 1638 if( hMaxChild) 1639 { 1640 SendMessageW( hMaxChild, WM_SETREDRAW, FALSE, 0 ); 1641 1642 MDI_RestoreFrameMenu( GetParent(client), hMaxChild, ci->hBmpClose ); 1643 ShowWindow( hMaxChild, SW_SHOWNOACTIVATE ); 1644 1645 SendMessageW( hMaxChild, WM_SETREDRAW, TRUE, 0 ); 1646 } 1647 1648 TRACE("maximizing child %p\n", hwnd ); 1649 1650 /* keep track of the maximized window. */ 1651 ci->hwndChildMaximized = hwnd; /* !!! */ 1652 1653 frame = GetParent(client); 1654 MDI_AugmentFrameMenu( frame, hwnd ); 1655 MDI_UpdateFrameText( frame, client, TRUE, NULL ); 1656 } 1657 1658 if( wParam == SIZE_MINIMIZED ) 1659 { 1660 HWND switchTo = MDI_GetWindow( ci, hwnd, TRUE, WS_MINIMIZE ); 1661 1662 if (!switchTo) switchTo = hwnd; 1663 SendMessageW( switchTo, WM_CHILDACTIVATE, 0, 0 ); 1664 } 1665 1666 MDI_PostUpdate(client, ci, SB_BOTH+1); 1667 break; 1668 1669 case WM_NEXTMENU: 1670 { 1671 MDINEXTMENU *next_menu = (MDINEXTMENU *)lParam; 1672 HWND parent = GetParent(client); 1673 1674 if( wParam == VK_LEFT ) /* switch to frame system menu */ 1675 { 1676 // WND *wndPtr = WIN_GetPtr( parent ); 1677 next_menu->hmenuNext = GetSubMenu( GetMenu(parent), 0 ); 1678 // WIN_ReleasePtr( wndPtr ); 1679 } 1680 if( wParam == VK_RIGHT ) /* to frame menu bar */ 1681 { 1682 next_menu->hmenuNext = GetMenu(parent); 1683 } 1684 next_menu->hwndNext = parent; 1685 return 0; 1686 } 1687 1688 case WM_SYSCHAR: 1689 if (wParam == '-') 1690 { 1691 SendMessageW( hwnd, WM_SYSCOMMAND, SC_KEYMENU, VK_SPACE); 1692 return 0; 1693 } 1694 break; 1695 1696 case WM_DESTROY: 1697 /* Remove itself from the Window menu */ 1698 MDI_RefreshMenu(ci); 1699 break; 1700 } 1701 return DefWindowProcW(hwnd, message, wParam, lParam); 1702 } 1703 1704 /********************************************************************** 1705 * CreateMDIWindowA (USER32.@) Creates a MDI child 1706 * 1707 * RETURNS 1708 * Success: Handle to created window 1709 * Failure: NULL 1710 */ 1711 HWND WINAPI CreateMDIWindowA( 1712 LPCSTR lpClassName, /* [in] Pointer to registered child class name */ 1713 LPCSTR lpWindowName, /* [in] Pointer to window name */ 1714 DWORD dwStyle, /* [in] Window style */ 1715 INT X, /* [in] Horizontal position of window */ 1716 INT Y, /* [in] Vertical position of window */ 1717 INT nWidth, /* [in] Width of window */ 1718 INT nHeight, /* [in] Height of window */ 1719 HWND hWndParent, /* [in] Handle to parent window */ 1720 HINSTANCE hInstance, /* [in] Handle to application instance */ 1721 LPARAM lParam) /* [in] Application-defined value */ 1722 { 1723 TRACE("(%s,%s,%08lx,%d,%d,%d,%d,%p,%p,%08lx)\n", 1724 debugstr_a(lpClassName),debugstr_a(lpWindowName),dwStyle,X,Y, 1725 nWidth,nHeight,hWndParent,hInstance,lParam); 1726 1727 return CreateWindowExA(WS_EX_MDICHILD, lpClassName, lpWindowName, 1728 dwStyle, X, Y, nWidth, nHeight, hWndParent, 1729 0, hInstance, (LPVOID)lParam); 1730 } 1731 1732 /*********************************************************************** 1733 * CreateMDIWindowW (USER32.@) Creates a MDI child 1734 * 1735 * RETURNS 1736 * Success: Handle to created window 1737 * Failure: NULL 1738 */ 1739 HWND WINAPI CreateMDIWindowW( 1740 LPCWSTR lpClassName, /* [in] Pointer to registered child class name */ 1741 LPCWSTR lpWindowName, /* [in] Pointer to window name */ 1742 DWORD dwStyle, /* [in] Window style */ 1743 INT X, /* [in] Horizontal position of window */ 1744 INT Y, /* [in] Vertical position of window */ 1745 INT nWidth, /* [in] Width of window */ 1746 INT nHeight, /* [in] Height of window */ 1747 HWND hWndParent, /* [in] Handle to parent window */ 1748 HINSTANCE hInstance, /* [in] Handle to application instance */ 1749 LPARAM lParam) /* [in] Application-defined value */ 1750 { 1751 TRACE("(%s,%s,%08lx,%d,%d,%d,%d,%p,%p,%08lx)\n", 1752 debugstr_w(lpClassName), debugstr_w(lpWindowName), dwStyle, X, Y, 1753 nWidth, nHeight, hWndParent, hInstance, lParam); 1754 1755 return CreateWindowExW(WS_EX_MDICHILD, lpClassName, lpWindowName, 1756 dwStyle, X, Y, nWidth, nHeight, hWndParent, 1757 0, hInstance, (LPVOID)lParam); 1758 } 1759 1760 /********************************************************************** 1761 * TranslateMDISysAccel (USER32.@) 1762 */ 1763 BOOL WINAPI TranslateMDISysAccel( HWND hwndClient, LPMSG msg ) 1764 { 1765 if (msg->message == WM_KEYDOWN || msg->message == WM_SYSKEYDOWN) 1766 { 1767 MDICLIENTINFO *ci = get_client_info( hwndClient ); 1768 WPARAM wParam = 0; 1769 1770 if (!ci || !IsWindowEnabled(ci->hwndActiveChild)) return 0; 1771 1772 /* translate if the Ctrl key is down and Alt not. */ 1773 1774 if( (GetKeyState(VK_CONTROL) & 0x8000) && !(GetKeyState(VK_MENU) & 0x8000)) 1775 { 1776 switch( msg->wParam ) 1777 { 1778 case VK_F6: 1779 case VK_TAB: 1780 wParam = ( GetKeyState(VK_SHIFT) & 0x8000 ) ? SC_NEXTWINDOW : SC_PREVWINDOW; 1781 break; 1782 case VK_F4: 1783 case VK_RBUTTON: 1784 if (is_close_enabled(ci->hwndActiveChild, 0)) 1785 { 1786 wParam = SC_CLOSE; 1787 break; 1788 } 1789 /* fall through */ 1790 default: 1791 return FALSE; 1792 } 1793 TRACE("wParam = %04lx\n", wParam); 1794 SendMessageW(ci->hwndActiveChild, WM_SYSCOMMAND, wParam, msg->wParam); 1795 return TRUE; 1796 } 1797 } 1798 return FALSE; /* failure */ 1799 } 1800 1801 /*********************************************************************** 1802 * CalcChildScroll (USER32.@) 1803 */ 1804 void WINAPI CalcChildScroll( HWND hwnd, INT scroll ) 1805 { 1806 SCROLLINFO info; 1807 RECT childRect, clientRect; 1808 HWND *list; 1809 DWORD style; 1810 WINDOWINFO WindowInfo; 1811 1812 GetClientRect( hwnd, &clientRect ); 1813 SetRectEmpty( &childRect ); 1814 1815 /* The rectangle returned by GetClientRect always has 0,0 as top left 1816 * because it is in client coordinates. The rectangles returned by 1817 * GetWindowRect are in screen coordinates to make this complicated. 1818 * 1819 * Apparently (in ReactOS at least) the rcClient returned by GetWindowInfo 1820 * is in screen coordinates too. 1821 */ 1822 WindowInfo.cbSize = sizeof(WindowInfo); 1823 if (!GetWindowInfo(hwnd, &WindowInfo)) 1824 { 1825 ERR("Can't get window info\n"); 1826 return; 1827 } 1828 1829 TRACE("CalcChildScroll 1\n"); 1830 if ((list = WIN_ListChildren( hwnd ))) 1831 { 1832 int i; 1833 for (i = 0; list[i]; i++) 1834 { 1835 style = GetWindowLongPtrW( list[i], GWL_STYLE ); 1836 if (style & WS_MAXIMIZE) 1837 { 1838 HeapFree( GetProcessHeap(), 0, list ); 1839 ShowScrollBar( hwnd, SB_BOTH, FALSE ); 1840 ERR("CalcChildScroll 2\n"); 1841 return; 1842 } 1843 if (style & WS_VISIBLE) 1844 { 1845 RECT rect; 1846 GetWindowRect( list[i], &rect ); 1847 OffsetRect(&rect, -WindowInfo.rcClient.left, 1848 -WindowInfo.rcClient.top); 1849 //WIN_GetRectangles( list[i], COORDS_PARENT, &rect, NULL ); 1850 TRACE("CalcChildScroll L\n"); 1851 UnionRect( &childRect, &rect, &childRect ); 1852 } 1853 } 1854 HeapFree( GetProcessHeap(), 0, list ); 1855 } 1856 UnionRect( &childRect, &clientRect, &childRect ); 1857 TRACE("CalcChildScroll 3\n"); 1858 /* set common info values */ 1859 info.cbSize = sizeof(info); 1860 info.fMask = SIF_POS | SIF_RANGE | SIF_PAGE; 1861 info.nPos = 0; 1862 1863 /* set the specific values and apply but only if window style allows */ 1864 /* Note how we set nPos to 0 because we scroll the clients instead of 1865 * the window, and we set nPage to 1 bigger than the clientRect because 1866 * otherwise the scrollbar never disables. This causes a somewhat ugly 1867 * effect though while scrolling. 1868 */ 1869 style = GetWindowLongW( hwnd, GWL_STYLE ); 1870 switch( scroll ) 1871 { 1872 case SB_BOTH: 1873 case SB_HORZ: 1874 if (style & (WS_HSCROLL | WS_VSCROLL)) 1875 { 1876 info.nMin = childRect.left; 1877 info.nMax = childRect.right; 1878 info.nPage = 1 + clientRect.right - clientRect.left; 1879 //info.nMax = childRect.right - clientRect.right; 1880 //info.nPos = clientRect.left - childRect.left; 1881 SetScrollInfo(hwnd, SB_HORZ, &info, TRUE); 1882 } 1883 if (scroll == SB_HORZ) break; 1884 /* fall through */ 1885 case SB_VERT: 1886 if (style & (WS_HSCROLL | WS_VSCROLL)) 1887 { 1888 info.nMin = childRect.top; 1889 info.nMax = childRect.bottom; 1890 info.nPage = 1 + clientRect.bottom - clientRect.top; 1891 //info.nMax = childRect.bottom - clientRect.bottom; 1892 //info.nPos = clientRect.top - childRect.top; 1893 SetScrollInfo(hwnd, SB_VERT, &info, TRUE); 1894 } 1895 break; 1896 } 1897 } 1898 1899 1900 /*********************************************************************** 1901 * ScrollChildren (USER32.@) 1902 */ 1903 void WINAPI ScrollChildren(HWND hWnd, UINT uMsg, WPARAM wParam, 1904 LPARAM lParam) 1905 { 1906 INT newPos = -1; 1907 INT curPos, length, minPos, maxPos, shift; 1908 RECT rect; 1909 1910 GetClientRect( hWnd, &rect ); 1911 1912 switch(uMsg) 1913 { 1914 case WM_HSCROLL: 1915 GetScrollRange(hWnd,SB_HORZ,&minPos,&maxPos); 1916 curPos = GetScrollPos(hWnd,SB_HORZ); 1917 length = (rect.right - rect.left) / 2; 1918 shift = GetSystemMetrics(SM_CYHSCROLL); 1919 break; 1920 case WM_VSCROLL: 1921 GetScrollRange(hWnd,SB_VERT,&minPos,&maxPos); 1922 curPos = GetScrollPos(hWnd,SB_VERT); 1923 length = (rect.bottom - rect.top) / 2; 1924 shift = GetSystemMetrics(SM_CXVSCROLL); 1925 break; 1926 default: 1927 return; 1928 } 1929 1930 switch( wParam ) 1931 { 1932 case SB_LINEUP: 1933 newPos = curPos - shift; 1934 break; 1935 case SB_LINEDOWN: 1936 newPos = curPos + shift; 1937 break; 1938 case SB_PAGEUP: 1939 newPos = curPos - length; 1940 break; 1941 case SB_PAGEDOWN: 1942 newPos = curPos + length; 1943 break; 1944 1945 case SB_THUMBPOSITION: 1946 newPos = LOWORD(lParam); 1947 break; 1948 1949 case SB_THUMBTRACK: 1950 return; 1951 1952 case SB_TOP: 1953 newPos = minPos; 1954 break; 1955 case SB_BOTTOM: 1956 newPos = maxPos; 1957 break; 1958 case SB_ENDSCROLL: 1959 CalcChildScroll(hWnd,(uMsg == WM_VSCROLL)?SB_VERT:SB_HORZ); 1960 return; 1961 } 1962 1963 if( newPos > maxPos ) 1964 newPos = maxPos; 1965 else 1966 if( newPos < minPos ) 1967 newPos = minPos; 1968 1969 SetScrollPos(hWnd, (uMsg == WM_VSCROLL)?SB_VERT:SB_HORZ , newPos, TRUE); 1970 1971 if( uMsg == WM_VSCROLL ) 1972 ScrollWindowEx(hWnd ,0 ,curPos - newPos, NULL, NULL, 0, NULL, 1973 SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN ); 1974 else 1975 ScrollWindowEx(hWnd ,curPos - newPos, 0, NULL, NULL, 0, NULL, 1976 SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN ); 1977 } 1978 1979 /****************************************************************************** 1980 * CascadeWindows (USER32.@) Cascades MDI child windows 1981 * 1982 * RETURNS 1983 * Success: Number of cascaded windows. 1984 * Failure: 0 1985 */ 1986 1987 typedef struct CASCADE_INFO 1988 { 1989 HWND hwndTop; 1990 UINT wFlags; 1991 HWND hwndParent; 1992 HWND hwndDesktop; 1993 HWND hTrayWnd; 1994 HWND hwndProgman; 1995 HWND *ahwnd; 1996 DWORD chwnd; 1997 } CASCADE_INFO; 1998 1999 static BOOL CALLBACK 2000 GetCascadeChildProc(HWND hwnd, LPARAM lParam) 2001 { 2002 DWORD count, size; 2003 HWND *ahwnd; 2004 CASCADE_INFO *pInfo = (CASCADE_INFO *)lParam; 2005 2006 if (hwnd == pInfo->hwndDesktop || hwnd == pInfo->hTrayWnd || 2007 hwnd == pInfo->hwndProgman || hwnd == pInfo->hwndTop) 2008 { 2009 return TRUE; 2010 } 2011 2012 if (pInfo->hwndParent && GetParent(hwnd) != pInfo->hwndParent) 2013 return TRUE; 2014 2015 if ((pInfo->wFlags & MDITILE_SKIPDISABLED) && !IsWindowEnabled(hwnd)) 2016 return TRUE; 2017 2018 if (!IsWindowVisible(hwnd) || IsIconic(hwnd)) 2019 return TRUE; 2020 2021 count = pInfo->chwnd; 2022 size = (count + 1) * sizeof(HWND); 2023 2024 if (count == 0 || pInfo->ahwnd == NULL) 2025 { 2026 count = 0; 2027 pInfo->ahwnd = (HWND *)HeapAlloc(GetProcessHeap(), 0, size); 2028 } 2029 else 2030 { 2031 ahwnd = (HWND *)HeapReAlloc(GetProcessHeap(), 0, pInfo->ahwnd, size); 2032 if (ahwnd == NULL) 2033 { 2034 HeapFree(GetProcessHeap(), 0, pInfo->ahwnd); 2035 } 2036 pInfo->ahwnd = ahwnd; 2037 } 2038 2039 if (pInfo->ahwnd == NULL) 2040 { 2041 pInfo->chwnd = 0; 2042 return FALSE; 2043 } 2044 2045 pInfo->ahwnd[count] = hwnd; 2046 pInfo->chwnd = count + 1; 2047 return TRUE; 2048 } 2049 2050 static BOOL 2051 QuerySizeFix(HWND hwnd, LPINT pcx, LPINT pcy) 2052 { 2053 MINMAXINFO mmi; 2054 DWORD_PTR dwResult; 2055 2056 mmi.ptMinTrackSize.x = mmi.ptMinTrackSize.y = 0; 2057 mmi.ptMaxTrackSize.x = mmi.ptMaxTrackSize.y = MAXLONG; 2058 if (SendMessageTimeoutW(hwnd, WM_GETMINMAXINFO, 0, (LPARAM)&mmi, 2059 SMTO_ABORTIFHUNG | SMTO_NORMAL, 120, &dwResult)) 2060 { 2061 *pcx = min(max(*pcx, mmi.ptMinTrackSize.x), mmi.ptMaxTrackSize.x); 2062 *pcy = min(max(*pcy, mmi.ptMinTrackSize.y), mmi.ptMaxTrackSize.y); 2063 return TRUE; 2064 } 2065 return FALSE; 2066 } 2067 2068 WORD WINAPI 2069 CascadeWindows(HWND hwndParent, UINT wFlags, LPCRECT lpRect, 2070 UINT cKids, const HWND *lpKids) 2071 { 2072 CASCADE_INFO info; 2073 HWND hwnd, hwndTop, hwndPrev; 2074 HMONITOR hMon; 2075 MONITORINFO mi; 2076 RECT rcWork, rcWnd; 2077 DWORD i, ret = 0; 2078 INT x, y, cx, cy, cxNew, cyNew, cxWork, cyWork, dx, dy; 2079 HDWP hDWP; 2080 POINT pt; 2081 2082 TRACE("(%p,0x%08x,...,%u,...)\n", hwndParent, wFlags, cKids); 2083 2084 hwndTop = GetTopWindow(hwndParent); 2085 2086 ZeroMemory(&info, sizeof(info)); 2087 info.hwndDesktop = GetDesktopWindow(); 2088 info.hTrayWnd = FindWindowW(L"Shell_TrayWnd", NULL); 2089 info.hwndProgman = FindWindowW(L"Progman", NULL); 2090 info.hwndParent = hwndParent; 2091 info.wFlags = wFlags; 2092 2093 if (cKids == 0 || lpKids == NULL) 2094 { 2095 info.hwndTop = hwndTop; 2096 EnumChildWindows(hwndParent, GetCascadeChildProc, (LPARAM)&info); 2097 2098 info.hwndTop = NULL; 2099 GetCascadeChildProc(hwndTop, (LPARAM)&info); 2100 } 2101 else 2102 { 2103 info.chwnd = cKids; 2104 info.ahwnd = (HWND *)lpKids; 2105 } 2106 2107 if (info.chwnd == 0 || info.ahwnd == NULL) 2108 return ret; 2109 2110 if (lpRect) 2111 { 2112 rcWork = *lpRect; 2113 } 2114 else if (hwndParent) 2115 { 2116 GetClientRect(hwndParent, &rcWork); 2117 } 2118 else 2119 { 2120 pt.x = pt.y = 0; 2121 hMon = MonitorFromPoint(pt, MONITOR_DEFAULTTOPRIMARY); 2122 mi.cbSize = sizeof(mi); 2123 GetMonitorInfoW(hMon, &mi); 2124 rcWork = mi.rcWork; 2125 } 2126 2127 hDWP = BeginDeferWindowPos(info.chwnd); 2128 if (hDWP == NULL) 2129 goto cleanup; 2130 2131 x = rcWork.left; 2132 y = rcWork.top; 2133 dx = GetSystemMetrics(SM_CXSIZEFRAME) + GetSystemMetrics(SM_CXSIZE); 2134 dy = GetSystemMetrics(SM_CYSIZEFRAME) + GetSystemMetrics(SM_CYSIZE); 2135 cxWork = rcWork.right - rcWork.left; 2136 cyWork = rcWork.bottom - rcWork.top; 2137 hwndPrev = NULL; 2138 for (i = info.chwnd; i > 0;) /* in reverse order */ 2139 { 2140 --i; 2141 hwnd = info.ahwnd[i]; 2142 2143 if (!IsWindowVisible(hwnd) || IsIconic(hwnd)) 2144 continue; 2145 2146 if ((info.wFlags & MDITILE_SKIPDISABLED) && !IsWindowEnabled(hwnd)) 2147 continue; 2148 2149 if (IsZoomed(hwnd)) 2150 ShowWindow(hwnd, SW_RESTORE | SW_SHOWNA); 2151 2152 GetWindowRect(hwnd, &rcWnd); 2153 cxNew = cx = rcWnd.right - rcWnd.left; 2154 cyNew = cy = rcWnd.bottom - rcWnd.top; 2155 2156 /* if we can change the window size and it is not only one */ 2157 if (info.chwnd != 1 && (GetWindowLongPtrW(hwnd, GWL_STYLE) & WS_THICKFRAME)) 2158 { 2159 /* check the size */ 2160 #define THRESHOLD(xy) (((xy) * 5) / 7) /* in the rate 5/7 */ 2161 cxNew = min(cxNew, THRESHOLD(cxWork)); 2162 cyNew = min(cyNew, THRESHOLD(cyWork)); 2163 #undef THRESHOLD 2164 if (cx != cxNew || cy != cyNew) 2165 { 2166 /* too large. shrink if we can */ 2167 if (QuerySizeFix(hwnd, &cxNew, &cyNew)) 2168 { 2169 cx = cxNew; 2170 cy = cyNew; 2171 } 2172 } 2173 } 2174 2175 if (x + cx > rcWork.right) 2176 x = rcWork.left; 2177 if (y + cy > rcWork.bottom) 2178 y = rcWork.top; 2179 2180 hDWP = DeferWindowPos(hDWP, hwnd, HWND_TOP, x, y, cx, cy, SWP_NOACTIVATE); 2181 if (hDWP == NULL) 2182 { 2183 ret = 0; 2184 goto cleanup; 2185 } 2186 2187 x += dx; 2188 y += dy; 2189 hwndPrev = hwnd; 2190 ++ret; 2191 } 2192 2193 EndDeferWindowPos(hDWP); 2194 2195 if (hwndPrev) 2196 SetForegroundWindow(hwndPrev); 2197 2198 cleanup: 2199 if (cKids == 0 || lpKids == NULL) 2200 HeapFree(GetProcessHeap(), 0, info.ahwnd); 2201 2202 return (WORD)ret; 2203 } 2204 2205 2206 /*********************************************************************** 2207 * CascadeChildWindows (USER32.@) 2208 */ 2209 WORD WINAPI CascadeChildWindows( HWND parent, UINT flags ) 2210 { 2211 return CascadeWindows( parent, flags, NULL, 0, NULL ); 2212 } 2213 2214 2215 /****************************************************************************** 2216 * TileWindows (USER32.@) Tiles MDI child windows 2217 * 2218 * RETURNS 2219 * Success: Number of tiled windows. 2220 * Failure: 0 2221 */ 2222 WORD WINAPI 2223 TileWindows(HWND hwndParent, UINT wFlags, LPCRECT lpRect, 2224 UINT cKids, const HWND *lpKids) 2225 { 2226 HWND hwnd, hwndTop, hwndPrev; 2227 CASCADE_INFO info; 2228 RECT rcWork, rcWnd; 2229 DWORD i, iRow, iColumn, cRows, cColumns, ret = 0; 2230 INT x, y, cx, cy, cxNew, cyNew, cxWork, cyWork, cxCell, cyCell, cxMin2, cyMin3; 2231 HDWP hDWP; 2232 MONITORINFO mi; 2233 HMONITOR hMon; 2234 POINT pt; 2235 2236 TRACE("(%p,0x%08x,...,%u,...)\n", hwndParent, wFlags, cKids); 2237 2238 hwndTop = GetTopWindow(hwndParent); 2239 2240 ZeroMemory(&info, sizeof(info)); 2241 info.hwndDesktop = GetDesktopWindow(); 2242 info.hTrayWnd = FindWindowW(L"Shell_TrayWnd", NULL); 2243 info.hwndProgman = FindWindowW(L"Progman", NULL); 2244 info.hwndParent = hwndParent; 2245 info.wFlags = wFlags; 2246 2247 if (cKids == 0 || lpKids == NULL) 2248 { 2249 info.hwndTop = hwndTop; 2250 EnumChildWindows(hwndParent, GetCascadeChildProc, (LPARAM)&info); 2251 2252 info.hwndTop = NULL; 2253 GetCascadeChildProc(hwndTop, (LPARAM)&info); 2254 } 2255 else 2256 { 2257 info.chwnd = cKids; 2258 info.ahwnd = (HWND *)lpKids; 2259 } 2260 2261 if (info.chwnd == 0 || info.ahwnd == NULL) 2262 return ret; 2263 2264 if (lpRect) 2265 { 2266 rcWork = *lpRect; 2267 } 2268 else if (hwndParent) 2269 { 2270 GetClientRect(hwndParent, &rcWork); 2271 } 2272 else 2273 { 2274 pt.x = pt.y = 0; 2275 hMon = MonitorFromPoint(pt, MONITOR_DEFAULTTOPRIMARY); 2276 mi.cbSize = sizeof(mi); 2277 GetMonitorInfoW(hMon, &mi); 2278 rcWork = mi.rcWork; 2279 } 2280 2281 cxWork = rcWork.right - rcWork.left; 2282 cyWork = rcWork.bottom - rcWork.top; 2283 2284 cxMin2 = GetSystemMetrics(SM_CXMIN) * 2; 2285 cyMin3 = GetSystemMetrics(SM_CYMIN) * 3; 2286 2287 /* calculate the numbers and widths of columns and rows */ 2288 if (info.wFlags & MDITILE_HORIZONTAL) 2289 { 2290 cColumns = info.chwnd; 2291 cRows = 1; 2292 for (;;) 2293 { 2294 cxCell = cxWork / cColumns; 2295 cyCell = cyWork / cRows; 2296 if (cyCell <= cyMin3 || cxCell >= cxMin2) 2297 break; 2298 2299 ++cRows; 2300 cColumns = (info.chwnd + cRows - 1) / cRows; 2301 } 2302 } 2303 else 2304 { 2305 cRows = info.chwnd; 2306 cColumns = 1; 2307 for (;;) 2308 { 2309 cxCell = cxWork / cColumns; 2310 cyCell = cyWork / cRows; 2311 if (cxCell <= cxMin2 || cyCell >= cyMin3) 2312 break; 2313 2314 ++cColumns; 2315 cRows = (info.chwnd + cColumns - 1) / cColumns; 2316 } 2317 } 2318 2319 hDWP = BeginDeferWindowPos(info.chwnd); 2320 if (hDWP == NULL) 2321 goto cleanup; 2322 2323 x = rcWork.left; 2324 y = rcWork.top; 2325 hwndPrev = NULL; 2326 iRow = iColumn = 0; 2327 for (i = info.chwnd; i > 0;) /* in reverse order */ 2328 { 2329 --i; 2330 hwnd = info.ahwnd[i]; 2331 2332 if (IsZoomed(hwnd)) 2333 ShowWindow(hwnd, SW_RESTORE | SW_SHOWNA); 2334 2335 GetWindowRect(hwnd, &rcWnd); 2336 cx = rcWnd.right - rcWnd.left; 2337 cy = rcWnd.bottom - rcWnd.top; 2338 2339 /* if we can change the window size */ 2340 if (GetWindowLongPtrW(hwnd, GWL_STYLE) & WS_THICKFRAME) 2341 { 2342 cxNew = cxCell; 2343 cyNew = cyCell; 2344 /* shrink if we can */ 2345 if (QuerySizeFix(hwnd, &cxNew, &cyNew)) 2346 { 2347 cx = cxNew; 2348 cy = cyNew; 2349 } 2350 } 2351 2352 hDWP = DeferWindowPos(hDWP, hwnd, HWND_TOP, x, y, cx, cy, SWP_NOACTIVATE); 2353 if (hDWP == NULL) 2354 { 2355 ret = 0; 2356 goto cleanup; 2357 } 2358 2359 if (info.wFlags & MDITILE_HORIZONTAL) 2360 { 2361 x += cxCell; 2362 ++iColumn; 2363 if (iColumn >= cColumns) 2364 { 2365 iColumn = 0; 2366 ++iRow; 2367 x = rcWork.left; 2368 y += cyCell; 2369 } 2370 } 2371 else 2372 { 2373 y += cyCell; 2374 ++iRow; 2375 if (iRow >= cRows) 2376 { 2377 iRow = 0; 2378 ++iColumn; 2379 x += cxCell; 2380 y = rcWork.top; 2381 } 2382 } 2383 hwndPrev = hwnd; 2384 ++ret; 2385 } 2386 2387 EndDeferWindowPos(hDWP); 2388 2389 if (hwndPrev) 2390 SetForegroundWindow(hwndPrev); 2391 2392 cleanup: 2393 if (cKids == 0 || lpKids == NULL) 2394 HeapFree(GetProcessHeap(), 0, info.ahwnd); 2395 2396 return (WORD)ret; 2397 } 2398 2399 2400 /*********************************************************************** 2401 * TileChildWindows (USER32.@) 2402 */ 2403 WORD WINAPI TileChildWindows( HWND parent, UINT flags ) 2404 { 2405 return TileWindows( parent, flags, NULL, 0, NULL ); 2406 } 2407 2408 2409 /************************************************************************ 2410 * "More Windows..." functionality 2411 */ 2412 2413 /* MDI_MoreWindowsDlgProc 2414 * 2415 * This function will process the messages sent to the "More Windows..." 2416 * dialog. 2417 * Return values: 0 = cancel pressed 2418 * HWND = ok pressed or double-click in the list... 2419 * 2420 */ 2421 2422 static INT_PTR WINAPI MDI_MoreWindowsDlgProc (HWND hDlg, UINT iMsg, WPARAM wParam, LPARAM lParam) 2423 { 2424 switch (iMsg) 2425 { 2426 case WM_INITDIALOG: 2427 { 2428 UINT widest = 0; 2429 UINT length; 2430 UINT i; 2431 MDICLIENTINFO *ci = get_client_info( (HWND)lParam ); 2432 HWND hListBox = GetDlgItem(hDlg, MDI_IDC_LISTBOX); 2433 2434 for (i = 0; i < ci->nActiveChildren; i++) 2435 { 2436 WCHAR buffer[MDI_MAXTITLELENGTH]; 2437 2438 if (!InternalGetWindowText( ci->child[i], buffer, sizeof(buffer)/sizeof(WCHAR) )) 2439 continue; 2440 SendMessageW(hListBox, LB_ADDSTRING, 0, (LPARAM)buffer ); 2441 SendMessageW(hListBox, LB_SETITEMDATA, i, (LPARAM)ci->child[i] ); 2442 length = strlenW(buffer); /* FIXME: should use GetTextExtentPoint */ 2443 if (length > widest) 2444 widest = length; 2445 } 2446 /* Make sure the horizontal scrollbar scrolls ok */ 2447 SendMessageW(hListBox, LB_SETHORIZONTALEXTENT, widest * 6, 0); 2448 2449 /* Set the current selection */ 2450 SendMessageW(hListBox, LB_SETCURSEL, MDI_MOREWINDOWSLIMIT, 0); 2451 return TRUE; 2452 } 2453 2454 case WM_COMMAND: 2455 switch (LOWORD(wParam)) 2456 { 2457 default: 2458 if (HIWORD(wParam) != LBN_DBLCLK) break; 2459 /* fall through */ 2460 case IDOK: 2461 { 2462 /* windows are sorted by menu ID, so we must return the 2463 * window associated to the given id 2464 */ 2465 HWND hListBox = GetDlgItem(hDlg, MDI_IDC_LISTBOX); 2466 UINT index = SendMessageW(hListBox, LB_GETCURSEL, 0, 0); 2467 LRESULT res = SendMessageW(hListBox, LB_GETITEMDATA, index, 0); 2468 EndDialog(hDlg, res); 2469 return TRUE; 2470 } 2471 case IDCANCEL: 2472 EndDialog(hDlg, 0); 2473 return TRUE; 2474 } 2475 break; 2476 } 2477 return FALSE; 2478 } 2479 2480 /* 2481 * 2482 * MDI_MoreWindowsDialog 2483 * 2484 * Prompts the user with a listbox containing the opened 2485 * documents. The user can then choose a windows and click 2486 * on OK to set the current window to the one selected, or 2487 * CANCEL to cancel. The function returns a handle to the 2488 * selected window. 2489 */ 2490 2491 static HWND MDI_MoreWindowsDialog(HWND hwnd) 2492 { 2493 LPCVOID template; 2494 HRSRC hRes; 2495 HANDLE hDlgTmpl; 2496 2497 hRes = FindResourceA(User32Instance, "MDI_MOREWINDOWS", (LPSTR)RT_DIALOG); 2498 2499 if (hRes == 0) 2500 return 0; 2501 2502 hDlgTmpl = LoadResource(User32Instance, hRes ); 2503 2504 if (hDlgTmpl == 0) 2505 return 0; 2506 2507 template = LockResource( hDlgTmpl ); 2508 2509 if (template == 0) 2510 return 0; 2511 2512 return (HWND) DialogBoxIndirectParamA(User32Instance, template, hwnd, 2513 MDI_MoreWindowsDlgProc, (LPARAM) hwnd); 2514 } 2515