[USER32] Switch to only one window (Alt+Tab) (#1718)
[reactos.git] / win32ss / user / user32 / controls / appswitch.c
index 3bda965..03531ac 100644 (file)
@@ -1,17 +1,39 @@
 /*
  * COPYRIGHT:       See COPYING in the top level directory
  * PROJECT:         ReactOS system libraries
- * FILE:            dll/win32/user32/controls/appswitch.c
+ * FILE:            win32ss/user/user32/controls/appswitch.c
  * PURPOSE:         app switching functionality
  * PROGRAMMERS:     Johannes Anderwald (johannes.anderwald@reactos.org)
  *                  David Quintana (gigaherz@gmail.com)
+ *                  Katayama Hirofumi MZ (katayama.hirofumi.mz@gmail.com)
  */
 
+//
+// TODO:
+//       Move to Win32k.
+//       Add registry support.
+//
+//
+
 #include <user32.h>
 
-#include <wine/debug.h>
 WINE_DEFAULT_DEBUG_CHANNEL(user32);
 
+#define DIALOG_MARGIN   8       // margin of dialog contents
+
+#define CX_ICON         32      // width of icon
+#define CY_ICON         32      // height of icon
+#define ICON_MARGIN     4       // margin width around an icon
+
+#define CX_ITEM         (CX_ICON + 2 * ICON_MARGIN)
+#define CY_ITEM         (CY_ICON + 2 * ICON_MARGIN)
+#define ITEM_MARGIN     4       // margin width around an item
+
+#define CX_ITEM_SPACE   (CX_ITEM + 2 * ITEM_MARGIN)
+#define CY_ITEM_SPACE   (CY_ITEM + 2 * ITEM_MARGIN)
+
+#define CY_TEXT_MARGIN  4       // margin height around text
+
 // limit the number of windows shown in the alt-tab window
 // 120 windows results in (12*40) by (10*40) pixels worth of icons.
 #define MAX_WINDOWS 120
@@ -35,29 +57,75 @@ int nItems, nCols, nRows;
 int itemsW, itemsH;
 int totalW, totalH;
 int xOffset, yOffset;
-POINT pt;
+POINT ptStart;
+
+int nShift = 0;
+
+BOOL Esc = FALSE;
+
+BOOL CoolSwitch = TRUE;
+int CoolSwitchRows = 3;
+int CoolSwitchColumns = 7;
+
+// window style
+const DWORD Style = WS_POPUP | WS_BORDER | WS_DISABLED;
+const DWORD ExStyle = WS_EX_TOPMOST | WS_EX_DLGMODALFRAME | WS_EX_TOOLWINDOW;
+
+DWORD wtodw(const WCHAR *psz)
+{
+   const WCHAR *pch = psz;
+   DWORD Value = 0;
+   while ('0' <= *pch && *pch <= '9')
+   {
+      Value *= 10;
+      Value += *pch - L'0';
+   }
+   return Value;
+}
+
+BOOL LoadCoolSwitchSettings(void)
+{
+   CoolSwitch = TRUE;
+   CoolSwitchRows = 3;
+   CoolSwitchColumns = 7;
+
+   // FIXME: load the settings from registry
+
+   TRACE("CoolSwitch: %d\n", CoolSwitch);
+   TRACE("CoolSwitchRows: %d\n", CoolSwitchRows);
+   TRACE("CoolSwitchColumns: %d\n", CoolSwitchColumns);
+
+   return TRUE;
+}
 
 void ResizeAndCenter(HWND hwnd, int width, int height)
 {
+   int x, y;
+   RECT Rect;
+
    int screenwidth = GetSystemMetrics(SM_CXSCREEN);
    int screenheight = GetSystemMetrics(SM_CYSCREEN);
 
-   pt.x = (screenwidth - width) / 2;
-   pt.y = (screenheight - height) / 2;
+   x = (screenwidth - width) / 2;
+   y = (screenheight - height) / 2;
+
+   SetRect(&Rect, x, y, x + width, y + height);
+   AdjustWindowRectEx(&Rect, Style, FALSE, ExStyle);
+
+   x = Rect.left;
+   y = Rect.top;
+   width = Rect.right - Rect.left;
+   height = Rect.bottom - Rect.top;
+   MoveWindow(hwnd, x, y, width, height, FALSE);
 
-   MoveWindow(hwnd, pt.x, pt.y, width, height, FALSE);
+   ptStart.x = x;
+   ptStart.y = y;
 }
 
 void MakeWindowActive(HWND hwnd)
 {
-   WINDOWPLACEMENT wpl;
-
-   wpl.length = sizeof(WINDOWPLACEMENT);
-   GetWindowPlacement(hwnd, &wpl);
-  
-   TRACE("GetWindowPlacement wpl.showCmd %d\n",wpl.showCmd);
-   if (wpl.showCmd == SW_SHOWMINIMIZED)
-      ShowWindowAsync(hwnd, SW_RESTORE);
+   if (IsIconic(hwnd))
+      PostMessageW(hwnd, WM_SYSCOMMAND, SC_RESTORE, 0);
 
    BringWindowToTop(hwnd);  // same as: SetWindowPos(hwnd,HWND_TOP,0,0,0,0,SWP_NOMOVE|SWP_NOSIZE); ?
    SetForegroundWindow(hwnd);
@@ -82,7 +150,7 @@ void CompleteSwitch(BOOL doSwitch)
       //if(selectedWindow != 0)
       {
          HWND hwnd = windowList[selectedWindow];
-                  
+
          GetWindowTextW(hwnd, windowText, _countof(windowText));
 
          TRACE("[ATbot] CompleteSwitch Switching to 0x%08x (%ls)\n", hwnd, windowText);
@@ -100,14 +168,6 @@ BOOL CALLBACK EnumerateCallback(HWND window, LPARAM lParam)
 
    UNREFERENCED_PARAMETER(lParam);
 
-   if (!IsWindowVisible(window))
-            return TRUE;
-
-   GetClassNameW(window, windowText, _countof(windowText));
-   if ((wcscmp(L"Shell_TrayWnd", windowText)==0) ||
-       (wcscmp(L"Progman", windowText)==0) )
-            return TRUE;
-      
    // First try to get the big icon assigned to the window
    hIcon = (HICON)SendMessageW(window, WM_GETICON, ICON_BIG, 0);
    if (!hIcon)
@@ -121,8 +181,13 @@ BOOL CALLBACK EnumerateCallback(HWND window, LPARAM lParam)
          hIcon = (HICON)SendMessageW(window, WM_GETICON, ICON_SMALL2, 0);
          if (!hIcon)
          {
-            // If all fails, give up and continue with the next window
-            return TRUE;
+            // using windows logo icon as default
+            hIcon = gpsi->hIconWindows;
+            if (!hIcon)
+            {
+               //if all attempts to get icon fails go to the next window
+               return TRUE;
+            }
          }
       }
    }
@@ -134,42 +199,111 @@ BOOL CALLBACK EnumerateCallback(HWND window, LPARAM lParam)
 
    // If we got to the max number of windows,
    // we won't be able to add any more
-   if(windowCount == MAX_WINDOWS)
+   if(windowCount >= MAX_WINDOWS)
       return FALSE;
 
    return TRUE;
 }
 
