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