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