-// Function mostly compatible with the normal EnumWindows,
-// except it lists in Z-Order and it doesn't ensure consistency
-// if a window is removed while enumerating
-void EnumWindowsZOrder(WNDENUMPROC callback, LPARAM lParam)
+static HWND GetNiceRootOwner(HWND hwnd)
 {
-    HWND next = GetTopWindow(NULL);
-    while (next != NULL)
+    HWND hwndOwner;
+    DWORD ExStyle, OwnerExStyle;
+
+    for (;;)
     {
-        if(!callback(next, lParam))
-         break;
-        next = GetWindow(next, GW_HWNDNEXT);
+        // A window with WS_EX_APPWINDOW is treated as if it has no owner
+        ExStyle = GetWindowLong(hwnd, GWL_EXSTYLE);
+        if (ExStyle & WS_EX_APPWINDOW)
+            break;
+
+        // Is the owner visible?
+        // An window with WS_EX_TOOLWINDOW is treated as if it weren't visible
+        hwndOwner = GetWindow(hwnd, GW_OWNER);
+        OwnerExStyle = GetWindowLong(hwndOwner, GWL_EXSTYLE);
+        if (!IsWindowVisible(hwndOwner) || (OwnerExStyle & WS_EX_TOOLWINDOW))
+            break;
+
+        hwnd = hwndOwner;
     }
+
+    return hwnd;
 }
 
