1 /* 2 * COPYRIGHT: See COPYING in the top level directory 3 * PROJECT: ReactOS system libraries 4 * FILE: win32ss/user/user32/controls/appswitch.c 5 * PURPOSE: app switching functionality 6 * PROGRAMMERS: Johannes Anderwald (johannes.anderwald@reactos.org) 7 * David Quintana (gigaherz@gmail.com) 8 * Katayama Hirofumi MZ (katayama.hirofumi.mz@gmail.com) 9 */ 10 11 // 12 // TODO: 13 // Move to Win32k. 14 // Add registry support. 15 // 16 // 17 18 #include <user32.h> 19 20 WINE_DEFAULT_DEBUG_CHANNEL(user32); 21 22 #define DIALOG_MARGIN 8 // margin of dialog contents 23 24 #define CX_ICON 32 // width of icon 25 #define CY_ICON 32 // height of icon 26 #define ICON_MARGIN 4 // margin width around an icon 27 28 #define CX_ITEM (CX_ICON + 2 * ICON_MARGIN) 29 #define CY_ITEM (CY_ICON + 2 * ICON_MARGIN) 30 #define ITEM_MARGIN 4 // margin width around an item 31 32 #define CX_ITEM_SPACE (CX_ITEM + 2 * ITEM_MARGIN) 33 #define CY_ITEM_SPACE (CY_ITEM + 2 * ITEM_MARGIN) 34 35 #define CY_TEXT_MARGIN 4 // margin height around text 36 37 // limit the number of windows shown in the alt-tab window 38 // 120 windows results in (12*40) by (10*40) pixels worth of icons. 39 #define MAX_WINDOWS 120 40 41 // Global variables 42 HWND switchdialog = NULL; 43 HFONT dialogFont; 44 int selectedWindow = 0; 45 BOOL isOpen = FALSE; 46 47 int fontHeight=0; 48 49 WCHAR windowText[1024]; 50 51 HWND windowList[MAX_WINDOWS]; 52 HICON iconList[MAX_WINDOWS]; 53 int windowCount = 0; 54 55 int cxBorder, cyBorder; 56 int nItems, nCols, nRows; 57 int itemsW, itemsH; 58 int totalW, totalH; 59 int xOffset, yOffset; 60 POINT ptStart; 61 62 int nShift = 0; 63 64 BOOL Esc = FALSE; 65 66 BOOL CoolSwitch = TRUE; 67 int CoolSwitchRows = 3; 68 int CoolSwitchColumns = 7; 69 70 // window style 71 const DWORD Style = WS_POPUP | WS_BORDER | WS_DISABLED; 72 const DWORD ExStyle = WS_EX_TOPMOST | WS_EX_DLGMODALFRAME | WS_EX_TOOLWINDOW; 73 74 DWORD wtodw(const WCHAR *psz) 75 { 76 const WCHAR *pch = psz; 77 DWORD Value = 0; 78 while ('0' <= *pch && *pch <= '9') 79 { 80 Value *= 10; 81 Value += *pch - L'0'; 82 } 83 return Value; 84 } 85 86 BOOL LoadCoolSwitchSettings(void) 87 { 88 CoolSwitch = TRUE; 89 CoolSwitchRows = 3; 90 CoolSwitchColumns = 7; 91 92 // FIXME: load the settings from registry 93 94 TRACE("CoolSwitch: %d\n", CoolSwitch); 95 TRACE("CoolSwitchRows: %d\n", CoolSwitchRows); 96 TRACE("CoolSwitchColumns: %d\n", CoolSwitchColumns); 97 98 return TRUE; 99 } 100 101 void ResizeAndCenter(HWND hwnd, int width, int height) 102 { 103 int x, y; 104 RECT Rect; 105 106 int screenwidth = GetSystemMetrics(SM_CXSCREEN); 107 int screenheight = GetSystemMetrics(SM_CYSCREEN); 108 109 x = (screenwidth - width) / 2; 110 y = (screenheight - height) / 2; 111 112 SetRect(&Rect, x, y, x + width, y + height); 113 AdjustWindowRectEx(&Rect, Style, FALSE, ExStyle); 114 115 x = Rect.left; 116 y = Rect.top; 117 width = Rect.right - Rect.left; 118 height = Rect.bottom - Rect.top; 119 MoveWindow(hwnd, x, y, width, height, FALSE); 120 121 ptStart.x = x; 122 ptStart.y = y; 123 } 124 125 void MakeWindowActive(HWND hwnd) 126 { 127 if (IsIconic(hwnd)) 128 PostMessageW(hwnd, WM_SYSCOMMAND, SC_RESTORE, 0); 129 130 BringWindowToTop(hwnd); // same as: SetWindowPos(hwnd,HWND_TOP,0,0,0,0,SWP_NOMOVE|SWP_NOSIZE); ? 131 SetForegroundWindow(hwnd); 132 } 133 134 void CompleteSwitch(BOOL doSwitch) 135 { 136 if (!isOpen) 137 return; 138 139 isOpen = FALSE; 140 141 TRACE("[ATbot] CompleteSwitch Hiding Window.\n"); 142 ShowWindow(switchdialog, SW_HIDE); 143 144 if(doSwitch) 145 { 146 if(selectedWindow >= windowCount) 147 return; 148 149 // FIXME: workaround because reactos fails to activate the previous window correctly. 150 //if(selectedWindow != 0) 151 { 152 HWND hwnd = windowList[selectedWindow]; 153 154 GetWindowTextW(hwnd, windowText, _countof(windowText)); 155 156 TRACE("[ATbot] CompleteSwitch Switching to 0x%08x (%ls)\n", hwnd, windowText); 157 158 MakeWindowActive(hwnd); 159 } 160 } 161 162 windowCount = 0; 163 } 164 165 BOOL CALLBACK EnumerateCallback(HWND window, LPARAM lParam) 166 { 167 HICON hIcon; 168 169 UNREFERENCED_PARAMETER(lParam); 170 171 // First try to get the big icon assigned to the window 172 hIcon = (HICON)SendMessageW(window, WM_GETICON, ICON_BIG, 0); 173 if (!hIcon) 174 { 175 // If no icon is assigned, try to get the icon assigned to the windows' class 176 hIcon = (HICON)GetClassLongPtrW(window, GCL_HICON); 177 if (!hIcon) 178 { 179 // If we still don't have an icon, see if we can do with the small icon, 180 // or a default application icon 181 hIcon = (HICON)SendMessageW(window, WM_GETICON, ICON_SMALL2, 0); 182 if (!hIcon) 183 { 184 // using windows logo icon as default 185 hIcon = gpsi->hIconWindows; 186 if (!hIcon) 187 { 188 //if all attempts to get icon fails go to the next window 189 return TRUE; 190 } 191 } 192 } 193 } 194 195 windowList[windowCount] = window; 196 iconList[windowCount] = CopyIcon(hIcon); 197 198 windowCount++; 199 200 // If we got to the max number of windows, 201 // we won't be able to add any more 202 if(windowCount >= MAX_WINDOWS) 203 return FALSE; 204 205 return TRUE; 206 } 207 208 static HWND GetNiceRootOwner(HWND hwnd) 209 { 210 HWND hwndOwner; 211 DWORD ExStyle, OwnerExStyle; 212 213 for (;;) 214 { 215 // A window with WS_EX_APPWINDOW is treated as if it has no owner 216 ExStyle = GetWindowLong(hwnd, GWL_EXSTYLE); 217 if (ExStyle & WS_EX_APPWINDOW) 218 break; 219 220 // Is the owner visible? 221 // An window with WS_EX_TOOLWINDOW is treated as if it weren't visible 222 hwndOwner = GetWindow(hwnd, GW_OWNER); 223 OwnerExStyle = GetWindowLong(hwndOwner, GWL_EXSTYLE); 224 if (!IsWindowVisible(hwndOwner) || (OwnerExStyle & WS_EX_TOOLWINDOW)) 225 break; 226 227 hwnd = hwndOwner; 228 } 229 230 return hwnd; 231 } 232 233 // c.f. http://blogs.msdn.com/b/oldnewthing/archive/2007/10/08/5351207.aspx 234 BOOL IsAltTabWindow(HWND hwnd) 235 { 236 DWORD ExStyle; 237 RECT rc; 238 HWND hwndTry, hwndWalk; 239 WCHAR szClass[64]; 240 241 // must be visible 242 if (!IsWindowVisible(hwnd)) 243 return FALSE; 244 245 // must not be WS_EX_TOOLWINDOW 246 ExStyle = GetWindowLong(hwnd, GWL_EXSTYLE); 247 if (ExStyle & WS_EX_TOOLWINDOW) 248 return FALSE; 249 250 // must be not empty rect 251 GetWindowRect(hwnd, &rc); 252 if (IsRectEmpty(&rc)) 253 return FALSE; 254 255 // check special windows 256 if (!GetClassNameW(hwnd, szClass, _countof(szClass)) || 257 wcscmp(szClass, L"Shell_TrayWnd") == 0 || 258 wcscmp(szClass, L"Progman") == 0) 259 { 260 return TRUE; 261 } 262 263 // get 'nice' root owner 264 hwndWalk = GetNiceRootOwner(hwnd); 265 266 // walk back from hwndWalk toward hwnd 267 for (;;) 268 { 269 hwndTry = GetLastActivePopup(hwndWalk); 270 if (hwndTry == hwndWalk) 271 break; 272 273 ExStyle = GetWindowLong(hwndTry, GWL_EXSTYLE); 274 if (IsWindowVisible(hwndTry) && !(ExStyle & WS_EX_TOOLWINDOW)) 275 break; 276 277 hwndWalk = hwndTry; 278 } 279 280 return hwnd == hwndTry; // Reached? 281 } 282 283 static BOOL CALLBACK 284 EnumWindowsProc(HWND hwnd, LPARAM lParam) 285 { 286 if (IsAltTabWindow(hwnd)) 287 { 288 if (!EnumerateCallback(hwnd, lParam)) 289 return FALSE; 290 } 291 return TRUE; 292 } 293 294 void ProcessMouseMessage(UINT message, LPARAM lParam) 295 { 296 int xPos = LOWORD(lParam); 297 int yPos = HIWORD(lParam); 298 299 int xIndex = (xPos - DIALOG_MARGIN) / CX_ITEM_SPACE; 300 int yIndex = (yPos - DIALOG_MARGIN) / CY_ITEM_SPACE; 301 302 if (xIndex < 0 || nCols <= xIndex || 303 yIndex < 0 || nRows <= yIndex) 304 { 305 return; 306 } 307 308 selectedWindow = (yIndex*nCols) + xIndex; 309 if (message == WM_MOUSEMOVE) 310 { 311 InvalidateRect(switchdialog, NULL, TRUE); 312 //RedrawWindow(switchdialog, NULL, NULL, 0); 313 } 314 else 315 { 316 selectedWindow = (yIndex*nCols) + xIndex; 317 CompleteSwitch(TRUE); 318 } 319 } 320 321 void OnPaint(HWND hWnd) 322 { 323 HDC dialogDC; 324 PAINTSTRUCT paint; 325 RECT cRC, textRC; 326 int i, xPos, yPos, CharCount; 327 HFONT dcFont; 328 HICON hIcon; 329 HPEN hPen; 330 COLORREF Color; 331 332 // check 333 if (nCols == 0 || nItems == 0) 334 return; 335 336 // begin painting 337 dialogDC = BeginPaint(hWnd, &paint); 338 if (dialogDC == NULL) 339 return; 340 341 // fill the client area 342 GetClientRect(hWnd, &cRC); 343 FillRect(dialogDC, &cRC, (HBRUSH)(COLOR_3DFACE + 1)); 344 345 // if the selection index exceeded the display items, then 346 // do display item shifting 347 if (selectedWindow >= nItems) 348 nShift = selectedWindow - nItems + 1; 349 else 350 nShift = 0; 351 352 for (i = 0; i < nItems; ++i) 353 { 354 // get the icon to display 355 hIcon = iconList[i + nShift]; 356 357 // calculate the position where we start drawing 358 xPos = DIALOG_MARGIN + CX_ITEM_SPACE * (i % nCols) + ITEM_MARGIN; 359 yPos = DIALOG_MARGIN + CY_ITEM_SPACE * (i / nCols) + ITEM_MARGIN; 360 361 // centering 362 if (nItems < CoolSwitchColumns) 363 { 364 xPos += (itemsW - nItems * CX_ITEM_SPACE) / 2; 365 } 366 367 // if this position is selected, 368 if (selectedWindow == i + nShift) 369 { 370 // create a solid pen 371 Color = GetSysColor(COLOR_HIGHLIGHT); 372 hPen = CreatePen(PS_SOLID, 1, Color); 373 374 // draw a rectangle with using the pen 375 SelectObject(dialogDC, hPen); 376 SelectObject(dialogDC, GetStockObject(NULL_BRUSH)); 377 Rectangle(dialogDC, xPos, yPos, xPos + CX_ITEM, yPos + CY_ITEM); 378 Rectangle(dialogDC, xPos + 1, yPos + 1, 379 xPos + CX_ITEM - 1, yPos + CY_ITEM - 1); 380 381 // delete the pen 382 DeleteObject(hPen); 383 } 384 385 // draw icon 386 DrawIconEx(dialogDC, xPos + ICON_MARGIN, yPos + ICON_MARGIN, 387 hIcon, CX_ICON, CY_ICON, 0, NULL, DI_NORMAL); 388 } 389 390 // set the text rectangle 391 SetRect(&textRC, DIALOG_MARGIN, DIALOG_MARGIN + itemsH, 392 totalW - DIALOG_MARGIN, totalH - DIALOG_MARGIN); 393 394 // draw the sunken button around text 395 DrawFrameControl(dialogDC, &textRC, DFC_BUTTON, 396 DFCS_BUTTONPUSH | DFCS_PUSHED); 397 398 // get text 399 CharCount = GetWindowTextW(windowList[selectedWindow], windowText, 400 _countof(windowText)); 401 402 // draw text 403 dcFont = SelectObject(dialogDC, dialogFont); 404 SetTextColor(dialogDC, GetSysColor(COLOR_BTNTEXT)); 405 SetBkMode(dialogDC, TRANSPARENT); 406 DrawTextW(dialogDC, windowText, CharCount, &textRC, 407 DT_CENTER | DT_VCENTER | DT_END_ELLIPSIS | DT_SINGLELINE); 408 SelectObject(dialogDC, dcFont); 409 410 // end painting 411 EndPaint(hWnd, &paint); 412 } 413 414 DWORD CreateSwitcherWindow(HINSTANCE hInstance) 415 { 416 switchdialog = CreateWindowExW( WS_EX_TOPMOST|WS_EX_DLGMODALFRAME|WS_EX_TOOLWINDOW, 417 WC_SWITCH, 418 L"", 419 WS_POPUP|WS_BORDER|WS_DISABLED, 420 CW_USEDEFAULT, 421 CW_USEDEFAULT, 422 400, 150, 423 NULL, NULL, 424 hInstance, NULL); 425 if (!switchdialog) 426 { 427 TRACE("[ATbot] Task Switcher Window failed to create.\n"); 428 return 0; 429 } 430 431 isOpen = FALSE; 432 return 1; 433 } 434 435 DWORD GetDialogFont(VOID) 436 { 437 HDC tDC; 438 TEXTMETRIC tm; 439 440 dialogFont = (HFONT)GetStockObject(DEFAULT_GUI_FONT); 441 442 tDC = GetDC(0); 443 GetTextMetrics(tDC, &tm); 444 fontHeight = tm.tmHeight; 445 ReleaseDC(0, tDC); 446 447 return 1; 448 } 449 450 void PrepareWindow(VOID) 451 { 452 nItems = windowCount; 453 454 nCols = CoolSwitchColumns; 455 nRows = (nItems + CoolSwitchColumns - 1) / CoolSwitchColumns; 456 if (nRows > CoolSwitchRows) 457 { 458 nRows = CoolSwitchRows; 459 nItems = nRows * nCols; 460 } 461 462 itemsW = nCols * CX_ITEM_SPACE; 463 itemsH = nRows * CY_ITEM_SPACE; 464 465 totalW = itemsW + 2 * DIALOG_MARGIN; 466 totalH = itemsH + 2 * DIALOG_MARGIN; 467 totalH += fontHeight + 2 * CY_TEXT_MARGIN; 468 469 ResizeAndCenter(switchdialog, totalW, totalH); 470 } 471 472 BOOL ProcessHotKey(VOID) 473 { 474 if (!isOpen) 475 { 476 windowCount = 0; 477 EnumWindows(EnumWindowsProc, 0); 478 479 if (windowCount == 0) 480 return FALSE; 481 482 if (windowCount == 1) 483 { 484 MakeWindowActive(windowList[0]); 485 return FALSE; 486 } 487 488 if (!CreateSwitcherWindow(User32Instance)) 489 return FALSE; 490 491 selectedWindow = 1; 492 493 TRACE("[ATbot] HotKey Received. Opening window.\n"); 494 ShowWindow(switchdialog, SW_SHOWNORMAL); 495 MakeWindowActive(switchdialog); 496 isOpen = TRUE; 497 } 498 else 499 { 500 TRACE("[ATbot] HotKey Received Rotating.\n"); 501 selectedWindow = (selectedWindow + 1)%windowCount; 502 InvalidateRect(switchdialog, NULL, TRUE); 503 } 504 return TRUE; 505 } 506 507 void RotateTasks(BOOL bShift) 508 { 509 HWND hwndFirst, hwndLast; 510 DWORD Size; 511 512 if (windowCount < 2 || !Esc) 513 return; 514 515 hwndFirst = windowList[0]; 516 hwndLast = windowList[windowCount - 1]; 517 518 if (bShift) 519 { 520 SetWindowPos(hwndLast, HWND_TOP, 0, 0, 0, 0, 521 SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | 522 SWP_NOOWNERZORDER | SWP_NOREPOSITION); 523 524 MakeWindowActive(hwndLast); 525 526 Size = (windowCount - 1) * sizeof(HWND); 527 MoveMemory(&windowList[1], &windowList[0], Size); 528 windowList[0] = hwndLast; 529 } 530 else 531 { 532 SetWindowPos(hwndFirst, hwndLast, 0, 0, 0, 0, 533 SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | 534 SWP_NOOWNERZORDER | SWP_NOREPOSITION); 535 536 MakeWindowActive(windowList[1]); 537 538 Size = (windowCount - 1) * sizeof(HWND); 539 MoveMemory(&windowList[0], &windowList[1], Size); 540 windowList[windowCount - 1] = hwndFirst; 541 } 542 } 543 544 static void MoveLeft(void) 545 { 546 selectedWindow = selectedWindow - 1; 547 if (selectedWindow < 0) 548 selectedWindow = windowCount - 1; 549 InvalidateRect(switchdialog, NULL, TRUE); 550 } 551 552 static void MoveRight(void) 553 { 554 selectedWindow = (selectedWindow + 1) % windowCount; 555 InvalidateRect(switchdialog, NULL, TRUE); 556 } 557 558 static void MoveUp(void) 559 { 560 INT iRow = selectedWindow / nCols; 561 INT iCol = selectedWindow % nCols; 562 563 --iRow; 564 if (iRow < 0) 565 iRow = nRows - 1; 566 567 selectedWindow = iRow * nCols + iCol; 568 if (selectedWindow >= windowCount) 569 selectedWindow = windowCount - 1; 570 InvalidateRect(switchdialog, NULL, TRUE); 571 } 572 573 static void MoveDown(void) 574 { 575 INT iRow = selectedWindow / nCols; 576 INT iCol = selectedWindow % nCols; 577 578 ++iRow; 579 if (iRow >= nRows) 580 iRow = 0; 581 582 selectedWindow = iRow * nCols + iCol; 583 if (selectedWindow >= windowCount) 584 selectedWindow = windowCount - 1; 585 InvalidateRect(switchdialog, NULL, TRUE); 586 } 587 588 VOID 589 DestroyAppWindows(VOID) 590 { 591 // for every item of the icon list: 592 INT i; 593 for (i = 0; i < windowCount; ++i) 594 { 595 // destroy the icon 596 DestroyIcon(iconList[i]); 597 iconList[i] = NULL; 598 } 599 } 600 601 LRESULT WINAPI DoAppSwitch( WPARAM wParam, LPARAM lParam ) 602 { 603 HWND hwndActive; 604 MSG msg; 605 606 // FIXME: Is loading timing OK? 607 LoadCoolSwitchSettings(); 608 609 if (!CoolSwitch) 610 return 0; 611 612 // Already in the loop. 613 if (switchdialog || Esc) return 0; 614 615 if (lParam == VK_ESCAPE) 616 { 617 Esc = TRUE; 618 619 windowCount = 0; 620 EnumWindows(EnumWindowsProc, 0); 621 622 if (windowCount < 2) 623 return 0; 624 625 RotateTasks(GetAsyncKeyState(VK_SHIFT) < 0); 626 627 hwndActive = GetActiveWindow(); 628 629 if (hwndActive == NULL) 630 { 631 Esc = FALSE; 632 return 0; 633 } 634 } 635 636 // Capture current active window. 637 hwndActive = GetActiveWindow(); 638 if (hwndActive) 639 SetCapture(hwndActive); 640 641 switch (lParam) 642 { 643 case VK_TAB: 644 if (!GetDialogFont() || !ProcessHotKey()) 645 goto Exit; 646 break; 647 648 case VK_ESCAPE: 649 break; 650 651 default: 652 goto Exit; 653 } 654 655 if (!hwndActive) 656 goto Exit; 657 658 // Main message loop: 659 while (1) 660 { 661 for (;;) 662 { 663 if (PeekMessageW( &msg, 0, 0, 0, PM_NOREMOVE )) 664 { 665 if (!CallMsgFilterW( &msg, MSGF_NEXTWINDOW )) break; 666 /* remove the message from the queue */ 667 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE ); 668 } 669 else 670 WaitMessage(); 671 } 672 673 switch (msg.message) 674 { 675 case WM_KEYUP: 676 { 677 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE ); 678 if (msg.wParam == VK_MENU) 679 { 680 CompleteSwitch(TRUE); 681 } 682 else if (msg.wParam == VK_RETURN) 683 { 684 CompleteSwitch(TRUE); 685 } 686 else if (msg.wParam == VK_ESCAPE) 687 { 688 TRACE("DoAppSwitch VK_ESCAPE 2\n"); 689 CompleteSwitch(FALSE); 690 } 691 goto Exit; //break; 692 } 693 694 case WM_SYSKEYDOWN: 695 { 696 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE ); 697 if (HIWORD(msg.lParam) & KF_ALTDOWN) 698 { 699 if ( msg.wParam == VK_TAB ) 700 { 701 if (Esc) break; 702 if (GetKeyState(VK_SHIFT) < 0) 703 { 704 MoveLeft(); 705 } 706 else 707 { 708 MoveRight(); 709 } 710 } 711 else if ( msg.wParam == VK_ESCAPE ) 712 { 713 if (!Esc) break; 714 RotateTasks(GetKeyState(VK_SHIFT) < 0); 715 } 716 else if ( msg.wParam == VK_LEFT ) 717 { 718 MoveLeft(); 719 } 720 else if ( msg.wParam == VK_RIGHT ) 721 { 722 MoveRight(); 723 } 724 else if ( msg.wParam == VK_UP ) 725 { 726 MoveUp(); 727 } 728 else if ( msg.wParam == VK_DOWN ) 729 { 730 MoveDown(); 731 } 732 } 733 break; 734 } 735 736 case WM_LBUTTONUP: 737 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE ); 738 ProcessMouseMessage(msg.message, msg.lParam); 739 goto Exit; 740 741 default: 742 if (PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE )) 743 { 744 TranslateMessage(&msg); 745 DispatchMessageW(&msg); 746 } 747 break; 748 } 749 } 750 Exit: 751 ReleaseCapture(); 752 if (switchdialog) DestroyWindow(switchdialog); 753 if (Esc) DestroyAppWindows(); 754 switchdialog = NULL; 755 selectedWindow = 0; 756 windowCount = 0; 757 Esc = FALSE; 758 return 0; 759 } 760 761 // 762 // Switch System Class Window Proc. 763 // 764 LRESULT WINAPI SwitchWndProc_common(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL unicode ) 765 { 766 PWND pWnd; 767 PALTTABINFO ati; 768 pWnd = ValidateHwnd(hWnd); 769 if (pWnd) 770 { 771 if (!pWnd->fnid) 772 { 773 NtUserSetWindowFNID(hWnd, FNID_SWITCH); 774 } 775 } 776 777 switch (uMsg) 778 { 779 case WM_NCCREATE: 780 if (!(ati = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*ati)))) 781 return 0; 782 SetWindowLongPtrW( hWnd, 0, (LONG_PTR)ati ); 783 return TRUE; 784 785 case WM_SHOWWINDOW: 786 if (wParam) 787 { 788 PrepareWindow(); 789 ati = (PALTTABINFO)GetWindowLongPtrW(hWnd, 0); 790 ati->cbSize = sizeof(ALTTABINFO); 791 ati->cItems = nItems; 792 ati->cColumns = nCols; 793 ati->cRows = nRows; 794 if (nCols) 795 { 796 ati->iColFocus = (selectedWindow - nShift) % nCols; 797 ati->iRowFocus = (selectedWindow - nShift) / nCols; 798 } 799 else 800 { 801 ati->iColFocus = 0; 802 ati->iRowFocus = 0; 803 } 804 ati->cxItem = CX_ITEM_SPACE; 805 ati->cyItem = CY_ITEM_SPACE; 806 ati->ptStart = ptStart; 807 } 808 return 0; 809 810 case WM_MOUSEMOVE: 811 ProcessMouseMessage(uMsg, lParam); 812 return 0; 813 814 case WM_ACTIVATE: 815 if (wParam == WA_INACTIVE) 816 { 817 CompleteSwitch(FALSE); 818 } 819 return 0; 820 821 case WM_PAINT: 822 OnPaint(hWnd); 823 return 0; 824 825 case WM_DESTROY: 826 isOpen = FALSE; 827 ati = (PALTTABINFO)GetWindowLongPtrW(hWnd, 0); 828 HeapFree( GetProcessHeap(), 0, ati ); 829 SetWindowLongPtrW( hWnd, 0, 0 ); 830 DestroyAppWindows(); 831 NtUserSetWindowFNID(hWnd, FNID_DESTROY); 832 return 0; 833 } 834 return DefWindowProcW(hWnd, uMsg, wParam, lParam); 835 } 836 837 LRESULT WINAPI SwitchWndProcA(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) 838 { 839 return SwitchWndProc_common(hWnd, uMsg, wParam, lParam, FALSE); 840 } 841 842 LRESULT WINAPI SwitchWndProcW(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) 843 { 844 return SwitchWndProc_common(hWnd, uMsg, wParam, lParam, TRUE); 845 } 846