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