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