Revert "[USER32] Make App Switcher use the owner window's icon (#1299)" (#1605)
[reactos.git] / win32ss / user / user32 / controls / appswitch.c
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 selectedWindow = 0;
485 CompleteSwitch(TRUE);
486 return TRUE;
487 }
488
489 selectedWindow = 1;
490
491 TRACE("[ATbot] HotKey Received. Opening window.\n");
492 ShowWindow(switchdialog, SW_SHOWNORMAL);
493 MakeWindowActive(switchdialog);
494 isOpen = TRUE;
495 }
496 else
497 {
498 TRACE("[ATbot] HotKey Received Rotating.\n");
499 selectedWindow = (selectedWindow + 1)%windowCount;
500 InvalidateRect(switchdialog, NULL, TRUE);
501 }
502 return TRUE;
503 }
504
505 void RotateTasks(BOOL bShift)
506 {
507 HWND hwndFirst, hwndLast;
508 DWORD Size;
509
510 if (windowCount < 2 || !Esc)
511 return;
512
513 hwndFirst = windowList[0];
514 hwndLast = windowList[windowCount - 1];
515
516 if (bShift)
517 {
518 SetWindowPos(hwndLast, HWND_TOP, 0, 0, 0, 0,
519 SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE |
520 SWP_NOOWNERZORDER | SWP_NOREPOSITION);
521
522 MakeWindowActive(hwndLast);
523
524 Size = (windowCount - 1) * sizeof(HWND);
525 MoveMemory(&windowList[1], &windowList[0], Size);
526 windowList[0] = hwndLast;
527 }
528 else
529 {
530 SetWindowPos(hwndFirst, hwndLast, 0, 0, 0, 0,
531 SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE |
532 SWP_NOOWNERZORDER | SWP_NOREPOSITION);
533
534 MakeWindowActive(windowList[1]);
535
536 Size = (windowCount - 1) * sizeof(HWND);
537 MoveMemory(&windowList[0], &windowList[1], Size);
538 windowList[windowCount - 1] = hwndFirst;
539 }
540 }
541
542 static void MoveLeft(void)
543 {
544 selectedWindow = selectedWindow - 1;
545 if (selectedWindow < 0)
546 selectedWindow = windowCount - 1;
547 InvalidateRect(switchdialog, NULL, TRUE);
548 }
549
550 static void MoveRight(void)
551 {
552 selectedWindow = (selectedWindow + 1) % windowCount;
553 InvalidateRect(switchdialog, NULL, TRUE);
554 }
555
556 static void MoveUp(void)
557 {
558 INT iRow = selectedWindow / nCols;
559 INT iCol = selectedWindow % nCols;
560
561 --iRow;
562 if (iRow < 0)
563 iRow = nRows - 1;
564
565 selectedWindow = iRow * nCols + iCol;
566 if (selectedWindow >= windowCount)
567 selectedWindow = windowCount - 1;
568 InvalidateRect(switchdialog, NULL, TRUE);
569 }
570
571 static void MoveDown(void)
572 {
573 INT iRow = selectedWindow / nCols;
574 INT iCol = selectedWindow % nCols;
575
576 ++iRow;
577 if (iRow >= nRows)
578 iRow = 0;
579
580 selectedWindow = iRow * nCols + iCol;
581 if (selectedWindow >= windowCount)
582 selectedWindow = windowCount - 1;
583 InvalidateRect(switchdialog, NULL, TRUE);
584 }
585
586 VOID
587 DestroyAppWindows(VOID)
588 {
589 // for every item of the icon list:
590 INT i;
591 for (i = 0; i < windowCount; ++i)
592 {
593 // destroy the icon
594 DestroyIcon(iconList[i]);
595 iconList[i] = NULL;
596 }
597 }
598
599 LRESULT WINAPI DoAppSwitch( WPARAM wParam, LPARAM lParam )
600 {
601 HWND hwndActive;
602 MSG msg;
603
604 // FIXME: Is loading timing OK?
605 LoadCoolSwitchSettings();
606
607 if (!CoolSwitch)
608 return 0;
609
610 // Already in the loop.
611 if (switchdialog || Esc) return 0;
612
613 hwndActive = GetActiveWindow();
614 // Nothing is active so exit.
615 if (!hwndActive) return 0;
616
617 if (lParam == VK_ESCAPE)
618 {
619 Esc = TRUE;
620
621 windowCount = 0;
622 EnumWindows(EnumWindowsProc, 0);
623
624 if (windowCount < 2)
625 return 0;
626
627 RotateTasks(GetAsyncKeyState(VK_SHIFT) < 0);
628
629 hwndActive = GetActiveWindow();
630
631 if (hwndActive == NULL)
632 {
633 Esc = FALSE;
634 return 0;
635 }
636 }
637
638 // Capture current active window.
639 SetCapture( hwndActive );
640
641 switch (lParam)
642 {
643 case VK_TAB:
644 if( !CreateSwitcherWindow(User32Instance) ) goto Exit;
645 if( !GetDialogFont() ) goto Exit;
646 if( !ProcessHotKey() ) goto Exit;
647 break;
648
649 case VK_ESCAPE:
650 break;
651
652 default:
653 goto Exit;
654 }
655 // Main message loop:
656 while (1)
657 {
658 for (;;)
659 {
660 if (PeekMessageW( &msg, 0, 0, 0, PM_NOREMOVE ))
661 {
662 if (!CallMsgFilterW( &msg, MSGF_NEXTWINDOW )) break;
663 /* remove the message from the queue */
664 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
665 }
666 else
667 WaitMessage();
668 }
669
670 switch (msg.message)
671 {
672 case WM_KEYUP:
673 {
674 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
675 if (msg.wParam == VK_MENU)
676 {
677 CompleteSwitch(TRUE);
678 }
679 else if (msg.wParam == VK_RETURN)
680 {
681 CompleteSwitch(TRUE);
682 }
683 else if (msg.wParam == VK_ESCAPE)
684 {
685 TRACE("DoAppSwitch VK_ESCAPE 2\n");
686 CompleteSwitch(FALSE);
687 }
688 goto Exit; //break;
689 }
690
691 case WM_SYSKEYDOWN:
692 {
693 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
694 if (HIWORD(msg.lParam) & KF_ALTDOWN)
695 {
696 if ( msg.wParam == VK_TAB )
697 {
698 if (Esc) break;
699 if (GetKeyState(VK_SHIFT) < 0)
700 {
701 MoveLeft();
702 }
703 else
704 {
705 MoveRight();
706 }
707 }
708 else if ( msg.wParam == VK_ESCAPE )
709 {
710 if (!Esc) break;
711 RotateTasks(GetKeyState(VK_SHIFT) < 0);
712 }
713 else if ( msg.wParam == VK_LEFT )
714 {
715 MoveLeft();
716 }
717 else if ( msg.wParam == VK_RIGHT )
718 {
719 MoveRight();
720 }
721 else if ( msg.wParam == VK_UP )
722 {
723 MoveUp();
724 }
725 else if ( msg.wParam == VK_DOWN )
726 {
727 MoveDown();
728 }
729 }
730 break;
731 }
732
733 case WM_LBUTTONUP:
734 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
735 ProcessMouseMessage(msg.message, msg.lParam);
736 goto Exit;
737
738 default:
739 if (PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE ))
740 {
741 TranslateMessage(&msg);
742 DispatchMessageW(&msg);
743 }
744 break;
745 }
746 }
747 Exit:
748 ReleaseCapture();
749 if (switchdialog) DestroyWindow(switchdialog);
750 if (Esc) DestroyAppWindows();
751 switchdialog = NULL;
752 selectedWindow = 0;
753 windowCount = 0;
754 Esc = FALSE;
755 return 0;
756 }
757
758 //
759 // Switch System Class Window Proc.
760 //
761 LRESULT WINAPI SwitchWndProc_common(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL unicode )
762 {
763 PWND pWnd;
764 PALTTABINFO ati;
765 pWnd = ValidateHwnd(hWnd);
766 if (pWnd)
767 {
768 if (!pWnd->fnid)
769 {
770 NtUserSetWindowFNID(hWnd, FNID_SWITCH);
771 }
772 }
773
774 switch (uMsg)
775 {
776 case WM_NCCREATE:
777 if (!(ati = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*ati))))
778 return 0;
779 SetWindowLongPtrW( hWnd, 0, (LONG_PTR)ati );
780 return TRUE;
781
782 case WM_SHOWWINDOW:
783 if (wParam)
784 {
785 PrepareWindow();
786 ati = (PALTTABINFO)GetWindowLongPtrW(hWnd, 0);
787 ati->cbSize = sizeof(ALTTABINFO);
788 ati->cItems = nItems;
789 ati->cColumns = nCols;
790 ati->cRows = nRows;
791 if (nCols)
792 {
793 ati->iColFocus = (selectedWindow - nShift) % nCols;
794 ati->iRowFocus = (selectedWindow - nShift) / nCols;
795 }
796 else
797 {
798 ati->iColFocus = 0;
799 ati->iRowFocus = 0;
800 }
801 ati->cxItem = CX_ITEM_SPACE;
802 ati->cyItem = CY_ITEM_SPACE;
803 ati->ptStart = ptStart;
804 }
805 return 0;
806
807 case WM_MOUSEMOVE:
808 ProcessMouseMessage(uMsg, lParam);
809 return 0;
810
811 case WM_ACTIVATE:
812 if (wParam == WA_INACTIVE)
813 {
814 CompleteSwitch(FALSE);
815 }
816 return 0;
817
818 case WM_PAINT:
819 OnPaint(hWnd);
820 return 0;
821
822 case WM_DESTROY:
823 isOpen = FALSE;
824 ati = (PALTTABINFO)GetWindowLongPtrW(hWnd, 0);
825 HeapFree( GetProcessHeap(), 0, ati );
826 SetWindowLongPtrW( hWnd, 0, 0 );
827 DestroyAppWindows();
828 NtUserSetWindowFNID(hWnd, FNID_DESTROY);
829 return 0;
830 }
831 return DefWindowProcW(hWnd, uMsg, wParam, lParam);
832 }
833
834 LRESULT WINAPI SwitchWndProcA(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
835 {
836 return SwitchWndProc_common(hWnd, uMsg, wParam, lParam, FALSE);
837 }
838
839 LRESULT WINAPI SwitchWndProcW(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
840 {
841 return SwitchWndProc_common(hWnd, uMsg, wParam, lParam, TRUE);
842 }