-void ProcessMouseMessage(UINT message, LPARAM lParam)
+// c.f. http://blogs.msdn.com/b/oldnewthing/archive/2007/10/08/5351207.aspx
+BOOL IsAltTabWindow(HWND hwnd)
 {
-   int xPos = LOWORD(lParam); 
-   int yPos = HIWORD(lParam); 
+    DWORD ExStyle;
+    RECT rc;
+    HWND hwndTry, hwndWalk;
+    WCHAR szClass[64];
+
+    // must be visible
+    if (!IsWindowVisible(hwnd))
+        return FALSE;
+
+    // must not be WS_EX_TOOLWINDOW
+    ExStyle = GetWindowLong(hwnd, GWL_EXSTYLE);
+    if (ExStyle & WS_EX_TOOLWINDOW)
+        return FALSE;
+
+    // must be not empty rect
+    GetWindowRect(hwnd, &rc);
+    if (IsRectEmpty(&rc))
+        return FALSE;
+
+    // check special windows
+    if (!GetClassNameW(hwnd, szClass, _countof(szClass)) ||
+        wcscmp(szClass, L"Shell_TrayWnd") == 0 ||
+        wcscmp(szClass, L"Progman") == 0)
+    {
+        return TRUE;
+    }
+
+    // get 'nice' root owner
+    hwndWalk = GetNiceRootOwner(hwnd);
 
-   int xIndex = (xPos - xOffset)/40;
-   int xOff   = (xPos - xOffset)%40;
+    // walk back from hwndWalk toward hwnd
+    for (;;)
+    {
+        hwndTry = GetLastActivePopup(hwndWalk);
+        if (hwndTry == hwndWalk)
+            break;
 
-   int yIndex = (yPos - yOffset)/40;
-   int yOff   = (yPos - yOffset)%40;
+        ExStyle = GetWindowLong(hwndTry, GWL_EXSTYLE);
+        if (IsWindowVisible(hwndTry) && !(ExStyle & WS_EX_TOOLWINDOW))
+            break;
 
-   if(xOff > 32 || xIndex > nItems)
-      return;
+        hwndWalk = hwndTry;
+    }
 
-   if(yOff > 32 || yIndex > nRows)
-      return;
+    return hwnd == hwndTry;     // Reached?
+}
+
+static BOOL CALLBACK
+EnumWindowsProc(HWND hwnd, LPARAM lParam)
+{
+    if (IsAltTabWindow(hwnd))
+    {
+        if (!EnumerateCallback(hwnd, lParam))
+            return FALSE;
+    }
+    return TRUE;
+}
+
+void ProcessMouseMessage(UINT message, LPARAM lParam)
+{
+   int xPos = LOWORD(lParam);
+   int yPos = HIWORD(lParam);
+
+   int xIndex = (xPos - DIALOG_MARGIN) / CX_ITEM_SPACE;
+   int yIndex = (yPos - DIALOG_MARGIN) / CY_ITEM_SPACE;
+
+   if (xIndex < 0 || nCols <= xIndex ||
+       yIndex < 0 || nRows <= yIndex)
+   {
+        return;
+   }
 
    selectedWindow = (yIndex*nCols) + xIndex;
    if (message == WM_MOUSEMOVE)
@@ -186,63 +320,95 @@ void ProcessMouseMessage(UINT message, LPARAM lParam)
 
 void OnPaint(HWND hWnd)
 {
-   HDC dialogDC;
-   PAINTSTRUCT paint;
-   RECT cRC, textRC;
-   int i;
-   HBRUSH hBrush;
-   HPEN hPen;
-   HFONT dcFont;
-   COLORREF cr;
-   int nch = GetWindowTextW(windowList[selectedWindow], windowText, _countof(windowText));
-
-   dialogDC = BeginPaint(hWnd, &paint);
-   {
-      GetClientRect(hWnd, &cRC);
-      FillRect(dialogDC, &cRC, GetSysColorBrush(COLOR_MENU));
+    HDC dialogDC;
+    PAINTSTRUCT paint;
+    RECT cRC, textRC;
+    int i, xPos, yPos, CharCount;
+    HFONT dcFont;
+    HICON hIcon;
+    HPEN hPen;
+    COLORREF Color;
+
+    // check
+    if (nCols == 0 || nItems == 0)
+        return;
+
+    // begin painting
+    dialogDC = BeginPaint(hWnd, &paint);
+    if (dialogDC == NULL)
+        return;
+
+    // fill the client area
+    GetClientRect(hWnd, &cRC);
+    FillRect(dialogDC, &cRC, (HBRUSH)(COLOR_3DFACE + 1));
+
+    // if the selection index exceeded the display items, then
+    // do display item shifting
+    if (selectedWindow >= nItems)
+        nShift = selectedWindow - nItems + 1;
+    else
+        nShift = 0;
+
+    for (i = 0; i < nItems; ++i)
+    {
+        // get the icon to display
+        hIcon = iconList[i + nShift];
 
-      for(i=0; i< windowCount; i++)
-      {
-         HICON hIcon = iconList[i];
-         
-         int xpos = xOffset + 40 * (i % nCols);
-         int ypos = yOffset + 40 * (i / nCols);
+        // calculate the position where we start drawing
+        xPos = DIALOG_MARGIN + CX_ITEM_SPACE * (i % nCols) + ITEM_MARGIN;
+        yPos = DIALOG_MARGIN + CY_ITEM_SPACE * (i / nCols) + ITEM_MARGIN;
 
-         if (selectedWindow == i)
-         {
-            hBrush = GetSysColorBrush(COLOR_HIGHLIGHT);
-         }
-         else
-         {
-            hBrush = GetSysColorBrush(COLOR_MENU);
-         }
-#if TRUE
-         cr = GetSysColor(COLOR_BTNTEXT); // doesn't look right! >_<
-         hPen = CreatePen(PS_DOT, 1, cr);
-         SelectObject(dialogDC, hPen);
-         SelectObject(dialogDC, hBrush);
-         Rectangle(dialogDC, xpos-2, ypos-2, xpos+32+2, ypos+32+2);
-         DeleteObject(hPen);
-         // Must NOT destroy the system brush!
-#else
-         RECT rc = { xpos-2, ypos-2, xpos+32+2, ypos+32+2 };
-         FillRect(dialogDC, &rc, hBrush);
-#endif
-         DrawIcon(dialogDC, xpos, ypos, hIcon);
-      }
+        // centering
+        if (nItems < CoolSwitchColumns)
+        {
+            xPos += (itemsW - nItems * CX_ITEM_SPACE) / 2;
+        }
 
-      dcFont = SelectObject(dialogDC, dialogFont);
-      SetTextColor(dialogDC, GetSysColor(COLOR_BTNTEXT));
-      SetBkColor(dialogDC, GetSysColor(COLOR_BTNFACE));
+        // if this position is selected,
+        if (selectedWindow == i + nShift)
+        {
+            // create a solid pen
+            Color = GetSysColor(COLOR_HIGHLIGHT);
+            hPen = CreatePen(PS_SOLID, 1, Color);
+
+            // draw a rectangle with using the pen
+            SelectObject(dialogDC, hPen);
+            SelectObject(dialogDC, GetStockObject(NULL_BRUSH));
+            Rectangle(dialogDC, xPos, yPos, xPos + CX_ITEM, yPos + CY_ITEM);
+            Rectangle(dialogDC, xPos + 1, yPos + 1,
+                                xPos + CX_ITEM - 1, yPos + CY_ITEM - 1);
+
+            // delete the pen
+            DeleteObject(hPen);
+        }
 
-      textRC.top = itemsH;
-      textRC.left = 8;
-      textRC.right = totalW - 8;
-      textRC.bottom = totalH - 8;
-      DrawTextW(dialogDC, windowText, nch, &textRC, DT_CENTER|DT_END_ELLIPSIS);
-      SelectObject(dialogDC, dcFont);
-   }
-   EndPaint(hWnd, &paint);
+        // draw icon
+        DrawIconEx(dialogDC, xPos + ICON_MARGIN, yPos + ICON_MARGIN,
+                   hIcon, CX_ICON, CY_ICON, 0, NULL, DI_NORMAL);
+    }
+
+    // set the text rectangle
+    SetRect(&textRC, DIALOG_MARGIN, DIALOG_MARGIN + itemsH,
+            totalW - DIALOG_MARGIN, totalH - DIALOG_MARGIN);
+
+    // draw the sunken button around text
+    DrawFrameControl(dialogDC, &textRC, DFC_BUTTON,
+                     DFCS_BUTTONPUSH | DFCS_PUSHED);
+
+    // get text
+    CharCount = GetWindowTextW(windowList[selectedWindow], windowText,
+                               _countof(windowText));
+
+    // draw text
+    dcFont = SelectObject(dialogDC, dialogFont);
+    SetTextColor(dialogDC, GetSysColor(COLOR_BTNTEXT));
+    SetBkMode(dialogDC, TRANSPARENT);
+    DrawTextW(dialogDC, windowText, CharCount, &textRC,
+              DT_CENTER | DT_VCENTER | DT_END_ELLIPSIS | DT_SINGLELINE);
+    SelectObject(dialogDC, dcFont);
+
+    // end painting
+    EndPaint(hWnd, &paint);
 }
 
 DWORD CreateSwitcherWindow(HINSTANCE hInstance)
@@ -261,12 +427,12 @@ DWORD CreateSwitcherWindow(HINSTANCE hInstance)
        TRACE("[ATbot] Task Switcher Window failed to create.\n");
        return 0;
     }
