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