-                                
+
     isOpen = FALSE;
     return 1;
 }
-                                        
-DWORD GetDialogFont()
+
+DWORD GetDialogFont(VOID)
 {
    HDC tDC;
    TEXTMETRIC tm;
@@ -281,41 +447,46 @@ DWORD GetDialogFont()
    return 1;
 }
 
-void PrepareWindow()
+void PrepareWindow(VOID)
 {
-   cxBorder = GetSystemMetrics(SM_CXBORDER);
-   cyBorder = GetSystemMetrics(SM_CYBORDER);
-   
    nItems = windowCount;
-   nCols = min(max(nItems,8),12);
-   nRows = (nItems+nCols-1)/nCols;
 
-   itemsW = nCols*32 + (nCols+1)*8;
-   itemsH = nRows*32 + (nRows+1)*8;
+   nCols = CoolSwitchColumns;
+   nRows = (nItems + CoolSwitchColumns - 1) / CoolSwitchColumns;
+   if (nRows > CoolSwitchRows)
+   {
+      nRows = CoolSwitchRows;
+      nItems = nRows * nCols;
+   }
 
-   totalW = itemsW + 2*cxBorder + 4;
-   totalH = itemsH + 2*cyBorder + fontHeight + 8; // give extra pixels for the window title
+   itemsW = nCols * CX_ITEM_SPACE;
+   itemsH = nRows * CY_ITEM_SPACE;
 
-   xOffset = 8;
-   yOffset = 8;
+   totalW = itemsW + 2 * DIALOG_MARGIN;
+   totalH = itemsH + 2 * DIALOG_MARGIN;
+   totalH += fontHeight + 2 * CY_TEXT_MARGIN;
 
-   if (nItems < nCols)
-   {
-      int w2 = nItems*32 + (nItems-1)*8;
-      xOffset = (itemsW-w2)/2;
-   }
    ResizeAndCenter(switchdialog, totalW, totalH);
 }
 
-void ProcessHotKey()
+BOOL ProcessHotKey(VOID)
 {
    if (!isOpen)
    {
-      windowCount=0;
-      EnumWindowsZOrder(EnumerateCallback, 0);
+      windowCount = 0;
+      EnumWindows(EnumWindowsProc, 0);
 
-      if (windowCount < 2)
-         return;
+      if (windowCount == 0)
+         return FALSE;
+
+      if (windowCount == 1)
+      {
+         MakeWindowActive(windowList[0]);
+         return FALSE;
+      }
+
+      if (!CreateSwitcherWindow(User32Instance))
+         return FALSE;
 
       selectedWindow = 1;
 
@@ -330,58 +501,160 @@ void ProcessHotKey()
       selectedWindow = (selectedWindow + 1)%windowCount;
       InvalidateRect(switchdialog, NULL, TRUE);
    }
+   return TRUE;
+}
+
+void RotateTasks(BOOL bShift)
+{
+    HWND hwndFirst, hwndLast;
+    DWORD Size;
+
+    if (windowCount < 2 || !Esc)
+        return;
+
+    hwndFirst = windowList[0];
+    hwndLast = windowList[windowCount - 1];
+
+    if (bShift)
+    {
+        SetWindowPos(hwndLast, HWND_TOP, 0, 0, 0, 0,
+                     SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE |
+                     SWP_NOOWNERZORDER | SWP_NOREPOSITION);
+
+        MakeWindowActive(hwndLast);
+
+        Size = (windowCount - 1) * sizeof(HWND);
+        MoveMemory(&windowList[1], &windowList[0], Size);
+        windowList[0] = hwndLast;
+    }
+    else
+    {
+        SetWindowPos(hwndFirst, hwndLast, 0, 0, 0, 0,
+                     SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE |
+                     SWP_NOOWNERZORDER | SWP_NOREPOSITION);
+
+        MakeWindowActive(windowList[1]);
+
+        Size = (windowCount - 1) * sizeof(HWND);
+        MoveMemory(&windowList[0], &windowList[1], Size);
+        windowList[windowCount - 1] = hwndFirst;
+    }
+}
+
+static void MoveLeft(void)
+{
+    selectedWindow = selectedWindow - 1;
+    if (selectedWindow < 0)
+        selectedWindow = windowCount - 1;
+    InvalidateRect(switchdialog, NULL, TRUE);
+}
+
+static void MoveRight(void)
+{
+    selectedWindow = (selectedWindow + 1) % windowCount;
+    InvalidateRect(switchdialog, NULL, TRUE);
+}
+
+static void MoveUp(void)
+{
+    INT iRow = selectedWindow / nCols;
+    INT iCol = selectedWindow % nCols;
+
+    --iRow;
+    if (iRow < 0)
+        iRow = nRows - 1;
+
+    selectedWindow = iRow * nCols + iCol;
+    if (selectedWindow >= windowCount)
+        selectedWindow = windowCount - 1;
+    InvalidateRect(switchdialog, NULL, TRUE);
+}
+
+static void MoveDown(void)
+{
+    INT iRow = selectedWindow / nCols;
+    INT iCol = selectedWindow % nCols;
+
+    ++iRow;
+    if (iRow >= nRows)
+        iRow = 0;
+
+    selectedWindow = iRow * nCols + iCol;
+    if (selectedWindow >= windowCount)
+        selectedWindow = windowCount - 1;
+    InvalidateRect(switchdialog, NULL, TRUE);
+}
+
+VOID
+DestroyAppWindows(VOID)
+{
+    // for every item of the icon list:
+    INT i;
+    for (i = 0; i < windowCount; ++i)
+    {
+        // destroy the icon
+        DestroyIcon(iconList[i]);
+        iconList[i] = NULL;
+    }
 }
 
 LRESULT WINAPI DoAppSwitch( WPARAM wParam, LPARAM lParam )
 {
-   HWND hwnd, hwndActive;
+   HWND hwndActive;
    MSG msg;
-   BOOL Esc = FALSE;
-   INT Count = 0;
-   WCHAR Text[1024];
+
+   // FIXME: Is loading timing OK?
+   LoadCoolSwitchSettings();
+
+   if (!CoolSwitch)
+      return 0;
 
    // Already in the loop.
-   if (switchdialog) return 0;
+   if (switchdialog || Esc) return 0;
+
+   if (lParam == VK_ESCAPE)
+   {
+      Esc = TRUE;
+
+      windowCount = 0;
+      EnumWindows(EnumWindowsProc, 0);
+
+      if (windowCount < 2)
+          return 0;
+
+      RotateTasks(GetAsyncKeyState(VK_SHIFT) < 0);
+
+      hwndActive = GetActiveWindow();
+
+      if (hwndActive == NULL)
+      {
+          Esc = FALSE;
+          return 0;
+      }
+   }
 
-   hwndActive = GetActiveWindow();
-   // Nothing is active so exit.
-   if (!hwndActive) return 0;
    // Capture current active window.
-   SetCapture( hwndActive );
+   hwndActive = GetActiveWindow();
+   if (hwndActive)
+       SetCapture(hwndActive);
 
    switch (lParam)
    {
       case VK_TAB:
-         if( !CreateSwitcherWindow(User32Instance) ) goto Exit;
-         if( !GetDialogFont() ) goto Exit;
-         ProcessHotKey();
+         if (!GetDialogFont() || !ProcessHotKey())
+             goto Exit;
          break;
 
       case VK_ESCAPE:
-         windowCount = 0;
-         Count = 0;
-         EnumWindowsZOrder(EnumerateCallback, 0);
-         if (windowCount < 2) goto Exit;
-         if (wParam == SC_NEXTWINDOW)
-            Count = 1;
-         else
-         {
-            if (windowCount == 2)
-               Count = 0;
-            else
-               Count = windowCount - 1;
-         }
-         TRACE("DoAppSwitch VK_ESCAPE 1 Count %d windowCount %d\n",Count,windowCount);
-         hwnd = windowList[Count];
-         GetWindowTextW(hwnd, Text, _countof(Text));
-         TRACE("[ATbot] Switching to 0x%08x (%ls)\n", hwnd, Text);
-         MakeWindowActive(hwnd);
-         Esc = TRUE;
          break;
 
       default:
          goto Exit;
    }
+
+   if (!hwndActive)
+       goto Exit;
+
    // Main message loop:
    while (1)
    {
@@ -423,41 +696,38 @@ LRESULT WINAPI DoAppSwitch( WPARAM wParam, LPARAM lParam )
           PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
           if (HIWORD(msg.lParam) & KF_ALTDOWN)
           {
-             INT Shift;
              if ( msg.wParam == VK_TAB )
              {
                 if (Esc) break;
-                Shift = GetKeyState(VK_SHIFT) & 0x8000 ? SC_PREVWINDOW : SC_NEXTWINDOW;
-                if (Shift == SC_NEXTWINDOW)
+                if (GetKeyState(VK_SHIFT) < 0)
                 {
-                   selectedWindow = (selectedWindow + 1)%windowCount;
+                    MoveLeft();
                 }
                 else
                 {
-                   selectedWindow = selectedWindow - 1;
-                   if (selectedWindow < 0)
-                      selectedWindow = windowCount - 1;
+                    MoveRight();
                 }
-                InvalidateRect(switchdialog, NULL, TRUE);
              }
              else if ( msg.wParam == VK_ESCAPE )
              {
                 if (!Esc) break;
-                if (windowCount < 2)
-                   goto Exit;
-                if (wParam == SC_NEXTWINDOW)
-                {
-                   Count = (Count + 1)%windowCount;
-                }
-                else
-                {
-                   Count--;
-                   if (Count < 0)
-                      Count = windowCount - 1;
-                }
-                hwnd = windowList[Count];
-                GetWindowTextW(hwnd, Text, _countof(Text));
-                MakeWindowActive(hwnd);
+                RotateTasks(GetKeyState(VK_SHIFT) < 0);
+             }
+             else if ( msg.wParam == VK_LEFT )
+             {
+                MoveLeft();
+             }
+             else if ( msg.wParam == VK_RIGHT )
+             {
+                MoveRight();
+             }
+             else if ( msg.wParam == VK_UP )
+             {
+                MoveUp();
+             }
+             else if ( msg.wParam == VK_DOWN )
+             {
+                MoveDown();
              }
           }
           break;
@@ -480,23 +750,14 @@ LRESULT WINAPI DoAppSwitch( WPARAM wParam, LPARAM lParam )
 Exit:
    ReleaseCapture();
    if (switchdialog) DestroyWindow(switchdialog);
+   if (Esc) DestroyAppWindows();
    switchdialog = NULL;
    selectedWindow = 0;
    windowCount = 0;
+   Esc = FALSE;
    return 0;
 }
 
-VOID
-DestroyAppWindows()
-{
-   INT i;
-   for (i=0; i< windowCount; i++)
-   {
-      HICON hIcon = iconList[i];
-      DestroyIcon(hIcon);
-   }
-}
-
 //
 // Switch System Class Window Proc.
 //
@@ -511,7 +772,7 @@ LRESULT WINAPI SwitchWndProc_common(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM
       {
          NtUserSetWindowFNID(hWnd, FNID_SWITCH);
       }
-   }    
+   }
 
    switch (uMsg)
    {
@@ -526,10 +787,23 @@ LRESULT WINAPI SwitchWndProc_common(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM
          {
             PrepareWindow();
             ati = (PALTTABINFO)GetWindowLongPtrW(hWnd, 0);
+            ati->cbSize = sizeof(ALTTABINFO);
             ati->cItems = nItems;
-            ati->cxItem = ati->cyItem = 43;
-            ati->cRows = nRows;
             ati->cColumns = nCols;
+            ati->cRows = nRows;
+            if (nCols)
+            {
+               ati->iColFocus = (selectedWindow - nShift) % nCols;
+               ati->iRowFocus = (selectedWindow - nShift) / nCols;
+            }
+            else
+            {
+               ati->iColFocus = 0;
+               ati->iRowFocus = 0;
+            }
+            ati->cxItem = CX_ITEM_SPACE;
+            ati->cyItem = CY_ITEM_SPACE;
+            ati->ptStart = ptStart;
          }
          return 0;